[PATCH v2 19/39] xen/riscv: implement trap redirection to a guest
Oleksii Kurochko <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <583b0a190bad7121eae9bc99bd1e13dc1743efe4.1787838835.git.oleksii.kurochko@gmail.com> |
Some traps taken by Xen on behalf of a guest can't or shouldn't be handled by the hypervisor and have to be reflected to the guest's own S-mode trap handler instead: the access faults which handle_guest_page_fault() injects for a fault that can never become an emulated access, and, later on, a fault taken by the hlv/hlvx sequences of riscv_read_guest() while accessing guest memory on a vCPU's behalf. Implement trap_redirect(), until now a BUG_ON() placeholder, for that purpose. It makes the trap appear to the guest as if it had been taken directly in VS-mode: the trap information is transferred to the guest's virtual supervisor CSRs and the vCPU is resumed at its exception vector in supervisor mode, following the trap entry rules of the RISC-V privileged specification. Add the STVEC_* definitions needed to tell the BASE and MODE fields of vstvec apart. The implementation is based on kvm_riscv_vcpu_trap_redirect() from Linux, with a few deviations: - The function reads and writes physical VS-mode CSRs, so it is only meaningful for the currently running vCPU. Instead of taking a struct vcpu argument, it always operates on current. - The MODE field of vstvec is masked off explicitly when computing the exception target PC (exceptions always vector to BASE), rather than relying on the hardwired zero bit of sepc to drop it on VM entry. - Assertions document the preconditions: the trap must have been taken from virtualized mode (hstatus.SPV set), and only synchronous exceptions may be redirected - interrupts must be injected via hvip instead, so that the hardware performs VS-mode trap entry itself, respecting vsstatus.SIE and vectored vstvec dispatch. Signed-off-by: Oleksii Kurochko <[email protected]> --- Changes in v2: - Add new defines STVEC_*. The STVEC_MODE_DIRECT/_VECTORED values are currently unused and were included because riscv encoding header is a spec mirror full of unused encodings. - Use STVEC_BASE_MASK instead of open-coding it. - Rename riscv_vcpu_trap_redirect() to trap_redirect(): unlike its KVM counterpart the function takes no vCPU argument, it implicitly operates on current, so "vcpu" in the name describes nothing. --- xen/arch/riscv/include/asm/riscv_encoding.h | 6 +++ xen/arch/riscv/traps.c | 50 ++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/xen/arch/riscv/include/asm/riscv_encoding.h b/xen/arch/riscv/include/asm/riscv_encoding.h index 2d2e7e11b3ef..b2071f47587c 100644 --- a/xen/arch/riscv/include/asm/riscv_encoding.h +++ b/xen/arch/riscv/include/asm/riscv_encoding.h @@ -109,6 +109,12 @@ #define SIP_SSIP MIP_SSIP #define SIP_STIP MIP_STIP +/* stvec/vstvec: MODE is bits [1:0], BASE is bits [XLEN-1:2] */ +#define STVEC_MODE_MASK _UL(0x3) +#define STVEC_MODE_DIRECT _UL(0x0) +#define STVEC_MODE_VECTORED _UL(0x1) +#define STVEC_BASE_MASK (~STVEC_MODE_MASK) + #define PRV_U _UL(0) #define PRV_S _UL(1) #define PRV_M _UL(3) diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index 9cd37d943be1..8372f34497ad 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -294,5 +294,53 @@ enum mc_disposition arch_do_multicall_call(struct mc_state *state) /* Redirect trap to Guest. */ void trap_redirect(const struct trap_info *trap) { - BUG_ON("unimplemented"); + struct cpu_user_regs *regs = vcpu_guest_cpu_user_regs(current); + unsigned long vsstatus = csr_read(CSR_VSSTATUS); + + /* + * Redirecting a trap makes sense only if the trap was taken from + * virtualized mode, i.e. sret is going to return to VS-mode. + */ + ASSERT(regs->hstatus & HSTATUS_SPV); + + /* + * Only synchronous exceptions can be redirected. Interrupts must be + * injected via hvip instead, so that the hardware itself performs + * VS-mode trap entry, respecting vsstatus.SIE and the vectored + * dispatch (BASE + 4 * cause) if vstvec is configured so. + */ + ASSERT(!(trap->scause & CAUSE_IRQ_FLAG)); + + /* Change Guest SSTATUS.SPP bit */ + vsstatus &= ~SSTATUS_SPP; + if ( regs->sstatus & SSTATUS_SPP ) + vsstatus |= SSTATUS_SPP; + + /* Change Guest SSTATUS.SPIE bit */ + vsstatus &= ~SSTATUS_SPIE; + if ( vsstatus & SSTATUS_SIE ) + vsstatus |= SSTATUS_SPIE; + + /* Clear Guest SSTATUS.SIE bit */ + vsstatus &= ~SSTATUS_SIE; + + /* Update Guest SSTATUS */ + csr_write(CSR_VSSTATUS, vsstatus); + + /* Update Guest SCAUSE, STVAL, and SEPC */ + csr_write(CSR_VSCAUSE, trap->scause); + csr_write(CSR_VSTVAL, trap->stval); + csr_write(CSR_VSEPC, trap->sepc); + + /* + * Set Guest PC to Guest exception vector. + * + * vstvec's MODE field is not part of the address. Exceptions always + * target BASE regardless of MODE, so mask it off explicitly instead of + * relying on the hardwired zero bit of sepc to drop it. + */ + regs->sepc = csr_read(CSR_VSTVEC) & STVEC_BASE_MASK; + + /* Set Guest privilege mode to supervisor */ + regs->sstatus |= SSTATUS_SPP; } -- 2.55.0