From: Vincent Donnefort <vdonnefort@google.com>
To: Fuad Tabba <tabba@google.com>
Cc: maz@kernel.org, oliver.upton@linux.dev, joey.gouly@arm.com,
suzuki.poulose@arm.com, yuzenghui@huawei.com,
catalin.marinas@arm.com, will@kernel.org, rostedt@goodmis.org,
linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev,
kernel-team@android.com, qerret@google.com
Subject: Re: [PATCH v1 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp
Date: Mon, 15 Jun 2026 09:29:34 +0100 [thread overview]
Message-ID: <ai-37kQ0e_VCma2z@google.com> (raw)
In-Reply-To: <CA+EHjTwqp=Hjzyn469Gvm-JRNovfnj=0eFB5+Kei_9pRQrnG0Q@mail.gmail.com>
On Sun, Jun 14, 2026 at 04:25:00PM +0100, Fuad Tabba wrote:
> Hi Vincent,
>
> On Fri, 12 Jun 2026 at 15:22, Vincent Donnefort <vdonnefort@google.com> wrote:
> >
> > 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().
> >
> > 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.
> >
> > To not waste tracing buffer data, use a dynamic size. Each
> > argument is 8 bytes and the in-kernel printing function can simply know
> > how many of them there are by looking at the event length.
> >
> > Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
> >
> > diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
> > index 743c49bd878f..8465b523cb1d 100644
> > --- a/arch/arm64/include/asm/kvm_hypevents.h
> > +++ b/arch/arm64/include/asm/kvm_hypevents.h
> > @@ -57,4 +57,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..46097105fdd8 100644
> > --- a/arch/arm64/include/asm/kvm_hyptrace.h
> > +++ b/arch/arm64/include/asm/kvm_hyptrace.h
> > @@ -23,4 +23,12 @@ 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 128
> > +
> > +struct hyp_string_fmt {
> > + const char fmt[HYP_STRING_FMT_MAX_SIZE];
> > +};
> > +
> > +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 d4c7d45ae6bc..ec03621d7a81 100644
> > --- a/arch/arm64/kernel/image-vars.h
> > +++ b/arch/arm64/kernel/image-vars.h
> > @@ -141,6 +141,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 e1ac876200a3..b6d62642b6bd 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..3d0b5c634bb3 100644
> > --- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
> > +++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
> > @@ -46,6 +46,69 @@ 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;
>
> nit: fmt_id is u16, the subtraction is ptrdiff_t. Silent truncation if
> the section ever has more than 65536 entries. Not realistic today, but
> a WARN_ON on the section size in hyp_trace_init_events() would catch
> it at boot for free.
Ack. That wouldn't cost anything. I'll add that in a v2
>
> Reviewed-by: Fuad Tabba <tabba@google.com>
> Tested-by: Fuad Tabba <tabba@google.com>
>
> Cheers,
> /fuad
>
>
> > +
> > + 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") fmt = { \
> > + .fmt = __fmt \
> > + }; \
> > + BUILD_BUG_ON(sizeof(__fmt) > HYP_STRING_FMT_MAX_SIZE); \
> > + /* __TO_U64 prepends a comma if there are arguments */ \
> > + __trace_hyp_printk(&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 +121,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 821bc93ecdd1..187a8a295d6f 100644
> > --- a/arch/arm64/kvm/hyp_trace.c
> > +++ b/arch/arm64/kvm/hyp_trace.c
> > @@ -391,6 +391,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)
> > @@ -409,6 +412,61 @@ 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 char *fmt = (const char *)(&__hyp_string_fmts_start[entry->fmt_id]);
> > + struct ring_buffer_event *evt = (void *)entry - RB_EVNT_HDR_SIZE;
> > + int nr_args;
> > +
> > + trace_seq_putc(seq, ' ');
> > +
> > + if ((void *)fmt >= (void *)__hyp_string_fmts_end) {
> > + trace_seq_printf(seq, "Unknown hyp_string_fmt ID %d\n", entry->fmt_id);
> > + return;
> > + }
> > +
> > + nr_args = (ring_buffer_event_length(evt) -
> > + offsetof(struct remote_event_format_hyp_printk, args)) / sizeof(entry->args[0]);
> > + switch (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;
> > --
> > 2.54.0.1136.gdb2ca164c4-goog
> >
next prev parent reply other threads:[~2026-06-15 8:29 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-12 14:22 [PATCH v1 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Vincent Donnefort
2026-06-12 14:22 ` [PATCH v1 1/4] KVM: arm64: Allow early calls to pKVM host_share/unshare_hyp Vincent Donnefort
2026-06-14 13:39 ` Fuad Tabba
2026-06-12 14:22 ` [PATCH v1 2/4] KVM: arm64: Move kvm_define_hypevents.h to arch/arm64/kvm/ Vincent Donnefort
2026-06-14 13:41 ` Fuad Tabba
2026-06-12 14:22 ` [PATCH v1 3/4] tracing/remotes: Add REMOTE_EVENT_CUSTOM_PRINTK() helper Vincent Donnefort
2026-06-14 14:46 ` Fuad Tabba
2026-06-12 14:22 ` [PATCH v1 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp Vincent Donnefort
2026-06-14 15:25 ` Fuad Tabba
2026-06-15 8:29 ` Vincent Donnefort [this message]
2026-06-14 12:57 ` [PATCH v1 0/4] trace_hyp_printk() for pKVM/nVHE hypervisor Fuad Tabba
2026-06-15 8:27 ` Vincent Donnefort
2026-06-15 8:30 ` Fuad Tabba
2026-06-15 8:57 ` Fuad Tabba
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ai-37kQ0e_VCma2z@google.com \
--to=vdonnefort@google.com \
--cc=catalin.marinas@arm.com \
--cc=joey.gouly@arm.com \
--cc=kernel-team@android.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=maz@kernel.org \
--cc=oliver.upton@linux.dev \
--cc=qerret@google.com \
--cc=rostedt@goodmis.org \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox