gxemul_cons.c revision 234920
1/*-
2 * Copyright (c) 2011-2012 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * This software was developed by SRI International and the University of
6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7 * ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/dev/gxemul/cons/gxemul_cons.c 234920 2012-05-02 08:10:15Z rwatson $");
33
34#include <sys/param.h>
35#include <sys/cons.h>
36#include <sys/endian.h>
37#include <sys/kdb.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/tty.h>
41
42#include <ddb/ddb.h>
43
44#define	GC_LOCK_INIT()		mtx_init(&gc_lock, "gc_lock", NULL, MTX_SPIN)
45
46#define	GC_LOCK() do {							\
47	if (!kdb_active)						\
48		mtx_lock_spin(&gc_lock);				\
49} while (0)
50
51#define	GC_LOCK_ASSERT() do {						\
52	if (!kdb_active)						\
53		mtx_assert(&gc_lock, MA_OWNED);				\
54} while (0)
55
56#define	GC_UNLOCK() do {						\
57	if (!kdb_active)						\
58		mtx_unlock_spin(&gc_lock);				\
59} while (0)
60
61
62static struct mtx	gc_lock;
63
64/*
65 * Low-level console driver functions.
66 */
67static cn_probe_t	gxemul_cons_cnprobe;
68static cn_init_t	gxemul_cons_cninit;
69static cn_term_t	gxemul_cons_cnterm;
70static cn_getc_t	gxemul_cons_cngetc;
71static cn_putc_t	gxemul_cons_cnputc;
72static cn_grab_t	gxemul_cons_cngrab;
73static cn_ungrab_t	gxemul_cons_cnungrab;
74
75/*
76 * TTY-level fields.
77 */
78static tsw_outwakeup_t	gxemul_cons_outwakeup;
79
80static struct ttydevsw gxemul_cons_ttydevsw = {
81	.tsw_flags	= TF_NOPREFIX,
82	.tsw_outwakeup	= gxemul_cons_outwakeup,
83};
84
85static struct callout	gxemul_cons_callout;
86static u_int		gxemul_cons_polltime = 10;
87#ifdef KDB
88static int		gxemul_cons_alt_break_state;
89#endif
90
91static void		gxemul_cons_timeout(void *);
92
93/*
94 * I/O routines lifted from Deimos.
95 *
96 * XXXRW: Should be using FreeBSD's bus routines here, but they are not
97 * available until later in the boot.
98 */
99#define	MIPS_XKPHYS_UNCACHED_BASE	0x9000000000000000
100
101typedef	uint64_t	paddr_t;
102typedef	uint64_t	vaddr_t;
103
104static inline vaddr_t
105mips_phys_to_uncached(paddr_t phys)
106{
107
108	return (phys | MIPS_XKPHYS_UNCACHED_BASE);
109}
110
111static inline uint8_t
112mips_ioread_uint8(vaddr_t vaddr)
113{
114	uint8_t v;
115
116	__asm__ __volatile__ ("lbu %0, 0(%1)" : "=r" (v) : "r" (vaddr));
117	return (v);
118}
119
120static inline void
121mips_iowrite_uint8(vaddr_t vaddr, uint8_t v)
122{
123
124	__asm__ __volatile__ ("sb %0, 0(%1)" : : "r" (v), "r" (vaddr));
125}
126
127/*
128 * gxemul-specific constants.
129 */
130#define	GXEMUL_CONS_BASE	0x10000000	/* gxemul console device. */
131
132/*
133 * Routines for interacting with the gxemul test console.  Programming details
134 * are a result of manually inspecting the source code for gxemul's
135 * dev_cons.cc and dev_cons.h.
136 *
137 * Offsets of I/O channels relative to the base.
138 */
139#define	GXEMUL_PUTGETCHAR_OFF		0x00000000
140#define	GXEMUL_CONS_HALT		0x00000010
141
142/*
143 * One-byte buffer as we can't check whether the console is readable without
144 * actually reading from it.
145 */
146static char	buffer_data;
147static int	buffer_valid;
148
149/*
150 * Low-level read and write routines.
151 */
152static inline uint8_t
153gxemul_cons_data_read(void)
154{
155
156	return (mips_ioread_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE +
157	    GXEMUL_PUTGETCHAR_OFF)));
158}
159
160static inline void
161gxemul_cons_data_write(uint8_t v)
162{
163
164	mips_iowrite_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE +
165	    GXEMUL_PUTGETCHAR_OFF), v);
166}
167
168static int
169gxemul_cons_writable(void)
170{
171
172	return (1);
173}
174
175static int
176gxemul_cons_readable(void)
177{
178	uint32_t v;
179
180	GC_LOCK_ASSERT();
181
182	if (buffer_valid)
183		return (1);
184	v = gxemul_cons_data_read();
185	if (v != 0) {
186		buffer_valid = 1;
187		buffer_data = v;
188		return (1);
189	}
190	return (0);
191}
192
193static void
194gxemul_cons_write(char ch)
195{
196
197	GC_LOCK_ASSERT();
198
199	while (!gxemul_cons_writable());
200	gxemul_cons_data_write(ch);
201}
202
203static char
204gxemul_cons_read(void)
205{
206
207	GC_LOCK_ASSERT();
208
209	while (!gxemul_cons_readable());
210	buffer_valid = 0;
211	return (buffer_data);
212}
213
214/*
215 * Implementation of a FreeBSD low-level, polled console driver.
216 */
217static void
218gxemul_cons_cnprobe(struct consdev *cp)
219{
220
221	sprintf(cp->cn_name, "gxcons");
222	cp->cn_pri = CN_NORMAL;
223}
224
225static void
226gxemul_cons_cninit(struct consdev *cp)
227{
228
229	GC_LOCK_INIT();
230}
231
232static void
233gxemul_cons_cnterm(struct consdev *cp)
234{
235
236}
237
238static int
239gxemul_cons_cngetc(struct consdev *cp)
240{
241	int ret;
242
243	GC_LOCK();
244	ret = gxemul_cons_read();
245	GC_UNLOCK();
246	return (ret);
247}
248
249static void
250gxemul_cons_cnputc(struct consdev *cp, int c)
251{
252
253	GC_LOCK();
254	gxemul_cons_write(c);
255	GC_UNLOCK();
256}
257
258static void
259gxemul_cons_cngrab(struct consdev *cp)
260{
261
262}
263
264static void
265gxemul_cons_cnungrab(struct consdev *cp)
266{
267
268}
269
270CONSOLE_DRIVER(gxemul_cons);
271
272/*
273 * TTY-level functions for gxemul_cons.
274 */
275static void
276gxemul_cons_ttyinit(void *unused)
277{
278	struct tty *tp;
279
280	tp = tty_alloc(&gxemul_cons_ttydevsw, NULL);
281	tty_init_console(tp, 0);
282	tty_makedev(tp, NULL, "%s", "gxcons");
283	callout_init(&gxemul_cons_callout, CALLOUT_MPSAFE);
284	callout_reset(&gxemul_cons_callout, gxemul_cons_polltime,
285	    gxemul_cons_timeout, tp);
286
287}
288SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
289    gxemul_cons_ttyinit, NULL);
290
291static void
292gxemul_cons_outwakeup(struct tty *tp)
293{
294	int len;
295	u_char ch;
296
297	/*
298	 * XXXRW: Would be nice not to do blocking writes to the console here,
299	 * rescheduling on our timer tick if work remains to be done..
300	 */
301	for (;;) {
302		len = ttydisc_getc(tp, &ch, sizeof(ch));
303		if (len == 0)
304			break;
305		GC_LOCK();
306		gxemul_cons_write(ch);
307		GC_UNLOCK();
308	}
309}
310
311static void
312gxemul_cons_timeout(void *v)
313{
314	struct tty *tp;
315	int c;
316
317	tp = v;
318	tty_lock(tp);
319	GC_LOCK();
320	while (gxemul_cons_readable()) {
321		c = gxemul_cons_read();
322		GC_UNLOCK();
323#ifdef KDB
324		kdb_alt_break(c, &gxemul_cons_alt_break_state);
325#endif
326		ttydisc_rint(tp, c, 0);
327		GC_LOCK();
328	}
329	GC_UNLOCK();
330	ttydisc_rint_done(tp);
331	tty_unlock(tp);
332	callout_reset(&gxemul_cons_callout, gxemul_cons_polltime,
333	    gxemul_cons_timeout, tp);
334}
335