* [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor
@ 2026-10-01 8:49 Vincent Donnefort
2026-10-01 8:49 ` [PATCH v4 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp Vincent Donnefort
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Vincent Donnefort @ 2026-10-01 8:49 UTC (permalink / raw)
To: maz, oupton, kvmarm, linux-arm-kernel
Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas,
will, kernel-team, fuad.tabba, Vincent Donnefort
Hi all,
This series adds a hypervisor event "hyp_printk" which enables
developers to log pretty much anything into the hypervisor tracing
buffer, just like the kernel function trace_printk().
This enables rich logging from the hypervisor, while leaving all the
string parsing burden to the kernel. This has been the main way of
debugging pKVM in Android.
Even though not strictly related to trace_hyp_printk, I have added the
following two patches:
* KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp
This one mainly intends to support one of the new features I have
posted here [1], which allows to enable tracing as early as
possible. I have added it here to limit cross-posting.
* KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/
This one is just a cleanup.
[1] https://lore.kernel.org/all/20260605163825.1762953-1-vdonnefort@google.com/
Changelog:
v4:
- Store nr_args in struct hyp_string_fmt instead of using
ring_buffer_event_length() (Sashiko)
v3 (https://lore.kernel.org/all/20260930153505.2144559-1-vdonnefort@google.com/):
- Collect Steven's tag
- Rebase on 7.3-rc3
v2 (https://lore.kernel.org/all/20260708075435.47419-1-vdonnefort@google.com/):
- Collect Fuad's tags
- Rebase on 7.2-rc2
- Warn when trace_hyp_printk callsites are exhausted (Fuad)
v1 (https://lore.kernel.org/all/20260612142245.1015744-1-vdonnefort@google.com/)
Vincent Donnefort (4):
KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp
KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/
tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper
KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp
arch/arm64/include/asm/kvm_asm.h | 4 +-
arch/arm64/include/asm/kvm_hypevents.h | 14 ++++
arch/arm64/include/asm/kvm_hyptrace.h | 9 +++
arch/arm64/kernel/image-vars.h | 1 +
arch/arm64/kernel/vmlinux.lds.S | 4 ++
.../define_hypevents.h} | 0
.../kvm/hyp/include/nvhe/define_events.h | 2 -
arch/arm64/kvm/hyp/include/nvhe/trace.h | 66 +++++++++++++++++++
arch/arm64/kvm/hyp/nvhe/events.c | 6 ++
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 2 +-
arch/arm64/kvm/hyp_trace.c | 60 ++++++++++++++++-
include/trace/define_remote_events.h | 19 +++++-
12 files changed, 178 insertions(+), 9 deletions(-)
rename arch/arm64/{include/asm/kvm_define_hypevents.h => kvm/define_hypevents.h} (100%)
base-commit: fd73f4a6659897191fa0d40695fe370925dd3780
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v4 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp 2026-10-01 8:49 [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort @ 2026-10-01 8:49 ` Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 2/4] KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/ Vincent Donnefort ` (2 subsequent siblings) 3 siblings, 0 replies; 7+ messages in thread From: Vincent Donnefort @ 2026-10-01 8:49 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team, fuad.tabba, Vincent Donnefort, Fuad Tabba The hypervisor tracing for pKVM relies on the __pkvm_host_share_hyp and __pkvm_host_unshare_hyp HVCs. In order to start tracing as early as possible, allow those two HVCs before the host is deprivileged. Signed-off-by: Vincent Donnefort <vdonnefort@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> --- arch/arm64/include/asm/kvm_asm.h | 4 ++-- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index e5b92ac09e69..da8b6e3c01e3 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -89,12 +89,12 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs, __KVM_HOST_SMCCC_FUNC___vgic_v5_save_apr, __KVM_HOST_SMCCC_FUNC___vgic_v5_restore_vmcr_apr, + __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, + __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, MARKER(__KVM_HOST_SMCCC_FUNC_PKVM_ONLY), /* Hypercalls that are available only when pKVM has finalised. */ - __KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp, - __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp, __KVM_HOST_SMCCC_FUNC___pkvm_host_donate_guest, __KVM_HOST_SMCCC_FUNC___pkvm_host_share_guest, __KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_guest, diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 9a3b92e626ad..4f824b01465a 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -934,9 +934,9 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__vgic_v3_restore_vmcr_aprs), HANDLE_FUNC(__vgic_v5_save_apr), HANDLE_FUNC(__vgic_v5_restore_vmcr_apr), - HANDLE_FUNC(__pkvm_host_share_hyp), HANDLE_FUNC(__pkvm_host_unshare_hyp), + HANDLE_FUNC(__pkvm_host_donate_guest), HANDLE_FUNC(__pkvm_host_share_guest), HANDLE_FUNC(__pkvm_host_unshare_guest), -- 2.56.0.rc1.315.gc6ed9934b7-goog ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 2/4] KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/ 2026-10-01 8:49 [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp Vincent Donnefort @ 2026-10-01 8:49 ` Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 3/4] tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Vincent Donnefort 3 siblings, 0 replies; 7+ messages in thread From: Vincent Donnefort @ 2026-10-01 8:49 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team, fuad.tabba, Vincent Donnefort, Fuad Tabba kvm_define_hypevents.h is used to define the kernel-side structures for hypervisor events. It doesn't need to be used anywhere else than in hyp_trace.c. Rename and move it to arch/arm64/kvm/ Signed-off-by: Vincent Donnefort <vdonnefort@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> --- .../asm/kvm_define_hypevents.h => kvm/define_hypevents.h} | 0 arch/arm64/kvm/hyp_trace.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename arch/arm64/{include/asm/kvm_define_hypevents.h => kvm/define_hypevents.h} (100%) diff --git a/arch/arm64/include/asm/kvm_define_hypevents.h b/arch/arm64/kvm/define_hypevents.h similarity index 100% rename from arch/arm64/include/asm/kvm_define_hypevents.h rename to arch/arm64/kvm/define_hypevents.h diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 9644c424819b..8faaec899f92 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -402,7 +402,7 @@ static struct trace_remote_callbacks trace_remote_callbacks = { static const char *__hyp_enter_exit_reason_str(u8 reason); -#include <asm/kvm_define_hypevents.h> +#include "define_hypevents.h" static const char *__hyp_enter_exit_reason_str(u8 reason) { -- 2.56.0.rc1.315.gc6ed9934b7-goog ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 3/4] tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper 2026-10-01 8:49 [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 2/4] KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/ Vincent Donnefort @ 2026-10-01 8:49 ` Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Vincent Donnefort 3 siblings, 0 replies; 7+ messages in thread From: Vincent Donnefort @ 2026-10-01 8:49 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team, fuad.tabba, Vincent Donnefort, Fuad Tabba, Steven Rostedt The current REMOTE_EVENT() takes as a __printk argument a string format and a list of arguments, such as RE_PRINTK("foo=%d bar=%d", foo, bar). Add a REMOTE_EVENT_CUSTOM_PRINTK() where the __printk argument can be a function. This intends to support the creation of a "printk" event for the arm64 nVHE/pKVM hypervisor with a dynamic prototype and by extension a dynamic print format. Signed-off-by: Vincent Donnefort <vdonnefort@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Acked-by: Steven Rostedt <rostedt@goodmis.org> --- include/trace/define_remote_events.h | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/include/trace/define_remote_events.h b/include/trace/define_remote_events.h index 676e803dc144..4f4d58e37b84 100644 --- a/include/trace/define_remote_events.h +++ b/include/trace/define_remote_events.h @@ -35,17 +35,26 @@ do { \ #define RE_PRINTK(__args...) __args -#define REMOTE_EVENT(__name, __id, __struct, __printk) \ - REMOTE_EVENT_FORMAT(__name, __struct); \ +#define REMOTE_EVENT_PRINT_FUNC(__name, __printk) \ static void remote_event_print_##__name(void *evt, struct trace_seq *seq) \ { \ struct remote_event_format_##__name __maybe_unused *__entry = evt; \ trace_seq_puts(seq, #__name); \ - remote_printk(__printk); \ + __printk; \ } + +#define REMOTE_EVENT(__name, __id, __struct, __printk) \ + REMOTE_EVENT_FORMAT(__name, __struct); \ + REMOTE_EVENT_PRINT_FUNC(__name, remote_printk(__printk)) + +#define REMOTE_EVENT_CUSTOM_PRINTK(__name, __id, __struct, __printk) \ + REMOTE_EVENT_FORMAT(__name, __struct); \ + REMOTE_EVENT_PRINT_FUNC(__name, __printk) + #include REMOTE_EVENT_INCLUDE(REMOTE_EVENT_INCLUDE_FILE) #undef REMOTE_EVENT +#undef REMOTE_EVENT_CUSTOM_PRINTK #undef RE_PRINTK #undef re_field #define re_field(__type, __field) \ @@ -70,4 +79,8 @@ do { \ .print_fmt = remote_event_print_fmt_##__name, \ .print = remote_event_print_##__name, \ } + +#define REMOTE_EVENT_CUSTOM_PRINTK(__name, __id, __struct, __printk) \ + REMOTE_EVENT(__name, __id, RE_STRUCT(__struct), "Unknown") + #include REMOTE_EVENT_INCLUDE(REMOTE_EVENT_INCLUDE_FILE) -- 2.56.0.rc1.315.gc6ed9934b7-goog ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp 2026-10-01 8:49 [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort ` (2 preceding siblings ...) 2026-10-01 8:49 ` [PATCH v4 3/4] tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper Vincent Donnefort @ 2026-10-01 8:49 ` Vincent Donnefort 2026-10-01 9:07 ` sashiko-bot 3 siblings, 1 reply; 7+ messages in thread From: Vincent Donnefort @ 2026-10-01 8:49 UTC (permalink / raw) To: maz, oupton, kvmarm, linux-arm-kernel Cc: joey.gouly, seiden, suzuki.poulose, yuzenghui, catalin.marinas, will, kernel-team, fuad.tabba, Vincent Donnefort, Fuad Tabba Create an event to allow developers to log pretty much anything into the hypervisor tracing buffer with trace_hyp_printk(), just like the kernel tracing has the function trace_printk(). #include <asm/kvm_hypevents.h> trace_hyp_printk("foobar"); trace_hyp_printk("foo=%d", foo); trace_hyp_printk("foo=%d bar=0x%016llx", foo, bar); To ensure writing into the tracing buffer is fast, store the string format into a kernel-accessible ELF section. The hypervisor only has to write into the event the string ID, which is the delta between the hyp_string_fmt and the start of the ELF section. The string format is parsed by the kernel. It is therefore not possible to dereference hypervisor pointers and the format specifier %s is not supported. Also the %p specifiers will fail to find the hypervisor function. To not waste tracing buffer data, use a dynamic size. Each argument is 8 bytes. Signed-off-by: Vincent Donnefort <vdonnefort@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> --- arch/arm64/include/asm/kvm_hypevents.h | 14 ++++ arch/arm64/include/asm/kvm_hyptrace.h | 9 +++ arch/arm64/kernel/image-vars.h | 1 + arch/arm64/kernel/vmlinux.lds.S | 4 ++ .../kvm/hyp/include/nvhe/define_events.h | 2 - arch/arm64/kvm/hyp/include/nvhe/trace.h | 66 +++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/events.c | 6 ++ arch/arm64/kvm/hyp_trace.c | 58 ++++++++++++++++ 8 files changed, 158 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h index 5f6e6789d121..3396deb1a1f8 100644 --- a/arch/arm64/include/asm/kvm_hypevents.h +++ b/arch/arm64/include/asm/kvm_hypevents.h @@ -58,4 +58,18 @@ HYP_EVENT(selftest, ), RE_PRINTK("id=%llu", __entry->id) ); + +/* + * trace_hyp_printk() has too many specificities to be declared with HYP_EVENT(). + * However, we can use a REMOTE_EVENT macro to automatically do the declaration + * for the kernel side. + */ +REMOTE_EVENT_CUSTOM_PRINTK(hyp_printk, + 0, /* id will be overwritten during hyp event init */ + RE_STRUCT( + re_field(u16, fmt_id) + re_field(u64, args[]) + ), + __hyp_trace_printk(evt, seq) +); #endif diff --git a/arch/arm64/include/asm/kvm_hyptrace.h b/arch/arm64/include/asm/kvm_hyptrace.h index de133b735f72..0be618786799 100644 --- a/arch/arm64/include/asm/kvm_hyptrace.h +++ b/arch/arm64/include/asm/kvm_hyptrace.h @@ -23,4 +23,13 @@ extern struct remote_event __hyp_events_end[]; extern struct hyp_event_id __hyp_event_ids_start[]; extern struct hyp_event_id __hyp_event_ids_end[]; +#define HYP_STRING_FMT_MAX_SIZE 127 + +struct hyp_string_fmt { + const char fmt[HYP_STRING_FMT_MAX_SIZE]; + u8 nr_args; +}; + +extern struct hyp_string_fmt __hyp_string_fmts_start[]; +extern struct hyp_string_fmt __hyp_string_fmts_end[]; #endif diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h index 14beb7b9d304..ef9875a95f95 100644 --- a/arch/arm64/kernel/image-vars.h +++ b/arch/arm64/kernel/image-vars.h @@ -143,6 +143,7 @@ KVM_NVHE_ALIAS(__hyp_rodata_end); #ifdef CONFIG_NVHE_EL2_TRACING KVM_NVHE_ALIAS(__hyp_event_ids_start); KVM_NVHE_ALIAS(__hyp_event_ids_end); +KVM_NVHE_ALIAS(__hyp_string_fmts_start); #endif /* pKVM static key */ diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index af1d72020976..7f9b6c7dfe70 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -324,6 +324,10 @@ SECTIONS __hyp_events_start = .; *(SORT(_hyp_events.*)) __hyp_events_end = .; + + __hyp_string_fmts_start = .; + *(_hyp_string_fmts) + __hyp_string_fmts_end = .; } #endif /* diff --git a/arch/arm64/kvm/hyp/include/nvhe/define_events.h b/arch/arm64/kvm/hyp/include/nvhe/define_events.h index 776d4c6cb702..370e8c2d39fe 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/define_events.h +++ b/arch/arm64/kvm/hyp/include/nvhe/define_events.h @@ -10,5 +10,3 @@ #define HYP_EVENT_MULTI_READ #include <asm/kvm_hypevents.h> #undef HYP_EVENT_MULTI_READ - -#undef HYP_EVENT diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h index 8813ff250f8e..fa50720e2cff 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/trace.h +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h @@ -46,6 +46,70 @@ 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); +/* + * The trace_hyp_printk boilerplate is too fiddly to be declared with + * HYP_EVENT(): + * + * The string format is stored into a kernel-accessible ELF section. The + * hypervisor only writes the format ID. + * + * The function has a variadic prototype. We have no easy way to know each + * argument width so they must all cast to u64. + */ +#define REMOTE_EVENT_CUSTOM_PRINTK(...) + +#define __TO_U64_0() +#define __TO_U64_1(x) , (u64)(x) +#define __TO_U64_2(x, ...) , (u64)(x) __TO_U64_1(__VA_ARGS__) +#define __TO_U64_3(x, ...) , (u64)(x) __TO_U64_2(__VA_ARGS__) +#define __TO_U64_4(x, ...) , (u64)(x) __TO_U64_3(__VA_ARGS__) +#define __TO_U64_5(x, ...) , (u64)(x) __TO_U64_4(__VA_ARGS__) +#define __TO_U64_6(x, ...) , (u64)(x) __TO_U64_5(__VA_ARGS__) +#define __TO_U64_7(x, ...) , (u64)(x) __TO_U64_6(__VA_ARGS__) +#define __TO_U64_8(x, ...) , (u64)(x) __TO_U64_7(__VA_ARGS__) + +#define __TO_U64_X(N, ...) CONCATENATE(__TO_U64_, N)(__VA_ARGS__) +#define __TO_U64(...) __TO_U64_X(COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__) + +REMOTE_EVENT_FORMAT(hyp_printk, HE_STRUCT(he_field(u16, fmt_id) he_field(u64, args[]))); +extern struct hyp_event_id hyp_event_id_hyp_printk; + +static __always_inline void __trace_hyp_printk(struct hyp_string_fmt *fmt, int nr_args, ...) +{ + struct remote_event_format_hyp_printk *entry; + va_list va; + int i; + + if (!atomic_read(&hyp_event_id_hyp_printk.enabled)) + return; + + entry = tracing_reserve_entry(struct_size(entry, args, nr_args)); + if (!entry) + return; + + entry->hdr.id = hyp_event_id_hyp_printk.id; + entry->fmt_id = fmt - __hyp_string_fmts_start; + + va_start(va, nr_args); + for (i = 0; i < nr_args; i++) + entry->args[i] = va_arg(va, u64); + va_end(va); + + tracing_commit_entry(); +} + +#define trace_hyp_printk(__fmt, __args...) \ +do { \ + static struct hyp_string_fmt __used __section("_hyp_string_fmts") \ + __hyp_str_fmt = { \ + .fmt = __fmt, \ + .nr_args = COUNT_ARGS(__args), \ + }; \ + BUILD_BUG_ON(sizeof(__fmt) > HYP_STRING_FMT_MAX_SIZE); \ + /* __TO_U64 prepends a comma if there are arguments */ \ + __trace_hyp_printk(&__hyp_str_fmt, COUNT_ARGS(__args) __TO_U64(__args)); \ +} while (0) + int __tracing_load(unsigned long desc_va, size_t desc_size); void __tracing_unload(void); int __tracing_enable(bool enable); @@ -58,6 +122,8 @@ static inline void *tracing_reserve_entry(unsigned long length) { return NULL; } static inline void tracing_commit_entry(void) { } #define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \ static inline void trace_##__name(__proto) {} +#define REMOTE_EVENT_CUSTOM_PRINTK(...) +#define trace_hyp_printk(fmt, args...) do { } while (0) static inline int __tracing_load(unsigned long desc_va, size_t desc_size) { return -ENODEV; } static inline void __tracing_unload(void) { } diff --git a/arch/arm64/kvm/hyp/nvhe/events.c b/arch/arm64/kvm/hyp/nvhe/events.c index add9383aadb5..12223d2e3618 100644 --- a/arch/arm64/kvm/hyp/nvhe/events.c +++ b/arch/arm64/kvm/hyp/nvhe/events.c @@ -9,6 +9,12 @@ #include <nvhe/define_events.h> +/* + * The hyp_printk event is not declared with HYP_EVENT in kvm_hypevents.h, + * so we manually add the boilerplate here. + */ +HYP_EVENT(hyp_printk, 0, 0, 0, 0); + int __tracing_enable_event(unsigned short id, bool enable) { struct hyp_event_id *event_id = &__hyp_event_ids_start[id]; diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 8faaec899f92..ebb882d0152b 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -402,6 +402,9 @@ static struct trace_remote_callbacks trace_remote_callbacks = { static const char *__hyp_enter_exit_reason_str(u8 reason); +struct remote_event_format_hyp_printk; +static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq); + #include "define_hypevents.h" static const char *__hyp_enter_exit_reason_str(u8 reason) @@ -421,6 +424,58 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) return strs[min(reason, HYP_REASON_UNKNOWN)]; } +static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq) +{ + const struct hyp_string_fmt *hyp_str_fmt = &__hyp_string_fmts_start[entry->fmt_id]; + const char *fmt = hyp_str_fmt->fmt; + + trace_seq_putc(seq, ' '); + + if (hyp_str_fmt >= __hyp_string_fmts_end) { + trace_seq_printf(seq, "Unknown hyp_string_fmt ID %d\n", entry->fmt_id); + return; + } + + switch (hyp_str_fmt->nr_args) { + case 0: + trace_seq_printf(seq, fmt); + break; + case 1: + trace_seq_printf(seq, fmt, entry->args[0]); + break; + case 2: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1]); + break; + case 3: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2]); + break; + case 4: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2], + entry->args[3]); + break; + case 5: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2], + entry->args[3], entry->args[4]); + break; + case 6: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2], + entry->args[3], entry->args[4], entry->args[5]); + break; + case 7: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2], + entry->args[3], entry->args[4], entry->args[5], entry->args[6]); + break; + default: + trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], + entry->args[2], entry->args[3], entry->args[4], entry->args[5], + entry->args[6], entry->args[7]); + break; + } + + if (seq->buffer[trace_seq_used(seq) - 1] != '\n') + trace_seq_putc(seq, '\n'); +} + static void __init hyp_trace_init_events(void) { struct hyp_event_id *hyp_event_id = __hyp_event_ids_start; @@ -430,6 +485,9 @@ static void __init hyp_trace_init_events(void) /* Events on both sides hypervisor are sorted */ for (; event < __hyp_events_end; event++, hyp_event_id++, id++) event->id = hyp_event_id->id = id; + + WARN(__hyp_string_fmts_end - __hyp_string_fmts_start > U16_MAX + 1, + "Too many trace_hyp_printk() callsites\n"); } int __init kvm_hyp_trace_init(void) -- 2.56.0.rc1.315.gc6ed9934b7-goog ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp 2026-10-01 8:49 ` [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Vincent Donnefort @ 2026-10-01 9:07 ` sashiko-bot 2026-10-01 13:52 ` Vincent Donnefort 0 siblings, 1 reply; 7+ messages in thread From: sashiko-bot @ 2026-10-01 9:07 UTC (permalink / raw) To: Vincent Donnefort; +Cc: Marc Zyngier, kvmarm, Oliver Upton Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] kvm: trace_hyp_printk lacks format string validation leading to host panic/leak - [Medium] arm64: unaligned _hyp_string_fmts section start leads to pointer corruption - [Medium] kvm: out-of-bounds read in __hyp_trace_printk when trace sequence is empty - [Medium] kvm: uninitialized padding in remote_event_format_hyp_printk leaks hyp memory -- commit bc7c47888c5fbd307a50212081f15f7498660a9b Author: Vincent Donnefort <vdonnefort@google.com> KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Create an event to allow developers to log pretty much anything into the hypervisor tracing buffer with trace_hyp_printk(), just like the kernel tracing has the function trace_printk(). > diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S > index af1d720209764..7f9b6c7dfe70f 100644 > --- a/arch/arm64/kernel/vmlinux.lds.S > +++ b/arch/arm64/kernel/vmlinux.lds.S > @@ -324,6 +324,10 @@ SECTIONS > __hyp_events_start = .; > *(SORT(_hyp_events.*)) > __hyp_events_end = .; > + > + __hyp_string_fmts_start = .; > + *(_hyp_string_fmts) > + __hyp_string_fmts_end = .; > } > #endif [Severity: Medium] Could __hyp_string_fmts_start capture an unaligned address before section padding is applied? The compiler might align struct hyp_string_fmt elements, but without an ALIGN() directive, __hyp_string_fmts_start captures the location counter before any alignment padding. When calculating fmt_id in the hypervisor (fmt - __hyp_string_fmts_start), this unaligned offset might yield incorrect values and cause memory access errors. > diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h > index 8813ff250f8e0..fa50720e2cffd 100644 > --- a/arch/arm64/kvm/hyp/include/nvhe/trace.h > +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h [ ... ] > @@ -46,6 +46,70 @@ 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); > > +/* > + * The trace_hyp_printk boilerplate is too fiddly to be declared with > + * HYP_EVENT(): > + * > + * The string format is stored into a kernel-accessible ELF section. The > + * hypervisor only writes the format ID. > + * > + * The function has a variadic prototype. We have no easy way to know each > + * argument width so they must all cast to u64. > + */ > +#define REMOTE_EVENT_CUSTOM_PRINTK(...) > + > +#define __TO_U64_0() > +#define __TO_U64_1(x) , (u64)(x) > +#define __TO_U64_2(x, ...) , (u64)(x) __TO_U64_1(__VA_ARGS__) > +#define __TO_U64_3(x, ...) , (u64)(x) __TO_U64_2(__VA_ARGS__) > +#define __TO_U64_4(x, ...) , (u64)(x) __TO_U64_3(__VA_ARGS__) > +#define __TO_U64_5(x, ...) , (u64)(x) __TO_U64_4(__VA_ARGS__) > +#define __TO_U64_6(x, ...) , (u64)(x) __TO_U64_5(__VA_ARGS__) > +#define __TO_U64_7(x, ...) , (u64)(x) __TO_U64_6(__VA_ARGS__) > +#define __TO_U64_8(x, ...) , (u64)(x) __TO_U64_7(__VA_ARGS__) > + > +#define __TO_U64_X(N, ...) CONCATENATE(__TO_U64_, N)(__VA_ARGS__) > +#define __TO_U64(...) __TO_U64_X(COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__) > + > +REMOTE_EVENT_FORMAT(hyp_printk, HE_STRUCT(he_field(u16, fmt_id) he_field(u64, args[]))); [Severity: Medium] Can this leak hypervisor memory to the host or userspace through uninitialized struct padding? The REMOTE_EVENT_FORMAT definition creates a structure with u16 fmt_id followed by a u64 args[] array, which introduces padding bytes before the array to satisfy the 8-byte alignment requirement of u64. Because tracing_reserve_entry() does not zero the allocated space, these padding bytes are never initialized before tracing_commit_entry() exposes them. > +extern struct hyp_event_id hyp_event_id_hyp_printk; > + > +static __always_inline void __trace_hyp_printk(struct hyp_string_fmt *fmt, int nr_args, ...) > +{ > + struct remote_event_format_hyp_printk *entry; > + va_list va; > + int i; > + > + if (!atomic_read(&hyp_event_id_hyp_printk.enabled)) > + return; > + > + entry = tracing_reserve_entry(struct_size(entry, args, nr_args)); > + if (!entry) > + return; > + > + entry->hdr.id = hyp_event_id_hyp_printk.id; > + entry->fmt_id = fmt - __hyp_string_fmts_start; > + > + va_start(va, nr_args); > + for (i = 0; i < nr_args; i++) > + entry->args[i] = va_arg(va, u64); > + va_end(va); > + > + tracing_commit_entry(); > +} > + > +#define trace_hyp_printk(__fmt, __args...) \ > +do { \ > + static struct hyp_string_fmt __used __section("_hyp_string_fmts") \ > + __hyp_str_fmt = { \ > + .fmt = __fmt, \ > + .nr_args = COUNT_ARGS(__args), \ > + }; \ > + BUILD_BUG_ON(sizeof(__fmt) > HYP_STRING_FMT_MAX_SIZE); \ > + /* __TO_U64 prepends a comma if there are arguments */ \ > + __trace_hyp_printk(&__hyp_str_fmt, COUNT_ARGS(__args) __TO_U64(__args)); \ > +} while (0) [Severity: High] Does this macro validate that the provided format string matches the given arguments? Without checking the format string (e.g. using __printf attributes or if (0) printk(...) type-checking), a mismatch like trace_hyp_printk("val=%d") will set nr_args to 0. The host kernel's __hyp_trace_printk() will then unconditionally call trace_seq_printf(seq, fmt); with no arguments, potentially causing out-of-bounds reads or invalid dereferences when it parses the %d or %s specifiers. > diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c > index 8faaec899f92c..ebb882d0152b6 100644 > --- a/arch/arm64/kvm/hyp_trace.c > +++ b/arch/arm64/kvm/hyp_trace.c [ ... ] > @@ -421,6 +424,58 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) > return strs[min(reason, HYP_REASON_UNKNOWN)]; > } > > +static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq) > +{ [ ... ] > + switch (hyp_str_fmt->nr_args) { > + case 0: > + trace_seq_printf(seq, fmt); > + break; > + case 1: > + trace_seq_printf(seq, fmt, entry->args[0]); > + break; [ ... ] > + } > + > + if (seq->buffer[trace_seq_used(seq) - 1] != '\n') > + trace_seq_putc(seq, '\n'); [Severity: Medium] Is it possible for trace_seq_used(seq) to be 0 here? If the sequence buffer was empty or in an overflowed state when passed to this function, subtracting 1 would result in an out-of-bounds array read (accessing seq->buffer[-1]). Would adding a check for trace_seq_used(seq) > 0 prevent this? > +} -- Sashiko AI review · https://sashiko.dev/#/patchset/20261001084909.2937943-1-vdonnefort@google.com?part=4 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp 2026-10-01 9:07 ` sashiko-bot @ 2026-10-01 13:52 ` Vincent Donnefort 0 siblings, 0 replies; 7+ messages in thread From: Vincent Donnefort @ 2026-10-01 13:52 UTC (permalink / raw) To: sashiko-reviews; +Cc: Marc Zyngier, kvmarm, Oliver Upton On Thu, Oct 01, 2026 at 09:07:09AM +0000, sashiko-bot@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: > - [High] kvm: trace_hyp_printk lacks format string validation leading to host panic/leak > - [Medium] arm64: unaligned _hyp_string_fmts section start leads to pointer corruption > - [Medium] kvm: out-of-bounds read in __hyp_trace_printk when trace sequence is empty > - [Medium] kvm: uninitialized padding in remote_event_format_hyp_printk leaks hyp memory > -- > > commit bc7c47888c5fbd307a50212081f15f7498660a9b > Author: Vincent Donnefort <vdonnefort@google.com> > > KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp > > Create an event to allow developers to log pretty much anything into the > hypervisor tracing buffer with trace_hyp_printk(), just like the kernel > tracing has the function trace_printk(). > > > diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S > > index af1d720209764..7f9b6c7dfe70f 100644 > > --- a/arch/arm64/kernel/vmlinux.lds.S > > +++ b/arch/arm64/kernel/vmlinux.lds.S > > @@ -324,6 +324,10 @@ SECTIONS > > __hyp_events_start = .; > > *(SORT(_hyp_events.*)) > > __hyp_events_end = .; > > + > > + __hyp_string_fmts_start = .; > > + *(_hyp_string_fmts) > > + __hyp_string_fmts_end = .; > > } > > #endif > > [Severity: Medium] > Could __hyp_string_fmts_start capture an unaligned address before section > padding is applied? The compiler might align struct hyp_string_fmt > elements, but without an ALIGN() directive, __hyp_string_fmts_start > captures the location counter before any alignment padding. When calculating > fmt_id in the hypervisor (fmt - __hyp_string_fmts_start), this unaligned > offset might yield incorrect values and cause memory access errors. arg, that is correct, it depends on the number of events... I'll move into its own .rodata.hyp_string_fmts output section. And make the hyp_string_fmts struct const at the same time... > > > diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h > > index 8813ff250f8e0..fa50720e2cffd 100644 > > --- a/arch/arm64/kvm/hyp/include/nvhe/trace.h > > +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h > [ ... ] > > @@ -46,6 +46,70 @@ 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); > > > > +/* > > + * The trace_hyp_printk boilerplate is too fiddly to be declared with > > + * HYP_EVENT(): > > + * > > + * The string format is stored into a kernel-accessible ELF section. The > > + * hypervisor only writes the format ID. > > + * > > + * The function has a variadic prototype. We have no easy way to know each > > + * argument width so they must all cast to u64. > > + */ > > +#define REMOTE_EVENT_CUSTOM_PRINTK(...) > > + > > +#define __TO_U64_0() > > +#define __TO_U64_1(x) , (u64)(x) > > +#define __TO_U64_2(x, ...) , (u64)(x) __TO_U64_1(__VA_ARGS__) > > +#define __TO_U64_3(x, ...) , (u64)(x) __TO_U64_2(__VA_ARGS__) > > +#define __TO_U64_4(x, ...) , (u64)(x) __TO_U64_3(__VA_ARGS__) > > +#define __TO_U64_5(x, ...) , (u64)(x) __TO_U64_4(__VA_ARGS__) > > +#define __TO_U64_6(x, ...) , (u64)(x) __TO_U64_5(__VA_ARGS__) > > +#define __TO_U64_7(x, ...) , (u64)(x) __TO_U64_6(__VA_ARGS__) > > +#define __TO_U64_8(x, ...) , (u64)(x) __TO_U64_7(__VA_ARGS__) > > + > > +#define __TO_U64_X(N, ...) CONCATENATE(__TO_U64_, N)(__VA_ARGS__) > > +#define __TO_U64(...) __TO_U64_X(COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__) > > + > > +REMOTE_EVENT_FORMAT(hyp_printk, HE_STRUCT(he_field(u16, fmt_id) he_field(u64, args[]))); > > [Severity: Medium] > Can this leak hypervisor memory to the host or userspace through uninitialized > struct padding? The REMOTE_EVENT_FORMAT definition creates a structure with > u16 fmt_id followed by a u64 args[] array, which introduces padding bytes > before the array to satisfy the 8-byte alignment requirement of u64. Because > tracing_reserve_entry() does not zero the allocated space, these padding bytes > are never initialized before tracing_commit_entry() exposes them. That memory is kernel-shared memory anyway. I do not believe this is an issue. > > > +extern struct hyp_event_id hyp_event_id_hyp_printk; > > + > > +static __always_inline void __trace_hyp_printk(struct hyp_string_fmt *fmt, int nr_args, ...) > > +{ > > + struct remote_event_format_hyp_printk *entry; > > + va_list va; > > + int i; > > + > > + if (!atomic_read(&hyp_event_id_hyp_printk.enabled)) > > + return; > > + > > + entry = tracing_reserve_entry(struct_size(entry, args, nr_args)); > > + if (!entry) > > + return; > > + > > + entry->hdr.id = hyp_event_id_hyp_printk.id; > > + entry->fmt_id = fmt - __hyp_string_fmts_start; > > + > > + va_start(va, nr_args); > > + for (i = 0; i < nr_args; i++) > > + entry->args[i] = va_arg(va, u64); > > + va_end(va); > > + > > + tracing_commit_entry(); > > +} > > + > > +#define trace_hyp_printk(__fmt, __args...) \ > > +do { \ > > + static struct hyp_string_fmt __used __section("_hyp_string_fmts") \ > > + __hyp_str_fmt = { \ > > + .fmt = __fmt, \ > > + .nr_args = COUNT_ARGS(__args), \ > > + }; \ > > + BUILD_BUG_ON(sizeof(__fmt) > HYP_STRING_FMT_MAX_SIZE); \ > > + /* __TO_U64 prepends a comma if there are arguments */ \ > > + __trace_hyp_printk(&__hyp_str_fmt, COUNT_ARGS(__args) __TO_U64(__args)); \ > > +} while (0) > > [Severity: High] > Does this macro validate that the provided format string matches the given > arguments? Without checking the format string (e.g. using __printf attributes > or if (0) printk(...) type-checking), a mismatch like > trace_hyp_printk("val=%d") will set nr_args to 0. It is impossible to use __printf here due to the u64 cast. > > The host kernel's __hyp_trace_printk() will then unconditionally call > trace_seq_printf(seq, fmt); with no arguments, potentially causing > out-of-bounds reads or invalid dereferences when it parses the %d or %s > specifiers. > > > diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c > > index 8faaec899f92c..ebb882d0152b6 100644 > > --- a/arch/arm64/kvm/hyp_trace.c > > +++ b/arch/arm64/kvm/hyp_trace.c > [ ... ] > > @@ -421,6 +424,58 @@ static const char *__hyp_enter_exit_reason_str(u8 reason) > > return strs[min(reason, HYP_REASON_UNKNOWN)]; > > } > > > > +static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq) > > +{ > [ ... ] > > + switch (hyp_str_fmt->nr_args) { > > + case 0: > > + trace_seq_printf(seq, fmt); > > + break; > > + case 1: > > + trace_seq_printf(seq, fmt, entry->args[0]); > > + break; > [ ... ] > > + } > > + > > + if (seq->buffer[trace_seq_used(seq) - 1] != '\n') > > + trace_seq_putc(seq, '\n'); > > [Severity: Medium] > Is it possible for trace_seq_used(seq) to be 0 here? If the sequence buffer > was empty or in an overflowed state when passed to this function, subtracting > 1 would result in an out-of-bounds array read (accessing seq->buffer[-1]). > Would adding a check for trace_seq_used(seq) > 0 prevent this? I will fix that too. > > > +} > > -- > Sashiko AI review · https://sashiko.dev/#/patchset/20261001084909.2937943-1-vdonnefort@google.com?part=4 I will also change the "\"Unknown\"" even though we do not support yet external tooling. -- Vincent ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-01 13:52 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-01 8:49 [PATCH v4 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 2/4] KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/ Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 3/4] tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper Vincent Donnefort 2026-10-01 8:49 ` [PATCH v4 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Vincent Donnefort 2026-10-01 9:07 ` sashiko-bot 2026-10-01 13:52 ` Vincent Donnefort
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.