diff --git a/Makefile.objs b/Makefile.objs
index 7a9828da282..adbba37d65c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -253,6 +253,7 @@ trace-events-subdirs += target/arm
trace-events-subdirs += target/i386
trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc
+trace-events-subdirs += target/riscv
trace-events-subdirs += target/s390x
trace-events-subdirs += target/sparc
trace-events-subdirs += ui
diff --git a/disas/riscv.c b/disas/riscv.c
index 7fd1019623e..27546dd7902 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -87,33 +87,10 @@ typedef enum {
typedef enum {
rvc_end,
- rvc_simm_6,
- rvc_imm_6,
- rvc_imm_7,
- rvc_imm_8,
- rvc_imm_9,
- rvc_imm_10,
- rvc_imm_12,
- rvc_imm_18,
- rvc_imm_nz,
- rvc_imm_x2,
- rvc_imm_x4,
- rvc_imm_x8,
- rvc_imm_x16,
- rvc_rd_b3,
- rvc_rs1_b3,
- rvc_rs2_b3,
- rvc_rd_eq_rs1,
rvc_rd_eq_ra,
- rvc_rd_eq_sp,
rvc_rd_eq_x0,
- rvc_rs1_eq_sp,
rvc_rs1_eq_x0,
rvc_rs2_eq_x0,
- rvc_rd_ne_x0_x2,
- rvc_rd_ne_x0,
- rvc_rs1_ne_x0,
- rvc_rs2_ne_x0,
rvc_rs2_eq_rs1,
rvc_rs1_eq_ra,
rvc_imm_eq_zero,
@@ -2522,111 +2499,16 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c)
uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2;
while (*c != rvc_end) {
switch (*c) {
- case rvc_simm_6:
- if (!(imm >= -32 && imm < 32)) {
- return false;
- }
- break;
- case rvc_imm_6:
- if (!(imm <= 63)) {
- return false;
- }
- break;
- case rvc_imm_7:
- if (!(imm <= 127)) {
- return false;
- }
- break;
- case rvc_imm_8:
- if (!(imm <= 255)) {
- return false;
- }
- break;
- case rvc_imm_9:
- if (!(imm <= 511)) {
- return false;
- }
- break;
- case rvc_imm_10:
- if (!(imm <= 1023)) {
- return false;
- }
- break;
- case rvc_imm_12:
- if (!(imm <= 4095)) {
- return false;
- }
- break;
- case rvc_imm_18:
- if (!(imm <= 262143)) {
- return false;
- }
- break;
- case rvc_imm_nz:
- if (!(imm != 0)) {
- return false;
- }
- break;
- case rvc_imm_x2:
- if (!((imm & 0b1) == 0)) {
- return false;
- }
- break;
- case rvc_imm_x4:
- if (!((imm & 0b11) == 0)) {
- return false;
- }
- break;
- case rvc_imm_x8:
- if (!((imm & 0b111) == 0)) {
- return false;
- }
- break;
- case rvc_imm_x16:
- if (!((imm & 0b1111) == 0)) {
- return false;
- }
- break;
- case rvc_rd_b3:
- if (!(rd >= 8 && rd <= 15)) {
- return false;
- }
- break;
- case rvc_rs1_b3:
- if (!(rs1 >= 8 && rs1 <= 15)) {
- return false;
- }
- break;
- case rvc_rs2_b3:
- if (!(rs2 >= 8 && rs2 <= 15)) {
- return false;
- }
- break;
- case rvc_rd_eq_rs1:
- if (!(rd == rs1)) {
- return false;
- }
- break;
case rvc_rd_eq_ra:
if (!(rd == 1)) {
return false;
}
break;
- case rvc_rd_eq_sp:
- if (!(rd == 2)) {
- return false;
- }
- break;
case rvc_rd_eq_x0:
if (!(rd == 0)) {
return false;
}
break;
- case rvc_rs1_eq_sp:
- if (!(rs1 == 2)) {
- return false;
- }
- break;
case rvc_rs1_eq_x0:
if (!(rs1 == 0)) {
return false;
@@ -2637,26 +2519,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c)
return false;
}
break;
- case rvc_rd_ne_x0_x2:
- if (!(rd != 0 && rd != 2)) {
- return false;
- }
- break;
- case rvc_rd_ne_x0:
- if (!(rd != 0)) {
- return false;
- }
- break;
- case rvc_rs1_ne_x0:
- if (!(rs1 != 0)) {
- return false;
- }
- break;
- case rvc_rs2_ne_x0:
- if (!(rs2 != 0)) {
- return false;
- }
- break;
case rvc_rs2_eq_rs1:
if (!(rs2 == rs1)) {
return false;
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
index 1dde01d39dc..d36b004ab0f 100644
--- a/hw/riscv/Makefile.objs
+++ b/hw/riscv/Makefile.objs
@@ -1,3 +1,4 @@
+obj-y += boot.o
obj-y += riscv_htif.o
obj-y += riscv_hart.o
obj-y += sifive_e.o
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
new file mode 100644
index 00000000000..cf4e5d59463
--- /dev/null
+++ b/hw/riscv/boot.c
@@ -0,0 +1,172 @@
+/*
+ * QEMU RISCV firmware and kernel loader
+ *
+ * Copyright (c) 2017-2018 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 .
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/loader.h"
+#include "hw/boards.h"
+#include "sysemu/device_tree.h"
+#include "elf.h"
+#include "hw/riscv/boot.h"
+
+#define RISCV_BOOT_DEBUG 0
+
+#define boot_debug(fs, ...) \
+ if (RISCV_BOOT_DEBUG) { \
+ fprintf(stderr, "boot: %s: "fs, __func__, ##__VA_ARGS__); \
+ }
+
+static uint64_t kernel_offset;
+
+static uint64_t kernel_translate(void *opaque, uint64_t addr)
+{
+ /* mask kernel virtual address and offset by load address */
+ if (kernel_offset) {
+ return (addr & 0x7fffffff) + kernel_offset;
+ } else {
+ return addr;
+ }
+}
+
+hwaddr riscv_load_firmware(const char *filename)
+{
+ uint64_t firmware_entry, firmware_start, firmware_end;
+
+ if (load_elf(filename, NULL, NULL,
+ &firmware_entry, &firmware_start, &firmware_end,
+ 0, EM_RISCV, 1, 0) < 0) {
+ error_report("riscv_boot: could not load firmware '%s'", filename);
+ exit(1);
+ }
+
+ /* align kernel load address to the megapage after the firmware */
+#if defined(TARGET_RISCV32)
+ kernel_offset = (firmware_end + 0x3fffff) & ~0x3fffff;
+#else
+ kernel_offset = (firmware_end + 0x1fffff) & ~0x1fffff;
+#endif
+
+ boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " "
+ "end=0x" TARGET_FMT_plx " kernel_offset=0x" TARGET_FMT_plx "\n",
+ firmware_entry, firmware_start, firmware_end, kernel_offset);
+
+ return firmware_entry;
+}
+
+hwaddr riscv_load_kernel(const char *filename, void *fdt)
+{
+ uint64_t kernel_entry, kernel_start, kernel_end;
+
+ if (load_elf(filename, kernel_translate, NULL,
+ &kernel_entry, &kernel_start, &kernel_end,
+ 0, EM_RISCV, 1, 0) < 0) {
+ error_report("riscv_boot: could not load kernel '%s'", filename);
+ exit(1);
+ }
+
+ boot_debug("entry=0x" TARGET_FMT_plx " start=0x" TARGET_FMT_plx " "
+ "end=0x" TARGET_FMT_plx "\n", kernel_entry, kernel_start,
+ kernel_end);
+
+ /*
+ * pass kernel load address via device-tree to firmware
+ *
+ * BBL reads the kernel address from device-tree
+ */
+ if (fdt) {
+ qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-end",
+ kernel_end >> 32, kernel_end);
+ qemu_fdt_setprop_cells(fdt, "/chosen", "riscv,kernel-start",
+ kernel_start >> 32, kernel_start);
+ }
+
+ return kernel_entry;
+}
+
+void riscv_load_initrd(const char *filename, uint64_t mem_size,
+ hwaddr firmware_entry, void *fdt)
+{
+ uint64_t start, 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 = firmware_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("riscv_boot: could not load ramdisk '%s'", filename);
+ exit(1);
+ }
+ }
+
+ boot_debug("start=0x" TARGET_FMT_plx " end=0x" TARGET_FMT_plx "\n",
+ start, start + size);
+
+ /*
+ * pass initrd load address via device-tree to kernel
+ *
+ * linux-kernel reads the initrd address from device-tree
+ */
+ if (fdt) {
+ qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-end",
+ (start + size) >> 32, start + size);
+ qemu_fdt_setprop_cells(fdt, "/chosen", "linux,initrd-start",
+ start >> 32, start);
+ }
+}
+
+hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt)
+{
+ hwaddr firmware_entry = 0;
+
+ /* load firmware e.g. -bios bbl */
+ if (machine->firmware) {
+ firmware_entry = riscv_load_firmware(machine->firmware);
+ }
+
+ /* load combined bbl+kernel or separate kernel */
+ if (machine->kernel_filename) {
+ if (machine->firmware) {
+ /* load separate bios and kernel e.g. -bios bbl -kernel vmlinux */
+ riscv_load_kernel(machine->kernel_filename, fdt);
+ } else {
+ /* load traditional combined bbl+kernel e.g. -kernel bbl_vmlimux */
+ firmware_entry = riscv_load_kernel(machine->kernel_filename, NULL);
+ }
+ if (machine->initrd_filename) {
+ /* load separate initrd */
+ riscv_load_initrd(machine->initrd_filename, machine->ram_size,
+ firmware_entry, fdt);
+ }
+ }
+
+ return firmware_entry;
+}
diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c
index 7cc606e0654..d4c159e9373 100644
--- a/hw/riscv/sifive_clint.c
+++ b/hw/riscv/sifive_clint.c
@@ -47,12 +47,12 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t 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);
+ riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
return;
}
/* otherwise, set up the future timer interrupt */
- riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
+ riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0));
diff = cpu->env.timecmp - rtc_r;
/* back to ns (note args switched in muldiv64) */
next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
@@ -67,7 +67,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
static void sifive_clint_timer_cb(void *opaque)
{
RISCVCPU *cpu = opaque;
- riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+ riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1));
}
/* CPU wants to read rtc or timecmp register */
@@ -132,7 +132,7 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
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);
+ riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value));
} else {
error_report("clint: invalid sip write: %08x", (uint32_t)addr);
}
@@ -146,15 +146,15 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
error_report("clint: invalid timecmp hartid: %zu", hartid);
} else if ((addr & 0x7) == 0) {
/* timecmp_lo */
- uint64_t timecmp = env->timecmp;
+ uint64_t timecmp_hi = env->timecmp >> 32;
sifive_clint_write_timecmp(RISCV_CPU(cpu),
- timecmp << 32 | (value & 0xFFFFFFFF));
+ timecmp_hi << 32 | (value & 0xFFFFFFFF));
return;
} else if ((addr & 0x7) == 4) {
/* timecmp_hi */
- uint64_t timecmp = env->timecmp;
+ uint64_t timecmp_lo = env->timecmp;
sifive_clint_write_timecmp(RISCV_CPU(cpu),
- value << 32 | (timecmp & 0xFFFFFFFF));
+ value << 32 | (timecmp_lo & 0xFFFFFFFF));
} else {
error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 4577d720372..16b547eec81 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -41,6 +41,7 @@
#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/sifive_prci.h"
#include "hw/riscv/sifive_uart.h"
#include "hw/riscv/sifive_e.h"
@@ -56,6 +57,7 @@ static const struct MemmapEntry {
[SIFIVE_E_DEBUG] = { 0x0, 0x100 },
[SIFIVE_E_MROM] = { 0x1000, 0x2000 },
[SIFIVE_E_OTP] = { 0x20000, 0x2000 },
+ [SIFIVE_E_TEST] = { 0x100000, 0x1000 },
[SIFIVE_E_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_E_PLIC] = { 0xc000000, 0x4000000 },
[SIFIVE_E_AON] = { 0x10000000, 0x8000 },
@@ -98,21 +100,27 @@ static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
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 *xip_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+
int i;
- /* Initialize SoC */
- object_initialize_child(OBJECT(machine), "soc", &s->soc,
- sizeof(s->soc), TYPE_RISCV_E_SOC,
- &error_abort, NULL);
- object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ /* Initialize harts */
+ object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "cpus", OBJECT(&s->cpus),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->cpus), true, "realized",
&error_abort);
/* Data Tightly Integrated Memory */
- memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram",
+ memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.dtim",
memmap[SIFIVE_E_DTIM].size, &error_fatal);
memory_region_add_subregion(sys_mem,
memmap[SIFIVE_E_DTIM].base, main_mem);
@@ -133,32 +141,6 @@ static void riscv_sifive_e_init(MachineState *machine)
if (machine->kernel_filename) {
load_kernel(machine->kernel_filename);
}
-}
-
-static void riscv_sifive_e_soc_init(Object *obj)
-{
- SiFiveESoCState *s = RISCV_E_SOC(obj);
-
- object_initialize_child(obj, "cpus", &s->cpus,
- sizeof(s->cpus), TYPE_RISCV_HART_ARRAY,
- &error_abort, NULL);
- object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type",
- &error_abort);
- object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
- &error_abort);
-}
-
-static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
-{
- const struct MemmapEntry *memmap = sifive_e_memmap;
-
- SiFiveESoCState *s = RISCV_E_SOC(dev);
- MemoryRegion *sys_mem = get_system_memory();
- MemoryRegion *xip_mem = g_new(MemoryRegion, 1);
- MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
-
- object_property_set_bool(OBJECT(&s->cpus), true, "realized",
- &error_abort);
/* Mask ROM */
memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom",
@@ -179,8 +161,12 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
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);
+ memmap[SIFIVE_E_CLINT].size,
+ smp_cpus,
+ SIFIVE_SIP_BASE,
+ SIFIVE_TIMECMP_BASE,
+ SIFIVE_TIME_BASE);
+ sifive_test_create(memmap[SIFIVE_E_TEST].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);
@@ -192,9 +178,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
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_hd(1), qdev_get_gpio_in(DEVICE(s->plic),
- SIFIVE_E_UART1_IRQ)); */
+ sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base,
+ serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), 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",
@@ -219,27 +204,3 @@ static void riscv_sifive_e_machine_init(MachineClass *mc)
}
DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
-
-static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- dc->realize = riscv_sifive_e_soc_realize;
- /* Reason: Uses serial_hds in realize function, thus can't be used twice */
- dc->user_creatable = false;
-}
-
-static const TypeInfo riscv_sifive_e_soc_type_info = {
- .name = TYPE_RISCV_E_SOC,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(SiFiveESoCState),
- .instance_init = riscv_sifive_e_soc_init,
- .class_init = riscv_sifive_e_soc_class_init,
-};
-
-static void riscv_sifive_e_soc_register_types(void)
-{
- type_register_static(&riscv_sifive_e_soc_type_info);
-}
-
-type_init(riscv_sifive_e_soc_register_types)
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
index a91aeb97aba..bcde8cc7585 100644
--- a/hw/riscv/sifive_plic.c
+++ b/hw/riscv/sifive_plic.c
@@ -23,6 +23,7 @@
#include "qemu/error-report.h"
#include "hw/sysbus.h"
#include "target/riscv/cpu.h"
+#include "sysemu/sysemu.h"
#include "hw/riscv/sifive_plic.h"
#define RISCV_DEBUG_PLIC 0
@@ -81,36 +82,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic)
}
}
-static
-void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
+static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value)
{
- 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);
+ uint32_t old, new, cmp = atomic_read(a);
+
+ do {
+ old = cmp;
+ new = (old & ~mask) | (value & mask);
+ cmp = atomic_cmpxchg(a, old, new);
+ } while (old != cmp);
+
+ return old;
}
-static
-void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
+static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level)
{
- 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);
+ atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level);
+}
+
+static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level)
+{
+ atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level);
}
-static
-int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
+static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
{
- int i, j, count = 0;
+ int i, j;
for (i = 0; i < plic->bitfield_words; i++) {
uint32_t pending_enabled_not_claimed =
(plic->pending[i] & ~plic->claimed[i]) &
@@ -123,11 +120,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
uint32_t prio = plic->source_priority[irq];
int enabled = pending_enabled_not_claimed & (1 << j);
if (enabled && prio > plic->target_priority[addrid]) {
- count++;
+ return 1;
}
}
}
- return count;
+ return 0;
}
static void sifive_plic_update(SiFivePLICState *plic)
@@ -143,13 +140,13 @@ static void sifive_plic_update(SiFivePLICState *plic)
if (!env) {
continue;
}
- int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
+ int level = sifive_plic_irqs_pending(plic, addrid);
switch (mode) {
case PLICMode_M:
- riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
+ riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level));
break;
case PLICMode_S:
- riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
+ riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level));
break;
default:
break;
@@ -218,7 +215,7 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
} 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;
+ uint32_t word = (addr - plic->pending_base) >> 2;
if (RISCV_DEBUG_PLIC) {
qemu_log("plic: read pending: word=%d value=%d\n",
word, plic->pending[word]);
@@ -387,7 +384,7 @@ static void parse_hart_config(SiFivePLICState *plic)
p = plic->hart_config;
while ((c = *p++)) {
if (c == ',') {
- addrid += __builtin_popcount(modes);
+ addrid += ctpop8(modes);
modes = 0;
hartid++;
} else {
@@ -401,7 +398,7 @@ static void parse_hart_config(SiFivePLICState *plic)
}
}
if (modes) {
- addrid += __builtin_popcount(modes);
+ addrid += ctpop8(modes);
}
hartid++;
@@ -435,11 +432,11 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level)
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);
@@ -448,6 +445,18 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp)
plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio);
qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources);
+
+ /* We can't allow the supervisor to control SEIP as this would allow the
+ * supervisor to clear a pending external interrupt which will result in
+ * lost a interrupt in the case a PLIC is attached. The SEIP bit must be
+ * hardware controlled when a PLIC is attached. */
+ for (i = 0; i < smp_cpus; i++) {
+ RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i));
+ if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) {
+ error_report("sifive_plic_realize: SEIP already claimed");
+ exit(1);
+ }
+ }
}
static void sifive_plic_class_init(ObjectClass *klass, void *data)
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
index 0910ea32c1a..1435423a23e 100644
--- a/hw/riscv/sifive_prci.c
+++ b/hw/riscv/sifive_prci.c
@@ -23,15 +23,19 @@
#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 */
+ SiFivePRCIState *s = opaque;
+ switch(addr)
+ {
+ case SIFIVE_PRCI_HFROSCCFG:
+ return s->hfrosccfg;
+ case SIFIVE_PRCI_HFXOSCCFG:
+ return s->hfxosccfg;
+ case SIFIVE_PRCI_PLLCFG:
+ return s->pllcfg;
+ case SIFIVE_PRCI_PLLOUTDIV:
+ return s->plloutdiv;
}
hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
return 0;
@@ -40,7 +44,31 @@ static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
static void sifive_prci_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
- /* discard writes */
+ SiFivePRCIState *s = opaque;
+ switch(addr)
+ {
+ case SIFIVE_PRCI_HFROSCCFG:
+ s->hfrosccfg = (uint32_t) val64;
+ /* OSC stays ready */
+ s->hfrosccfg |= SIFIVE_PRCI_HFROSCCFG_RDY;
+ break;
+ case SIFIVE_PRCI_HFXOSCCFG:
+ s->hfxosccfg = (uint32_t) val64;
+ /* OSC stays ready */
+ s->hfxosccfg |= SIFIVE_PRCI_HFXOSCCFG_RDY;
+ break;
+ case SIFIVE_PRCI_PLLCFG:
+ s->pllcfg = (uint32_t) val64;
+ /* PLL stays locked */
+ s->pllcfg |= SIFIVE_PRCI_PLLCFG_LOCK;
+ break;
+ case SIFIVE_PRCI_PLLOUTDIV:
+ s->plloutdiv = (uint32_t) val64;
+ break;
+ default:
+ hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+ __func__, (int)addr, (int)val64);
+ }
}
static const MemoryRegionOps sifive_prci_ops = {
@@ -60,6 +88,13 @@ static void sifive_prci_init(Object *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);
+
+ s->hfrosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN);
+ s->hfxosccfg = (SIFIVE_PRCI_HFROSCCFG_RDY | SIFIVE_PRCI_HFROSCCFG_EN);
+ s->pllcfg = (SIFIVE_PRCI_PLLCFG_REFSEL | SIFIVE_PRCI_PLLCFG_BYPASS |
+ SIFIVE_PRCI_PLLCFG_LOCK);
+ s->plloutdiv = SIFIVE_PRCI_PLLOUTDIV_DIV1;
+
}
static const TypeInfo sifive_prci_info = {
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 59ae1ce24a0..fc84ab42e91 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -34,10 +34,12 @@
#include "hw/loader.h"
#include "hw/sysbus.h"
#include "hw/char/serial.h"
+#include "hw/net/cadence_gem.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_test.h"
#include "hw/riscv/sifive_uart.h"
#include "hw/riscv/sifive_prci.h"
#include "hw/riscv/sifive_u.h"
@@ -55,6 +57,7 @@ static const struct MemmapEntry {
} sifive_u_memmap[] = {
[SIFIVE_U_DEBUG] = { 0x0, 0x100 },
[SIFIVE_U_MROM] = { 0x1000, 0x11000 },
+ [SIFIVE_U_TEST] = { 0x100000, 0x1000 },
[SIFIVE_U_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 },
[SIFIVE_U_UART0] = { 0x10013000, 0x1000 },
@@ -119,10 +122,10 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
- for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) {
+ for (cpu = s->cpus.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.cpus.harts[cpu]);
+ char *isa = riscv_isa_string(&s->cpus.harts[cpu]);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency",
SIFIVE_U_CLOCK_FREQ);
@@ -143,8 +146,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
g_free(nodename);
}
- cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
- for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
+ cells = g_new0(uint32_t, s->cpus.num_harts * 4);
+ for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
@@ -162,12 +165,12 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
0x0, memmap[SIFIVE_U_CLINT].base,
0x0, memmap[SIFIVE_U_CLINT].size);
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
- cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4);
+ cells, s->cpus.num_harts * sizeof(uint32_t) * 4);
g_free(cells);
g_free(nodename);
- cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4);
- for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) {
+ cells = g_new0(uint32_t, s->cpus.num_harts * 4);
+ for (cpu = 0; cpu < s->cpus.num_harts; cpu++) {
nodename =
g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
@@ -184,7 +187,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
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.cpus.num_harts * sizeof(uint32_t) * 4);
+ cells, s->cpus.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);
@@ -218,6 +221,14 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0);
g_free(nodename);
+ nodename = g_strdup_printf("/soc/test@%lx",
+ (long)memmap[SIFIVE_U_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[SIFIVE_U_TEST].base,
+ 0x0, memmap[SIFIVE_U_TEST].size);
+
nodename = g_strdup_printf("/soc/uart@%lx",
(long)memmap[SIFIVE_U_UART0].base);
qemu_fdt_add_subnode(fdt, nodename);
@@ -230,24 +241,32 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
- qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ if (cmdline) {
+ 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 *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
+ MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
+ Error *err = NULL;
+
int i;
- /* Initialize SoC */
- object_initialize_child(OBJECT(machine), "soc", &s->soc,
- sizeof(s->soc), TYPE_RISCV_U_SOC,
- &error_abort, NULL);
- object_property_set_bool(OBJECT(&s->soc), true, "realized",
+ /* Initialize harts */
+ object_initialize(&s->cpus, sizeof(s->cpus), TYPE_RISCV_HART_ARRAY);
+ object_property_add_child(OBJECT(machine), "cpus", OBJECT(&s->cpus),
+ &error_abort);
+ object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type",
+ &error_abort);
+ object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
+ &error_abort);
+ object_property_set_bool(OBJECT(&s->cpus), true, "realized",
&error_abort);
/* register RAM */
@@ -297,36 +316,6 @@ static void riscv_sifive_u_init(MachineState *machine)
rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt),
memmap[SIFIVE_U_MROM].base + sizeof(reset_vec),
&address_space_memory);
-}
-
-static void riscv_sifive_u_soc_init(Object *obj)
-{
- SiFiveUSoCState *s = RISCV_U_SOC(obj);
-
- object_initialize_child(obj, "cpus", &s->cpus, sizeof(s->cpus),
- TYPE_RISCV_HART_ARRAY, &error_abort, NULL);
- object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type",
- &error_abort);
- object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts",
- &error_abort);
-
- sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem),
- TYPE_CADENCE_GEM);
-}
-
-static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
-{
- SiFiveUSoCState *s = RISCV_U_SOC(dev);
- const struct MemmapEntry *memmap = sifive_u_memmap;
- MemoryRegion *system_memory = get_system_memory();
- MemoryRegion *mask_rom = g_new(MemoryRegion, 1);
- qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES];
- int i;
- Error *err = NULL;
- NICInfo *nd = &nd_table[0];
-
- object_property_set_bool(OBJECT(&s->cpus), true, "realized",
- &error_abort);
/* boot rom */
memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom",
@@ -348,31 +337,29 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
memmap[SIFIVE_U_PLIC].size);
sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base,
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ));
- /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base,
- serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic),
- SIFIVE_U_UART1_IRQ)); */
+ sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base,
+ serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), 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);
-
- for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) {
- plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i);
- }
-
- if (nd->used) {
- qemu_check_nic_model(nd, TYPE_CADENCE_GEM);
- qdev_set_nic_properties(DEVICE(&s->gem), nd);
+ SIFIVE_SIP_BASE,
+ SIFIVE_TIMECMP_BASE,
+ SIFIVE_TIME_BASE);
+ sifive_test_create(memmap[SIFIVE_U_TEST].base);
+
+ /* Initialize gem ethernet */
+ object_initialize(&s->gem, sizeof(s->gem), TYPE_CADENCE_GEM);
+ object_property_add_child(OBJECT(machine), "gem", OBJECT(&s->gem),
+ &error_abort);
+ if (nd_table[0].used) {
+ qemu_check_nic_model(nd_table, TYPE_CADENCE_GEM);
+ qdev_set_nic_properties(DEVICE(&s->gem), nd_table);
}
object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision",
&error_abort);
object_property_set_bool(OBJECT(&s->gem), true, "realized", &err);
- if (err) {
- error_propagate(errp, err);
- return;
- }
sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
- plic_gpios[SIFIVE_U_GEM_IRQ]);
+ qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ));
}
static void riscv_sifive_u_machine_init(MachineClass *mc)
@@ -383,27 +370,3 @@ static void riscv_sifive_u_machine_init(MachineClass *mc)
}
DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
-
-static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
-
- dc->realize = riscv_sifive_u_soc_realize;
- /* Reason: Uses serial_hds in realize function, thus can't be used twice */
- dc->user_creatable = false;
-}
-
-static const TypeInfo riscv_sifive_u_soc_type_info = {
- .name = TYPE_RISCV_U_SOC,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(SiFiveUSoCState),
- .instance_init = riscv_sifive_u_soc_init,
- .class_init = riscv_sifive_u_soc_class_init,
-};
-
-static void riscv_sifive_u_soc_register_types(void)
-{
- type_register_static(&riscv_sifive_u_soc_type_info);
-}
-
-type_init(riscv_sifive_u_soc_register_types)
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
index b0c3798cf27..e87937bc6bf 100644
--- a/hw/riscv/sifive_uart.c
+++ b/hw/riscv/sifive_uart.c
@@ -28,12 +28,24 @@
* 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.
*/
+/* Returns the state of the IP (interrupt pending) register */
+static uint64_t uart_ip(SiFiveUARTState *s)
+{
+ uint64_t ret = 0;
+
+ uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl);
+ uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl);
+
+ if(txcnt != 0)
+ ret |= SIFIVE_UART_IP_TXWM;
+ if(s->rx_fifo_len > rxcnt)
+ ret |= SIFIVE_UART_IP_RXWM;
+
+ return ret;
+}
+
static void update_irq(SiFiveUARTState *s)
{
int cond = 0;
@@ -69,7 +81,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size)
case SIFIVE_UART_IE:
return s->ie;
case SIFIVE_UART_IP:
- return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+ return uart_ip(s);
case SIFIVE_UART_TXCTRL:
return s->txctrl;
case SIFIVE_UART_RXCTRL:
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index c8c056c50b0..b0f0e97a1dd 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -156,8 +156,10 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
g_free(cells);
g_free(nodename);
- qemu_fdt_add_subnode(fdt, "/chosen");
- qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ if (cmdline) {
+ qemu_fdt_add_subnode(fdt, "/chosen");
+ qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
}
static void spike_v1_10_0_board_init(MachineState *machine)
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 248bbdffd3a..66dc4e07c3f 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -34,6 +34,7 @@
#include "hw/riscv/sifive_plic.h"
#include "hw/riscv/sifive_clint.h"
#include "hw/riscv/sifive_test.h"
+#include "hw/riscv/boot.h"
#include "hw/riscv/virt.h"
#include "chardev/char.h"
#include "sysemu/arch_init.h"
@@ -57,47 +58,6 @@ static const struct MemmapEntry {
[VIRT_DRAM] = { 0x80000000, 0x0 },
};
-static uint64_t load_kernel(const char *kernel_filename)
-{
- uint64_t kernel_entry, kernel_high;
-
- if (load_elf(kernel_filename, NULL, NULL,
- &kernel_entry, NULL, &kernel_high,
- 0, EM_RISCV, 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 * MiB);
-
- 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)
{
@@ -254,7 +214,9 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
- qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ if (cmdline) {
+ qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
g_free(nodename);
return fdt;
@@ -272,6 +234,7 @@ static void riscv_virt_board_init(MachineState *machine)
size_t plic_hart_config_len;
int i;
void *fdt;
+ hwaddr firmware_entry;
/* Initialize SOC */
object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc),
@@ -298,20 +261,12 @@ static void riscv_virt_board_init(MachineState *machine)
memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base,
mask_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);
- }
- }
+ /*
+ * combined firmware and kernel: -kernel bbl_vmlimux
+ * separate firmware and kernel: -bios bbl -kernel vmlinux
+ * firmware, kernel and ramdisk: -bios bbl -kernel vmlinux -initrd initramfs
+ */
+ firmware_entry = riscv_load_firmware_kernel_initrd(machine, fdt);
/* reset vector */
uint32_t reset_vec[8] = {
@@ -325,8 +280,8 @@ static void riscv_virt_board_init(MachineState *machine)
#endif
0x00028067, /* jr t0 */
0x00000000,
- memmap[VIRT_DRAM].base, /* start: .dword memmap[VIRT_DRAM].base */
- 0x00000000,
+ firmware_entry, /* .word firmware_entry */
+ firmware_entry >> 32,
/* dtb: */
};
@@ -385,6 +340,8 @@ static void riscv_virt_board_init(MachineState *machine)
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193,
serial_hd(0), DEVICE_LITTLE_ENDIAN);
+
+ g_free(plic_hart_config);
}
static void riscv_virt_board_machine_init(MachineClass *mc)
diff --git a/include/elf.h b/include/elf.h
index 934dbbd6b3a..217c8ff9ca7 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -1285,6 +1285,16 @@ typedef struct {
#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+/* RISC-V ELF Flags. */
+#define EF_RISCV_RVC 0x0001
+#define EF_RISCV_FLOAT_ABI 0x0006
+#define EF_RISCV_FLOAT_ABI_SOFT 0x0000
+#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002
+#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004
+#define EF_RISCV_FLOAT_ABI_QUAD 0x0006
+#define EF_RISCV_RVE 0x0008
+#define EF_RISCV_TSO 0x0010
+
typedef struct elf32_rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
new file mode 100644
index 00000000000..aa30bf1c45b
--- /dev/null
+++ b/include/hw/riscv/boot.h
@@ -0,0 +1,30 @@
+/*
+ * QEMU RISCV firmware and kernel loader interface
+ *
+ * Copyright (c) 2017-2018 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 .
+ */
+
+#ifndef HW_RISCV_BOOT_H
+#define HW_RISCV_BOOT_H
+
+hwaddr riscv_load_firmware(const char *filename);
+hwaddr riscv_load_kernel(const char *filename, void *fdt);
+void riscv_load_initrd(const char *filename, uint64_t mem_size,
+ hwaddr firmware_entry, void *fdt);
+hwaddr riscv_load_firmware_kernel_initrd(MachineState *machine, void *fdt);
+
+#endif
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
index 7b6d8aed968..0d3666bb124 100644
--- a/include/hw/riscv/sifive_e.h
+++ b/include/hw/riscv/sifive_e.h
@@ -19,31 +19,20 @@
#ifndef HW_SIFIVE_E_H
#define HW_SIFIVE_E_H
-#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc"
-#define RISCV_E_SOC(obj) \
- OBJECT_CHECK(SiFiveESoCState, (obj), TYPE_RISCV_E_SOC)
-
-typedef struct SiFiveESoCState {
+typedef struct SiFiveEState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
RISCVHartArrayState cpus;
DeviceState *plic;
-} SiFiveESoCState;
-
-typedef struct SiFiveEState {
- /*< private >*/
- SysBusDevice parent_obj;
-
- /*< public >*/
- SiFiveESoCState soc;
} SiFiveEState;
enum {
SIFIVE_E_DEBUG,
SIFIVE_E_MROM,
SIFIVE_E_OTP,
+ SIFIVE_E_TEST,
SIFIVE_E_CLINT,
SIFIVE_E_PLIC,
SIFIVE_E_AON,
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
index 2f2af7e6862..688cd97f826 100644
--- a/include/hw/riscv/sifive_plic.h
+++ b/include/hw/riscv/sifive_plic.h
@@ -55,7 +55,6 @@ typedef struct SiFivePLICState {
uint32_t *pending;
uint32_t *claimed;
uint32_t *enable;
- QemuMutex lock;
/* config */
char *hart_config;
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
index b6f4c486cc1..bd51c4af3c1 100644
--- a/include/hw/riscv/sifive_prci.h
+++ b/include/hw/riscv/sifive_prci.h
@@ -19,6 +19,34 @@
#ifndef HW_SIFIVE_PRCI_H
#define HW_SIFIVE_PRCI_H
+enum {
+ SIFIVE_PRCI_HFROSCCFG = 0x0,
+ SIFIVE_PRCI_HFXOSCCFG = 0x4,
+ SIFIVE_PRCI_PLLCFG = 0x8,
+ SIFIVE_PRCI_PLLOUTDIV = 0xC
+};
+
+enum {
+ SIFIVE_PRCI_HFROSCCFG_RDY = (1 << 31),
+ SIFIVE_PRCI_HFROSCCFG_EN = (1 << 30)
+};
+
+enum {
+ SIFIVE_PRCI_HFXOSCCFG_RDY = (1 << 31),
+ SIFIVE_PRCI_HFXOSCCFG_EN = (1 << 30)
+};
+
+enum {
+ SIFIVE_PRCI_PLLCFG_PLLSEL = (1 << 16),
+ SIFIVE_PRCI_PLLCFG_REFSEL = (1 << 17),
+ SIFIVE_PRCI_PLLCFG_BYPASS = (1 << 18),
+ SIFIVE_PRCI_PLLCFG_LOCK = (1 << 31)
+};
+
+enum {
+ SIFIVE_PRCI_PLLOUTDIV_DIV1 = (1 << 8)
+};
+
#define TYPE_SIFIVE_PRCI "riscv.sifive.prci"
#define SIFIVE_PRCI(obj) \
@@ -30,6 +58,10 @@ typedef struct SiFivePRCIState {
/*< public >*/
MemoryRegion mmio;
+ uint32_t hfrosccfg;
+ uint32_t hfxosccfg;
+ uint32_t pllcfg;
+ uint32_t plloutdiv;
} SiFivePRCIState;
DeviceState *sifive_prci_create(hwaddr addr);
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
index e8b4d9ffa3f..5aab5351c60 100644
--- a/include/hw/riscv/sifive_u.h
+++ b/include/hw/riscv/sifive_u.h
@@ -19,13 +19,7 @@
#ifndef HW_SIFIVE_U_H
#define HW_SIFIVE_U_H
-#include "hw/net/cadence_gem.h"
-
-#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc"
-#define RISCV_U_SOC(obj) \
- OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC)
-
-typedef struct SiFiveUSoCState {
+typedef struct SiFiveUState {
/*< private >*/
SysBusDevice parent_obj;
@@ -33,14 +27,6 @@ typedef struct SiFiveUSoCState {
RISCVHartArrayState cpus;
DeviceState *plic;
CadenceGEMState gem;
-} SiFiveUSoCState;
-
-typedef struct SiFiveUState {
- /*< private >*/
- SysBusDevice parent_obj;
-
- /*< public >*/
- SiFiveUSoCState soc;
void *fdt;
int fdt_size;
} SiFiveUState;
@@ -48,6 +34,7 @@ typedef struct SiFiveUState {
enum {
SIFIVE_U_DEBUG,
SIFIVE_U_MROM,
+ SIFIVE_U_TEST,
SIFIVE_U_CLINT,
SIFIVE_U_PLIC,
SIFIVE_U_UART0,
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
index 504f18a60f1..c8dc1c57fd0 100644
--- a/include/hw/riscv/sifive_uart.h
+++ b/include/hw/riscv/sifive_uart.h
@@ -43,6 +43,9 @@ enum {
SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */
};
+#define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7)
+#define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7)
+
#define TYPE_SIFIVE_UART "riscv.sifive.uart"
#define SIFIVE_UART(obj) \
diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c
index f137d39d7e8..5f6a941c6c1 100644
--- a/linux-user/riscv/cpu_loop.c
+++ b/linux-user/riscv/cpu_loop.c
@@ -20,6 +20,7 @@
#include "qemu/osdep.h"
#include "qemu.h"
#include "cpu_loop-common.h"
+#include "elf.h"
void cpu_loop(CPURISCVState *env)
{
@@ -53,7 +54,8 @@ void cpu_loop(CPURISCVState *env)
ret = 0;
} else {
ret = do_syscall(env,
- env->gpr[xA7],
+ env->gpr[(env->elf_flags & EF_RISCV_RVE)
+ ? xT0 : xA7],
env->gpr[xA0],
env->gpr[xA1],
env->gpr[xA2],
@@ -113,6 +115,16 @@ void cpu_loop(CPURISCVState *env)
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
+ CPUState *cpu = ENV_GET_CPU(env);
+ TaskState *ts = cpu->opaque;
+ struct image_info *info = ts->info;
+
env->pc = regs->sepc;
env->gpr[xSP] = regs->sp;
+ env->elf_flags = info->elf_flags;
+
+ if ((env->misa & RVE) && !(env->elf_flags & EF_RISCV_RVE)) {
+ fprintf(stderr, "Incompatible ELF: RVE cpu requires RVE ABI binary\n");
+ exit(EXIT_FAILURE);
+ }
}
diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index f598d41891c..83ecc6f799a 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -83,7 +83,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
__put_user(env->fpr[i], &sc->fpr[i]);
}
- uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/
+ uint32_t fcsr = riscv_csr_read(env, CSR_FCSR);
__put_user(fcsr, &sc->fcsr);
}
@@ -159,7 +159,7 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
uint32_t fcsr;
__get_user(fcsr, &sc->fcsr);
- csr_write_helper(env, fcsr, CSR_FCSR);
+ riscv_csr_write(env, CSR_FCSR, fcsr);
}
static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs
index abd0a7cde33..4072abe3e45 100644
--- a/target/riscv/Makefile.objs
+++ b/target/riscv/Makefile.objs
@@ -1 +1 @@
-obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o
+obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o pmp.o
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index d630e8fd6c6..1ecd6d47e36 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -74,8 +74,10 @@ const char * const riscv_intr_names[] = {
"s_external",
"h_external",
"m_external",
- "coprocessor",
- "host"
+ "reserved",
+ "reserved",
+ "reserved",
+ "reserved"
};
typedef struct RISCVCPUInfo {
@@ -86,7 +88,7 @@ typedef struct RISCVCPUInfo {
static void set_misa(CPURISCVState *env, target_ulong misa)
{
- env->misa = misa;
+ env->misa_mask = env->misa = misa;
}
static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
@@ -124,6 +126,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
set_resetvec(env, DEFAULT_RSTVEC);
set_feature(env, RISCV_FEATURE_MMU);
+ set_feature(env, RISCV_FEATURE_PMP);
}
static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
@@ -133,6 +136,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
set_resetvec(env, DEFAULT_RSTVEC);
set_feature(env, RISCV_FEATURE_MMU);
+ set_feature(env, RISCV_FEATURE_PMP);
}
static void rv32imacu_nommu_cpu_init(Object *obj)
@@ -141,6 +145,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
set_resetvec(env, DEFAULT_RSTVEC);
+ set_feature(env, RISCV_FEATURE_PMP);
}
#elif defined(TARGET_RISCV64)
@@ -152,6 +157,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
set_resetvec(env, DEFAULT_RSTVEC);
set_feature(env, RISCV_FEATURE_MMU);
+ set_feature(env, RISCV_FEATURE_PMP);
}
static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
@@ -161,6 +167,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
set_resetvec(env, DEFAULT_RSTVEC);
set_feature(env, RISCV_FEATURE_MMU);
+ set_feature(env, RISCV_FEATURE_PMP);
}
static void rv64imacu_nommu_cpu_init(Object *obj)
@@ -169,6 +176,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj)
set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
set_resetvec(env, DEFAULT_RSTVEC);
+ set_feature(env, RISCV_FEATURE_PMP);
}
#endif
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 34abc383e3d..ee678900f19 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -24,12 +24,12 @@
#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
#if defined(TARGET_RISCV64)
#define TARGET_LONG_BITS 64
-#define TARGET_PHYS_ADDR_SPACE_BITS 50
-#define TARGET_VIRT_ADDR_SPACE_BITS 39
+#define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */
+#define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */
#elif defined(TARGET_RISCV32)
#define TARGET_LONG_BITS 32
-#define TARGET_PHYS_ADDR_SPACE_BITS 34
-#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */
+#define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */
#endif
#define TCG_GUEST_DEFAULT_MO 0
@@ -83,9 +83,11 @@
/* S extension denotes that Supervisor mode exists, however it is possible
to have a core that support S mode but does not have an MMU and there
is currently no bit in misa to indicate whether an MMU exists or not
- so a cpu features bitfield is required */
+ so a cpu features bitfield is required, likewise for optional PMP support */
enum {
- RISCV_FEATURE_MMU
+ RISCV_FEATURE_MMU,
+ RISCV_FEATURE_PMP,
+ RISCV_FEATURE_MISA
};
#define USER_VERSION_2_02_0 0x00020200
@@ -117,22 +119,33 @@ struct CPURISCVState {
target_ulong user_ver;
target_ulong priv_ver;
target_ulong misa;
+ target_ulong misa_mask;
uint32_t features;
+#ifdef CONFIG_USER_ONLY
+ uint32_t elf_flags;
+#endif
+
#ifndef CONFIG_USER_ONLY
target_ulong priv;
target_ulong resetvec;
target_ulong mhartid;
target_ulong mstatus;
+
/*
* CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
- * by I/O threads and other vCPUs, so hold the iothread mutex before
- * operating on it. CPU_INTERRUPT_HARD should be in effect iff this is
- * non-zero. Use riscv_cpu_set_local_interrupt.
+ * by I/O threads. It should be read with atomic_read. It should be updated
+ * using riscv_cpu_update_mip with the iothread mutex held. The iothread
+ * mutex must be held because mip must be consistent with the CPU inturrept
+ * state. riscv_cpu_update_mip calls cpu_interrupt or cpu_reset_interrupt
+ * wuth the invariant that CPU_INTERRUPT_HARD is set iff mip is non-zero.
+ * mip is 32-bits to allow atomic_read on 32-bit hosts.
*/
- uint32_t mip; /* allow atomic_read for >= 32-bit hosts */
+ uint32_t mip;
+ uint32_t miclaim;
+
target_ulong mie;
target_ulong mideleg;
@@ -247,28 +260,30 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
uintptr_t retaddr);
int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
int rw, int mmu_idx);
-
char *riscv_isa_string(RISCVCPU *cpu);
void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
-#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
-#define cpu_signal_handler cpu_riscv_signal_handler
+#define cpu_signal_handler riscv_cpu_signal_handler
#define cpu_list riscv_cpu_list
#define cpu_mmu_index riscv_cpu_mmu_index
-void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
+#ifndef CONFIG_USER_ONLY
+int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts);
+uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
+#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
+#endif
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
void riscv_translate_init(void);
-RISCVCPU *cpu_riscv_init(const char *cpu_model);
-int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
-void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
- uint32_t exception, uintptr_t pc);
+int riscv_cpu_signal_handler(int host_signum, void *pinfo, void *puc);
+void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
+ uint32_t exception, uintptr_t pc);
-target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
-void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
+target_ulong riscv_cpu_get_fflags(CPURISCVState *env);
+void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
-#define TB_FLAGS_MMU_MASK 3
-#define TB_FLAGS_FP_ENABLE MSTATUS_FS
+#define TB_FLAGS_MMU_MASK 3
+#define TB_FLAGS_MSTATUS_FS MSTATUS_FS
static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
@@ -276,19 +291,45 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
*pc = env->pc;
*cs_base = 0;
#ifdef CONFIG_USER_ONLY
- *flags = TB_FLAGS_FP_ENABLE;
+ *flags = TB_FLAGS_MSTATUS_FS;
#else
*flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
#endif
}
-void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
- target_ulong csrno);
-target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask);
-#ifndef CONFIG_USER_ONLY
-void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value);
-#endif
+static inline void riscv_csr_write(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ riscv_csrrw(env, csrno, NULL, val, -1);
+}
+
+static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno)
+{
+ target_ulong val = 0;
+ riscv_csrrw(env, csrno, &val, 0, 0);
+ return val;
+}
+
+typedef int (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno);
+typedef int (*riscv_csr_read_fn)(CPURISCVState *env, int csrno,
+ target_ulong *ret_value);
+typedef int (*riscv_csr_write_fn)(CPURISCVState *env, int csrno,
+ target_ulong new_value);
+typedef int (*riscv_csr_op_fn)(CPURISCVState *env, int csrno,
+ target_ulong *ret_value, target_ulong new_value, target_ulong write_mask);
+
+typedef struct {
+ riscv_csr_predicate_fn predicate;
+ riscv_csr_read_fn read;
+ riscv_csr_write_fn write;
+ riscv_csr_op_fn op;
+} riscv_csr_operations;
+
+void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops);
+void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops);
#include "exec/cpu-all.h"
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 64aa097181f..7afcb2468d8 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -6,242 +6,283 @@
(((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \
(target_ulong)(mask)))
-#define PGSHIFT 12
-
-#define FSR_RD_SHIFT 5
-#define FSR_RD (0x7 << FSR_RD_SHIFT)
-
-#define FPEXC_NX 0x01
-#define FPEXC_UF 0x02
-#define FPEXC_OF 0x04
-#define FPEXC_DZ 0x08
-#define FPEXC_NV 0x10
-
-#define FSR_AEXC_SHIFT 0
-#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT)
-#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT)
-#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT)
-#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT)
-#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT)
-#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
-
-/* CSR numbers */
-#define CSR_FFLAGS 0x1
-#define CSR_FRM 0x2
-#define CSR_FCSR 0x3
-#define CSR_CYCLE 0xc00
-#define CSR_TIME 0xc01
-#define CSR_INSTRET 0xc02
-#define CSR_HPMCOUNTER3 0xc03
-#define CSR_HPMCOUNTER4 0xc04
-#define CSR_HPMCOUNTER5 0xc05
-#define CSR_HPMCOUNTER6 0xc06
-#define CSR_HPMCOUNTER7 0xc07
-#define CSR_HPMCOUNTER8 0xc08
-#define CSR_HPMCOUNTER9 0xc09
-#define CSR_HPMCOUNTER10 0xc0a
-#define CSR_HPMCOUNTER11 0xc0b
-#define CSR_HPMCOUNTER12 0xc0c
-#define CSR_HPMCOUNTER13 0xc0d
-#define CSR_HPMCOUNTER14 0xc0e
-#define CSR_HPMCOUNTER15 0xc0f
-#define CSR_HPMCOUNTER16 0xc10
-#define CSR_HPMCOUNTER17 0xc11
-#define CSR_HPMCOUNTER18 0xc12
-#define CSR_HPMCOUNTER19 0xc13
-#define CSR_HPMCOUNTER20 0xc14
-#define CSR_HPMCOUNTER21 0xc15
-#define CSR_HPMCOUNTER22 0xc16
-#define CSR_HPMCOUNTER23 0xc17
-#define CSR_HPMCOUNTER24 0xc18
-#define CSR_HPMCOUNTER25 0xc19
-#define CSR_HPMCOUNTER26 0xc1a
-#define CSR_HPMCOUNTER27 0xc1b
-#define CSR_HPMCOUNTER28 0xc1c
-#define CSR_HPMCOUNTER29 0xc1d
-#define CSR_HPMCOUNTER30 0xc1e
-#define CSR_HPMCOUNTER31 0xc1f
-#define CSR_SSTATUS 0x100
-#define CSR_SIE 0x104
-#define CSR_STVEC 0x105
-#define CSR_SCOUNTEREN 0x106
-#define CSR_SSCRATCH 0x140
-#define CSR_SEPC 0x141
-#define CSR_SCAUSE 0x142
-#define CSR_SBADADDR 0x143
-#define CSR_SIP 0x144
-#define CSR_SPTBR 0x180
-#define CSR_SATP 0x180
-#define CSR_MSTATUS 0x300
-#define CSR_MISA 0x301
-#define CSR_MEDELEG 0x302
-#define CSR_MIDELEG 0x303
-#define CSR_MIE 0x304
-#define CSR_MTVEC 0x305
-#define CSR_MCOUNTEREN 0x306
-#define CSR_MSCRATCH 0x340
-#define CSR_MEPC 0x341
-#define CSR_MCAUSE 0x342
-#define CSR_MBADADDR 0x343
-#define CSR_MIP 0x344
-#define CSR_PMPCFG0 0x3a0
-#define CSR_PMPCFG1 0x3a1
-#define CSR_PMPCFG2 0x3a2
-#define CSR_PMPCFG3 0x3a3
-#define CSR_PMPADDR0 0x3b0
-#define CSR_PMPADDR1 0x3b1
-#define CSR_PMPADDR2 0x3b2
-#define CSR_PMPADDR3 0x3b3
-#define CSR_PMPADDR4 0x3b4
-#define CSR_PMPADDR5 0x3b5
-#define CSR_PMPADDR6 0x3b6
-#define CSR_PMPADDR7 0x3b7
-#define CSR_PMPADDR8 0x3b8
-#define CSR_PMPADDR9 0x3b9
-#define CSR_PMPADDR10 0x3ba
-#define CSR_PMPADDR11 0x3bb
-#define CSR_PMPADDR12 0x3bc
-#define CSR_PMPADDR13 0x3bd
-#define CSR_PMPADDR14 0x3be
-#define CSR_PMPADDR15 0x3bf
-#define CSR_TSELECT 0x7a0
-#define CSR_TDATA1 0x7a1
-#define CSR_TDATA2 0x7a2
-#define CSR_TDATA3 0x7a3
-#define CSR_DCSR 0x7b0
-#define CSR_DPC 0x7b1
-#define CSR_DSCRATCH 0x7b2
-#define CSR_MCYCLE 0xb00
-#define CSR_MINSTRET 0xb02
-#define CSR_MHPMCOUNTER3 0xb03
-#define CSR_MHPMCOUNTER4 0xb04
-#define CSR_MHPMCOUNTER5 0xb05
-#define CSR_MHPMCOUNTER6 0xb06
-#define CSR_MHPMCOUNTER7 0xb07
-#define CSR_MHPMCOUNTER8 0xb08
-#define CSR_MHPMCOUNTER9 0xb09
-#define CSR_MHPMCOUNTER10 0xb0a
-#define CSR_MHPMCOUNTER11 0xb0b
-#define CSR_MHPMCOUNTER12 0xb0c
-#define CSR_MHPMCOUNTER13 0xb0d
-#define CSR_MHPMCOUNTER14 0xb0e
-#define CSR_MHPMCOUNTER15 0xb0f
-#define CSR_MHPMCOUNTER16 0xb10
-#define CSR_MHPMCOUNTER17 0xb11
-#define CSR_MHPMCOUNTER18 0xb12
-#define CSR_MHPMCOUNTER19 0xb13
-#define CSR_MHPMCOUNTER20 0xb14
-#define CSR_MHPMCOUNTER21 0xb15
-#define CSR_MHPMCOUNTER22 0xb16
-#define CSR_MHPMCOUNTER23 0xb17
-#define CSR_MHPMCOUNTER24 0xb18
-#define CSR_MHPMCOUNTER25 0xb19
-#define CSR_MHPMCOUNTER26 0xb1a
-#define CSR_MHPMCOUNTER27 0xb1b
-#define CSR_MHPMCOUNTER28 0xb1c
-#define CSR_MHPMCOUNTER29 0xb1d
-#define CSR_MHPMCOUNTER30 0xb1e
-#define CSR_MHPMCOUNTER31 0xb1f
-#define CSR_MUCOUNTEREN 0x320
-#define CSR_MSCOUNTEREN 0x321
-#define CSR_MHPMEVENT3 0x323
-#define CSR_MHPMEVENT4 0x324
-#define CSR_MHPMEVENT5 0x325
-#define CSR_MHPMEVENT6 0x326
-#define CSR_MHPMEVENT7 0x327
-#define CSR_MHPMEVENT8 0x328
-#define CSR_MHPMEVENT9 0x329
-#define CSR_MHPMEVENT10 0x32a
-#define CSR_MHPMEVENT11 0x32b
-#define CSR_MHPMEVENT12 0x32c
-#define CSR_MHPMEVENT13 0x32d
-#define CSR_MHPMEVENT14 0x32e
-#define CSR_MHPMEVENT15 0x32f
-#define CSR_MHPMEVENT16 0x330
-#define CSR_MHPMEVENT17 0x331
-#define CSR_MHPMEVENT18 0x332
-#define CSR_MHPMEVENT19 0x333
-#define CSR_MHPMEVENT20 0x334
-#define CSR_MHPMEVENT21 0x335
-#define CSR_MHPMEVENT22 0x336
-#define CSR_MHPMEVENT23 0x337
-#define CSR_MHPMEVENT24 0x338
-#define CSR_MHPMEVENT25 0x339
-#define CSR_MHPMEVENT26 0x33a
-#define CSR_MHPMEVENT27 0x33b
-#define CSR_MHPMEVENT28 0x33c
-#define CSR_MHPMEVENT29 0x33d
-#define CSR_MHPMEVENT30 0x33e
-#define CSR_MHPMEVENT31 0x33f
-#define CSR_MVENDORID 0xf11
-#define CSR_MARCHID 0xf12
-#define CSR_MIMPID 0xf13
-#define CSR_MHARTID 0xf14
-#define CSR_CYCLEH 0xc80
-#define CSR_TIMEH 0xc81
-#define CSR_INSTRETH 0xc82
-#define CSR_HPMCOUNTER3H 0xc83
-#define CSR_HPMCOUNTER4H 0xc84
-#define CSR_HPMCOUNTER5H 0xc85
-#define CSR_HPMCOUNTER6H 0xc86
-#define CSR_HPMCOUNTER7H 0xc87
-#define CSR_HPMCOUNTER8H 0xc88
-#define CSR_HPMCOUNTER9H 0xc89
-#define CSR_HPMCOUNTER10H 0xc8a
-#define CSR_HPMCOUNTER11H 0xc8b
-#define CSR_HPMCOUNTER12H 0xc8c
-#define CSR_HPMCOUNTER13H 0xc8d
-#define CSR_HPMCOUNTER14H 0xc8e
-#define CSR_HPMCOUNTER15H 0xc8f
-#define CSR_HPMCOUNTER16H 0xc90
-#define CSR_HPMCOUNTER17H 0xc91
-#define CSR_HPMCOUNTER18H 0xc92
-#define CSR_HPMCOUNTER19H 0xc93
-#define CSR_HPMCOUNTER20H 0xc94
-#define CSR_HPMCOUNTER21H 0xc95
-#define CSR_HPMCOUNTER22H 0xc96
-#define CSR_HPMCOUNTER23H 0xc97
-#define CSR_HPMCOUNTER24H 0xc98
-#define CSR_HPMCOUNTER25H 0xc99
-#define CSR_HPMCOUNTER26H 0xc9a
-#define CSR_HPMCOUNTER27H 0xc9b
-#define CSR_HPMCOUNTER28H 0xc9c
-#define CSR_HPMCOUNTER29H 0xc9d
-#define CSR_HPMCOUNTER30H 0xc9e
-#define CSR_HPMCOUNTER31H 0xc9f
-#define CSR_MCYCLEH 0xb80
-#define CSR_MINSTRETH 0xb82
-#define CSR_MHPMCOUNTER3H 0xb83
-#define CSR_MHPMCOUNTER4H 0xb84
-#define CSR_MHPMCOUNTER5H 0xb85
-#define CSR_MHPMCOUNTER6H 0xb86
-#define CSR_MHPMCOUNTER7H 0xb87
-#define CSR_MHPMCOUNTER8H 0xb88
-#define CSR_MHPMCOUNTER9H 0xb89
-#define CSR_MHPMCOUNTER10H 0xb8a
-#define CSR_MHPMCOUNTER11H 0xb8b
-#define CSR_MHPMCOUNTER12H 0xb8c
-#define CSR_MHPMCOUNTER13H 0xb8d
-#define CSR_MHPMCOUNTER14H 0xb8e
-#define CSR_MHPMCOUNTER15H 0xb8f
-#define CSR_MHPMCOUNTER16H 0xb90
-#define CSR_MHPMCOUNTER17H 0xb91
-#define CSR_MHPMCOUNTER18H 0xb92
-#define CSR_MHPMCOUNTER19H 0xb93
-#define CSR_MHPMCOUNTER20H 0xb94
-#define CSR_MHPMCOUNTER21H 0xb95
-#define CSR_MHPMCOUNTER22H 0xb96
-#define CSR_MHPMCOUNTER23H 0xb97
-#define CSR_MHPMCOUNTER24H 0xb98
-#define CSR_MHPMCOUNTER25H 0xb99
-#define CSR_MHPMCOUNTER26H 0xb9a
-#define CSR_MHPMCOUNTER27H 0xb9b
-#define CSR_MHPMCOUNTER28H 0xb9c
-#define CSR_MHPMCOUNTER29H 0xb9d
-#define CSR_MHPMCOUNTER30H 0xb9e
-#define CSR_MHPMCOUNTER31H 0xb9f
-
-/* mstatus bits */
+/* Floating point round mode */
+#define FSR_RD_SHIFT 5
+#define FSR_RD (0x7 << FSR_RD_SHIFT)
+
+/* Floating point accrued exception flags */
+#define FPEXC_NX 0x01
+#define FPEXC_UF 0x02
+#define FPEXC_OF 0x04
+#define FPEXC_DZ 0x08
+#define FPEXC_NV 0x10
+
+/* Floating point status register bits */
+#define FSR_AEXC_SHIFT 0
+#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT)
+#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT)
+#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT)
+#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT)
+#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT)
+#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
+
+/* Control and Status Registers */
+
+/* User Trap Setup */
+#define CSR_USTATUS 0x000
+#define CSR_UIE 0x004
+#define CSR_UTVEC 0x005
+
+/* User Trap Handling */
+#define CSR_USCRATCH 0x040
+#define CSR_UEPC 0x041
+#define CSR_UCAUSE 0x042
+#define CSR_UTVAL 0x043
+#define CSR_UIP 0x044
+
+/* User Floating-Point CSRs */
+#define CSR_FFLAGS 0x001
+#define CSR_FRM 0x002
+#define CSR_FCSR 0x003
+
+/* User Timers and Counters */
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+
+/* Machine Timers and Counters */
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+
+/* Machine Information Registers */
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+
+/* Machine Trap Setup */
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MCOUNTEREN 0x306
+
+/* Legacy Counter Setup (priv v1.9.1) */
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+
+/* Machine Trap Handling */
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MBADADDR 0x343
+#define CSR_MIP 0x344
+
+/* Supervisor Trap Setup */
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+
+/* Supervisor Trap Handling */
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_SBADADDR 0x143
+#define CSR_SIP 0x144
+
+/* Supervisor Protection and Translation */
+#define CSR_SPTBR 0x180
+#define CSR_SATP 0x180
+
+/* Physical Memory Protection */
+#define CSR_PMPCFG0 0x3a0
+#define CSR_PMPCFG1 0x3a1
+#define CSR_PMPCFG2 0x3a2
+#define CSR_PMPCFG3 0x3a3
+#define CSR_PMPADDR0 0x3b0
+#define CSR_PMPADDR1 0x3b1
+#define CSR_PMPADDR2 0x3b2
+#define CSR_PMPADDR3 0x3b3
+#define CSR_PMPADDR4 0x3b4
+#define CSR_PMPADDR5 0x3b5
+#define CSR_PMPADDR6 0x3b6
+#define CSR_PMPADDR7 0x3b7
+#define CSR_PMPADDR8 0x3b8
+#define CSR_PMPADDR9 0x3b9
+#define CSR_PMPADDR10 0x3ba
+#define CSR_PMPADDR11 0x3bb
+#define CSR_PMPADDR12 0x3bc
+#define CSR_PMPADDR13 0x3bd
+#define CSR_PMPADDR14 0x3be
+#define CSR_PMPADDR15 0x3bf
+
+/* Debug/Trace Registers (shared with Debug Mode) */
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+
+/* Debug Mode Registers */
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+
+/* Performance Counters */
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+
+/* mstatus CSR bits */
#define MSTATUS_UIE 0x00000001
#define MSTATUS_SIE 0x00000002
#define MSTATUS_HIE 0x00000004
@@ -270,13 +311,24 @@
#define MSTATUS32_SD 0x80000000
#define MSTATUS64_SD 0x8000000000000000ULL
+#define MISA32_MXL 0xC0000000
+#define MISA64_MXL 0xC000000000000000ULL
+
+#define MXL_RV32 1
+#define MXL_RV64 2
+#define MXL_RV128 3
+
#if defined(TARGET_RISCV32)
#define MSTATUS_SD MSTATUS32_SD
+#define MISA_MXL MISA32_MXL
+#define MXL_VAL MXL_RV32
#elif defined(TARGET_RISCV64)
#define MSTATUS_SD MSTATUS64_SD
+#define MISA_MXL MISA64_MXL
+#define MXL_VAL MXL_RV64
#endif
-/* sstatus bits */
+/* sstatus CSR bits */
#define SSTATUS_UIE 0x00000001
#define SSTATUS_SIE 0x00000002
#define SSTATUS_UPIE 0x00000010
@@ -297,83 +349,71 @@
#define SSTATUS_SD SSTATUS64_SD
#endif
-/* irqs */
-#define MIP_SSIP (1 << IRQ_S_SOFT)
-#define MIP_HSIP (1 << IRQ_H_SOFT)
-#define MIP_MSIP (1 << IRQ_M_SOFT)
-#define MIP_STIP (1 << IRQ_S_TIMER)
-#define MIP_HTIP (1 << IRQ_H_TIMER)
-#define MIP_MTIP (1 << IRQ_M_TIMER)
-#define MIP_SEIP (1 << IRQ_S_EXT)
-#define MIP_HEIP (1 << IRQ_H_EXT)
-#define MIP_MEIP (1 << IRQ_M_EXT)
-
-#define SIP_SSIP MIP_SSIP
-#define SIP_STIP MIP_STIP
-#define SIP_SEIP MIP_SEIP
-
+/* Privilege modes */
#define PRV_U 0
#define PRV_S 1
#define PRV_H 2
#define PRV_M 3
-/* privileged ISA 1.9.1 VM modes (mstatus.vm) */
-#define VM_1_09_MBARE 0
-#define VM_1_09_MBB 1
-#define VM_1_09_MBBID 2
-#define VM_1_09_SV32 8
-#define VM_1_09_SV39 9
-#define VM_1_09_SV48 10
-
-/* privileged ISA 1.10.0 VM modes (satp.mode) */
-#define VM_1_10_MBARE 0
-#define VM_1_10_SV32 1
-#define VM_1_10_SV39 8
-#define VM_1_10_SV48 9
-#define VM_1_10_SV57 10
-#define VM_1_10_SV64 11
-
-/* privileged ISA interrupt causes */
-#define IRQ_U_SOFT 0 /* since: priv-1.10 */
-#define IRQ_S_SOFT 1
-#define IRQ_H_SOFT 2 /* until: priv-1.9.1 */
-#define IRQ_M_SOFT 3 /* until: priv-1.9.1 */
-#define IRQ_U_TIMER 4 /* since: priv-1.10 */
-#define IRQ_S_TIMER 5
-#define IRQ_H_TIMER 6 /* until: priv-1.9.1 */
-#define IRQ_M_TIMER 7 /* until: priv-1.9.1 */
-#define IRQ_U_EXT 8 /* since: priv-1.10 */
-#define IRQ_S_EXT 9
-#define IRQ_H_EXT 10 /* until: priv-1.9.1 */
-#define IRQ_M_EXT 11 /* until: priv-1.9.1 */
-#define IRQ_X_COP 12 /* non-standard */
-
-/* Default addresses */
-#define DEFAULT_RSTVEC 0x00001000
-
-/* RV32 satp field masks */
-#define SATP32_MODE 0x80000000
-#define SATP32_ASID 0x7fc00000
-#define SATP32_PPN 0x003fffff
-
-/* RV64 satp field masks */
-#define SATP64_MODE 0xF000000000000000ULL
-#define SATP64_ASID 0x0FFFF00000000000ULL
-#define SATP64_PPN 0x00000FFFFFFFFFFFULL
+/* RV32 satp CSR field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN 0x003fffff
+
+/* RV64 satp CSR field masks */
+#define SATP64_MODE 0xF000000000000000ULL
+#define SATP64_ASID 0x0FFFF00000000000ULL
+#define SATP64_PPN 0x00000FFFFFFFFFFFULL
#if defined(TARGET_RISCV32)
-#define SATP_MODE SATP32_MODE
-#define SATP_ASID SATP32_ASID
-#define SATP_PPN SATP32_PPN
+#define SATP_MODE SATP32_MODE
+#define SATP_ASID SATP32_ASID
+#define SATP_PPN SATP32_PPN
#endif
#if defined(TARGET_RISCV64)
-#define SATP_MODE SATP64_MODE
-#define SATP_ASID SATP64_ASID
-#define SATP_PPN SATP64_PPN
+#define SATP_MODE SATP64_MODE
+#define SATP_ASID SATP64_ASID
+#define SATP_PPN SATP64_PPN
#endif
-/* RISCV Exception Codes */
-#define EXCP_NONE -1 /* not a real RISCV exception code */
+/* VM modes (mstatus.vm) privileged ISA 1.9.1 */
+#define VM_1_09_MBARE 0
+#define VM_1_09_MBB 1
+#define VM_1_09_MBBID 2
+#define VM_1_09_SV32 8
+#define VM_1_09_SV39 9
+#define VM_1_09_SV48 10
+
+/* VM modes (satp.mode) privileged ISA 1.10 */
+#define VM_1_10_MBARE 0
+#define VM_1_10_SV32 1
+#define VM_1_10_SV39 8
+#define VM_1_10_SV48 9
+#define VM_1_10_SV57 10
+#define VM_1_10_SV64 11
+
+/* Page table entry (PTE) fields */
+#define PTE_V 0x001 /* Valid */
+#define PTE_R 0x002 /* Read */
+#define PTE_W 0x004 /* Write */
+#define PTE_X 0x008 /* Execute */
+#define PTE_U 0x010 /* User */
+#define PTE_G 0x020 /* Global */
+#define PTE_A 0x040 /* Accessed */
+#define PTE_D 0x080 /* Dirty */
+#define PTE_SOFT 0x300 /* Reserved for Software */
+
+/* Page table PPN shift amount */
+#define PTE_PPN_SHIFT 10
+
+/* Leaf page shift amount */
+#define PGSHIFT 12
+
+/* Default Reset Vector adress */
+#define DEFAULT_RSTVEC 0x1000
+
+/* Exception causes */
+#define EXCP_NONE -1 /* sentinel value */
#define RISCV_EXCP_INST_ADDR_MIS 0x0
#define RISCV_EXCP_INST_ACCESS_FAULT 0x1
#define RISCV_EXCP_ILLEGAL_INST 0x2
@@ -382,9 +422,7 @@
#define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5
#define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6
#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7
-#define RISCV_EXCP_U_ECALL 0x8 /* for convenience, report all
- ECALLs as this, handler
- fixes */
+#define RISCV_EXCP_U_ECALL 0x8
#define RISCV_EXCP_S_ECALL 0x9
#define RISCV_EXCP_H_ECALL 0xa
#define RISCV_EXCP_M_ECALL 0xb
@@ -395,17 +433,35 @@
#define RISCV_EXCP_INT_FLAG 0x80000000
#define RISCV_EXCP_INT_MASK 0x7fffffff
-/* page table entry (PTE) fields */
-#define PTE_V 0x001 /* Valid */
-#define PTE_R 0x002 /* Read */
-#define PTE_W 0x004 /* Write */
-#define PTE_X 0x008 /* Execute */
-#define PTE_U 0x010 /* User */
-#define PTE_G 0x020 /* Global */
-#define PTE_A 0x040 /* Accessed */
-#define PTE_D 0x080 /* Dirty */
-#define PTE_SOFT 0x300 /* Reserved for Software */
+/* Interrupt causes */
+#define IRQ_U_SOFT 0
+#define IRQ_S_SOFT 1
+#define IRQ_H_SOFT 2 /* reserved */
+#define IRQ_M_SOFT 3
+#define IRQ_U_TIMER 4
+#define IRQ_S_TIMER 5
+#define IRQ_H_TIMER 6 /* reserved */
+#define IRQ_M_TIMER 7
+#define IRQ_U_EXT 8
+#define IRQ_S_EXT 9
+#define IRQ_H_EXT 10 /* reserved */
+#define IRQ_M_EXT 11
-#define PTE_PPN_SHIFT 10
+/* mip masks */
+#define MIP_USIP (1 << IRQ_U_SOFT)
+#define MIP_SSIP (1 << IRQ_S_SOFT)
+#define MIP_HSIP (1 << IRQ_H_SOFT)
+#define MIP_MSIP (1 << IRQ_M_SOFT)
+#define MIP_UTIP (1 << IRQ_U_TIMER)
+#define MIP_STIP (1 << IRQ_S_TIMER)
+#define MIP_HTIP (1 << IRQ_H_TIMER)
+#define MIP_MTIP (1 << IRQ_M_TIMER)
+#define MIP_UEIP (1 << IRQ_U_EXT)
+#define MIP_SEIP (1 << IRQ_S_EXT)
+#define MIP_HEIP (1 << IRQ_H_EXT)
+#define MIP_MEIP (1 << IRQ_M_EXT)
-#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
+/* sip masks */
+#define SIP_SSIP MIP_SSIP
+#define SIP_STIP MIP_STIP
+#define SIP_SEIP MIP_SEIP
diff --git a/target/riscv/helper.c b/target/riscv/cpu_helper.c
similarity index 63%
rename from target/riscv/helper.c
rename to target/riscv/cpu_helper.c
index 29e1a603dc3..c6f3d57b49f 100644
--- a/target/riscv/helper.c
+++ b/target/riscv/cpu_helper.c
@@ -1,5 +1,5 @@
/*
- * RISC-V emulation helpers for qemu.
+ * RISC-V CPU helpers for qemu.
*
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
* Copyright (c) 2017-2018 SiFive, Inc.
@@ -22,8 +22,7 @@
#include "cpu.h"
#include "exec/exec-all.h"
#include "tcg-op.h"
-
-#define RISCV_DEBUG_INTERRUPT 0
+#include "trace.h"
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
{
@@ -35,28 +34,18 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
}
#ifndef CONFIG_USER_ONLY
-/*
- * Return RISC-V IRQ number if an interrupt should be taken, else -1.
- * Used in cpu-exec.c
- *
- * Adapted from Spike's processor_t::take_interrupt()
- */
-static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env)
+static int riscv_cpu_local_irq_pending(CPURISCVState *env)
{
- target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie;
-
- target_ulong mie = get_field(env->mstatus, MSTATUS_MIE);
- target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie);
- target_ulong enabled_interrupts = pending_interrupts &
- ~env->mideleg & -m_enabled;
-
- target_ulong sie = get_field(env->mstatus, MSTATUS_SIE);
- target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie);
- enabled_interrupts |= pending_interrupts & env->mideleg &
- -s_enabled;
-
- if (enabled_interrupts) {
- return ctz64(enabled_interrupts); /* since non-zero */
+ target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE);
+ target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE);
+ target_ulong pending = atomic_read(&env->mip) & env->mie;
+ target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie);
+ target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie);
+ target_ulong irqs = (pending & ~env->mideleg & -mie) |
+ (pending & env->mideleg & -sie);
+
+ if (irqs) {
+ return ctz64(irqs); /* since non-zero */
} else {
return EXCP_NONE; /* indicates no pending interrupt */
}
@@ -69,7 +58,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
if (interrupt_request & CPU_INTERRUPT_HARD) {
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- int interruptno = riscv_cpu_hw_interrupts_pending(env);
+ int interruptno = riscv_cpu_local_irq_pending(env);
if (interruptno >= 0) {
cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno;
riscv_cpu_do_interrupt(cs);
@@ -82,6 +71,50 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
#if !defined(CONFIG_USER_ONLY)
+int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts)
+{
+ CPURISCVState *env = &cpu->env;
+ if (env->miclaim & interrupts) {
+ return -1;
+ } else {
+ env->miclaim |= interrupts;
+ return 0;
+ }
+}
+
+/* iothread_mutex must be held */
+uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
+{
+ CPURISCVState *env = &cpu->env;
+ uint32_t old, new, cmp = atomic_read(&env->mip);
+
+ do {
+ old = cmp;
+ new = (old & ~mask) | (value & mask);
+ cmp = atomic_cmpxchg(&env->mip, old, new);
+ } while (old != cmp);
+
+ if (new) {
+ cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+ }
+
+ return old;
+}
+
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
+{
+ if (newpriv > PRV_M) {
+ g_assert_not_reached();
+ }
+ if (newpriv == PRV_H) {
+ newpriv = PRV_U;
+ }
+ /* tlb_flush is unnecessary as mode is contained in mmu_idx */
+ env->priv = newpriv;
+}
+
/* get_physical_address - get the physical address for this virtual address
*
* Do a page table walk to obtain the physical address corresponding to a
@@ -185,16 +218,39 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
#endif
target_ulong ppn = pte >> PTE_PPN_SHIFT;
- if (PTE_TABLE(pte)) { /* next level of page table */
+ if (!(pte & PTE_V)) {
+ /* Invalid PTE */
+ return TRANSLATE_FAIL;
+ } else if (!(pte & (PTE_R | PTE_W | PTE_X))) {
+ /* Inner PTE, continue walking */
base = ppn << PGSHIFT;
- } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) {
- break;
- } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
- break;
- } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) :
- access_type == MMU_DATA_LOAD ? !(pte & PTE_R) &&
- !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) {
- break;
+ } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
+ /* Reserved leaf PTE flags: PTE_W */
+ return TRANSLATE_FAIL;
+ } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
+ /* Reserved leaf PTE flags: PTE_W + PTE_X */
+ return TRANSLATE_FAIL;
+ } else if ((pte & PTE_U) && ((mode != PRV_U) &&
+ (!sum || access_type == MMU_INST_FETCH))) {
+ /* User PTE flags when not U mode and mstatus.SUM is not set,
+ or the access type is an instruction fetch */
+ return TRANSLATE_FAIL;
+ } else if (!(pte & PTE_U) && (mode != PRV_S)) {
+ /* Supervisor PTE flags when not S mode */
+ return TRANSLATE_FAIL;
+ } else if (ppn & ((1ULL << ptshift) - 1)) {
+ /* Misaligned PPN */
+ return TRANSLATE_FAIL;
+ } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
+ ((pte & PTE_X) && mxr))) {
+ /* Read access check failed */
+ return TRANSLATE_FAIL;
+ } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
+ /* Write access check failed */
+ return TRANSLATE_FAIL;
+ } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
+ /* Fetch access check failed */
+ return TRANSLATE_FAIL;
} else {
/* if necessary, set accessed and dirty bits. */
target_ulong updated_pte = pte | PTE_A |
@@ -202,16 +258,19 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
/* Page table updates need to be atomic with MTTCG enabled */
if (updated_pte != pte) {
- /* if accessed or dirty bits need updating, and the PTE is
- * in RAM, then we do so atomically with a compare and swap.
- * if the PTE is in IO space, then it can't be updated.
- * if the PTE changed, then we must re-walk the page table
- as the PTE is no longer valid */
+ /*
+ * - if accessed or dirty bits need updating, and the PTE is
+ * in RAM, then we do so atomically with a compare and swap.
+ * - if the PTE is in IO space or ROM, then it can't be updated
+ * and we return TRANSLATE_FAIL.
+ * - if the PTE changed by the time we went to update it, then
+ * it is no longer valid and we must re-walk the page table.
+ */
MemoryRegion *mr;
hwaddr l = sizeof(target_ulong), addr1;
mr = address_space_translate(cs->as, pte_addr,
&addr1, &l, false, MEMTXATTRS_UNSPECIFIED);
- if (memory_access_is_direct(mr, true)) {
+ if (memory_region_is_ram(mr)) {
target_ulong *pte_pa =
qemu_map_ram_ptr(mr->ram_block, addr1);
#if TCG_OVERSIZED_GUEST
@@ -239,15 +298,15 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
target_ulong vpn = addr >> PGSHIFT;
*physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
- if ((pte & PTE_R)) {
+ /* set permissions on the TLB entry */
+ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
*prot |= PAGE_READ;
}
if ((pte & PTE_X)) {
*prot |= PAGE_EXEC;
}
- /* only add write permission on stores or if the page
- is already dirty, so that we don't miss further
- page table walks to update the dirty bit */
+ /* add write permission on stores or if the page is already dirty,
+ so that we TLB miss on later writes to update the dirty bit */
if ((pte & PTE_W) &&
(access_type == MMU_DATA_STORE || (pte & PTE_D))) {
*prot |= PAGE_WRITE;
@@ -317,7 +376,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
g_assert_not_reached();
}
env->badaddr = addr;
- do_raise_exception_err(env, cs->exception_index, retaddr);
+ riscv_raise_exception(env, cs->exception_index, retaddr);
}
/* called by qemu's softmmu to fill the qemu tlb */
@@ -329,7 +388,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
if (ret == TRANSLATE_FAIL) {
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- do_raise_exception_err(env, cs->exception_index, retaddr);
+ riscv_raise_exception(env, cs->exception_index, retaddr);
}
}
@@ -355,7 +414,8 @@ int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
qemu_log_mask(CPU_LOG_MMU,
"%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
" prot %d\n", __func__, address, ret, pa, prot);
- if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
+ if (riscv_feature(env, RISCV_FEATURE_PMP) &&
+ !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
ret = TRANSLATE_FAIL;
}
if (ret == TRANSLATE_SUCCESS) {
@@ -393,119 +453,83 @@ void riscv_cpu_do_interrupt(CPUState *cs)
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- if (RISCV_DEBUG_INTERRUPT) {
- int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
- if (cs->exception_index & RISCV_EXCP_INT_FLAG) {
- qemu_log_mask(LOG_TRACE, "core 0: trap %s, epc 0x" TARGET_FMT_lx,
- riscv_intr_names[log_cause], env->pc);
- } else {
- qemu_log_mask(LOG_TRACE, "core 0: intr %s, epc 0x" TARGET_FMT_lx,
- riscv_excp_names[log_cause], env->pc);
+ /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide
+ so we mask off the MSB and separate into trap type and cause */
+ bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG);
+ target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
+ target_ulong deleg = async ? deleg = env->mideleg : env->medeleg;
+ target_ulong tval = 0;
+
+ static const int ecall_cause_map[] = {
+ [PRV_U] = RISCV_EXCP_U_ECALL,
+ [PRV_S] = RISCV_EXCP_S_ECALL,
+ [PRV_H] = RISCV_EXCP_H_ECALL,
+ [PRV_M] = RISCV_EXCP_M_ECALL
+ };
+
+ if (!async) {
+ /* set tval to badaddr for traps with address information */
+ switch (cause) {
+ case RISCV_EXCP_INST_ADDR_MIS:
+ case RISCV_EXCP_INST_ACCESS_FAULT:
+ case RISCV_EXCP_LOAD_ADDR_MIS:
+ case RISCV_EXCP_STORE_AMO_ADDR_MIS:
+ case RISCV_EXCP_LOAD_ACCESS_FAULT:
+ case RISCV_EXCP_STORE_AMO_ACCESS_FAULT:
+ case RISCV_EXCP_INST_PAGE_FAULT:
+ case RISCV_EXCP_LOAD_PAGE_FAULT:
+ case RISCV_EXCP_STORE_PAGE_FAULT:
+ tval = env->badaddr;
+ break;
+ default:
+ break;
}
- }
-
- target_ulong fixed_cause = 0;
- if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) {
- /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception
- index is only 32 bits wide */
- fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
- fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1);
- } else {
- /* fixup User ECALL -> correct priv ECALL */
- if (cs->exception_index == RISCV_EXCP_U_ECALL) {
- switch (env->priv) {
- case PRV_U:
- fixed_cause = RISCV_EXCP_U_ECALL;
- break;
- case PRV_S:
- fixed_cause = RISCV_EXCP_S_ECALL;
- break;
- case PRV_H:
- fixed_cause = RISCV_EXCP_H_ECALL;
- break;
- case PRV_M:
- fixed_cause = RISCV_EXCP_M_ECALL;
- break;
- }
- } else {
- fixed_cause = cs->exception_index;
+ /* ecall is dispatched as one cause so translate based on mode */
+ if (cause == RISCV_EXCP_U_ECALL) {
+ assert(env->priv <= 3);
+ cause = ecall_cause_map[env->priv];
}
}
- target_ulong backup_epc = env->pc;
-
- target_ulong bit = fixed_cause;
- target_ulong deleg = env->medeleg;
-
- int hasbadaddr =
- (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) ||
- (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) ||
- (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) ||
- (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) ||
- (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) ||
- (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) ||
- (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) ||
- (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) ||
- (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT);
-
- if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) {
- deleg = env->mideleg;
- bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1));
- }
+ trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 16 ?
+ (async ? riscv_intr_names : riscv_excp_names)[cause] : "(unknown)");
- if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) {
+ if (env->priv <= PRV_S &&
+ cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
/* handle the trap in S-mode */
- /* No need to check STVEC for misaligned - lower 2 bits cannot be set */
- env->pc = env->stvec;
- env->scause = fixed_cause;
- env->sepc = backup_epc;
-
- if (hasbadaddr) {
- if (RISCV_DEBUG_INTERRUPT) {
- qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
- ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
- }
- env->sbadaddr = env->badaddr;
- } else {
- /* otherwise we must clear sbadaddr/stval
- * todo: support populating stval on illegal instructions */
- env->sbadaddr = 0;
- }
-
target_ulong s = env->mstatus;
s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv));
s = set_field(s, MSTATUS_SPP, env->priv);
s = set_field(s, MSTATUS_SIE, 0);
- csr_write_helper(env, s, CSR_MSTATUS);
- riscv_set_mode(env, PRV_S);
+ env->mstatus = s;
+ env->scause = cause | ~(((target_ulong)-1) >> async);
+ env->sepc = env->pc;
+ env->sbadaddr = tval;
+ env->pc = (env->stvec >> 2 << 2) +
+ ((async && (env->stvec & 3) == 1) ? cause * 4 : 0);
+ riscv_cpu_set_mode(env, PRV_S);
} else {
- /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */
- env->pc = env->mtvec;
- env->mepc = backup_epc;
- env->mcause = fixed_cause;
-
- if (hasbadaddr) {
- if (RISCV_DEBUG_INTERRUPT) {
- qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
- ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
- }
- env->mbadaddr = env->badaddr;
- } else {
- /* otherwise we must clear mbadaddr/mtval
- * todo: support populating mtval on illegal instructions */
- env->mbadaddr = 0;
- }
-
+ /* handle the trap in M-mode */
target_ulong s = env->mstatus;
s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv));
s = set_field(s, MSTATUS_MPP, env->priv);
s = set_field(s, MSTATUS_MIE, 0);
- csr_write_helper(env, s, CSR_MSTATUS);
- riscv_set_mode(env, PRV_M);
+ env->mstatus = s;
+ env->mcause = cause | ~(((target_ulong)-1) >> async);
+ env->mepc = env->pc;
+ env->mbadaddr = tval;
+ env->pc = (env->mtvec >> 2 << 2) +
+ ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0);
+ riscv_cpu_set_mode(env, PRV_M);
}
- /* TODO yield load reservation */
+
+ /* NOTE: it is not necessary to yield load reservations here. It is only
+ necessary for an SC from "another hart" to cause a load reservation
+ to be yielded. Refer to the memory consistency model section of the
+ RISC-V ISA Specification. */
+
#endif
cs->exception_index = EXCP_NONE; /* mark handled to qemu */
}
diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h
index c2199610abf..52d380aa98c 100644
--- a/target/riscv/cpu_user.h
+++ b/target/riscv/cpu_user.h
@@ -10,4 +10,5 @@
#define xA4 14
#define xA5 15
#define xA6 16
-#define xA7 17 /* syscall number goes here */
+#define xA7 17 /* syscall number for RVI ABI */
+#define xT0 5 /* syscall number for RVE ABI */
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
new file mode 100644
index 00000000000..e876c617f79
--- /dev/null
+++ b/target/riscv/csr.c
@@ -0,0 +1,921 @@
+/*
+ * RISC-V Control and Status Registers.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * 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 .
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "qemu/main-loop.h"
+#include "exec/exec-all.h"
+
+/* CSR function table */
+
+static riscv_csr_operations csr_ops[];
+
+/* CSR function table constants */
+
+enum {
+ CSR_TABLE_SIZE = 0xfff
+};
+
+/* CSR function table public API */
+
+void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops)
+{
+ *ops = csr_ops[csrno & CSR_TABLE_SIZE];
+}
+
+void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops)
+{
+ csr_ops[csrno & CSR_TABLE_SIZE] = *ops;
+}
+
+/* CSR function table predicates (private) */
+
+static int fs(CPURISCVState *env, int csrno)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+static int ctr(CPURISCVState *env, int csrno)
+{
+#if !defined(CONFIG_USER_ONLY)
+ target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
+ env->priv == PRV_S ? env->mcounteren : -1U;
+ if (!(ctr_en & (1 << (csrno & 31)))) {
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static int any(CPURISCVState *env, int csrno)
+{
+ return 0;
+}
+
+static int smode(CPURISCVState *env, int csrno)
+{
+ return -!riscv_has_ext(env, RVS);
+}
+
+static int pmp(CPURISCVState *env, int csrno)
+{
+ return -!riscv_feature(env, RISCV_FEATURE_PMP);
+}
+#endif
+
+/* User Floating-Point CSRs */
+
+static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+#endif
+ *val = riscv_cpu_get_fflags(env);
+ return 0;
+}
+
+static int write_fflags(CPURISCVState *env, int csrno, target_ulong val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_FS;
+#endif
+ riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT));
+ return 0;
+}
+
+static int read_frm(CPURISCVState *env, int csrno, target_ulong *val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+#endif
+ *val = env->frm;
+ return 0;
+}
+
+static int write_frm(CPURISCVState *env, int csrno, target_ulong val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_FS;
+#endif
+ env->frm = val & (FSR_RD >> FSR_RD_SHIFT);
+ return 0;
+}
+
+static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+#endif
+ *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT)
+ | (env->frm << FSR_RD_SHIFT);
+ return 0;
+}
+
+static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!(env->mstatus & MSTATUS_FS)) {
+ return -1;
+ }
+ env->mstatus |= MSTATUS_FS;
+#endif
+ env->frm = (val & FSR_RD) >> FSR_RD_SHIFT;
+ riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT);
+ return 0;
+}
+
+/* User Timers and Counters */
+
+static int read_instret(CPURISCVState *env, int csrno, target_ulong *val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (use_icount) {
+ *val = cpu_get_icount();
+ } else {
+ *val = cpu_get_host_ticks();
+ }
+#else
+ *val = cpu_get_host_ticks();
+#endif
+ return 0;
+}
+
+#if defined(TARGET_RISCV32)
+static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (use_icount) {
+ *val = cpu_get_icount() >> 32;
+ } else {
+ *val = cpu_get_host_ticks() >> 32;
+ }
+#else
+ *val = cpu_get_host_ticks() >> 32;
+#endif
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+
+static int read_time(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = cpu_get_host_ticks();
+ return 0;
+}
+
+#if defined(TARGET_RISCV32)
+static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = cpu_get_host_ticks() >> 32;
+ return 0;
+}
+#endif
+
+#else
+
+/* Machine constants */
+
+#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP)
+#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP)
+
+static const target_ulong delegable_ints = S_MODE_INTERRUPTS;
+static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS;
+static const target_ulong delegable_excps =
+ (1ULL << (RISCV_EXCP_INST_ADDR_MIS)) |
+ (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) |
+ (1ULL << (RISCV_EXCP_ILLEGAL_INST)) |
+ (1ULL << (RISCV_EXCP_BREAKPOINT)) |
+ (1ULL << (RISCV_EXCP_LOAD_ADDR_MIS)) |
+ (1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT)) |
+ (1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS)) |
+ (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) |
+ (1ULL << (RISCV_EXCP_U_ECALL)) |
+ (1ULL << (RISCV_EXCP_S_ECALL)) |
+ (1ULL << (RISCV_EXCP_H_ECALL)) |
+ (1ULL << (RISCV_EXCP_M_ECALL)) |
+ (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) |
+ (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) |
+ (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT));
+static const target_ulong sstatus_v1_9_mask = SSTATUS_SIE | SSTATUS_SPIE |
+ SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS |
+ SSTATUS_SUM | SSTATUS_SD;
+static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE |
+ SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS |
+ SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD;
+
+#if defined(TARGET_RISCV32)
+static const char valid_vm_1_09[16] = {
+ [VM_1_09_MBARE] = 1,
+ [VM_1_09_SV32] = 1,
+};
+static const char valid_vm_1_10[16] = {
+ [VM_1_10_MBARE] = 1,
+ [VM_1_10_SV32] = 1
+};
+#elif defined(TARGET_RISCV64)
+static const char valid_vm_1_09[16] = {
+ [VM_1_09_MBARE] = 1,
+ [VM_1_09_SV39] = 1,
+ [VM_1_09_SV48] = 1,
+};
+static const char valid_vm_1_10[16] = {
+ [VM_1_10_MBARE] = 1,
+ [VM_1_10_SV39] = 1,
+ [VM_1_10_SV48] = 1,
+ [VM_1_10_SV57] = 1
+};
+#endif
+
+/* Machine Information Registers */
+
+static int read_zero(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ return *val = 0;
+}
+
+static int read_mhartid(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mhartid;
+ return 0;
+}
+
+/* Machine Trap Setup */
+
+static int read_mstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mstatus;
+ return 0;
+}
+
+static int validate_vm(CPURISCVState *env, target_ulong vm)
+{
+ return (env->priv_ver >= PRIV_VERSION_1_10_0) ?
+ valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf];
+}
+
+static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+ target_ulong mstatus = env->mstatus;
+ target_ulong mask = 0;
+ target_ulong mpp = get_field(val, MSTATUS_MPP);
+
+ /* flush tlb on mstatus fields that affect VM */
+ if (env->priv_ver <= PRIV_VERSION_1_09_1) {
+ if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+ MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) {
+ tlb_flush(CPU(riscv_env_get_cpu(env)));
+ }
+ mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+ MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+ MSTATUS_MPP | MSTATUS_MXR |
+ (validate_vm(env, get_field(val, MSTATUS_VM)) ?
+ MSTATUS_VM : 0);
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+ MSTATUS_MPRV | MSTATUS_SUM)) {
+ tlb_flush(CPU(riscv_env_get_cpu(env)));
+ }
+ mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+ MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+ MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR |
+ MSTATUS_TW;
+ }
+
+ /* silenty discard mstatus.mpp writes for unsupported modes */
+ if (mpp == PRV_H ||
+ (!riscv_has_ext(env, RVS) && mpp == PRV_S) ||
+ (!riscv_has_ext(env, RVU) && mpp == PRV_U)) {
+ mask &= ~MSTATUS_MPP;
+ }
+
+ mstatus = (mstatus & ~mask) | (val & mask);
+
+ int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
+ ((mstatus & MSTATUS_XS) == MSTATUS_XS);
+ mstatus = set_field(mstatus, MSTATUS_SD, dirty);
+ env->mstatus = mstatus;
+
+ return 0;
+}
+
+static int read_misa(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->misa;
+ return 0;
+}
+
+static int write_misa(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (!riscv_feature(env, RISCV_FEATURE_MISA)) {
+ /* drop write to misa */
+ return 0;
+ }
+
+ /* 'I' or 'E' must be present */
+ if (!(val & (RVI | RVE))) {
+ /* it not, drop write to misa */
+ return 0;
+ }
+
+ /* 'E' excludes all other extensions */
+ if (val & RVE) {
+ /* when we support 'E' we can do "val = RVE;" however
+ * for now we just drop writes if 'E' is present */
+ return 0;
+ }
+
+ /* Mask extensions that are not supported by this hart */
+ val &= env->misa_mask;
+
+ /* Mask extensions that are not supported by QEMU */
+ val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+
+ /* 'D' depends on 'F', so clear 'D' if 'F' is not present */
+ if ((val & RVD) && !(val & RVF)) {
+ val &= ~RVD;
+ }
+
+ /* Suppress 'C' if next instruction is not aligned
+ TODO: this should check next_pc */
+ if ((val & RVC) && (GETPC() & ~3) != 0) {
+ val &= ~RVC;
+ }
+
+ /* misa.MXL writes are not supported by QEMU */
+ val = (env->misa & MISA_MXL) | (val & ~MISA_MXL);
+
+ /* flush translation cache */
+ if (val != env->misa) {
+ tb_flush(CPU(riscv_env_get_cpu(env)));
+ }
+
+ env->misa = val;
+
+ return 0;
+}
+
+static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->medeleg;
+ return 0;
+}
+
+static int write_medeleg(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->medeleg = (env->medeleg & ~delegable_excps) | (val & delegable_excps);
+ return 0;
+}
+
+static int read_mideleg(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mideleg;
+ return 0;
+}
+
+static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints);
+ return 0;
+}
+
+static int read_mie(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mie;
+ return 0;
+}
+
+static int write_mie(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mie = (env->mie & ~all_ints) | (val & all_ints);
+ return 0;
+}
+
+static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mtvec;
+ return 0;
+}
+
+static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val)
+{
+ /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+ if ((val & 3) < 2) {
+ env->mtvec = val;
+ } else {
+ qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n");
+ }
+ return 0;
+}
+
+static int read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ if (env->priv_ver < PRIV_VERSION_1_10_0) {
+ return -1;
+ }
+ *val = env->mcounteren;
+ return 0;
+}
+
+static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (env->priv_ver < PRIV_VERSION_1_10_0) {
+ return -1;
+ }
+ env->mcounteren = val;
+ return 0;
+}
+
+static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ if (env->priv_ver > PRIV_VERSION_1_09_1) {
+ return -1;
+ }
+ *val = env->mcounteren;
+ return 0;
+}
+
+static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (env->priv_ver > PRIV_VERSION_1_09_1) {
+ return -1;
+ }
+ env->mcounteren = val;
+ return 0;
+}
+
+static int read_mucounteren(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ if (env->priv_ver > PRIV_VERSION_1_09_1) {
+ return -1;
+ }
+ *val = env->scounteren;
+ return 0;
+}
+
+static int write_mucounteren(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (env->priv_ver > PRIV_VERSION_1_09_1) {
+ return -1;
+ }
+ env->scounteren = val;
+ return 0;
+}
+
+/* Machine Trap Handling */
+
+static int read_mscratch(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mscratch;
+ return 0;
+}
+
+static int write_mscratch(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mscratch = val;
+ return 0;
+}
+
+static int read_mepc(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mepc;
+ return 0;
+}
+
+static int write_mepc(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mepc = val;
+ return 0;
+}
+
+static int read_mcause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mcause;
+ return 0;
+}
+
+static int write_mcause(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mcause = val;
+ return 0;
+}
+
+static int read_mbadaddr(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mbadaddr;
+ return 0;
+}
+
+static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->mbadaddr = val;
+ return 0;
+}
+
+static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ RISCVCPU *cpu = riscv_env_get_cpu(env);
+
+ /* Allow software control of delegable interrupts not claimed by hardware */
+ target_ulong mask = write_mask & delegable_ints & ~env->miclaim;
+
+ uint32_t old_mip;
+
+ if (mask) {
+ qemu_mutex_lock_iothread();
+ old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask));
+ qemu_mutex_unlock_iothread();
+ } else {
+ old_mip = atomic_read(&env->mip);
+ }
+
+ if (ret_value) {
+ *ret_value = old_mip;
+ }
+
+ return 0;
+}
+
+/* Supervisor Trap Setup */
+
+static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ?
+ sstatus_v1_10_mask : sstatus_v1_9_mask);
+ *val = env->mstatus & mask;
+ return 0;
+}
+
+static int write_sstatus(CPURISCVState *env, int csrno, target_ulong val)
+{
+ target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ?
+ sstatus_v1_10_mask : sstatus_v1_9_mask);
+ target_ulong newval = (env->mstatus & ~mask) | (val & mask);
+ return write_mstatus(env, CSR_MSTATUS, newval);
+}
+
+static int read_sie(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->mie & env->mideleg;
+ return 0;
+}
+
+static int write_sie(CPURISCVState *env, int csrno, target_ulong val)
+{
+ target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg);
+ return write_mie(env, CSR_MIE, newval);
+}
+
+static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->stvec;
+ return 0;
+}
+
+static int write_stvec(CPURISCVState *env, int csrno, target_ulong val)
+{
+ /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
+ if ((val & 3) < 2) {
+ env->stvec = val;
+ } else {
+ qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n");
+ }
+ return 0;
+}
+
+static int read_scounteren(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ if (env->priv_ver < PRIV_VERSION_1_10_0) {
+ return -1;
+ }
+ *val = env->scounteren;
+ return 0;
+}
+
+static int write_scounteren(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (env->priv_ver < PRIV_VERSION_1_10_0) {
+ return -1;
+ }
+ env->scounteren = val;
+ return 0;
+}
+
+/* Supervisor Trap Handling */
+
+static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->sscratch;
+ return 0;
+}
+
+static int write_sscratch(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->sscratch = val;
+ return 0;
+}
+
+static int read_sepc(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->sepc;
+ return 0;
+}
+
+static int write_sepc(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->sepc = val;
+ return 0;
+}
+
+static int read_scause(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->scause;
+ return 0;
+}
+
+static int write_scause(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->scause = val;
+ return 0;
+}
+
+static int read_sbadaddr(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = env->sbadaddr;
+ return 0;
+}
+
+static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val)
+{
+ env->sbadaddr = val;
+ return 0;
+}
+
+static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ return rmw_mip(env, CSR_MSTATUS, ret_value, new_value,
+ write_mask & env->mideleg);
+}
+
+/* Supervisor Protection and Translation */
+
+static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
+ *val = 0;
+ } else if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+ if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+ return -1;
+ } else {
+ *val = env->satp;
+ }
+ } else {
+ *val = env->sptbr;
+ }
+ return 0;
+}
+
+static int write_satp(CPURISCVState *env, int csrno, target_ulong val)
+{
+ if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
+ return 0;
+ }
+ if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val ^ env->sptbr)) {
+ tlb_flush(CPU(riscv_env_get_cpu(env)));
+ env->sptbr = val & (((target_ulong)
+ 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1);
+ }
+ if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
+ validate_vm(env, get_field(val, SATP_MODE)) &&
+ ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
+ {
+ if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+ return -1;
+ } else {
+ tlb_flush(CPU(riscv_env_get_cpu(env)));
+ env->satp = val;
+ }
+ }
+ return 0;
+}
+
+/* Physical Memory Protection */
+
+static int read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0);
+ return 0;
+}
+
+static int write_pmpcfg(CPURISCVState *env, int csrno, target_ulong val)
+{
+ pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val);
+ return 0;
+}
+
+static int read_pmpaddr(CPURISCVState *env, int csrno, target_ulong *val)
+{
+ *val = pmpaddr_csr_read(env, csrno - CSR_PMPADDR0);
+ return 0;
+}
+
+static int write_pmpaddr(CPURISCVState *env, int csrno, target_ulong val)
+{
+ pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val);
+ return 0;
+}
+
+#endif
+
+/*
+ * riscv_csrrw - read and/or update control and status register
+ *
+ * csrr <-> riscv_csrrw(env, csrno, ret_value, 0, 0);
+ * csrrw <-> riscv_csrrw(env, csrno, ret_value, value, -1);
+ * csrrs <-> riscv_csrrw(env, csrno, ret_value, -1, value);
+ * csrrc <-> riscv_csrrw(env, csrno, ret_value, 0, value);
+ */
+
+int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
+ target_ulong new_value, target_ulong write_mask)
+{
+ int ret;
+ target_ulong old_value;
+
+ /* check privileges and return -1 if check fails */
+#if !defined(CONFIG_USER_ONLY)
+ int csr_priv = get_field(csrno, 0x300);
+ int read_only = get_field(csrno, 0xC00) == 3;
+ if ((write_mask && read_only) || (env->priv < csr_priv)) {
+ return -1;
+ }
+#endif
+
+ /* check predicate */
+ if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) {
+ return -1;
+ }
+
+ /* execute combined read/write operation if it exists */
+ if (csr_ops[csrno].op) {
+ return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask);
+ }
+
+ /* if no accessor exists then return failure */
+ if (!csr_ops[csrno].read) {
+ return -1;
+ }
+
+ /* read old value */
+ ret = csr_ops[csrno].read(env, csrno, &old_value);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* write value if writable and write mask set, otherwise drop writes */
+ if (write_mask) {
+ new_value = (old_value & ~write_mask) | (new_value & write_mask);
+ if (csr_ops[csrno].write) {
+ ret = csr_ops[csrno].write(env, csrno, new_value);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+
+ /* return old value */
+ if (ret_value) {
+ *ret_value = old_value;
+ }
+
+ return 0;
+}
+
+/* Control and Status Register function table */
+
+static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
+ /* User Floating-Point CSRs */
+ [CSR_FFLAGS] = { fs, read_fflags, write_fflags },
+ [CSR_FRM] = { fs, read_frm, write_frm },
+ [CSR_FCSR] = { fs, read_fcsr, write_fcsr },
+
+ /* User Timers and Counters */
+ [CSR_CYCLE] = { ctr, read_instret },
+ [CSR_INSTRET] = { ctr, read_instret },
+#if defined(TARGET_RISCV32)
+ [CSR_CYCLEH] = { ctr, read_instreth },
+ [CSR_INSTRETH] = { ctr, read_instreth },
+#endif
+
+ /* User-level time CSRs are only available in linux-user
+ * In privileged mode, the monitor emulates these CSRs */
+#if defined(CONFIG_USER_ONLY)
+ [CSR_TIME] = { ctr, read_time },
+#if defined(TARGET_RISCV32)
+ [CSR_TIMEH] = { ctr, read_timeh },
+#endif
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+ /* Machine Timers and Counters */
+ [CSR_MCYCLE] = { any, read_instret },
+ [CSR_MINSTRET] = { any, read_instret },
+#if defined(TARGET_RISCV32)
+ [CSR_MCYCLEH] = { any, read_instreth },
+ [CSR_MINSTRETH] = { any, read_instreth },
+#endif
+
+ /* Machine Information Registers */
+ [CSR_MVENDORID] = { any, read_zero },
+ [CSR_MARCHID] = { any, read_zero },
+ [CSR_MIMPID] = { any, read_zero },
+ [CSR_MHARTID] = { any, read_mhartid },
+
+ /* Machine Trap Setup */
+ [CSR_MSTATUS] = { any, read_mstatus, write_mstatus },
+ [CSR_MISA] = { any, read_misa, write_misa },
+ [CSR_MIDELEG] = { any, read_mideleg, write_mideleg },
+ [CSR_MEDELEG] = { any, read_medeleg, write_medeleg },
+ [CSR_MIE] = { any, read_mie, write_mie },
+ [CSR_MTVEC] = { any, read_mtvec, write_mtvec },
+ [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren },
+
+ /* Legacy Counter Setup (priv v1.9.1) */
+ [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren },
+ [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren },
+
+ /* Machine Trap Handling */
+ [CSR_MSCRATCH] = { any, read_mscratch, write_mscratch },
+ [CSR_MEPC] = { any, read_mepc, write_mepc },
+ [CSR_MCAUSE] = { any, read_mcause, write_mcause },
+ [CSR_MBADADDR] = { any, read_mbadaddr, write_mbadaddr },
+ [CSR_MIP] = { any, NULL, NULL, rmw_mip },
+
+ /* Supervisor Trap Setup */
+ [CSR_SSTATUS] = { smode, read_sstatus, write_sstatus },
+ [CSR_SIE] = { smode, read_sie, write_sie },
+ [CSR_STVEC] = { smode, read_stvec, write_stvec },
+ [CSR_SCOUNTEREN] = { smode, read_scounteren, write_scounteren },
+
+ /* Supervisor Trap Handling */
+ [CSR_SSCRATCH] = { smode, read_sscratch, write_sscratch },
+ [CSR_SEPC] = { smode, read_sepc, write_sepc },
+ [CSR_SCAUSE] = { smode, read_scause, write_scause },
+ [CSR_SBADADDR] = { smode, read_sbadaddr, write_sbadaddr },
+ [CSR_SIP] = { smode, NULL, NULL, rmw_sip },
+
+ /* Supervisor Protection and Translation */
+ [CSR_SATP] = { smode, read_satp, write_satp },
+
+ /* Physical Memory Protection */
+ [CSR_PMPCFG0 ... CSR_PMPADDR9] = { pmp, read_pmpcfg, write_pmpcfg },
+ [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp, read_pmpaddr, write_pmpaddr },
+
+ /* Performance Counters */
+ [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { ctr, read_zero },
+ [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { any, read_zero },
+ [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { any, read_zero },
+#if defined(TARGET_RISCV32)
+ [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { ctr, read_zero },
+ [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { any, read_zero },
+#endif
+#endif
+};
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
index fdb87d8d82c..1452a153f26 100644
--- a/target/riscv/fpu_helper.c
+++ b/target/riscv/fpu_helper.c
@@ -23,7 +23,7 @@
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
-target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
+target_ulong riscv_cpu_get_fflags(CPURISCVState *env)
{
int soft = get_float_exception_flags(&env->fp_status);
target_ulong hard = 0;
@@ -37,7 +37,7 @@ target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
return hard;
}
-void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard)
+void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard)
{
int soft = 0;
@@ -74,7 +74,7 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm)
softrm = float_round_ties_away;
break;
default:
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
set_float_rounding_mode(softrm, &env->fp_status);
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 4f919b6c341..3cabb21cd01 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -33,7 +33,10 @@ int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
} else if (n < 65) {
return gdb_get_reg64(mem_buf, env->fpr[n - 33]);
} else if (n < 4096 + 65) {
- return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65));
+ target_ulong val = 0;
+ if (riscv_csrrw(env, n - 65, &val, 0, 0) == 0) {
+ return gdb_get_regl(mem_buf, val);
+ }
}
return 0;
}
@@ -56,7 +59,10 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */
return sizeof(uint64_t);
} else if (n < 4096 + 65) {
- csr_write_helper(env, ldtul_p(mem_buf), n - 65);
+ target_ulong val = ldtul_p(mem_buf);
+ if (riscv_csrrw(env, n - 65, NULL, val, -1) == 0) {
+ return sizeof(target_ulong);
+ }
}
return 0;
}
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index aec7558e1b9..b7dc18a41e2 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -24,41 +24,8 @@
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
-#ifndef CONFIG_USER_ONLY
-
-#if defined(TARGET_RISCV32)
-static const char valid_vm_1_09[16] = {
- [VM_1_09_MBARE] = 1,
- [VM_1_09_SV32] = 1,
-};
-static const char valid_vm_1_10[16] = {
- [VM_1_10_MBARE] = 1,
- [VM_1_10_SV32] = 1
-};
-#elif defined(TARGET_RISCV64)
-static const char valid_vm_1_09[16] = {
- [VM_1_09_MBARE] = 1,
- [VM_1_09_SV39] = 1,
- [VM_1_09_SV48] = 1,
-};
-static const char valid_vm_1_10[16] = {
- [VM_1_10_MBARE] = 1,
- [VM_1_10_SV39] = 1,
- [VM_1_10_SV48] = 1,
- [VM_1_10_SV57] = 1
-};
-#endif
-
-static int validate_vm(CPURISCVState *env, target_ulong vm)
-{
- return (env->priv_ver >= PRIV_VERSION_1_10_0) ?
- valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf];
-}
-
-#endif
-
/* Exceptions processing helpers */
-void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
uint32_t exception, uintptr_t pc)
{
CPUState *cs = CPU(riscv_env_get_cpu(env));
@@ -69,627 +36,55 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
void helper_raise_exception(CPURISCVState *env, uint32_t exception)
{
- do_raise_exception_err(env, exception, 0);
-}
-
-static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra)
-{
-#ifndef CONFIG_USER_ONLY
- if (!(env->mstatus & MSTATUS_FS)) {
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
- }
-#endif
-}
-
-/*
- * Handle writes to CSRs and any resulting special behavior
- *
- * Adapted from Spike's processor_t::set_csr
- */
-void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
- target_ulong csrno)
-{
-#ifndef CONFIG_USER_ONLY
- uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP);
- uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP;
-#endif
-
- switch (csrno) {
- case CSR_FFLAGS:
- validate_mstatus_fs(env, GETPC());
- cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT));
- break;
- case CSR_FRM:
- validate_mstatus_fs(env, GETPC());
- env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT);
- break;
- case CSR_FCSR:
- validate_mstatus_fs(env, GETPC());
- env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT;
- cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT);
- break;
-#ifndef CONFIG_USER_ONLY
- case CSR_MSTATUS: {
- target_ulong mstatus = env->mstatus;
- target_ulong mask = 0;
- target_ulong mpp = get_field(val_to_write, MSTATUS_MPP);
-
- /* flush tlb on mstatus fields that affect VM */
- if (env->priv_ver <= PRIV_VERSION_1_09_1) {
- if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
- MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) {
- helper_tlb_flush(env);
- }
- mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
- MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
- MSTATUS_MPP | MSTATUS_MXR |
- (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ?
- MSTATUS_VM : 0);
- }
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
- MSTATUS_MPRV | MSTATUS_SUM)) {
- helper_tlb_flush(env);
- }
- mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
- MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
- MSTATUS_MPP | MSTATUS_MXR;
- }
-
- /* silenty discard mstatus.mpp writes for unsupported modes */
- if (mpp == PRV_H ||
- (!riscv_has_ext(env, RVS) && mpp == PRV_S) ||
- (!riscv_has_ext(env, RVU) && mpp == PRV_U)) {
- mask &= ~MSTATUS_MPP;
- }
-
- mstatus = (mstatus & ~mask) | (val_to_write & mask);
-
- /* Note: this is a workaround for an issue where mstatus.FS
- does not report dirty after floating point operations
- that modify floating point state. This workaround is
- technically compliant with the RISC-V Privileged
- specification as it is legal to return only off, or dirty.
- at the expense of extra floating point save/restore. */
-
- /* FP is always dirty or off */
- if (mstatus & MSTATUS_FS) {
- mstatus |= MSTATUS_FS;
- }
-
- int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
- ((mstatus & MSTATUS_XS) == MSTATUS_XS);
- mstatus = set_field(mstatus, MSTATUS_SD, dirty);
- env->mstatus = mstatus;
- break;
- }
- case CSR_MIP: {
- /*
- * Since the writeable bits in MIP are not set asynchrously by the
- * CLINT, no additional locking is needed for read-modifiy-write
- * CSR operations
- */
- qemu_mutex_lock_iothread();
- RISCVCPU *cpu = riscv_env_get_cpu(env);
- riscv_set_local_interrupt(cpu, MIP_SSIP,
- (val_to_write & MIP_SSIP) != 0);
- riscv_set_local_interrupt(cpu, MIP_STIP,
- (val_to_write & MIP_STIP) != 0);
- /*
- * csrs, csrc on mip.SEIP is not decomposable into separate read and
- * write steps, so a different implementation is needed
- */
- qemu_mutex_unlock_iothread();
- break;
- }
- case CSR_MIE: {
- env->mie = (env->mie & ~all_ints) |
- (val_to_write & all_ints);
- break;
- }
- case CSR_MIDELEG:
- env->mideleg = (env->mideleg & ~delegable_ints)
- | (val_to_write & delegable_ints);
- break;
- case CSR_MEDELEG: {
- target_ulong mask = 0;
- mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS);
- mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT);
- mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST);
- mask |= 1ULL << (RISCV_EXCP_BREAKPOINT);
- mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS);
- mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT);
- mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS);
- mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT);
- mask |= 1ULL << (RISCV_EXCP_U_ECALL);
- mask |= 1ULL << (RISCV_EXCP_S_ECALL);
- mask |= 1ULL << (RISCV_EXCP_H_ECALL);
- mask |= 1ULL << (RISCV_EXCP_M_ECALL);
- mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT);
- mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT);
- mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT);
- env->medeleg = (env->medeleg & ~mask)
- | (val_to_write & mask);
- break;
- }
- case CSR_MINSTRET:
- /* minstret is WARL so unsupported writes are ignored */
- break;
- case CSR_MCYCLE:
- /* mcycle is WARL so unsupported writes are ignored */
- break;
-#if defined(TARGET_RISCV32)
- case CSR_MINSTRETH:
- /* minstreth is WARL so unsupported writes are ignored */
- break;
- case CSR_MCYCLEH:
- /* mcycleh is WARL so unsupported writes are ignored */
- break;
-#endif
- case CSR_MUCOUNTEREN:
- if (env->priv_ver <= PRIV_VERSION_1_09_1) {
- env->scounteren = val_to_write;
- break;
- } else {
- goto do_illegal;
- }
- case CSR_MSCOUNTEREN:
- if (env->priv_ver <= PRIV_VERSION_1_09_1) {
- env->mcounteren = val_to_write;
- break;
- } else {
- goto do_illegal;
- }
- case CSR_SSTATUS: {
- target_ulong ms = env->mstatus;
- target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE
- | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS
- | SSTATUS_SUM | SSTATUS_SD;
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- mask |= SSTATUS_MXR;
- }
- ms = (ms & ~mask) | (val_to_write & mask);
- csr_write_helper(env, ms, CSR_MSTATUS);
- break;
- }
- case CSR_SIP: {
- qemu_mutex_lock_iothread();
- target_ulong next_mip = (env->mip & ~env->mideleg)
- | (val_to_write & env->mideleg);
- qemu_mutex_unlock_iothread();
- csr_write_helper(env, next_mip, CSR_MIP);
- break;
- }
- case CSR_SIE: {
- target_ulong next_mie = (env->mie & ~env->mideleg)
- | (val_to_write & env->mideleg);
- csr_write_helper(env, next_mie, CSR_MIE);
- break;
- }
- case CSR_SATP: /* CSR_SPTBR */ {
- if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
- break;
- }
- if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr))
- {
- helper_tlb_flush(env);
- env->sptbr = val_to_write & (((target_ulong)
- 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1);
- }
- if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
- validate_vm(env, get_field(val_to_write, SATP_MODE)) &&
- ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
- {
- helper_tlb_flush(env);
- env->satp = val_to_write;
- }
- break;
- }
- case CSR_SEPC:
- env->sepc = val_to_write;
- break;
- case CSR_STVEC:
- /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val_to_write & 3) == 0) {
- env->stvec = val_to_write >> 2 << 2;
- } else {
- qemu_log_mask(LOG_UNIMP,
- "CSR_STVEC: vectored traps not supported\n");
- }
- break;
- case CSR_SCOUNTEREN:
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- env->scounteren = val_to_write;
- break;
- } else {
- goto do_illegal;
- }
- case CSR_SSCRATCH:
- env->sscratch = val_to_write;
- break;
- case CSR_SCAUSE:
- env->scause = val_to_write;
- break;
- case CSR_SBADADDR:
- env->sbadaddr = val_to_write;
- break;
- case CSR_MEPC:
- env->mepc = val_to_write;
- break;
- case CSR_MTVEC:
- /* bits [1:0] indicate mode; 0 = direct, 1 = vectored, 2 >= reserved */
- if ((val_to_write & 3) == 0) {
- env->mtvec = val_to_write >> 2 << 2;
- } else {
- qemu_log_mask(LOG_UNIMP,
- "CSR_MTVEC: vectored traps not supported\n");
- }
- break;
- case CSR_MCOUNTEREN:
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- env->mcounteren = val_to_write;
- break;
- } else {
- goto do_illegal;
- }
- case CSR_MSCRATCH:
- env->mscratch = val_to_write;
- break;
- case CSR_MCAUSE:
- env->mcause = val_to_write;
- break;
- case CSR_MBADADDR:
- env->mbadaddr = val_to_write;
- break;
- case CSR_MISA:
- /* misa is WARL so unsupported writes are ignored */
- break;
- case CSR_PMPCFG0:
- case CSR_PMPCFG1:
- case CSR_PMPCFG2:
- case CSR_PMPCFG3:
- pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write);
- break;
- case CSR_PMPADDR0:
- case CSR_PMPADDR1:
- case CSR_PMPADDR2:
- case CSR_PMPADDR3:
- case CSR_PMPADDR4:
- case CSR_PMPADDR5:
- case CSR_PMPADDR6:
- case CSR_PMPADDR7:
- case CSR_PMPADDR8:
- case CSR_PMPADDR9:
- case CSR_PMPADDR10:
- case CSR_PMPADDR11:
- case CSR_PMPADDR12:
- case CSR_PMPADDR13:
- case CSR_PMPADDR14:
- case CSR_PMPADDR15:
- pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write);
- break;
-#endif
-#if !defined(CONFIG_USER_ONLY)
- do_illegal:
-#endif
- default:
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
- }
-}
-
-/*
- * Handle reads to CSRs and any resulting special behavior
- *
- * Adapted from Spike's processor_t::get_csr
- */
-target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno)
-{
-#ifndef CONFIG_USER_ONLY
- target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
- env->priv == PRV_S ? env->mcounteren : -1U;
-#else
- target_ulong ctr_en = -1;
-#endif
- target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1;
-
- if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) {
- if (ctr_ok) {
- return 0;
- }
- }
-#if defined(TARGET_RISCV32)
- if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) {
- if (ctr_ok) {
- return 0;
- }
- }
-#endif
- if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
- return 0;
- }
-#if defined(TARGET_RISCV32)
- if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
- return 0;
- }
-#endif
- if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) {
- return 0;
- }
-
- switch (csrno) {
- case CSR_FFLAGS:
- validate_mstatus_fs(env, GETPC());
- return cpu_riscv_get_fflags(env);
- case CSR_FRM:
- validate_mstatus_fs(env, GETPC());
- return env->frm;
- case CSR_FCSR:
- validate_mstatus_fs(env, GETPC());
- return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT)
- | (env->frm << FSR_RD_SHIFT);
- /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */
-#ifdef CONFIG_USER_ONLY
- case CSR_TIME:
- return cpu_get_host_ticks();
-#if defined(TARGET_RISCV32)
- case CSR_TIMEH:
- return cpu_get_host_ticks() >> 32;
-#endif
-#endif
- case CSR_INSTRET:
- case CSR_CYCLE:
- if (ctr_ok) {
-#if !defined(CONFIG_USER_ONLY)
- if (use_icount) {
- return cpu_get_icount();
- } else {
- return cpu_get_host_ticks();
- }
-#else
- return cpu_get_host_ticks();
-#endif
- }
- break;
-#if defined(TARGET_RISCV32)
- case CSR_INSTRETH:
- case CSR_CYCLEH:
- if (ctr_ok) {
-#if !defined(CONFIG_USER_ONLY)
- if (use_icount) {
- return cpu_get_icount() >> 32;
- } else {
- return cpu_get_host_ticks() >> 32;
- }
-#else
- return cpu_get_host_ticks() >> 32;
-#endif
- }
- break;
-#endif
-#ifndef CONFIG_USER_ONLY
- case CSR_MINSTRET:
- case CSR_MCYCLE:
- if (use_icount) {
- return cpu_get_icount();
- } else {
- return cpu_get_host_ticks();
- }
- case CSR_MINSTRETH:
- case CSR_MCYCLEH:
-#if defined(TARGET_RISCV32)
- if (use_icount) {
- return cpu_get_icount() >> 32;
- } else {
- return cpu_get_host_ticks() >> 32;
- }
-#endif
- break;
- case CSR_MUCOUNTEREN:
- if (env->priv_ver <= PRIV_VERSION_1_09_1) {
- return env->scounteren;
- } else {
- break; /* illegal instruction */
- }
- case CSR_MSCOUNTEREN:
- if (env->priv_ver <= PRIV_VERSION_1_09_1) {
- return env->mcounteren;
- } else {
- break; /* illegal instruction */
- }
- case CSR_SSTATUS: {
- target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE
- | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS
- | SSTATUS_SUM | SSTATUS_SD;
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- mask |= SSTATUS_MXR;
- }
- return env->mstatus & mask;
- }
- case CSR_SIP: {
- qemu_mutex_lock_iothread();
- target_ulong tmp = env->mip & env->mideleg;
- qemu_mutex_unlock_iothread();
- return tmp;
- }
- case CSR_SIE:
- return env->mie & env->mideleg;
- case CSR_SEPC:
- return env->sepc;
- case CSR_SBADADDR:
- return env->sbadaddr;
- case CSR_STVEC:
- return env->stvec;
- case CSR_SCOUNTEREN:
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- return env->scounteren;
- } else {
- break; /* illegal instruction */
- }
- case CSR_SCAUSE:
- return env->scause;
- case CSR_SATP: /* CSR_SPTBR */
- if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
- return 0;
- }
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- return env->satp;
- } else {
- return env->sptbr;
- }
- case CSR_SSCRATCH:
- return env->sscratch;
- case CSR_MSTATUS:
- return env->mstatus;
- case CSR_MIP: {
- qemu_mutex_lock_iothread();
- target_ulong tmp = env->mip;
- qemu_mutex_unlock_iothread();
- return tmp;
- }
- case CSR_MIE:
- return env->mie;
- case CSR_MEPC:
- return env->mepc;
- case CSR_MSCRATCH:
- return env->mscratch;
- case CSR_MCAUSE:
- return env->mcause;
- case CSR_MBADADDR:
- return env->mbadaddr;
- case CSR_MISA:
- return env->misa;
- case CSR_MARCHID:
- return 0; /* as spike does */
- case CSR_MIMPID:
- return 0; /* as spike does */
- case CSR_MVENDORID:
- return 0; /* as spike does */
- case CSR_MHARTID:
- return env->mhartid;
- case CSR_MTVEC:
- return env->mtvec;
- case CSR_MCOUNTEREN:
- if (env->priv_ver >= PRIV_VERSION_1_10_0) {
- return env->mcounteren;
- } else {
- break; /* illegal instruction */
- }
- case CSR_MEDELEG:
- return env->medeleg;
- case CSR_MIDELEG:
- return env->mideleg;
- case CSR_PMPCFG0:
- case CSR_PMPCFG1:
- case CSR_PMPCFG2:
- case CSR_PMPCFG3:
- return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0);
- case CSR_PMPADDR0:
- case CSR_PMPADDR1:
- case CSR_PMPADDR2:
- case CSR_PMPADDR3:
- case CSR_PMPADDR4:
- case CSR_PMPADDR5:
- case CSR_PMPADDR6:
- case CSR_PMPADDR7:
- case CSR_PMPADDR8:
- case CSR_PMPADDR9:
- case CSR_PMPADDR10:
- case CSR_PMPADDR11:
- case CSR_PMPADDR12:
- case CSR_PMPADDR13:
- case CSR_PMPADDR14:
- case CSR_PMPADDR15:
- return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0);
-#endif
- }
- /* used by e.g. MTIME read */
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
-}
-
-/*
- * Check that CSR access is allowed.
- *
- * Adapted from Spike's decode.h:validate_csr
- */
-static void validate_csr(CPURISCVState *env, uint64_t which,
- uint64_t write, uintptr_t ra)
-{
-#ifndef CONFIG_USER_ONLY
- unsigned csr_priv = get_field((which), 0x300);
- unsigned csr_read_only = get_field((which), 0xC00) == 3;
- if (((write) && csr_read_only) || (env->priv < csr_priv)) {
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
- }
-#endif
+ riscv_raise_exception(env, exception, 0);
}
target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
target_ulong csr)
{
- validate_csr(env, csr, 1, GETPC());
- uint64_t csr_backup = csr_read_helper(env, csr);
- csr_write_helper(env, src, csr);
- return csr_backup;
+ target_ulong val = 0;
+ if (riscv_csrrw(env, csr, &val, src, -1) < 0) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ }
+ return val;
}
target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
target_ulong csr, target_ulong rs1_pass)
{
- validate_csr(env, csr, rs1_pass != 0, GETPC());
- uint64_t csr_backup = csr_read_helper(env, csr);
- if (rs1_pass != 0) {
- csr_write_helper(env, src | csr_backup, csr);
+ target_ulong val = 0;
+ if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
- return csr_backup;
+ return val;
}
target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
target_ulong csr, target_ulong rs1_pass)
{
- validate_csr(env, csr, rs1_pass != 0, GETPC());
- uint64_t csr_backup = csr_read_helper(env, csr);
- if (rs1_pass != 0) {
- csr_write_helper(env, (~src) & csr_backup, csr);
+ target_ulong val = 0;
+ if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
- return csr_backup;
+ return val;
}
#ifndef CONFIG_USER_ONLY
-/* iothread_mutex must be held */
-void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value)
-{
- target_ulong old_mip = cpu->env.mip;
- cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0);
-
- if (cpu->env.mip && !old_mip) {
- cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
- } else if (!cpu->env.mip && old_mip) {
- cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
- }
-}
-
-void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
-{
- if (newpriv > PRV_M) {
- g_assert_not_reached();
- }
- if (newpriv == PRV_H) {
- newpriv = PRV_U;
- }
- /* tlb_flush is unnecessary as mode is contained in mmu_idx */
- env->priv = newpriv;
-}
-
target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
{
if (!(env->priv >= PRV_S)) {
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
target_ulong retpc = env->sepc;
if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
- do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+ }
+
+ if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
+ get_field(env->mstatus, MSTATUS_TSR)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
target_ulong mstatus = env->mstatus;
@@ -700,8 +95,8 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
get_field(mstatus, MSTATUS_SPIE));
mstatus = set_field(mstatus, MSTATUS_SPIE, 0);
mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
- riscv_set_mode(env, prev_priv);
- csr_write_helper(env, mstatus, CSR_MSTATUS);
+ riscv_cpu_set_mode(env, prev_priv);
+ env->mstatus = mstatus;
return retpc;
}
@@ -709,12 +104,12 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
{
if (!(env->priv >= PRV_M)) {
- do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
}
target_ulong retpc = env->mepc;
if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
- do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
}
target_ulong mstatus = env->mstatus;
@@ -725,27 +120,38 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
get_field(mstatus, MSTATUS_MPIE));
mstatus = set_field(mstatus, MSTATUS_MPIE, 0);
mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
- riscv_set_mode(env, prev_priv);
- csr_write_helper(env, mstatus, CSR_MSTATUS);
+ riscv_cpu_set_mode(env, prev_priv);
+ env->mstatus = mstatus;
return retpc;
}
-
void helper_wfi(CPURISCVState *env)
{
CPUState *cs = CPU(riscv_env_get_cpu(env));
- cs->halted = 1;
- cs->exception_index = EXCP_HLT;
- cpu_loop_exit(cs);
+ if (env->priv == PRV_S &&
+ env->priv_ver >= PRIV_VERSION_1_10_0 &&
+ get_field(env->mstatus, MSTATUS_TW)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ } else {
+ cs->halted = 1;
+ cs->exception_index = EXCP_HLT;
+ cpu_loop_exit(cs);
+ }
}
void helper_tlb_flush(CPURISCVState *env)
{
RISCVCPU *cpu = riscv_env_get_cpu(env);
CPUState *cs = CPU(cpu);
- tlb_flush(cs);
+ if (env->priv == PRV_S &&
+ env->priv_ver >= PRIV_VERSION_1_10_0 &&
+ get_field(env->mstatus, MSTATUS_TVM)) {
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ } else {
+ tlb_flush(cs);
+ }
}
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index f432f3b7594..459e5564c10 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -138,7 +138,7 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea)
return;
} else {
target_ulong t1 = ctz64(~a);
- target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3;
+ target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2;
target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1;
*sa = base;
*ea = base + range;
@@ -245,7 +245,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
from low to high */
for (i = 0; i < MAX_RISCV_PMPS; i++) {
s = pmp_is_in_range(env, i, addr);
- e = pmp_is_in_range(env, i, addr + size);
+ e = pmp_is_in_range(env, i, addr + size - 1);
/* partially inside */
if ((s + e) == 1) {
diff --git a/target/riscv/trace-events b/target/riscv/trace-events
new file mode 100644
index 00000000000..48af0373df6
--- /dev/null
+++ b/target/riscv/trace-events
@@ -0,0 +1,2 @@
+# target/riscv/cpu_helper.c
+riscv_trap(uint64_t hartid, bool async, uint64_t cause, uint64_t epc, uint64_t tval, const char *desc) "hart:%"PRId64", async:%d, cause:%"PRId64", epc:0x%"PRIx64", tval:0x%"PRIx64", desc=%s"
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 0b6be74f2dd..b98fd64132b 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -44,7 +44,8 @@ typedef struct DisasContext {
/* pc_succ_insn points to the instruction following base.pc_next */
target_ulong pc_succ_insn;
uint32_t opcode;
- uint32_t flags;
+ uint32_t mstatus_fs;
+ uint32_t misa;
uint32_t mem_idx;
/* Remember the rounding mode encoded in the previous fp instruction,
which we have already installed into env->fp_status. Or -1 for
@@ -74,6 +75,11 @@ static const int tcg_memop_lookup[8] = {
#define CASE_OP_32_64(X) case X
#endif
+static inline bool has_ext(DisasContext *ctx, uint32_t ext)
+{
+ return ctx->misa & ext;
+}
+
static void generate_exception(DisasContext *ctx, int excp)
{
tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
@@ -284,24 +290,42 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
tcg_gen_and_tl(source1, source1, source2);
break;
CASE_OP_32_64(OPC_RISC_MUL):
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_mul_tl(source1, source1, source2);
break;
case OPC_RISC_MULH:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_muls2_tl(source2, source1, source1, source2);
break;
case OPC_RISC_MULHSU:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
gen_mulhsu(source1, source1, source2);
break;
case OPC_RISC_MULHU:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_mulu2_tl(source2, source1, source1, source2);
break;
#if defined(TARGET_RISCV64)
case OPC_RISC_DIVW:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_ext32s_tl(source1, source1);
tcg_gen_ext32s_tl(source2, source2);
/* fall through to DIV */
#endif
case OPC_RISC_DIV:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
/* Handle by altering args to tcg_gen_div to produce req'd results:
* For overflow: want source1 in source1 and 1 in source2
* For div by zero: want -1 in source1 and 1 in source2 -> -1 result */
@@ -333,11 +357,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
break;
#if defined(TARGET_RISCV64)
case OPC_RISC_DIVUW:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_ext32u_tl(source1, source1);
tcg_gen_ext32u_tl(source2, source2);
/* fall through to DIVU */
#endif
case OPC_RISC_DIVU:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
cond1 = tcg_temp_new();
zeroreg = tcg_const_tl(0);
resultopt1 = tcg_temp_new();
@@ -357,11 +387,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
break;
#if defined(TARGET_RISCV64)
case OPC_RISC_REMW:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_ext32s_tl(source1, source1);
tcg_gen_ext32s_tl(source2, source2);
/* fall through to REM */
#endif
case OPC_RISC_REM:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
cond1 = tcg_temp_new();
cond2 = tcg_temp_new();
zeroreg = tcg_const_tl(0);
@@ -389,11 +425,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
break;
#if defined(TARGET_RISCV64)
case OPC_RISC_REMUW:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
tcg_gen_ext32u_tl(source1, source1);
tcg_gen_ext32u_tl(source2, source2);
/* fall through to REMU */
#endif
case OPC_RISC_REMU:
+ if (!has_ext(ctx, RVM)) {
+ goto do_illegal;
+ }
cond1 = tcg_temp_new();
zeroreg = tcg_const_tl(0);
resultopt1 = tcg_temp_new();
@@ -411,6 +453,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
tcg_temp_free(zeroreg);
tcg_temp_free(resultopt1);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
return;
@@ -505,14 +548,13 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd,
tcg_temp_free(source1);
}
-static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
- target_ulong imm)
+static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
{
target_ulong next_pc;
/* check misaligned: */
next_pc = ctx->base.pc_next + imm;
- if (!riscv_has_ext(env, RVC)) {
+ if (!has_ext(ctx, RVC)) {
if ((next_pc & 0x3) != 0) {
gen_exception_inst_addr_mis(ctx);
return;
@@ -526,8 +568,8 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
ctx->base.is_jmp = DISAS_NORETURN;
}
-static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
- int rd, int rs1, target_long imm)
+static void gen_jalr(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+ target_long imm)
{
/* no chaining with JALR */
TCGLabel *misaligned = NULL;
@@ -539,7 +581,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
tcg_gen_addi_tl(cpu_pc, cpu_pc, imm);
tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2);
- if (!riscv_has_ext(env, RVC)) {
+ if (!has_ext(ctx, RVC)) {
misaligned = gen_new_label();
tcg_gen_andi_tl(t0, cpu_pc, 0x2);
tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned);
@@ -564,8 +606,8 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
tcg_temp_free(t0);
}
-static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
- int rs1, int rs2, target_long bimm)
+static void gen_branch(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
+ target_long bimm)
{
TCGLabel *l = gen_new_label();
TCGv source1, source2;
@@ -602,7 +644,7 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
gen_goto_tb(ctx, 1, ctx->pc_succ_insn);
gen_set_label(l); /* branch taken */
- if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) {
+ if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) {
/* misaligned */
gen_exception_inst_addr_mis(ctx);
} else {
@@ -651,12 +693,37 @@ static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
tcg_temp_free(dat);
}
+#ifndef CONFIG_USER_ONLY
+/* The states of mstatus_fs are:
+ * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty
+ * We will have already diagnosed disabled state,
+ * and need to turn initial/clean into dirty.
+ */
+static void mark_fs_dirty(DisasContext *ctx)
+{
+ TCGv tmp;
+ if (ctx->mstatus_fs == MSTATUS_FS) {
+ return;
+ }
+ /* Remember the state change for the rest of the TB. */
+ ctx->mstatus_fs = MSTATUS_FS;
+
+ tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS);
+ tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+ tcg_temp_free(tmp);
+}
+#else
+static inline void mark_fs_dirty(DisasContext *ctx) { }
+#endif
+
static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
int rs1, target_long imm)
{
TCGv t0;
- if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+ if (ctx->mstatus_fs == 0) {
gen_exception_illegal(ctx);
return;
}
@@ -667,18 +734,27 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
switch (opc) {
case OPC_RISC_FLW:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL);
/* RISC-V requires NaN-boxing of narrower width floating point values */
tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL);
break;
case OPC_RISC_FLD:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
}
tcg_temp_free(t0);
+
+ mark_fs_dirty(ctx);
}
static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
@@ -686,7 +762,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
{
TCGv t0;
- if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+ if (ctx->mstatus_fs == 0) {
gen_exception_illegal(ctx);
return;
}
@@ -697,11 +773,18 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
switch (opc) {
case OPC_RISC_FSW:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL);
break;
case OPC_RISC_FSD:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
@@ -865,15 +948,22 @@ static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd,
{
switch (opc) {
case OPC_RISC_FMADD_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
case OPC_RISC_FMADD_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
@@ -885,15 +975,22 @@ static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd,
{
switch (opc) {
case OPC_RISC_FMSUB_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
case OPC_RISC_FMSUB_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
@@ -905,15 +1002,22 @@ static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd,
{
switch (opc) {
case OPC_RISC_FNMSUB_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
case OPC_RISC_FNMSUB_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
@@ -925,15 +1029,22 @@ static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd,
{
switch (opc) {
case OPC_RISC_FNMADD_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
case OPC_RISC_FNMADD_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
cpu_fpr[rs2], cpu_fpr[rs3]);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
@@ -944,37 +1055,59 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
int rs1, int rs2, int rm)
{
TCGv t0 = NULL;
+ bool fp_output = true;
- if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+ if (ctx->mstatus_fs == 0) {
goto do_illegal;
}
switch (opc) {
case OPC_RISC_FADD_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FSUB_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FMUL_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FDIV_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FSQRT_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
break;
case OPC_RISC_FSGNJ_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN);
break;
case OPC_RISC_FMIN_S:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
/* also handles: OPC_RISC_FMAX_S */
switch (rm) {
case 0x0:
@@ -990,6 +1123,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
case OPC_RISC_FEQ_S:
/* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
switch (rm) {
case 0x0:
@@ -1006,10 +1142,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
}
gen_set_gpr(rd, t0);
tcg_temp_free(t0);
+ fp_output = false;
break;
case OPC_RISC_FCVT_W_S:
/* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
switch (rs2) {
case 0: /* FCVT_W_S */
@@ -1035,10 +1175,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
}
gen_set_gpr(rd, t0);
tcg_temp_free(t0);
+ fp_output = false;
break;
case OPC_RISC_FCVT_S_W:
/* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
gen_get_gpr(t0, rs1);
switch (rs2) {
@@ -1068,6 +1212,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
case OPC_RISC_FMV_X_S:
/* also OPC_RISC_FCLASS_S */
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
switch (rm) {
case 0: /* FMV */
@@ -1085,9 +1232,13 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
}
gen_set_gpr(rd, t0);
tcg_temp_free(t0);
+ fp_output = false;
break;
case OPC_RISC_FMV_S_X:
+ if (!has_ext(ctx, RVF)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
gen_get_gpr(t0, rs1);
#if defined(TARGET_RISCV64)
@@ -1100,22 +1251,37 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
/* double */
case OPC_RISC_FADD_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FSUB_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FMUL_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FDIV_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
break;
case OPC_RISC_FSQRT_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
gen_set_rm(ctx, rm);
gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
break;
@@ -1125,6 +1291,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
case OPC_RISC_FMIN_D:
/* also OPC_RISC_FMAX_D */
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
switch (rm) {
case 0:
gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
@@ -1138,6 +1307,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
break;
case OPC_RISC_FCVT_S_D:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
switch (rs2) {
case 1:
gen_set_rm(ctx, rm);
@@ -1149,6 +1321,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
break;
case OPC_RISC_FCVT_D_S:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
switch (rs2) {
case 0:
gen_set_rm(ctx, rm);
@@ -1161,6 +1336,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
case OPC_RISC_FEQ_D:
/* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
switch (rm) {
case 0:
@@ -1177,10 +1355,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
}
gen_set_gpr(rd, t0);
tcg_temp_free(t0);
+ fp_output = false;
break;
case OPC_RISC_FCVT_W_D:
/* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
switch (rs2) {
case 0:
@@ -1206,10 +1388,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
}
gen_set_gpr(rd, t0);
tcg_temp_free(t0);
+ fp_output = false;
break;
case OPC_RISC_FCVT_D_W:
/* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
gen_get_gpr(t0, rs1);
switch (rs2) {
@@ -1240,6 +1426,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
#if defined(TARGET_RISCV64)
case OPC_RISC_FMV_X_D:
/* also OPC_RISC_FCLASS_D */
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
switch (rm) {
case 0: /* FMV */
gen_set_gpr(rd, cpu_fpr[rs1]);
@@ -1253,9 +1442,13 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
default:
goto do_illegal;
}
+ fp_output = false;
break;
case OPC_RISC_FMV_D_X:
+ if (!has_ext(ctx, RVD)) {
+ goto do_illegal;
+ }
t0 = tcg_temp_new();
gen_get_gpr(t0, rs1);
tcg_gen_mov_tl(cpu_fpr[rd], t0);
@@ -1269,12 +1462,16 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
tcg_temp_free(t0);
}
gen_exception_illegal(ctx);
- break;
+ return;
+ }
+
+ if (fp_output) {
+ mark_fs_dirty(ctx);
}
}
-static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
- int rd, int rs1, int csr)
+static void gen_system(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+ int csr)
{
TCGv source1, csr_store, dest, rs1_pass, imm_rs1;
source1 = tcg_temp_new();
@@ -1316,7 +1513,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
gen_exception_illegal(ctx);
break;
case 0x102: /* SRET */
- if (riscv_has_ext(env, RVS)) {
+ if (has_ext(ctx, RVS)) {
gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
tcg_gen_exit_tb(NULL, 0); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
@@ -1457,7 +1654,7 @@ static void decode_RV32_64C0(DisasContext *ctx)
}
}
-static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C1(DisasContext *ctx)
{
uint8_t funct3 = extract32(ctx->opcode, 13, 3);
uint8_t rd_rs1 = GET_C_RS1(ctx->opcode);
@@ -1477,7 +1674,7 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
GET_C_IMM(ctx->opcode));
#else
/* C.JAL(RV32) -> jal x1, offset[11:1] */
- gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode));
+ gen_jal(ctx, 1, GET_C_J_IMM(ctx->opcode));
#endif
break;
case 2:
@@ -1556,22 +1753,22 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
break;
case 5:
/* C.J -> jal x0, offset[11:1]*/
- gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode));
+ gen_jal(ctx, 0, GET_C_J_IMM(ctx->opcode));
break;
case 6:
/* C.BEQZ -> beq rs1', x0, offset[8:1]*/
rs1s = GET_C_RS1S(ctx->opcode);
- gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+ gen_branch(ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
break;
case 7:
/* C.BNEZ -> bne rs1', x0, offset[8:1]*/
rs1s = GET_C_RS1S(ctx->opcode);
- gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+ gen_branch(ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
break;
}
}
-static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C2(DisasContext *ctx)
{
uint8_t rd, rs2;
uint8_t funct3 = extract32(ctx->opcode, 13, 3);
@@ -1605,7 +1802,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
if (extract32(ctx->opcode, 12, 1) == 0) {
if (rs2 == 0) {
/* C.JR -> jalr x0, rs1, 0*/
- gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0);
+ gen_jalr(ctx, OPC_RISC_JALR, 0, rd, 0);
} else {
/* C.MV -> add rd, x0, rs2 */
gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2);
@@ -1613,11 +1810,11 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
} else {
if (rd == 0) {
/* C.EBREAK -> ebreak*/
- gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1);
+ gen_system(ctx, OPC_RISC_ECALL, 0, 0, 0x1);
} else {
if (rs2 == 0) {
/* C.JALR -> jalr x1, rs1, 0*/
- gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0);
+ gen_jalr(ctx, OPC_RISC_JALR, 1, rd, 0);
} else {
/* C.ADD -> add rd, rd, rs2 */
gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2);
@@ -1649,7 +1846,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
}
}
-static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C(DisasContext *ctx)
{
uint8_t op = extract32(ctx->opcode, 0, 2);
@@ -1658,15 +1855,15 @@ static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
decode_RV32_64C0(ctx);
break;
case 1:
- decode_RV32_64C1(env, ctx);
+ decode_RV32_64C1(ctx);
break;
case 2:
- decode_RV32_64C2(env, ctx);
+ decode_RV32_64C2(ctx);
break;
}
}
-static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64G(DisasContext *ctx)
{
int rs1;
int rs2;
@@ -1701,13 +1898,13 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
break;
case OPC_RISC_JAL:
imm = GET_JAL_IMM(ctx->opcode);
- gen_jal(env, ctx, rd, imm);
+ gen_jal(ctx, rd, imm);
break;
case OPC_RISC_JALR:
- gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
+ gen_jalr(ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
break;
case OPC_RISC_BRANCH:
- gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
+ gen_branch(ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
GET_B_IMM(ctx->opcode));
break;
case OPC_RISC_LOAD:
@@ -1743,6 +1940,9 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
GET_STORE_IMM(ctx->opcode));
break;
case OPC_RISC_ATOMIC:
+ if (!has_ext(ctx, RVA)) {
+ goto do_illegal;
+ }
gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2);
break;
case OPC_RISC_FMADD:
@@ -1780,38 +1980,41 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
#endif
break;
case OPC_RISC_SYSTEM:
- gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
+ gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
(ctx->opcode & 0xFFF00000) >> 20);
break;
+ do_illegal:
default:
gen_exception_illegal(ctx);
break;
}
}
-static void decode_opc(CPURISCVState *env, DisasContext *ctx)
+static void decode_opc(DisasContext *ctx)
{
/* check for compressed insn */
if (extract32(ctx->opcode, 0, 2) != 3) {
- if (!riscv_has_ext(env, RVC)) {
+ if (!has_ext(ctx, RVC)) {
gen_exception_illegal(ctx);
} else {
ctx->pc_succ_insn = ctx->base.pc_next + 2;
- decode_RV32_64C(env, ctx);
+ decode_RV32_64C(ctx);
}
} else {
ctx->pc_succ_insn = ctx->base.pc_next + 4;
- decode_RV32_64G(env, ctx);
+ decode_RV32_64G(ctx);
}
}
-static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
+static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
+ CPURISCVState *env = cpu->env_ptr;
ctx->pc_succ_insn = ctx->base.pc_first;
- ctx->flags = ctx->base.tb->flags;
ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK;
+ ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS;
+ ctx->misa = env->misa;
ctx->frm = -1; /* unknown rounding mode */
}
@@ -1842,14 +2045,13 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
return true;
}
-
static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
CPURISCVState *env = cpu->env_ptr;
ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
- decode_opc(env, ctx);
+ decode_opc(ctx);
ctx->base.pc_next = ctx->pc_succ_insn;
if (ctx->base.is_jmp == DISAS_NEXT) {