]> git.saurik.com Git - apple/xnu.git/blob - osfmk/console/i386/serial_console.c
a8bc82d29bd624663f41ef15af7cef0ce54924e1
[apple/xnu.git] / osfmk / console / i386 / serial_console.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <i386/mp.h>
24 #include <i386/cpu_data.h>
25 #include <i386/machine_cpu.h>
26 #include <i386/machine_routines.h>
27 #include <i386/misc_protos.h>
28 #include <vm/vm_kern.h>
29 #include <console/video_console.h>
30 #include <console/serial_protos.h>
31 #include <kern/kalloc.h>
32
33 static 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
43 hw_lock_data_t cnputc_lock;
44 static volatile long console_output = 0;
45
46 typedef 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;
53
54 extern int serial_getc(void);
55 extern void serial_putc(int);
56
57 static void _serial_putc(int, int, int);
58
59 int vcgetc(int, int, boolean_t, boolean_t);
60
61 console_ops_t cons_ops[] = {
62 {_serial_putc, _serial_getc},
63 {vcputc, vcgetc}
64 };
65
66 uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]);
67
68 uint32_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) \
73 MACRO_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 } \
81 MACRO_END
82
83 void
84 console_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);
99 hw_lock_init(&cnputc_lock);
100 }
101
102 void *
103 console_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
123 void
124 console_cpu_free(void *buf)
125 {
126 if (buf != NULL)
127 kfree((void *) buf, sizeof(console_buf_t));
128 }
129
130 static inline int
131 console_ring_space(void)
132 {
133 return console_ring.len - console_ring.used;
134 }
135
136 static boolean_t
137 console_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
151 static int
152 console_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
166 static inline void
167 cpu_buffer_put(console_buf_t *cbp, char ch)
168 {
169 if (ch != '\0' && cbp->buf_ptr < cbp->buf_end)
170 *(cbp->buf_ptr++) = ch;
171 }
172
173 static inline void
174 _cnputc(char c)
175 {
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);
194 if (c == '\n')
195 cons_ops[cons_ops_index].putc(0, 0, '\r');
196 hw_lock_unlock(&cnputc_lock);
197 mp_enable_preemption();
198 }
199
200 void
201 cnputcusr(char c)
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
217 _cnputc(c);
218 }
219
220 static void
221 console_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);
246 simple_unlock(&console_ring.read_lock);
247 }
248
249 void
250 cnputc(char c)
251 {
252 console_buf_t *cbp;
253 #if MACH_KDB
254 /* Bypass locking/buffering if in debugger */
255 if (kdb_cpu == cpu_number()) {
256 _cnputc(c);
257 return;
258 }
259 #endif /* MACH_KDB */
260 mp_disable_preemption();
261 cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf;
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 */
270 if (c != '\n') {
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 */
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);
285 SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
286
287 /*
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.
292 */
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);
297 }
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);
304 }
305 console_ring_try_empty();
306 mp_enable_preemption();
307 }
308
309 int _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
319 static void _serial_putc(__unused int a, __unused int b, int c)
320 {
321 serial_putc(c);
322 }
323
324
325 int
326 cngetc(void)
327 {
328 return cons_ops[cons_ops_index].getc(0, 0,
329 TRUE, FALSE);
330 }
331
332 int
333 cnmaygetc(void)
334 {
335 return cons_ops[cons_ops_index].getc(0, 0,
336 FALSE, FALSE);
337 }
338
339 int
340 vcgetc(__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 }