[PATCH 08/17] KVM: arm64: Implement HVC handling for protected guests at EL2
Fuad Tabba
fuad.tabba at linux.dev
Mon Aug 31 09:34:12 PDT 2026
Extend kvm_handle_pvm_hvc64() to handle SMCCC_VERSION,
SMCCC_ARCH_FEATURES and the vendor hypervisor call UID at EL2, so
these queries do not reach the host. ARCH_FEATURES is mandatory from
SMCCC 1.1, the version EL2 reports: it returns SUCCESS for itself and
for SMCCC_VERSION, and NOT_SUPPORTED for anything else.
Add handle_pvm_entry_hvc64() and handle_pvm_exit_hvc64(), which
forward a protected guest's HVCs to the host and return the reply; a
later patch wires them into the per-EC dispatch tables.
Signed-off-by: Fuad Tabba <fuad.tabba at linux.dev>
---
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 28 ++++++++++++++++++++++++++++
arch/arm64/kvm/hyp/nvhe/pkvm.c | 29 +++++++++++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index d4b0f69ff130c..62864db1e099a 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -4,6 +4,8 @@
* Author: Andrew Scull <ascull at google.com>
*/
+#include <kvm/arm_hypercalls.h>
+
#include <hyp/adjust_pc.h>
#include <hyp/switch.h>
@@ -33,6 +35,32 @@ void __kvm_hyp_host_forward_smc(struct kvm_cpu_context *host_ctxt);
typedef void (*hyp_entry_exit_handler_fn)(struct pkvm_hyp_vcpu *);
+static void __maybe_unused handle_pvm_entry_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ u64 ret =
+ READ_ONCE(hyp_vcpu->host_vcpu->arch.ctxt.regs.regs[i]);
+ vcpu_set_reg(&hyp_vcpu->vcpu, i, ret);
+ }
+}
+
+static void __maybe_unused handle_pvm_exit_hvc64(struct pkvm_hyp_vcpu *hyp_vcpu)
+{
+ struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+ int i;
+
+ WRITE_ONCE(host_vcpu->arch.fault.esr_el2,
+ hyp_vcpu->vcpu.arch.fault.esr_el2);
+
+ /* Pass the HVC function id (r0) and its arguments. */
+ for (i = 0; i < 8; i++) {
+ WRITE_ONCE(host_vcpu->arch.ctxt.regs.regs[i],
+ vcpu_get_reg(&hyp_vcpu->vcpu, i));
+ }
+}
+
static void handle_vm_entry_generic(struct pkvm_hyp_vcpu *hyp_vcpu)
{
vcpu_copy_flag(&hyp_vcpu->vcpu, hyp_vcpu->host_vcpu, PC_UPDATE_REQ);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index af334318d0a03..0fe11f95e2e26 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -1144,8 +1144,37 @@ bool kvm_handle_pvm_hvc64(struct kvm_vcpu *vcpu, u64 *exit_code)
{
u64 val[4] = { SMCCC_RET_INVALID_PARAMETER };
bool handled = true;
+ u32 feature;
+ uuid_t uuid;
switch (smccc_get_function(vcpu)) {
+ case ARM_SMCCC_VERSION_FUNC_ID:
+ /* Nothing to be handled by the host. Go back to the guest. */
+ val[0] = ARM_SMCCC_VERSION_1_1;
+ val[1] = 0;
+ val[2] = 0;
+ val[3] = 0;
+ break;
+ case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
+ /* SUCCESS only for the architecture calls EL2 implements. */
+ feature = smccc_get_arg1(vcpu);
+ switch (feature) {
+ case ARM_SMCCC_VERSION_FUNC_ID:
+ case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
+ val[0] = SMCCC_RET_SUCCESS;
+ break;
+ default:
+ val[0] = SMCCC_RET_NOT_SUPPORTED;
+ break;
+ }
+ break;
+ case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
+ uuid = ARM_SMCCC_VENDOR_HYP_UID_KVM;
+ val[0] = smccc_uuid_to_reg(&uuid, 0);
+ val[1] = smccc_uuid_to_reg(&uuid, 1);
+ val[2] = smccc_uuid_to_reg(&uuid, 2);
+ val[3] = smccc_uuid_to_reg(&uuid, 3);
+ break;
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_HYP_MEMINFO);
--
2.39.5
More information about the linux-arm-kernel
mailing list