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