aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/char/sclpconsole-lm.c10
-rw-r--r--hw/char/sclpconsole.c10
-rw-r--r--hw/core/loader.c18
-rw-r--r--hw/riscv/Makefile.objs11
-rw-r--r--hw/riscv/riscv_hart.c89
-rw-r--r--hw/riscv/riscv_htif.c258
-rw-r--r--hw/riscv/sifive_clint.c254
-rw-r--r--hw/riscv/sifive_e.c234
-rw-r--r--hw/riscv/sifive_plic.c505
-rw-r--r--hw/riscv/sifive_prci.c89
-rw-r--r--hw/riscv/sifive_test.c93
-rw-r--r--hw/riscv/sifive_u.c339
-rw-r--r--hw/riscv/sifive_uart.c176
-rw-r--r--hw/riscv/spike.c376
-rw-r--r--hw/riscv/virt.c420
-rw-r--r--hw/s390x/event-facility.c115
-rw-r--r--hw/s390x/ipl.c15
-rw-r--r--hw/s390x/s390-virtio-ccw.c20
-rw-r--r--hw/s390x/sclpcpu.c4
-rw-r--r--hw/s390x/sclpquiesce.c4
-rw-r--r--hw/s390x/virtio-ccw.c34
-rw-r--r--hw/s390x/virtio-ccw.h2
-rw-r--r--hw/sparc/sun4m.c1
-rw-r--r--hw/vfio/ccw.c4
24 files changed, 2991 insertions, 90 deletions
diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c
index c500bdaf29..dbc91a1e5b 100644
--- a/hw/char/sclpconsole-lm.c
+++ b/hw/char/sclpconsole-lm.c
@@ -102,12 +102,12 @@ static bool can_handle_event(uint8_t type)
return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD;
}
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
{
return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD;
}
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
{
return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD;
}
@@ -318,11 +318,6 @@ static int console_init(SCLPEvent *event)
return 0;
}
-static int console_exit(SCLPEvent *event)
-{
- return 0;
-}
-
static void console_reset(DeviceState *dev)
{
SCLPEvent *event = SCLP_EVENT(dev);
@@ -349,7 +344,6 @@ static void console_class_init(ObjectClass *klass, void *data)
dc->reset = console_reset;
dc->vmsd = &vmstate_sclplmconsole;
ec->init = console_init;
- ec->exit = console_exit;
ec->get_send_mask = send_mask;
ec->get_receive_mask = receive_mask;
ec->can_handle_event = can_handle_event;
diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c
index d0265dfa7a..1fa16e9055 100644
--- a/hw/char/sclpconsole.c
+++ b/hw/char/sclpconsole.c
@@ -83,12 +83,12 @@ static bool can_handle_event(uint8_t type)
return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
}
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
{
return SCLP_EVENT_MASK_MSG_ASCII;
}
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
{
return SCLP_EVENT_MASK_MSG_ASCII;
}
@@ -246,11 +246,6 @@ static void console_reset(DeviceState *dev)
scon->notify = false;
}
-static int console_exit(SCLPEvent *event)
-{
- return 0;
-}
-
static Property console_properties[] = {
DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
DEFINE_PROP_END_OF_LIST(),
@@ -265,7 +260,6 @@ static void console_class_init(ObjectClass *klass, void *data)
dc->reset = console_reset;
dc->vmsd = &vmstate_sclpconsole;
ec->init = console_init;
- ec->exit = console_exit;
ec->get_send_mask = send_mask;
ec->get_receive_mask = receive_mask;
ec->can_handle_event = can_handle_event;
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 76b244c508..06bdbca537 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -450,6 +450,20 @@ int load_elf_ram(const char *filename,
int clear_lsb, int data_swab, AddressSpace *as,
bool load_rom)
{
+ return load_elf_ram_sym(filename, translate_fn, translate_opaque,
+ pentry, lowaddr, highaddr, big_endian,
+ elf_machine, clear_lsb, data_swab, as,
+ load_rom, NULL);
+}
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf_ram_sym(const char *filename,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry,
+ uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
+ int elf_machine, int clear_lsb, int data_swab,
+ AddressSpace *as, bool load_rom, symbol_fn_t sym_cb)
+{
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
uint8_t e_ident[EI_NIDENT];
@@ -488,11 +502,11 @@ int load_elf_ram(const char *filename,
if (e_ident[EI_CLASS] == ELFCLASS64) {
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
- data_swab, as, load_rom);
+ data_swab, as, load_rom, sym_cb);
} else {
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
- data_swab, as, load_rom);
+ data_swab, as, load_rom, sym_cb);
}
fail:
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
new file mode 100644
index 0000000000..1dde01d39d
--- /dev/null
+++ b/hw/riscv/Makefile.objs
@@ -0,0 +1,11 @@
+obj-y += riscv_htif.o
+obj-y += riscv_hart.o
+obj-y += sifive_e.o
+obj-y += sifive_clint.o
+obj-y += sifive_prci.o
+obj-y += sifive_plic.o
+obj-y += sifive_test.o
+obj-y += sifive_u.o
+obj-y += sifive_uart.o
+obj-y += spike.o
+obj-y += virt.o
diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
new file mode 100644
index 0000000000..14e3c186fe
--- /dev/null
+++ b/hw/riscv/riscv_hart.c
@@ -0,0 +1,89 @@
+/*
+ * QEMU RISCV Hart Array
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+
+static Property riscv_harts_props[] = {
+ DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
+ DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_harts_cpu_reset(void *opaque)
+{
+ RISCVCPU *cpu = opaque;
+ cpu_reset(CPU(cpu));
+}
+
+static void riscv_harts_realize(DeviceState *dev, Error **errp)
+{
+ RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
+ Error *err = NULL;
+ int n;
+
+ s->harts = g_new0(RISCVCPU, s->num_harts);
+
+ for (n = 0; n < s->num_harts; n++) {
+
+ object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type);
+ s->harts[n].env.mhartid = n;
+ object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]),
+ &error_abort);
+ qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]);
+ object_property_set_bool(OBJECT(&s->harts[n]), true,
+ "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ }
+}
+
+static void riscv_harts_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = riscv_harts_props;
+ dc->realize = riscv_harts_realize;
+}
+
+static void riscv_harts_init(Object *obj)
+{
+ /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */
+}
+
+static const TypeInfo riscv_harts_info = {
+ .name = TYPE_RISCV_HART_ARRAY,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVHartArrayState),
+ .instance_init = riscv_harts_init,
+ .class_init = riscv_harts_class_init,
+};
+
+static void riscv_harts_register_types(void)
+{
+ type_register_static(&riscv_harts_info);
+}
+
+type_init(riscv_harts_register_types)
diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
new file mode 100644
index 0000000000..3e17f30251
--- /dev/null
+++ b/hw/riscv/riscv_htif.c
@@ -0,0 +1,258 @@
+/*
+ * QEMU RISC-V Host Target Interface (HTIF) Emulation
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides HTIF device emulation for QEMU. At the moment this allows
+ * for identical copies of bbl/linux to run on both spike and QEMU.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/riscv/riscv_htif.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
+
+#define RISCV_DEBUG_HTIF 0
+#define HTIF_DEBUG(fmt, ...) \
+ do { \
+ if (RISCV_DEBUG_HTIF) { \
+ qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
+ } \
+ } while (0)
+
+static uint64_t fromhost_addr, tohost_addr;
+
+void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
+ uint64_t st_size)
+{
+ if (strcmp("fromhost", st_name) == 0) {
+ fromhost_addr = st_value;
+ if (st_size != 8) {
+ error_report("HTIF fromhost must be 8 bytes");
+ exit(1);
+ }
+ } else if (strcmp("tohost", st_name) == 0) {
+ tohost_addr = st_value;
+ if (st_size != 8) {
+ error_report("HTIF tohost must be 8 bytes");
+ exit(1);
+ }
+ }
+}
+
+/*
+ * Called by the char dev to see if HTIF is ready to accept input.
+ */
+static int htif_can_recv(void *opaque)
+{
+ return 1;
+}
+
+/*
+ * Called by the char dev to supply input to HTIF console.
+ * We assume that we will receive one character at a time.
+ */
+static void htif_recv(void *opaque, const uint8_t *buf, int size)
+{
+ HTIFState *htifstate = opaque;
+
+ if (size != 1) {
+ return;
+ }
+
+ /* TODO - we need to check whether mfromhost is zero which indicates
+ the device is ready to receive. The current implementation
+ will drop characters */
+
+ uint64_t val_written = htifstate->pending_read;
+ uint64_t resp = 0x100 | *buf;
+
+ htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+}
+
+/*
+ * Called by the char dev to supply special events to the HTIF console.
+ * Not used for HTIF.
+ */
+static void htif_event(void *opaque, int event)
+{
+
+}
+
+static int htif_be_change(void *opaque)
+{
+ HTIFState *s = opaque;
+
+ qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
+ htif_be_change, s, NULL, true);
+
+ return 0;
+}
+
+static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
+{
+ uint8_t device = val_written >> 56;
+ uint8_t cmd = val_written >> 48;
+ uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
+ int resp = 0;
+
+ HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64
+ " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
+
+ /*
+ * Currently, there is a fixed mapping of devices:
+ * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
+ * 1: Console
+ */
+ if (unlikely(device == 0x0)) {
+ /* frontend syscall handler, shutdown and exit code support */
+ if (cmd == 0x0) {
+ if (payload & 0x1) {
+ /* exit code */
+ int exit_code = payload >> 1;
+ exit(exit_code);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n");
+ }
+ } else {
+ qemu_log("HTIF device %d: unknown command\n", device);
+ }
+ } else if (likely(device == 0x1)) {
+ /* HTIF Console */
+ if (cmd == 0x0) {
+ /* this should be a queue, but not yet implemented as such */
+ htifstate->pending_read = val_written;
+ htifstate->env->mtohost = 0; /* clear to indicate we read */
+ return;
+ } else if (cmd == 0x1) {
+ qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
+ resp = 0x100 | (uint8_t)payload;
+ } else {
+ qemu_log("HTIF device %d: unknown command\n", device);
+ }
+ } else {
+ qemu_log("HTIF unknown device or command\n");
+ HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64
+ " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload);
+ }
+ /*
+ * - latest bbl does not set fromhost to 0 if there is a value in tohost
+ * - with this code enabled, qemu hangs waiting for fromhost to go to 0
+ * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10
+ * - HTIF needs protocol documentation and a more complete state machine
+
+ while (!htifstate->fromhost_inprogress &&
+ htifstate->env->mfromhost != 0x0) {
+ }
+ */
+ htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+ htifstate->env->mtohost = 0; /* clear to indicate we read */
+}
+
+#define TOHOST_OFFSET1 (htifstate->tohost_offset)
+#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4)
+#define FROMHOST_OFFSET1 (htifstate->fromhost_offset)
+#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4)
+
+/* CPU wants to read an HTIF register */
+static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ HTIFState *htifstate = opaque;
+ if (addr == TOHOST_OFFSET1) {
+ return htifstate->env->mtohost & 0xFFFFFFFF;
+ } else if (addr == TOHOST_OFFSET2) {
+ return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF;
+ } else if (addr == FROMHOST_OFFSET1) {
+ return htifstate->env->mfromhost & 0xFFFFFFFF;
+ } else if (addr == FROMHOST_OFFSET2) {
+ return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF;
+ } else {
+ qemu_log("Invalid htif read: address %016" PRIx64 "\n",
+ (uint64_t)addr);
+ return 0;
+ }
+}
+
+/* CPU wrote to an HTIF register */
+static void htif_mm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ HTIFState *htifstate = opaque;
+ if (addr == TOHOST_OFFSET1) {
+ if (htifstate->env->mtohost == 0x0) {
+ htifstate->allow_tohost = 1;
+ htifstate->env->mtohost = value & 0xFFFFFFFF;
+ } else {
+ htifstate->allow_tohost = 0;
+ }
+ } else if (addr == TOHOST_OFFSET2) {
+ if (htifstate->allow_tohost) {
+ htifstate->env->mtohost |= value << 32;
+ htif_handle_tohost_write(htifstate, htifstate->env->mtohost);
+ }
+ } else if (addr == FROMHOST_OFFSET1) {
+ htifstate->fromhost_inprogress = 1;
+ htifstate->env->mfromhost = value & 0xFFFFFFFF;
+ } else if (addr == FROMHOST_OFFSET2) {
+ htifstate->env->mfromhost |= value << 32;
+ htifstate->fromhost_inprogress = 0;
+ } else {
+ qemu_log("Invalid htif write: address %016" PRIx64 "\n",
+ (uint64_t)addr);
+ }
+}
+
+static const MemoryRegionOps htif_mm_ops = {
+ .read = htif_mm_read,
+ .write = htif_mm_write,
+};
+
+HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
+ CPURISCVState *env, Chardev *chr)
+{
+ uint64_t base = MIN(tohost_addr, fromhost_addr);
+ uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
+ uint64_t tohost_offset = tohost_addr - base;
+ uint64_t fromhost_offset = fromhost_addr - base;
+
+ HTIFState *s = g_malloc0(sizeof(HTIFState));
+ s->address_space = address_space;
+ s->main_mem = main_mem;
+ s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem);
+ s->env = env;
+ s->tohost_offset = tohost_offset;
+ s->fromhost_offset = fromhost_offset;
+ s->pending_read = 0;
+ s->allow_tohost = 0;
+ s->fromhost_inprogress = 0;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
+ htif_be_change, s, NULL, true);
+ if (base) {
+ memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
+ TYPE_HTIF_UART, size);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ }
+
+ return s;
+}
diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c
new file mode 100644
index 0000000000..4893453b70
--- /dev/null
+++ b/hw/riscv/sifive_clint.c
@@ -0,0 +1,254 @@
+/*
+ * SiFive CLINT (Core Local Interruptor)
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides real-time clock, timer and interprocessor interrupts.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_clint.h"
+#include "qemu/timer.h"
+
+/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */
+#define TIMER_FREQ (10 * 1000 * 1000)
+
+static uint64_t cpu_riscv_read_rtc(void)
+{
+ return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ,
+ NANOSECONDS_PER_SECOND);
+}
+
+/*
+ * Called when timecmp is written to update the QEMU timer or immediately
+ * trigger timer interrupt if mtimecmp <= current timer value.
+ */
+static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
+{
+ uint64_t next;
+ uint64_t diff;
+
+ uint64_t rtc_r = cpu_riscv_read_rtc();
+
+ cpu->env.timecmp = value;
+ if (cpu->env.timecmp <= rtc_r) {
+ /* if we're setting an MTIMECMP value in the "past",
+ immediately raise the timer interrupt */
+ riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+ return;
+ }
+
+ /* otherwise, set up the future timer interrupt */
+ riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
+ diff = cpu->env.timecmp - rtc_r;
+ /* back to ns (note args switched in muldiv64) */
+ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ);
+ timer_mod(cpu->env.timer, next);
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ * Should raise the timer interrupt line
+ */
+static void sifive_clint_timer_cb(void *opaque)
+{
+ RISCVCPU *cpu = opaque;
+ riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+}
+
+/* CPU wants to read rtc or timecmp register */
+static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
+{
+ SiFiveCLINTState *clint = opaque;
+ if (addr >= clint->sip_base &&
+ addr < clint->sip_base + (clint->num_harts << 2)) {
+ size_t hartid = (addr - clint->sip_base) >> 2;
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ error_report("clint: invalid timecmp hartid: %zu", hartid);
+ } else if ((addr & 0x3) == 0) {
+ return (env->mip & MIP_MSIP) > 0;
+ } else {
+ error_report("clint: invalid read: %08x", (uint32_t)addr);
+ return 0;
+ }
+ } else if (addr >= clint->timecmp_base &&
+ addr < clint->timecmp_base + (clint->num_harts << 3)) {
+ size_t hartid = (addr - clint->timecmp_base) >> 3;
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ error_report("clint: invalid timecmp hartid: %zu", hartid);
+ } else if ((addr & 0x7) == 0) {
+ /* timecmp_lo */
+ uint64_t timecmp = env->timecmp;
+ return timecmp & 0xFFFFFFFF;
+ } else if ((addr & 0x7) == 4) {
+ /* timecmp_hi */
+ uint64_t timecmp = env->timecmp;
+ return (timecmp >> 32) & 0xFFFFFFFF;
+ } else {
+ error_report("clint: invalid read: %08x", (uint32_t)addr);
+ return 0;
+ }
+ } else if (addr == clint->time_base) {
+ /* time_lo */
+ return cpu_riscv_read_rtc() & 0xFFFFFFFF;
+ } else if (addr == clint->time_base + 4) {
+ /* time_hi */
+ return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF;
+ }
+
+ error_report("clint: invalid read: %08x", (uint32_t)addr);
+ return 0;
+}
+
+/* CPU wrote to rtc or timecmp register */
+static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ SiFiveCLINTState *clint = opaque;
+
+ if (addr >= clint->sip_base &&
+ addr < clint->sip_base + (clint->num_harts << 2)) {
+ size_t hartid = (addr - clint->sip_base) >> 2;
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ error_report("clint: invalid timecmp hartid: %zu", hartid);
+ } else if ((addr & 0x3) == 0) {
+ riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0);
+ } else {
+ error_report("clint: invalid sip write: %08x", (uint32_t)addr);
+ }
+ return;
+ } else if (addr >= clint->timecmp_base &&
+ addr < clint->timecmp_base + (clint->num_harts << 3)) {
+ size_t hartid = (addr - clint->timecmp_base) >> 3;
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ error_report("clint: invalid timecmp hartid: %zu", hartid);
+ } else if ((addr & 0x7) == 0) {
+ /* timecmp_lo */
+ uint64_t timecmp = env->timecmp;
+ sifive_clint_write_timecmp(RISCV_CPU(cpu),
+ timecmp << 32 | (value & 0xFFFFFFFF));
+ return;
+ } else if ((addr & 0x7) == 4) {
+ /* timecmp_hi */
+ uint64_t timecmp = env->timecmp;
+ sifive_clint_write_timecmp(RISCV_CPU(cpu),
+ value << 32 | (timecmp & 0xFFFFFFFF));
+ } else {
+ error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
+ }
+ return;
+ } else if (addr == clint->time_base) {
+ /* time_lo */
+ error_report("clint: time_lo write not implemented");
+ return;
+ } else if (addr == clint->time_base + 4) {
+ /* time_hi */
+ error_report("clint: time_hi write not implemented");
+ return;
+ }
+
+ error_report("clint: invalid write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_clint_ops = {
+ .read = sifive_clint_read,
+ .write = sifive_clint_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static Property sifive_clint_properties[] = {
+ DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
+ DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
+ DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
+ DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
+ DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_clint_realize(DeviceState *dev, Error **errp)
+{
+ SiFiveCLINTState *s = SIFIVE_CLINT(dev);
+ memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
+ TYPE_SIFIVE_CLINT, s->aperture_size);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void sifive_clint_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = sifive_clint_realize;
+ dc->props = sifive_clint_properties;
+}
+
+static const TypeInfo sifive_clint_info = {
+ .name = TYPE_SIFIVE_CLINT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveCLINTState),
+ .class_init = sifive_clint_class_init,
+};
+
+static void sifive_clint_register_types(void)
+{
+ type_register_static(&sifive_clint_info);
+}
+
+type_init(sifive_clint_register_types)
+
+
+/*
+ * Create CLINT device.
+ */
+DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
+ uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base)
+{
+ int i;
+ for (i = 0; i < num_harts; i++) {
+ CPUState *cpu = qemu_get_cpu(i);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ continue;
+ }
+ env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &sifive_clint_timer_cb, cpu);
+ env->timecmp = 0;
+ }
+
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT);
+ qdev_prop_set_uint32(dev, "num-harts", num_harts);
+ qdev_prop_set_uint32(dev, "sip-base", sip_base);
+ qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
+ qdev_prop_set_uint32(dev, "time-base", time_base);
+ qdev_prop_set_uint32(dev, "aperture-size", size);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
new file mode 100644
index 0000000000..19eca36ff4
--- /dev/null
+++ b/hw/riscv/sifive_e.c
@@ -0,0 +1,234 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom E SDK
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom E SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ * 3) PRCI (Power, Reset, Clock, Interrupt)
+ * 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM
+ * 5) Flash memory emulated as RAM
+ *
+ * The Mask ROM reset vector jumps to the flash payload at 0x2040_0000.
+ * The OTP ROM and Flash boot code will be emulated in a future version.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_e.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+} sifive_e_memmap[] = {
+ [SIFIVE_E_DEBUG] = { 0x0, 0x100 },
+ [SIFIVE_E_MROM] = { 0x1000, 0x2000 },
+ [SIFIVE_E_OTP] = { 0x20000, 0x2000 },
+ [SIFIVE_E_CLINT] = { 0x2000000, 0x10000 },
+ [SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 },
+ [SIFIVE_E_AON] = { 0x10000000, 0x8000 },
+ [SIFIVE_E_PRCI] = { 0x10008000, 0x8000 },
+ [SIFIVE_E_OTP_CTRL] = { 0x10010000, 0x1000 },
+ [SIFIVE_E_GPIO0] = { 0x10012000, 0x1000 },
+ [SIFIVE_E_UART0] = { 0x10013000, 0x1000 },
+ [SIFIVE_E_QSPI0] = { 0x10014000, 0x1000 },
+ [SIFIVE_E_PWM0] = { 0x10015000, 0x1000 },
+ [SIFIVE_E_UART1] = { 0x10023000, 0x1000 },
+ [SIFIVE_E_QSPI1] = { 0x10024000, 0x1000 },
+ [SIFIVE_E_PWM1] = { 0x10025000, 0x1000 },
+ [SIFIVE_E_QSPI2] = { 0x10034000, 0x1000 },
+ [SIFIVE_E_PWM2] = { 0x10035000, 0x1000 },
+ [SIFIVE_E_XIP] = { 0x20000000, 0x20000000 },
+ [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 }
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+ int i;
+ for (i = 0; i < (len >> 2); i++) {
+ stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+ }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+ return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+ uint64_t kernel_entry, kernel_high;
+
+ if (load_elf(kernel_filename, identity_translate, NULL,
+ &kernel_entry, NULL, &kernel_high,
+ 0, ELF_MACHINE, 1, 0) < 0) {
+ error_report("qemu: could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ return kernel_entry;
+}
+
+static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
+ uintptr_t offset, uintptr_t length)
+{
+ MemoryRegion *mock_mmio = g_new(MemoryRegion, 1);
+ memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal);
+ memory_region_add_subregion(parent, offset, mock_mmio);
+}
+
+static void riscv_sifive_e_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = sifive_e_memmap;
+
+ SiFiveEState *s = g_new0(SiFiveEState, 1);
+ MemoryRegion *sys_mem = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+ MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
+
+ /* Initialize SOC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ &error_abort);
+
+ /* Data Tightly Integrated Memory */
+ memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram",
+ memmap[SIFIVE_E_DTIM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem,
+ memmap[SIFIVE_E_DTIM].base, main_mem);
+
+ /* Mask ROM */
+ memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom",
+ memmap[SIFIVE_E_MROM].size, &error_fatal);
+ memory_region_add_subregion(sys_mem,
+ memmap[SIFIVE_E_MROM].base, mask_rom);
+
+ /* MMIO */
+ s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base,
+ (char *)SIFIVE_E_PLIC_HART_CONFIG,
+ SIFIVE_E_PLIC_NUM_SOURCES,
+ SIFIVE_E_PLIC_NUM_PRIORITIES,
+ SIFIVE_E_PLIC_PRIORITY_BASE,
+ SIFIVE_E_PLIC_PENDING_BASE,
+ SIFIVE_E_PLIC_ENABLE_BASE,
+ SIFIVE_E_PLIC_ENABLE_STRIDE,
+ SIFIVE_E_PLIC_CONTEXT_BASE,
+ SIFIVE_E_PLIC_CONTEXT_STRIDE,
+ memmap[SIFIVE_E_PLIC].size);
+ sifive_clint_create(memmap[SIFIVE_E_CLINT].base,
+ memmap[SIFIVE_E_CLINT].size, smp_cpus,
+ SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon",
+ memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size);
+ sifive_prci_create(memmap[SIFIVE_E_PRCI].base);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0",
+ memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size);
+ sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base,
+ serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0",
+ memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0",
+ memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size);
+ /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
+ serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1",
+ memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1",
+ memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2",
+ memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size);
+ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2",
+ memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
+
+ /* Flash memory */
+ memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip",
+ memmap[SIFIVE_E_XIP].size, &error_fatal);
+ memory_region_set_readonly(xip_mem, true);
+ memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem);
+
+ /* Mask ROM reset vector */
+ uint32_t reset_vec[2] = {
+ 0x204002b7, /* 0x1000: lui t0,0x20400 */
+ 0x00028067, /* 0x1004: jr t0 */
+ };
+
+ /* copy in the reset vector */
+ copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec));
+ memory_region_set_readonly(mask_rom, true);
+
+ if (machine->kernel_filename) {
+ load_kernel(machine->kernel_filename);
+ }
+}
+
+static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_sifive_e_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_sifive_e_sysbus_device_init;
+}
+
+static const TypeInfo riscv_sifive_e_device = {
+ .name = TYPE_SIFIVE_E,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveEState),
+ .class_init = riscv_sifive_e_class_init,
+};
+
+static void riscv_sifive_e_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Board compatible with SiFive E SDK";
+ mc->init = riscv_sifive_e_init;
+ mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
+
+static void riscv_sifive_e_register_types(void)
+{
+ type_register_static(&riscv_sifive_e_device);
+}
+
+type_init(riscv_sifive_e_register_types);
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
new file mode 100644
index 0000000000..874de2ebaf
--- /dev/null
+++ b/hw/riscv/sifive_plic.c
@@ -0,0 +1,505 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a parameterizable interrupt controller based on SiFive's PLIC.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_plic.h"
+
+#define RISCV_DEBUG_PLIC 0
+
+static PLICMode char_to_mode(char c)
+{
+ switch (c) {
+ case 'U': return PLICMode_U;
+ case 'S': return PLICMode_S;
+ case 'H': return PLICMode_H;
+ case 'M': return PLICMode_M;
+ default:
+ error_report("plic: invalid mode '%c'", c);
+ exit(1);
+ }
+}
+
+static char mode_to_char(PLICMode m)
+{
+ switch (m) {
+ case PLICMode_U: return 'U';
+ case PLICMode_S: return 'S';
+ case PLICMode_H: return 'H';
+ case PLICMode_M: return 'M';
+ default: return '?';
+ }
+}
+
+static void sifive_plic_print_state(SiFivePLICState *plic)
+{
+ int i;
+ int addrid;
+
+ /* pending */
+ qemu_log("pending : ");
+ for (i = plic->bitfield_words - 1; i >= 0; i--) {
+ qemu_log("%08x", plic->pending[i]);
+ }
+ qemu_log("\n");
+
+ /* pending */
+ qemu_log("claimed : ");
+ for (i = plic->bitfield_words - 1; i >= 0; i--) {
+ qemu_log("%08x", plic->claimed[i]);
+ }
+ qemu_log("\n");
+
+ for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+ qemu_log("hart%d-%c enable: ",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode));
+ for (i = plic->bitfield_words - 1; i >= 0; i--) {
+ qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
+ }
+ qemu_log("\n");
+ }
+}
+
+static
+void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
+{
+ qemu_mutex_lock(&plic->lock);
+ uint32_t word = irq >> 5;
+ if (pending) {
+ plic->pending[word] |= (1 << (irq & 31));
+ } else {
+ plic->pending[word] &= ~(1 << (irq & 31));
+ }
+ qemu_mutex_unlock(&plic->lock);
+}
+
+static
+void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
+{
+ qemu_mutex_lock(&plic->lock);
+ uint32_t word = irq >> 5;
+ if (claimed) {
+ plic->claimed[word] |= (1 << (irq & 31));
+ } else {
+ plic->claimed[word] &= ~(1 << (irq & 31));
+ }
+ qemu_mutex_unlock(&plic->lock);
+}
+
+static
+int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
+{
+ int i, j, count = 0;
+ for (i = 0; i < plic->bitfield_words; i++) {
+ uint32_t pending_enabled_not_claimed =
+ (plic->pending[i] & ~plic->claimed[i]) &
+ plic->enable[addrid * plic->bitfield_words + i];
+ if (!pending_enabled_not_claimed) {
+ continue;
+ }
+ for (j = 0; j < 32; j++) {
+ int irq = (i << 5) + j;
+ uint32_t prio = plic->source_priority[irq];
+ int enabled = pending_enabled_not_claimed & (1 << j);
+ if (enabled && prio > plic->target_priority[addrid]) {
+ count++;
+ }
+ }
+ }
+ return count;
+}
+
+static void sifive_plic_update(SiFivePLICState *plic)
+{
+ int addrid;
+
+ /* raise irq on harts where this irq is enabled */
+ for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+ uint32_t hartid = plic->addr_config[addrid].hartid;
+ PLICMode mode = plic->addr_config[addrid].mode;
+ CPUState *cpu = qemu_get_cpu(hartid);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ continue;
+ }
+ int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
+ switch (mode) {
+ case PLICMode_M:
+ riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
+ break;
+ case PLICMode_S:
+ riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (RISCV_DEBUG_PLIC) {
+ sifive_plic_print_state(plic);
+ }
+}
+
+void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
+{
+ sifive_plic_set_pending(plic, irq, true);
+ sifive_plic_update(plic);
+}
+
+void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
+{
+ sifive_plic_set_pending(plic, irq, false);
+ sifive_plic_update(plic);
+}
+
+static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
+{
+ int i, j;
+ for (i = 0; i < plic->bitfield_words; i++) {
+ uint32_t pending_enabled_not_claimed =
+ (plic->pending[i] & ~plic->claimed[i]) &
+ plic->enable[addrid * plic->bitfield_words + i];
+ if (!pending_enabled_not_claimed) {
+ continue;
+ }
+ for (j = 0; j < 32; j++) {
+ int irq = (i << 5) + j;
+ uint32_t prio = plic->source_priority[irq];
+ int enabled = pending_enabled_not_claimed & (1 << j);
+ if (enabled && prio > plic->target_priority[addrid]) {
+ sifive_plic_set_pending(plic, irq, false);
+ sifive_plic_set_claimed(plic, irq, true);
+ return irq;
+ }
+ }
+ }
+ return 0;
+}
+
+static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
+{
+ SiFivePLICState *plic = opaque;
+
+ /* writes must be 4 byte words */
+ if ((addr & 0x3) != 0) {
+ goto err;
+ }
+
+ if (addr >= plic->priority_base && /* 4 bytes per source */
+ addr < plic->priority_base + (plic->num_sources << 2))
+ {
+ uint32_t irq = (addr - plic->priority_base) >> 2;
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: read priority: irq=%d priority=%d\n",
+ irq, plic->source_priority[irq]);
+ }
+ return plic->source_priority[irq];
+ } else if (addr >= plic->pending_base && /* 1 bit per source */
+ addr < plic->pending_base + (plic->num_sources >> 3))
+ {
+ uint32_t word = (addr - plic->priority_base) >> 2;
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: read pending: word=%d value=%d\n",
+ word, plic->pending[word]);
+ }
+ return plic->pending[word];
+ } else if (addr >= plic->enable_base && /* 1 bit per source */
+ addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+ {
+ uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+ uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+ if (wordid < plic->bitfield_words) {
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode), wordid,
+ plic->enable[addrid * plic->bitfield_words + wordid]);
+ }
+ return plic->enable[addrid * plic->bitfield_words + wordid];
+ }
+ } else if (addr >= plic->context_base && /* 1 bit per source */
+ addr < plic->context_base + plic->num_addrs * plic->context_stride)
+ {
+ uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+ uint32_t contextid = (addr & (plic->context_stride - 1));
+ if (contextid == 0) {
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: read priority: hart%d-%c priority=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode),
+ plic->target_priority[addrid]);
+ }
+ return plic->target_priority[addrid];
+ } else if (contextid == 4) {
+ uint32_t value = sifive_plic_claim(plic, addrid);
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: read claim: hart%d-%c irq=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode),
+ value);
+ sifive_plic_print_state(plic);
+ }
+ return value;
+ }
+ }
+
+err:
+ error_report("plic: invalid register read: %08x", (uint32_t)addr);
+ return 0;
+}
+
+static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ SiFivePLICState *plic = opaque;
+
+ /* writes must be 4 byte words */
+ if ((addr & 0x3) != 0) {
+ goto err;
+ }
+
+ if (addr >= plic->priority_base && /* 4 bytes per source */
+ addr < plic->priority_base + (plic->num_sources << 2))
+ {
+ uint32_t irq = (addr - plic->priority_base) >> 2;
+ plic->source_priority[irq] = value & 7;
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: write priority: irq=%d priority=%d\n",
+ irq, plic->source_priority[irq]);
+ }
+ return;
+ } else if (addr >= plic->pending_base && /* 1 bit per source */
+ addr < plic->pending_base + (plic->num_sources >> 3))
+ {
+ error_report("plic: invalid pending write: %08x", (uint32_t)addr);
+ return;
+ } else if (addr >= plic->enable_base && /* 1 bit per source */
+ addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+ {
+ uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+ uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+ if (wordid < plic->bitfield_words) {
+ plic->enable[addrid * plic->bitfield_words + wordid] = value;
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode), wordid,
+ plic->enable[addrid * plic->bitfield_words + wordid]);
+ }
+ return;
+ }
+ } else if (addr >= plic->context_base && /* 4 bytes per reg */
+ addr < plic->context_base + plic->num_addrs * plic->context_stride)
+ {
+ uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+ uint32_t contextid = (addr & (plic->context_stride - 1));
+ if (contextid == 0) {
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: write priority: hart%d-%c priority=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode),
+ plic->target_priority[addrid]);
+ }
+ if (value <= plic->num_priorities) {
+ plic->target_priority[addrid] = value;
+ sifive_plic_update(plic);
+ }
+ return;
+ } else if (contextid == 4) {
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("plic: write claim: hart%d-%c irq=%x\n",
+ plic->addr_config[addrid].hartid,
+ mode_to_char(plic->addr_config[addrid].mode),
+ (uint32_t)value);
+ }
+ if (value < plic->num_sources) {
+ sifive_plic_set_claimed(plic, value, false);
+ sifive_plic_update(plic);
+ }
+ return;
+ }
+ }
+
+err:
+ error_report("plic: invalid register write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_plic_ops = {
+ .read = sifive_plic_read,
+ .write = sifive_plic_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static Property sifive_plic_properties[] = {
+ DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
+ DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
+ DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
+ DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
+ DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
+ DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
+ DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
+ DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
+ DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
+ DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+/*
+ * parse PLIC hart/mode address offset config
+ *
+ * "M" 1 hart with M mode
+ * "MS,MS" 2 harts, 0-1 with M and S mode
+ * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode
+ */
+static void parse_hart_config(SiFivePLICState *plic)
+{
+ int addrid, hartid, modes;
+ const char *p;
+ char c;
+
+ /* count and validate hart/mode combinations */
+ addrid = 0, hartid = 0, modes = 0;
+ p = plic->hart_config;
+ while ((c = *p++)) {
+ if (c == ',') {
+ addrid += __builtin_popcount(modes);
+ modes = 0;
+ hartid++;
+ } else {
+ int m = 1 << char_to_mode(c);
+ if (modes == (modes | m)) {
+ error_report("plic: duplicate mode '%c' in config: %s",
+ c, plic->hart_config);
+ exit(1);
+ }
+ modes |= m;
+ }
+ }
+ if (modes) {
+ addrid += __builtin_popcount(modes);
+ }
+ hartid++;
+
+ /* store hart/mode combinations */
+ plic->num_addrs = addrid;
+ plic->addr_config = g_new(PLICAddr, plic->num_addrs);
+ addrid = 0, hartid = 0;
+ p = plic->hart_config;
+ while ((c = *p++)) {
+ if (c == ',') {
+ hartid++;
+ } else {
+ plic->addr_config[addrid].addrid = addrid;
+ plic->addr_config[addrid].hartid = hartid;
+ plic->addr_config[addrid].mode = char_to_mode(c);
+ addrid++;
+ }
+ }
+}
+
+static void sifive_plic_irq_request(void *opaque, int irq, int level)
+{
+ SiFivePLICState *plic = opaque;
+ if (RISCV_DEBUG_PLIC) {
+ qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
+ }
+ sifive_plic_set_pending(plic, irq, level > 0);
+ sifive_plic_update(plic);
+}
+
+static void sifive_plic_realize(DeviceState *dev, Error **errp)
+{
+ SiFivePLICState *plic = SIFIVE_PLIC(dev);
+ int i;
+
+ memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic,
+ TYPE_SIFIVE_PLIC, plic->aperture_size);
+ parse_hart_config(plic);
+ qemu_mutex_init(&plic->lock);
+ plic->bitfield_words = (plic->num_sources + 31) >> 5;
+ plic->source_priority = g_new0(uint32_t, plic->num_sources);
+ plic->target_priority = g_new(uint32_t, plic->num_addrs);
+ plic->pending = g_new0(uint32_t, plic->bitfield_words);
+ plic->claimed = g_new0(uint32_t, plic->bitfield_words);
+ plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
+ plic->irqs = g_new0(qemu_irq, plic->num_sources + 1);
+ for (i = 0; i <= plic->num_sources; i++) {
+ plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i);
+ }
+}
+
+static void sifive_plic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = sifive_plic_properties;
+ dc->realize = sifive_plic_realize;
+}
+
+static const TypeInfo sifive_plic_info = {
+ .name = TYPE_SIFIVE_PLIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePLICState),
+ .class_init = sifive_plic_class_init,
+};
+
+static void sifive_plic_register_types(void)
+{
+ type_register_static(&sifive_plic_info);
+}
+
+type_init(sifive_plic_register_types)
+
+/*
+ * Create PLIC device.
+ */
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+ uint32_t num_sources, uint32_t num_priorities,
+ uint32_t priority_base, uint32_t pending_base,
+ uint32_t enable_base, uint32_t enable_stride,
+ uint32_t context_base, uint32_t context_stride,
+ uint32_t aperture_size)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
+ assert(enable_stride == (enable_stride & -enable_stride));
+ assert(context_stride == (context_stride & -context_stride));
+ qdev_prop_set_string(dev, "hart-config", hart_config);
+ qdev_prop_set_uint32(dev, "num-sources", num_sources);
+ qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
+ qdev_prop_set_uint32(dev, "priority-base", priority_base);
+ qdev_prop_set_uint32(dev, "pending-base", pending_base);
+ qdev_prop_set_uint32(dev, "enable-base", enable_base);
+ qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
+ qdev_prop_set_uint32(dev, "context-base", context_base);
+ qdev_prop_set_uint32(dev, "context-stride", context_stride);
+ qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644
index 0000000000..0910ea32c1
--- /dev/null
+++ b/hw/riscv/sifive_prci.c
@@ -0,0 +1,89 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Simple model of the PRCI to emulate register reads made by the SDK BSP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_prci.h"
+
+/* currently implements enough to mock freedom-e-sdk BSP clock programming */
+
+static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ if (addr == 0 /* PRCI_HFROSCCFG */) {
+ return 1 << 31; /* ROSC_RDY */
+ }
+ if (addr == 8 /* PRCI_PLLCFG */) {
+ return 1 << 31; /* PLL_LOCK */
+ }
+ hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
+ return 0;
+}
+
+static void sifive_prci_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ /* discard writes */
+}
+
+static const MemoryRegionOps sifive_prci_ops = {
+ .read = sifive_prci_read,
+ .write = sifive_prci_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_prci_init(Object *obj)
+{
+ SiFivePRCIState *s = SIFIVE_PRCI(obj);
+
+ memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s,
+ TYPE_SIFIVE_PRCI, 0x8000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_prci_info = {
+ .name = TYPE_SIFIVE_PRCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFivePRCIState),
+ .instance_init = sifive_prci_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+ type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
new file mode 100644
index 0000000000..8abd2cd525
--- /dev/null
+++ b/hw/riscv/sifive_test.c
@@ -0,0 +1,93 @@
+/*
+ * QEMU SiFive Test Finisher
+ *
+ * Copyright (c) 2018 SiFive, Inc.
+ *
+ * Test finisher memory mapped device used to exit simulation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_test.h"
+
+static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ return 0;
+}
+
+static void sifive_test_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ if (addr == 0) {
+ int status = val64 & 0xffff;
+ int code = (val64 >> 16) & 0xffff;
+ switch (status) {
+ case FINISHER_FAIL:
+ exit(code);
+ case FINISHER_PASS:
+ exit(0);
+ default:
+ break;
+ }
+ }
+ hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
+ __func__, (int)addr, val64);
+}
+
+static const MemoryRegionOps sifive_test_ops = {
+ .read = sifive_test_read,
+ .write = sifive_test_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_test_init(Object *obj)
+{
+ SiFiveTestState *s = SIFIVE_TEST(obj);
+
+ memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
+ TYPE_SIFIVE_TEST, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_test_info = {
+ .name = TYPE_SIFIVE_TEST,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveTestState),
+ .instance_init = sifive_test_init,
+};
+
+static void sifive_test_register_types(void)
+{
+ type_register_static(&sifive_test_info);
+}
+
+type_init(sifive_test_register_types)
+
+
+/*
+ * Create Test device.
+ */
+DeviceState *sifive_test_create(hwaddr addr)
+{
+ DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ return dev;
+}
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
new file mode 100644
index 0000000000..1c2deefa6c
--- /dev/null
+++ b/hw/riscv/sifive_u.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom U SDK
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom U SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This board currently uses a hardcoded devicetree that indicates one hart.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_u.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+} sifive_u_memmap[] = {
+ [SIFIVE_U_DEBUG] = { 0x0, 0x100 },
+ [SIFIVE_U_MROM] = { 0x1000, 0x2000 },
+ [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 },
+ [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 },
+ [SIFIVE_U_UART0] = { 0x10013000, 0x1000 },
+ [SIFIVE_U_UART1] = { 0x10023000, 0x1000 },
+ [SIFIVE_U_DRAM] = { 0x80000000, 0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+ int i;
+ for (i = 0; i < (len >> 2); i++) {
+ stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+ }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+ return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+ uint64_t kernel_entry, kernel_high;
+
+ if (load_elf(kernel_filename, identity_translate, NULL,
+ &kernel_entry, NULL, &kernel_high,
+ 0, ELF_MACHINE, 1, 0) < 0) {
+ error_report("qemu: could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ return kernel_entry;
+}
+
+static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
+ uint64_t mem_size, const char *cmdline)
+{
+ void *fdt;
+ int cpu;
+ uint32_t *cells;
+ char *nodename;
+ uint32_t plic_phandle;
+
+ fdt = s->fdt = create_device_tree(&s->fdt_size);
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+ nodename = g_strdup_printf("/memory@%lx",
+ (long)memmap[SIFIVE_U_DRAM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base,
+ mem_size >> 32, mem_size);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+ g_free(nodename);
+
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+ for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+ nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+ char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+ qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+ qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+ qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+ qemu_fdt_add_subnode(fdt, intc);
+ qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+ qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+ qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+ qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+ g_free(isa);
+ g_free(intc);
+ g_free(nodename);
+ }
+
+ cells = g_new0(uint32_t, s->soc.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ nodename =
+ g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+ cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+ g_free(nodename);
+ }
+ nodename = g_strdup_printf("/soc/clint@%lx",
+ (long)memmap[SIFIVE_U_CLINT].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_CLINT].base,
+ 0x0, memmap[SIFIVE_U_CLINT].size);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ g_free(cells);
+ g_free(nodename);
+
+ cells = g_new0(uint32_t, s->soc.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ nodename =
+ g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+ cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+ g_free(nodename);
+ }
+ nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+ (long)memmap[SIFIVE_U_PLIC].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
+ qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_PLIC].base,
+ 0x0, memmap[SIFIVE_U_PLIC].size);
+ qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4);
+ qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2);
+ qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2);
+ plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ g_free(cells);
+ g_free(nodename);
+
+ nodename = g_strdup_printf("/uart@%lx",
+ (long)memmap[SIFIVE_U_UART0].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SIFIVE_U_UART0].base,
+ 0x0, memmap[SIFIVE_U_UART0].size);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+ qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ g_free(nodename);
+}
+
+static void riscv_sifive_u_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = sifive_u_memmap;
+
+ SiFiveUState *s = g_new0(SiFiveUState, 1);
+ MemoryRegion *sys_memory = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+ /* Initialize SOC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ &error_abort);
+
+ /* register RAM */
+ memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base,
+ main_mem);
+
+ /* create device tree */
+ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+ /* boot rom */
+ memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom",
+ memmap[SIFIVE_U_MROM].base, &error_fatal);
+ memory_region_set_readonly(boot_rom, true);
+ memory_region_add_subregion(sys_memory, 0x0, boot_rom);
+
+ if (machine->kernel_filename) {
+ load_kernel(machine->kernel_filename);
+ }
+
+ /* reset vector */
+ uint32_t reset_vec[8] = {
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
+ 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
+ 0xf1402573, /* csrr a0, mhartid */
+#if defined(TARGET_RISCV32)
+ 0x0182a283, /* lw t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+ 0x0182b283, /* ld t0, 24(t0) */
+#endif
+ 0x00028067, /* jr t0 */
+ 0x00000000,
+ memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */
+ 0x00000000,
+ /* dtb: */
+ };
+
+ /* copy in the reset vector */
+ copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec));
+
+ /* copy in the device tree */
+ qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+ cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base +
+ sizeof(reset_vec), s->fdt, s->fdt_size);
+
+ /* MMIO */
+ s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
+ (char *)SIFIVE_U_PLIC_HART_CONFIG,
+ SIFIVE_U_PLIC_NUM_SOURCES,
+ SIFIVE_U_PLIC_NUM_PRIORITIES,
+ SIFIVE_U_PLIC_PRIORITY_BASE,
+ SIFIVE_U_PLIC_PENDING_BASE,
+ SIFIVE_U_PLIC_ENABLE_BASE,
+ SIFIVE_U_PLIC_ENABLE_STRIDE,
+ SIFIVE_U_PLIC_CONTEXT_BASE,
+ SIFIVE_U_PLIC_CONTEXT_STRIDE,
+ memmap[SIFIVE_U_PLIC].size);
+ sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base,
+ serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]);
+ /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base,
+ serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */
+ sifive_clint_create(memmap[SIFIVE_U_CLINT].base,
+ memmap[SIFIVE_U_CLINT].size, smp_cpus,
+ SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_sifive_u_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_sifive_u_sysbus_device_init;
+}
+
+static const TypeInfo riscv_sifive_u_device = {
+ .name = TYPE_SIFIVE_U,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveUState),
+ .class_init = riscv_sifive_u_class_init,
+};
+
+static void riscv_sifive_u_register_types(void)
+{
+ type_register_static(&riscv_sifive_u_device);
+}
+
+type_init(riscv_sifive_u_register_types);
+
+static void riscv_sifive_u_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Board compatible with SiFive U SDK";
+ mc->init = riscv_sifive_u_init;
+ mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644
index 0000000000..b0c3798cf2
--- /dev/null
+++ b/hw/riscv/sifive_uart.c
@@ -0,0 +1,176 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_uart.h"
+
+/*
+ * Not yet implemented:
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
+ * SIFIVE_UART_IE_TXWM interrupts
+ * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
+ * Rx FIFO watermark interrupt trigger threshold
+ * Tx FIFO watermark interrupt trigger threshold.
+ */
+
+static void update_irq(SiFiveUARTState *s)
+{
+ int cond = 0;
+ if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
+ cond = 1;
+ }
+ if (cond) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveUARTState *s = opaque;
+ unsigned char r;
+ switch (addr) {
+ case SIFIVE_UART_RXFIFO:
+ if (s->rx_fifo_len) {
+ r = s->rx_fifo[0];
+ memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+ s->rx_fifo_len--;
+ qemu_chr_fe_accept_input(&s->chr);
+ update_irq(s);
+ return r;
+ }
+ return 0x80000000;
+
+ case SIFIVE_UART_TXFIFO:
+ return 0; /* Should check tx fifo */
+ case SIFIVE_UART_IE:
+ return s->ie;
+ case SIFIVE_UART_IP:
+ return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ case SIFIVE_UART_TXCTRL:
+ return s->txctrl;
+ case SIFIVE_UART_RXCTRL:
+ return s->rxctrl;
+ case SIFIVE_UART_DIV:
+ return s->div;
+ }
+
+ hw_error("%s: bad read: addr=0x%x\n",
+ __func__, (int)addr);
+ return 0;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveUARTState *s = opaque;
+ uint32_t value = val64;
+ unsigned char ch = value;
+
+ switch (addr) {
+ case SIFIVE_UART_TXFIFO:
+ qemu_chr_fe_write(&s->chr, &ch, 1);
+ return;
+ case SIFIVE_UART_IE:
+ s->ie = val64;
+ update_irq(s);
+ return;
+ case SIFIVE_UART_TXCTRL:
+ s->txctrl = val64;
+ return;
+ case SIFIVE_UART_RXCTRL:
+ s->rxctrl = val64;
+ return;
+ case SIFIVE_UART_DIV:
+ s->div = val64;
+ return;
+ }
+ hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)value);
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ SiFiveUARTState *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+ update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+ SiFiveUARTState *s = opaque;
+
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+
+ return 0;
+}
+
+/*
+ * Create UART device.
+ */
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+ Chardev *chr, qemu_irq irq)
+{
+ SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+ s->irq = irq;
+ qemu_chr_fe_init(&s->chr, chr, &error_abort);
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+ uart_be_change, s, NULL, true);
+ memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+ TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+ memory_region_add_subregion(address_space, base, &s->mmio);
+ return s;
+}
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
new file mode 100644
index 0000000000..2d1f114d40
--- /dev/null
+++ b/hw/riscv/spike.c
@@ -0,0 +1,376 @@
+/*
+ * QEMU RISC-V Spike Board
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides a RISC-V Board with the following devices:
+ *
+ * 0) HTIF Console and Poweroff
+ * 1) CLINT (Timer and IPI)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/spike.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+} spike_memmap[] = {
+ [SPIKE_MROM] = { 0x1000, 0x2000 },
+ [SPIKE_CLINT] = { 0x2000000, 0x10000 },
+ [SPIKE_DRAM] = { 0x80000000, 0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+ int i;
+ for (i = 0; i < (len >> 2); i++) {
+ stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+ }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+ return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+ uint64_t kernel_entry, kernel_high;
+
+ if (load_elf_ram_sym(kernel_filename, identity_translate, NULL,
+ &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0,
+ NULL, true, htif_symbol_callback) < 0) {
+ error_report("qemu: could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ return kernel_entry;
+}
+
+static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
+ uint64_t mem_size, const char *cmdline)
+{
+ void *fdt;
+ int cpu;
+ uint32_t *cells;
+ char *nodename;
+
+ fdt = s->fdt = create_device_tree(&s->fdt_size);
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(fdt, "/htif");
+ qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+ nodename = g_strdup_printf("/memory@%lx",
+ (long)memmap[SPIKE_DRAM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
+ mem_size >> 32, mem_size);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+ g_free(nodename);
+
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+ for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+ nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+ char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+ qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+ qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+ qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+ qemu_fdt_add_subnode(fdt, intc);
+ qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+ qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+ qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+ qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+ g_free(isa);
+ g_free(intc);
+ g_free(nodename);
+ }
+
+ cells = g_new0(uint32_t, s->soc.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ nodename =
+ g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+ cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+ g_free(nodename);
+ }
+ nodename = g_strdup_printf("/soc/clint@%lx",
+ (long)memmap[SPIKE_CLINT].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[SPIKE_CLINT].base,
+ 0x0, memmap[SPIKE_CLINT].size);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ g_free(cells);
+ g_free(nodename);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
+
+static void spike_v1_10_0_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = spike_memmap;
+
+ SpikeState *s = g_new0(SpikeState, 1);
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+ /* Initialize SOC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ &error_abort);
+
+ /* register system main memory (actual RAM) */
+ memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
+ main_mem);
+
+ /* create device tree */
+ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+ /* boot rom */
+ memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
+ s->fdt_size + 0x2000, &error_fatal);
+ memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+ if (machine->kernel_filename) {
+ load_kernel(machine->kernel_filename);
+ }
+
+ /* reset vector */
+ uint32_t reset_vec[8] = {
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
+ 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
+ 0xf1402573, /* csrr a0, mhartid */
+#if defined(TARGET_RISCV32)
+ 0x0182a283, /* lw t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+ 0x0182b283, /* ld t0, 24(t0) */
+#endif
+ 0x00028067, /* jr t0 */
+ 0x00000000,
+ memmap[SPIKE_DRAM].base, /* start: .dword DRAM_BASE */
+ 0x00000000,
+ /* dtb: */
+ };
+
+ /* copy in the reset vector */
+ copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
+
+ /* copy in the device tree */
+ qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+ cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
+ s->fdt, s->fdt_size);
+
+ /* initialize HTIF using symbols found in load_kernel */
+ htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
+
+ /* Core Local Interruptor (timer and IPI) */
+ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
+ smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static void spike_v1_09_1_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = spike_memmap;
+
+ SpikeState *s = g_new0(SpikeState, 1);
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+ /* Initialize SOC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ &error_abort);
+
+ /* register system main memory (actual RAM) */
+ memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
+ main_mem);
+
+ /* boot rom */
+ memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom",
+ 0x40000, &error_fatal);
+ memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+ if (machine->kernel_filename) {
+ load_kernel(machine->kernel_filename);
+ }
+
+ /* reset vector */
+ uint32_t reset_vec[8] = {
+ 0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */
+ 0x00028067, /* jump to DRAM_BASE */
+ 0x00000000, /* reserved */
+ memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */
+ 0, 0, 0, 0 /* trap vector */
+ };
+
+ /* part one of config string - before memory size specified */
+ const char *config_string_tmpl =
+ "platform {\n"
+ " vendor ucb;\n"
+ " arch spike;\n"
+ "};\n"
+ "rtc {\n"
+ " addr 0x%" PRIx64 "x;\n"
+ "};\n"
+ "ram {\n"
+ " 0 {\n"
+ " addr 0x%" PRIx64 "x;\n"
+ " size 0x%" PRIx64 "x;\n"
+ " };\n"
+ "};\n"
+ "core {\n"
+ " 0" " {\n"
+ " " "0 {\n"
+ " isa %s;\n"
+ " timecmp 0x%" PRIx64 "x;\n"
+ " ipi 0x%" PRIx64 "x;\n"
+ " };\n"
+ " };\n"
+ "};\n";
+
+ /* build config string with supplied memory size */
+ char *isa = riscv_isa_string(&s->soc.harts[0]);
+ size_t config_string_size = strlen(config_string_tmpl) + 48;
+ char *config_string = malloc(config_string_size);
+ snprintf(config_string, config_string_size, config_string_tmpl,
+ (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
+ (uint64_t)memmap[SPIKE_DRAM].base,
+ (uint64_t)ram_size, isa,
+ (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
+ (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
+ g_free(isa);
+ size_t config_string_len = strlen(config_string);
+
+ /* copy in the reset vector */
+ copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
+
+ /* copy in the config string */
+ cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
+ config_string, config_string_len);
+
+ /* initialize HTIF using symbols found in load_kernel */
+ htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]);
+
+ /* Core Local Interruptor (timer and IPI) */
+ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size,
+ smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+static const TypeInfo spike_v_1_09_1_device = {
+ .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpikeState),
+};
+
+static const TypeInfo spike_v_1_10_0_device = {
+ .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SpikeState),
+};
+
+static void spike_v1_09_1_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)";
+ mc->init = spike_v1_09_1_board_init;
+ mc->max_cpus = 1;
+}
+
+static void spike_v1_10_0_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
+ mc->init = spike_v1_10_0_board_init;
+ mc->max_cpus = 1;
+ mc->is_default = 1;
+}
+
+DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init)
+DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init)
+
+static void riscv_spike_board_register_types(void)
+{
+ type_register_static(&spike_v_1_09_1_device);
+ type_register_static(&spike_v_1_10_0_device);
+}
+
+type_init(riscv_spike_board_register_types);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
new file mode 100644
index 0000000000..e2c214e86a
--- /dev/null
+++ b/hw/riscv/virt.c
@@ -0,0 +1,420 @@
+/*
+ * QEMU RISC-V VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * RISC-V machine with 16550a UART and VirtIO MMIO
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_test.h"
+#include "hw/riscv/virt.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+ hwaddr base;
+ hwaddr size;
+} virt_memmap[] = {
+ [VIRT_DEBUG] = { 0x0, 0x100 },
+ [VIRT_MROM] = { 0x1000, 0x2000 },
+ [VIRT_TEST] = { 0x4000, 0x1000 },
+ [VIRT_CLINT] = { 0x2000000, 0x10000 },
+ [VIRT_PLIC] = { 0xc000000, 0x4000000 },
+ [VIRT_UART0] = { 0x10000000, 0x100 },
+ [VIRT_VIRTIO] = { 0x10001000, 0x1000 },
+ [VIRT_DRAM] = { 0x80000000, 0x0 },
+};
+
+static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len)
+{
+ int i;
+ for (i = 0; i < (len >> 2); i++) {
+ stl_phys(&address_space_memory, pa + (i << 2), rom[i]);
+ }
+}
+
+static uint64_t identity_translate(void *opaque, uint64_t addr)
+{
+ return addr;
+}
+
+static uint64_t load_kernel(const char *kernel_filename)
+{
+ uint64_t kernel_entry, kernel_high;
+
+ if (load_elf(kernel_filename, identity_translate, NULL,
+ &kernel_entry, NULL, &kernel_high,
+ 0, ELF_MACHINE, 1, 0) < 0) {
+ error_report("qemu: could not load kernel '%s'", kernel_filename);
+ exit(1);
+ }
+ return kernel_entry;
+}
+
+static hwaddr load_initrd(const char *filename, uint64_t mem_size,
+ uint64_t kernel_entry, hwaddr *start)
+{
+ int size;
+
+ /* We want to put the initrd far enough into RAM that when the
+ * kernel is uncompressed it will not clobber the initrd. However
+ * on boards without much RAM we must ensure that we still leave
+ * enough room for a decent sized initrd, and on boards with large
+ * amounts of RAM we must avoid the initrd being so far up in RAM
+ * that it is outside lowmem and inaccessible to the kernel.
+ * So for boards with less than 256MB of RAM we put the initrd
+ * halfway into RAM, and for boards with 256MB of RAM or more we put
+ * the initrd at 128MB.
+ */
+ *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024);
+
+ size = load_ramdisk(filename, *start, mem_size - *start);
+ if (size == -1) {
+ size = load_image_targphys(filename, *start, mem_size - *start);
+ if (size == -1) {
+ error_report("qemu: could not load ramdisk '%s'", filename);
+ exit(1);
+ }
+ }
+ return *start + size;
+}
+
+static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
+ uint64_t mem_size, const char *cmdline)
+{
+ void *fdt;
+ int cpu;
+ uint32_t *cells;
+ char *nodename;
+ uint32_t plic_phandle, phandle = 1;
+ int i;
+
+ fdt = s->fdt = create_device_tree(&s->fdt_size);
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+ nodename = g_strdup_printf("/memory@%lx",
+ (long)memmap[VIRT_DRAM].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ memmap[VIRT_DRAM].base >> 32, memmap[VIRT_DRAM].base,
+ mem_size >> 32, mem_size);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+ g_free(nodename);
+
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+ for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+ int cpu_phandle = phandle++;
+ nodename = g_strdup_printf("/cpus/cpu@%d", cpu);
+ char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ char *isa = riscv_isa_string(&s->soc.harts[cpu]);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+ qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
+ qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+ qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+ qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+ qemu_fdt_add_subnode(fdt, intc);
+ qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
+ qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
+ qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+ qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+ g_free(isa);
+ g_free(intc);
+ g_free(nodename);
+ }
+
+ cells = g_new0(uint32_t, s->soc.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ nodename =
+ g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+ cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+ g_free(nodename);
+ }
+ nodename = g_strdup_printf("/soc/clint@%lx",
+ (long)memmap[VIRT_CLINT].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[VIRT_CLINT].base,
+ 0x0, memmap[VIRT_CLINT].size);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ g_free(cells);
+ g_free(nodename);
+
+ plic_phandle = phandle++;
+ cells = g_new0(uint32_t, s->soc.num_harts * 4);
+ for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ nodename =
+ g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+ uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+ cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+ cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+ g_free(nodename);
+ }
+ nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+ (long)memmap[VIRT_PLIC].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0");
+ qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+ cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[VIRT_PLIC].base,
+ 0x0, memmap[VIRT_PLIC].size);
+ qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control");
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7);
+ qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", VIRTIO_NDEV);
+ qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
+ plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+ g_free(cells);
+ g_free(nodename);
+
+ for (i = 0; i < VIRTIO_COUNT; i++) {
+ nodename = g_strdup_printf("/virtio_mmio@%lx",
+ (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+ 0x0, memmap[VIRT_VIRTIO].size);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
+ g_free(nodename);
+ }
+
+ nodename = g_strdup_printf("/test@%lx",
+ (long)memmap[VIRT_TEST].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[VIRT_TEST].base,
+ 0x0, memmap[VIRT_TEST].size);
+
+ nodename = g_strdup_printf("/uart@%lx",
+ (long)memmap[VIRT_UART0].base);
+ qemu_fdt_add_subnode(fdt, nodename);
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a");
+ qemu_fdt_setprop_cells(fdt, nodename, "reg",
+ 0x0, memmap[VIRT_UART0].base,
+ 0x0, memmap[VIRT_UART0].size);
+ qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+ qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ g_free(nodename);
+
+ return fdt;
+}
+
+static void riscv_virt_board_init(MachineState *machine)
+{
+ const struct MemmapEntry *memmap = virt_memmap;
+
+ RISCVVirtState *s = g_new0(RISCVVirtState, 1);
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+ char *plic_hart_config;
+ size_t plic_hart_config_len;
+ int i;
+ void *fdt;
+
+ /* Initialize SOC */
+ object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ &error_abort);
+
+ /* register system main memory (actual RAM) */
+ memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
+ machine->ram_size, &error_fatal);
+ memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base,
+ main_mem);
+
+ /* create device tree */
+ fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
+
+ /* boot rom */
+ memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom",
+ s->fdt_size + 0x2000, &error_fatal);
+ memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+ if (machine->kernel_filename) {
+ uint64_t kernel_entry = load_kernel(machine->kernel_filename);
+
+ if (machine->initrd_filename) {
+ hwaddr start;
+ hwaddr end = load_initrd(machine->initrd_filename,
+ machine->ram_size, kernel_entry,
+ &start);
+ qemu_fdt_setprop_cell(fdt, "/chosen",
+ "linux,initrd-start", start);
+ qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ end);
+ }
+ }
+
+ /* reset vector */
+ uint32_t reset_vec[8] = {
+ 0x00000297, /* 1: auipc t0, %pcrel_hi(dtb) */
+ 0x02028593, /* addi a1, t0, %pcrel_lo(1b) */
+ 0xf1402573, /* csrr a0, mhartid */
+#if defined(TARGET_RISCV32)
+ 0x0182a283, /* lw t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+ 0x0182b283, /* ld t0, 24(t0) */
+#endif
+ 0x00028067, /* jr t0 */
+ 0x00000000,
+ memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */
+ 0x00000000,
+ /* dtb: */
+ };
+
+ /* copy in the reset vector */
+ copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec));
+
+ /* copy in the device tree */
+ qemu_fdt_dumpdtb(s->fdt, s->fdt_size);
+ cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec),
+ s->fdt, s->fdt_size);
+
+ /* create PLIC hart topology configuration string */
+ plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
+ plic_hart_config = g_malloc0(plic_hart_config_len);
+ for (i = 0; i < smp_cpus; i++) {
+ if (i != 0) {
+ strncat(plic_hart_config, ",", plic_hart_config_len);
+ }
+ strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len);
+ plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
+ }
+
+ /* MMIO */
+ s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
+ plic_hart_config,
+ VIRT_PLIC_NUM_SOURCES,
+ VIRT_PLIC_NUM_PRIORITIES,
+ VIRT_PLIC_PRIORITY_BASE,
+ VIRT_PLIC_PENDING_BASE,
+ VIRT_PLIC_ENABLE_BASE,
+ VIRT_PLIC_ENABLE_STRIDE,
+ VIRT_PLIC_CONTEXT_BASE,
+ VIRT_PLIC_CONTEXT_STRIDE,
+ memmap[VIRT_PLIC].size);
+ sifive_clint_create(memmap[VIRT_CLINT].base,
+ memmap[VIRT_CLINT].size, smp_cpus,
+ SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+ sifive_test_create(memmap[VIRT_TEST].base);
+
+ for (i = 0; i < VIRTIO_COUNT; i++) {
+ sysbus_create_simple("virtio-mmio",
+ memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+ SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]);
+ }
+
+ serial_mm_init(system_memory, memmap[VIRT_UART0].base,
+ 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
+ serial_hds[0], DEVICE_LITTLE_ENDIAN);
+}
+
+static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev)
+{
+ return 0;
+}
+
+static void riscv_virt_board_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->init = riscv_virt_board_sysbus_device_init;
+}
+
+static const TypeInfo riscv_virt_board_device = {
+ .name = TYPE_RISCV_VIRT_BOARD,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RISCVVirtState),
+ .class_init = riscv_virt_board_class_init,
+};
+
+static void riscv_virt_board_machine_init(MachineClass *mc)
+{
+ mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)";
+ mc->init = riscv_virt_board_init;
+ mc->max_cpus = 8; /* hardcoded limit in BBL */
+}
+
+DEFINE_MACHINE("virt", riscv_virt_board_machine_init)
+
+static void riscv_virt_board_register_types(void)
+{
+ type_register_static(&riscv_virt_board_device);
+}
+
+type_init(riscv_virt_board_register_types);
diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c
index 155a69467b..9c24bc6f7c 100644
--- a/hw/s390x/event-facility.c
+++ b/hw/s390x/event-facility.c
@@ -29,8 +29,17 @@ typedef struct SCLPEventsBus {
struct SCLPEventFacility {
SysBusDevice parent_obj;
SCLPEventsBus sbus;
- /* guest' receive mask */
- unsigned int receive_mask;
+ /* guest's receive mask */
+ sccb_mask_t receive_mask;
+ /*
+ * when false, we keep the same broken, backwards compatible behaviour as
+ * before, allowing only masks of size exactly 4; when true, we implement
+ * the architecture correctly, allowing all valid mask sizes. Needed for
+ * migration toward older versions.
+ */
+ bool allow_all_mask_sizes;
+ /* length of the receive mask */
+ uint16_t mask_length;
};
/* return true if any child has event pending set */
@@ -52,9 +61,9 @@ static bool event_pending(SCLPEventFacility *ef)
return false;
}
-static unsigned int get_host_send_mask(SCLPEventFacility *ef)
+static sccb_mask_t get_host_send_mask(SCLPEventFacility *ef)
{
- unsigned int mask;
+ sccb_mask_t mask;
BusChild *kid;
SCLPEventClass *child;
@@ -68,9 +77,9 @@ static unsigned int get_host_send_mask(SCLPEventFacility *ef)
return mask;
}
-static unsigned int get_host_receive_mask(SCLPEventFacility *ef)
+static sccb_mask_t get_host_receive_mask(SCLPEventFacility *ef)
{
- unsigned int mask;
+ sccb_mask_t mask;
BusChild *kid;
SCLPEventClass *child;
@@ -180,7 +189,7 @@ out:
}
static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
- unsigned int mask)
+ sccb_mask_t mask)
{
uint16_t rc;
int slen;
@@ -220,10 +229,21 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
return rc;
}
+/* copy up to src_len bytes and fill the rest of dst with zeroes */
+static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
+ uint16_t src_len)
+{
+ int i;
+
+ for (i = 0; i < dst_len; i++) {
+ dst[i] = i < src_len ? src[i] : 0;
+ }
+}
+
static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
{
- unsigned int sclp_active_selection_mask;
- unsigned int sclp_cp_receive_mask;
+ sccb_mask_t sclp_active_selection_mask;
+ sccb_mask_t sclp_cp_receive_mask;
ReadEventData *red = (ReadEventData *) sccb;
@@ -240,7 +260,9 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
sclp_active_selection_mask = sclp_cp_receive_mask;
break;
case SCLP_SELECTIVE_READ:
- sclp_active_selection_mask = be32_to_cpu(red->mask);
+ copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask,
+ sizeof(sclp_active_selection_mask), ef->mask_length);
+ sclp_active_selection_mask = be32_to_cpu(sclp_active_selection_mask);
if (!sclp_cp_receive_mask ||
(sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
sccb->h.response_code =
@@ -259,24 +281,14 @@ out:
return;
}
-/* copy up to dst_len bytes and fill the rest of dst with zeroes */
-static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
- uint16_t src_len)
-{
- int i;
-
- for (i = 0; i < dst_len; i++) {
- dst[i] = i < src_len ? src[i] : 0;
- }
-}
-
static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
{
WriteEventMask *we_mask = (WriteEventMask *) sccb;
uint16_t mask_length = be16_to_cpu(we_mask->mask_length);
- uint32_t tmp_mask;
+ sccb_mask_t tmp_mask;
- if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX)) {
+ if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) ||
+ ((mask_length != 4) && !ef->allow_all_mask_sizes)) {
sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH);
goto out;
}
@@ -301,6 +313,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
mask_length, sizeof(tmp_mask));
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+ ef->mask_length = mask_length;
out:
return;
@@ -356,6 +369,24 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
}
}
+static bool vmstate_event_facility_mask_length_needed(void *opaque)
+{
+ SCLPEventFacility *ef = opaque;
+
+ return ef->allow_all_mask_sizes;
+}
+
+static const VMStateDescription vmstate_event_facility_mask_length = {
+ .name = "vmstate-event-facility/mask_length",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .needed = vmstate_event_facility_mask_length_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(mask_length, SCLPEventFacility),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_event_facility = {
.name = "vmstate-event-facility",
.version_id = 0,
@@ -363,15 +394,39 @@ static const VMStateDescription vmstate_event_facility = {
.fields = (VMStateField[]) {
VMSTATE_UINT32(receive_mask, SCLPEventFacility),
VMSTATE_END_OF_LIST()
+ },
+ .subsections = (const VMStateDescription * []) {
+ &vmstate_event_facility_mask_length,
+ NULL
}
};
+static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value,
+ Error **errp)
+{
+ SCLPEventFacility *ef = (SCLPEventFacility *)obj;
+
+ ef->allow_all_mask_sizes = value;
+}
+
+static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **e)
+{
+ SCLPEventFacility *ef = (SCLPEventFacility *)obj;
+
+ return ef->allow_all_mask_sizes;
+}
+
static void init_event_facility(Object *obj)
{
SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
DeviceState *sdev = DEVICE(obj);
Object *new;
+ event_facility->mask_length = 4;
+ event_facility->allow_all_mask_sizes = true;
+ object_property_add_bool(obj, "allow_all_mask_sizes",
+ sclp_event_get_allow_all_mask_sizes,
+ sclp_event_set_allow_all_mask_sizes, NULL);
/* Spawn a new bus for SCLP events */
qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
TYPE_SCLP_EVENTS_BUS, sdev, NULL);
@@ -431,26 +486,12 @@ static void event_realize(DeviceState *qdev, Error **errp)
}
}
-static void event_unrealize(DeviceState *qdev, Error **errp)
-{
- SCLPEvent *event = SCLP_EVENT(qdev);
- SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
- if (child->exit) {
- int rc = child->exit(event);
- if (rc < 0) {
- error_setg(errp, "SCLP event exit failed.");
- return;
- }
- }
-}
-
static void event_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->bus_type = TYPE_SCLP_EVENTS_BUS;
dc->realize = event_realize;
- dc->unrealize = event_unrealize;
}
static const TypeInfo sclp_event_type_info = {
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 798e99aadf..fdeaec3a58 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -234,7 +234,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
if (!get_boot_device(0)) {
if (boot_menu) {
error_report("boot menu requires a bootindex to be specified for "
- "the IPL device.");
+ "the IPL device");
}
return;
}
@@ -250,7 +250,9 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
case S390_IPL_TYPE_QEMU_SCSI:
break;
default:
- error_report("boot menu is not supported for this device type.");
+ if (boot_menu) {
+ error_report("boot menu is not supported for this device type");
+ }
return;
}
@@ -263,13 +265,13 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
tmp = qemu_opt_get(opts, "splash-time");
if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) {
- error_report("splash-time is invalid, forcing it to 0.");
+ error_report("splash-time is invalid, forcing it to 0");
*timeout = 0;
return;
}
if (splash_time > 0xffffffff) {
- error_report("splash-time is too large, forcing it to max value.");
+ error_report("splash-time is too large, forcing it to max value");
*timeout = 0xffffffff;
return;
}
@@ -380,7 +382,8 @@ static int load_netboot_image(Error **errp)
netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
if (netboot_filename == NULL) {
- error_setg(errp, "Could not find network bootloader");
+ error_setg(errp, "Could not find network bootloader '%s'",
+ ipl->netboot_fw);
goto unref_mr;
}
@@ -489,7 +492,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
if (ipl->netboot) {
if (load_netboot_image(&err) < 0) {
error_report_err(err);
- vm_stop(RUN_STATE_INTERNAL_ERROR);
+ exit(1);
}
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 4d0c3deba6..864145a7c6 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -27,6 +27,7 @@
#include "s390-pci-bus.h"
#include "hw/s390x/storage-keys.h"
#include "hw/s390x/storage-attributes.h"
+#include "hw/s390x/event-facility.h"
#include "hw/compat.h"
#include "ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
@@ -254,8 +255,10 @@ static void s390_init_ipl_dev(const char *kernel_filename,
}
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
qdev_prop_set_string(dev, "firmware", firmware);
- qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
+ if (!strlen(object_property_get_str(new, "netboot_fw", &error_abort))) {
+ qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
+ }
object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
new, NULL);
object_unref(new);
@@ -388,12 +391,14 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev,
}
}
-static CpuInstanceProperties s390_cpu_index_to_props(MachineState *machine,
+static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms,
unsigned cpu_index)
{
- g_assert(machine->possible_cpus && cpu_index < machine->possible_cpus->len);
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
- return machine->possible_cpus->cpus[cpu_index].props;
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
}
static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms)
@@ -664,7 +669,12 @@ bool css_migration_enabled(void)
type_init(ccw_machine_register_##suffix)
#define CCW_COMPAT_2_11 \
- HW_COMPAT_2_11
+ HW_COMPAT_2_11 \
+ {\
+ .driver = TYPE_SCLP_EVENT_FACILITY,\
+ .property = "allow_all_mask_sizes",\
+ .value = "off",\
+ },
#define CCW_COMPAT_2_10 \
HW_COMPAT_2_10
diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c
index 3ee890b392..50c021b9c2 100644
--- a/hw/s390x/sclpcpu.c
+++ b/hw/s390x/sclpcpu.c
@@ -37,12 +37,12 @@ void raise_irq_cpu_hotplug(void)
sclp_service_interrupt(0);
}
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
{
return SCLP_EVENT_MASK_CONFIG_MGT_DATA;
}
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
{
return 0;
}
diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c
index 02416435a1..1c8f5c9393 100644
--- a/hw/s390x/sclpquiesce.c
+++ b/hw/s390x/sclpquiesce.c
@@ -28,12 +28,12 @@ static bool can_handle_event(uint8_t type)
return type == SCLP_EVENT_SIGNAL_QUIESCE;
}
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
{
return SCLP_EVENT_MASK_SIGNAL_QUIESCE;
}
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
{
return 0;
}
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 8f7fbc2ab7..e51fbefd23 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -752,7 +752,7 @@ out_err:
g_free(sch);
}
-static int virtio_ccw_exit(VirtioCcwDevice *dev)
+static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp)
{
CcwDevice *ccw_dev = CCW_DEVICE(dev);
SubchDev *sch = ccw_dev->sch;
@@ -760,12 +760,12 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
if (sch) {
css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
g_free(sch);
+ ccw_dev->sch = NULL;
}
if (dev->indicators) {
release_indicator(&dev->routes.adapter, dev->indicators);
dev->indicators = NULL;
}
- return 0;
}
static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
@@ -1344,7 +1344,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_net_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_net_properties;
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
@@ -1372,7 +1372,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_blk_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_blk_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1400,7 +1400,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_serial_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_serial_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
@@ -1428,7 +1428,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_balloon_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_balloon_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1456,7 +1456,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_scsi_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1483,7 +1483,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = vhost_ccw_scsi_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = vhost_ccw_scsi_properties;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1520,7 +1520,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_rng_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_rng_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1558,7 +1558,7 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_crypto_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_crypto_properties;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1596,7 +1596,7 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_gpu_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_gpu_properties;
dc->hotpluggable = false;
@@ -1625,7 +1625,7 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = virtio_ccw_input_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_input_properties;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
@@ -1705,12 +1705,12 @@ static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
virtio_ccw_device_realize(_dev, errp);
}
-static int virtio_ccw_busdev_exit(DeviceState *dev)
+static void virtio_ccw_busdev_unrealize(DeviceState *dev, Error **errp)
{
VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
- return _info->exit(_dev);
+ _info->unrealize(_dev, errp);
}
static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
@@ -1728,7 +1728,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
k->unplug = virtio_ccw_busdev_unplug;
dc->realize = virtio_ccw_busdev_realize;
- dc->exit = virtio_ccw_busdev_exit;
+ dc->unrealize = virtio_ccw_busdev_unrealize;
dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
}
@@ -1804,7 +1804,7 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
k->realize = virtio_ccw_9p_realize;
dc->reset = virtio_ccw_reset;
dc->props = virtio_ccw_9p_properties;
@@ -1853,7 +1853,7 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data)
VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
k->realize = vhost_vsock_ccw_realize;
- k->exit = virtio_ccw_exit;
+ k->unrealize = virtio_ccw_unrealize;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->props = vhost_vsock_ccw_properties;
dc->reset = virtio_ccw_reset;
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
index 3905f3a3d6..2fc513001e 100644
--- a/hw/s390x/virtio-ccw.h
+++ b/hw/s390x/virtio-ccw.h
@@ -76,7 +76,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice;
typedef struct VirtIOCCWDeviceClass {
CCWDeviceClass parent_class;
void (*realize)(VirtioCcwDevice *dev, Error **errp);
- int (*exit)(VirtioCcwDevice *dev);
+ void (*unrealize)(VirtioCcwDevice *dev, Error **errp);
} VirtIOCCWDeviceClass;
/* Performance improves when virtqueue kick processing is decoupled from the
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index 61eb424bbc..0f5804b3b4 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -324,6 +324,7 @@ static void *sparc32_dma_init(hwaddr dma_base,
esp = ESP_STATE(object_resolve_path_component(OBJECT(espdma), "esp"));
sysbus_mmio_map(SYS_BUS_DEVICE(esp), 0, esp_base);
+ scsi_bus_legacy_handle_cmdline(&esp->esp.bus);
ledma = SPARC32_LEDMA_DEVICE(object_resolve_path_component(
OBJECT(dma), "ledma"));
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index 16713f2c52..4e5855741a 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -6,8 +6,8 @@
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
* Pierre Morel <pmorel@linux.vnet.ibm.com>
*
- * This work is licensed under the terms of the GNU GPL, version 2 or(at
- * your option) any version. See the COPYING file in the top-level
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
* directory.
*/