KVM: arm64: Expose SMC/HVC width to userspace
authorMarc Zyngier <maz@kernel.org>
Wed, 5 Apr 2023 11:48:58 +0000 (12:48 +0100)
committerMarc Zyngier <maz@kernel.org>
Wed, 5 Apr 2023 18:20:23 +0000 (19:20 +0100)
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: Oliver Upton <oliver.upton@linux.dev>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/86pm8iv8tj.wl-maz@kernel.org
Documentation/virt/kvm/api.rst
arch/arm64/include/uapi/asm/kvm.h
arch/arm64/kvm/hypercalls.c

index c8ab2f7309451e67e2175d64ef4e84011a7d6c8d..103f945959ed25b21507c36e58e64ad830ad7857 100644 (file)
@@ -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 */
index 3dcfa4bfdf83f275a266e6e8afa1678914ee5de9..b1c1edf854805a98112f5f61756f7a4fc928182d 100644 (file)
@@ -491,7 +491,8 @@ struct kvm_smccc_filter {
 };
 
 /* arm64-specific KVM_EXIT_HYPERCALL flags */
-#define KVM_HYPERCALL_EXIT_SMC (1U << 0)
+#define KVM_HYPERCALL_EXIT_SMC         (1U << 0)
+#define KVM_HYPERCALL_EXIT_16BIT       (1U << 1)
 
 #endif
 
index 9a35d6d18193edc71d12c0eb674349e714523834..3b6523f25afc262ef640da67294731a1d837257e 100644 (file)
@@ -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)