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