Commit 5a5d79ac authored by Anup Patel's avatar Anup Patel Committed by Anup Patel
Browse files

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: default avatarAnup Patel <anup.patel@wdc.com>
Acked-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Reviewed-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Acked-by: default avatarPalmer Dabbelt <palmerdabbelt@google.com>
parent 9f701326
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
@@ -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: