28029b68c0
This is the final step required allowing to compile and to run RISC-V kernel and userland from HEAD. RISC-V is a completely open ISA that is freely available to academia and industry. Thanks to all the people involved! Special thanks to Andrew Turner, David Chisnall, Ed Maste, Konstantin Belousov, John Baldwin and Arun Thomas for their help. Thanks to Robert Watson for organizing this project. This project sponsored by UK Higher Education Innovation Fund (HEIF5) and DARPA CTSRD project at the University of Cambridge Computer Laboratory. FreeBSD/RISC-V project home: https://wiki.freebsd.org/riscv Reviewed by: andrew, emaste, kib Relnotes: Yes Sponsored by: DARPA, AFRL Sponsored by: HEIF5 Differential Revision: https://reviews.freebsd.org/D4982
362 lines
7.1 KiB
C
362 lines
7.1 KiB
C
/*-
|
|
* Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
|
|
* All rights reserved.
|
|
*
|
|
* Portions of this software were 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.
|
|
*
|
|
* Portions of this software were developed by the University of Cambridge
|
|
* Computer Laboratory as part of the CTSRD Project, with support from the
|
|
* UK Higher Education Innovation Fund (HEIF).
|
|
*
|
|
* 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 <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/kdb.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/priv.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/cons.h>
|
|
#include <sys/consio.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/module.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/trap.h>
|
|
|
|
#include "htif.h"
|
|
|
|
#include <dev/ofw/openfirm.h>
|
|
|
|
#include <ddb/ddb.h>
|
|
|
|
extern uint64_t console_intr;
|
|
|
|
static tsw_outwakeup_t riscvtty_outwakeup;
|
|
|
|
static struct ttydevsw riscv_ttydevsw = {
|
|
.tsw_flags = TF_NOPREFIX,
|
|
.tsw_outwakeup = riscvtty_outwakeup,
|
|
};
|
|
|
|
static int polltime;
|
|
static struct callout riscv_callout;
|
|
static struct tty *tp = NULL;
|
|
|
|
#if defined(KDB)
|
|
static int alt_break_state;
|
|
#endif
|
|
|
|
static void riscv_timeout(void *);
|
|
|
|
static cn_probe_t riscv_cnprobe;
|
|
static cn_init_t riscv_cninit;
|
|
static cn_term_t riscv_cnterm;
|
|
static cn_getc_t riscv_cngetc;
|
|
static cn_putc_t riscv_cnputc;
|
|
static cn_grab_t riscv_cngrab;
|
|
static cn_ungrab_t riscv_cnungrab;
|
|
|
|
CONSOLE_DRIVER(riscv);
|
|
|
|
#define MAX_BURST_LEN 1
|
|
#define QUEUE_SIZE 256
|
|
#define CONSOLE_DEFAULT_ID 1ul
|
|
|
|
struct queue_entry {
|
|
uint64_t data;
|
|
uint64_t used;
|
|
struct queue_entry *next;
|
|
};
|
|
|
|
struct queue_entry cnqueue[QUEUE_SIZE];
|
|
struct queue_entry *entry_last;
|
|
struct queue_entry *entry_served;
|
|
|
|
static void
|
|
htif_putc(int c)
|
|
{
|
|
uint64_t cmd;
|
|
|
|
cmd = (HTIF_CMD_WRITE << HTIF_CMD_SHIFT);
|
|
cmd |= (CONSOLE_DEFAULT_ID << HTIF_DEV_ID_SHIFT);
|
|
cmd |= c;
|
|
|
|
htif_command(cmd);
|
|
}
|
|
|
|
static uint8_t
|
|
htif_getc(void)
|
|
{
|
|
uint64_t cmd;
|
|
uint8_t res;
|
|
|
|
cmd = (HTIF_CMD_READ << HTIF_CMD_SHIFT);
|
|
cmd |= (CONSOLE_DEFAULT_ID << HTIF_DEV_ID_SHIFT);
|
|
|
|
res = htif_command(cmd);
|
|
|
|
return (res);
|
|
}
|
|
|
|
static void
|
|
riscv_putc(int c)
|
|
{
|
|
uint64_t counter;
|
|
uint64_t *cc;
|
|
uint64_t val;
|
|
|
|
val = 0;
|
|
counter = 0;
|
|
|
|
cc = (uint64_t*)&console_intr;
|
|
*cc = 0;
|
|
|
|
htif_putc(c);
|
|
|
|
/* Wait for an interrupt */
|
|
__asm __volatile(
|
|
"li %0, 1\n" /* counter = 1 */
|
|
"slli %0, %0, 12\n" /* counter <<= 12 */
|
|
"1:"
|
|
"addi %0, %0, -1\n" /* counter -= 1 */
|
|
"beqz %0, 2f\n" /* counter == 0 ? finish */
|
|
"ld %1, 0(%2)\n" /* val = *cc */
|
|
"beqz %1, 1b\n" /* val == 0 ? repeat */
|
|
"2:"
|
|
: "=&r"(counter), "=&r"(val) : "r"(cc)
|
|
);
|
|
}
|
|
|
|
#ifdef EARLY_PRINTF
|
|
early_putc_t *early_putc = riscv_putc;
|
|
#endif
|
|
|
|
static void
|
|
cn_drvinit(void *unused)
|
|
{
|
|
|
|
if (riscv_consdev.cn_pri != CN_DEAD &&
|
|
riscv_consdev.cn_name[0] != '\0') {
|
|
tp = tty_alloc(&riscv_ttydevsw, NULL);
|
|
tty_init_console(tp, 0);
|
|
tty_makedev(tp, NULL, "%s", "rcons");
|
|
|
|
polltime = 1;
|
|
|
|
callout_init(&riscv_callout, 1);
|
|
callout_reset(&riscv_callout, polltime, riscv_timeout, NULL);
|
|
}
|
|
}
|
|
|
|
SYSINIT(cndev, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, cn_drvinit, NULL);
|
|
|
|
static void
|
|
riscvtty_outwakeup(struct tty *tp)
|
|
{
|
|
u_char buf[MAX_BURST_LEN];
|
|
int len;
|
|
int i;
|
|
|
|
for (;;) {
|
|
len = ttydisc_getc(tp, buf, sizeof(buf));
|
|
if (len == 0)
|
|
break;
|
|
|
|
KASSERT(len == 1, ("tty error"));
|
|
|
|
for (i = 0; i < len; i++)
|
|
riscv_putc(buf[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
riscv_timeout(void *v)
|
|
{
|
|
int c;
|
|
|
|
tty_lock(tp);
|
|
while ((c = riscv_cngetc(NULL)) != -1)
|
|
ttydisc_rint(tp, c, 0);
|
|
ttydisc_rint_done(tp);
|
|
tty_unlock(tp);
|
|
|
|
callout_reset(&riscv_callout, polltime, riscv_timeout, NULL);
|
|
}
|
|
|
|
static void
|
|
riscv_cnprobe(struct consdev *cp)
|
|
{
|
|
|
|
cp->cn_pri = CN_NORMAL;
|
|
}
|
|
|
|
static void
|
|
riscv_cninit(struct consdev *cp)
|
|
{
|
|
int i;
|
|
|
|
strcpy(cp->cn_name, "rcons");
|
|
|
|
for (i = 0; i < QUEUE_SIZE; i++) {
|
|
if (i == (QUEUE_SIZE - 1))
|
|
cnqueue[i].next = &cnqueue[0];
|
|
else
|
|
cnqueue[i].next = &cnqueue[i+1];
|
|
cnqueue[i].data = 0;
|
|
cnqueue[i].used = 0;
|
|
}
|
|
|
|
entry_last = &cnqueue[0];
|
|
entry_served = &cnqueue[0];
|
|
}
|
|
|
|
static void
|
|
riscv_cnterm(struct consdev *cp)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
riscv_cngrab(struct consdev *cp)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
riscv_cnungrab(struct consdev *cp)
|
|
{
|
|
|
|
}
|
|
|
|
static int
|
|
riscv_cngetc(struct consdev *cp)
|
|
{
|
|
uint8_t data;
|
|
int ch;
|
|
|
|
ch = htif_getc();
|
|
|
|
if (entry_served->used == 1) {
|
|
data = entry_served->data;
|
|
entry_served->used = 0;
|
|
entry_served = entry_served->next;
|
|
ch = (data & 0xff);
|
|
if (ch > 0 && ch < 0xff) {
|
|
#if defined(KDB)
|
|
kdb_alt_break(ch, &alt_break_state);
|
|
#endif
|
|
return (ch);
|
|
}
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
static void
|
|
riscv_cnputc(struct consdev *cp, int c)
|
|
{
|
|
|
|
riscv_putc(c);
|
|
}
|
|
|
|
/*
|
|
* Bus interface.
|
|
*/
|
|
|
|
struct htif_console_softc {
|
|
device_t dev;
|
|
int running;
|
|
int intr_chan;
|
|
int cmd_done;
|
|
int curtag;
|
|
int index;
|
|
};
|
|
|
|
static void
|
|
htif_console_intr(void *arg, uint64_t entry)
|
|
{
|
|
struct htif_console_softc *sc;
|
|
uint8_t devcmd;
|
|
uint64_t data;
|
|
|
|
sc = arg;
|
|
|
|
devcmd = HTIF_DEV_CMD(entry);
|
|
data = HTIF_DEV_DATA(entry);
|
|
|
|
if (devcmd == 0) {
|
|
entry_last->data = data;
|
|
entry_last->used = 1;
|
|
entry_last = entry_last->next;
|
|
}
|
|
}
|
|
|
|
static int
|
|
htif_console_probe(device_t dev)
|
|
{
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
htif_console_attach(device_t dev)
|
|
{
|
|
struct htif_console_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
sc->dev = dev;
|
|
|
|
sc->index = htif_get_index(dev);
|
|
if (sc->index < 0)
|
|
return (EINVAL);
|
|
|
|
htif_setup_intr(sc->index, htif_console_intr, sc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static device_method_t htif_console_methods[] = {
|
|
DEVMETHOD(device_probe, htif_console_probe),
|
|
DEVMETHOD(device_attach, htif_console_attach),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t htif_console_driver = {
|
|
"htif_console",
|
|
htif_console_methods,
|
|
sizeof(struct htif_console_softc)
|
|
};
|
|
|
|
static devclass_t htif_console_devclass;
|
|
|
|
DRIVER_MODULE(htif_console, htif, htif_console_driver,
|
|
htif_console_devclass, 0, 0);
|