ANDROID: KVM: arm64: trace_hyp_printk()

Simple trace_printk() equivalent for the pKVM hypervisor. This only
supports up to 4 arguments and can't print '%p' or '%s' formats.

Bug: 357781595
Change-Id: Ief8355accfbcb630d7df36c93498d62d50f63d0c
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/kvm_define_hypevents.h b/arch/arm64/include/asm/kvm_define_hypevents.h
index efa2c2c..39e5963 100644
--- a/arch/arm64/include/asm/kvm_define_hypevents.h
+++ b/arch/arm64/include/asm/kvm_define_hypevents.h
@@ -43,8 +43,10 @@
 
 #undef HYP_EVENT
 #undef HE_PRINTK
+#undef HE_PRINTK_UNKNOWN_FMT
 #define __entry REC
 #define HE_PRINTK(fmt, args...) "\"" fmt "\", " __stringify(args)
+#define HE_PRINTK_UNKNOWN_FMT(fmt, args...) "Unknown"
 #define HYP_EVENT(__name, __proto, __struct, __assign, __printk)	\
 	static char hyp_event_print_fmt_##__name[] = __printk;		\
 	static bool hyp_event_enabled_##__name;				\
diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
index bae2f92..f7a326b 100644
--- a/arch/arm64/include/asm/kvm_hypevents.h
+++ b/arch/arm64/include/asm/kvm_hypevents.h
@@ -72,6 +72,26 @@ HYP_EVENT(host_mem_abort,
 		  __entry->esr, __entry->addr)
 );
 
+HYP_EVENT(__hyp_printk,
+	HE_PROTO(const char *fmt, u64 a, u64 b, u64 c, u64 d),
+	HE_STRUCT(
+		he_field(u8, fmt_id)
+		he_field(u64, a)
+		he_field(u64, b)
+		he_field(u64, c)
+		he_field(u64, d)
+	),
+	HE_ASSIGN(
+		__entry->fmt_id = hyp_printk_fmt_to_id(fmt);
+		__entry->a = a;
+		__entry->b = b;
+		__entry->c = c;
+		__entry->d = d;
+	),
+	HE_PRINTK_UNKNOWN_FMT(hyp_printk_fmt_from_id(__entry->fmt_id),
+		__entry->a, __entry->b, __entry->c, __entry->d)
+);
+
 #ifdef CONFIG_PROTECTED_NVHE_TESTING
 HYP_EVENT(selftest,
 	  HE_PROTO(void),
diff --git a/arch/arm64/include/asm/kvm_hypevents_defs.h b/arch/arm64/include/asm/kvm_hypevents_defs.h
index 473bf43..b961410f 100644
--- a/arch/arm64/include/asm/kvm_hypevents_defs.h
+++ b/arch/arm64/include/asm/kvm_hypevents_defs.h
@@ -23,6 +23,12 @@ struct hyp_entry_hdr {
 	unsigned short id;
 };
 
+struct hyp_printk_fmt {
+	/* __MUST__ be the first element */
+	const char	fmt[127];
+	const char	null;
+};
+
 /*
  * Hyp events definitions common to the hyp and the host
  */
@@ -32,10 +38,11 @@ struct hyp_entry_hdr {
 		__struct				\
 	}
 
-#define HE_PROTO(args...)	args
-#define HE_STRUCT(args...)	args
-#define HE_ASSIGN(args...)	args
-#define HE_PRINTK(args...)	args
+#define HE_PROTO(args...)		args
+#define HE_STRUCT(args...)		args
+#define HE_ASSIGN(args...)		args
+#define HE_PRINTK(args...)		args
+#define HE_PRINTK_UNKNOWN_FMT(args...)	args
 
 #define he_field(type, item)	type item;
 #endif
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index e60754c..6084332c 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -137,6 +137,7 @@ KVM_NVHE_ALIAS(__hyp_rodata_end);
 #ifdef CONFIG_TRACING
 KVM_NVHE_ALIAS(__hyp_event_ids_start);
 KVM_NVHE_ALIAS(__hyp_event_ids_end);
+KVM_NVHE_ALIAS(__hyp_printk_fmts_start);
 #endif
 
 /* pKVM static key */
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 8e578dc..04c6dc8 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -217,6 +217,11 @@
 		*(_hyp_events)
 		__hyp_events_end = .;
 	}
+	.rodata.hyp_printk_fmts : {
+		__hyp_printk_fmts_start = .;
+		*(HYP_SECTION_NAME(.printk_fmts))
+		__hyp_printk_fmts_end = .;
+	}
 #endif
 	/* code sections that are never executed via the kernel mapping */
 	.rodata.text : {
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 8384801..abc0ee5 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -42,6 +42,40 @@ int __pkvm_enable_tracing(bool enable);
 int __pkvm_reset_tracing(unsigned int cpu);
 int __pkvm_swap_reader_tracing(unsigned int cpu);
 int __pkvm_enable_event(unsigned short id, bool enable);
+
+extern char __hyp_printk_fmts_start[];
+
+static inline u8 hyp_printk_fmt_to_id(const char *fmt)
+{
+	return (fmt - __hyp_printk_fmts_start) / sizeof(struct hyp_printk_fmt);
+}
+
+#define __trace_hyp_printk(__fmt, a, b, c, d)		\
+do {							\
+	static struct hyp_printk_fmt __used		\
+			__section(".hyp.printk_fmts")	\
+			ht_fmt = {			\
+				.fmt = __fmt		\
+	};						\
+	trace___hyp_printk(ht_fmt.fmt, a, b, c, d);	\
+} while (0)
+
+#define __trace_hyp_printk_0(fmt, arg)		\
+	__trace_hyp_printk(fmt, 0, 0, 0, 0)
+#define __trace_hyp_printk_1(fmt, a)		\
+	__trace_hyp_printk(fmt, a, 0, 0, 0)
+#define __trace_hyp_printk_2(fmt, a, b)		\
+	__trace_hyp_printk(fmt, a, b, 0, 0)
+#define __trace_hyp_printk_3(fmt, a, b, c)	\
+	__trace_hyp_printk(fmt, a, b, c, 0)
+#define __trace_hyp_printk_4(fmt, a, b, c, d) \
+	__trace_hyp_printk(fmt, a, b, c, d)
+
+#define __trace_hyp_printk_N(fmt, ...) \
+	CONCATENATE(__trace_hyp_printk_, COUNT_ARGS(__VA_ARGS__))(fmt, ##__VA_ARGS__)
+
+#define trace_hyp_printk(fmt, ...) \
+	__trace_hyp_printk_N(fmt, __VA_ARGS__)
 #else
 static inline void *tracing_reserve_entry(unsigned long length) { return NULL; }
 static inline void tracing_commit_entry(void) { }
@@ -56,5 +90,6 @@ static inline int __pkvm_enable_tracing(bool enable) { return -ENODEV; }
 static inline int __pkvm_reset_tracing(unsigned int cpu) { return -ENODEV; }
 static inline int __pkvm_swap_reader_tracing(unsigned int cpu) { return -ENODEV; }
 static inline int __pkvm_enable_event(unsigned short id, bool enable)  { return -ENODEV; }
+#define trace_hyp_printk(fmt, ...)
 #endif
 #endif
diff --git a/arch/arm64/kvm/hyp_events.c b/arch/arm64/kvm/hyp_events.c
index de5cc7e..2fe7726 100644
--- a/arch/arm64/kvm/hyp_events.c
+++ b/arch/arm64/kvm/hyp_events.c
@@ -6,11 +6,29 @@
 #include <linux/tracefs.h>
 
 #include <asm/kvm_host.h>
-#include <asm/kvm_define_hypevents.h>
 #include <asm/setup.h>
 
 #include "hyp_trace.h"
 
+static const char *hyp_printk_fmt_from_id(u8 fmt_id);
+
+#include <asm/kvm_define_hypevents.h>
+
+extern char __hyp_printk_fmts_start[];
+extern char __hyp_printk_fmts_end[];
+
+static const char *hyp_printk_fmt_from_id(u8 fmt_id)
+{
+	u8 max_ids = (__hyp_printk_fmts_end -
+		      __hyp_printk_fmts_start) / sizeof(struct hyp_printk_fmt);
+
+	if (fmt_id >= max_ids)
+		return "Unknown Format";
+
+	return (const char *)(__hyp_printk_fmts_start +
+			      (fmt_id * sizeof(struct hyp_printk_fmt)));
+}
+
 extern struct hyp_event __hyp_events_start[];
 extern struct hyp_event __hyp_events_end[];
 
@@ -311,6 +329,9 @@ int hyp_trace_init_events(void)
 	struct hyp_event *event = __hyp_events_start;
 	int id = 0;
 
+	/* __hyp_printk event only supports U8_MAX different formats */
+	WARN_ON((__hyp_printk_fmts_end - __hyp_printk_fmts_start) > U8_MAX);
+
 	for (; (unsigned long)event < (unsigned long)__hyp_events_end;
 		event++, hyp_event_id++, id++) {