[PATCH v2 23/39] xen/riscv: look up the exception table for any trap taken in Xen context
Oleksii Kurochko <[email protected]>
| Newsgroups | gmane.comp.emulators.xen.devel |
|---|---|
| Message-ID | <c8c767e5932057e1fe3ca7756246ded691adad4f.1787838835.git.oleksii.kurochko@gmail.com> |
do_trap() consulted the exception table only for CAUSE_ILLEGAL_INSTRUCTION, which covers csr_read_safe() but not the hlv/hlvx sequences reading guest memory: those fault with load/store (guest) page fault causes and would reach do_unexpected_trap() instead of their fixup. Move the lookup ahead of the cause switch, and gate it on the trap having been taken in Xen context and not being an interrupt: - sepc of a trap taken from the guest is a guest VA/PA, which the guest can point at an address listed in the exception table; Xen would then act on that entry and, for EX_TYPE_TRAP_INFO, write through a pointer fully under guest control. Entries are matched by exact address, so this needs no more than a numerical collision. - an interrupt taken at an address listed in the table would otherwise be "fixed up" as if the access itself had faulted, silently skipping it and handing the caller the interrupt's scause as a fault cause. Returning early skips check_for_pcpu_work(), which is correct: that only runs for traps taken from the guest. With that in place a G-stage fault reaching the switch can no longer have been caused by an hlv/hlvx covered by an entry, so anything left must have come from the guest; assert as much. Cache the "trap came from the guest" test in a local, it is now used four times. Signed-off-by: Oleksii Kurochko <[email protected]> --- Changes in v2: - New patch. --- --- xen/arch/riscv/traps.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index 8372f34497ad..f5f83fce10ba 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -196,11 +196,34 @@ void do_trap(struct cpu_user_regs *cpu_regs) unsigned long cause = csr_read(CSR_SCAUSE); bool from_guest = cpu_regs->hstatus & HSTATUS_SPV; + /* + * A synchronous trap taken in Xen context may come from an access done on + * a vCPU's behalf, e.g. the hlv/hlvx sequences in riscv_read_guest(), + * or from a probing access like csr_read_safe(). Both are covered by + * exception table entries which record the fault details for the caller + * and resume execution past the faulting instruction. + * + * Traps taken from the guest must never be fixed up: sepc is then a guest + * address, which the guest could point at an address listed in the + * exception table, making Xen act on an entry (and, for EX_TYPE_TRAP_INFO, + * write through a pointer) fully under guest control. + * + * Interrupts must be excluded too: one taken at an address which happens + * to be listed in the exception table would otherwise be "fixed up" as if + * the access itself had faulted, silently skipping it. + * + * Returning early skips check_for_pcpu_work() below, which is correct: + * that only runs for traps taken from the guest. + */ + if ( !from_guest && !(cause & CAUSE_IRQ_FLAG) && + fixup_exception(cpu_regs, cause) ) + return; + switch ( cause ) { case CAUSE_VIRTUAL_SUPERVISOR_ECALL: /* CAUSE_VIRTUAL_SUPERVISOR_ECALL should come from VS-mode */ - BUG_ON(!(cpu_regs->hstatus & HSTATUS_SPV)); + BUG_ON(!from_guest); vsbi_handle_ecall(cpu_regs); break; @@ -232,9 +255,6 @@ void do_trap(struct cpu_user_regs *cpu_regs) break; } - if ( fixup_exception(cpu_regs, cause) ) - break; - fallthrough; default: if ( cause & CAUSE_IRQ_FLAG ) -- 2.55.0