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