[PATCH 1/1] firmware: smccc: Add tracepoints when SMCCC calls are made

Elliot Berman
2021-09-22T22:37:23Z

Add trace events for SMCCC calls. These traces allow for convenient
mechanism for kernel to log SMC/HVC instructions without requiring
extracting traces from firmware. SMCCC spec currently [1] allows for 7
argument registers and 4 result registers. The first argument should
always be a function identifier, so use that to match the exit trace.

[1]: ARM DEN 0028D (https://developer.arm.com/documentation/den0028/d/)

Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
drivers/firmware/smccc/Kconfig | 8 ++
drivers/firmware/smccc/Makefile | 1 +
drivers/firmware/smccc/arm-smccc-traces.c | 14 ++++
include/linux/arm-smccc.h | 38 +++++++--
include/trace/events/arm_smccc.h | 99 +++++++++++++++++++++++
5 files changed, 154 insertions(+), 6 deletions(-)
create mode 100644 drivers/firmware/smccc/arm-smccc-traces.c
create mode 100644 include/trace/events/arm_smccc.h

diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig
index 15e7466179a6..fb7d2da1558e 100644
--- a/drivers/firmware/smccc/Kconfig
+++ b/drivers/firmware/smccc/Kconfig
@@ -23,3 +23,11 @@ config ARM_SMCCC_SOC_ID
help
Include support for the SoC bus on the ARM SMCCC firmware based
platforms providing some sysfs information about the SoC variant.
+
+config TRACE_ARM_SMCCC
+ bool "Trace ARM SMCCC Calls"
+ depends on HAVE_ARM_SMCCC
+ depends on TRACEPOINTS
+ default y
+ help
+ Enable trace events to log SMC and HVC service calls.
diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
index 40d19144a860..be50ae665040 100644
--- a/drivers/firmware/smccc/Makefile
+++ b/drivers/firmware/smccc/Makefile
@@ -2,3 +2,4 @@
#
obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o kvm_guest.o
obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
+obj-$(CONFIG_TRACE_ARM_SMCCC) += arm-smccc-traces.o
diff --git a/drivers/firmware/smccc/arm-smccc-traces.c b/drivers/firmware/smccc/arm-smccc-traces.c
new file mode 100644
index 000000000000..9c6282644c02
--- /dev/null
+++ b/drivers/firmware/smccc/arm-smccc-traces.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM SMCCC Trace points
+ *
+ * Copyright (C) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/export.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/arm_smccc.h>
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(arm_smccc_start);
+EXPORT_TRACEPOINT_SYMBOL_GPL(arm_smccc_end);
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 7d1cabe15262..c214cebb1603 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -190,6 +190,7 @@

#include <linux/linkage.h>
#include <linux/types.h>
+#include <trace/events/arm_smccc.h>

enum arm_smccc_conduit {
SMCCC_CONDUIT_NONE,
@@ -343,13 +344,25 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);

-#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
+#define __arm_smccc(call, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, res, quirk) \
+ do { \
+ trace_arm_smccc_start(tracepoint_string(#call), \
+ (arg0), (arg1), (arg2), (arg3), (arg4), \
+ (arg5), (arg6), (arg7)); \
+ __arm_smccc_##call((arg0), (arg1), (arg2), (arg3), (arg4), (arg5), (arg6), (arg7),\
+ (res), (quirk)); \
+ trace_arm_smccc_end(tracepoint_string(#call), (arg0), \
+ (res)->a0, (res)->a1, (res)->a2, (res)->a3); \
+ } while (0)
+

-#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__)
+#define arm_smccc_smc(...) __arm_smccc(smc, __VA_ARGS__, NULL)

-#define arm_smccc_hvc(...) __arm_smccc_hvc(__VA_ARGS__, NULL)
+#define arm_smccc_smc_quirk(...) __arm_smccc(smc, __VA_ARGS__)

-#define arm_smccc_hvc_quirk(...) __arm_smccc_hvc(__VA_ARGS__)
+#define arm_smccc_hvc(...) __arm_smccc(hvc, __VA_ARGS__, NULL)
+
+#define arm_smccc_hvc_quirk(...) __arm_smccc(hvc, __VA_ARGS__)

/* SMCCC v1.1 implementation madness follows */
#ifdef CONFIG_ARM64
@@ -450,22 +463,35 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
: smccc_sve_clobbers "memory"
#define __constraints(count) ___constraints(count)

+#define __trace_arg_0 ((unsigned long)arg0)
+#define __trace_arg_1 __trace_arg_0, ((unsigned long)arg1)
+#define __trace_arg_2 __trace_arg_1, ((unsigned long)arg2)
+#define __trace_arg_3 __trace_arg_2, ((unsigned long)arg3)
+#define __trace_arg_4 __trace_arg_3, ((unsigned long)arg4)
+#define __trace_arg_5 __trace_arg_4, ((unsigned long)arg5)
+#define __trace_arg_6 __trace_arg_5, ((unsigned long)arg6)
+#define __trace_arg_7 __trace_arg_6, ((unsigned long)arg7)
+#define ___trace_args(count) __trace_arg_##count
+#define __trace_args(count) ___trace_args(count)
+
/*
* We have an output list that is not necessarily used, and GCC feels
* entitled to optimise the whole sequence away. "volatile" is what
* makes it stick.
*/
-#define __arm_smccc_1_1(inst, ...) \
+#define __arm_smccc_1_1(inst, a0, ...) \
do { \
register unsigned long r0 asm("r0"); \
register unsigned long r1 asm("r1"); \
register unsigned long r2 asm("r2"); \
register unsigned long r3 asm("r3"); \
__declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
+ trace_arm_smccc_start_vargs(tracepoint_string(inst), __trace_args(__count_args(a0, __VA_ARGS__))); \
asm volatile(SMCCC_SVE_CHECK \
inst "\n" : \
"=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) \
- __constraints(__count_args(__VA_ARGS__))); \
+ __constraints(__count_args(a0, __VA_ARGS__))); \
+ trace_arm_smccc_end(tracepoint_string(inst), a0, r0, r1, r2, r3); \
if (___res) \
*___res = (typeof(*___res)){r0, r1, r2, r3}; \
} while (0)
diff --git a/include/trace/events/arm_smccc.h b/include/trace/events/arm_smccc.h
new file mode 100644
index 000000000000..533377d2d066
--- /dev/null
+++ b/include/trace/events/arm_smccc.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#if IS_REACHABLE(CONFIG_TRACE_ARM_SMCCC) && !defined(__KVM_NVHE_HYPERVISOR__)
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM arm_smccc
+
+#if !defined(_TRACE_ARM_SMCCC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_ARM_SMCCC_H
+
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(arm_smccc_start,
+ TP_PROTO(const char *call, unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6,
+ unsigned long a7),
+
+ TP_ARGS(call, a0, a1, a2, a3, a4, a5, a6, a7),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, a0)
+ __field(unsigned long, a1)
+ __field(unsigned long, a2)
+ __field(unsigned long, a3)
+ __field(unsigned long, a4)
+ __field(unsigned long, a5)
+ __field(unsigned long, a6)
+ __field(unsigned long, a7)
+ __string(call, call)
+ ),
+
+ TP_fast_assign(
+ __entry->a0 = a0;
+ __entry->a1 = a1;
+ __entry->a2 = a2;
+ __entry->a3 = a3;
+ __entry->a4 = a4;
+ __entry->a5 = a5;
+ __entry->a6 = a6;
+ __entry->a7 = a7;
+
+ __assign_str(call, call);
+ ),
+
+ TP_printk("call=%s fn=%04x args={%0lx %0lx %0lx %0lx %0lx %0lx %0lx}",
+ __get_str(call), __entry->a0, __entry->a1, __entry->a2,
+ __entry->a3, __entry->a4, __entry->a5, __entry->a6, __entry->a7)
+);
+
+TRACE_EVENT(arm_smccc_end,
+ TP_PROTO(const char *call, unsigned long a0, unsigned long r0, unsigned long r1,
+ unsigned long r2, unsigned long r3),
+
+ TP_ARGS(call, a0, r0, r1, r2, r3),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, a0)
+ __field(unsigned long, r0)
+ __field(unsigned long, r1)
+ __field(unsigned long, r2)
+ __field(unsigned long, r3)
+ __string(call, call)
+ ),
+
+ TP_fast_assign(
+ __entry->a0 = a0;
+ __entry->r0 = r0;
+ __entry->r1 = r1;
+ __entry->r2 = r2;
+ __entry->r3 = r3;
+
+ __assign_str(call, call);
+ ),
+
+ TP_printk("call=%s fn=%04x res={%0lx %0lx %0lx %0lx}",
+ __get_str(call), __entry->a0, __entry->r0, __entry->r1,
+ __entry->r2, __entry->r3)
+);
+
+#define _trace_arm_smccc_start(call, a0, a1, a2, a3, a4, a5, a6, a7, ...) \
+ trace_arm_smccc_start(call, a0, a1, a2, a3, a4, a5, a6, a7)
+
+#define trace_arm_smccc_start_vargs(call, ...) \
+ _trace_arm_smccc_start(call, __VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0)
+
+#endif
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+
+#else
+#define trace_arm_smccc_start(...)
+#define trace_arm_smccc_end(...)
+
+#define trace_arm_smccc_start_vargs(...)
+#endif
--
2.33.0