From b55d7d34f6adf7bd246ae2eabf2d66eb5e024a68 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 6 May 2019 22:49:53 +0000 Subject: [PATCH 01/32] target/riscv: Allow setting ISA extensions via CPU props This patch allows us to enable/disable the RISC-V ISA extensions from the QEMU command line. This works with the rv32 and rv64 machines. The idea is that in the future we can now add extensions and leave them disabled by default until enabled by the user. Signed-off-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 70 ++++++++++++++++++++++++++++++++++++++++++++-- target/riscv/cpu.h | 11 ++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6f2b644220..0632ac08cf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -119,7 +120,8 @@ static void riscv_any_cpu_init(Object *obj) static void riscv_base32_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + /* We set this in the realise function */ + set_misa(env, 0); } static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) @@ -156,7 +158,8 @@ static void rv32imacu_nommu_cpu_init(Object *obj) static void riscv_base64_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; - set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + /* We set this in the realise function */ + set_misa(env, 0); } static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) @@ -315,6 +318,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); int priv_version = PRIV_VERSION_1_10_0; int user_version = USER_VERSION_2_02_0; + target_ulong target_misa = 0; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -358,6 +362,58 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) set_feature(env, RISCV_FEATURE_PMP); } + /* If misa isn't set (rv32 and rv64 machines) set it here */ + if (!env->misa) { + /* Do some ISA extension error checking */ + if (cpu->cfg.ext_i && cpu->cfg.ext_e) { + error_setg(errp, + "I and E extensions are incompatible"); + return; + } + + if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & + cpu->cfg.ext_a & cpu->cfg.ext_f & + cpu->cfg.ext_d)) { + warn_report("Setting G will also set IMAFD"); + cpu->cfg.ext_i = true; + cpu->cfg.ext_m = true; + cpu->cfg.ext_a = true; + cpu->cfg.ext_f = true; + cpu->cfg.ext_d = true; + } + + /* Set the ISA extensions, checks should have happened above */ + if (cpu->cfg.ext_i) { + target_misa |= RVI; + } + if (cpu->cfg.ext_e) { + target_misa |= RVE; + } + if (cpu->cfg.ext_m) { + target_misa |= RVM; + } + if (cpu->cfg.ext_a) { + target_misa |= RVA; + } + if (cpu->cfg.ext_f) { + target_misa |= RVF; + } + if (cpu->cfg.ext_d) { + target_misa |= RVD; + } + if (cpu->cfg.ext_c) { + target_misa |= RVC; + } + if (cpu->cfg.ext_s) { + target_misa |= RVS; + } + if (cpu->cfg.ext_u) { + target_misa |= RVU; + } + + set_misa(env, RVXLEN | target_misa); + } + riscv_cpu_register_gdb_regs_for_features(cs); qemu_init_vcpu(cs); @@ -379,6 +435,16 @@ static const VMStateDescription vmstate_riscv_cpu = { }; static Property riscv_cpu_properties[] = { + DEFINE_PROP_BOOL("i", RISCVCPU, cfg.ext_i, true), + DEFINE_PROP_BOOL("e", RISCVCPU, cfg.ext_e, false), + DEFINE_PROP_BOOL("g", RISCVCPU, cfg.ext_g, true), + DEFINE_PROP_BOOL("m", RISCVCPU, cfg.ext_m, true), + DEFINE_PROP_BOOL("a", RISCVCPU, cfg.ext_a, true), + DEFINE_PROP_BOOL("f", RISCVCPU, cfg.ext_f, true), + DEFINE_PROP_BOOL("d", RISCVCPU, cfg.ext_d, true), + DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true), + DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), + DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_STRING("user_spec", RISCVCPU, cfg.user_spec), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6c5de37b25..b47cde5017 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -211,6 +211,17 @@ typedef struct RISCVCPU { /* Configuration Settings */ struct { + bool ext_i; + bool ext_e; + bool ext_g; + bool ext_m; + bool ext_a; + bool ext_f; + bool ext_d; + bool ext_c; + bool ext_s; + bool ext_u; + char *priv_spec; char *user_spec; bool mmu; From b9d1848ebe8f083482307be725cca7883fca7566 Mon Sep 17 00:00:00 2001 From: Nathaniel Graff Date: Tue, 24 Jul 2018 09:52:46 -0700 Subject: [PATCH 02/32] sifive_prci: Read and write PRCI registers Writes to the SiFive PRCI registers are preserved while leaving the ready bits set for the HFX/HFR oscillators and the lock bit set for the PLL. Signed-off-by: Nathaniel Graff Reviewed-by: Michael Clark Signed-off-by: Palmer Dabbelt --- hw/riscv/sifive_prci.c | 49 ++++++++++++++++++++++++++++------ include/hw/riscv/sifive_prci.h | 32 ++++++++++++++++++++++ 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c index fa136b5a9f..f406682c91 100644 --- a/hw/riscv/sifive_prci.c +++ b/hw/riscv/sifive_prci.c @@ -24,15 +24,18 @@ #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; @@ -41,7 +44,30 @@ 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 = { @@ -61,6 +87,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/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h index b6f4c486cc..bd51c4af3c 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); From 49db9fa1fd7c252596b53cf80876e06f407d09ed Mon Sep 17 00:00:00 2001 From: Dayeol Lee Date: Fri, 17 May 2019 15:10:58 -0700 Subject: [PATCH 03/32] target/riscv: Fix PMP range boundary address bug A wrong address is passed to `pmp_is_in_range` while checking if a memory access is within a PMP range. Since the ending address of the pmp range (i.e., pmp_state.addr[i].ea) is set to the last address in the range (i.e., pmp base + pmp size - 1), memory accesses containg the last address in the range will always fail. For example, assume that a PMP range is 4KB from 0x87654000 such that the last address within the range is 0x87654fff. 1-byte access to 0x87654fff should be considered to be fully inside the PMP range. However the access now fails and complains partial inclusion because pmp_is_in_range(env, i, addr + size) returns 0 whereas pmp_is_in_range(env, i, addr) returns 1. Signed-off-by: Dayeol Lee Reviewed-by: Alistair Francis Reviewed-by: Michael Clark Signed-off-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/pmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index fed1c3c030..e0fe206407 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -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) { From cbf5827693addaff4e4d2102afedbf078a204eb2 Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Fri, 17 May 2019 15:11:06 -0700 Subject: [PATCH 04/32] target/riscv: Implement riscv_cpu_unassigned_access This patch adds support for the riscv_cpu_unassigned_access call and will raise a load or store access fault. Signed-off-by: Michael Clark [Changes by AF: - Squash two patches and rewrite commit message - Set baddr to the access address ] Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 2 ++ target/riscv/cpu_helper.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0632ac08cf..5b9fae608c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -482,6 +482,7 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = riscv_cpu_disas_set_info; #ifndef CONFIG_USER_ONLY + cc->do_unassigned_access = riscv_cpu_unassigned_access; cc->do_unaligned_access = riscv_cpu_do_unaligned_access; cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug; #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b47cde5017..2e74331253 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -259,6 +259,8 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +void riscv_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, + bool is_exec, int unused, unsigned size); char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(void); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8b6754b917..0bbfb7f48b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -375,6 +375,22 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return phys_addr; } +void riscv_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write, + bool is_exec, int unused, unsigned size) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + if (is_write) { + cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } else { + cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; + } + + env->badaddr = addr; + riscv_raise_exception(&cpu->env, cs->exception_index, GETPC()); +} + void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) From e0f8fa72deba7ac7a7ae06ba25e6498aaad93ace Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Thu, 30 May 2019 14:51:30 +0100 Subject: [PATCH 05/32] RISC-V: Only Check PMP if MMU translation succeeds The current implementation unnecessarily checks for PMP even if MMU translation failed. This may trigger a wrong PMP access exception instead of a page exception. For example, the very first instruction fetched after the first satp write in S-Mode will trigger a PMP access fault instead of an instruction fetch page fault. This patch prioritises MMU exceptions over PMP exceptions and only checks for PMP if MMU translation succeeds. This patch is required for future commits that properly report PMP exception violations if PTW succeeds. Signed-off-by: Hesham Almatary Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 0bbfb7f48b..a45b05ef83 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -436,6 +436,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, " prot %d\n", __func__, address, ret, pa, prot); if (riscv_feature(env, RISCV_FEATURE_PMP) && + (ret == TRANSLATE_SUCCESS) && !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type)) { ret = TRANSLATE_FAIL; } From 635b0b0ea39a13d1a3df932452e5728aebbb3f6e Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Fri, 14 Jun 2019 05:17:28 -0700 Subject: [PATCH 06/32] RISC-V: Raise access fault exceptions on PMP violations Section 3.6 in RISC-V v1.10 privilege specification states that PMP violations report "access exceptions." The current PMP implementation has a bug which wrongly reports "page exceptions" on PMP violations. This patch fixes this bug by reporting the correct PMP access exceptions trap values. Signed-off-by: Hesham Almatary Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index a45b05ef83..ffbfaf4332 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -337,12 +337,13 @@ restart: } static void raise_mmu_exception(CPURISCVState *env, target_ulong address, - MMUAccessType access_type) + MMUAccessType access_type, bool pmp_violation) { CPUState *cs = env_cpu(env); int page_fault_exceptions = (env->priv_ver >= PRIV_VERSION_1_10_0) && - get_field(env->satp, SATP_MODE) != VM_1_10_MBARE; + get_field(env->satp, SATP_MODE) != VM_1_10_MBARE && + !pmp_violation; switch (access_type) { case MMU_INST_FETCH: cs->exception_index = page_fault_exceptions ? @@ -424,6 +425,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPURISCVState *env = &cpu->env; hwaddr pa = 0; int prot; + bool pmp_violation = false; int ret = TRANSLATE_FAIL; qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", @@ -438,6 +440,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type)) { + pmp_violation = true; ret = TRANSLATE_FAIL; } if (ret == TRANSLATE_SUCCESS) { @@ -447,7 +450,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { - raise_mmu_exception(env, address, access_type); + raise_mmu_exception(env, address, access_type, pmp_violation); riscv_raise_exception(env, cs->exception_index, retaddr); } #else From cc0fdb298517ce56c770803447f8b02a90271152 Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Thu, 30 May 2019 14:51:32 +0100 Subject: [PATCH 07/32] RISC-V: Check for the effective memory privilege mode during PMP checks The current PMP check function checks for env->priv which is not the effective memory privilege mode. For example, mstatus.MPRV could be set while executing in M-Mode, and in that case the privilege mode for the PMP check should be S-Mode rather than M-Mode (in env->priv) if mstatus.MPP == PRV_S. This patch passes the effective memory privilege mode to the PMP check. Functions that call the PMP check should pass the correct memory privilege mode after reading mstatus' MPRV/MPP or hstatus.SPRV (if Hypervisor mode exists). Suggested-by: Alistair Francis Signed-off-by: Hesham Almatary Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 10 +++++++++- target/riscv/pmp.c | 6 +++--- target/riscv/pmp.h | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index ffbfaf4332..71b8123b10 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -427,19 +427,27 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, int prot; bool pmp_violation = false; int ret = TRANSLATE_FAIL; + int mode = mmu_idx; qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx); + if (mode == PRV_M && access_type != MMU_INST_FETCH) { + if (get_field(env->mstatus, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + } + } + 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 (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && - !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type)) { + !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type, + mode)) { pmp_violation = true; ret = TRANSLATE_FAIL; } diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index e0fe206407..5944f4cb66 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -228,7 +228,7 @@ static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) * Check if the address has required RWX privs to complete desired operation */ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t privs) + target_ulong size, pmp_priv_t privs, target_ulong mode) { int i = 0; int ret = -1; @@ -264,7 +264,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, } allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; - if ((env->priv != PRV_M) || pmp_is_locked(env, i)) { + if ((mode != PRV_M) || pmp_is_locked(env, i)) { allowed_privs &= env->pmp_state.pmp[i].cfg_reg; } @@ -280,7 +280,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* No rule matched */ if (ret == -1) { - if (env->priv == PRV_M) { + if (mode == PRV_M) { ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an * M-Mode access, the access succeeds */ } else { diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h index 66790950eb..8e19793132 100644 --- a/target/riscv/pmp.h +++ b/target/riscv/pmp.h @@ -59,6 +59,6 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong val); target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index); bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, - target_ulong size, pmp_priv_t priv); + target_ulong size, pmp_priv_t priv, target_ulong mode); #endif From 1f447aec787bfbbd078afccae44fc4c92acb4fed Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Fri, 14 Jun 2019 05:19:02 -0700 Subject: [PATCH 08/32] RISC-V: Check PMP during Page Table Walks The PMP should be checked when doing a page table walk, and report access fault exception if the to-be-read PTE failed the PMP check. Suggested-by: Jonathan Behrens Signed-off-by: Hesham Almatary Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2e74331253..934b71c85e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -81,6 +81,7 @@ enum { #define PRIV_VERSION_1_09_1 0x00010901 #define PRIV_VERSION_1_10_0 0x00011000 +#define TRANSLATE_PMP_FAIL 2 #define TRANSLATE_FAIL 1 #define TRANSLATE_SUCCESS 0 #define MMU_USER_IDX 3 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 71b8123b10..66be83210f 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -230,6 +230,12 @@ restart: /* check that physical address of PTE is legal */ target_ulong pte_addr = base + idx * ptesize; + + if (riscv_feature(env, RISCV_FEATURE_PMP) && + !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), + 1 << MMU_DATA_LOAD, PRV_S)) { + return TRANSLATE_PMP_FAIL; + } #if defined(TARGET_RISCV32) target_ulong pte = ldl_phys(cs->as, pte_addr); #elif defined(TARGET_RISCV64) @@ -448,8 +454,10 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, (ret == TRANSLATE_SUCCESS) && !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type, mode)) { + ret = TRANSLATE_PMP_FAIL; + } + if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; - ret = TRANSLATE_FAIL; } if (ret == TRANSLATE_SUCCESS) { tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, From f8162068f18f2f264a0355938784f54089234211 Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Thu, 30 May 2019 14:51:34 +0100 Subject: [PATCH 09/32] RISC-V: Fix a PMP bug where it succeeds even if PMP entry is off The current implementation returns 1 (PMP check success) if the address is in range even if the PMP entry is off. This is a bug. For example, if there is a PMP check in S-Mode which is in range, but its PMP entry is off, this will succeed, which it should not. The patch fixes this bug by only checking the PMP permissions if the address is in range and its corresponding PMP entry it not off. Otherwise, it will keep the ret = -1 which will be checked and handled correctly at the end of the function. Signed-off-by: Hesham Almatary Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/pmp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 5944f4cb66..958c7502a0 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -258,11 +258,12 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* fully inside */ const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); - if ((s + e) == 2) { - if (PMP_AMATCH_OFF == a_field) { - return 1; - } + /* + * If the PMP entry is not off and the address is in range, do the priv + * check + */ + if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) { allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; if ((mode != PRV_M) || pmp_is_locked(env, i)) { allowed_privs &= env->pmp_state.pmp[i].cfg_reg; From db21e6f72721996ddf1948c35a8ee35238089da4 Mon Sep 17 00:00:00 2001 From: Hesham Almatary Date: Thu, 30 May 2019 14:51:35 +0100 Subject: [PATCH 10/32] RISC-V: Fix a PMP check with the correct access size The PMP check should be of the memory access size rather than TARGET_PAGE_SIZE. Signed-off-by: Hesham Almatary Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 66be83210f..e1b079e69c 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -452,8 +452,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (riscv_feature(env, RISCV_FEATURE_PMP) && (ret == TRANSLATE_SUCCESS) && - !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << access_type, - mode)) { + !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { ret = TRANSLATE_PMP_FAIL; } if (ret == TRANSLATE_PMP_FAIL) { From 5b7ae1cec9af5bf96fdc963a52a4595c51b128f9 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Wed, 29 May 2019 01:52:01 -0700 Subject: [PATCH 11/32] riscv: virt: Correct pci "bus-range" encoding The largest pci bus number should be calculated from ECAM size, instead of its base address. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- hw/riscv/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 84d94d0c42..487f61404b 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -298,7 +298,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "device_type", "pci"); qemu_fdt_setprop_cell(fdt, nodename, "linux,pci-domain", 0); qemu_fdt_setprop_cells(fdt, nodename, "bus-range", 0, - memmap[VIRT_PCIE_ECAM].base / + memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); qemu_fdt_setprop(fdt, nodename, "dma-coherent", NULL, 0); qemu_fdt_setprop_cells(fdt, nodename, "reg", 0, memmap[VIRT_PCIE_ECAM].base, From c988de4119af0a0ae7983e870babb5f15793590e Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Fri, 14 Jun 2019 04:58:41 -0700 Subject: [PATCH 12/32] RISC-V: Fix a memory leak when realizing a sifive_e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity pointed out a memory leak in riscv_sifive_e_soc_realize(), where a pair of recently added MemoryRegion instances would not be freed if there were errors elsewhere in the function. The fix here is to simply not use dynamic allocation for these instances: there's always one of each in SiFiveESoCState, so instead we just include them within the struct. Fixes: 30efbf330a45 ("SiFive RISC-V GPIO Device") Signed-off-by: Palmer Dabbelt Suggested-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis --- hw/riscv/sifive_e.c | 13 ++++++------- include/hw/riscv/sifive_e.h | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 80ac56fa7d..a5b4086da3 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -158,17 +158,15 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) 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", + memory_region_init_rom(&s->mask_rom, NULL, "riscv.sifive.e.mrom", memmap[SIFIVE_E_MROM].size, &error_fatal); memory_region_add_subregion(sys_mem, - memmap[SIFIVE_E_MROM].base, mask_rom); + memmap[SIFIVE_E_MROM].base, &s->mask_rom); /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base, @@ -228,10 +226,11 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size); /* Flash memory */ - memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip", + memory_region_init_ram(&s->xip_mem, NULL, "riscv.sifive.e.xip", memmap[SIFIVE_E_XIP].size, &error_fatal); - memory_region_set_readonly(xip_mem, true); - memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem); + memory_region_set_readonly(&s->xip_mem, true); + memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, + &s->xip_mem); } static void riscv_sifive_e_machine_init(MachineClass *mc) diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 3b14eb7462..d175b24cb2 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -33,6 +33,8 @@ typedef struct SiFiveESoCState { RISCVHartArrayState cpus; DeviceState *plic; SIFIVEGPIOState gpio; + MemoryRegion xip_mem; + MemoryRegion mask_rom; } SiFiveESoCState; typedef struct SiFiveEState { From c1fb65e63cfca4506a14b084afd0eca2dc464fe8 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:02 -0700 Subject: [PATCH 13/32] target/riscv: Restructure deprecatd CPUs Restructure the deprecated CPUs to make it clear in the code that these are depreated. They are already marked as deprecated in qemu-deprecated.texi. There are no functional changes. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 18 ++++++++++-------- target/riscv/cpu.h | 13 +++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5b9fae608c..17467c3d87 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -559,18 +559,20 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init), #if defined(TARGET_RISCV32) DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init) + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init), + /* Depreacted */ + DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init) #elif defined(TARGET_RISCV64) DEFINE_CPU(TYPE_RISCV_CPU_BASE64, riscv_base64_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init), DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E51, rv64imacu_nommu_cpu_init), - DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init) + DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U54, rv64gcsu_priv1_10_0_cpu_init), + /* Deprecated */ + DEFINE_CPU(TYPE_RISCV_CPU_RV64IMACU_NOMMU, rv64imacu_nommu_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init), + DEFINE_CPU(TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init) #endif }; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 934b71c85e..5eb9cab2ad 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -35,16 +35,17 @@ #define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any") #define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32") #define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64") -#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1") -#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0") -#define TYPE_RISCV_CPU_RV32IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv32imacu-nommu") -#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1") -#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0") -#define TYPE_RISCV_CPU_RV64IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv64imacu-nommu") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") #define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") #define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") #define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") +/* Deprecated */ +#define TYPE_RISCV_CPU_RV32IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv32imacu-nommu") +#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1") +#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0") +#define TYPE_RISCV_CPU_RV64IMACU_NOMMU RISCV_CPU_TYPE_NAME("rv64imacu-nommu") +#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1") +#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0") #define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2)) #define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2)) From 6729dbbd420696fcf69cf2c86bdfc66e072058ce Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:05 -0700 Subject: [PATCH 14/32] target/riscv: Add the privledge spec version 1.11.0 Add support for the ratified RISC-V privledge spec. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 1 + target/riscv/insn_trans/trans_privileged.inc.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 5eb9cab2ad..d559d28bcd 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -81,6 +81,7 @@ enum { #define USER_VERSION_2_02_0 0x00020200 #define PRIV_VERSION_1_09_1 0x00010901 #define PRIV_VERSION_1_10_0 0x00011000 +#define PRIV_VERSION_1_11_0 0x00011100 #define TRANSLATE_PMP_FAIL 2 #define TRANSLATE_FAIL 1 diff --git a/target/riscv/insn_trans/trans_privileged.inc.c b/target/riscv/insn_trans/trans_privileged.inc.c index 664d6ba3f2..c5e4b3e49a 100644 --- a/target/riscv/insn_trans/trans_privileged.inc.c +++ b/target/riscv/insn_trans/trans_privileged.inc.c @@ -90,7 +90,7 @@ static bool trans_wfi(DisasContext *ctx, arg_wfi *a) static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) { #ifndef CONFIG_USER_ONLY - if (ctx->priv_ver == PRIV_VERSION_1_10_0) { + if (ctx->priv_ver >= PRIV_VERSION_1_10_0) { gen_helper_tlb_flush(cpu_env); return true; } From 747a43e818dc36bd50ef98c2b11a7c31ceb810fa Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:08 -0700 Subject: [PATCH 15/32] target/riscv: Add the mcountinhibit CSR 1.11 defines mcountinhibit, which has the same numeric CSR value as mucounteren from 1.09.1 but has different semantics. This patch enables the CSR for 1.11-based targets, which is trivial to implement because the counters in QEMU never tick (legal according to the spec). Signed-off-by: Alistair Francis [Palmer: Fix counter access semantics, change commit message to indicate the behavior is fully emulated.] Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_bits.h | 1 + target/riscv/csr.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 47450a3cdb..11f971ad5d 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -136,6 +136,7 @@ #define CSR_MCOUNTEREN 0x306 /* Legacy Counter Setup (priv v1.9.1) */ +/* Update to #define CSR_MCOUNTINHIBIT 0x320 for 1.11.0 */ #define CSR_MUCOUNTEREN 0x320 #define CSR_MSCOUNTEREN 0x321 #define CSR_MHCOUNTEREN 0x322 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c67d29e206..448162e484 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -56,6 +56,15 @@ static int fs(CPURISCVState *env, int csrno) static int ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) + /* + * The counters are always enabled on newer priv specs, as the CSR has + * changed from controlling that the counters can be read to controlling + * that the counters increment. + */ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return 0; + } + uint32_t ctr_en = ~0u; if (env->priv < PRV_M) { @@ -461,18 +470,22 @@ static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val) return 0; } +/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */ static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val) { - if (env->priv_ver > PRIV_VERSION_1_09_1) { + if (env->priv_ver > PRIV_VERSION_1_09_1 + && env->priv_ver < PRIV_VERSION_1_11_0) { return -1; } *val = env->mcounteren; return 0; } +/* This regiser is replaced with CSR_MCOUNTINHIBIT in 1.11.0 */ static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val) { - if (env->priv_ver > PRIV_VERSION_1_09_1) { + if (env->priv_ver > PRIV_VERSION_1_09_1 + && env->priv_ver < PRIV_VERSION_1_11_0) { return -1; } env->mcounteren = val; From e3147506b02edcdd7c14ebb41a10fcc3027dcc5c Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:11 -0700 Subject: [PATCH 16/32] target/riscv: Set privledge spec 1.11.0 as default Set the priv spec version 1.11.0 as the default and allow selecting it via the command line. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 17467c3d87..ba1325f435 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -111,7 +111,7 @@ static void riscv_any_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_11_0); set_resetvec(env, DEFAULT_RSTVEC); } @@ -316,7 +316,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPU *cpu = RISCV_CPU(dev); CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); - int priv_version = PRIV_VERSION_1_10_0; + int priv_version = PRIV_VERSION_1_11_0; int user_version = USER_VERSION_2_02_0; target_ulong target_misa = 0; Error *local_err = NULL; @@ -328,7 +328,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } if (cpu->cfg.priv_spec) { - if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { + if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) { + priv_version = PRIV_VERSION_1_11_0; + } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) { priv_version = PRIV_VERSION_1_10_0; } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.9.1")) { priv_version = PRIV_VERSION_1_09_1; From a101b643c2a74671ffedd50f7a2974937c7a0791 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:13 -0700 Subject: [PATCH 17/32] qemu-deprecated.texi: Deprecate the RISC-V privledge spec 1.09.1 Deprecate the RISC-V privledge spec version 1.09.1 in favour of the new 1.10.0 and the ratified 1.11.0. Signed-off-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- qemu-deprecated.texi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index df04f2840b..97ea4ef300 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -144,6 +144,14 @@ The ``acl_show'', ``acl_reset'', ``acl_policy'', ``acl_add'', and ``acl_remove'' commands are deprecated with no replacement. Authorization for VNC should be performed using the pluggable QAuthZ objects. +@section Guest Emulator ISAs + +@subsection RISC-V ISA privledge specification version 1.09.1 (since 4.1) + +The RISC-V ISA privledge specification version 1.09.1 has been deprecated. +QEMU supports both the newer version 1.10.0 and the ratified version 1.11.0, these +should be used instead of the 1.09.1 version. + @section System emulator CPUS @subsection RISC-V ISA CPUs (since 4.1) From bdddd44635a7d3b0f746e449ff7a02328bc2b405 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:16 -0700 Subject: [PATCH 18/32] target/riscv: Require either I or E base extension Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ba1325f435..1689ffecf8 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -373,6 +373,12 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) { + error_setg(errp, + "Either I or E extension must be set"); + return; + } + if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m & cpu->cfg.ext_a & cpu->cfg.ext_f & cpu->cfg.ext_d)) { From c9a73910c34a2147bcf6a3b5194d27abb19c2e54 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:19 -0700 Subject: [PATCH 19/32] target/riscv: Remove user version information Remove the user version information. This was never used and never publically exposed in a release of QEMU, so let's just remove it. In future to manage versions we can extend the extension properties to specify version. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 32 +++++++++----------------------- target/riscv/cpu.h | 2 -- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1689ffecf8..6a54ebf10c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -89,9 +89,8 @@ static void set_misa(CPURISCVState *env, target_ulong misa) env->misa_mask = env->misa = misa; } -static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) +static void set_priv_version(CPURISCVState *env, int priv_ver) { - env->user_ver = user_ver; env->priv_ver = priv_ver; } @@ -111,7 +110,7 @@ static void riscv_any_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_11_0); + set_priv_version(env, PRIV_VERSION_1_11_0); set_resetvec(env, DEFAULT_RSTVEC); } @@ -128,7 +127,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_priv_version(env, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); @@ -138,7 +137,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_priv_version(env, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); @@ -148,7 +147,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_priv_version(env, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_PMP); } @@ -166,7 +165,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_priv_version(env, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); @@ -176,7 +175,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_priv_version(env, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); set_feature(env, RISCV_FEATURE_PMP); @@ -186,7 +185,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); - set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_priv_version(env, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_PMP); } @@ -317,7 +316,6 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) CPURISCVState *env = &cpu->env; RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); int priv_version = PRIV_VERSION_1_11_0; - int user_version = USER_VERSION_2_02_0; target_ulong target_misa = 0; Error *local_err = NULL; @@ -342,18 +340,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } } - if (cpu->cfg.user_spec) { - if (!g_strcmp0(cpu->cfg.user_spec, "v2.02.0")) { - user_version = USER_VERSION_2_02_0; - } else { - error_setg(errp, - "Unsupported user spec version '%s'", - cpu->cfg.user_spec); - return; - } - } - - set_versions(env, user_version, priv_version); + set_priv_version(env, priv_version); set_resetvec(env, DEFAULT_RSTVEC); if (cpu->cfg.mmu) { @@ -454,7 +441,6 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), - DEFINE_PROP_STRING("user_spec", RISCVCPU, cfg.user_spec), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), DEFINE_PROP_END_OF_LIST(), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d559d28bcd..0855277b92 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -78,7 +78,6 @@ enum { RISCV_FEATURE_MISA }; -#define USER_VERSION_2_02_0 0x00020200 #define PRIV_VERSION_1_09_1 0x00010901 #define PRIV_VERSION_1_10_0 0x00011000 #define PRIV_VERSION_1_11_0 0x00011100 @@ -105,7 +104,6 @@ struct CPURISCVState { target_ulong badaddr; - target_ulong user_ver; target_ulong priv_ver; target_ulong misa; target_ulong misa_mask; From 0a13a5b856ebb59dec6d165b87a0ba0e1e2dd952 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 17 Jun 2019 18:31:22 -0700 Subject: [PATCH 20/32] target/riscv: Add support for disabling/enabling Counters Add support for disabling/enabling the "Counters" extension. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 1 + target/riscv/csr.c | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6a54ebf10c..be90fa7d08 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -440,6 +440,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("c", RISCVCPU, cfg.ext_c, true), DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), + DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 0855277b92..4d4e0f89e2 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -222,6 +222,7 @@ typedef struct RISCVCPU { bool ext_c; bool ext_s; bool ext_u; + bool ext_counters; char *priv_spec; char *user_spec; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 448162e484..de67741f36 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -56,17 +56,24 @@ static int fs(CPURISCVState *env, int csrno) static int ctr(CPURISCVState *env, int csrno) { #if !defined(CONFIG_USER_ONLY) + CPUState *cs = env_cpu(env); + RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t ctr_en = ~0u; + + if (!cpu->cfg.ext_counters) { + /* The Counters extensions is not enabled */ + return -1; + } + /* - * The counters are always enabled on newer priv specs, as the CSR has - * changed from controlling that the counters can be read to controlling - * that the counters increment. + * The counters are always enabled at run time on newer priv specs, as the + * CSR has changed from controlling that the counters can be read to + * controlling that the counters increment. */ if (env->priv_ver > PRIV_VERSION_1_09_1) { return 0; } - uint32_t ctr_en = ~0u; - if (env->priv < PRV_M) { ctr_en &= env->mcounteren; } From 50fba816cd226001bec3e495c39879deb2fa5432 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Mon, 24 Jun 2019 01:59:05 -0700 Subject: [PATCH 21/32] RISC-V: Add support for the Zifencei extension fence.i has been split out of the base ISA as part of the ratification process. This patch adds a Zifencei argument, which disables the fence.i instruction. Signed-off-by: Palmer Dabbelt Reviewed-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 1 + target/riscv/insn_trans/trans_rvi.inc.c | 4 ++++ target/riscv/translate.c | 3 +++ 4 files changed, 9 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index be90fa7d08..bbad39a337 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -441,6 +441,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("s", RISCVCPU, cfg.ext_s, true), DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), + DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4d4e0f89e2..ba551cd308 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -223,6 +223,7 @@ typedef struct RISCVCPU { bool ext_s; bool ext_u; bool ext_counters; + bool ext_ifencei; char *priv_spec; char *user_spec; diff --git a/target/riscv/insn_trans/trans_rvi.inc.c b/target/riscv/insn_trans/trans_rvi.inc.c index 6cda078ed6..ea6473111c 100644 --- a/target/riscv/insn_trans/trans_rvi.inc.c +++ b/target/riscv/insn_trans/trans_rvi.inc.c @@ -484,6 +484,10 @@ static bool trans_fence(DisasContext *ctx, arg_fence *a) static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) { + if (!ctx->ext_ifencei) { + return false; + } + /* * FENCE_I is a no-op in QEMU, * however we need to end the translation block diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 313c27b700..8d6ab73258 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -54,6 +54,7 @@ typedef struct DisasContext { to any system register, which includes CSR_FRM, so we do not have to reset this known value. */ int frm; + bool ext_ifencei; } DisasContext; #ifdef TARGET_RISCV64 @@ -752,6 +753,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cs->env_ptr; + RISCVCPU *cpu = RISCV_CPU(cs); ctx->pc_succ_insn = ctx->base.pc_first; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; @@ -759,6 +761,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->priv_ver = env->priv_ver; ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ + ctx->ext_ifencei = cpu->cfg.ext_ifencei; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) From 591bddea8d874e1500921de0353818e5586618f5 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Mon, 24 Jun 2019 01:59:51 -0700 Subject: [PATCH 22/32] RISC-V: Add support for the Zicsr extension The various CSR instructions have been split out of the base ISA as part of the ratification process. This patch adds a Zicsr argument, which disables all the CSR instructions. Signed-off-by: Palmer Dabbelt Reviewed-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 1 + target/riscv/csr.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index bbad39a337..915b9e77df 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -442,6 +442,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("u", RISCVCPU, cfg.ext_u, true), DEFINE_PROP_BOOL("Counters", RISCVCPU, cfg.ext_counters, true), DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), + DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec), DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true), DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ba551cd308..0adb307f32 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -224,6 +224,7 @@ typedef struct RISCVCPU { bool ext_u; bool ext_counters; bool ext_ifencei; + bool ext_icsr; char *priv_spec; char *user_spec; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index de67741f36..e0d4586760 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -793,6 +793,7 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, { int ret; target_ulong old_value; + RISCVCPU *cpu = env_archcpu(env); /* check privileges and return -1 if check fails */ #if !defined(CONFIG_USER_ONLY) @@ -803,6 +804,11 @@ int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, } #endif + /* ensure the CSR extension is enabled. */ + if (!cpu->cfg.ext_icsr) { + return -1; + } + /* check predicate */ if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) { return -1; From c13b169f1a3dd158d6c75727cdc388f95988db39 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Tue, 25 Jun 2019 04:08:38 +1000 Subject: [PATCH 23/32] RISC-V: Clear load reservations on context switch and SC This prevents a load reservation from being placed in one context/process, then being used in another, resulting in an SC succeeding incorrectly and breaking atomics. Signed-off-by: Joel Sing Reviewed-by: Palmer Dabbelt Reviewed-by: Richard Henderson Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 1 + target/riscv/cpu_helper.c | 10 ++++++++++ target/riscv/insn_trans/trans_rva.inc.c | 8 +++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 915b9e77df..f8d07bd20a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -297,6 +297,7 @@ static void riscv_cpu_reset(CPUState *cs) env->pc = env->resetvec; #endif cs->exception_index = EXCP_NONE; + env->load_res = -1; set_default_nan_mode(1, &env->fp_status); } diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index e1b079e69c..e32b6126af 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -132,6 +132,16 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) } /* tlb_flush is unnecessary as mode is contained in mmu_idx */ env->priv = newpriv; + + /* + * Clear the load reservation - otherwise a reservation placed in one + * context/process can be used by another, resulting in an SC succeeding + * incorrectly. Version 2.2 of the ISA specification explicitly requires + * this behaviour, while later revisions say that the kernel "should" use + * an SC instruction to force the yielding of a load reservation on a + * preemptive context switch. As a result, do both. + */ + env->load_res = -1; } /* get_physical_address - get the physical address for this virtual address diff --git a/target/riscv/insn_trans/trans_rva.inc.c b/target/riscv/insn_trans/trans_rva.inc.c index f6dbbc065e..fadd88849e 100644 --- a/target/riscv/insn_trans/trans_rva.inc.c +++ b/target/riscv/insn_trans/trans_rva.inc.c @@ -61,7 +61,7 @@ static inline bool gen_sc(DisasContext *ctx, arg_atomic *a, TCGMemOp mop) gen_set_label(l1); /* - * Address comparion failure. However, we still need to + * Address comparison failure. However, we still need to * provide the memory barrier implied by AQ/RL. */ tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + a->rl * TCG_BAR_STRL); @@ -69,6 +69,12 @@ static inline bool gen_sc(DisasContext *ctx, arg_atomic *a, TCGMemOp mop) gen_set_gpr(a->rd, dat); gen_set_label(l2); + /* + * Clear the load reservation, since an SC must fail if there is + * an SC to any address, in between an LR and SC pair. + */ + tcg_gen_movi_tl(load_res, -1); + tcg_temp_free(dat); tcg_temp_free(src1); tcg_temp_free(src2); From ab433cccadb9f8e73bc3350273b049377d54d083 Mon Sep 17 00:00:00 2001 From: Jim Wilson Date: Tue, 18 Jun 2019 15:32:52 -0700 Subject: [PATCH 24/32] RISC-V: Update syscall list for 32-bit support. 32-bit RISC-V uses _llseek instead of lseek as syscall number 62. Update syscall list from open-embedded build, primarily because 32-bit RISC-V requires statx support. Tested with cross gcc testsuite runs for rv32 and rv64, with the pending statx patch also applied. Signed-off-by: Jim Wilson Reviewed-by: Laurent Vivier Signed-off-by: Palmer Dabbelt --- linux-user/riscv/syscall_nr.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h index dab6509e3a..5c87282209 100644 --- a/linux-user/riscv/syscall_nr.h +++ b/linux-user/riscv/syscall_nr.h @@ -72,7 +72,11 @@ #define TARGET_NR_pipe2 59 #define TARGET_NR_quotactl 60 #define TARGET_NR_getdents64 61 +#ifdef TARGET_RISCV32 +#define TARGET_NR__llseek 62 +#else #define TARGET_NR_lseek 62 +#endif #define TARGET_NR_read 63 #define TARGET_NR_write 64 #define TARGET_NR_readv 65 @@ -286,7 +290,16 @@ #define TARGET_NR_membarrier 283 #define TARGET_NR_mlock2 284 #define TARGET_NR_copy_file_range 285 +#define TARGET_NR_preadv2 286 +#define TARGET_NR_pwritev2 287 +#define TARGET_NR_pkey_mprotect 288 +#define TARGET_NR_pkey_alloc 289 +#define TARGET_NR_pkey_free 290 +#define TARGET_NR_statx 291 +#define TARGET_NR_io_pgetevents 292 +#define TARGET_NR_rseq 293 +#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_syscalls (TARGET_NR_copy_file_range + 1) +#define TARGET_NR_syscalls (TARGET_NR_kexec_file_load + 1) #endif From 28a4df979a2ef92d9129886cd9dcb00c497ac4cb Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Mon, 24 Jun 2019 16:41:44 -0700 Subject: [PATCH 25/32] riscv: virt: Add cpu-topology DT node. Currently, there is no cpu topology defined in RISC-V. Define a device tree node that clearly describes the entire topology. This saves the trouble of scanning individual cache to figure out the topology. Here is the linux kernel patch series that enables topology for RISC-V. http://lists.infradead.org/pipermail/linux-riscv/2019-June/005072.html CPU topology after applying this patch in QEMU & above series in kernel / # cat /sys/devices/system/cpu/cpu2/topology/thread_siblings_list 2 / # cat /sys/devices/system/cpu/cpu2/topology/physical_package_id 0 / # cat /sys/devices/system/cpu/cpu2/topology/core_siblings_list 0-7 Signed-off-by: Atish Patra Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- hw/riscv/virt.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 487f61404b..28d96daf8c 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -191,6 +191,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { int cpu_phandle = phandle++; + int intc_phandle; nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); @@ -203,9 +204,12 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", cpu_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", cpu_phandle); + intc_phandle = phandle++; qemu_fdt_add_subnode(fdt, intc); - qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle); - qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle); + qemu_fdt_setprop_cell(fdt, intc, "phandle", intc_phandle); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", intc_phandle); qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); @@ -214,6 +218,20 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, g_free(nodename); } + /* Add cpu-topology node */ + qemu_fdt_add_subnode(fdt, "/cpus/cpu-map"); + qemu_fdt_add_subnode(fdt, "/cpus/cpu-map/cluster0"); + for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + char *core_nodename = g_strdup_printf("/cpus/cpu-map/cluster0/core%d", + cpu); + char *cpu_nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, cpu_nodename); + qemu_fdt_add_subnode(fdt, core_nodename); + qemu_fdt_setprop_cell(fdt, core_nodename, "cpu", intc_phandle); + g_free(core_nodename); + g_free(cpu_nodename); + } + cells = g_new0(uint32_t, s->soc.num_harts * 4); for (cpu = 0; cpu < s->soc.num_harts; cpu++) { nodename = From f88222dae552c202685f5bee43e6c2e12d3c088c Mon Sep 17 00:00:00 2001 From: Michael Clark Date: Mon, 24 Jun 2019 16:42:33 -0700 Subject: [PATCH 26/32] disas/riscv: Disassemble reserved compressed encodings as illegal Due to the design of the disassembler, the immediate is not known during decoding of the opcode; so to handle compressed encodings with reserved immediate values (non-zero), we need to add an additional check during decompression to match reserved encodings with zero immediates and translate them into the illegal instruction. The following compressed opcodes have reserved encodings with zero immediates: c.addi4spn, c.addi, c.lui, c.addi16sp, c.srli, c.srai, c.andi and c.slli Signed-off-by: Michael Clark Signed-off-by: Alistair Francis [Palmer: Broke long lines] Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- disas/riscv.c | 62 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 59a9b0437a..d373127055 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -504,14 +504,19 @@ typedef struct { const rvc_constraint *constraints; } rv_comp_data; +enum { + rvcd_imm_nz = 0x1 +}; + typedef struct { const char * const name; const rv_codec codec; const char * const format; const rv_comp_data *pseudo; - const int decomp_rv32; - const int decomp_rv64; - const int decomp_rv128; + const short decomp_rv32; + const short decomp_rv64; + const short decomp_rv128; + const short decomp_data; } rv_opcode_data; /* register names */ @@ -1011,7 +1016,8 @@ const rv_opcode_data opcode_data[] = { { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, + rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, @@ -1019,14 +1025,20 @@ const rv_opcode_data opcode_data[] = { { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, + { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, + rv_op_addi, rvcd_imm_nz }, { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, + { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, + rv_op_addi, rv_op_addi, rvcd_imm_nz }, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, + rv_op_lui, rvcd_imm_nz }, + { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, + rv_op_srli, rv_op_srli, rvcd_imm_nz }, + { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, + rv_op_srai, rv_op_srai, rvcd_imm_nz }, + { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, + rv_op_andi, rv_op_andi, rvcd_imm_nz }, { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, @@ -1036,7 +1048,8 @@ const rv_opcode_data opcode_data[] = { { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal }, { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq }, { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli }, + { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, + rv_op_slli, rv_op_slli, rvcd_imm_nz }, { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw }, { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, @@ -2795,8 +2808,13 @@ static void decode_inst_decompress_rv32(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv32; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) + && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } @@ -2804,8 +2822,13 @@ static void decode_inst_decompress_rv64(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv64; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) + && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } @@ -2813,8 +2836,13 @@ static void decode_inst_decompress_rv128(rv_decode *dec) { int decomp_op = opcode_data[dec->op].decomp_rv128; if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; + if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) + && dec->imm == 0) { + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + dec->codec = opcode_data[decomp_op].codec; + } } } From 2e3df911c5bb8199b72427a36ce68a8fe2decf1f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 27 Jun 2019 02:34:54 -0700 Subject: [PATCH 27/32] disas/riscv: Fix `rdinstreth` constraint The constraint for `rdinstreth` was comparing the csr number to 0xc80, which is `cycleh` instead. Fix this. Signed-off-by: Wladimir J. van der Laan Signed-off-by: Michael Clark Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- disas/riscv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index d373127055..278d9be924 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -614,7 +614,8 @@ static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, r static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end }; static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end }; -static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end }; +static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, + rvc_csr_eq_0xc82, rvc_end }; static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end }; static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; @@ -1038,7 +1039,7 @@ const rv_opcode_data opcode_data[] = { { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai, rvcd_imm_nz }, { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, - rv_op_andi, rv_op_andi, rvcd_imm_nz }, + rv_op_andi, rv_op_andi }, { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub }, { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor }, { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or }, From 382cb4392ff8c3fa911d10da959eb8b0cbee15af Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 17 May 2019 08:51:24 -0700 Subject: [PATCH 28/32] riscv: sifive_u: Do not create hard-coded phandles in DT At present the cpu, plic and ethclk nodes' phandles are hard-coded to 1/2/3 in DT. If we configure more than 1 cpu for the machine, all cpu nodes' phandles conflict with each other as they are all 1. Fix it by removing the hardcode. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- hw/riscv/sifive_u.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 5ecc47cea3..e2120ac7a5 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -86,7 +86,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, uint32_t *cells; char *nodename; char ethclk_names[] = "pclk\0hclk\0tx_clk"; - uint32_t plic_phandle, ethclk_phandle; + uint32_t plic_phandle, ethclk_phandle, phandle = 1; fdt = s->fdt = create_device_tree(&s->fdt_size); if (!fdt) { @@ -121,6 +121,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) { + int cpu_phandle = phandle++; nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]); @@ -134,8 +135,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); qemu_fdt_add_subnode(fdt, intc); - qemu_fdt_setprop_cell(fdt, intc, "phandle", 1); - qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1); + qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle); + qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle); qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); @@ -167,6 +168,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); + plic_phandle = phandle++; cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { nodename = @@ -192,20 +194,21 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35); - qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2); - qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2); + qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle); plic_phandle = qemu_fdt_get_phandle(fdt, nodename); g_free(cells); g_free(nodename); + ethclk_phandle = phandle++; nodename = g_strdup_printf("/soc/ethclk"); qemu_fdt_add_subnode(fdt, nodename); qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock"); qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0); qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", SIFIVE_U_GEM_CLOCK_FREQ); - qemu_fdt_setprop_cell(fdt, nodename, "phandle", 3); - qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", 3); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", ethclk_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", ethclk_phandle); ethclk_phandle = qemu_fdt_get_phandle(fdt, nodename); g_free(nodename); From 05446f4133ea1fe4b444ba80a823fc1df1a9eeaf Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Fri, 17 May 2019 08:51:25 -0700 Subject: [PATCH 29/32] riscv: sifive_u: Update the plic hart config to support multicore At present the PLIC is instantiated to support only one hart, while the machine allows at most 4 harts to be created. When more than 1 hart is configured, PLIC needs to instantiated to support multicore, otherwise an SMP OS does not work. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- hw/riscv/sifive_u.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index e2120ac7a5..a416d5d08b 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -344,6 +344,8 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; + char *plic_hart_config; + size_t plic_hart_config_len; int i; Error *err = NULL; NICInfo *nd = &nd_table[0]; @@ -357,9 +359,21 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, mask_rom); + /* create PLIC hart topology configuration string */ + plic_hart_config_len = (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1) * smp_cpus; + plic_hart_config = g_malloc0(plic_hart_config_len); + for (i = 0; i < smp_cpus; i++) { + if (i != 0) { + strncat(plic_hart_config, ",", plic_hart_config_len); + } + strncat(plic_hart_config, SIFIVE_U_PLIC_HART_CONFIG, + plic_hart_config_len); + plic_hart_config_len -= (strlen(SIFIVE_U_PLIC_HART_CONFIG) + 1); + } + /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, - (char *)SIFIVE_U_PLIC_HART_CONFIG, + plic_hart_config, SIFIVE_U_PLIC_NUM_SOURCES, SIFIVE_U_PLIC_NUM_PRIORITIES, SIFIVE_U_PLIC_PRIORITY_BASE, From 0ac24d56c5e7d32423ea78ac58a06b444d1df04d Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jun 2019 15:11:49 -0700 Subject: [PATCH 30/32] hw/riscv: Split out the boot functions Split the common RISC-V boot functions into a seperate file. This allows us to share the common code. Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Tested-by: Bin Meng Signed-off-by: Palmer Dabbelt --- hw/riscv/Makefile.objs | 1 + hw/riscv/boot.c | 69 +++++++++++++++++++++++++++++++++++++++++ hw/riscv/sifive_e.c | 17 ++-------- hw/riscv/sifive_u.c | 17 ++-------- hw/riscv/spike.c | 21 +++---------- hw/riscv/virt.c | 51 +++--------------------------- include/hw/riscv/boot.h | 27 ++++++++++++++++ 7 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 hw/riscv/boot.c create mode 100644 include/hw/riscv/boot.h diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index a65027304a..eb9d4f9ffc 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1,3 +1,4 @@ +obj-y += boot.o obj-$(CONFIG_SPIKE) += riscv_htif.o obj-$(CONFIG_HART) += riscv_hart.o obj-$(CONFIG_SIFIVE_E) += sifive_e.o diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c new file mode 100644 index 0000000000..0c8e72e455 --- /dev/null +++ b/hw/riscv/boot.c @@ -0,0 +1,69 @@ +/* + * QEMU RISC-V Boot Helper + * + * Copyright (c) 2017 SiFive, Inc. + * Copyright (c) 2019 Alistair Francis + * + * 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/units.h" +#include "qemu/error-report.h" +#include "exec/cpu-defs.h" +#include "hw/loader.h" +#include "hw/riscv/boot.h" +#include "elf.h" + +target_ulong riscv_load_kernel(const char *kernel_filename) +{ + uint64_t kernel_entry, kernel_high; + + if (load_elf(kernel_filename, NULL, NULL, NULL, + &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) < 0) { + error_report("could not load kernel '%s'", kernel_filename); + exit(1); + } + + return kernel_entry; +} + +hwaddr riscv_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("could not load ramdisk '%s'", filename); + exit(1); + } + } + + return *start + size; +} diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index a5b4086da3..d27f626529 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -44,10 +44,10 @@ #include "hw/riscv/sifive_prci.h" #include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_e.h" +#include "hw/riscv/boot.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "exec/address-spaces.h" -#include "elf.h" static const struct MemmapEntry { hwaddr base; @@ -74,19 +74,6 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static target_ulong load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, - 0, EM_RISCV, 1, 0) < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - return kernel_entry; -} - static void sifive_mmio_emulate(MemoryRegion *parent, const char *name, uintptr_t offset, uintptr_t length) { @@ -131,7 +118,7 @@ static void riscv_sifive_e_init(MachineState *machine) memmap[SIFIVE_E_MROM].base, &address_space_memory); if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); + riscv_load_kernel(machine->kernel_filename); } } diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index a416d5d08b..f6b9c12e60 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -41,11 +41,11 @@ #include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_prci.h" #include "hw/riscv/sifive_u.h" +#include "hw/riscv/boot.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "exec/address-spaces.h" -#include "elf.h" #include @@ -65,19 +65,6 @@ static const struct MemmapEntry { #define GEM_REVISION 0x10070109 -static target_ulong load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, - 0, EM_RISCV, 1, 0) < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - return kernel_entry; -} - static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { @@ -283,7 +270,7 @@ static void riscv_sifive_u_init(MachineState *machine) create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); + riscv_load_kernel(machine->kernel_filename); } /* reset vector */ diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 5b33d4be3b..e68be00a5f 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -36,12 +36,12 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_clint.h" #include "hw/riscv/spike.h" +#include "hw/riscv/boot.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" -#include "elf.h" #include @@ -54,19 +54,6 @@ static const struct MemmapEntry { [SPIKE_DRAM] = { 0x80000000, 0x0 }, }; -static target_ulong load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0, - NULL, true, htif_symbol_callback) < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); - } - return kernel_entry; -} - static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { @@ -199,7 +186,7 @@ static void spike_board_init(MachineState *machine) mask_rom); if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); + riscv_load_kernel(machine->kernel_filename); } /* reset vector */ @@ -287,7 +274,7 @@ static void spike_v1_10_0_board_init(MachineState *machine) mask_rom); if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); + riscv_load_kernel(machine->kernel_filename); } /* reset vector */ @@ -372,7 +359,7 @@ static void spike_v1_09_1_board_init(MachineState *machine) mask_rom); if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); + riscv_load_kernel(machine->kernel_filename); } /* reset vector */ diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 28d96daf8c..485aefa995 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -34,13 +34,13 @@ #include "hw/riscv/sifive_clint.h" #include "hw/riscv/sifive_test.h" #include "hw/riscv/virt.h" +#include "hw/riscv/boot.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "exec/address-spaces.h" #include "hw/pci/pci.h" #include "hw/pci-host/gpex.h" -#include "elf.h" #include @@ -61,47 +61,6 @@ static const struct MemmapEntry { [VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 }, }; -static target_ulong load_kernel(const char *kernel_filename) -{ - uint64_t kernel_entry, kernel_high; - - if (load_elf(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, - 0, EM_RISCV, 1, 0) < 0) { - error_report("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("could not load ramdisk '%s'", filename); - exit(1); - } - } - return *start + size; -} - static void create_pcie_irq_map(void *fdt, char *nodename, uint32_t plic_phandle) { @@ -440,13 +399,13 @@ static void riscv_virt_board_init(MachineState *machine) mask_rom); if (machine->kernel_filename) { - uint64_t kernel_entry = load_kernel(machine->kernel_filename); + uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename); if (machine->initrd_filename) { hwaddr start; - hwaddr end = load_initrd(machine->initrd_filename, - machine->ram_size, kernel_entry, - &start); + hwaddr end = riscv_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", diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h new file mode 100644 index 0000000000..f84fd6c2df --- /dev/null +++ b/include/hw/riscv/boot.h @@ -0,0 +1,27 @@ +/* + * QEMU RISC-V Boot Helper + * + * Copyright (c) 2017 SiFive, Inc. + * Copyright (c) 2019 Alistair Francis + * + * 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 RISCV_BOOT_H +#define RISCV_BOOT_H + +target_ulong riscv_load_kernel(const char *kernel_filename); +hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, + uint64_t kernel_entry, hwaddr *start); + +#endif /* RISCV_BOOT_H */ From b30422231b90c089ce73aa4b40461bfdc1be0371 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jun 2019 15:11:52 -0700 Subject: [PATCH 31/32] hw/riscv: Add support for loading a firmware Add support for loading a firmware file for the virt machine and the SiFive U. This can be run with the following command: qemu-system-riscv64 -machine virt -bios fw_jump.bin -kernel vmlinux Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Tested-by: Bin Meng Signed-off-by: Palmer Dabbelt --- hw/riscv/boot.c | 26 ++++++++++++++++++++++++++ hw/riscv/sifive_u.c | 4 ++++ hw/riscv/virt.c | 4 ++++ include/hw/riscv/boot.h | 2 ++ 4 files changed, 36 insertions(+) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 0c8e72e455..883df49a0c 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -23,8 +23,34 @@ #include "exec/cpu-defs.h" #include "hw/loader.h" #include "hw/riscv/boot.h" +#include "hw/boards.h" #include "elf.h" +#if defined(TARGET_RISCV32) +# define KERNEL_BOOT_ADDRESS 0x80400000 +#else +# define KERNEL_BOOT_ADDRESS 0x80200000 +#endif + +target_ulong riscv_load_firmware(const char *firmware_filename, + hwaddr firmware_load_addr) +{ + uint64_t firmware_entry, firmware_start, firmware_end; + + if (load_elf(firmware_filename, NULL, NULL, NULL, &firmware_entry, + &firmware_start, &firmware_end, 0, EM_RISCV, 1, 0) > 0) { + return firmware_entry; + } + + if (load_image_targphys_as(firmware_filename, firmware_load_addr, + ram_size, NULL) > 0) { + return firmware_load_addr; + } + + error_report("could not load firmware '%s'", firmware_filename); + exit(1); +} + target_ulong riscv_load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index f6b9c12e60..4208671552 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -269,6 +269,10 @@ static void riscv_sifive_u_init(MachineState *machine) /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); + if (machine->firmware) { + riscv_load_firmware(machine->firmware, memmap[SIFIVE_U_DRAM].base); + } + if (machine->kernel_filename) { riscv_load_kernel(machine->kernel_filename); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 485aefa995..d8181a4ff1 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -398,6 +398,10 @@ static void riscv_virt_board_init(MachineState *machine) memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, mask_rom); + if (machine->firmware) { + riscv_load_firmware(machine->firmware, memmap[VIRT_DRAM].base); + } + if (machine->kernel_filename) { uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename); diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index f84fd6c2df..daa179b600 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -20,6 +20,8 @@ #ifndef RISCV_BOOT_H #define RISCV_BOOT_H +target_ulong riscv_load_firmware(const char *firmware_filename, + hwaddr firmware_load_addr); target_ulong riscv_load_kernel(const char *kernel_filename); hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size, uint64_t kernel_entry, hwaddr *start); From 395fd69582a00b76a89c12d9c074055a9d207997 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Mon, 24 Jun 2019 15:11:54 -0700 Subject: [PATCH 32/32] hw/riscv: Extend the kernel loading support Extend the RISC-V kernel loader to support Image and uImage files. A Linux kernel can now be booted with: qemu-system-riscv64 -machine virt -bios fw_jump.bin -kernel Image Signed-off-by: Alistair Francis Reviewed-by: Bin Meng Tested-by: Bin Meng Signed-off-by: Palmer Dabbelt --- hw/riscv/boot.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 883df49a0c..ff023f42d0 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -56,12 +56,22 @@ target_ulong riscv_load_kernel(const char *kernel_filename) uint64_t kernel_entry, kernel_high; if (load_elf(kernel_filename, NULL, NULL, NULL, - &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) < 0) { - error_report("could not load kernel '%s'", kernel_filename); - exit(1); + &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) > 0) { + return kernel_entry; } - return kernel_entry; + if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, + NULL, NULL, NULL) > 0) { + return kernel_entry; + } + + if (load_image_targphys_as(kernel_filename, KERNEL_BOOT_ADDRESS, + ram_size, NULL) > 0) { + return KERNEL_BOOT_ADDRESS; + } + + error_report("could not load kernel '%s'", kernel_filename); + exit(1); } hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,