1/*
2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
14 *
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
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <i386/mp.h>
30#include <i386/cpu_data.h>
31#include <i386/bit_routines.h>
32#include <i386/machine_cpu.h>
33#include <i386/machine_routines.h>
34#include <i386/misc_protos.h>
35#include <i386/serial_io.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
41static 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
51hw_lock_data_t cnputc_lock;
52static volatile long console_output = 0;
53
54typedef 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
62static void _serial_putc(int, int, int);
63
64struct console_ops cons_ops[] = {
65	{
66		.putc = _serial_putc,
67		.getc = _serial_getc,
68	},
69	{
70		.putc = vcputc,
71		.getc = vcgetc,
72	},
73};
74
75uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]);
76
77uint32_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)				\
82MACRO_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	}						\
90MACRO_END
91
92void
93console_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,
99			 console_ring.len);
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);
108	hw_lock_init(&cnputc_lock);
109}
110
111void *
112console_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,
118				sizeof(console_buf_t));
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
132void
133console_cpu_free(void *buf)
134{
135	if (buf != NULL)
136		kfree((void *) buf, sizeof(console_buf_t));
137}
138
139/* So we can re-write the serial device functions at boot-time */
140void
141console_set_serial_ops( struct console_ops *newops )
142{
143	cons_ops[SERIAL_CONS_OPS] = *newops;
144}
145
146static inline int
147console_ring_space(void)
148{
149	return console_ring.len - console_ring.used;
150}
151
152static boolean_t
153console_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
167static int
168console_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
182static inline void
183cpu_buffer_put(console_buf_t *cbp, char ch)
184{
185	if (ch != '\0' && cbp->buf_ptr < cbp->buf_end)
186		*(cbp->buf_ptr++) = ch;
187}
188
189static inline void
190_cnputc(char c)
191{
192	/* The console device output routines are assumed to be
193	 * non-reentrant.
194	 */
195	mp_disable_preemption();
196	if (!hw_lock_to(&cnputc_lock, LockTimeOutTSC)) {
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);
210	if (c == '\n')
211            cons_ops[cons_ops_index].putc(0, 0, '\r');
212	hw_lock_unlock(&cnputc_lock);
213	mp_enable_preemption();
214}
215
216void cnputc_unbuffered(char c) {
217	_cnputc(c);
218}
219
220void
221cnputcusr(char c)
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
237	_cnputc(c);
238}
239
240static void
241console_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);
266	simple_unlock(&console_ring.read_lock);
267}
268
269void
270cnputc(char c)
271{
272	console_buf_t	*cbp;
273	mp_disable_preemption();
274	cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf;
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 */
283	if (c != '\n') {
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		 */
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);
298		SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
299
300		/*
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.
305		 */
306		while (cbp->buf_ptr-cbp->buf_base + 1 > console_ring_space()) {
307			simple_unlock(&console_ring.write_lock);
308			ml_set_interrupts_enabled(state);
309			console_ring_try_empty();
310			state = ml_set_interrupts_enabled(FALSE);
311			SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
312		}
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);
319	}
320	console_ring_try_empty();
321	mp_enable_preemption();
322}
323
324int _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
334static void _serial_putc(__unused int a, __unused int b, int c)
335{
336    serial_putc(c);
337}
338
339
340int
341cngetc(void)
342{
343	return cons_ops[cons_ops_index].getc(0, 0,
344					     TRUE, FALSE);
345}
346
347int
348cnmaygetc(void)
349{
350	return cons_ops[cons_ops_index].getc(0, 0,
351					     FALSE, FALSE);
352}
353
354int
355vcgetc(__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}
367