[PATCH 18/19] coresight: trbe: Add kunit tests

Leo Yan posted 19 patches 10 hours ago
[PATCH 18/19] coresight: trbe: Add kunit tests
Posted by Leo Yan 10 hours ago
Add tests to verify the calculation of the limit and trigger count.

Because trigger mode can be disabled, provide two test suites: one with
trigger mode enabled and one with it disabled.

The cpudata structure is initialized by the test stub, so move its
definition into the header for including.

Signed-off-by: Leo Yan <leo.yan@arm.com>
---
 drivers/hwtracing/coresight/Kconfig                |   9 +
 drivers/hwtracing/coresight/Makefile               |   1 +
 .../coresight/coresight-trbe-kunit-tests.c         | 536 +++++++++++++++++++++
 drivers/hwtracing/coresight/coresight-trbe.c       | 112 +----
 drivers/hwtracing/coresight/coresight-trbe.h       |  89 ++++
 5 files changed, 660 insertions(+), 87 deletions(-)

diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 6a4239ebb582e95f0ebe8e9c8738a726f27f60a1..f5758563c0090141cdca67f16e7b7b32e7c75bb8 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -214,6 +214,15 @@ config CORESIGHT_TRBE
 	  To compile this driver as a module, choose M here: the module will be
 	  called coresight-trbe.
 
+config CORESIGHT_TRBE_KUNIT_TESTS
+	tristate "Enable Coresight TRBE unit tests"
+	depends on KUNIT
+	depends on CORESIGHT_TRBE
+	default KUNIT_ALL_TESTS
+	help
+	  Enable Coresight TRBE unit tests. Only useful for development and not
+	  intended for production.
+
 config ULTRASOC_SMB
 	tristate "Ultrasoc system memory buffer drivers"
 	depends on ACPI || COMPILE_TEST
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index ab16d06783a572ea1308dfb3a30c96df9e5ffdb7..f8961f6883d167bc2c4bca8008eceb08c3c3a0e9 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
 obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
 coresight-ctcu-y := coresight-ctcu-core.o
 obj-$(CONFIG_CORESIGHT_KUNIT_TESTS) += coresight-kunit-tests.o
+obj-$(CONFIG_CORESIGHT_TRBE_KUNIT_TESTS) += coresight-trbe-kunit-tests.o
diff --git a/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c b/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c
new file mode 100644
index 0000000000000000000000000000000000000000..836f76dce155d533f9076e85dc97ba25221b7bbf
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-trbe-kunit-tests.c
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <kunit/test.h>
+#include <kunit/device.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+#include "coresight-trbe.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+static void test_compute_offset(struct kunit *test)
+{
+	struct perf_output_handle handle = { 0 };
+	struct trbe_buf buf = { 0 };
+	struct trbe_cpudata cpudata = { .trbe_align = PAGE_SIZE };
+	unsigned long limit;
+
+	if (!static_branch_unlikely(&trbe_trigger_mode_bypass))
+		return;
+
+	cpudata.trbe_hw_align = 1;
+
+	buf.nr_pages = SZ_1M / SZ_4K;
+	buf.cpudata = &cpudata;
+
+	handle.rb = (void *)&buf;
+
+	/*
+	 * ### : Free space, $$$ : Filled space
+	 *
+	 * |################|################|
+	 * `head            `wakeup
+	 * `tail            `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2);
+
+	/*
+	 * |################|################|
+	 * `head            `wakeup         `tail
+	 *                  `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2);
+
+	/*
+	 * |#################################|
+	 * `head                            `tail
+	 * `wakeup                         `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, 0);
+
+	/*
+	 * |#################################|
+	 * `head                            `tail
+	 *                                   `wakeup
+	 *                                 `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = SZ_1M;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+
+	/*
+	 * |$$$$$$$$$$$$$$$$|########|#######|
+	 *                  `head           `tail
+	 *                           `wakeup
+	 *                                 `limit
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M * 3 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+
+	/*
+	 * |$$$$$$$$|$$$$$$$|################|
+	 *                  `head           `tail
+	 *          `wakeup
+	 *                                 `limit
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M * 1 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+
+	/*
+	 * |$$$$$$$$$$$$$$$$|################|
+	 *                  `head           `tail
+	 *                                  `wakeup
+	 *                                 `limit
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M - 1;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+
+	/*
+	 * |#########|$$$$$$$$$$|########|###|
+	 *           `tail      `head    `wakeup
+	 *                                   `limit
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = handle.head + SZ_1M / 8;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 7 / 8);
+
+	/*
+	 * |####|####|$$$$$$$$$$|############|
+	 *           `tail      `head
+	 *      `wakeup
+	 *                                   `limit
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 8;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+
+	/*
+	 * |#######|########|$$$$$$$$$$$$$$$$|
+	 * `head   `wakeup  `>tail
+	 *         `limit
+	 */
+	handle.head = SZ_1M;
+	handle.wakeup = SZ_1M + SZ_1M / 8;
+	handle.size = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M / 8);
+
+	/*
+	 * |#######|$$$$$$$$$$$$$$$$$|#######|
+	 *         `tail             `head
+	 *         `wakeup
+	 *                                   `limit
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+
+	/*
+	 * |#######|$$$$$$$$|$$$$$$$$|#######|
+	 *         `tail    `wakeup  `head
+	 *                                   `limit
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+
+	/*
+	 * |$$$$$$$|########|########|$$$$$$$|
+	 *         `head    `wakeup  `tail
+	 *                           `limit
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2);
+
+	/*
+	 * |$$$$$$$|#################|$$$$$$$|
+	 *         `head             `tail
+	 *                           `wakeup
+	 *                           `limit
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M * 3 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+
+	/*
+	 * |$$$$$$$|#################|$$$$$$$|
+	 * `wakeup `head             `tail
+	 *                           `limit
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+
+	/*
+	 * |$$$$$$|$$$$$$$$$$$$$$$$$$$$$$$$$$|
+	 *        `head
+	 *        `tail
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, 0);
+
+	/*
+	 * |$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$|#$|
+	 *                                `head
+	 *                                  `tail
+	 */
+	handle.head = SZ_1M - SZ_1K * 2;
+	handle.size = SZ_1K;
+	handle.wakeup = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	KUNIT_ASSERT_EQ(test, limit, 0);
+}
+
+static void test_compute_offset_and_counter(struct kunit *test)
+{
+	struct perf_output_handle handle = { 0 };
+	struct trbe_buf buf = { 0 };
+	struct trbe_cpudata cpudata = { .trbe_align = PAGE_SIZE };
+	unsigned long limit;
+	u64 count;
+
+	if (static_branch_unlikely(&trbe_trigger_mode_bypass))
+		return;
+
+	cpudata.trbe_hw_align = 1;
+
+	buf.nr_pages = SZ_1M / SZ_4K;
+	buf.cpudata = &cpudata;
+
+	handle.rb = (void *)&buf;
+
+	/*
+	 * ### : Free space, $$$ : Filled space
+	 *
+	 * |################|################|
+	 * `head            `wakeup          `limit
+	 * `tail
+	 * `----- count ----'
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 2);
+
+	/*
+	 * |################|################|
+	 * `head            `wakeup         `tail
+	 *                                 `limit
+	 * `----- count ----'
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 2);
+
+	/*
+	 * |#################################|
+	 * `head                            `tail
+	 * `wakeup                         `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, 0);
+
+	/*
+	 * |#################################|
+	 * `head                            `tail
+	 *                                   `wakeup
+	 *                                 `limit
+	 */
+	handle.head = 0;
+	handle.size = SZ_1M - 1;
+	handle.wakeup = SZ_1M;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, 0);
+
+	/*
+	 * |$$$$$$$$$$$$$$$$|########|#######|
+	 *                  `head           `tail
+	 *                           `wakeup
+	 *                                 `limit
+	 *                  [  count ]
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M * 3 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 4);
+
+	/*
+	 * |$$$$$$$$|$$$$$$$|################|
+	 *                  `head           `tail
+	 *          `wakeup
+	 *                                 `limit
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M * 1 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, 0);
+
+	/*
+	 * |$$$$$$$$$$$$$$$$|################|
+	 *                  `head           `tail
+	 *                                  `wakeup
+	 *                                 `limit
+	 */
+	handle.head = SZ_1M / 2;
+	handle.size = SZ_1M / 2 - 1;
+	handle.wakeup = SZ_1M - 1;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M - SZ_4K);
+	KUNIT_ASSERT_EQ(test, count, 0);
+
+	/*
+	 * |#########|$$$$$$$$$$|########|###|
+	 *           `tail      `head    `wakeup
+	 *                                   `limit
+	 *                      [  count ]
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = handle.head + SZ_1M / 8;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 8);
+
+	/*
+	 * |####|####|$$$$$$$$$$|############|
+	 *           `tail      `head
+	 *      `wakeup
+	 *                                   `limit
+	 *                      [   count  >>>
+	 * >>>       ]
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 8;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 2);
+
+	/*
+	 * |#######|########|$$$$$$$$$$$$$$$$|
+	 * `head   `wakeup  `>tail
+	 *                  `limit
+	 * [ count ]
+	 */
+	handle.head = SZ_1M;
+	handle.wakeup = SZ_1M + SZ_1M / 8;
+	handle.size = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M / 2);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 8);
+
+	/*
+	 * |#######|$$$$$$$$$$$$$$$$$|#######|
+	 *         `tail             `head
+	 *         `wakeup
+	 *                                   `limit
+	 *                           [ count >
+	 * >>>     ]
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 2);
+
+	/*
+	 * |#######|$$$$$$$$|$$$$$$$$|#######|
+	 *         `tail    `wakeup  `head
+	 *                                   `limit
+	 *                           [ count >
+	 * >>>     ]
+	 */
+	handle.head = SZ_1M * 3 / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M + SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 2);
+
+	/*
+	 * |$$$$$$$|########|########|$$$$$$$|
+	 *         `head    `wakeup  `tail
+	 *                           `limit
+	 *         [ count  ]
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M / 2;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+	KUNIT_ASSERT_EQ(test, count, SZ_1M / 4);
+
+	/*
+	 * |$$$$$$$|#################|$$$$$$$|
+	 *         `head             `tail
+	 *                           `wakeup
+	 *                           `limit
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = SZ_1M * 3 / 4;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+	KUNIT_ASSERT_EQ(test, count, 0);
+
+	/*
+	 * |$$$$$$$|#################|$$$$$$$|
+	 * `wakeup `head             `tail
+	 *                           `limit
+	 */
+	handle.head = SZ_1M / 4;
+	handle.size = SZ_1M / 2;
+	handle.wakeup = 0;
+
+	limit = __trbe_normal_offset(&handle);
+	buf.trbe_limit = limit;
+	count = __trbe_normal_trigger_count(&handle);
+
+	KUNIT_ASSERT_EQ(test, limit, SZ_1M * 3 / 4);
+	KUNIT_ASSERT_EQ(test, count, 0);
+}
+
+static struct kunit_case coresight_trbe_testcases[] = {
+	KUNIT_CASE(test_compute_offset),
+	KUNIT_CASE(test_compute_offset_and_counter),
+	{}
+};
+
+static struct kunit_suite coresight_trbe_test_suite = {
+	.name = "coresight_trbe_test_suite",
+	.test_cases = coresight_trbe_testcases,
+};
+
+kunit_test_suites(&coresight_trbe_test_suite);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Leo Yan <leo.yan@arm.com>");
+MODULE_DESCRIPTION("Arm CoreSight TRBE KUnit tests");
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index ee9993d518d2a41f0d709b7d0690b2dfe0bef2d9..25d42683ab74b55efa2e19a2d77ab8ae2d68d228 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -54,92 +54,12 @@ enum trbe_fault_action {
 	TRBE_FAULT_ACT_FATAL,
 };
 
-struct trbe_buf {
-	/*
-	 * Even though trbe_base represents vmap()
-	 * mapped allocated buffer's start address,
-	 * it's being as unsigned long for various
-	 * arithmetic and comparision operations &
-	 * also to be consistent with trbe_write &
-	 * trbe_limit sibling pointers.
-	 */
-	unsigned long trbe_base;
-	/* The base programmed into the TRBE */
-	unsigned long trbe_hw_base;
-	unsigned long trbe_limit;
-	unsigned long trbe_write;
-	unsigned long trbe_count;
-	int nr_pages;
-	void **pages;
-	bool snapshot;
-	struct trbe_cpudata *cpudata;
-};
-
-/*
- * TRBE erratum list
- *
- * The errata are defined in arm64 generic cpu_errata framework.
- * Since the errata work arounds could be applied individually
- * to the affected CPUs inside the TRBE driver, we need to know if
- * a given CPU is affected by the erratum. Unlike the other erratum
- * work arounds, TRBE driver needs to check multiple times during
- * a trace session. Thus we need a quicker access to per-CPU
- * errata and not issue costly this_cpu_has_cap() everytime.
- * We keep a set of the affected errata in trbe_cpudata, per TRBE.
- *
- * We rely on the corresponding cpucaps to be defined for a given
- * TRBE erratum. We map the given cpucap into a TRBE internal number
- * to make the tracking of the errata lean.
- *
- * This helps in :
- *   - Not duplicating the detection logic
- *   - Streamlined detection of erratum across the system
- */
-#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE	0
-#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE	1
-#define TRBE_NEEDS_DRAIN_AFTER_DISABLE		2
-#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE	3
-#define TRBE_IS_BROKEN				4
-
-static int trbe_errata_cpucaps[] = {
-	[TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE,
-	[TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE,
-	[TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142,
-	[TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923,
-	[TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691,
-	-1,		/* Sentinel, must be the last entry */
-};
-
-/* The total number of listed errata in trbe_errata_cpucaps */
-#define TRBE_ERRATA_MAX			(ARRAY_SIZE(trbe_errata_cpucaps) - 1)
-
 /*
  * Safe limit for the number of bytes that may be overwritten
  * when ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE is triggered.
  */
 #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES	256
 
-/*
- * struct trbe_cpudata: TRBE instance specific data
- * @trbe_flag		- TRBE dirty/access flag support
- * @trbe_hw_align	- Actual TRBE alignment required for TRBPTR_EL1.
- * @trbe_align		- Software alignment used for the TRBPTR_EL1.
- * @cpu			- CPU this TRBE belongs to.
- * @mode		- Mode of current operation. (perf/disabled)
- * @drvdata		- TRBE specific drvdata
- * @errata		- Bit map for the errata on this TRBE.
- */
-struct trbe_cpudata {
-	bool trbe_flag;
-	u64 trbe_hw_align;
-	u64 trbe_align;
-	int cpu;
-	enum cs_mode mode;
-	struct trbe_buf *buf;
-	struct trbe_drvdata *drvdata;
-	DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
-};
-
 struct trbe_drvdata {
 	struct trbe_cpudata __percpu *cpudata;
 	struct perf_output_handle * __percpu *handle;
@@ -150,7 +70,8 @@ struct trbe_drvdata {
 	struct platform_device *pdev;
 };
 
-DEFINE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass);
+VISIBLE_IF_KUNIT DEFINE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass);
+EXPORT_SYMBOL_IF_KUNIT(trbe_trigger_mode_bypass);
 
 #define trbe_trigger_mode_need_bypass(cpudata)		\
 	(trbe_may_overwrite_in_fill_mode((cpudata)) ||	\
@@ -333,8 +254,17 @@ static void __trbe_pad_buf(struct trbe_buf *buf, u64 offset, int len)
 
 static void trbe_pad_buf(struct perf_output_handle *handle, int len)
 {
-	struct trbe_buf *buf = etm_perf_sink_config(handle);
-	u64 head = PERF_IDX2OFF(handle->head, buf);
+	struct trbe_buf *buf;
+	u64 head;
+
+	if (kunit_get_current_test()) {
+		handle->head += len;
+		handle->size -= len;
+		return;
+	}
+
+	buf = etm_perf_sink_config(handle);
+	head = PERF_IDX2OFF(handle->head, buf);
 
 	__trbe_pad_buf(buf, head, len);
 	if (!buf->snapshot)
@@ -383,9 +313,11 @@ static u64 trbe_min_trace_buf_size(struct perf_output_handle *handle)
  * %%%% - Free area, disabled, trace will not be written
  * ==== - Free area, padded with ETE_IGNORE_PACKET, trace will be skipped
  */
-static unsigned long __trbe_normal_offset(struct perf_output_handle *handle)
+VISIBLE_IF_KUNIT
+unsigned long __trbe_normal_offset(struct perf_output_handle *handle)
 {
-	struct trbe_buf *buf = etm_perf_sink_config(handle);
+	struct trbe_buf *buf =
+		kunit_get_current_test() ? handle->rb : etm_perf_sink_config(handle);
 	struct trbe_cpudata *cpudata = buf->cpudata;
 	const u64 bufsize = buf->nr_pages * PAGE_SIZE;
 	u64 limit = bufsize;
@@ -525,9 +457,13 @@ static unsigned long __trbe_normal_offset(struct perf_output_handle *handle)
 	return 0;
 }
 
-static u64 __trbe_normal_trigger_count(struct perf_output_handle *handle)
+EXPORT_SYMBOL_IF_KUNIT(__trbe_normal_offset);
+
+VISIBLE_IF_KUNIT
+u64 __trbe_normal_trigger_count(struct perf_output_handle *handle)
 {
-	struct trbe_buf *buf = etm_perf_sink_config(handle);
+	struct trbe_buf *buf =
+		kunit_get_current_test() ? handle->rb : etm_perf_sink_config(handle);
 	struct trbe_cpudata *cpudata = buf->cpudata;
 	u64 limit, head, wakeup;
 	u64 count = 0;
@@ -558,6 +494,8 @@ static u64 __trbe_normal_trigger_count(struct perf_output_handle *handle)
 	return count;
 }
 
+EXPORT_SYMBOL_IF_KUNIT(__trbe_normal_trigger_count);
+
 static int trbe_normal_offset(struct perf_output_handle *handle)
 {
 	struct trbe_buf *buf = etm_perf_sink_config(handle);
diff --git a/drivers/hwtracing/coresight/coresight-trbe.h b/drivers/hwtracing/coresight/coresight-trbe.h
index 4c65d164a946ec9860825e7564196745b60d730b..8f90836b5f71d44213699ec1915d59864863a4db 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.h
+++ b/drivers/hwtracing/coresight/coresight-trbe.h
@@ -17,8 +17,91 @@
 #include <linux/platform_device.h>
 #include <linux/smp.h>
 
+#include <kunit/test-bug.h>
+#include <kunit/visibility.h>
+
 #include "coresight-etm-perf.h"
 
+struct trbe_buf {
+	/*
+	 * Even though trbe_base represents vmap()
+	 * mapped allocated buffer's start address,
+	 * it's being as unsigned long for various
+	 * arithmetic and comparision operations &
+	 * also to be consistent with trbe_write &
+	 * trbe_limit sibling pointers.
+	 */
+	unsigned long trbe_base;
+	/* The base programmed into the TRBE */
+	unsigned long trbe_hw_base;
+	unsigned long trbe_limit;
+	unsigned long trbe_write;
+	unsigned long trbe_count;
+	int nr_pages;
+	void **pages;
+	bool snapshot;
+	struct trbe_cpudata *cpudata;
+};
+
+/*
+ * TRBE erratum list
+ *
+ * The errata are defined in arm64 generic cpu_errata framework.
+ * Since the errata work arounds could be applied individually
+ * to the affected CPUs inside the TRBE driver, we need to know if
+ * a given CPU is affected by the erratum. Unlike the other erratum
+ * work arounds, TRBE driver needs to check multiple times during
+ * a trace session. Thus we need a quicker access to per-CPU
+ * errata and not issue costly this_cpu_has_cap() everytime.
+ * We keep a set of the affected errata in trbe_cpudata, per TRBE.
+ *
+ * We rely on the corresponding cpucaps to be defined for a given
+ * TRBE erratum. We map the given cpucap into a TRBE internal number
+ * to make the tracking of the errata lean.
+ *
+ * This helps in :
+ *   - Not duplicating the detection logic
+ *   - Streamlined detection of erratum across the system
+ */
+#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE	0
+#define TRBE_WORKAROUND_WRITE_OUT_OF_RANGE	1
+#define TRBE_NEEDS_DRAIN_AFTER_DISABLE		2
+#define TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE	3
+#define TRBE_IS_BROKEN				4
+
+static int trbe_errata_cpucaps[] = {
+	[TRBE_WORKAROUND_OVERWRITE_FILL_MODE] = ARM64_WORKAROUND_TRBE_OVERWRITE_FILL_MODE,
+	[TRBE_WORKAROUND_WRITE_OUT_OF_RANGE] = ARM64_WORKAROUND_TRBE_WRITE_OUT_OF_RANGE,
+	[TRBE_NEEDS_DRAIN_AFTER_DISABLE] = ARM64_WORKAROUND_2064142,
+	[TRBE_NEEDS_CTXT_SYNC_AFTER_ENABLE] = ARM64_WORKAROUND_2038923,
+	[TRBE_IS_BROKEN] = ARM64_WORKAROUND_1902691,
+	-1,		/* Sentinel, must be the last entry */
+};
+
+/* The total number of listed errata in trbe_errata_cpucaps */
+#define TRBE_ERRATA_MAX			(ARRAY_SIZE(trbe_errata_cpucaps) - 1)
+
+/*
+ * struct trbe_cpudata: TRBE instance specific data
+ * @trbe_flag		- TRBE dirty/access flag support
+ * @trbe_hw_align	- Actual TRBE alignment required for TRBPTR_EL1.
+ * @trbe_align		- Software alignment used for the TRBPTR_EL1.
+ * @cpu			- CPU this TRBE belongs to.
+ * @mode		- Mode of current operation. (perf/disabled)
+ * @drvdata		- TRBE specific drvdata
+ * @errata		- Bit map for the errata on this TRBE.
+ */
+struct trbe_cpudata {
+	bool trbe_flag;
+	u64 trbe_hw_align;
+	u64 trbe_align;
+	int cpu;
+	enum cs_mode mode;
+	struct trbe_buf *buf;
+	struct trbe_drvdata *drvdata;
+	DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
+};
+
 static inline bool is_trbe_available(void)
 {
 	u64 aa64dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1);
@@ -153,3 +236,9 @@ static inline void set_trbe_base_pointer(unsigned long addr)
 	WARN_ON(!IS_ALIGNED(addr, PAGE_SIZE));
 	write_sysreg_s(addr, SYS_TRBBASER_EL1);
 }
+
+#if IS_ENABLED(CONFIG_KUNIT)
+DECLARE_STATIC_KEY_FALSE(trbe_trigger_mode_bypass);
+unsigned long __trbe_normal_offset(struct perf_output_handle *handle);
+u64 __trbe_normal_trigger_count(struct perf_output_handle *handle);
+#endif

-- 
2.34.1