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 67static struct mtx gc_lock; 68 69/* 70 * Low-level console driver functions. 71 */ 72static cn_probe_t gxemul_cons_cnprobe; 73static cn_init_t gxemul_cons_cninit; 74static cn_term_t gxemul_cons_cnterm; 75static cn_getc_t gxemul_cons_cngetc; 76static cn_putc_t gxemul_cons_cnputc; 77static cn_grab_t gxemul_cons_cngrab; 78static cn_ungrab_t gxemul_cons_cnungrab; 79 80/* 81 * TTY-level fields. 82 */ 83static tsw_outwakeup_t gxemul_cons_outwakeup; 84 85static struct ttydevsw gxemul_cons_ttydevsw = { 86 .tsw_flags = TF_NOPREFIX, 87 .tsw_outwakeup = gxemul_cons_outwakeup, 88}; 89 90static struct callout gxemul_cons_callout; 91static u_int gxemul_cons_polltime = 10; 92#ifdef KDB 93static int gxemul_cons_alt_break_state; 94#endif 95 96static 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 105static inline vm_offset_t 106mips_phys_to_uncached(vm_paddr_t phys) 107{ 108 109 return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys)); 110} 111 112static inline uint8_t 113mips_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 121static inline void 122mips_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 */ 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, 1); 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