Commit 9b661950 authored by Mark Rutland's avatar Mark Rutland Committed by Marc Zyngier
Browse files

KVM: arm64: Refactor exit handlers



The hyp exit handling logic is largely shared between VHE and nVHE/hVHE,
with common logic in arch/arm64/kvm/hyp/include/hyp/switch.h. The code
in the header depends on function definitions provided by
arch/arm64/kvm/hyp/vhe/switch.c and arch/arm64/kvm/hyp/nvhe/switch.c
when they include the header.

This is an unusual header dependency, and prevents the use of
arch/arm64/kvm/hyp/include/hyp/switch.h in other files as this would
result in compiler warnings regarding missing definitions, e.g.

| In file included from arch/arm64/kvm/hyp/nvhe/hyp-main.c:8:
| ./arch/arm64/kvm/hyp/include/hyp/switch.h:733:31: warning: 'kvm_get_exit_handler_array' used but never defined
|   733 | static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu);
|       |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
| ./arch/arm64/kvm/hyp/include/hyp/switch.h:735:13: warning: 'early_exit_filter' used but never defined
|   735 | static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code);
|       |             ^~~~~~~~~~~~~~~~~

Refactor the logic such that the header doesn't depend on anything from
the C files. There should be no functional change as a result of this
patch.

Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Reviewed-by: default avatarMark Brown <broonie@kernel.org>
Tested-by: default avatarMark Brown <broonie@kernel.org>
Acked-by: default avatarWill Deacon <will@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Fuad Tabba <tabba@google.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Oliver Upton <oliver.upton@linux.dev>
Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20250210195226.1215254-7-mark.rutland@arm.com


Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parent ee14db31
Loading
Loading
Loading
Loading
+6 −24
Original line number Diff line number Diff line
@@ -679,23 +679,16 @@ static bool kvm_hyp_handle_dabt_low(struct kvm_vcpu *vcpu, u64 *exit_code)

typedef bool (*exit_handler_fn)(struct kvm_vcpu *, u64 *);

static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu);

static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code);

/*
 * Allow the hypervisor to handle the exit with an exit handler if it has one.
 *
 * Returns true if the hypervisor handled the exit, and control should go back
 * to the guest, or false if it hasn't.
 */
static inline bool kvm_hyp_handle_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
static inline bool kvm_hyp_handle_exit(struct kvm_vcpu *vcpu, u64 *exit_code,
				       const exit_handler_fn *handlers)
{
	const exit_handler_fn *handlers = kvm_get_exit_handler_array(vcpu);
	exit_handler_fn fn;

	fn = handlers[kvm_vcpu_trap_get_class(vcpu)];

	exit_handler_fn fn = handlers[kvm_vcpu_trap_get_class(vcpu)];
	if (fn)
		return fn(vcpu, exit_code);

@@ -725,20 +718,9 @@ static inline void synchronize_vcpu_pstate(struct kvm_vcpu *vcpu, u64 *exit_code
 * the guest, false when we should restore the host state and return to the
 * main run loop.
 */
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
static inline bool __fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code,
				      const exit_handler_fn *handlers)
{
	/*
	 * Save PSTATE early so that we can evaluate the vcpu mode
	 * early on.
	 */
	synchronize_vcpu_pstate(vcpu, exit_code);

	/*
	 * Check whether we want to repaint the state one way or
	 * another.
	 */
	early_exit_filter(vcpu, exit_code);

	if (ARM_EXCEPTION_CODE(*exit_code) != ARM_EXCEPTION_IRQ)
		vcpu->arch.fault.esr_el2 = read_sysreg_el2(SYS_ESR);

@@ -768,7 +750,7 @@ static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
		goto exit;

	/* Check if there's an exit handler and allow it to handle the exit. */
	if (kvm_hyp_handle_exit(vcpu, exit_code))
	if (kvm_hyp_handle_exit(vcpu, exit_code, handlers))
		goto guest;
exit:
	/* Return to the host kernel and handle the exit */
+16 −12
Original line number Diff line number Diff line
@@ -250,19 +250,21 @@ static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
	return hyp_exit_handlers;
}

static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
{
	const exit_handler_fn *handlers = kvm_get_exit_handler_array(vcpu);

	synchronize_vcpu_pstate(vcpu, exit_code);

	/*
 * Some guests (e.g., protected VMs) are not be allowed to run in AArch32.
 * The ARMv8 architecture does not give the hypervisor a mechanism to prevent a
 * guest from dropping to AArch32 EL0 if implemented by the CPU. If the
 * hypervisor spots a guest in such a state ensure it is handled, and don't
 * trust the host to spot or fix it.  The check below is based on the one in
	 * Some guests (e.g., protected VMs) are not be allowed to run in
	 * AArch32.  The ARMv8 architecture does not give the hypervisor a
	 * mechanism to prevent a guest from dropping to AArch32 EL0 if
	 * implemented by the CPU. If the hypervisor spots a guest in such a
	 * state ensure it is handled, and don't trust the host to spot or fix
	 * it.  The check below is based on the one in
	 * kvm_arch_vcpu_ioctl_run().
 *
 * Returns false if the guest ran in AArch32 when it shouldn't have, and
 * thus should exit to the host, or true if a the guest run loop can continue.
	 */
static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
{
	if (unlikely(vcpu_is_protected(vcpu) && vcpu_mode_is_32bit(vcpu))) {
		/*
		 * As we have caught the guest red-handed, decide that it isn't
@@ -275,6 +277,8 @@ static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
		*exit_code &= BIT(ARM_EXIT_WITH_SERROR_BIT);
		*exit_code |= ARM_EXCEPTION_IL;
	}

	return __fixup_guest_exit(vcpu, exit_code, handlers);
}

/* Switch to the guest for legacy non-VHE systems */
+4 −5
Original line number Diff line number Diff line
@@ -540,13 +540,10 @@ static const exit_handler_fn hyp_exit_handlers[] = {
	[ESR_ELx_EC_MOPS]		= kvm_hyp_handle_mops,
};

static const exit_handler_fn *kvm_get_exit_handler_array(struct kvm_vcpu *vcpu)
static inline bool fixup_guest_exit(struct kvm_vcpu *vcpu, u64 *exit_code)
{
	return hyp_exit_handlers;
}
	synchronize_vcpu_pstate(vcpu, exit_code);

static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
{
	/*
	 * If we were in HYP context on entry, adjust the PSTATE view
	 * so that the usual helpers work correctly.
@@ -566,6 +563,8 @@ static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code)
		*vcpu_cpsr(vcpu) &= ~(PSR_MODE_MASK | PSR_MODE32_BIT);
		*vcpu_cpsr(vcpu) |= mode;
	}

	return __fixup_guest_exit(vcpu, exit_code, hyp_exit_handlers);
}

/* Switch to the guest for VHE systems running in EL2 */