/* * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include static struct { char *buffer; int len; int used; char *write_ptr; char *read_ptr; decl_simple_lock_data(,read_lock); decl_simple_lock_data(,write_lock); } console_ring; hw_lock_data_t cnputc_lock; static volatile long console_output = 0; typedef struct console_buf { char *buf_base; char *buf_end; char *buf_ptr; #define CPU_BUFFER_LEN (256 - 3*(sizeof(char*))) char buf[CPU_BUFFER_LEN]; } console_buf_t; static void _serial_putc(int, int, int); struct console_ops cons_ops[] = { { .putc = _serial_putc, .getc = _serial_getc, }, { .putc = vcputc, .getc = vcgetc, }, }; uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]); uint32_t cons_ops_index = VC_CONS_OPS; /* This macro polls for pending TLB flushes while spinning on a lock */ #define SIMPLE_LOCK_NO_INTRS(l) \ MACRO_BEGIN \ boolean_t istate = ml_get_interrupts_enabled(); \ while (!simple_lock_try((l))) \ { \ if (!istate) \ handle_pending_TLB_flushes(); \ cpu_pause(); \ } \ MACRO_END void console_init(void) { int ret; console_ring.len = PAGE_SIZE; ret = kmem_alloc(kernel_map, (vm_offset_t *) &console_ring.buffer, console_ring.len); if (ret != KERN_SUCCESS) panic("console_ring_init() " "failed to allocate ring buffer, error %d\n", ret); console_ring.used = 0; console_ring.read_ptr = console_ring.buffer; console_ring.write_ptr = console_ring.buffer; simple_lock_init(&console_ring.read_lock, 0); simple_lock_init(&console_ring.write_lock, 0); hw_lock_init(&cnputc_lock); } void * console_cpu_alloc(__unused boolean_t boot_processor) { int ret; console_buf_t *cbp; ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbp, sizeof(console_buf_t)); if (ret != KERN_SUCCESS) { printf("console_cpu_alloc() " "failed to allocate cpu buffer, error=%d\n", ret); return NULL; } cbp->buf_base = (char *) &cbp->buf; cbp->buf_ptr = cbp->buf_base; cbp->buf_end = cbp->buf_base + CPU_BUFFER_LEN; return (void *) cbp; } void console_cpu_free(void *buf) { if (buf != NULL) kfree((void *) buf, sizeof(console_buf_t)); } /* So we can re-write the serial device functions at boot-time */ void console_set_serial_ops( struct console_ops *newops ) { cons_ops[SERIAL_CONS_OPS] = *newops; } static inline int console_ring_space(void) { return console_ring.len - console_ring.used; } static boolean_t console_ring_put(char ch) { if (console_ring.used < console_ring.len) { console_ring.used++;; *console_ring.write_ptr++ = ch; if (console_ring.write_ptr - console_ring.buffer == console_ring.len) console_ring.write_ptr = console_ring.buffer; return TRUE; } else { return FALSE; } } static int console_ring_get(void) { char ch = 0; if (console_ring.used > 0) { console_ring.used--; ch = *console_ring.read_ptr++; if (console_ring.read_ptr - console_ring.buffer == console_ring.len) console_ring.read_ptr = console_ring.buffer; } return (int) ch; } static inline void cpu_buffer_put(console_buf_t *cbp, char ch) { if (ch != '\0' && cbp->buf_ptr < cbp->buf_end) *(cbp->buf_ptr++) = ch; } static inline void _cnputc(char c) { /* The console device output routines are assumed to be * non-reentrant. */ cons_ops[cons_ops_index].putc(0, 0, c); if (c == '\n') cons_ops[cons_ops_index].putc(0, 0, '\r'); } void cnputc_unbuffered(char c) { _cnputc(c); } void cnputcusr(char c) { /* Spin (with pre-emption enabled) waiting for console_ring_try_empty() * to complete output. There is a small window here where we could * end up with a stale value of console_output, but it's unlikely, * and _cnputc(), which outputs to the console device, is internally * synchronized. There's something of a conflict between the * character-at-a-time (with pre-emption enabled) unbuffered * output model here, and the buffered output from cnputc(), * whose consumers include printf() ( which outputs a sequence * with pre-emption disabled, and should be safe to call with * interrupts off); we don't want to disable pre-emption indefinitely * here, and spinlocks and mutexes are inappropriate. */ while (console_output != 0); _cnputc(c); } static void console_ring_try_empty(void) { boolean_t state = ml_get_interrupts_enabled(); /* * Try to get the read lock on the ring buffer to empty it. * If this fails someone else is already emptying... */ if (!simple_lock_try(&console_ring.read_lock)) return; /* Indicate that we're in the process of writing a block of data * to the console. */ atomic_incl(&console_output, 1); for (;;) { char ch; if (!state) handle_pending_TLB_flushes(); SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); ch = console_ring_get(); simple_unlock(&console_ring.write_lock); if (ch == 0) break; _cnputc(ch); } atomic_decl(&console_output, 1); simple_unlock(&console_ring.read_lock); } void cnputc(char c) { console_buf_t *cbp; /* Put directly if console ring is not initialized */ _cnputc(c); return; } int _serial_getc(__unused int a, __unused int b, boolean_t wait, __unused boolean_t raw) { int c; do { c = serial_getc(); } while (wait && c < 0); return c; } static void _serial_putc(__unused int a, __unused int b, int c) { serial_putc(c); } int cngetc(void) { return cons_ops[cons_ops_index].getc(0, 0, TRUE, FALSE); } int cnmaygetc(void) { return cons_ops[cons_ops_index].getc(0, 0, FALSE, FALSE); } int vcgetc(__unused int l, __unused int u, __unused boolean_t wait, __unused boolean_t raw) { return( 0); }