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