From ab67a1d07a4f6f1b4d577c5c47013273b9804551 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:01:46 -0800 Subject: target/riscv: Add support for the new execption numbers The v0.5 Hypervisor spec add new execption numbers, let's add support for those. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 85403da9c8..a10582b310 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -528,13 +528,16 @@ void riscv_cpu_do_interrupt(CPUState *cs) 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_H] = RISCV_EXCP_VS_ECALL, [PRV_M] = RISCV_EXCP_M_ECALL }; if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { + case RISCV_EXCP_INST_GUEST_PAGE_FAULT: + case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: + case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: case RISCV_EXCP_INST_ADDR_MIS: case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: @@ -556,7 +559,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) } } - trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 16 ? + trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 23 ? (async ? riscv_intr_names : riscv_excp_names)[cause] : "(unknown)"); if (env->priv <= PRV_S && -- cgit 1.4.1 From ef6bb7b62682badefdcb744831510aaa5971684f Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:01:51 -0800 Subject: target/riscv: Add the virtulisation mode Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 4 ++++ target/riscv/cpu_bits.h | 3 +++ target/riscv/cpu_helper.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c7f7ae5c38..a9cbd8584e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -117,6 +117,8 @@ struct CPURISCVState { #ifndef CONFIG_USER_ONLY target_ulong priv; + /* This contains QEMU specific information about the virt state. */ + target_ulong virt; target_ulong resetvec; target_ulong mhartid; @@ -269,6 +271,8 @@ int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); bool riscv_cpu_fp_enabled(CPURISCVState *env); +bool riscv_cpu_virt_enabled(CPURISCVState *env); +void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index eeaa03c0f8..2cdb0de4fe 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -430,6 +430,9 @@ #define PRV_H 2 /* Reserved */ #define PRV_M 3 +/* Virtulisation Register Fields */ +#define VIRT_ONOFF 1 + /* RV32 satp CSR field masks */ #define SATP32_MODE 0x80000000 #define SATP32_ASID 0x7fc00000 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index a10582b310..e5311160e7 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -82,6 +82,24 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) return false; } +bool riscv_cpu_virt_enabled(CPURISCVState *env) +{ + if (!riscv_has_ext(env, RVH)) { + return false; + } + + return get_field(env->virt, VIRT_ONOFF); +} + +void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) +{ + if (!riscv_has_ext(env, RVH)) { + return; + } + + env->virt = set_field(env->virt, VIRT_ONOFF, enable); +} + int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) { CPURISCVState *env = &cpu->env; -- cgit 1.4.1 From c7b1bbc80fc2af17395d3986c346fd2307e57829 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:01:54 -0800 Subject: target/riscv: Add the force HS exception mode Add a FORCE_HS_EXCEP mode to the RISC-V virtulisation status. This bit specifies if an exeption should be taken to HS mode no matter the current delegation status. This is used when an exeption must be taken to HS mode, such as when handling interrupts. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 2 ++ target/riscv/cpu_bits.h | 6 ++++++ target/riscv/cpu_helper.c | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a9cbd8584e..42720d65f9 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -273,6 +273,8 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); bool riscv_cpu_fp_enabled(CPURISCVState *env); bool riscv_cpu_virt_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); +bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env); +void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable); int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch); hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 2cdb0de4fe..ad6479796c 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -432,6 +432,12 @@ /* Virtulisation Register Fields */ #define VIRT_ONOFF 1 +/* This is used to save state for when we take an exception. If this is set + * that means that we want to force a HS level exception (no matter what the + * delegation is set to). This will occur for things such as a second level + * page table fault. + */ +#define FORCE_HS_EXCEP 2 /* RV32 satp CSR field masks */ #define SATP32_MODE 0x80000000 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index e5311160e7..e36ee7d58e 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -100,6 +100,24 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) env->virt = set_field(env->virt, VIRT_ONOFF, enable); } +bool riscv_cpu_force_hs_excep_enabled(CPURISCVState *env) +{ + if (!riscv_has_ext(env, RVH)) { + return false; + } + + return get_field(env->virt, FORCE_HS_EXCEP); +} + +void riscv_cpu_set_force_hs_excep(CPURISCVState *env, bool enable) +{ + if (!riscv_has_ext(env, RVH)) { + return; + } + + env->virt = set_field(env->virt, FORCE_HS_EXCEP, enable); +} + int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) { CPURISCVState *env = &cpu->env; -- cgit 1.4.1 From 66e594f2800ddc55f908830bf9e8dc4cda1304fe Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:12 -0800 Subject: target/riscv: Add virtual register swapping function Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 11 +++++++++ target/riscv/cpu_bits.h | 7 ++++++ target/riscv/cpu_helper.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 42720d65f9..5b889a0065 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -125,6 +125,7 @@ struct CPURISCVState { target_ulong mstatus; target_ulong mip; + uint32_t miclaim; target_ulong mie; @@ -166,6 +167,15 @@ struct CPURISCVState { target_ulong mtval2; target_ulong mtinst; + /* HS Backup CSRs */ + target_ulong stvec_hs; + target_ulong sscratch_hs; + target_ulong sepc_hs; + target_ulong scause_hs; + target_ulong stval_hs; + target_ulong satp_hs; + target_ulong mstatus_hs; + target_ulong scounteren; target_ulong mcounteren; @@ -296,6 +306,7 @@ void riscv_cpu_list(void); #define cpu_mmu_index riscv_cpu_mmu_index #ifndef CONFIG_USER_ONLY +void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); 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 */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index ad6479796c..a24654d137 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -553,4 +553,11 @@ #define SIP_STIP MIP_STIP #define SIP_SEIP MIP_SEIP +/* MIE masks */ +#define MIE_SEIE (1 << IRQ_S_EXT) +#define MIE_UEIE (1 << IRQ_U_EXT) +#define MIE_STIE (1 << IRQ_S_TIMER) +#define MIE_UTIE (1 << IRQ_U_TIMER) +#define MIE_SSIE (1 << IRQ_S_SOFT) +#define MIE_USIE (1 << IRQ_U_SOFT) #endif diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index e36ee7d58e..9d5a06499f 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -82,6 +82,67 @@ bool riscv_cpu_fp_enabled(CPURISCVState *env) return false; } +void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) +{ + target_ulong mstatus_mask = MSTATUS_MXR | MSTATUS_SUM | MSTATUS_FS | + MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE; + bool current_virt = riscv_cpu_virt_enabled(env); + + g_assert(riscv_has_ext(env, RVH)); + +#if defined(TARGET_RISCV64) + mstatus_mask |= MSTATUS64_UXL; +#endif + + if (current_virt) { + /* Current V=1 and we are about to change to V=0 */ + env->vsstatus = env->mstatus & mstatus_mask; + env->mstatus &= ~mstatus_mask; + env->mstatus |= env->mstatus_hs; + + env->vstvec = env->stvec; + env->stvec = env->stvec_hs; + + env->vsscratch = env->sscratch; + env->sscratch = env->sscratch_hs; + + env->vsepc = env->sepc; + env->sepc = env->sepc_hs; + + env->vscause = env->scause; + env->scause = env->scause_hs; + + env->vstval = env->sbadaddr; + env->sbadaddr = env->stval_hs; + + env->vsatp = env->satp; + env->satp = env->satp_hs; + } else { + /* Current V=0 and we are about to change to V=1 */ + env->mstatus_hs = env->mstatus & mstatus_mask; + env->mstatus &= ~mstatus_mask; + env->mstatus |= env->vsstatus; + + env->stvec_hs = env->stvec; + env->stvec = env->vstvec; + + env->sscratch_hs = env->sscratch; + env->sscratch = env->vsscratch; + + env->sepc_hs = env->sepc; + env->sepc = env->vsepc; + + env->scause_hs = env->scause; + env->scause = env->vscause; + + env->stval_hs = env->sbadaddr; + env->sbadaddr = env->vstval; + + env->satp_hs = env->satp; + env->satp = env->vsatp; + } +} + bool riscv_cpu_virt_enabled(CPURISCVState *env) { if (!riscv_has_ext(env, RVH)) { -- cgit 1.4.1 From 3ef10a098b0d3ebb02bf8e1325adc3b77af92f0b Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:23 -0800 Subject: target/riscv: Add support for virtual interrupt setting Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 9d5a06499f..ccf67aca05 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -37,13 +37,36 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) #ifndef CONFIG_USER_ONLY static int riscv_cpu_local_irq_pending(CPURISCVState *env) { + target_ulong irqs; + target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong pending = 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); + target_ulong hs_mstatus_sie = get_field(env->mstatus_hs, MSTATUS_SIE); + + target_ulong pending = env->mip & env->mie & + ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + target_ulong vspending = (env->mip & env->mie & + (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) >> 1; + + 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 hs_sie = env->priv < PRV_S || + (env->priv == PRV_S && hs_mstatus_sie); + + if (riscv_cpu_virt_enabled(env)) { + target_ulong pending_hs_irq = pending & -hs_sie; + + if (pending_hs_irq) { + riscv_cpu_set_force_hs_excep(env, FORCE_HS_EXCEP); + return ctz64(pending_hs_irq); + } + + pending = vspending; + } + + irqs = (pending & ~env->mideleg & -mie) | (pending & env->mideleg & -sie); if (irqs) { return ctz64(irqs); /* since non-zero */ -- cgit 1.4.1 From eccc5a12c2fd1c646c69a1e7de29183b7a559973 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:25 -0800 Subject: target/ricsv: Flush the TLB on virtulisation mode changes To ensure our TLB isn't out-of-date we flush it on all virt mode changes. Unlike priv mode this isn't saved in the mmu_idx as all guests share V=1. The easiest option is just to flush on all changes. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index ccf67aca05..a8b114ae16 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -181,6 +181,11 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) return; } + /* Flush the TLB on all virt mode changes. */ + if (get_field(env->virt, VIRT_ONOFF) != enable) { + tlb_flush(env_cpu(env)); + } + env->virt = set_field(env->virt, VIRT_ONOFF, enable); } -- cgit 1.4.1 From 5eb9e782f523d2898e2dacd86c6e41365dae74b3 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:30 -0800 Subject: target/riscv: Add hypvervisor trap support Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 69 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 10 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index a8b114ae16..895b6ca25d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -641,6 +641,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool force_hs_execp = riscv_cpu_force_hs_excep_enabled(env); + target_ulong s; /* 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. @@ -650,19 +652,14 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong deleg = async ? 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_VS_ECALL, - [PRV_M] = RISCV_EXCP_M_ECALL - }; - if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { case RISCV_EXCP_INST_GUEST_PAGE_FAULT: case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: + force_hs_execp = true; + /* fallthrough */ case RISCV_EXCP_INST_ADDR_MIS: case RISCV_EXCP_INST_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: @@ -680,7 +677,16 @@ void riscv_cpu_do_interrupt(CPUState *cs) /* 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]; + + if (env->priv == PRV_M) { + cause = RISCV_EXCP_M_ECALL; + } else if (env->priv == PRV_S && riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_VS_ECALL; + } else if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env)) { + cause = RISCV_EXCP_S_ECALL; + } else if (env->priv == PRV_U) { + cause = RISCV_EXCP_U_ECALL; + } } } @@ -690,7 +696,36 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (env->priv <= PRV_S && cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ - target_ulong s = env->mstatus; + if (riscv_has_ext(env, RVH)) { + target_ulong hdeleg = async ? env->hideleg : env->hedeleg; + + if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1) && + !force_hs_execp) { + /* Trap to VS mode */ + } else if (riscv_cpu_virt_enabled(env)) { + /* Trap into HS mode, from virt */ + riscv_cpu_swap_hypervisor_regs(env); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + + riscv_cpu_set_virt_enabled(env, 0); + riscv_cpu_set_force_hs_excep(env, 0); + } else { + /* Trap into HS mode */ + env->hstatus = set_field(env->hstatus, HSTATUS_SP2V, + get_field(env->hstatus, HSTATUS_SPV)); + env->hstatus = set_field(env->hstatus, HSTATUS_SP2P, + get_field(env->mstatus, SSTATUS_SPP)); + env->hstatus = set_field(env->hstatus, HSTATUS_SPV, + riscv_cpu_virt_enabled(env)); + } + } + + 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); @@ -704,7 +739,21 @@ void riscv_cpu_do_interrupt(CPUState *cs) riscv_cpu_set_mode(env, PRV_S); } else { /* handle the trap in M-mode */ - target_ulong s = env->mstatus; + if (riscv_has_ext(env, RVH)) { + if (riscv_cpu_virt_enabled(env)) { + riscv_cpu_swap_hypervisor_regs(env); + } + env->mstatus = set_field(env->mstatus, MSTATUS_MPV, + riscv_cpu_virt_enabled(env)); + env->mstatus = set_field(env->mstatus, MSTATUS_MTL, + riscv_cpu_force_hs_excep_enabled(env)); + + /* Trapping to M mode, virt is disabled */ + riscv_cpu_set_virt_enabled(env, 0); + riscv_cpu_set_force_hs_excep(env, 0); + } + + 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); -- cgit 1.4.1 From 29409c1d921d607873268671bf11a088efb5558e Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:44 -0800 Subject: target/riscv: Disable guest FP support based on virtual status When the Hypervisor extension is in use we only enable floating point support when both status and vsstatus have enabled floating point support. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 895b6ca25d..d9a29d702a 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -99,6 +99,9 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) bool riscv_cpu_fp_enabled(CPURISCVState *env) { if (env->mstatus & MSTATUS_FS) { + if (riscv_cpu_virt_enabled(env) && !(env->mstatus_hs & MSTATUS_FS)) { + return false; + } return true; } -- cgit 1.4.1 From 1448689c7b23690f49a4cce248c6e4ac973d37b8 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:53 -0800 Subject: target/riscv: Allow specifying MMU stage Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d9a29d702a..8ae1038bcd 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -277,10 +277,19 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * * Adapted from Spike's mmu_t::translate and mmu_t::walk * + * @env: CPURISCVState + * @physical: This will be set to the calculated physical address + * @prot: The returned protection attributes + * @addr: The virtual address to be translated + * @access_type: The type of MMU access + * @mmu_idx: Indicates current privilege level + * @first_stage: Are we in first stage translation? + * Second stage is used for hypervisor guest translation */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, - int access_type, int mmu_idx) + int access_type, int mmu_idx, + bool first_stage) { /* NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler @@ -485,13 +494,21 @@ restart: } static void raise_mmu_exception(CPURISCVState *env, target_ulong address, - MMUAccessType access_type, bool pmp_violation) + MMUAccessType access_type, bool pmp_violation, + bool first_stage) { 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 && - !pmp_violation; + int page_fault_exceptions; + if (first_stage) { + page_fault_exceptions = + (env->priv_ver >= PRIV_VERSION_1_10_0) && + get_field(env->satp, SATP_MODE) != VM_1_10_MBARE && + !pmp_violation; + } else { + page_fault_exceptions = + get_field(env->hgatp, HGATP_MODE) != VM_1_10_MBARE && + !pmp_violation; + } switch (access_type) { case MMU_INST_FETCH: cs->exception_index = page_fault_exceptions ? @@ -518,7 +535,8 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); - if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) { + if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx, + true)) { return -1; } return phys_addr; @@ -583,7 +601,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 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); + ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, + true); if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { @@ -610,7 +629,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, pmp_violation); + raise_mmu_exception(env, address, access_type, pmp_violation, true); riscv_raise_exception(env, cs->exception_index, retaddr); } #else -- cgit 1.4.1 From 36a18664bafcfafa5e997b47458387f6fe53d537 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:56 -0800 Subject: target/riscv: Implement second stage MMU Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 1 + target/riscv/cpu_helper.c | 193 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 175 insertions(+), 19 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index aa04e5cca7..a8534fdf2b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -104,6 +104,7 @@ struct CPURISCVState { target_ulong frm; target_ulong badaddr; + target_ulong guest_phys_fault_addr; target_ulong priv_ver; target_ulong misa; diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 8ae1038bcd..584b0c71fb 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -285,11 +285,12 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * @mmu_idx: Indicates current privilege level * @first_stage: Are we in first stage translation? * Second stage is used for hypervisor guest translation + * @two_stage: Are we going to perform two stage translation */ static int get_physical_address(CPURISCVState *env, hwaddr *physical, int *prot, target_ulong addr, int access_type, int mmu_idx, - bool first_stage) + bool first_stage, bool two_stage) { /* NOTE: the env->pc value visible here will not be * correct, but the value visible to the exception handler @@ -297,13 +298,40 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxResult res; MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmu_idx; + bool use_background = false; + /* + * Check if we should use the background registers for the two + * stage translation. We don't need to check if we actually need + * two stage translation as that happened before this function + * was called. Background registers will be used if the guest has + * forced a two stage translation to be on (in HS or M mode). + */ if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { mode = get_field(env->mstatus, MSTATUS_MPP); + + if (riscv_has_ext(env, RVH) && + get_field(env->mstatus, MSTATUS_MPV)) { + use_background = true; + } + } + } + + if (mode == PRV_S && access_type != MMU_INST_FETCH && + riscv_has_ext(env, RVH) && !riscv_cpu_virt_enabled(env)) { + if (get_field(env->hstatus, HSTATUS_SPRV)) { + mode = get_field(env->mstatus, SSTATUS_SPP); + use_background = true; } } + if (first_stage == false) { + /* We are in stage 2 translation, this is similar to stage 1. */ + /* Stage 2 is always taken as U-mode */ + mode = PRV_U; + } + if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) { *physical = addr; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; @@ -313,13 +341,30 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, *prot = 0; hwaddr base; - int levels, ptidxbits, ptesize, vm, sum; - int mxr = get_field(env->mstatus, MSTATUS_MXR); + int levels, ptidxbits, ptesize, vm, sum, mxr, widened; + + if (first_stage == true) { + mxr = get_field(env->mstatus, MSTATUS_MXR); + } else { + mxr = get_field(env->vsstatus, MSTATUS_MXR); + } if (env->priv_ver >= PRIV_VERSION_1_10_0) { - base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; + if (first_stage == true) { + if (use_background) { + base = (hwaddr)get_field(env->vsatp, SATP_PPN) << PGSHIFT; + vm = get_field(env->vsatp, SATP_MODE); + } else { + base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; + vm = get_field(env->satp, SATP_MODE); + } + widened = 0; + } else { + base = (hwaddr)get_field(env->hgatp, HGATP_PPN) << PGSHIFT; + vm = get_field(env->hgatp, HGATP_MODE); + widened = 2; + } sum = get_field(env->mstatus, MSTATUS_SUM); - vm = get_field(env->satp, SATP_MODE); switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; @@ -337,6 +382,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, g_assert_not_reached(); } } else { + widened = 0; base = (hwaddr)(env->sptbr) << PGSHIFT; sum = !get_field(env->mstatus, MSTATUS_PUM); vm = get_field(env->mstatus, MSTATUS_VM); @@ -357,9 +403,16 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, } CPUState *cs = env_cpu(env); - int va_bits = PGSHIFT + levels * ptidxbits; - target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; - target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; + int va_bits = PGSHIFT + levels * ptidxbits + widened; + target_ulong mask, masked_msbs; + + if (TARGET_LONG_BITS > (va_bits - 1)) { + mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; + } else { + mask = 0; + } + masked_msbs = (addr >> (va_bits - 1)) & mask; + if (masked_msbs != 0 && masked_msbs != mask) { return TRANSLATE_FAIL; } @@ -371,11 +424,29 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, restart: #endif for (i = 0; i < levels; i++, ptshift -= ptidxbits) { - target_ulong idx = (addr >> (PGSHIFT + ptshift)) & + target_ulong idx; + if (i == 0) { + idx = (addr >> (PGSHIFT + ptshift)) & + ((1 << (ptidxbits + widened)) - 1); + } else { + idx = (addr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); + } /* check that physical address of PTE is legal */ - hwaddr pte_addr = base + idx * ptesize; + hwaddr pte_addr; + + if (two_stage && first_stage) { + hwaddr vbase; + + /* Do the second stage translation on the base PTE address. */ + get_physical_address(env, &vbase, prot, base, access_type, + mmu_idx, false, true); + + pte_addr = vbase + idx * ptesize; + } else { + pte_addr = base + idx * ptesize; + } if (riscv_feature(env, RISCV_FEATURE_PMP) && !pmp_hart_has_privs(env, pte_addr, sizeof(target_ulong), @@ -472,7 +543,12 @@ restart: /* for superpage mappings, make a fake leaf PTE for the TLB's benefit. */ target_ulong vpn = addr >> PGSHIFT; - *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + if (i == 0) { + *physical = (ppn | (vpn & ((1L << (ptshift + widened)) - 1))) << + PGSHIFT; + } else { + *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + } /* set permissions on the TLB entry */ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { @@ -531,14 +607,23 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; hwaddr phys_addr; int prot; int mmu_idx = cpu_mmu_index(&cpu->env, false); - if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx, - true)) { + if (get_physical_address(env, &phys_addr, &prot, addr, 0, mmu_idx, + true, riscv_cpu_virt_enabled(env))) { return -1; } + + if (riscv_cpu_virt_enabled(env)) { + if (get_physical_address(env, &phys_addr, &prot, phys_addr, + 0, mmu_idx, false, true)) { + return -1; + } + } + return phys_addr; } @@ -592,17 +677,37 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; #ifndef CONFIG_USER_ONLY + vaddr im_address; hwaddr pa = 0; int prot; bool pmp_violation = false; + bool m_mode_two_stage = false; + bool hs_mode_two_stage = false; + bool first_stage_error = true; int ret = TRANSLATE_FAIL; int mode = mmu_idx; + env->guest_phys_fault_addr = 0; + 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, - true); + /* + * Determine if we are in M mode and MPRV is set or in HS mode and SPRV is + * set and we want to access a virtulisation address. + */ + if (riscv_has_ext(env, RVH)) { + m_mode_two_stage = env->priv == PRV_M && + access_type != MMU_INST_FETCH && + get_field(env->mstatus, MSTATUS_MPRV) && + get_field(env->mstatus, MSTATUS_MPV); + + hs_mode_two_stage = env->priv == PRV_S && + !riscv_cpu_virt_enabled(env) && + access_type != MMU_INST_FETCH && + get_field(env->hstatus, HSTATUS_SPRV) && + get_field(env->hstatus, HSTATUS_SPV); + } if (mode == PRV_M && access_type != MMU_INST_FETCH) { if (get_field(env->mstatus, MSTATUS_MPRV)) { @@ -610,9 +715,55 @@ bool riscv_cpu_tlb_fill(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 (riscv_cpu_virt_enabled(env) || m_mode_two_stage || hs_mode_two_stage) { + /* Two stage lookup */ + ret = get_physical_address(env, &pa, &prot, address, access_type, + mmu_idx, true, true); + + qemu_log_mask(CPU_LOG_MMU, + "%s 1st-stage address=%" VADDR_PRIx " ret %d physical " + TARGET_FMT_plx " prot %d\n", + __func__, address, ret, pa, prot); + + if (ret != TRANSLATE_FAIL) { + /* Second stage lookup */ + im_address = pa; + + ret = get_physical_address(env, &pa, &prot, im_address, + access_type, mmu_idx, false, true); + + qemu_log_mask(CPU_LOG_MMU, + "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical " + TARGET_FMT_plx " prot %d\n", + __func__, im_address, ret, pa, prot); + + if (riscv_feature(env, RISCV_FEATURE_PMP) && + (ret == TRANSLATE_SUCCESS) && + !pmp_hart_has_privs(env, pa, size, 1 << access_type, mode)) { + ret = TRANSLATE_PMP_FAIL; + } + + if (ret != TRANSLATE_SUCCESS) { + /* + * Guest physical address translation failed, this is a HS + * level exception + */ + first_stage_error = false; + env->guest_phys_fault_addr = (im_address | + (address & + (TARGET_PAGE_SIZE - 1))) >> 2; + } + } + } else { + /* Single stage lookup */ + ret = get_physical_address(env, &pa, &prot, address, access_type, + mmu_idx, true, false); + + 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) && @@ -622,6 +773,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_PMP_FAIL) { pmp_violation = true; } + if (ret == TRANSLATE_SUCCESS) { tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); @@ -629,9 +781,12 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, } else if (probe) { return false; } else { - raise_mmu_exception(env, address, access_type, pmp_violation, true); + raise_mmu_exception(env, address, access_type, pmp_violation, first_stage_error); riscv_raise_exception(env, cs->exception_index, retaddr); } + + return true; + #else switch (access_type) { case MMU_INST_FETCH: -- cgit 1.4.1 From b2ef6ab9fee6948cf016f9e741feecdfb333fcbe Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:02:59 -0800 Subject: target/riscv: Raise the new execptions when 2nd stage translation fails Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 584b0c71fb..9e28b19c29 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -587,16 +587,28 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, } switch (access_type) { case MMU_INST_FETCH: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_INST_GUEST_PAGE_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT; + } break; case MMU_DATA_LOAD: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT; + } break; case MMU_DATA_STORE: - cs->exception_index = page_fault_exceptions ? - RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + if (riscv_cpu_virt_enabled(env) && !first_stage) { + cs->exception_index = RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; + } else { + cs->exception_index = page_fault_exceptions ? + RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } break; default: g_assert_not_reached(); -- cgit 1.4.1 From 3067553993ae986b76a92df8a978778134ecdc84 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:03:02 -0800 Subject: target/riscv: Set htval and mtval2 on execptions Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_helper.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 9e28b19c29..d3b764e694 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -840,6 +840,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; target_ulong deleg = async ? env->mideleg : env->medeleg; target_ulong tval = 0; + target_ulong htval = 0; + target_ulong mtval2 = 0; if (!async) { /* set tval to badaddr for traps with address information */ @@ -901,6 +903,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->hstatus = set_field(env->hstatus, HSTATUS_SPV, riscv_cpu_virt_enabled(env)); + htval = env->guest_phys_fault_addr; + riscv_cpu_set_virt_enabled(env, 0); riscv_cpu_set_force_hs_excep(env, 0); } else { @@ -911,6 +915,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) get_field(env->mstatus, SSTATUS_SPP)); env->hstatus = set_field(env->hstatus, HSTATUS_SPV, riscv_cpu_virt_enabled(env)); + + htval = env->guest_phys_fault_addr; } } @@ -923,6 +929,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->scause = cause | ((target_ulong)async << (TARGET_LONG_BITS - 1)); env->sepc = env->pc; env->sbadaddr = tval; + env->htval = htval; env->pc = (env->stvec >> 2 << 2) + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_S); @@ -937,6 +944,8 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mstatus = set_field(env->mstatus, MSTATUS_MTL, riscv_cpu_force_hs_excep_enabled(env)); + mtval2 = env->guest_phys_fault_addr; + /* Trapping to M mode, virt is disabled */ riscv_cpu_set_virt_enabled(env, 0); riscv_cpu_set_force_hs_excep(env, 0); @@ -951,6 +960,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->mcause = cause | ~(((target_ulong)-1) >> async); env->mepc = env->pc; env->mbadaddr = tval; + env->mtval2 = mtval2; env->pc = (env->mtvec >> 2 << 2) + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); riscv_cpu_set_mode(env, PRV_M); -- cgit 1.4.1 From 551fa7e8a695ea5fd1cca8ffd318556855bbf54f Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:03:05 -0800 Subject: target/riscv: Add support for the 32-bit MSTATUSH CSR Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.c | 3 +++ target/riscv/cpu.h | 10 ++++++++++ target/riscv/cpu_bits.h | 3 +++ target/riscv/cpu_helper.c | 17 +++++++++++++++++ target/riscv/csr.c | 25 +++++++++++++++++++++++++ target/riscv/op_helper.c | 4 ++++ 6 files changed, 62 insertions(+) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 44ad768a84..b27066f6a7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -237,6 +237,9 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #ifndef CONFIG_USER_ONLY qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus); +#ifdef TARGET_RISCV32 + qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatush ", env->mstatush); +#endif if (riscv_has_ext(env, RVH)) { qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "hstatus ", env->hstatus); qemu_fprintf(f, " %s " TARGET_FMT_lx "\n", "vsstatus ", env->vsstatus); diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index a8534fdf2b..6f9c29322a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -127,6 +127,10 @@ struct CPURISCVState { target_ulong mip; +#ifdef TARGET_RISCV32 + target_ulong mstatush; +#endif + uint32_t miclaim; target_ulong mie; @@ -164,6 +168,9 @@ struct CPURISCVState { target_ulong vscause; target_ulong vstval; target_ulong vsatp; +#ifdef TARGET_RISCV32 + target_ulong vsstatush; +#endif target_ulong mtval2; target_ulong mtinst; @@ -176,6 +183,9 @@ struct CPURISCVState { target_ulong stval_hs; target_ulong satp_hs; target_ulong mstatus_hs; +#ifdef TARGET_RISCV32 + target_ulong mstatush_hs; +#endif target_ulong scounteren; target_ulong mcounteren; diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index a24654d137..049032f2ae 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -135,6 +135,9 @@ #define CSR_MTVEC 0x305 #define CSR_MCOUNTEREN 0x306 +/* 32-bit only */ +#define CSR_MSTATUSH 0x310 + /* Legacy Counter Setup (priv v1.9.1) */ /* Update to #define CSR_MCOUNTINHIBIT 0x320 for 1.11.0 */ #define CSR_MUCOUNTEREN 0x320 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d3b764e694..10f246ddf8 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -126,6 +126,11 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) env->mstatus &= ~mstatus_mask; env->mstatus |= env->mstatus_hs; +#if defined(TARGET_RISCV32) + env->vsstatush = env->mstatush; + env->mstatush |= env->mstatush_hs; +#endif + env->vstvec = env->stvec; env->stvec = env->stvec_hs; @@ -149,6 +154,11 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) env->mstatus &= ~mstatus_mask; env->mstatus |= env->vsstatus; +#if defined(TARGET_RISCV32) + env->mstatush_hs = env->mstatush; + env->mstatush |= env->vsstatush; +#endif + env->stvec_hs = env->stvec; env->stvec = env->vstvec; @@ -939,10 +949,17 @@ void riscv_cpu_do_interrupt(CPUState *cs) if (riscv_cpu_virt_enabled(env)) { riscv_cpu_swap_hypervisor_regs(env); } +#ifdef TARGET_RISCV32 + env->mstatush = set_field(env->mstatush, MSTATUS_MPV, + riscv_cpu_virt_enabled(env)); + env->mstatush = set_field(env->mstatush, MSTATUS_MTL, + riscv_cpu_force_hs_excep_enabled(env)); +#else env->mstatus = set_field(env->mstatus, MSTATUS_MPV, riscv_cpu_virt_enabled(env)); env->mstatus = set_field(env->mstatus, MSTATUS_MTL, riscv_cpu_force_hs_excep_enabled(env)); +#endif mtval2 = env->guest_phys_fault_addr; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 2e6700bbeb..572a478e8c 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -371,6 +371,27 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) return 0; } +#ifdef TARGET_RISCV32 +static int read_mstatush(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mstatush; + return 0; +} + +static int write_mstatush(CPURISCVState *env, int csrno, target_ulong val) +{ + if ((val ^ env->mstatush) & (MSTATUS_MPV)) { + tlb_flush(env_cpu(env)); + } + + val &= MSTATUS_MPV | MSTATUS_MTL; + + env->mstatush = val; + + return 0; +} +#endif + static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) { *val = env->misa; @@ -1214,6 +1235,10 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVEC] = { any, read_mtvec, write_mtvec }, [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren }, +#if defined(TARGET_RISCV32) + [CSR_MSTATUSH] = { any, read_mstatush, write_mstatush }, +#endif + /* Legacy Counter Setup (priv v1.9.1) */ [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren }, [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren }, diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 804936e9d5..dca68fa96e 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -153,7 +153,11 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 1); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); +#ifdef TARGET_RISCV32 + env->mstatush = set_field(env->mstatush, MSTATUS_MPV, 0); +#else mstatus = set_field(mstatus, MSTATUS_MPV, 0); +#endif env->mstatus = mstatus; riscv_cpu_set_mode(env, prev_priv); -- cgit 1.4.1 From e44b50b5b2e508fdd24915ab0e44ac49685e1de3 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Fri, 31 Jan 2020 17:03:08 -0800 Subject: target/riscv: Add the MSTATUS_MPV_ISSET helper macro Add a helper macro MSTATUS_MPV_ISSET() which will determine if the MSTATUS_MPV bit is set for both 32-bit and 64-bit RISC-V. Signed-off-by: Alistair Francis Reviewed-by: Palmer Dabbelt Signed-off-by: Palmer Dabbelt --- target/riscv/cpu_bits.h | 11 +++++++++++ target/riscv/cpu_helper.c | 4 ++-- target/riscv/op_helper.c | 2 +- target/riscv/translate.c | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 049032f2ae..7f64ee1174 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -363,8 +363,19 @@ #define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ #define MSTATUS_TW 0x20000000 /* since: priv-1.10 */ #define MSTATUS_TSR 0x40000000 /* since: priv-1.10 */ +#if defined(TARGET_RISCV64) #define MSTATUS_MTL 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL +#elif defined(TARGET_RISCV32) +#define MSTATUS_MTL 0x00000040 +#define MSTATUS_MPV 0x00000080 +#endif + +#ifdef TARGET_RISCV32 +# define MSTATUS_MPV_ISSET(env) get_field(env->mstatush, MSTATUS_MPV) +#else +# define MSTATUS_MPV_ISSET(env) get_field(env->mstatus, MSTATUS_MPV) +#endif #define MSTATUS64_UXL 0x0000000300000000ULL #define MSTATUS64_SXL 0x0000000C00000000ULL diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 10f246ddf8..29a1b37d88 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -322,7 +322,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, mode = get_field(env->mstatus, MSTATUS_MPP); if (riscv_has_ext(env, RVH) && - get_field(env->mstatus, MSTATUS_MPV)) { + MSTATUS_MPV_ISSET(env)) { use_background = true; } } @@ -722,7 +722,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, m_mode_two_stage = env->priv == PRV_M && access_type != MMU_INST_FETCH && get_field(env->mstatus, MSTATUS_MPRV) && - get_field(env->mstatus, MSTATUS_MPV); + MSTATUS_MPV_ISSET(env); hs_mode_two_stage = env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index dca68fa96e..8736f689c2 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -146,7 +146,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) target_ulong mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); - target_ulong prev_virt = get_field(mstatus, MSTATUS_MPV); + target_ulong prev_virt = MSTATUS_MPV_ISSET(env); mstatus = set_field(mstatus, env->priv_ver >= PRIV_VERSION_1_10_0 ? MSTATUS_MIE : MSTATUS_UIE << prev_priv, diff --git a/target/riscv/translate.c b/target/riscv/translate.c index b51ab92068..43bf7e39a6 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -755,7 +755,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->virt_enabled = riscv_cpu_virt_enabled(env); if (env->priv_ver == PRV_M && get_field(env->mstatus, MSTATUS_MPRV) && - get_field(env->mstatus, MSTATUS_MPV)) { + MSTATUS_MPV_ISSET(env)) { ctx->virt_enabled = true; } else if (env->priv == PRV_S && !riscv_cpu_virt_enabled(env) && -- cgit 1.4.1 From c695724868ce4049fd79c5a509880dbdf171e744 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Sun, 2 Feb 2020 19:12:16 +0530 Subject: target/riscv: Emulate TIME CSRs for privileged mode Currently, TIME CSRs are emulated only for user-only mode. This patch add TIME CSRs emulation for privileged mode. For privileged mode, the TIME CSRs will return value provided by rdtime callback which is registered by QEMU machine/platform emulation (i.e. CLINT emulation). If rdtime callback is not available then the monitor (i.e. OpenSBI) will trap-n-emulate TIME CSRs in software. We see 25+% performance improvement in hackbench numbers when TIME CSRs are not trap-n-emulated. Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Signed-off-by: Palmer Dabbelt --- target/riscv/cpu.h | 5 +++ target/riscv/cpu_helper.c | 5 +++ target/riscv/csr.c | 86 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 92 insertions(+), 4 deletions(-) (limited to 'target/riscv/cpu_helper.c') diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d52f209361..3dcdf92227 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -159,6 +159,7 @@ struct CPURISCVState { target_ulong htval; target_ulong htinst; target_ulong hgatp; + uint64_t htimedelta; /* Virtual CSRs */ target_ulong vsstatus; @@ -201,6 +202,9 @@ struct CPURISCVState { /* physical memory protection */ pmp_table_t pmp_state; + /* machine specific rdtime callback */ + uint64_t (*rdtime_fn)(void); + /* True if in debugger mode. */ bool debugger; #endif @@ -322,6 +326,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); 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 */ +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)); #endif void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 29a1b37d88..5ea5d133aa 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -258,6 +258,11 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) return old; } +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)) +{ + env->rdtime_fn = fn; +} + void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { if (newpriv > PRV_M) { diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 572a478e8c..11d184cd16 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -238,6 +238,32 @@ static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) #else /* CONFIG_USER_ONLY */ +static int read_time(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + + if (!env->rdtime_fn) { + return -1; + } + + *val = env->rdtime_fn() + delta; + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) +{ + uint64_t delta = riscv_cpu_virt_enabled(env) ? env->htimedelta : 0; + + if (!env->rdtime_fn) { + return -1; + } + + *val = (env->rdtime_fn() + delta) >> 32; + return 0; +} +#endif + /* Machine constants */ #define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) @@ -930,6 +956,56 @@ static int write_hgatp(CPURISCVState *env, int csrno, target_ulong val) return 0; } +static int read_htimedelta(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!env->rdtime_fn) { + return -1; + } + +#if defined(TARGET_RISCV32) + *val = env->htimedelta & 0xffffffff; +#else + *val = env->htimedelta; +#endif + return 0; +} + +static int write_htimedelta(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!env->rdtime_fn) { + return -1; + } + +#if defined(TARGET_RISCV32) + env->htimedelta = deposit64(env->htimedelta, 0, 32, (uint64_t)val); +#else + env->htimedelta = val; +#endif + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_htimedeltah(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!env->rdtime_fn) { + return -1; + } + + *val = env->htimedelta >> 32; + return 0; +} + +static int write_htimedeltah(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!env->rdtime_fn) { + return -1; + } + + env->htimedelta = deposit64(env->htimedelta, 32, 32, (uint64_t)val); + return 0; +} +#endif + /* Virtual CSR Registers */ static int read_vsstatus(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1202,14 +1278,12 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [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) + /* In privileged mode, the monitor will have to emulate TIME CSRs only if + * rdtime callback is not provided by machine/platform emulation */ [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 */ @@ -1275,6 +1349,10 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HTVAL] = { hmode, read_htval, write_htval }, [CSR_HTINST] = { hmode, read_htinst, write_htinst }, [CSR_HGATP] = { hmode, read_hgatp, write_hgatp }, + [CSR_HTIMEDELTA] = { hmode, read_htimedelta, write_htimedelta }, +#if defined(TARGET_RISCV32) + [CSR_HTIMEDELTAH] = { hmode, read_htimedeltah, write_htimedeltah}, +#endif [CSR_VSSTATUS] = { hmode, read_vsstatus, write_vsstatus }, [CSR_VSIP] = { hmode, NULL, NULL, rmw_vsip }, -- cgit 1.4.1