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