* [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary
@ 2026-07-20 16:13 Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h Fuad Tabba
` (8 more replies)
0 siblings, 9 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
Hi folks,
For a change, I thought I'd send a series that's neither a bunch of
Sashiko bug fixes nor pKVM-specific.
The SMCCC conversion of the host/hyp interface reduced every hypercall
to a function number and a pile of registers: kvm_call_hyp_nvhe() never
shows the compiler a callable, so the caller's arguments and the
handler's DECLARE_REG() casts can disagree in type, count or order
without a diagnostic. Reviewing a fix for exactly that class of bug,
Marc wrote [1]:
"we lost all form of type-checking when everything was hastily
converted to SMCCC to avoid function pointers. Somehow, I feel that
the cure was worse than the disease.
I wish we'd reintroduce some form of compile-time checks, maybe by
having generated stubs?"
and sketched the shape he preferred [2]: a generated nvhe_hvc_##f()
stub that does the type-checking, driven by "some form of declarative
IDL", with "both the caller and callee stubs ... totally generated".
This series implements that, in plain preprocessor macros rather
than an external generator, in the mold of the syscall wrappers.
Each hypercall's signature is declared once, in kvm_hcall.h:
kvm_call_hyp_nvhe() resolves to a typed nvhe_hvc_##f() stub
generated from the declaration, and the hyp-main.c handlers
unmarshal their arguments through glue that is type-checked
against the same declaration. A mistyped or stale call, or a
handler that drifts from its caller, now fails to compile. On top
of that shared declaration, host-VA parameters gain a __hostva
sparse address space, so dereferencing one at EL2 without
kern_hyp_va_host() translation is flagged by sparse. The address
space is KVM-private and lives with the interface it annotates
rather than in compiler_types.h, like x86's __seg_gs in asm/percpu.h.
The deeper instances of that bug class, host VAs reached through
struct fields after the boundary, are follow-up work.
The declarations do not also generate the function-number enum or the
dispatch table. The enum carries the availability-band markers that
decide when each hypercall becomes reachable, and its base entry is
consumed from assembly, so generating it would hide the banding. The
remaining duplication cannot drift silently: a name mismatch between
declaration, caller and handler fails to compile, and a missing
dispatch-table entry is rejected at run time as
SMCCC_RET_NOT_SUPPORTED.
Along the way it turned out "make C=2" has never checked the nVHE
objects at all: the custom %.nvhe.o rule reuses rule_cc_o_c, which
only hooks the checker for C=1, and that only on a rebuild. Patches
2-5 fix the hook and the handful of pre-existing sparse warnings it
flushes out, so the checker lands clean. The libfdt objects under
arch/arm64/kernel/pi/ have the same gap through the same rule, so
patch 6 gives them the same one-line fix; it touches arm64 core
rather than KVM, so it can go through either tree. Patch 1 is
adjacent: trace_remote_event.h is not self-contained (it uses bool
without including linux/types.h), which the nVHE include order had
been silently working around; it can go through the tracing tree
instead if preferred.
The first six patches do not depend on the type-checking work (the
tracing-header fix, the sparse cleanups, and the two C=2 hook fixes),
so they could be taken on their own, ahead of the rest of the series
if that is easier.
The series is structured as follows:
01: Make trace_remote_event.h self-contained.
02-04: Fix the pre-existing sparse warnings in the nVHE code.
05: Run the source checker on nVHE objects under C=2.
06: Same for the libfdt objects under arch/arm64/kernel/pi/.
07: Pass the host-VA hypercall arguments as pointers at EL2.
08: Move the dispatch macros to a new kvm_hcall.h.
09: Declare the hypercall signatures, type-check the callers.
10: Generate the handler unmarshalling, type-check the handlers.
11: Tag host-VA parameters __hostva for sparse.
The generated code is unchanged, checked by comparing the disassembly
of every KVM object, function by function, against the base: the
callers are identical apart from hypercall returns now being tested at
their declared width and the stage-2 TLB-flush level argument now
being sign-extended as it is loaded (an s8 passed as an int), and the
handlers are instruction-for-instruction identical apart from
flush_hyp_vcpu()/sync_hyp_vcpu() being inlined into their only
caller. The extra argument that prompted the thread, a
mistyped or reordered argument, and a handler that disagrees with its
declaration all now fail to compile. checkpatch complains about the
__KVM_HCALL_MAP() machinery, the (type, name) pair syntax and the
generated signature typedefs, as it does about __MAP() in the syscall
wrappers; the idiom cannot be parenthesized, and the typedef is what
checks the handler against the declaration. Enforcing the
__hostva tag needs sparse v0.6.5-rc1 or later for __typeof_unqual__,
as all sparse checking on current kernels does; no stable sparse
release has it yet, so build from sparse.git (checker-valid.sh skips
the check, with a warning, on anything older).
Based on Linux 7.2-rc4 (1590cf0329716).
Cheers,
/fuad
[1] https://lore.kernel.org/all/86zf7po2n3.wl-maz@kernel.org/
[2] https://lore.kernel.org/all/86wm2tnsd8.wl-maz@kernel.org/
Fuad Tabba (11):
tracing: Include linux/types.h in trace_remote_event.h
KVM: arm64: nVHE: Share the stacktrace per-CPU declarations with EL2
KVM: arm64: nVHE: Declare the hyp event IDs before defining them
KVM: arm64: nVHE: Use NULL to reset the trace buffer backing pointer
KVM: arm64: nVHE: Run the source checker under C=2
arm64: pi: Run the source checker on the libfdt objects under C=2
KVM: arm64: nVHE: Pass host VA arguments as pointers
KVM: arm64: Move the host hypercall interface to its own header
KVM: arm64: Type-check hypercall arguments at the caller
KVM: arm64: nVHE: Check hypercall handlers against the declared ABI
KVM: arm64: Tag host-VA hypercall parameters __hostva
arch/arm64/include/asm/kvm_hcall.h | 255 +++++++++++++
arch/arm64/include/asm/kvm_host.h | 48 +--
arch/arm64/include/asm/kvm_mmu.h | 10 +
arch/arm64/include/asm/stacktrace/nvhe.h | 10 +-
arch/arm64/kernel/pi/Makefile | 1 +
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 7 +-
arch/arm64/kvm/hyp/include/nvhe/trace.h | 5 +-
arch/arm64/kvm/hyp/nvhe/Makefile | 1 +
arch/arm64/kvm/hyp/nvhe/events.c | 2 +
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 443 +++++++++++------------
arch/arm64/kvm/hyp/nvhe/pkvm.c | 12 +-
arch/arm64/kvm/hyp/nvhe/stacktrace.c | 3 +-
arch/arm64/kvm/hyp/nvhe/trace.c | 6 +-
arch/arm64/kvm/hyp_trace.c | 2 +-
arch/arm64/kvm/stacktrace.c | 3 -
include/linux/trace_remote_event.h | 2 +
16 files changed, 508 insertions(+), 302 deletions(-)
create mode 100644 arch/arm64/include/asm/kvm_hcall.h
base-commit: 1590cf0329716306e948a8fc29f1d3ee87d3989f
--
2.39.5
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h
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 ` Fuad Tabba
2026-07-20 18:32 ` Steven Rostedt
2026-07-20 16:13 ` [PATCH v1 03/11] KVM: arm64: nVHE: Declare the hyp event IDs before defining them Fuad Tabba
` (7 subsequent siblings)
8 siblings, 1 reply; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
trace_remote_event.h uses bool without including linux/types.h, so a
translation unit that includes it ahead of anything else that pulls
types.h in fails to build, as with nvhe/trace.h at EL2.
Fixes: 072529158e60 ("tracing: Add events to trace remotes")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
include/linux/trace_remote_event.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/trace_remote_event.h b/include/linux/trace_remote_event.h
index c8ae1e1f5e721..e4cc2d4497bcf 100644
--- a/include/linux/trace_remote_event.h
+++ b/include/linux/trace_remote_event.h
@@ -3,6 +3,8 @@
#ifndef _LINUX_TRACE_REMOTE_EVENTS_H
#define _LINUX_TRACE_REMOTE_EVENTS_H
+#include <linux/types.h>
+
struct trace_remote;
struct trace_event_fields;
struct trace_seq;
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 03/11] KVM: arm64: nVHE: Declare the hyp event IDs before defining them
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 16:13 ` 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
` (6 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
The defining expansion of HYP_EVENT() in events.c is the first time
its translation unit meets hyp_event_id_<name>, so sparse suggests
making the symbols static. Include kvm_hypevents.h ahead of
define_events.h so the extern declarations come first, as with the
tracepoint headers.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/events.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/events.c b/arch/arm64/kvm/hyp/nvhe/events.c
index add9383aadb5a..b845be0acd117 100644
--- a/arch/arm64/kvm/hyp/nvhe/events.c
+++ b/arch/arm64/kvm/hyp/nvhe/events.c
@@ -7,6 +7,8 @@
#include <nvhe/mm.h>
#include <nvhe/trace.h>
+#include <asm/kvm_hypevents.h>
+
#include <nvhe/define_events.h>
int __tracing_enable_event(unsigned short id, bool enable)
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 04/11] KVM: arm64: nVHE: Use NULL to reset the trace buffer backing pointer
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 16:13 ` [PATCH v1 03/11] KVM: arm64: nVHE: Declare the hyp event IDs before defining them Fuad Tabba
@ 2026-07-20 16:13 ` Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 05/11] KVM: arm64: nVHE: Run the source checker under C=2 Fuad Tabba
` (5 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
bpages_backing_start is a pointer; resetting it to plain 0 triggers a
sparse warning.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/trace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index e7e150ab265ff..177fe3d8fbb13 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -93,7 +93,7 @@ static void hyp_trace_buffer_unload_bpage_backing(struct hyp_trace_buffer *trace
__release_host_mem(start, size);
- trace_buffer->bpages_backing_start = 0;
+ trace_buffer->bpages_backing_start = NULL;
trace_buffer->bpages_backing_size = 0;
}
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 05/11] KVM: arm64: nVHE: Run the source checker under C=2
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (2 preceding siblings ...)
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 ` Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 06/11] arm64: pi: Run the source checker on the libfdt objects " Fuad Tabba
` (4 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
The custom %.nvhe.o rule reuses rule_cc_o_c, which hooks the source
checker only for C=1, and that only when the object is rebuilt. The
C=2 hook, cmd_force_checksrc, hangs off the standard %.o rule that
nVHE objects do not use, so "make C=2" silently skips every nVHE
source file. Call cmd_force_checksrc after the compile rule, as the
standard rule does.
Fixes: 7621712918ad4 ("KVM: arm64: Add build rules for separate VHE/nVHE object files")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/nvhe/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile
index f57450ebcb498..ccc1fe8394094 100644
--- a/arch/arm64/kvm/hyp/nvhe/Makefile
+++ b/arch/arm64/kvm/hyp/nvhe/Makefile
@@ -49,6 +49,7 @@ targets += $(hyp-obj) kvm_nvhe.tmp.o kvm_nvhe.rel.o hyp.lds hyp-reloc.S hyp-relo
# avoids file name clashes for files shared with VHE.
$(obj)/%.nvhe.o: $(src)/%.c FORCE
$(call if_changed_rule,cc_o_c)
+ $(call cmd,force_checksrc)
$(obj)/%.nvhe.o: $(src)/%.S FORCE
$(call if_changed_rule,as_o_S)
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 06/11] arm64: pi: Run the source checker on the libfdt objects under C=2
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (3 preceding siblings ...)
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 ` Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 07/11] KVM: arm64: nVHE: Pass host VA arguments as pointers Fuad Tabba
` (3 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
The custom lib-%.o rule reuses rule_cc_o_c, which hooks the source
checker only for C=1, and that only when the object is rebuilt. The
C=2 hook, cmd_force_checksrc, hangs off the standard %.o rule, so
"make C=2" silently skips the two libfdt objects. Call
cmd_force_checksrc after the compile rule, as the standard rule does.
Fixes: aacd149b6238 ("arm64: head: avoid relocating the kernel twice for KASLR")
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kernel/pi/Makefile | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kernel/pi/Makefile b/arch/arm64/kernel/pi/Makefile
index be92d73c25b21..96243c291e39d 100644
--- a/arch/arm64/kernel/pi/Makefile
+++ b/arch/arm64/kernel/pi/Makefile
@@ -34,6 +34,7 @@ $(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
$(call if_changed_rule,cc_o_c)
+ $(call cmd,force_checksrc)
obj-y := idreg-override.pi.o \
map_kernel.pi.o map_range.pi.o \
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 07/11] KVM: arm64: nVHE: Pass host VA arguments as pointers
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (4 preceding siblings ...)
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 ` Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 08/11] KVM: arm64: Move the host hypercall interface to its own header Fuad Tabba
` (2 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
Four hypercalls take host VAs as unsigned long: the donated vm, pgd
and vcpu regions, and the tracing descriptor. Retype the arguments
and their EL2 consumers as void *, so that the typed hypercall
declarations introduced later in the series can attach a sparse
address space to them; an address space attaches only to pointers.
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 5 ++---
arch/arm64/kvm/hyp/include/nvhe/trace.h | 4 ++--
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 8 ++++----
arch/arm64/kvm/hyp/nvhe/pkvm.c | 11 +++++------
arch/arm64/kvm/hyp/nvhe/trace.c | 4 ++--
5 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index c904647d2f760..2643a1a819668 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -69,10 +69,9 @@ void pkvm_hyp_vm_table_init(void *tbl);
int __pkvm_reserve_vm(void);
void __pkvm_unreserve_vm(pkvm_handle_t handle);
-int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
- unsigned long pgd_hva);
+int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva);
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- unsigned long vcpu_hva);
+ void *vcpu_hva);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 8813ff250f8e0..4aa36fd76b9e2 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -46,7 +46,7 @@ static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
void *tracing_reserve_entry(unsigned long length);
void tracing_commit_entry(void);
-int __tracing_load(unsigned long desc_va, size_t desc_size);
+int __tracing_load(void *desc_va, size_t desc_size);
void __tracing_unload(void);
int __tracing_enable(bool enable);
int __tracing_swap_reader(unsigned int cpu);
@@ -59,7 +59,7 @@ static inline void tracing_commit_entry(void) { }
#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \
static inline void trace_##__name(__proto) {}
-static inline int __tracing_load(unsigned long desc_va, size_t desc_size) { return -ENODEV; }
+static inline int __tracing_load(void *desc_va, size_t desc_size) { return -ENODEV; }
static inline void __tracing_unload(void) { }
static inline int __tracing_enable(bool enable) { return -ENODEV; }
static inline int __tracing_swap_reader(unsigned int cpu) { return -ENODEV; }
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index d3c69de698f48..7537d422deab3 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -577,8 +577,8 @@ static void handle___pkvm_unreserve_vm(struct kvm_cpu_context *host_ctxt)
static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
- DECLARE_REG(unsigned long, vm_hva, host_ctxt, 2);
- DECLARE_REG(unsigned long, pgd_hva, host_ctxt, 3);
+ DECLARE_REG(void *, vm_hva, host_ctxt, 2);
+ DECLARE_REG(void *, pgd_hva, host_ctxt, 3);
host_kvm = kern_hyp_va(host_kvm);
cpu_reg(host_ctxt, 1) = __pkvm_init_vm(host_kvm, vm_hva, pgd_hva);
@@ -588,7 +588,7 @@ static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
{
DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 2);
- DECLARE_REG(unsigned long, vcpu_hva, host_ctxt, 3);
+ DECLARE_REG(void *, vcpu_hva, host_ctxt, 3);
host_vcpu = kern_hyp_va(host_vcpu);
cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
@@ -634,7 +634,7 @@ static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt
static void handle___tracing_load(struct kvm_cpu_context *host_ctxt)
{
- DECLARE_REG(unsigned long, desc_hva, host_ctxt, 1);
+ DECLARE_REG(void *, desc_hva, host_ctxt, 1);
DECLARE_REG(size_t, desc_size, host_ctxt, 2);
cpu_reg(host_ctxt, 1) = __tracing_load(desc_hva, desc_size);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 24d6f164129ac..205c52535c887 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -644,9 +644,9 @@ static size_t pkvm_get_hyp_vm_size(unsigned int nr_vcpus)
size_mul(sizeof(struct pkvm_hyp_vcpu *), nr_vcpus));
}
-static void *map_donated_memory_noclear(unsigned long host_va, size_t size)
+static void *map_donated_memory_noclear(void *host_va, size_t size)
{
- void *va = (void *)kern_hyp_va(host_va);
+ void *va = kern_hyp_va(host_va);
if (!PAGE_ALIGNED(va))
return NULL;
@@ -658,7 +658,7 @@ static void *map_donated_memory_noclear(unsigned long host_va, size_t size)
return va;
}
-static void *map_donated_memory(unsigned long host_va, size_t size)
+static void *map_donated_memory(void *host_va, size_t size)
{
void *va = map_donated_memory_noclear(host_va, size);
@@ -805,8 +805,7 @@ void teardown_selftest_vm(void)
*
* Return 0 success, negative error code on failure.
*/
-int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
- unsigned long pgd_hva)
+int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva)
{
struct pkvm_hyp_vm *hyp_vm = NULL;
size_t vm_size, pgd_size;
@@ -897,7 +896,7 @@ static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
}
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- unsigned long vcpu_hva)
+ void *vcpu_hva)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
struct pkvm_hyp_vm *hyp_vm;
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index 177fe3d8fbb13..97203ddd3cf45 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -206,9 +206,9 @@ static bool hyp_trace_desc_is_valid(struct hyp_trace_desc *desc, size_t desc_siz
return true;
}
-int __tracing_load(unsigned long desc_hva, size_t desc_size)
+int __tracing_load(void *desc_hva, size_t desc_size)
{
- struct hyp_trace_desc *desc = (struct hyp_trace_desc *)kern_hyp_va(desc_hva);
+ struct hyp_trace_desc *desc = kern_hyp_va(desc_hva);
int ret;
ret = __admit_host_mem(desc, desc_size);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 08/11] KVM: arm64: Move the host hypercall interface to its own header
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (5 preceding siblings ...)
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 ` Fuad Tabba
2026-07-20 16:13 ` [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller Fuad Tabba
2026-07-20 16:24 ` [PATCH v1 10/11] KVM: arm64: nVHE: Check hypercall handlers against the declared ABI Fuad Tabba
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
Move the kvm_call_hyp() dispatch macros and pkvm_handle_t out of
kvm_host.h into a new kvm_hcall.h, giving the host<->hyp hypercall
interface a single home that subsequent patches build on to restore
type-checking across the boundary. The only adjustment to the moved
code is the checkpatch-mandated space in "while (0)".
No functional change intended.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 68 ++++++++++++++++++++++++++++++
arch/arm64/include/asm/kvm_host.h | 48 +--------------------
2 files changed, 69 insertions(+), 47 deletions(-)
create mode 100644 arch/arm64/include/asm/kvm_hcall.h
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
new file mode 100644
index 0000000000000..d925b2c28a3d8
--- /dev/null
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The host<->hyp hypercall interface.
+ *
+ * Copyright (C) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+
+#ifndef __ARM64_KVM_HCALL_H__
+#define __ARM64_KVM_HCALL_H__
+
+#include <linux/arm-smccc.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#include <asm/barrier.h>
+#include <asm/kvm_asm.h>
+#include <asm/virt.h>
+
+typedef u16 pkvm_handle_t;
+
+#ifndef __KVM_NVHE_HYPERVISOR__
+#define kvm_call_hyp_nvhe(f, ...) \
+ ({ \
+ struct arm_smccc_res res; \
+ \
+ arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
+ ##__VA_ARGS__, &res); \
+ if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \
+ res.a1 = -EOPNOTSUPP; \
+ \
+ res.a1; \
+ })
+
+/*
+ * 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.
+ */
+#define kvm_call_hyp(f, ...) \
+ do { \
+ if (has_vhe()) { \
+ f(__VA_ARGS__); \
+ isb(); \
+ } else { \
+ kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+#define kvm_call_hyp_ret(f, ...) \
+ ({ \
+ typeof(f(__VA_ARGS__)) ret; \
+ \
+ if (has_vhe()) { \
+ ret = f(__VA_ARGS__); \
+ } else { \
+ ret = kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
+ } \
+ \
+ ret; \
+ })
+#else /* __KVM_NVHE_HYPERVISOR__ */
+#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__)
+#endif /* __KVM_NVHE_HYPERVISOR__ */
+
+#endif /* __ARM64_KVM_HCALL_H__ */
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5c..81d359ac7af14 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -27,6 +27,7 @@
#include <asm/fpsimd.h>
#include <asm/kvm.h>
#include <asm/kvm_asm.h>
+#include <asm/kvm_hcall.h>
#include <asm/vncr_mapping.h>
#define __KVM_HAVE_ARCH_INTC_INITIALIZED
@@ -251,8 +252,6 @@ struct kvm_smccc_features {
unsigned long vendor_hyp_bmap_2; /* Function numbers 64-127 */
};
-typedef u16 pkvm_handle_t;
-
struct kvm_protected_vm {
pkvm_handle_t handle;
struct kvm_hyp_memcache teardown_mc;
@@ -1252,51 +1251,6 @@ void kvm_arm_resume_guest(struct kvm *kvm);
#define vcpu_has_run_once(vcpu) (!!READ_ONCE((vcpu)->pid))
-#ifndef __KVM_NVHE_HYPERVISOR__
-#define kvm_call_hyp_nvhe(f, ...) \
- ({ \
- struct arm_smccc_res res; \
- \
- arm_smccc_1_1_hvc(KVM_HOST_SMCCC_FUNC(f), \
- ##__VA_ARGS__, &res); \
- if (WARN_ON(res.a0 != SMCCC_RET_SUCCESS)) \
- res.a1 = -EOPNOTSUPP; \
- \
- res.a1; \
- })
-
-/*
- * 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.
- */
-#define kvm_call_hyp(f, ...) \
- do { \
- if (has_vhe()) { \
- f(__VA_ARGS__); \
- isb(); \
- } else { \
- kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
- } \
- } while(0)
-
-#define kvm_call_hyp_ret(f, ...) \
- ({ \
- typeof(f(__VA_ARGS__)) ret; \
- \
- if (has_vhe()) { \
- ret = f(__VA_ARGS__); \
- } else { \
- ret = kvm_call_hyp_nvhe(f, ##__VA_ARGS__); \
- } \
- \
- ret; \
- })
-#else /* __KVM_NVHE_HYPERVISOR__ */
-#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__)
-#endif /* __KVM_NVHE_HYPERVISOR__ */
-
int handle_exit(struct kvm_vcpu *vcpu, int exception_index);
void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (6 preceding siblings ...)
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
2026-07-20 16:24 ` [PATCH v1 10/11] KVM: arm64: nVHE: Check hypercall handlers against the declared ABI Fuad Tabba
8 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:13 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
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
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 10/11] KVM: arm64: nVHE: Check hypercall handlers against the declared ABI
2026-07-20 16:13 [PATCH v1 00/11] KVM: arm64: Restore type-checking across the host/hyp hypercall boundary Fuad Tabba
` (7 preceding siblings ...)
2026-07-20 16:13 ` [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller Fuad Tabba
@ 2026-07-20 16:24 ` Fuad Tabba
2026-07-20 16:24 ` [PATCH v1 11/11] KVM: arm64: Tag host-VA hypercall parameters __hostva Fuad Tabba
8 siblings, 1 reply; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:24 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
Each hypercall handler unmarshals its arguments from the host context
with hand-written DECLARE_REG() casts that nothing ties to what the
caller passed: a handler can disagree with its caller in argument type,
count or register index without a diagnostic.
Generate the unmarshalling instead. DEFINE_KVM_HOST_HCALL() expands to
the handle_<name>() glue, modelled on the syscall wrappers, and checks
the handler's parameter list against the signature declared in
kvm_hcall.h, so both ends of every hypercall are now compiled against
the same declaration. Handler bodies keep their logic and lose the
DECLARE_REG() and return-register boilerplate. The compiled handlers
are instruction-for-instruction identical, apart from flush_hyp_vcpu()
and sync_hyp_vcpu() now being inlined into their only caller.
Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 19 +-
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 420 ++++++++++++++---------------
2 files changed, 213 insertions(+), 226 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index 51bfd14748464..6aa2df90a14a4 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -41,6 +41,8 @@ struct vgic_v5_cpu_if;
#define __KVM_HCALL_MAP(n, ...) __KVM_HCALL_MAP##n(__VA_ARGS__)
#define __KVM_HCALL_DECL(t, a) t a
+#define __KVM_HCALL_LONG(t, a) unsigned long a
+#define __KVM_HCALL_CAST(t, a) (__force t) a
#define __KVM_HCALL_ARGS(t, a) a
#ifndef __KVM_NVHE_HYPERVISOR__
@@ -124,10 +126,19 @@ struct vgic_v5_cpu_if;
#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)
+/*
+ * At EL2 each declaration emits the canonical signature of the hypercall,
+ * which DEFINE_KVM_HOST_HCALL() in hyp-main.c checks the handler
+ * definition against.
+ */
+#define DECLARE_KVM_HOST_HCALL(ret, name, x, ...) \
+ typedef ret kvm_host_hcall_sig_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__));
+#define DECLARE_KVM_HOST_HCALL_VOID(name, x, ...) \
+ typedef void kvm_host_hcall_sig_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__));
+#define DECLARE_KVM_HOST_HCALL0(ret, name) \
+ typedef ret kvm_host_hcall_sig_##name(void);
+#define DECLARE_KVM_HOST_HCALL0_VOID(name) \
+ typedef void kvm_host_hcall_sig_##name(void);
#endif /* __KVM_NVHE_HYPERVISOR__ */
/* Hypercalls that are unavailable once pKVM has finalised. */
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 7537d422deab3..f2501249f4391 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -24,6 +24,62 @@
DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params);
+/*
+ * Define a hypercall handler: handle_<name> unmarshals the arguments from
+ * the host context and hands them, correctly typed, to the body that
+ * follows the macro. The parameter list is type-checked against the
+ * signature declared in <asm/kvm_hcall.h>, so the handler cannot drift
+ * from what the typed caller stubs marshal in. Modelled on the syscall
+ * wrappers.
+ */
+#define KVM_HOST_HCALL_REGS(x) \
+ __KVM_HCALL_MAP(x, __KVM_HCALL_ARGS \
+ ,, cpu_reg(host_ctxt, 1),, cpu_reg(host_ctxt, 2) \
+ ,, cpu_reg(host_ctxt, 3),, cpu_reg(host_ctxt, 4) \
+ ,, cpu_reg(host_ctxt, 5),, cpu_reg(host_ctxt, 6))
+
+#define DEFINE_KVM_HOST_HCALL(ret, name, x, ...) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static __always_inline \
+ ret __se_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_LONG, __VA_ARGS__)) \
+ { \
+ return __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_CAST, __VA_ARGS__)); \
+ } \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ cpu_reg(host_ctxt, 1) = __se_##name(KVM_HOST_HCALL_REGS(x)); \
+ } \
+ static ret __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__))
+
+#define DEFINE_KVM_HOST_HCALL_VOID(name, x, ...) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static __always_inline \
+ void __se_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_LONG, __VA_ARGS__)) \
+ { \
+ __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_CAST, __VA_ARGS__)); \
+ } \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ __se_##name(KVM_HOST_HCALL_REGS(x)); \
+ } \
+ static void __do_##name(__KVM_HCALL_MAP(x, __KVM_HCALL_DECL, __VA_ARGS__))
+
+#define DEFINE_KVM_HOST_HCALL0(ret, name) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ cpu_reg(host_ctxt, 1) = __do_##name(); \
+ } \
+ static ret __do_##name(void)
+
+#define DEFINE_KVM_HOST_HCALL0_VOID(name) \
+ static kvm_host_hcall_sig_##name __do_##name; \
+ static void handle_##name(struct kvm_cpu_context *host_ctxt) \
+ { \
+ __do_##name(); \
+ } \
+ static void __do_##name(void)
+
/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */
unsigned int hyp_gicv3_nr_lr;
@@ -185,11 +241,9 @@ static void sync_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
host_cpu_if->vgic_lr[i] = hyp_cpu_if->vgic_lr[i];
}
-static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_vcpu_load, 3,
+ pkvm_handle_t, handle, unsigned int, vcpu_idx, u64, hcr_el2)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(unsigned int, vcpu_idx, host_ctxt, 2);
- DECLARE_REG(u64, hcr_el2, host_ctxt, 3);
struct pkvm_hyp_vcpu *hyp_vcpu;
hyp_vcpu = pkvm_load_hyp_vcpu(handle, vcpu_idx);
@@ -206,7 +260,7 @@ static void handle___pkvm_vcpu_load(struct kvm_cpu_context *host_ctxt)
}
}
-static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
{
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
@@ -214,9 +268,9 @@ static void handle___pkvm_vcpu_put(struct kvm_cpu_context *host_ctxt)
pkvm_put_hyp_vcpu(hyp_vcpu);
}
-static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
+ struct kvm_vcpu *, host_vcpu)
{
- DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 1);
int ret;
if (unlikely(is_protected_kvm_enabled())) {
@@ -228,15 +282,11 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
* loading a vcpu. Therefore, if SME features enabled the host
* is misbehaving.
*/
- if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR))) {
- ret = -EINVAL;
- goto out;
- }
+ if (unlikely(system_supports_sme() && read_sysreg_s(SYS_SVCR)))
+ return -EINVAL;
- if (!hyp_vcpu) {
- ret = -EINVAL;
- goto out;
- }
+ if (!hyp_vcpu)
+ return -EINVAL;
flush_hyp_vcpu(hyp_vcpu);
@@ -251,8 +301,8 @@ static void handle___kvm_vcpu_run(struct kvm_cpu_context *host_ctxt)
ret = __kvm_vcpu_run(vcpu);
fpsimd_lazy_switch_to_host(vcpu);
}
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu)
@@ -264,184 +314,150 @@ static int pkvm_refill_memcache(struct pkvm_hyp_vcpu *hyp_vcpu)
&host_vcpu->arch.pkvm_memcache);
}
-static void handle___pkvm_host_donate_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_donate_guest, 2,
+ u64, pfn, u64, gfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
+ int ret;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || !pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
ret = pkvm_refill_memcache(hyp_vcpu);
if (ret)
- goto out;
+ return ret;
- ret = __pkvm_host_donate_guest(pfn, gfn, hyp_vcpu);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_donate_guest(pfn, gfn, hyp_vcpu);
}
-static void handle___pkvm_host_share_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_share_guest, 4,
+ u64, pfn, u64, gfn, u64, nr_pages, u64, prot)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 4);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
+ int ret;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
ret = pkvm_refill_memcache(hyp_vcpu);
if (ret)
- goto out;
+ return ret;
- ret = __pkvm_host_share_guest(pfn, gfn, nr_pages, hyp_vcpu, prot);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_share_guest(pfn, gfn, nr_pages, hyp_vcpu, prot);
}
-static void handle___pkvm_host_unshare_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_unshare_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_unshare_guest(gfn, nr_pages, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_relax_perms_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_relax_perms_guest, 2,
+ u64, gfn, u64, prot)
{
- DECLARE_REG(u64, gfn, host_ctxt, 1);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 2);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
- ret = __pkvm_host_relax_perms_guest(gfn, hyp_vcpu, prot);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_relax_perms_guest(gfn, hyp_vcpu, prot);
}
-static void handle___pkvm_host_wrprotect_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_wrprotect_guest, 3,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_wrprotect_guest(gfn, nr_pages, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_test_clear_young_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_test_clear_young_guest, 4,
+ pkvm_handle_t, handle, u64, gfn, u64, nr_pages, bool, mkold)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
- DECLARE_REG(u64, nr_pages, host_ctxt, 3);
- DECLARE_REG(bool, mkold, host_ctxt, 4);
struct pkvm_hyp_vm *hyp_vm;
- int ret = -EINVAL;
+ int ret;
hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
- goto out;
+ return -EINVAL;
ret = __pkvm_host_test_clear_young_guest(gfn, nr_pages, mkold, hyp_vm);
put_pkvm_hyp_vm(hyp_vm);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+
+ return ret;
}
-static void handle___pkvm_host_mkyoung_guest(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
+ u64, gfn)
{
- DECLARE_REG(u64, gfn, host_ctxt, 1);
struct pkvm_hyp_vcpu *hyp_vcpu;
- int ret = -EINVAL;
hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
if (!hyp_vcpu || pkvm_hyp_vcpu_is_protected(hyp_vcpu))
- goto out;
+ return -EINVAL;
- ret = __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
-out:
- cpu_reg(host_ctxt, 1) = ret;
+ return __pkvm_host_mkyoung_guest(gfn, hyp_vcpu);
}
-static void handle___kvm_adjust_pc(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
+ struct kvm_vcpu *, vcpu)
{
- DECLARE_REG(struct kvm_vcpu *, vcpu, host_ctxt, 1);
-
__kvm_adjust_pc(kern_hyp_va(vcpu));
}
-static void handle___kvm_flush_vm_context(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
{
__kvm_flush_vm_context();
}
-static void handle___kvm_tlb_flush_vmid_ipa(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, ipa, host_ctxt, 2);
- DECLARE_REG(int, level, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_ipa(kern_hyp_va(mmu), ipa, level);
}
-static void handle___kvm_tlb_flush_vmid_ipa_nsh(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, ipa, host_ctxt, 2);
- DECLARE_REG(int, level, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va(mmu), ipa, level);
}
-static void
-handle___kvm_tlb_flush_vmid_range(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
+ struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
- DECLARE_REG(phys_addr_t, start, host_ctxt, 2);
- DECLARE_REG(unsigned long, pages, host_ctxt, 3);
-
__kvm_tlb_flush_vmid_range(kern_hyp_va(mmu), start, pages);
}
-static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
+ struct kvm_s2_mmu *, mmu)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
-
__kvm_tlb_flush_vmid(kern_hyp_va(mmu));
}
-static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
struct pkvm_hyp_vm *hyp_vm = get_np_pkvm_hyp_vm(handle);
if (!hyp_vm)
@@ -451,19 +467,19 @@ static void handle___pkvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt)
put_pkvm_hyp_vm(hyp_vm);
}
-static void handle___kvm_flush_cpu_context(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
+ struct kvm_s2_mmu *, mmu)
{
- DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1);
-
__kvm_flush_cpu_context(kern_hyp_va(mmu));
}
-static void handle___kvm_timer_set_cntvoff(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
+ u64, cntvoff)
{
- __kvm_timer_set_cntvoff(cpu_reg(host_ctxt, 1));
+ __kvm_timer_set_cntvoff(cntvoff);
}
-static void handle___kvm_enable_ssbs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__kvm_enable_ssbs)
{
u64 tmp;
@@ -472,72 +488,61 @@ static void handle___kvm_enable_ssbs(struct kvm_cpu_context *host_ctxt)
write_sysreg_el2(tmp, SYS_SCTLR);
}
-static void handle___vgic_v3_get_gic_config(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(u64, __vgic_v3_get_gic_config)
{
- cpu_reg(host_ctxt, 1) = __vgic_v3_get_gic_config();
+ return __vgic_v3_get_gic_config();
}
-static void handle___vgic_v3_init_lrs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
{
__vgic_v3_init_lrs();
}
-static void handle___vgic_v3_save_aprs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v3_save_aprs(kern_hyp_va(cpu_if));
}
-static void handle___vgic_v3_restore_vmcr_aprs(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
+ struct vgic_v3_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if));
}
-static void handle___pkvm_init(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init, 4,
+ phys_addr_t, phys, unsigned long, size,
+ unsigned long *, per_cpu_base, u32, hyp_va_bits)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
- DECLARE_REG(unsigned long, size, host_ctxt, 2);
- DECLARE_REG(unsigned long *, per_cpu_base, host_ctxt, 3);
- DECLARE_REG(u32, hyp_va_bits, host_ctxt, 4);
-
/*
* __pkvm_init() will return only if an error occurred, otherwise it
* will tail-call in __pkvm_init_finalise() which will have to deal
* with the host context directly.
*/
- cpu_reg(host_ctxt, 1) = __pkvm_init(phys, size, per_cpu_base, hyp_va_bits);
+ return __pkvm_init(phys, size, per_cpu_base, hyp_va_bits);
}
-static void handle___pkvm_cpu_set_vector(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_cpu_set_vector, 1,
+ enum arm64_hyp_spectre_vector, slot)
{
- DECLARE_REG(enum arm64_hyp_spectre_vector, slot, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = pkvm_cpu_set_vector(slot);
+ return pkvm_cpu_set_vector(slot);
}
-static void handle___pkvm_host_share_hyp(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
+ u64, pfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_share_hyp(pfn);
+ return __pkvm_host_share_hyp(pfn);
}
-static void handle___pkvm_host_unshare_hyp(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_host_unshare_hyp, 1,
+ u64, pfn)
{
- DECLARE_REG(u64, pfn, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_unshare_hyp(pfn);
+ return __pkvm_host_unshare_hyp(pfn);
}
-static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(unsigned long, __pkvm_create_private_mapping, 3,
+ phys_addr_t, phys, size_t, size, u64, prot)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
- DECLARE_REG(size_t, size, host_ctxt, 2);
- DECLARE_REG(enum kvm_pgtable_prot, prot, host_ctxt, 3);
-
/*
* __pkvm_create_private_mapping() populates a pointer with the
* hypervisor start address of the allocation.
@@ -554,154 +559,125 @@ static void handle___pkvm_create_private_mapping(struct kvm_cpu_context *host_ct
if (err)
haddr = (unsigned long)ERR_PTR(err);
- cpu_reg(host_ctxt, 1) = haddr;
+ return haddr;
}
-static void handle___pkvm_prot_finalize(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_prot_finalize)
{
- cpu_reg(host_ctxt, 1) = __pkvm_prot_finalize();
+ return __pkvm_prot_finalize();
}
-static void handle___pkvm_reserve_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_reserve_vm)
{
- cpu_reg(host_ctxt, 1) = __pkvm_reserve_vm();
+ return __pkvm_reserve_vm();
}
-static void handle___pkvm_unreserve_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
__pkvm_unreserve_vm(handle);
}
-static void handle___pkvm_init_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
+ struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
{
- DECLARE_REG(struct kvm *, host_kvm, host_ctxt, 1);
- DECLARE_REG(void *, vm_hva, host_ctxt, 2);
- DECLARE_REG(void *, pgd_hva, host_ctxt, 3);
-
- host_kvm = kern_hyp_va(host_kvm);
- cpu_reg(host_ctxt, 1) = __pkvm_init_vm(host_kvm, vm_hva, pgd_hva);
+ return __pkvm_init_vm(kern_hyp_va(host_kvm), vm_hva, pgd_hva);
}
-static void handle___pkvm_init_vcpu(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
+ pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
+ void *, vcpu_hva)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(struct kvm_vcpu *, host_vcpu, host_ctxt, 2);
- DECLARE_REG(void *, vcpu_hva, host_ctxt, 3);
-
- host_vcpu = kern_hyp_va(host_vcpu);
- cpu_reg(host_ctxt, 1) = __pkvm_init_vcpu(handle, host_vcpu, vcpu_hva);
+ return __pkvm_init_vcpu(handle, kern_hyp_va(host_vcpu), vcpu_hva);
}
-static void handle___pkvm_vcpu_in_poison_fault(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
{
- int ret;
struct pkvm_hyp_vcpu *hyp_vcpu = pkvm_get_loaded_hyp_vcpu();
- ret = hyp_vcpu ? __pkvm_vcpu_in_poison_fault(hyp_vcpu) : -EINVAL;
- cpu_reg(host_ctxt, 1) = ret;
+ return hyp_vcpu ? __pkvm_vcpu_in_poison_fault(hyp_vcpu) : -EINVAL;
}
-static void handle___pkvm_force_reclaim_guest_page(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_force_reclaim_guest_page, 1,
+ phys_addr_t, phys)
{
- DECLARE_REG(phys_addr_t, phys, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_host_force_reclaim_page_guest(phys);
+ return __pkvm_host_force_reclaim_page_guest(phys);
}
-static void handle___pkvm_reclaim_dying_guest_page(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_reclaim_dying_guest_page, 2,
+ pkvm_handle_t, handle, u64, gfn)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
- DECLARE_REG(u64, gfn, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __pkvm_reclaim_dying_guest_page(handle, gfn);
+ return __pkvm_reclaim_dying_guest_page(handle, gfn);
}
-static void handle___pkvm_start_teardown_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_start_teardown_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_start_teardown_vm(handle);
+ return __pkvm_start_teardown_vm(handle);
}
-static void handle___pkvm_finalize_teardown_vm(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
+ pkvm_handle_t, handle)
{
- DECLARE_REG(pkvm_handle_t, handle, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __pkvm_finalize_teardown_vm(handle);
+ return __pkvm_finalize_teardown_vm(handle);
}
-static void handle___tracing_load(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_load, 2,
+ void *, desc_hva, size_t, desc_size)
{
- DECLARE_REG(void *, desc_hva, host_ctxt, 1);
- DECLARE_REG(size_t, desc_size, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __tracing_load(desc_hva, desc_size);
+ return __tracing_load(desc_hva, desc_size);
}
-static void handle___tracing_unload(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL0_VOID(__tracing_unload)
{
__tracing_unload();
}
-static void handle___tracing_enable(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_enable, 1,
+ bool, enable)
{
- DECLARE_REG(bool, enable, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_enable(enable);
+ return __tracing_enable(enable);
}
-static void handle___tracing_swap_reader(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_swap_reader, 1,
+ unsigned int, cpu)
{
- DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_swap_reader(cpu);
+ return __tracing_swap_reader(cpu);
}
-static void handle___tracing_update_clock(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__tracing_update_clock, 4,
+ u32, mult, u32, shift, u64, epoch_ns, u64, epoch_cyc)
{
- DECLARE_REG(u32, mult, host_ctxt, 1);
- DECLARE_REG(u32, shift, host_ctxt, 2);
- DECLARE_REG(u64, epoch_ns, host_ctxt, 3);
- DECLARE_REG(u64, epoch_cyc, host_ctxt, 4);
-
__tracing_update_clock(mult, shift, epoch_ns, epoch_cyc);
}
-static void handle___tracing_reset(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_reset, 1,
+ unsigned int, cpu)
{
- DECLARE_REG(unsigned int, cpu, host_ctxt, 1);
-
- cpu_reg(host_ctxt, 1) = __tracing_reset(cpu);
+ return __tracing_reset(cpu);
}
-static void handle___tracing_enable_event(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
+ unsigned short, id, bool, enable)
{
- DECLARE_REG(unsigned short, id, host_ctxt, 1);
- DECLARE_REG(bool, enable, host_ctxt, 2);
-
- cpu_reg(host_ctxt, 1) = __tracing_enable_event(id, enable);
+ return __tracing_enable_event(id, enable);
}
-static void handle___tracing_write_event(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
+ u64, id)
{
- DECLARE_REG(u64, id, host_ctxt, 1);
-
trace_selftest(id);
}
-static void handle___vgic_v5_save_apr(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v5_save_apr(kern_hyp_va(cpu_if));
}
-static void handle___vgic_v5_restore_vmcr_apr(struct kvm_cpu_context *host_ctxt)
+DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
+ struct vgic_v5_cpu_if *, cpu_if)
{
- DECLARE_REG(struct vgic_v5_cpu_if *, cpu_if, host_ctxt, 1);
-
__vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
}
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v1 11/11] KVM: arm64: Tag host-VA hypercall parameters __hostva
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 ` Fuad Tabba
0 siblings, 0 replies; 12+ messages in thread
From: Fuad Tabba @ 2026-07-20 16:24 UTC (permalink / raw)
To: maz, oupton, linux-arm-kernel, kvmarm
Cc: catalin.marinas, will, rostedt, mhiramat, alexandru.elisei,
vdonnefort, joey.gouly, seiden, suzuki.poulose, yuzenghui,
qperret, ardb, linux-kernel, linux-trace-kernel, tabba,
fuad.tabba
The nVHE hypervisor takes host virtual addresses as hypercall arguments
and translates each with kern_hyp_va() before use. Nothing marks them as
host-owned, so dereferencing one untranslated at EL2 - a recurring bug
class - is invisible to the compiler.
Add a __hostva sparse address space, active only for EL2 code, and tag
the host-VA parameters in the hypercall declarations. kern_hyp_va_host()
is the only sanctioned unwrap: it translates the address, preserves the
pointee type (stripped of qualifiers, as with the percpu accessors) and
drops the tag with a __force cast, so an untranslated host VA fails
sparse. The tag flows from the shared declaration into the generated
handler and on into the donated-memory and tracing-descriptor helpers,
so a handler cannot extract a host VA without it. Host code sees plain
pointers, and the tag is checker-only: no code is generated.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arch/arm64/include/asm/kvm_hcall.h | 43 ++++++++++++-------
arch/arm64/include/asm/kvm_mmu.h | 10 +++++
arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 6 ++-
arch/arm64/kvm/hyp/include/nvhe/trace.h | 5 ++-
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 57 +++++++++++++------------
arch/arm64/kvm/hyp/nvhe/pkvm.c | 11 ++---
arch/arm64/kvm/hyp/nvhe/trace.c | 4 +-
7 files changed, 82 insertions(+), 54 deletions(-)
diff --git a/arch/arm64/include/asm/kvm_hcall.h b/arch/arm64/include/asm/kvm_hcall.h
index 6aa2df90a14a4..3033a6ba1462a 100644
--- a/arch/arm64/include/asm/kvm_hcall.h
+++ b/arch/arm64/include/asm/kvm_hcall.h
@@ -27,6 +27,18 @@ struct kvm_vcpu;
struct vgic_v3_cpu_if;
struct vgic_v5_cpu_if;
+/*
+ * A host VA carried by a hypercall argument. At EL2 such a pointer must not
+ * be dereferenced until it is translated with kern_hyp_va_host(); sparse
+ * flags any use that skips the translation. The tag describes the EL2 view
+ * only: the host dereferences its own VAs freely.
+ */
+#if defined(__KVM_NVHE_HYPERVISOR__) && defined(__CHECKER__)
+#define __hostva __attribute__((noderef, address_space(__hostva)))
+#else
+#define __hostva
+#endif
+
/*
* Hypercall signatures are declared as (type, name) argument pairs.
* __KVM_HCALL_MAP() applies a macro to each pair, in the mold of __MAP()
@@ -157,24 +169,24 @@ 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)
+ struct kvm_vcpu __hostva *, vcpu)
DECLARE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
- struct kvm_vcpu *, vcpu)
+ struct kvm_vcpu __hostva *, 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)
+ struct kvm_s2_mmu __hostva *, 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)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, start, unsigned long, pages)
DECLARE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, 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)
+ void __hostva *, desc_hva, size_t, desc_size)
DECLARE_KVM_HOST_HCALL0_VOID(__tracing_unload)
DECLARE_KVM_HOST_HCALL(int, __tracing_enable, 1,
bool, enable)
@@ -189,13 +201,13 @@ DECLARE_KVM_HOST_HCALL(int, __tracing_enable_event, 2,
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)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
DECLARE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
/* Hypercalls that are available only when pKVM has finalised. */
DECLARE_KVM_HOST_HCALL(int, __pkvm_host_share_hyp, 1,
@@ -220,10 +232,11 @@ 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)
+ struct kvm __hostva *, host_kvm, void __hostva *, vm_hva,
+ void __hostva *, pgd_hva)
DECLARE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
- pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
- void *, vcpu_hva)
+ pkvm_handle_t, handle, struct kvm_vcpu __hostva *, host_vcpu,
+ void __hostva *, 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)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 6eae7e7e2a684..55fac40377c03 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -7,6 +7,8 @@
#ifndef __ARM64_KVM_MMU_H__
#define __ARM64_KVM_MMU_H__
+#include <linux/compiler.h>
+
#include <asm/page.h>
#include <asm/memory.h>
#include <asm/mmu.h>
@@ -140,6 +142,14 @@ static __always_inline unsigned long __kern_hyp_va(unsigned long v)
#define kern_hyp_va(v) ((typeof(v))(__kern_hyp_va((unsigned long)(v))))
+/*
+ * Translate a __hostva-tagged host VA, dropping the tag: the only sanctioned
+ * unwrap. Translation only, no ownership or bounds validation; the result
+ * carries the pointee type stripped of the tag and of any cv-qualifiers.
+ */
+#define kern_hyp_va_host(v) \
+ ((TYPEOF_UNQUAL(*(v)) *)__kern_hyp_va((unsigned long)(__force void *)(v)))
+
extern u32 __hyp_va_bits;
/*
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index 2643a1a819668..dcbe8b8913270 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -7,6 +7,7 @@
#ifndef __ARM64_KVM_NVHE_PKVM_H__
#define __ARM64_KVM_NVHE_PKVM_H__
+#include <asm/kvm_hcall.h>
#include <asm/kvm_pkvm.h>
#include <nvhe/gfp.h>
@@ -69,9 +70,10 @@ void pkvm_hyp_vm_table_init(void *tbl);
int __pkvm_reserve_vm(void);
void __pkvm_unreserve_vm(pkvm_handle_t handle);
-int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva);
+int __pkvm_init_vm(struct kvm *host_kvm, void __hostva *vm_hva,
+ void __hostva *pgd_hva);
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- void *vcpu_hva);
+ void __hostva *vcpu_hva);
int __pkvm_reclaim_dying_guest_page(pkvm_handle_t handle, u64 gfn);
int __pkvm_start_teardown_vm(pkvm_handle_t handle);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 4aa36fd76b9e2..58e0ad3329034 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -4,6 +4,7 @@
#include <linux/trace_remote_event.h>
+#include <asm/kvm_hcall.h>
#include <asm/kvm_hyptrace.h>
static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
@@ -46,7 +47,7 @@ static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
void *tracing_reserve_entry(unsigned long length);
void tracing_commit_entry(void);
-int __tracing_load(void *desc_va, size_t desc_size);
+int __tracing_load(void __hostva *desc_va, size_t desc_size);
void __tracing_unload(void);
int __tracing_enable(bool enable);
int __tracing_swap_reader(unsigned int cpu);
@@ -59,7 +60,7 @@ static inline void tracing_commit_entry(void) { }
#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \
static inline void trace_##__name(__proto) {}
-static inline int __tracing_load(void *desc_va, size_t desc_size) { return -ENODEV; }
+static inline int __tracing_load(void __hostva *desc_va, size_t desc_size) { return -ENODEV; }
static inline void __tracing_unload(void) { }
static inline int __tracing_enable(bool enable) { return -ENODEV; }
static inline int __tracing_swap_reader(unsigned int cpu) { return -ENODEV; }
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index f2501249f4391..679b6df9c998d 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -269,7 +269,7 @@ DEFINE_KVM_HOST_HCALL0_VOID(__pkvm_vcpu_put)
}
DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
- struct kvm_vcpu *, host_vcpu)
+ struct kvm_vcpu __hostva *, host_vcpu)
{
int ret;
@@ -294,7 +294,7 @@ DEFINE_KVM_HOST_HCALL(int, __kvm_vcpu_run, 1,
sync_hyp_vcpu(hyp_vcpu);
} else {
- struct kvm_vcpu *vcpu = kern_hyp_va(host_vcpu);
+ struct kvm_vcpu *vcpu = kern_hyp_va_host(host_vcpu);
/* The host is fully trusted, run its vCPU directly. */
fpsimd_lazy_switch_to_guest(vcpu);
@@ -421,9 +421,9 @@ DEFINE_KVM_HOST_HCALL(int, __pkvm_host_mkyoung_guest, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_adjust_pc, 1,
- struct kvm_vcpu *, vcpu)
+ struct kvm_vcpu __hostva *, vcpu)
{
- __kvm_adjust_pc(kern_hyp_va(vcpu));
+ __kvm_adjust_pc(kern_hyp_va_host(vcpu));
}
DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
@@ -432,27 +432,27 @@ DEFINE_KVM_HOST_HCALL0_VOID(__kvm_flush_vm_context)
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
{
- __kvm_tlb_flush_vmid_ipa(kern_hyp_va(mmu), ipa, level);
+ __kvm_tlb_flush_vmid_ipa(kern_hyp_va_host(mmu), ipa, level);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_ipa_nsh, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, ipa, int, level)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, ipa, int, level)
{
- __kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va(mmu), ipa, level);
+ __kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va_host(mmu), ipa, level);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid_range, 3,
- struct kvm_s2_mmu *, mmu, phys_addr_t, start, unsigned long, pages)
+ struct kvm_s2_mmu __hostva *, mmu, phys_addr_t, start, unsigned long, pages)
{
- __kvm_tlb_flush_vmid_range(kern_hyp_va(mmu), start, pages);
+ __kvm_tlb_flush_vmid_range(kern_hyp_va_host(mmu), start, pages);
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_tlb_flush_vmid, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
{
- __kvm_tlb_flush_vmid(kern_hyp_va(mmu));
+ __kvm_tlb_flush_vmid(kern_hyp_va_host(mmu));
}
DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
@@ -468,9 +468,9 @@ DEFINE_KVM_HOST_HCALL_VOID(__pkvm_tlb_flush_vmid, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_flush_cpu_context, 1,
- struct kvm_s2_mmu *, mmu)
+ struct kvm_s2_mmu __hostva *, mmu)
{
- __kvm_flush_cpu_context(kern_hyp_va(mmu));
+ __kvm_flush_cpu_context(kern_hyp_va_host(mmu));
}
DEFINE_KVM_HOST_HCALL_VOID(__kvm_timer_set_cntvoff, 1,
@@ -499,15 +499,15 @@ DEFINE_KVM_HOST_HCALL0_VOID(__vgic_v3_init_lrs)
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_save_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
{
- __vgic_v3_save_aprs(kern_hyp_va(cpu_if));
+ __vgic_v3_save_aprs(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v3_restore_vmcr_aprs, 1,
- struct vgic_v3_cpu_if *, cpu_if)
+ struct vgic_v3_cpu_if __hostva *, cpu_if)
{
- __vgic_v3_restore_vmcr_aprs(kern_hyp_va(cpu_if));
+ __vgic_v3_restore_vmcr_aprs(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init, 4,
@@ -579,16 +579,17 @@ DEFINE_KVM_HOST_HCALL_VOID(__pkvm_unreserve_vm, 1,
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vm, 3,
- struct kvm *, host_kvm, void *, vm_hva, void *, pgd_hva)
+ struct kvm __hostva *, host_kvm, void __hostva *, vm_hva,
+ void __hostva *, pgd_hva)
{
- return __pkvm_init_vm(kern_hyp_va(host_kvm), vm_hva, pgd_hva);
+ return __pkvm_init_vm(kern_hyp_va_host(host_kvm), vm_hva, pgd_hva);
}
DEFINE_KVM_HOST_HCALL(int, __pkvm_init_vcpu, 3,
- pkvm_handle_t, handle, struct kvm_vcpu *, host_vcpu,
- void *, vcpu_hva)
+ pkvm_handle_t, handle, struct kvm_vcpu __hostva *, host_vcpu,
+ void __hostva *, vcpu_hva)
{
- return __pkvm_init_vcpu(handle, kern_hyp_va(host_vcpu), vcpu_hva);
+ return __pkvm_init_vcpu(handle, kern_hyp_va_host(host_vcpu), vcpu_hva);
}
DEFINE_KVM_HOST_HCALL0(int, __pkvm_vcpu_in_poison_fault)
@@ -623,7 +624,7 @@ DEFINE_KVM_HOST_HCALL(int, __pkvm_finalize_teardown_vm, 1,
}
DEFINE_KVM_HOST_HCALL(int, __tracing_load, 2,
- void *, desc_hva, size_t, desc_size)
+ void __hostva *, desc_hva, size_t, desc_size)
{
return __tracing_load(desc_hva, desc_size);
}
@@ -670,15 +671,15 @@ DEFINE_KVM_HOST_HCALL_VOID(__tracing_write_event, 1,
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_save_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
{
- __vgic_v5_save_apr(kern_hyp_va(cpu_if));
+ __vgic_v5_save_apr(kern_hyp_va_host(cpu_if));
}
DEFINE_KVM_HOST_HCALL_VOID(__vgic_v5_restore_vmcr_apr, 1,
- struct vgic_v5_cpu_if *, cpu_if)
+ struct vgic_v5_cpu_if __hostva *, cpu_if)
{
- __vgic_v5_restore_vmcr_apr(kern_hyp_va(cpu_if));
+ __vgic_v5_restore_vmcr_apr(kern_hyp_va_host(cpu_if));
}
typedef void (*hcall_t)(struct kvm_cpu_context *);
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index 205c52535c887..bfc9f07f336a1 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -644,9 +644,9 @@ static size_t pkvm_get_hyp_vm_size(unsigned int nr_vcpus)
size_mul(sizeof(struct pkvm_hyp_vcpu *), nr_vcpus));
}
-static void *map_donated_memory_noclear(void *host_va, size_t size)
+static void *map_donated_memory_noclear(void __hostva *host_va, size_t size)
{
- void *va = kern_hyp_va(host_va);
+ void *va = kern_hyp_va_host(host_va);
if (!PAGE_ALIGNED(va))
return NULL;
@@ -658,7 +658,7 @@ static void *map_donated_memory_noclear(void *host_va, size_t size)
return va;
}
-static void *map_donated_memory(void *host_va, size_t size)
+static void *map_donated_memory(void __hostva *host_va, size_t size)
{
void *va = map_donated_memory_noclear(host_va, size);
@@ -805,7 +805,8 @@ void teardown_selftest_vm(void)
*
* Return 0 success, negative error code on failure.
*/
-int __pkvm_init_vm(struct kvm *host_kvm, void *vm_hva, void *pgd_hva)
+int __pkvm_init_vm(struct kvm *host_kvm, void __hostva *vm_hva,
+ void __hostva *pgd_hva)
{
struct pkvm_hyp_vm *hyp_vm = NULL;
size_t vm_size, pgd_size;
@@ -896,7 +897,7 @@ static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
}
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
- void *vcpu_hva)
+ void __hostva *vcpu_hva)
{
struct pkvm_hyp_vcpu *hyp_vcpu;
struct pkvm_hyp_vm *hyp_vm;
diff --git a/arch/arm64/kvm/hyp/nvhe/trace.c b/arch/arm64/kvm/hyp/nvhe/trace.c
index 97203ddd3cf45..5d0fcdce9507d 100644
--- a/arch/arm64/kvm/hyp/nvhe/trace.c
+++ b/arch/arm64/kvm/hyp/nvhe/trace.c
@@ -206,9 +206,9 @@ static bool hyp_trace_desc_is_valid(struct hyp_trace_desc *desc, size_t desc_siz
return true;
}
-int __tracing_load(void *desc_hva, size_t desc_size)
+int __tracing_load(void __hostva *desc_hva, size_t desc_size)
{
- struct hyp_trace_desc *desc = kern_hyp_va(desc_hva);
+ struct hyp_trace_desc *desc = kern_hyp_va_host(desc_hva);
int ret;
ret = __admit_host_mem(desc, desc_size);
--
2.39.5
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v1 01/11] tracing: Include linux/types.h in trace_remote_event.h
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
0 siblings, 0 replies; 12+ messages in thread
From: Steven Rostedt @ 2026-07-20 18:32 UTC (permalink / raw)
To: Fuad Tabba
Cc: maz, oupton, linux-arm-kernel, kvmarm, catalin.marinas, will,
mhiramat, alexandru.elisei, vdonnefort, joey.gouly, seiden,
suzuki.poulose, yuzenghui, qperret, ardb, linux-kernel,
linux-trace-kernel, tabba
On Mon, 20 Jul 2026 17:13:33 +0100
Fuad Tabba <fuad.tabba@linux.dev> wrote:
> diff --git a/include/linux/trace_remote_event.h b/include/linux/trace_remote_event.h
> index c8ae1e1f5e721..e4cc2d4497bcf 100644
> --- a/include/linux/trace_remote_event.h
> +++ b/include/linux/trace_remote_event.h
> @@ -3,6 +3,8 @@
> #ifndef _LINUX_TRACE_REMOTE_EVENTS_H
> #define _LINUX_TRACE_REMOTE_EVENTS_H
>
> +#include <linux/types.h>
> +
> struct trace_remote;
> struct trace_event_fields;
> struct trace_seq;
I don't know if this is correct or not, but here's my ack so others can
decide and take it through their trees.
Acked-by: Steven Rostedt <rostedt@goodmis.org.
-- Steve
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-07-20 18:32 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 03/11] KVM: arm64: nVHE: Declare the hyp event IDs before defining them 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 ` [PATCH v1 09/11] KVM: arm64: Type-check hypercall arguments at the caller Fuad Tabba
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox