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$"); 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 */ 102 103static inline vm_offset_t 104mips_phys_to_uncached(vm_paddr_t phys) 105{ 106 107 return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys)); 108} 109 110static inline uint8_t 111mips_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 119static inline void 120mips_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 */ 145static char buffer_data; 146static int buffer_valid; 147 148/* 149 * Low-level read and write routines. 150 */ 151static inline uint8_t 152gxemul_cons_data_read(void) 153{ 154 155 return (mips_ioread_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE + 156 GXEMUL_PUTGETCHAR_OFF))); 157} 158 159static inline void 160gxemul_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 167static int 168gxemul_cons_writable(void) 169{ 170 171 return (1); 172} 173 174static int 175gxemul_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 192static void 193gxemul_cons_write(char ch) 194{ 195 196 GC_LOCK_ASSERT(); 197 198 while (!gxemul_cons_writable()); 199 gxemul_cons_data_write(ch); 200} 201 202static char 203gxemul_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 */ 216static void 217gxemul_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 224static void 225gxemul_cons_cninit(struct consdev *cp) 226{ 227 228 GC_LOCK_INIT(); 229} 230 231static void 232gxemul_cons_cnterm(struct consdev *cp) 233{ 234 235} 236 237static int 238gxemul_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 248static void 249gxemul_cons_cnputc(struct consdev *cp, int c) 250{ 251 252 GC_LOCK(); 253 gxemul_cons_write(c); 254 GC_UNLOCK(); 255} 256 257static void 258gxemul_cons_cngrab(struct consdev *cp) 259{ 260 261} 262 263static void 264gxemul_cons_cnungrab(struct consdev *cp) 265{ 266 267} 268 269CONSOLE_DRIVER(gxemul_cons); 270 271/* 272 * TTY-level functions for gxemul_cons. 273 */ 274static void 275gxemul_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, CALLOUT_MPSAFE); 283 callout_reset(&gxemul_cons_callout, gxemul_cons_polltime, 284 gxemul_cons_timeout, tp); 285 286} 287SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, 288 gxemul_cons_ttyinit, NULL); 289 290static void 291gxemul_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 310static void 311gxemul_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