]> git.saurik.com Git - apple/xnu.git/blame - osfmk/console/i386/serial_console.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / osfmk / console / i386 / serial_console.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b 27 */
1c79356b 28
55e303ae 29#include <i386/mp.h>
91447636 30#include <i386/cpu_data.h>
fe8ab488 31#include <i386/bit_routines.h>
91447636 32#include <i386/machine_cpu.h>
55e303ae 33#include <i386/machine_routines.h>
91447636 34#include <i386/misc_protos.h>
2d21ac55 35#include <i386/serial_io.h>
91447636
A
36#include <vm/vm_kern.h>
37#include <console/video_console.h>
0c530ab8 38#include <console/serial_protos.h>
91447636
A
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
0c530ab8
A
51hw_lock_data_t cnputc_lock;
52static volatile long console_output = 0;
53
91447636
A
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;
1c79356b 61
0c530ab8
A
62static void _serial_putc(int, int, int);
63
2d21ac55
A
64struct console_ops cons_ops[] = {
65 {
66 .putc = _serial_putc,
67 .getc = _serial_getc,
68 },
69 {
70 .putc = vcputc,
71 .getc = vcgetc,
72 },
0c530ab8
A
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
55e303ae 92void
91447636
A
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,
3e170ce0 99 console_ring.len, VM_KERN_MEMORY_OSFMK);
91447636
A
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);
0c530ab8 108 hw_lock_init(&cnputc_lock);
91447636
A
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,
3e170ce0 118 sizeof(console_buf_t), VM_KERN_MEMORY_OSFMK);
91447636
A
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
6d2010ae
A
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
0c530ab8
A
146static inline int
147console_ring_space(void)
148{
149 return console_ring.len - console_ring.used;
150}
151
91447636
A
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{
0c530ab8 185 if (ch != '\0' && cbp->buf_ptr < cbp->buf_end)
91447636
A
186 *(cbp->buf_ptr++) = ch;
187}
188
189static inline void
190_cnputc(char c)
1c79356b 191{
0c530ab8
A
192 /* The console device output routines are assumed to be
193 * non-reentrant.
194 */
195 mp_disable_preemption();
2d21ac55 196 if (!hw_lock_to(&cnputc_lock, LockTimeOutTSC)) {
0c530ab8
A
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);
55e303ae 210 if (c == '\n')
0c530ab8
A
211 cons_ops[cons_ops_index].putc(0, 0, '\r');
212 hw_lock_unlock(&cnputc_lock);
213 mp_enable_preemption();
91447636
A
214}
215
b0d623f7
A
216void cnputc_unbuffered(char c) {
217 _cnputc(c);
218}
219
91447636
A
220void
221cnputcusr(char c)
0c530ab8
A
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
91447636 237 _cnputc(c);
0c530ab8
A
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);
91447636
A
266 simple_unlock(&console_ring.read_lock);
267}
268
269void
270cnputc(char c)
271{
272 console_buf_t *cbp;
6601e61a 273 mp_disable_preemption();
6601e61a 274 cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf;
0c530ab8
A
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 */
91447636 283 if (c != '\n') {
0c530ab8
A
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 */
91447636
A
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);
0c530ab8 298 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
91447636
A
299
300 /*
0c530ab8
A
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.
91447636 305 */
0c530ab8
A
306 while (cbp->buf_ptr-cbp->buf_base + 1 > console_ring_space()) {
307 simple_unlock(&console_ring.write_lock);
060df5ea 308 ml_set_interrupts_enabled(state);
0c530ab8 309 console_ring_try_empty();
060df5ea 310 state = ml_set_interrupts_enabled(FALSE);
0c530ab8 311 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
91447636 312 }
0c530ab8
A
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);
91447636 319 }
0c530ab8 320 console_ring_try_empty();
91447636 321 mp_enable_preemption();
55e303ae 322}
0c530ab8
A
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}