Linux Trace Kernel
 help / color / mirror / Atom feed
From: Fuad Tabba <fuad.tabba@linux.dev>
To: maz@kernel.org, oupton@kernel.org,
	linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev
Cc: catalin.marinas@arm.com, will@kernel.org, rostedt@goodmis.org,
	mhiramat@kernel.org, alexandru.elisei@arm.com,
	vdonnefort@google.com, joey.gouly@arm.com, seiden@linux.ibm.com,
	suzuki.poulose@arm.com, yuzenghui@huawei.com, qperret@google.com,
	ardb@kernel.org, linux-kernel@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org, tabba@google.com,
	fuad.tabba@linux.dev
Subject: [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller
Date: Mon, 20 Jul 2026 17:13:41 +0100	[thread overview]
Message-ID: <20260720161343.1367007-10-fuad.tabba@linux.dev> (raw)
In-Reply-To: <20260720161343.1367007-1-fuad.tabba@linux.dev>

kvm_call_hyp_nvhe() reduces its target to an SMCCC function number, so
the compiler never sees a callable: arguments that are wrong in number,
type or order are silently marshalled into registers. The kvm_call_hyp()
wrappers only catch this on the VHE branch, and the pKVM-only hypercalls
have no such branch.

Declare each hypercall's signature once in kvm_hcall.h and generate a
typed stub from it, in the mold of the syscall wrappers. Make
kvm_call_hyp_nvhe() resolve to the stub so every caller is checked
against the declared signature; a stale or mistyped call now fails to
compile. The stubs inline to the same SMCCC call the untyped macro used
to make: the compiled callers are unchanged, apart from hypercall
returns now being tested at their declared width.

The stage-2 protection arguments are declared u64 rather than
enum kvm_pgtable_prot, as kvm_pgtable.h includes linux/kvm_host.h and
the enum cannot be completed here.

Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 arch/arm64/include/asm/kvm_hcall.h | 165 ++++++++++++++++++++++++++++-
 arch/arm64/kvm/hyp_trace.c         |   2 +-
 2 files changed, 165 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index d925b2c28a3d8..51bfd14748464 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -16,12 +16,35 @@
 
 #include <asm/barrier.h>
 #include <asm/kvm_asm.h>
+#include <asm/spectre.h>
 #include <asm/virt.h>
 
 typedef u16 pkvm_handle_t;
 
+struct kvm;
+struct kvm_s2_mmu;
+struct kvm_vcpu;
+struct vgic_v3_cpu_if;
+struct vgic_v5_cpu_if;
+
+/*
+ * Hypercall signatures are declared as (type, name) argument pairs.
+ * __KVM_HCALL_MAP() applies a macro to each pair, in the mold of __MAP()
+ * in <linux/syscalls.h>.
+ */
+#define __KVM_HCALL_MAP1(m, t, a, ...) m(t, a)
+#define __KVM_HCALL_MAP2(m, t, a, ...) m(t, a), __KVM_HCALL_MAP1(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP3(m, t, a, ...) m(t, a), __KVM_HCALL_MAP2(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP4(m, t, a, ...) m(t, a), __KVM_HCALL_MAP3(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP5(m, t, a, ...) m(t, a), __KVM_HCALL_MAP4(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP6(m, t, a, ...) m(t, a), __KVM_HCALL_MAP5(m, __VA_ARGS__)
+#define __KVM_HCALL_MAP(n, ...) __KVM_HCALL_MAP##n(__VA_ARGS__)
+
+#define __KVM_HCALL_DECL(t, a)	t a
+#define __KVM_HCALL_ARGS(t, a)	a
+
 #ifndef __KVM_NVHE_HYPERVISOR__
-#define kvm_call_hyp_nvhe(f, ...)					\
+#define __kvm_call_hyp_nvhe(f, ...)					\
 	({								\
 		struct arm_smccc_res res;				\
 									\
@@ -33,6 +56,43 @@ typedef u16 pkvm_handle_t;
 		res.a1;							\
 	})
 
+/*
+ * Generate a typed stub for each declared hypercall. kvm_call_hyp_nvhe()
+ * resolves to the stub, so a call with the wrong argument count or types
+ * fails to compile instead of being silently truncated to an SMCCC function
+ * number and a pile of registers. The stub inlines to the same SMCCC call
+ * the untyped macro used to make.
+ */
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...)			\
+	static __always_inline						\
+	ret nvhe_hvc_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__)) \
+	{								\
+		return (ret)__kvm_call_hyp_nvhe(name,			\
+			__KVM_HCALL_MAP(x, __KVM_HCALL_ARGS, __VA_ARGS__)); \
+	}
+
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...)			\
+	static __always_inline						\
+	void nvhe_hvc_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__)) \
+	{								\
+		__kvm_call_hyp_nvhe(name,				\
+			__KVM_HCALL_MAP(x, __KVM_HCALL_ARGS, __VA_ARGS__)); \
+	}
+
+#define DECLARE_KVM_HOST_HCALL0(ret, name)				\
+	static __always_inline ret nvhe_hvc_##name(void)		\
+	{								\
+		return (ret)__kvm_call_hyp_nvhe(name);			\
+	}
+
+#define DECLARE_KVM_HOST_HCALL0_VOID(name)				\
+	static __always_inline void nvhe_hvc_##name(void)		\
+	{								\
+		__kvm_call_hyp_nvhe(name);				\
+	}
+
+#define kvm_call_hyp_nvhe(f, ...)	nvhe_hvc_##f(__VA_ARGS__)
+
 /*
  * The isb() below is there to guarantee the same behaviour on VHE as on !VHE,
  * where the eret to EL1 acts as a context synchronization event.
@@ -63,6 +123,109 @@ typedef u16 pkvm_handle_t;
 #define kvm_call_hyp(f, ...) f(__VA_ARGS__)
 #define kvm_call_hyp_ret(f, ...) f(__VA_ARGS__)
 #define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__)
+
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...)
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...)
+#define DECLARE_KVM_HOST_HCALL0(ret, name)
+#define DECLARE_KVM_HOST_HCALL0_VOID(name)
 #endif /* __KVM_NVHE_HYPERVISOR__ */
 
+/* Hypercalls that are unavailable once pKVM has finalised. */
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init, 4,
+	phys_addr_t, phys, unsigned long, size,
+	unsigned long *, per_cpu_base, u32, hyp_va_bits)
+DECLARE_KVM_HOST_HCALL(unsigned long, __pkvm_create_private_mapping, 3,
+	phys_addr_t, phys, size_t, size, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_cpu_set_vector, 1,
+	enum arm64_hyp_spectre_vector, slot)
+DECLARE_KVM_HOST_HCALL0_VOID(__kvm_enable_ssbs)
+DECLARE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
+DECLARE_KVM_HOST_HCALL0(u64, __vgic_v3_get_gic_config)
+
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_prot_finalize)
+
+/* Hypercalls that are always available and common to [nh]VHE/pKVM. */
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
+	struct kvm_vcpu *, vcpu)
+DECLARE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
+	struct kvm_vcpu *, vcpu)
+DECLARE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
+	struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
+	struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
+	struct kvm_s2_mmu *, mmu)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
+	struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
+	struct kvm_s2_mmu *, mmu)
+DECLARE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
+	u64, cntvoff)
+DECLARE_KVM_HOST_HCALL(int, __tracing_load, 2,
+	void *, desc_hva, size_t, desc_size)
+DECLARE_KVM_HOST_HCALL0_VOID(__tracing_unload)
+DECLARE_KVM_HOST_HCALL(int, __tracing_enable, 1,
+	bool, enable)
+DECLARE_KVM_HOST_HCALL(int, __tracing_swap_reader, 1,
+	unsigned int, cpu)
+DECLARE_KVM_HOST_HCALL_VOID(__tracing_update_clock, 4,
+	u32, mult, u32, shift, u64, epoch_ns, u64, epoch_cyc)
+DECLARE_KVM_HOST_HCALL(int, __tracing_reset, 1,
+	unsigned int, cpu)
+DECLARE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
+	unsigned short, id, bool, enable)
+DECLARE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
+	u64, id)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
+	struct vgic_v3_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
+	struct vgic_v3_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
+	struct vgic_v5_cpu_if *, cpu_if)
+DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
+	struct vgic_v5_cpu_if *, cpu_if)
+
+/* Hypercalls that are available only when pKVM has finalised. */
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
+	u64, pfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_unshare_hyp, 1,
+	u64, pfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_donate_guest, 2,
+	u64, pfn, u64, gfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_guest, 4,
+	u64, pfn, u64, gfn, u64, nr_pages, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_unshare_guest, 3,
+	pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_relax_perms_guest, 2,
+	u64, gfn, u64, prot)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_wrprotect_guest, 3,
+	pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_test_clear_young_guest, 4,
+	pkvm_handle_t, handle, u64, gfn, u64, nr_pages, bool, mkold)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
+	u64, gfn)
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_reserve_vm)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
+	pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
+	struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
+	pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
+	void *, vcpu_hva)
+DECLARE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_force_reclaim_guest_page, 1,
+	phys_addr_t, phys)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_reclaim_dying_guest_page, 2,
+	pkvm_handle_t, handle, u64, gfn)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_start_teardown_vm, 1,
+	pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
+	pkvm_handle_t, handle)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_vcpu_load, 3,
+	pkvm_handle_t, handle, unsigned int, vcpu_idx, u64, hcr_el2)
+DECLARE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
+DECLARE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
+	pkvm_handle_t, handle)
+
 #endif /* __ARM64_KVM_HCALL_H__ */
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index 2411b4c32932c..7fe0e8feb7d7c 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -264,7 +264,7 @@ static struct trace_buffer_desc *hyp_trace_load(unsigned long size, void *priv)
 	if (ret)
 		goto err_free_buffer;
 
-	ret = kvm_call_hyp_nvhe(__tracing_load, (unsigned long)desc, desc_size);
+	ret = kvm_call_hyp_nvhe(__tracing_load, desc, desc_size);
 	if (ret)
 		goto err_unload_pages;
 
-- 
2.39.5


  parent reply	other threads:[~2026-07-20 16:14 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h Fuad Tabba
2026-07-20 18:32   ` Steven Rostedt
2026-07-20 16:13 ` [PATCH v1 02/11] KVM: arm64: nVHE: Share the stacktrace per-CPU declarations with EL2 Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 04/11] KVM: arm64: nVHE: Use NULL to reset the trace buffer backing pointer Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 05/11] KVM: arm64: nVHE: Run the source checker under C=2 Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 06/11] arm64: pi: Run the source checker on the libfdt objects " Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 07/11] KVM: arm64: nVHE: Pass host VA arguments as pointers Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 08/11] KVM: arm64: Move the host hypercall interface to its own header Fuad Tabba
2026-07-20 16:13 ` Fuad Tabba [this message]
2026-07-20 16:24 ` [PATCH v1 10/11] KVM: arm64: nVHE: Check hypercall handlers against the declared ABI Fuad Tabba
2026-07-20 16:24   ` [PATCH v1 11/11] KVM: arm64: Tag host-VA hypercall parameters __hostva Fuad Tabba

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260720161343.1367007-10-fuad.tabba@linux.dev \
    --to=fuad.tabba@linux.dev \
    --cc=alexandru.elisei@arm.com \
    --cc=ardb@kernel.org \
    --cc=catalin.marinas@arm.com \
    --cc=joey.gouly@arm.com \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=oupton@kernel.org \
    --cc=qperret@google.com \
    --cc=rostedt@goodmis.org \
    --cc=seiden@linux.ibm.com \
    --cc=suzuki.poulose@arm.com \
    --cc=tabba@google.com \
    --cc=vdonnefort@google.com \
    --cc=will@kernel.org \
    --cc=yuzenghui@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox