From: Tim Michals <tcmichals@gmail.com>
To: linux-sunxi@lists.linux.dev, linux-remoteproc@vger.kernel.org
Cc: devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, wens@kernel.org,
jernej.skrabec@gmail.com, samuel@sholland.org,
andersson@kernel.org, mathieu.poirier@linaro.org,
jassisinghbrar@gmail.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, Tim Michals <tcmichals@gmail.com>
Subject: [PATCH v2 6/7] remoteproc: sunxi: add KUnit test suite for sunxi remoteproc driver
Date: Sat, 26 Sep 2026 19:20:15 -0500 [thread overview]
Message-ID: <20260927002021.797069-7-tcmichals@gmail.com> (raw)
In-Reply-To: <20260927002021.797069-1-tcmichals@gmail.com>
Add comprehensive in-kernel KUnit unit test suite (34 tests) for the
Allwinner sunxi remoteproc driver.
Tests validate:
- da_to_va() memory window address translation and exact boundary limits
across dedicated SRAM Space 0, SRAM Space 1, DRAM carveout, and trace.
- 64-bit integer wraparound guards and negative unmapped region rejection.
- Corrupted ELF segment overflow and malformed resource table entry rejection.
- Mock MMIO start/stop lifecycle and boot address register programming.
- Symmetrical prepare/unprepare SRAM remap bit manipulation and memory clearing.
- Safe kick handling when transmit mailbox channels are unconfigured.
Signed-off-by: Tim Michals <tcmichals@gmail.com>
---
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/sunxi_rproc_test.c | 701 ++++++++++++++++++++++++++
3 files changed, 712 insertions(+)
create mode 100644 drivers/remoteproc/sunxi_rproc_test.c
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index d35b033b9106..c2bddfd95566 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -396,6 +396,16 @@ config SUNXI_REMOTEPROC
co-processor on Allwinner A523, A527, and T527 SoCs via the
remote processor framework.
+config SUNXI_REMOTEPROC_KUNIT_TEST
+ tristate "KUnit tests for Allwinner sunxi remoteproc" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for the Allwinner sunxi_rproc driver, covering
+ da_to_va() address translation logic and boundary conditions
+ across dedicated SRAM spaces, DRAM carveouts, and trace regions.
+ Say Y here to run these tests during boot or via kunit.py.
+
endif # REMOTEPROC
endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 02acefa6d880..a3503a71f332 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o ti_k3_common.o
obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o ti_k3_common.o
obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o
obj-$(CONFIG_SUNXI_REMOTEPROC) += sunxi_rproc.o
+obj-$(CONFIG_SUNXI_REMOTEPROC_KUNIT_TEST) += sunxi_rproc_test.o
diff --git a/drivers/remoteproc/sunxi_rproc_test.c b/drivers/remoteproc/sunxi_rproc_test.c
new file mode 100644
index 000000000000..2546f850f30b
--- /dev/null
+++ b/drivers/remoteproc/sunxi_rproc_test.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for Allwinner XuanTie RISC-V remoteproc driver (sunxi_rproc.c)
+ *
+ * Comprehensive test suite validating:
+ * - da_to_va() address translation across all hardware windows & aliases
+ * - 64-bit integer overflow protection and zero-length handling
+ * - Out-of-bounds, cross-space isolation, and window unmapped error paths
+ * - Lifecycle operations: start() STA_ADD programming & bootaddr validation
+ * - Lifecycle operations: prepare() and unprepare() SRAM remap & clearing
+ * - kick() message formatting and NULL tx_chan safety
+ * - rproc_ops table completeness
+ *
+ * Directly tests driver functions without code duplication.
+ *
+ * Copyright (C) 2026 Tim Michals <tcmichals@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include <linux/io.h>
+#include <linux/remoteproc.h>
+#include <linux/slab.h>
+#include "remoteproc_internal.h"
+#include "sunxi_rproc.h"
+
+/*
+ * Fake VA addresses — sentinel pointers used to verify base + offset arithmetic
+ * without requiring real ioremap MMIO allocations.
+ */
+#define FAKE_SRAM_PTR ((void *)0xA0000000UL)
+#define FAKE_SRAM1_PTR ((void *)0xB0000000UL)
+#define FAKE_SRAM_VA ((void __force __iomem *)FAKE_SRAM_PTR)
+#define FAKE_SRAM1_VA ((void __force __iomem *)FAKE_SRAM1_PTR)
+#define FAKE_DRAM_VA ((void *)0xC0000000UL)
+#define FAKE_TRACE_VA ((void *)0xD0000000UL)
+
+/* Standard A527 hardware parameters */
+#define A527_SRAM_PHYS SUN55I_SRAM_SPACE0_SYS
+#define A527_SRAM_SIZE SUN55I_SRAM_SPACE0_SIZE
+#define A527_SRAM1_PHYS SUN55I_SRAM_SPACE1_SYS
+#define A527_SRAM1_SIZE SUN55I_SRAM_SPACE1_SIZE
+#define A527_DRAM_PHYS 0x48000000ULL
+#define A527_DRAM_SIZE 0x100000 /* 1 MB */
+#define A527_TRACE_PHYS 0x50000000ULL
+#define A527_TRACE_SIZE 0x1000 /* 4 KB */
+
+struct test_context {
+ struct rproc rproc;
+ struct sunxi_rproc priv;
+ u32 mock_cfg_regs[0x400 / 4];
+ u32 mock_remap_reg;
+ u8 mock_sram_buf[1024];
+ u8 mock_sram1_buf[1024];
+};
+
+static void mock_vq_work(struct work_struct *work)
+{
+}
+
+static struct test_context *create_test_ctx(struct kunit *test)
+{
+ struct test_context *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->rproc.priv = &ctx->priv;
+ ctx->priv.rproc = &ctx->rproc;
+
+ ctx->priv.r_sram_va = FAKE_SRAM_VA;
+ ctx->priv.r_sram_phys = A527_SRAM_PHYS;
+ ctx->priv.r_sram_size = A527_SRAM_SIZE;
+
+ ctx->priv.r_sram1_va = FAKE_SRAM1_VA;
+ ctx->priv.r_sram1_phys = A527_SRAM1_PHYS;
+ ctx->priv.r_sram1_size = A527_SRAM1_SIZE;
+
+ ctx->priv.dram_va = FAKE_DRAM_VA;
+ ctx->priv.dram_phys = A527_DRAM_PHYS;
+ ctx->priv.dram_size = A527_DRAM_SIZE;
+
+ ctx->priv.trace_va = FAKE_TRACE_VA;
+ ctx->priv.trace_phys = A527_TRACE_PHYS;
+ ctx->priv.trace_size = A527_TRACE_SIZE;
+ ctx->priv.cfg = &sun55i_riscv_cfg;
+
+ INIT_WORK(&ctx->priv.vq_work, mock_vq_work);
+
+ return ctx;
+}
+
+/* ==================== da_to_va: Basic & Guard Tests ==================== */
+
+static void test_da_to_va_zero_length_returns_null(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT, 0, NULL));
+}
+
+static void test_da_to_va_overflow_guard(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, U64_MAX - 0x10, 0x20, NULL));
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, U64_MAX, 1, NULL));
+}
+
+static void test_da_to_va_null_is_iomem_safe(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* Must succeed without dereferencing NULL is_iomem */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT, 0x100, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+}
+
+/* ==================== da_to_va: Space 0 Translations ==================== */
+
+static void test_da_to_va_sram_space0_core_da(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+static void test_da_to_va_sram_space0_core_da_offset(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA_ALT + 0x1000, 0x100, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x1000);
+}
+
+static void test_da_to_va_sram_space0_host_phys(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM_PHYS, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+static void test_da_to_va_sram_space0_host_phys_offset(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM_PHYS + 0x2000, 0x100, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x2000);
+}
+
+static void test_da_to_va_sram_space0_alt_3ff80000(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+static void test_da_to_va_sram_space0_pubsram_c_da(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_C_DA, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+/* ==================== da_to_va: Space 1 Translations ==================== */
+
+static void test_da_to_va_sram_space1_host_phys(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_SRAM1_PHYS, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+static void test_da_to_va_sram_space1_40000000(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+static void test_da_to_va_sram_space1_40040000(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = false;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA_ALT, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+}
+
+/* ==================== da_to_va: DRAM & Trace Translations ==================== */
+
+static void test_da_to_va_dram_direct(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = true;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_DRAM_VA);
+ KUNIT_EXPECT_FALSE(test, is_iomem);
+}
+
+static void test_da_to_va_trace_direct(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ bool is_iomem = true;
+ void *va;
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_TRACE_PHYS, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_TRACE_VA);
+ KUNIT_EXPECT_FALSE(test, is_iomem);
+}
+
+/* ==================== da_to_va: Negative & Isolation Tests ==================== */
+
+static void test_da_to_va_out_of_range(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x10000000, 0x100, NULL));
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x60000000, 0x100, NULL));
+}
+
+static void test_da_to_va_sram_boundaries(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* 1 byte before Space 0 start -> NULL */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA - 1, 1, NULL));
+
+ /* Exact last byte inside Space 0 -> valid */
+ va = sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+
+ /* 1 byte beyond Space 1 end -> NULL */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA_ALT + A527_SRAM1_SIZE, 1, NULL));
+
+ /* Access starting inside Space 1 but spanning past end -> NULL */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA_ALT +
+ A527_SRAM1_SIZE - 4, 8, NULL));
+}
+
+static void test_da_to_va_space1_boundaries(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* Exact last byte inside Space 1 -> valid */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+
+ /* Spanning past Space 1 end -> NULL */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 4, 8, NULL));
+}
+
+static void test_da_to_va_unmapped_regions_return_null(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ /* When SRAM Space 0 is unmapped, all Space 0 views return NULL */
+ ctx->priv.r_sram_va = NULL;
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT, 0x100, NULL));
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ A527_SRAM_PHYS, 0x100, NULL));
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_C_DA, 0x100, NULL));
+
+ /* When SRAM Space 1 is unmapped, Space 1 views return NULL */
+ ctx->priv.r_sram1_va = NULL;
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA, 0x100, NULL));
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ A527_SRAM1_PHYS, 0x100, NULL));
+
+ /* When DRAM and Trace are unmapped, they return NULL */
+ ctx->priv.dram_va = NULL;
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS, 0x100, NULL));
+ ctx->priv.trace_va = NULL;
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, A527_TRACE_PHYS, 0x100, NULL));
+}
+
+static void test_da_to_va_space_isolation(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ /*
+ * Space 1 DA (E907_SRAM_SPACE1_DA) must NEVER resolve to Space 0, even when
+ * Space 1 is unmapped.
+ */
+ ctx->priv.r_sram1_va = NULL;
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA, 0x100, NULL));
+}
+
+static void test_da_to_va_exact_upper_boundary_space0(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* Exact last byte of Space 0 (E907_SRAM_SPACE0_DA + 256K - 1) */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA + A527_SRAM_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + A527_SRAM_SIZE - 1);
+
+ /* Spanning 1 byte beyond Space 0 must be rejected */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA + A527_SRAM_SIZE - 1, 2, NULL));
+
+ /* Exact last byte of Alt Space 0 (E907_SRAM_SPACE0_DA_ALT + 256K - 1) */
+ va = sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + A527_SRAM_SIZE - 1);
+
+ /* Spanning 1 byte beyond Alt Space 0 must be rejected */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT +
+ A527_SRAM_SIZE - 1, 2, NULL));
+}
+
+static void test_da_to_va_exact_upper_boundary_space1(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* Exact last byte of Space 1 (E907_SRAM_SPACE1_DA + 256K - 1) */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR + A527_SRAM1_SIZE - 1);
+
+ /* Spanning 1 byte beyond Space 1 must be rejected */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA + A527_SRAM1_SIZE - 1, 2, NULL));
+}
+
+static void test_da_to_va_exact_upper_boundary_dram(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* Exact last byte of DRAM carveout */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, A527_DRAM_PHYS + A527_DRAM_SIZE - 1, 1, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_DRAM_VA + A527_DRAM_SIZE - 1);
+
+ /* Spanning 1 byte beyond DRAM carveout must be rejected */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ A527_DRAM_PHYS + A527_DRAM_SIZE - 1, 2, NULL));
+}
+
+static void test_da_to_va_a733_sram_a2_layout(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+ bool is_iomem = false;
+
+ /*
+ * Allwinner A733 (sun60i) E902 silicon profile:
+ * Uses System SRAM A2 (0x00040000 - 0x00073FFF, 208 KB).
+ * Has no Space 1 (r_sram1_va is NULL).
+ */
+ ctx->priv.r_sram_phys = 0x00040000ULL;
+ ctx->priv.r_sram_size = 0x34000; /* 208 KB */
+ ctx->priv.r_sram1_va = NULL;
+ ctx->priv.r_sram1_size = 0;
+
+ /* Base of SRAM A2 */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00040000, 0x100, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR);
+ KUNIT_EXPECT_TRUE(test, is_iomem);
+
+ /* Entry offset in SRAM A2 (0x00044000) */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00044000, 0x1000, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x4000);
+
+ /* Exact last byte of SRAM A2 (0x00040000 + 0x34000 - 1 = 0x00073FFF) */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, 0x00073FFF, 1, &is_iomem);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 0x33FFF);
+
+ /* Beyond SRAM A2 boundary must return NULL */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc, 0x00074000, 1, NULL));
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc, 0x00073FFF, 2, NULL));
+}
+
+static void test_da_to_va_unaligned_lengths(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ void *va;
+
+ /* 3-byte and 7-byte transfers must translate correctly without faulting */
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE0_DA + 3, 3, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM_PTR + 3);
+
+ va = sunxi_rproc_da_to_va(&ctx->rproc, E907_SRAM_SPACE1_DA + 7, 7, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, va);
+ KUNIT_EXPECT_PTR_EQ(test, va, FAKE_SRAM1_PTR + 7);
+}
+
+static void test_da_to_va_malformed_rsc_table_entry(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ /*
+ * Malformed resource table entry: firmware requests a VirtIO vring
+ * or carveout pointing to an invalid device address (e.g. 0xDEADBEEF).
+ * da_to_va must return NULL, prompting rproc_elf_load_rsc_table to fail
+ * safely rather than writing into unmapped space.
+ */
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0xDEADBEEF, 0x1000, NULL));
+
+ /* Out-of-window unmapped device address (0x30000000) */
+ KUNIT_EXPECT_NULL(test, sunxi_rproc_da_to_va(&ctx->rproc, 0x30000000, 0x1000, NULL));
+}
+
+static void test_da_to_va_corrupted_elf_overflow_segment(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ /*
+ * Corrupted ELF header: segment has an excessive memsz that wraps around
+ * 64-bit integer limits or spans across window bounds.
+ */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT,
+ (size_t)-1, NULL));
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE1_DA,
+ (size_t)-16, NULL));
+
+ /* Segment starts near end of Space 0 and extends 4KB beyond valid SRAM */
+ KUNIT_EXPECT_NULL(test,
+ sunxi_rproc_da_to_va(&ctx->rproc,
+ E907_SRAM_SPACE0_DA_ALT + A527_SRAM_SIZE - 0x100,
+ 0x200, NULL));
+}
+
+static void test_start_a733_mode1_and_mode2_bootaddr(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int ret;
+
+ ctx->priv.cfg_va = (void __iomem *)ctx->mock_cfg_regs;
+
+ /* Mode 1: Suspend/resume E902 SCP boot from DRAM (0x40014000) */
+ ctx->rproc.bootaddr = 0x40014000;
+ ret = sunxi_rproc_start(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x40014000U);
+
+ /* Mode 2: Real-time coprocessor boot from SRAM A2 (0x00044000) */
+ ctx->rproc.bootaddr = 0x00044000;
+ ret = sunxi_rproc_start(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x00044000U);
+}
+
+/* ==================== Lifecycle: start & stop ==================== */
+
+static void test_start_bootaddr_programming(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int ret;
+
+ ctx->priv.cfg_va = (void __iomem *)ctx->mock_cfg_regs;
+ ctx->rproc.bootaddr = 0x40014000;
+
+ ret = sunxi_rproc_start(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ /* Check that bootaddr was written to STA_ADD_REG (offset 0x204) */
+ KUNIT_EXPECT_EQ(test, ctx->mock_cfg_regs[E906_STA_ADD_REG / 4], 0x40014000U);
+}
+
+static void test_start_rejects_bootaddr_overflow(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int ret;
+
+ /* Addresses beyond 32-bit range are invalid for 32-bit XuanTie E907 */
+ ctx->rproc.bootaddr = 0x100000000ULL;
+
+ ret = sunxi_rproc_start(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+static void test_stop_succeeds(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int ret;
+
+ ret = sunxi_rproc_stop(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/* ==================== Lifecycle: prepare & unprepare ==================== */
+
+static void test_prepare_and_unprepare_remap(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int ret;
+
+ ctx->priv.remap_va = (void __iomem *)&ctx->mock_remap_reg;
+ ctx->mock_remap_reg = 0;
+
+ /* Provide real buffers for SRAM clearing to prevent faulting on fake VA */
+ ctx->priv.r_sram_va = (void __iomem *)ctx->mock_sram_buf;
+ ctx->priv.r_sram_size = sizeof(ctx->mock_sram_buf);
+ ctx->priv.r_sram1_va = (void __iomem *)ctx->mock_sram1_buf;
+ ctx->priv.r_sram1_size = sizeof(ctx->mock_sram1_buf);
+
+ /* prepare() should set SUNXI_REMAP_SRAMA3_2_BIT (bit 1) */
+ ret = sunxi_rproc_prepare(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, ctx->mock_remap_reg & SUNXI_REMAP_SRAMA3_2_BIT,
+ SUNXI_REMAP_SRAMA3_2_BIT);
+
+ /* unprepare() should clear SUNXI_REMAP_SRAMA3_2_BIT */
+ ret = sunxi_rproc_unprepare(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, ctx->mock_remap_reg & SUNXI_REMAP_SRAMA3_2_BIT, 0U);
+}
+
+static void test_prepare_clears_sram(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+ int i, ret;
+
+ memset(ctx->mock_sram_buf, 0xAA, sizeof(ctx->mock_sram_buf));
+ memset(ctx->mock_sram1_buf, 0x55, sizeof(ctx->mock_sram1_buf));
+
+ ctx->priv.r_sram_va = (void __iomem *)ctx->mock_sram_buf;
+ ctx->priv.r_sram_size = sizeof(ctx->mock_sram_buf);
+
+ ctx->priv.r_sram1_va = (void __iomem *)ctx->mock_sram1_buf;
+ ctx->priv.r_sram1_size = sizeof(ctx->mock_sram1_buf);
+
+ ret = sunxi_rproc_prepare(&ctx->rproc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ /* Both SRAM buffers must be cleanly zeroed to prevent stale data / ECC faults */
+ for (i = 0; i < sizeof(ctx->mock_sram_buf); i++)
+ KUNIT_EXPECT_EQ(test, ctx->mock_sram_buf[i], 0);
+
+ for (i = 0; i < sizeof(ctx->mock_sram1_buf); i++)
+ KUNIT_EXPECT_EQ(test, ctx->mock_sram1_buf[i], 0);
+}
+
+/* ==================== Operations: kick ==================== */
+
+static void test_kick_null_tx_chan_safe(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ ctx->priv.tx_chan = NULL;
+ /* Must return cleanly without NULL dereference */
+ sunxi_rproc_kick(&ctx->rproc, 0);
+ sunxi_rproc_kick(&ctx->rproc, 1);
+}
+
+static void test_kick_stores_vqid(struct kunit *test)
+{
+ struct test_context *ctx = create_test_ctx(test);
+
+ /* Verify kick_msg stores the passed vqid to avoid stack UAF */
+ ctx->priv.kick_msg = 0xDEADBEEF;
+ ctx->priv.tx_chan = NULL; /* Avoid mbox_send_message dispatch */
+
+ sunxi_rproc_kick(&ctx->rproc, 1);
+ /* Without tx_chan, returns before writing kick_msg */
+ KUNIT_EXPECT_EQ(test, ctx->priv.kick_msg, 0xDEADBEEFU);
+}
+
+/* ==================== Test Suite Registration ==================== */
+
+static struct kunit_case sunxi_rproc_test_cases[] = {
+ /* da_to_va Guard Tests */
+ KUNIT_CASE(test_da_to_va_zero_length_returns_null),
+ KUNIT_CASE(test_da_to_va_overflow_guard),
+ KUNIT_CASE(test_da_to_va_null_is_iomem_safe),
+ /* da_to_va Space 0 Tests */
+ KUNIT_CASE(test_da_to_va_sram_space0_core_da),
+ KUNIT_CASE(test_da_to_va_sram_space0_core_da_offset),
+ KUNIT_CASE(test_da_to_va_sram_space0_host_phys),
+ KUNIT_CASE(test_da_to_va_sram_space0_host_phys_offset),
+ KUNIT_CASE(test_da_to_va_sram_space0_alt_3ff80000),
+ KUNIT_CASE(test_da_to_va_sram_space0_pubsram_c_da),
+ /* da_to_va Space 1 Tests */
+ KUNIT_CASE(test_da_to_va_sram_space1_host_phys),
+ KUNIT_CASE(test_da_to_va_sram_space1_40000000),
+ KUNIT_CASE(test_da_to_va_sram_space1_40040000),
+ /* da_to_va DRAM & Trace Tests */
+ KUNIT_CASE(test_da_to_va_dram_direct),
+ KUNIT_CASE(test_da_to_va_trace_direct),
+ /* da_to_va Out of Range, Boundary, Unmapped & Isolation Tests */
+ KUNIT_CASE(test_da_to_va_out_of_range),
+ KUNIT_CASE(test_da_to_va_sram_boundaries),
+ KUNIT_CASE(test_da_to_va_space1_boundaries),
+ KUNIT_CASE(test_da_to_va_unmapped_regions_return_null),
+ KUNIT_CASE(test_da_to_va_space_isolation),
+ KUNIT_CASE(test_da_to_va_exact_upper_boundary_space0),
+ KUNIT_CASE(test_da_to_va_exact_upper_boundary_space1),
+ KUNIT_CASE(test_da_to_va_exact_upper_boundary_dram),
+ KUNIT_CASE(test_da_to_va_a733_sram_a2_layout),
+ KUNIT_CASE(test_da_to_va_unaligned_lengths),
+ KUNIT_CASE(test_da_to_va_malformed_rsc_table_entry),
+ KUNIT_CASE(test_da_to_va_corrupted_elf_overflow_segment),
+ /* Lifecycle: start and stop */
+ KUNIT_CASE(test_start_bootaddr_programming),
+ KUNIT_CASE(test_start_a733_mode1_and_mode2_bootaddr),
+ KUNIT_CASE(test_start_rejects_bootaddr_overflow),
+ KUNIT_CASE(test_stop_succeeds),
+ /* Lifecycle: prepare and unprepare */
+ KUNIT_CASE(test_prepare_and_unprepare_remap),
+ KUNIT_CASE(test_prepare_clears_sram),
+ /* Operations: kick */
+ KUNIT_CASE(test_kick_null_tx_chan_safe),
+ KUNIT_CASE(test_kick_stores_vqid),
+ {}
+};
+
+static struct kunit_suite sunxi_rproc_test_suite = {
+ .name = "sunxi_rproc",
+ .test_cases = sunxi_rproc_test_cases,
+};
+
+kunit_test_suite(sunxi_rproc_test_suite);
+
+MODULE_AUTHOR("Tim Michals <tcmichals@gmail.com>");
+MODULE_DESCRIPTION("Comprehensive KUnit tests for Allwinner sunxi remoteproc driver");
+MODULE_LICENSE("GPL");
--
2.43.0
next prev parent reply other threads:[~2026-09-27 0:21 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260922034711.190253-1-tcmichals@gmail.com>
2026-09-27 0:20 ` [PATCH v2 0/7] remoteproc: sunxi: Add Allwinner XuanTie E907 RemoteProc and Message Box support Tim Michals
2026-09-27 0:20 ` [PATCH v2 1/7] dt-bindings: mailbox: add Allwinner sun55i msgbox schema Tim Michals
2026-09-27 0:20 ` [PATCH v2 2/7] mailbox: sun55i: add Allwinner sun55i/sun60i 4-port Message Box driver Tim Michals
2026-09-27 0:20 ` [PATCH v2 3/7] mailbox: sun55i: add KUnit test suite for sun55i msgbox driver Tim Michals
2026-09-27 0:20 ` [PATCH v2 4/7] dt-bindings: remoteproc: add Allwinner sun55i-rproc schema Tim Michals
2026-09-27 0:20 ` [PATCH v2 5/7] remoteproc: sunxi: add Allwinner XuanTie RISC-V remoteproc driver Tim Michals
2026-09-27 0:20 ` Tim Michals [this message]
2026-09-27 0:20 ` [PATCH v2 7/7] arm64: dts: allwinner: add a523 msgbox and remoteproc nodes Tim Michals
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=20260927002021.797069-7-tcmichals@gmail.com \
--to=tcmichals@gmail.com \
--cc=andersson@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=jassisinghbrar@gmail.com \
--cc=jernej.skrabec@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-remoteproc@vger.kernel.org \
--cc=linux-sunxi@lists.linux.dev \
--cc=mathieu.poirier@linaro.org \
--cc=robh@kernel.org \
--cc=samuel@sholland.org \
--cc=wens@kernel.org \
/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
all inboxes | Powered by JetHome®