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) {