]>
Commit | Line | Data |
---|---|---|
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 | ||
40 | static 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 |
50 | hw_lock_data_t cnputc_lock; |
51 | static volatile long console_output = 0; | |
52 | ||
91447636 A |
53 | typedef 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 |
61 | extern int serial_getc(void); |
62 | extern void serial_putc(int); | |
63 | ||
64 | static void _serial_putc(int, int, int); | |
65 | ||
2d21ac55 A |
66 | struct 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 | ||
77 | uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]); | |
78 | ||
79 | uint32_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) \ | |
84 | MACRO_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 | } \ | |
92 | MACRO_END | |
93 | ||
55e303ae | 94 | void |
91447636 A |
95 | console_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 | ||
113 | void * | |
114 | console_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 | ||
134 | void | |
135 | console_cpu_free(void *buf) | |
136 | { | |
137 | if (buf != NULL) | |
138 | kfree((void *) buf, sizeof(console_buf_t)); | |
139 | } | |
140 | ||
0c530ab8 A |
141 | static inline int |
142 | console_ring_space(void) | |
143 | { | |
144 | return console_ring.len - console_ring.used; | |
145 | } | |
146 | ||
91447636 A |
147 | static boolean_t |
148 | console_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 | ||
162 | static int | |
163 | console_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 | ||
177 | static inline void | |
178 | cpu_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 | ||
184 | static 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 | ||
211 | void | |
212 | cnputcusr(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 | ||
231 | static void | |
232 | console_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 | ||
260 | void | |
261 | cnputc(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 | |
320 | int _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 | ||
330 | static void _serial_putc(__unused int a, __unused int b, int c) | |
331 | { | |
332 | serial_putc(c); | |
333 | } | |
334 | ||
335 | ||
336 | int | |
337 | cngetc(void) | |
338 | { | |
339 | return cons_ops[cons_ops_index].getc(0, 0, | |
340 | TRUE, FALSE); | |
341 | } | |
342 | ||
343 | int | |
344 | cnmaygetc(void) | |
345 | { | |
346 | return cons_ops[cons_ops_index].getc(0, 0, | |
347 | FALSE, FALSE); | |
348 | } | |
349 | ||
350 | int | |
351 | vcgetc(__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 | } |