gxemul_cons.c revision 314667
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: stable/10/sys/dev/gxemul/cons/gxemul_cons.c 314667 2017-03-04 13:03:31Z avg $");
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/reboot.h>
41#include <sys/tty.h>
42
43#include <ddb/ddb.h>
44
45#include <machine/cpuregs.h>
46
47#define	GC_LOCK_INIT()		mtx_init(&gc_lock, "gc_lock", NULL, MTX_SPIN)
48
49#define	GC_LOCK() do {							\
50	if (!kdb_active)						\
51		mtx_lock_spin(&gc_lock);				\
52} while (0)
53
54#define	GC_LOCK_ASSERT() do {						\
55	if (!kdb_active)						\
56		mtx_assert(&gc_lock, MA_OWNED);				\
57} while (0)
58
59#define	GC_UNLOCK() do {						\
60	if (!kdb_active)						\
61		mtx_unlock_spin(&gc_lock);				\
62} while (0)
63
64
65static struct mtx	gc_lock;
66
67/*
68 * Low-level console driver functions.
69 */
70static cn_probe_t	gxemul_cons_cnprobe;
71static cn_init_t	gxemul_cons_cninit;
72static cn_term_t	gxemul_cons_cnterm;
73static cn_getc_t	gxemul_cons_cngetc;
74static cn_putc_t	gxemul_cons_cnputc;
75static cn_grab_t	gxemul_cons_cngrab;
76static cn_ungrab_t	gxemul_cons_cnungrab;
77
78/*
79 * TTY-level fields.
80 */
81static tsw_outwakeup_t	gxemul_cons_outwakeup;
82
83static struct ttydevsw gxemul_cons_ttydevsw = {
84	.tsw_flags	= TF_NOPREFIX,
85	.tsw_outwakeup	= gxemul_cons_outwakeup,
86};
87
88static struct callout	gxemul_cons_callout;
89static u_int		gxemul_cons_polltime = 10;
90#ifdef KDB
91static int		gxemul_cons_alt_break_state;
92#endif
93
94static void		gxemul_cons_timeout(void *);
95
96/*
97 * I/O routines lifted from Deimos.
98 *
99 * XXXRW: Should be using FreeBSD's bus routines here, but they are not
100 * available until later in the boot.
101 */
102
103static inline vm_offset_t
104mips_phys_to_uncached(vm_paddr_t phys)
105{
106
107	return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys));
108}
109
110static inline uint8_t
111mips_ioread_uint8(vm_offset_t vaddr)
112{
113	uint8_t v;
114
115	__asm__ __volatile__ ("lbu %0, 0(%1)" : "=r" (v) : "r" (vaddr));
116	return (v);
117}
118
119static inline void
120mips_iowrite_uint8(vm_offset_t vaddr, uint8_t v)
121{
122
123	__asm__ __volatile__ ("sb %0, 0(%1)" : : "r" (v), "r" (vaddr));
124}
125
126/*
127 * gxemul-specific constants.
128 */
129#define	GXEMUL_CONS_BASE	0x10000000	/* gxemul console device. */
130
131/*
132 * Routines for interacting with the gxemul test console.  Programming details
133 * are a result of manually inspecting the source code for gxemul's
134 * dev_cons.cc and dev_cons.h.
135 *
136 * Offsets of I/O channels relative to the base.
137 */
138#define	GXEMUL_PUTGETCHAR_OFF		0x00000000
139#define	GXEMUL_CONS_HALT		0x00000010
140
141/*
142 * One-byte buffer as we can't check whether the console is readable without
143 * actually reading from it.
144 */
145static char	buffer_data;
146static int	buffer_valid;
147
148/*
149 * Low-level read and write routines.
150 */
151static inline uint8_t
152gxemul_cons_data_read(void)
153{
154
155	return (mips_ioread_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE +
156	    GXEMUL_PUTGETCHAR_OFF)));
157}
158
159static inline void
160gxemul_cons_data_write(uint8_t v)
161{
162
163	mips_iowrite_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE +
164	    GXEMUL_PUTGETCHAR_OFF), v);
165}
166
167static int
168gxemul_cons_writable(void)
169{
170
171	return (1);
172}
173
174static int
175gxemul_cons_readable(void)
176{
177	uint32_t v;
178
179	GC_LOCK_ASSERT();
180
181	if (buffer_valid)
182		return (1);
183	v = gxemul_cons_data_read();
184	if (v != 0) {
185		buffer_valid = 1;
186		buffer_data = v;
187		return (1);
188	}
189	return (0);
190}
191
192static void
193gxemul_cons_write(char ch)
194{
195
196	GC_LOCK_ASSERT();
197
198	while (!gxemul_cons_writable());
199	gxemul_cons_data_write(ch);
200}
201
202static char
203gxemul_cons_read(void)
204{
205
206	GC_LOCK_ASSERT();
207
208	while (!gxemul_cons_readable());
209	buffer_valid = 0;
210	return (buffer_data);
211}
212
213/*
214 * Implementation of a FreeBSD low-level, polled console driver.
215 */
216static void
217gxemul_cons_cnprobe(struct consdev *cp)
218{
219
220	sprintf(cp->cn_name, "ttyu0");
221	cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
222}
223
224static void
225gxemul_cons_cninit(struct consdev *cp)
226{
227
228	GC_LOCK_INIT();
229}
230
231static void
232gxemul_cons_cnterm(struct consdev *cp)
233{
234
235}
236
237static int
238gxemul_cons_cngetc(struct consdev *cp)
239{
240	int ret;
241
242	GC_LOCK();
243	ret = gxemul_cons_read();
244	GC_UNLOCK();
245	return (ret);
246}
247
248static void
249gxemul_cons_cnputc(struct consdev *cp, int c)
250{
251
252	GC_LOCK();
253	gxemul_cons_write(c);
254	GC_UNLOCK();
255}
256
257static void
258gxemul_cons_cngrab(struct consdev *cp)
259{
260
261}
262
263static void
264gxemul_cons_cnungrab(struct consdev *cp)
265{
266
267}
268
269CONSOLE_DRIVER(gxemul_cons);
270
271/*
272 * TTY-level functions for gxemul_cons.
273 */
274static void
275gxemul_cons_ttyinit(void *unused)
276{
277	struct tty *tp;
278
279	tp = tty_alloc(&gxemul_cons_ttydevsw, NULL);
280	tty_init_console(tp, 0);
281	tty_makedev(tp, NULL, "%s", "ttyu0");
282	callout_init(&gxemul_cons_callout, 1);
283	callout_reset(&gxemul_cons_callout, gxemul_cons_polltime,
284	    gxemul_cons_timeout, tp);
285
286}
287SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
288    gxemul_cons_ttyinit, NULL);
289
290static void
291gxemul_cons_outwakeup(struct tty *tp)
292{
293	int len;
294	u_char ch;
295
296	/*
297	 * XXXRW: Would be nice not to do blocking writes to the console here,
298	 * rescheduling on our timer tick if work remains to be done..
299	 */
300	for (;;) {
301		len = ttydisc_getc(tp, &ch, sizeof(ch));
302		if (len == 0)
303			break;
304		GC_LOCK();
305		gxemul_cons_write(ch);
306		GC_UNLOCK();
307	}
308}
309
310static void
311gxemul_cons_timeout(void *v)
312{
313	struct tty *tp;
314	int c;
315
316	tp = v;
317	tty_lock(tp);
318	GC_LOCK();
319	while (gxemul_cons_readable()) {
320		c = gxemul_cons_read();
321		GC_UNLOCK();
322#ifdef KDB
323		kdb_alt_break(c, &gxemul_cons_alt_break_state);
324#endif
325		ttydisc_rint(tp, c, 0);
326		GC_LOCK();
327	}
328	GC_UNLOCK();
329	ttydisc_rint_done(tp);
330	tty_unlock(tp);
331	callout_reset(&gxemul_cons_callout, gxemul_cons_polltime,
332	    gxemul_cons_timeout, tp);
333}
334