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