Commit 0e5c9a9d authored by Marc Zyngier's avatar Marc Zyngier
Browse files

KVM: arm64: Expose SMC/HVC width to userspace



When returning to userspace to handle a SMCCC call, we consistently
set PC to point to the instruction immediately after the HVC/SMC.

However, should userspace need to know the exact address of the
trapping instruction, it needs to know about the *size* of that
instruction. For AArch64, this is pretty easy. For AArch32, this
is a bit more funky, as Thumb has 16bit encodings for both HVC
and SMC.

Expose this to userspace with a new flag that directly derives
from ESR_EL2.IL. Also update the documentation to reflect the PC
state at the point of exit.

Finally, this fixes a small buglet where the hypercall.{args,ret}
fields would not be cleared on exit, and could contain some
random junk.

Reviewed-by: default avatarOliver Upton <oliver.upton@linux.dev>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/86pm8iv8tj.wl-maz@kernel.org
parent 60e7dade
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -6244,6 +6244,14 @@ Definition of ``flags``:
   conduit to initiate the SMCCC call. If this bit is 0 then the guest
   used the HVC conduit for the SMCCC call.

 - ``KVM_HYPERCALL_EXIT_16BIT``: Indicates that the guest used a 16bit
   instruction to initiate the SMCCC call. If this bit is 0 then the
   guest used a 32bit instruction. An AArch64 guest always has this
   bit set to 0.

At the point of exit, PC points to the instruction immediately following
the trapping instruction.

::

		/* KVM_EXIT_TPR_ACCESS */
+2 −1
Original line number Diff line number Diff line
@@ -492,6 +492,7 @@ struct kvm_smccc_filter {

/* arm64-specific KVM_EXIT_HYPERCALL flags */
#define KVM_HYPERCALL_EXIT_SMC		(1U << 0)
#define KVM_HYPERCALL_EXIT_16BIT	(1U << 1)

#endif

+11 −5
Original line number Diff line number Diff line
@@ -222,13 +222,19 @@ static void kvm_prepare_hypercall_exit(struct kvm_vcpu *vcpu, u32 func_id)
{
	u8 ec = ESR_ELx_EC(kvm_vcpu_get_esr(vcpu));
	struct kvm_run *run = vcpu->run;

	run->exit_reason = KVM_EXIT_HYPERCALL;
	run->hypercall.nr = func_id;
	run->hypercall.flags = 0;
	u64 flags = 0;

	if (ec == ESR_ELx_EC_SMC32 || ec == ESR_ELx_EC_SMC64)
		run->hypercall.flags |= KVM_HYPERCALL_EXIT_SMC;
		flags |= KVM_HYPERCALL_EXIT_SMC;

	if (!kvm_vcpu_trap_il_is32bit(vcpu))
		flags |= KVM_HYPERCALL_EXIT_16BIT;

	run->exit_reason = KVM_EXIT_HYPERCALL;
	run->hypercall = (typeof(run->hypercall)) {
		.nr	= func_id,
		.flags	= flags,
	};
}

int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)