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
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
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
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
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
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
170 gxemul_cons_writable(void)
171 {
172
173 return (1);
174 }
175
176 static int
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
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
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
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
227 gxemul_cons_cninit(struct consdev *cp)
228 {
229
230 GC_LOCK_INIT();
231 }
232
233 static void
234 gxemul_cons_cnterm(struct consdev *cp)
235 {
236
237 }
238
239 static int
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
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
260 gxemul_cons_cngrab(struct consdev *cp)
261 {
262
263 }
264
265 static void
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
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
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
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 }
Cache object: fd86f71b7c54f3c6c5ae0e1906fde8ad
|