]>
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 | 30 | #include <i386/cpu_data.h> |
fe8ab488 | 31 | #include <i386/bit_routines.h> |
91447636 | 32 | #include <i386/machine_cpu.h> |
55e303ae | 33 | #include <i386/machine_routines.h> |
91447636 | 34 | #include <i386/misc_protos.h> |
2d21ac55 | 35 | #include <i386/serial_io.h> |
91447636 A |
36 | #include <vm/vm_kern.h> |
37 | #include <console/video_console.h> | |
0c530ab8 | 38 | #include <console/serial_protos.h> |
91447636 A |
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 | ||
0c530ab8 A |
51 | hw_lock_data_t cnputc_lock; |
52 | static volatile long console_output = 0; | |
53 | ||
91447636 A |
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; | |
1c79356b | 61 | |
0c530ab8 A |
62 | static void _serial_putc(int, int, int); |
63 | ||
2d21ac55 A |
64 | struct console_ops cons_ops[] = { |
65 | { | |
66 | .putc = _serial_putc, | |
67 | .getc = _serial_getc, | |
68 | }, | |
69 | { | |
70 | .putc = vcputc, | |
71 | .getc = vcgetc, | |
72 | }, | |
0c530ab8 A |
73 | }; |
74 | ||
75 | uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]); | |
76 | ||
77 | uint32_t cons_ops_index = VC_CONS_OPS; | |
78 | ||
79 | /* This macro polls for pending TLB flushes while spinning on a lock | |
80 | */ | |
81 | #define SIMPLE_LOCK_NO_INTRS(l) \ | |
82 | MACRO_BEGIN \ | |
83 | boolean_t istate = ml_get_interrupts_enabled(); \ | |
84 | while (!simple_lock_try((l))) \ | |
85 | { \ | |
86 | if (!istate) \ | |
87 | handle_pending_TLB_flushes(); \ | |
88 | cpu_pause(); \ | |
89 | } \ | |
90 | MACRO_END | |
91 | ||
55e303ae | 92 | void |
91447636 A |
93 | console_init(void) |
94 | { | |
95 | int ret; | |
96 | ||
97 | console_ring.len = PAGE_SIZE; | |
98 | ret = kmem_alloc(kernel_map, (vm_offset_t *) &console_ring.buffer, | |
3e170ce0 | 99 | console_ring.len, VM_KERN_MEMORY_OSFMK); |
91447636 A |
100 | if (ret != KERN_SUCCESS) |
101 | panic("console_ring_init() " | |
102 | "failed to allocate ring buffer, error %d\n", ret); | |
103 | console_ring.used = 0; | |
104 | console_ring.read_ptr = console_ring.buffer; | |
105 | console_ring.write_ptr = console_ring.buffer; | |
106 | simple_lock_init(&console_ring.read_lock, 0); | |
107 | simple_lock_init(&console_ring.write_lock, 0); | |
0c530ab8 | 108 | hw_lock_init(&cnputc_lock); |
91447636 A |
109 | } |
110 | ||
111 | void * | |
112 | console_cpu_alloc(__unused boolean_t boot_processor) | |
113 | { | |
114 | int ret; | |
115 | console_buf_t *cbp; | |
116 | ||
117 | ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbp, | |
3e170ce0 | 118 | sizeof(console_buf_t), VM_KERN_MEMORY_OSFMK); |
91447636 A |
119 | if (ret != KERN_SUCCESS) { |
120 | printf("console_cpu_alloc() " | |
121 | "failed to allocate cpu buffer, error=%d\n", ret); | |
122 | return NULL; | |
123 | } | |
124 | ||
125 | cbp->buf_base = (char *) &cbp->buf; | |
126 | cbp->buf_ptr = cbp->buf_base; | |
127 | cbp->buf_end = cbp->buf_base + CPU_BUFFER_LEN; | |
128 | ||
129 | return (void *) cbp; | |
130 | } | |
131 | ||
132 | void | |
133 | console_cpu_free(void *buf) | |
134 | { | |
135 | if (buf != NULL) | |
136 | kfree((void *) buf, sizeof(console_buf_t)); | |
137 | } | |
138 | ||
6d2010ae A |
139 | /* So we can re-write the serial device functions at boot-time */ |
140 | void | |
141 | console_set_serial_ops( struct console_ops *newops ) | |
142 | { | |
143 | cons_ops[SERIAL_CONS_OPS] = *newops; | |
144 | } | |
145 | ||
0c530ab8 A |
146 | static inline int |
147 | console_ring_space(void) | |
148 | { | |
149 | return console_ring.len - console_ring.used; | |
150 | } | |
151 | ||
91447636 A |
152 | static boolean_t |
153 | console_ring_put(char ch) | |
154 | { | |
155 | if (console_ring.used < console_ring.len) { | |
156 | console_ring.used++;; | |
157 | *console_ring.write_ptr++ = ch; | |
158 | if (console_ring.write_ptr - console_ring.buffer | |
159 | == console_ring.len) | |
160 | console_ring.write_ptr = console_ring.buffer; | |
161 | return TRUE; | |
162 | } else { | |
163 | return FALSE; | |
164 | } | |
165 | } | |
166 | ||
167 | static int | |
168 | console_ring_get(void) | |
169 | { | |
170 | char ch = 0; | |
171 | ||
172 | if (console_ring.used > 0) { | |
173 | console_ring.used--; | |
174 | ch = *console_ring.read_ptr++; | |
175 | if (console_ring.read_ptr - console_ring.buffer | |
176 | == console_ring.len) | |
177 | console_ring.read_ptr = console_ring.buffer; | |
178 | } | |
179 | return (int) ch; | |
180 | } | |
181 | ||
182 | static inline void | |
183 | cpu_buffer_put(console_buf_t *cbp, char ch) | |
184 | { | |
0c530ab8 | 185 | if (ch != '\0' && cbp->buf_ptr < cbp->buf_end) |
91447636 A |
186 | *(cbp->buf_ptr++) = ch; |
187 | } | |
188 | ||
189 | static inline void | |
190 | _cnputc(char c) | |
1c79356b | 191 | { |
0c530ab8 A |
192 | /* The console device output routines are assumed to be |
193 | * non-reentrant. | |
194 | */ | |
195 | mp_disable_preemption(); | |
2d21ac55 | 196 | if (!hw_lock_to(&cnputc_lock, LockTimeOutTSC)) { |
0c530ab8 A |
197 | /* If we timed out on the lock, and we're in the debugger, |
198 | * break the lock. | |
199 | */ | |
200 | if (debug_mode) { | |
201 | /* Since hw_lock_to takes a pre-emption count...*/ | |
202 | mp_enable_preemption(); | |
203 | hw_lock_init(&cnputc_lock); | |
204 | hw_lock_lock(&cnputc_lock); | |
205 | } | |
206 | else | |
207 | panic("Lock acquire timeout in _cnputc()"); | |
208 | } | |
209 | cons_ops[cons_ops_index].putc(0, 0, c); | |
55e303ae | 210 | if (c == '\n') |
0c530ab8 A |
211 | cons_ops[cons_ops_index].putc(0, 0, '\r'); |
212 | hw_lock_unlock(&cnputc_lock); | |
213 | mp_enable_preemption(); | |
91447636 A |
214 | } |
215 | ||
b0d623f7 A |
216 | void cnputc_unbuffered(char c) { |
217 | _cnputc(c); | |
218 | } | |
219 | ||
91447636 A |
220 | void |
221 | cnputcusr(char c) | |
0c530ab8 A |
222 | { |
223 | /* Spin (with pre-emption enabled) waiting for console_ring_try_empty() | |
224 | * to complete output. There is a small window here where we could | |
225 | * end up with a stale value of console_output, but it's unlikely, | |
226 | * and _cnputc(), which outputs to the console device, is internally | |
227 | * synchronized. There's something of a conflict between the | |
228 | * character-at-a-time (with pre-emption enabled) unbuffered | |
229 | * output model here, and the buffered output from cnputc(), | |
230 | * whose consumers include printf() ( which outputs a sequence | |
231 | * with pre-emption disabled, and should be safe to call with | |
232 | * interrupts off); we don't want to disable pre-emption indefinitely | |
233 | * here, and spinlocks and mutexes are inappropriate. | |
234 | */ | |
235 | while (console_output != 0); | |
236 | ||
91447636 | 237 | _cnputc(c); |
0c530ab8 A |
238 | } |
239 | ||
240 | static void | |
241 | console_ring_try_empty(void) | |
242 | { | |
243 | boolean_t state = ml_get_interrupts_enabled(); | |
244 | /* | |
245 | * Try to get the read lock on the ring buffer to empty it. | |
246 | * If this fails someone else is already emptying... | |
247 | */ | |
248 | if (!simple_lock_try(&console_ring.read_lock)) | |
249 | return; | |
250 | /* Indicate that we're in the process of writing a block of data | |
251 | * to the console. | |
252 | */ | |
253 | atomic_incl(&console_output, 1); | |
254 | for (;;) { | |
255 | char ch; | |
256 | if (!state) | |
257 | handle_pending_TLB_flushes(); | |
258 | SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); | |
259 | ch = console_ring_get(); | |
260 | simple_unlock(&console_ring.write_lock); | |
261 | if (ch == 0) | |
262 | break; | |
263 | _cnputc(ch); | |
264 | } | |
265 | atomic_decl(&console_output, 1); | |
91447636 A |
266 | simple_unlock(&console_ring.read_lock); |
267 | } | |
268 | ||
269 | void | |
270 | cnputc(char c) | |
271 | { | |
272 | console_buf_t *cbp; | |
6601e61a | 273 | mp_disable_preemption(); |
6601e61a | 274 | cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf; |
0c530ab8 A |
275 | if (cbp == NULL) { |
276 | mp_enable_preemption(); | |
277 | /* Put directly if console ring is not initialized */ | |
278 | _cnputc(c); | |
279 | return; | |
280 | } | |
281 | ||
282 | /* add to stack buf */ | |
91447636 | 283 | if (c != '\n') { |
0c530ab8 A |
284 | /* XXX - cpu_buffer_put() can fail silently if the buffer |
285 | * is exhausted, as can happen if there's a long sequence | |
286 | * of data with no newlines. We should, instead, attempt | |
287 | * a flush. | |
288 | */ | |
91447636 A |
289 | cpu_buffer_put(cbp, c); |
290 | } else { | |
291 | boolean_t state; | |
292 | char *cp; | |
293 | ||
294 | /* Here at end of printf -- time to try to output */ | |
295 | ||
296 | /* copy this buffer into the shared ring buffer */ | |
297 | state = ml_set_interrupts_enabled(FALSE); | |
0c530ab8 | 298 | SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); |
91447636 A |
299 | |
300 | /* | |
0c530ab8 A |
301 | * Is there enough space in the shared ring buffer? |
302 | * Try to empty if not. | |
303 | * Note, we want the entire local buffer to fit to | |
304 | * avoid another cpu interjecting. | |
91447636 | 305 | */ |
0c530ab8 A |
306 | while (cbp->buf_ptr-cbp->buf_base + 1 > console_ring_space()) { |
307 | simple_unlock(&console_ring.write_lock); | |
060df5ea | 308 | ml_set_interrupts_enabled(state); |
0c530ab8 | 309 | console_ring_try_empty(); |
060df5ea | 310 | state = ml_set_interrupts_enabled(FALSE); |
0c530ab8 | 311 | SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock); |
91447636 | 312 | } |
0c530ab8 A |
313 | for (cp = cbp->buf_base; cp < cbp->buf_ptr; cp++) |
314 | console_ring_put(*cp); | |
315 | console_ring_put('\n'); | |
316 | cbp->buf_ptr = cbp->buf_base; | |
317 | simple_unlock(&console_ring.write_lock); | |
318 | ml_set_interrupts_enabled(state); | |
91447636 | 319 | } |
0c530ab8 | 320 | console_ring_try_empty(); |
91447636 | 321 | mp_enable_preemption(); |
55e303ae | 322 | } |
0c530ab8 A |
323 | |
324 | int _serial_getc(__unused int a, __unused int b, boolean_t wait, __unused boolean_t raw) | |
325 | { | |
326 | int c; | |
327 | do { | |
328 | c = serial_getc(); | |
329 | } while (wait && c < 0); | |
330 | ||
331 | return c; | |
332 | } | |
333 | ||
334 | static void _serial_putc(__unused int a, __unused int b, int c) | |
335 | { | |
336 | serial_putc(c); | |
337 | } | |
338 | ||
339 | ||
340 | int | |
341 | cngetc(void) | |
342 | { | |
343 | return cons_ops[cons_ops_index].getc(0, 0, | |
344 | TRUE, FALSE); | |
345 | } | |
346 | ||
347 | int | |
348 | cnmaygetc(void) | |
349 | { | |
350 | return cons_ops[cons_ops_index].getc(0, 0, | |
351 | FALSE, FALSE); | |
352 | } | |
353 | ||
354 | int | |
355 | vcgetc(__unused int l, | |
356 | __unused int u, | |
357 | __unused boolean_t wait, | |
358 | __unused boolean_t raw) | |
359 | { | |
360 | char c; | |
361 | ||
362 | if( 0 == (*PE_poll_input)( 0, &c)) | |
363 | return( c); | |
364 | else | |
365 | return( 0); | |
366 | } |