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