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