xref: /freebsd-13.1/sys/dev/gxemul/cons/gxemul_cons.c (revision 718cf2cc)
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 
67 static struct mtx	gc_lock;
68 
69 /*
70  * Low-level console driver functions.
71  */
72 static cn_probe_t	gxemul_cons_cnprobe;
73 static cn_init_t	gxemul_cons_cninit;
74 static cn_term_t	gxemul_cons_cnterm;
75 static cn_getc_t	gxemul_cons_cngetc;
76 static cn_putc_t	gxemul_cons_cnputc;
77 static cn_grab_t	gxemul_cons_cngrab;
78 static cn_ungrab_t	gxemul_cons_cnungrab;
79 
80 /*
81  * TTY-level fields.
82  */
83 static tsw_outwakeup_t	gxemul_cons_outwakeup;
84 
85 static struct ttydevsw gxemul_cons_ttydevsw = {
86 	.tsw_flags	= TF_NOPREFIX,
87 	.tsw_outwakeup	= gxemul_cons_outwakeup,
88 };
89 
90 static struct callout	gxemul_cons_callout;
91 static u_int		gxemul_cons_polltime = 10;
92 #ifdef KDB
93 static int		gxemul_cons_alt_break_state;
94 #endif
95 
96 static 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 
105 static inline vm_offset_t
mips_phys_to_uncached(vm_paddr_t phys)106 mips_phys_to_uncached(vm_paddr_t phys)
107 {
108 
109 	return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys));
110 }
111 
112 static inline uint8_t
mips_ioread_uint8(vm_offset_t vaddr)113 mips_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 
121 static inline void
mips_iowrite_uint8(vm_offset_t vaddr,uint8_t v)122 mips_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  */
147 static char	buffer_data;
148 static int	buffer_valid;
149 
150 /*
151  * Low-level read and write routines.
152  */
153 static inline uint8_t
gxemul_cons_data_read(void)154 gxemul_cons_data_read(void)
155 {
156 
157 	return (mips_ioread_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE +
158 	    GXEMUL_PUTGETCHAR_OFF)));
159 }
160 
161 static inline void
gxemul_cons_data_write(uint8_t v)162 gxemul_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 
169 static int
gxemul_cons_writable(void)170 gxemul_cons_writable(void)
171 {
172 
173 	return (1);
174 }
175 
176 static int
gxemul_cons_readable(void)177 gxemul_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 
194 static void
gxemul_cons_write(char ch)195 gxemul_cons_write(char ch)
196 {
197 
198 	GC_LOCK_ASSERT();
199 
200 	while (!gxemul_cons_writable());
201 	gxemul_cons_data_write(ch);
202 }
203 
204 static char
gxemul_cons_read(void)205 gxemul_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  */
218 static void
gxemul_cons_cnprobe(struct consdev * cp)219 gxemul_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 
226 static void
gxemul_cons_cninit(struct consdev * cp)227 gxemul_cons_cninit(struct consdev *cp)
228 {
229 
230 	GC_LOCK_INIT();
231 }
232 
233 static void
gxemul_cons_cnterm(struct consdev * cp)234 gxemul_cons_cnterm(struct consdev *cp)
235 {
236 
237 }
238 
239 static int
gxemul_cons_cngetc(struct consdev * cp)240 gxemul_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 
250 static void
gxemul_cons_cnputc(struct consdev * cp,int c)251 gxemul_cons_cnputc(struct consdev *cp, int c)
252 {
253 
254 	GC_LOCK();
255 	gxemul_cons_write(c);
256 	GC_UNLOCK();
257 }
258 
259 static void
gxemul_cons_cngrab(struct consdev * cp)260 gxemul_cons_cngrab(struct consdev *cp)
261 {
262 
263 }
264 
265 static void
gxemul_cons_cnungrab(struct consdev * cp)266 gxemul_cons_cnungrab(struct consdev *cp)
267 {
268 
269 }
270 
271 CONSOLE_DRIVER(gxemul_cons);
272 
273 /*
274  * TTY-level functions for gxemul_cons.
275  */
276 static void
gxemul_cons_ttyinit(void * unused)277 gxemul_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 }
289 SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE,
290     gxemul_cons_ttyinit, NULL);
291 
292 static void
gxemul_cons_outwakeup(struct tty * tp)293 gxemul_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 
312 static void
gxemul_cons_timeout(void * v)313 gxemul_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