/*- * Copyright (c) 2011-2012 Robert N. M. Watson * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #define GC_LOCK_INIT() mtx_init(&gc_lock, "gc_lock", NULL, MTX_SPIN) #define GC_LOCK() do { \ if (!kdb_active) \ mtx_lock_spin(&gc_lock); \ } while (0) #define GC_LOCK_ASSERT() do { \ if (!kdb_active) \ mtx_assert(&gc_lock, MA_OWNED); \ } while (0) #define GC_UNLOCK() do { \ if (!kdb_active) \ mtx_unlock_spin(&gc_lock); \ } while (0) static struct mtx gc_lock; /* * Low-level console driver functions. */ static cn_probe_t gxemul_cons_cnprobe; static cn_init_t gxemul_cons_cninit; static cn_term_t gxemul_cons_cnterm; static cn_getc_t gxemul_cons_cngetc; static cn_putc_t gxemul_cons_cnputc; static cn_grab_t gxemul_cons_cngrab; static cn_ungrab_t gxemul_cons_cnungrab; /* * TTY-level fields. */ static tsw_outwakeup_t gxemul_cons_outwakeup; static struct ttydevsw gxemul_cons_ttydevsw = { .tsw_flags = TF_NOPREFIX, .tsw_outwakeup = gxemul_cons_outwakeup, }; static struct callout gxemul_cons_callout; static u_int gxemul_cons_polltime = 10; #ifdef KDB static int gxemul_cons_alt_break_state; #endif static void gxemul_cons_timeout(void *); /* * I/O routines lifted from Deimos. * * XXXRW: Should be using FreeBSD's bus routines here, but they are not * available until later in the boot. */ typedef uint64_t paddr_t; typedef uint64_t vaddr_t; static inline vaddr_t mips_phys_to_uncached(paddr_t phys) { return (MIPS_PHYS_TO_DIRECT_UNCACHED(phys)); } static inline uint8_t mips_ioread_uint8(vaddr_t vaddr) { uint8_t v; __asm__ __volatile__ ("lbu %0, 0(%1)" : "=r" (v) : "r" (vaddr)); return (v); } static inline void mips_iowrite_uint8(vaddr_t vaddr, uint8_t v) { __asm__ __volatile__ ("sb %0, 0(%1)" : : "r" (v), "r" (vaddr)); } /* * gxemul-specific constants. */ #define GXEMUL_CONS_BASE 0x10000000 /* gxemul console device. */ /* * Routines for interacting with the gxemul test console. Programming details * are a result of manually inspecting the source code for gxemul's * dev_cons.cc and dev_cons.h. * * Offsets of I/O channels relative to the base. */ #define GXEMUL_PUTGETCHAR_OFF 0x00000000 #define GXEMUL_CONS_HALT 0x00000010 /* * One-byte buffer as we can't check whether the console is readable without * actually reading from it. */ static char buffer_data; static int buffer_valid; /* * Low-level read and write routines. */ static inline uint8_t gxemul_cons_data_read(void) { return (mips_ioread_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE + GXEMUL_PUTGETCHAR_OFF))); } static inline void gxemul_cons_data_write(uint8_t v) { mips_iowrite_uint8(mips_phys_to_uncached(GXEMUL_CONS_BASE + GXEMUL_PUTGETCHAR_OFF), v); } static int gxemul_cons_writable(void) { return (1); } static int gxemul_cons_readable(void) { uint32_t v; GC_LOCK_ASSERT(); if (buffer_valid) return (1); v = gxemul_cons_data_read(); if (v != 0) { buffer_valid = 1; buffer_data = v; return (1); } return (0); } static void gxemul_cons_write(char ch) { GC_LOCK_ASSERT(); while (!gxemul_cons_writable()); gxemul_cons_data_write(ch); } static char gxemul_cons_read(void) { GC_LOCK_ASSERT(); while (!gxemul_cons_readable()); buffer_valid = 0; return (buffer_data); } /* * Implementation of a FreeBSD low-level, polled console driver. */ static void gxemul_cons_cnprobe(struct consdev *cp) { sprintf(cp->cn_name, "ttyu0"); cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; } static void gxemul_cons_cninit(struct consdev *cp) { GC_LOCK_INIT(); } static void gxemul_cons_cnterm(struct consdev *cp) { } static int gxemul_cons_cngetc(struct consdev *cp) { int ret; GC_LOCK(); ret = gxemul_cons_read(); GC_UNLOCK(); return (ret); } static void gxemul_cons_cnputc(struct consdev *cp, int c) { GC_LOCK(); gxemul_cons_write(c); GC_UNLOCK(); } static void gxemul_cons_cngrab(struct consdev *cp) { } static void gxemul_cons_cnungrab(struct consdev *cp) { } CONSOLE_DRIVER(gxemul_cons); /* * TTY-level functions for gxemul_cons. */ static void gxemul_cons_ttyinit(void *unused) { struct tty *tp; tp = tty_alloc(&gxemul_cons_ttydevsw, NULL); tty_init_console(tp, 0); tty_makedev(tp, NULL, "%s", "ttyu0"); callout_init(&gxemul_cons_callout, CALLOUT_MPSAFE); callout_reset(&gxemul_cons_callout, gxemul_cons_polltime, gxemul_cons_timeout, tp); } SYSINIT(gxemul_cons_ttyinit, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, gxemul_cons_ttyinit, NULL); static void gxemul_cons_outwakeup(struct tty *tp) { int len; u_char ch; /* * XXXRW: Would be nice not to do blocking writes to the console here, * rescheduling on our timer tick if work remains to be done.. */ for (;;) { len = ttydisc_getc(tp, &ch, sizeof(ch)); if (len == 0) break; GC_LOCK(); gxemul_cons_write(ch); GC_UNLOCK(); } } static void gxemul_cons_timeout(void *v) { struct tty *tp; int c; tp = v; tty_lock(tp); GC_LOCK(); while (gxemul_cons_readable()) { c = gxemul_cons_read(); GC_UNLOCK(); #ifdef KDB kdb_alt_break(c, &gxemul_cons_alt_break_state); #endif ttydisc_rint(tp, c, 0); GC_LOCK(); } GC_UNLOCK(); ttydisc_rint_done(tp); tty_unlock(tp); callout_reset(&gxemul_cons_callout, gxemul_cons_polltime, gxemul_cons_timeout, tp); }