]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
0c530ab8 A |
43 | hw_lock_data_t cnputc_lock; |
44 | static volatile long console_output = 0; | |
45 | ||
91447636 A |
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; | |
1c79356b | 53 | |
0c530ab8 A |
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 | ||
55e303ae | 83 | void |
91447636 A |
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); | |
0c530ab8 | 99 | hw_lock_init(&cnputc_lock); |
91447636 A |
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 | ||
0c530ab8 A |
130 | static inline int |
131 | console_ring_space(void) | |
132 | { | |
133 | return console_ring.len - console_ring.used; | |
134 | } | |
135 | ||
91447636 A |
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 | { | |
0c530ab8 | 169 | if (ch != '\0' && cbp->buf_ptr < cbp->buf_end) |
91447636 A |
170 | *(cbp->buf_ptr++) = ch; |
171 | } | |
172 | ||
173 | static 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 | ||
200 | void | |
201 | cnputcusr(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 | ||
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); | |
91447636 A |
246 | simple_unlock(&console_ring.read_lock); |
247 | } | |
248 | ||
249 | void | |
250 | cnputc(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 | |
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 | } |