1/* 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <i386/mp.h> 30#include <i386/cpu_data.h> 31#include <i386/machine_cpu.h> 32#include <i386/machine_routines.h> 33#include <i386/misc_protos.h> 34#include <i386/serial_io.h> 35#include <vm/vm_kern.h> 36#include <console/video_console.h> 37#include <console/serial_protos.h> 38#include <kern/kalloc.h> 39 40static struct { 41 char *buffer; 42 int len; 43 int used; 44 char *write_ptr; 45 char *read_ptr; 46 decl_simple_lock_data(,read_lock); 47 decl_simple_lock_data(,write_lock); 48} console_ring; 49 50hw_lock_data_t cnputc_lock; 51static volatile long console_output = 0; 52 53typedef struct console_buf { 54 char *buf_base; 55 char *buf_end; 56 char *buf_ptr; 57#define CPU_BUFFER_LEN (256 - 3*(sizeof(char*))) 58 char buf[CPU_BUFFER_LEN]; 59} console_buf_t; 60 61static void _serial_putc(int, int, int); 62 63struct console_ops cons_ops[] = { 64 { 65 .putc = _serial_putc, 66 .getc = _serial_getc, 67 }, 68 { 69 .putc = vcputc, 70 .getc = vcgetc, 71 }, 72}; 73 74uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]); 75 76uint32_t cons_ops_index = VC_CONS_OPS; 77 78/* This macro polls for pending TLB flushes while spinning on a lock 79 */ 80#define SIMPLE_LOCK_NO_INTRS(l) \ 81MACRO_BEGIN \ 82 boolean_t istate = ml_get_interrupts_enabled(); \ 83 while (!simple_lock_try((l))) \ 84 { \ 85 if (!istate) \ 86 handle_pending_TLB_flushes(); \ 87 cpu_pause(); \ 88 } \ 89MACRO_END 90 91void 92console_init(void) 93{ 94 int ret; 95 96 console_ring.len = PAGE_SIZE; 97 ret = kmem_alloc(kernel_map, (vm_offset_t *) &console_ring.buffer, 98 console_ring.len); 99 if (ret != KERN_SUCCESS) 100 panic("console_ring_init() " 101 "failed to allocate ring buffer, error %d\n", ret); 102 console_ring.used = 0; 103 console_ring.read_ptr = console_ring.buffer; 104 console_ring.write_ptr = console_ring.buffer; 105 simple_lock_init(&console_ring.read_lock, 0); 106 simple_lock_init(&console_ring.write_lock, 0); 107 hw_lock_init(&cnputc_lock); 108} 109 110void * 111console_cpu_alloc(__unused boolean_t boot_processor) 112{ 113 int ret; 114 console_buf_t *cbp; 115 116 ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbp, 117 sizeof(console_buf_t)); 118 if (ret != KERN_SUCCESS) { 119 printf("console_cpu_alloc() " 120 "failed to allocate cpu buffer, error=%d\n", ret); 121 return NULL; 122 } 123 124 cbp->buf_base = (char *) &cbp->buf; 125 cbp->buf_ptr = cbp->buf_base; 126 cbp->buf_end = cbp->buf_base + CPU_BUFFER_LEN; 127 128 return (void *) cbp; 129} 130 131void 132console_cpu_free(void *buf) 133{ 134 if (buf != NULL) 135 kfree((void *) buf, sizeof(console_buf_t)); 136} 137 138/* So we can re-write the serial device functions at boot-time */ 139void 140console_set_serial_ops( struct console_ops *newops ) 141{ 142 cons_ops[SERIAL_CONS_OPS] = *newops; 143} 144 145static inline int 146console_ring_space(void) 147{ 148 return console_ring.len - console_ring.used; 149} 150 151static boolean_t 152console_ring_put(char ch) 153{ 154 if (console_ring.used < console_ring.len) { 155 console_ring.used++;; 156 *console_ring.write_ptr++ = ch; 157 if (console_ring.write_ptr - console_ring.buffer 158 == console_ring.len) 159 console_ring.write_ptr = console_ring.buffer; 160 return TRUE; 161 } else { 162 return FALSE; 163 } 164} 165 166static int 167console_ring_get(void) 168{ 169 char ch = 0; 170 171 if (console_ring.used > 0) { 172 console_ring.used--; 173 ch = *console_ring.read_ptr++; 174 if (console_ring.read_ptr - console_ring.buffer 175 == console_ring.len) 176 console_ring.read_ptr = console_ring.buffer; 177 } 178 return (int) ch; 179} 180 181static inline void 182cpu_buffer_put(console_buf_t *cbp, char ch) 183{ 184 if (ch != '\0' && cbp->buf_ptr < cbp->buf_end) 185 *(cbp->buf_ptr++) = ch; 186} 187 188static inline void 189_cnputc(char c) 190{ 191 /* The console device output routines are assumed to be 192 * non-reentrant. 193 */ 194 mp_disable_preemption(); 195 if (!hw_lock_to(&cnputc_lock, LockTimeOutTSC)) { 196 /* If we timed out on the lock, and we're in the debugger, 197 * break the lock. 198 */ 199 if (debug_mode) { 200 /* Since hw_lock_to takes a pre-emption count...*/ 201 mp_enable_preemption(); 202 hw_lock_init(&cnputc_lock); 203 hw_lock_lock(&cnputc_lock); 204 } 205 else 206 panic("Lock acquire timeout in _cnputc()"); 207 } 208 cons_ops[cons_ops_index].putc(0, 0, c); 209 if (c == '\n') 210 cons_ops[cons_ops_index].putc(0, 0, '\r'); 211 hw_lock_unlock(&cnputc_lock); 212 mp_enable_preemption(); 213} 214 215void cnputc_unbuffered(char c) { 216 _cnputc(c); 217} 218 219void 220cnputcusr(char c) 221{ 222 /* Spin (with pre-emption enabled) waiting for console_ring_try_empty() 223 * to complete output. There is a small window here where we could 224 * end up with a stale value of console_output, but it's unlikely, 225 * and _cnputc(), which outputs to the console device, is internally 226 * synchronized. There's something of a conflict between the 227 * character-at-a-time (with pre-emption enabled) unbuffered 228 * output model here, and the buffered output from cnputc(), 229 * whose consumers include printf() ( which outputs a sequence 230 * with pre-emption disabled, and should be safe to call with 231 * interrupts off); we don't want to disable pre-emption indefinitely 232 * here, and spinlocks and mutexes are inappropriate. 233 */ 234 while (console_output != 0); 235 236 _cnputc(c); 237} 238 239static void 240console_ring_try_empty(void) 241{ 242 boolean_t state = ml_get_interrupts_enabled(); 243 /* 244 * Try to get the read lock on the ring buffer to empty it. 245 * If this fails someone else is already emptying... 246 */ 247 if (!simple_lock_try(&console_ring.read_lock)) 248 return; 249 /* Indicate that we're in the process of writing a block of data 250 * to the console. 251 */ 252 atomic_incl(&console_output, 1); 253 for (;;) { 254 char ch; 255 if (!state) 256 handle_pending_TLB_flushes(); 257 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); 258 ch = console_ring_get(); 259 simple_unlock(&console_ring.write_lock); 260 if (ch == 0) 261 break; 262 _cnputc(ch); 263 } 264 atomic_decl(&console_output, 1); 265 simple_unlock(&console_ring.read_lock); 266} 267 268void 269cnputc(char c) 270{ 271 console_buf_t *cbp; 272 mp_disable_preemption(); 273 cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf; 274 if (cbp == NULL) { 275 mp_enable_preemption(); 276 /* Put directly if console ring is not initialized */ 277 _cnputc(c); 278 return; 279 } 280 281 /* add to stack buf */ 282 if (c != '\n') { 283 /* XXX - cpu_buffer_put() can fail silently if the buffer 284 * is exhausted, as can happen if there's a long sequence 285 * of data with no newlines. We should, instead, attempt 286 * a flush. 287 */ 288 cpu_buffer_put(cbp, c); 289 } else { 290 boolean_t state; 291 char *cp; 292 293 /* Here at end of printf -- time to try to output */ 294 295 /* copy this buffer into the shared ring buffer */ 296 state = ml_set_interrupts_enabled(FALSE); 297 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); 298 299 /* 300 * Is there enough space in the shared ring buffer? 301 * Try to empty if not. 302 * Note, we want the entire local buffer to fit to 303 * avoid another cpu interjecting. 304 */ 305 while (cbp->buf_ptr-cbp->buf_base + 1 > console_ring_space()) { 306 simple_unlock(&console_ring.write_lock); 307 ml_set_interrupts_enabled(state); 308 console_ring_try_empty(); 309 state = ml_set_interrupts_enabled(FALSE); 310 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); 311 } 312 for (cp = cbp->buf_base; cp < cbp->buf_ptr; cp++) 313 console_ring_put(*cp); 314 console_ring_put('\n'); 315 cbp->buf_ptr = cbp->buf_base; 316 simple_unlock(&console_ring.write_lock); 317 ml_set_interrupts_enabled(state); 318 } 319 console_ring_try_empty(); 320 mp_enable_preemption(); 321} 322 323int _serial_getc(__unused int a, __unused int b, boolean_t wait, __unused boolean_t raw) 324{ 325 int c; 326 do { 327 c = serial_getc(); 328 } while (wait && c < 0); 329 330 return c; 331} 332 333static void _serial_putc(__unused int a, __unused int b, int c) 334{ 335 serial_putc(c); 336} 337 338 339int 340cngetc(void) 341{ 342 return cons_ops[cons_ops_index].getc(0, 0, 343 TRUE, FALSE); 344} 345 346int 347cnmaygetc(void) 348{ 349 return cons_ops[cons_ops_index].getc(0, 0, 350 FALSE, FALSE); 351} 352 353int 354vcgetc(__unused int l, 355 __unused int u, 356 __unused boolean_t wait, 357 __unused boolean_t raw) 358{ 359 char c; 360 361 if( 0 == (*PE_poll_input)( 0, &c)) 362 return( c); 363 else 364 return( 0); 365} 366