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