gxemul_cons.c revision 243422
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 243422 2012-11-23 04:28:13Z jmallett $"); 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 65static struct mtx gc_lock; 66 67/* 68 * Low-level console driver functions. 69 */ 70static cn_probe_t gxemul_cons_cnprobe; 71static cn_init_t gxemul_cons_cninit; 72static cn_term_t gxemul_cons_cnterm; 73static cn_getc_t gxemul_cons_cngetc; 74static cn_putc_t gxemul_cons_cnputc; 75static cn_grab_t gxemul_cons_cngrab; 76static cn_ungrab_t gxemul_cons_cnungrab; 77 78/* 79 * TTY-level fields. 80 */ 81static tsw_outwakeup_t gxemul_cons_outwakeup; 82 83static struct ttydevsw gxemul_cons_ttydevsw = { 84 .tsw_flags = TF_NOPREFIX, 85 .tsw_outwakeup = gxemul_cons_outwakeup, 86}; 87 88static struct callout gxemul_cons_callout; 89static u_int gxemul_cons_polltime = 10; 90#ifdef KDB 91static int gxemul_cons_alt_break_state; 92#endif 93 94static 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 */ 102typedef uint64_t paddr_t; 103typedef uint64_t vaddr_t; 104 105static inline vaddr_t 106mips_phys_to_uncached(paddr_t phys) 107{ 108 109 return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys)); 110} 111 112static inline uint8_t 113mips_ioread_uint8(vaddr_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(vaddr_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, CALLOUT_MPSAFE); 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