2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
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>
39 decl_simple_lock_data(,read_lock
);
40 decl_simple_lock_data(,write_lock
);
43 hw_lock_data_t cnputc_lock
;
44 static volatile long console_output
= 0;
46 typedef struct console_buf
{
50 #define CPU_BUFFER_LEN (256 - 3*(sizeof(char*)))
51 char buf
[CPU_BUFFER_LEN
];
54 extern int serial_getc(void);
55 extern void serial_putc(int);
57 static void _serial_putc(int, int, int);
59 int vcgetc(int, int, boolean_t
, boolean_t
);
61 console_ops_t cons_ops
[] = {
62 {_serial_putc
, _serial_getc
},
66 uint32_t nconsops
= (sizeof cons_ops
/ sizeof cons_ops
[0]);
68 uint32_t cons_ops_index
= VC_CONS_OPS
;
70 /* This macro polls for pending TLB flushes while spinning on a lock
72 #define SIMPLE_LOCK_NO_INTRS(l) \
74 boolean_t istate = ml_get_interrupts_enabled(); \
75 while (!simple_lock_try((l))) \
78 handle_pending_TLB_flushes(); \
88 console_ring
.len
= PAGE_SIZE
;
89 ret
= kmem_alloc(kernel_map
, (vm_offset_t
*) &console_ring
.buffer
,
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
);
103 console_cpu_alloc(__unused boolean_t boot_processor
)
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
);
116 cbp
->buf_base
= (char *) &cbp
->buf
;
117 cbp
->buf_ptr
= cbp
->buf_base
;
118 cbp
->buf_end
= cbp
->buf_base
+ CPU_BUFFER_LEN
;
124 console_cpu_free(void *buf
)
127 kfree((void *) buf
, sizeof(console_buf_t
));
131 console_ring_space(void)
133 return console_ring
.len
- console_ring
.used
;
137 console_ring_put(char ch
)
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
144 console_ring
.write_ptr
= console_ring
.buffer
;
152 console_ring_get(void)
156 if (console_ring
.used
> 0) {
158 ch
= *console_ring
.read_ptr
++;
159 if (console_ring
.read_ptr
- console_ring
.buffer
161 console_ring
.read_ptr
= console_ring
.buffer
;
167 cpu_buffer_put(console_buf_t
*cbp
, char ch
)
169 if (ch
!= '\0' && cbp
->buf_ptr
< cbp
->buf_end
)
170 *(cbp
->buf_ptr
++) = ch
;
176 /* The console device output routines are assumed to be
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,
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
);
191 panic("Lock acquire timeout in _cnputc()");
193 cons_ops
[cons_ops_index
].putc(0, 0, c
);
195 cons_ops
[cons_ops_index
].putc(0, 0, '\r');
196 hw_lock_unlock(&cnputc_lock
);
197 mp_enable_preemption();
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.
215 while (console_output
!= 0);
221 console_ring_try_empty(void)
223 boolean_t state
= ml_get_interrupts_enabled();
225 * Try to get the read lock on the ring buffer to empty it.
226 * If this fails someone else is already emptying...
228 if (!simple_lock_try(&console_ring
.read_lock
))
230 /* Indicate that we're in the process of writing a block of data
233 atomic_incl(&console_output
, 1);
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
);
245 atomic_decl(&console_output
, 1);
246 simple_unlock(&console_ring
.read_lock
);
254 /* Bypass locking/buffering if in debugger */
255 if (kdb_cpu
== cpu_number()) {
259 #endif /* MACH_KDB */
260 mp_disable_preemption();
261 cbp
= (console_buf_t
*) current_cpu_datap()->cpu_console_buf
;
263 mp_enable_preemption();
264 /* Put directly if console ring is not initialized */
269 /* add to stack buf */
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
276 cpu_buffer_put(cbp
, c
);
281 /* Here at end of printf -- time to try to output */
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
);
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.
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
);
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
);
305 console_ring_try_empty();
306 mp_enable_preemption();
309 int _serial_getc(__unused
int a
, __unused
int b
, boolean_t wait
, __unused boolean_t raw
)
314 } while (wait
&& c
< 0);
319 static void _serial_putc(__unused
int a
, __unused
int b
, int c
)
328 return cons_ops
[cons_ops_index
].getc(0, 0,
335 return cons_ops
[cons_ops_index
].getc(0, 0,
340 vcgetc(__unused
int l
,
342 __unused boolean_t wait
,
343 __unused boolean_t raw
)
347 if( 0 == (*PE_poll_input
)( 0, &c
))