summary refs log tree commit diff stats
path: root/hw/riscv
diff options
context:
space:
mode:
Diffstat (limited to 'hw/riscv')
-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
12 files changed, 2844 insertions, 0 deletions
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);