mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
RISC-V: KVM: Handle WFI exits for VCPU
We get illegal instruction trap whenever Guest/VM executes WFI instruction. This patch handles WFI trap by blocking the trapped VCPU using kvm_vcpu_block() API. The blocked VCPU will be automatically resumed whenever a VCPU interrupt is injected from user-space or from in-kernel IRQCHIP emulation. Signed-off-by: Anup Patel <anup.patel@wdc.com> Acked-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>
This commit is contained in:
parent
9f70132651
commit
5a5d79acd7
@ -12,6 +12,13 @@
|
||||
#include <linux/kvm_host.h>
|
||||
#include <asm/csr.h>
|
||||
|
||||
#define INSN_OPCODE_MASK 0x007c
|
||||
#define INSN_OPCODE_SHIFT 2
|
||||
#define INSN_OPCODE_SYSTEM 28
|
||||
|
||||
#define INSN_MASK_WFI 0xffffffff
|
||||
#define INSN_MATCH_WFI 0x10500073
|
||||
|
||||
#define INSN_MATCH_LB 0x3
|
||||
#define INSN_MASK_LB 0x707f
|
||||
#define INSN_MATCH_LH 0x1003
|
||||
@ -116,6 +123,71 @@
|
||||
(s32)(((insn) >> 7) & 0x1f))
|
||||
#define MASK_FUNCT3 0x7000
|
||||
|
||||
static int truly_illegal_insn(struct kvm_vcpu *vcpu,
|
||||
struct kvm_run *run,
|
||||
ulong insn)
|
||||
{
|
||||
struct kvm_cpu_trap utrap = { 0 };
|
||||
|
||||
/* Redirect trap to Guest VCPU */
|
||||
utrap.sepc = vcpu->arch.guest_context.sepc;
|
||||
utrap.scause = EXC_INST_ILLEGAL;
|
||||
utrap.stval = insn;
|
||||
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int system_opcode_insn(struct kvm_vcpu *vcpu,
|
||||
struct kvm_run *run,
|
||||
ulong insn)
|
||||
{
|
||||
if ((insn & INSN_MASK_WFI) == INSN_MATCH_WFI) {
|
||||
vcpu->stat.wfi_exit_stat++;
|
||||
if (!kvm_arch_vcpu_runnable(vcpu)) {
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->arch.srcu_idx);
|
||||
kvm_vcpu_block(vcpu);
|
||||
vcpu->arch.srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
kvm_clear_request(KVM_REQ_UNHALT, vcpu);
|
||||
}
|
||||
vcpu->arch.guest_context.sepc += INSN_LEN(insn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return truly_illegal_insn(vcpu, run, insn);
|
||||
}
|
||||
|
||||
static int virtual_inst_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||
struct kvm_cpu_trap *trap)
|
||||
{
|
||||
unsigned long insn = trap->stval;
|
||||
struct kvm_cpu_trap utrap = { 0 };
|
||||
struct kvm_cpu_context *ct;
|
||||
|
||||
if (unlikely(INSN_IS_16BIT(insn))) {
|
||||
if (insn == 0) {
|
||||
ct = &vcpu->arch.guest_context;
|
||||
insn = kvm_riscv_vcpu_unpriv_read(vcpu, true,
|
||||
ct->sepc,
|
||||
&utrap);
|
||||
if (utrap.scause) {
|
||||
utrap.sepc = ct->sepc;
|
||||
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (INSN_IS_16BIT(insn))
|
||||
return truly_illegal_insn(vcpu, run, insn);
|
||||
}
|
||||
|
||||
switch ((insn & INSN_OPCODE_MASK) >> INSN_OPCODE_SHIFT) {
|
||||
case INSN_OPCODE_SYSTEM:
|
||||
return system_opcode_insn(vcpu, run, insn);
|
||||
default:
|
||||
return truly_illegal_insn(vcpu, run, insn);
|
||||
}
|
||||
}
|
||||
|
||||
static int emulate_load(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||
unsigned long fault_addr, unsigned long htinst)
|
||||
{
|
||||
@ -596,6 +668,10 @@ int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
|
||||
ret = -EFAULT;
|
||||
run->exit_reason = KVM_EXIT_UNKNOWN;
|
||||
switch (trap->scause) {
|
||||
case EXC_VIRTUAL_INST_FAULT:
|
||||
if (vcpu->arch.guest_context.hstatus & HSTATUS_SPV)
|
||||
ret = virtual_inst_fault(vcpu, run, trap);
|
||||
break;
|
||||
case EXC_INST_GUEST_PAGE_FAULT:
|
||||
case EXC_LOAD_GUEST_PAGE_FAULT:
|
||||
case EXC_STORE_GUEST_PAGE_FAULT:
|
||||
|
Loading…
Reference in New Issue
Block a user