Re: [RFC PATCH] remoteproc: core: add KUnit tests for rproc_check_carveout_da()

From: Mathieu Poirier

Date: Tue Oct 13 2026 - 12:40:29 EST


On Sun, 27 Sept 2026 at 11:24, Sailesh Nandanavanam
<saileshnandanavanam@xxxxxxxxx> wrote:
>
> Add a KUnit test suite covering rproc_check_carveout_da(), the
> internal helper that validates a resource-table-requested (da, len)
> pair against an already-allocated carveout's bounds.
>
> The test is #include'd directly into remoteproc_core.c (a pattern
> already used by mm/damon/) rather than built as a separate module,
> since rproc_check_carveout_da() is static and this avoids adding an
> exported symbol purely for test visibility.
>
> Six cases are included, exercising every branch of the function:
> requested length exceeding the carveout, a specific da requested
> against a carveout with none set, a da before the carveout's start,
> a (da + len) past the carveout's end, an exact boundary fit, and the
> FW_RSC_ADDR_ANY skip-check path.
>

I will look at this patch when I have reviewed other patches in my backlog.

Thanks,
Mathieu

> Verified via:
> - QEMU (x86_64), booting a kernel with CONFIG_REMOTEPROC_KUNIT_TEST=y
> - tools/testing/kunit/kunit.py under UML
>
> All 6 cases pass in both environments:
>
> # remoteproc_core: pass:6 fail:0 skip:0 total:6
>
> This is posted as an RFC to check whether this structure (test
> headers embedded per-file, DAMON-style) is the preferred approach for
> remoteproc before expanding coverage to other files in the driver
> (remoteproc_virtio.c, remoteproc_sysfs.c, etc.).
>
> Signed-off-by: Sailesh Nandanavanam <saileshnandanavanam@xxxxxxxxx>
> ---
> drivers/remoteproc/Kconfig | 10 ++
> drivers/remoteproc/remoteproc_core.c | 4 +
> .../remoteproc/tests/remoteproc_core-kunit.h | 130 ++++++++++++++++++
> 3 files changed, 144 insertions(+)
> create mode 100644 drivers/remoteproc/tests/remoteproc_core-kunit.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 5b56b2dcc725..dc5a8f483693 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -390,6 +390,16 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +config REMOTEPROC_KUNIT_TEST
> + bool "KUnit tests for remoteproc core" if !KUNIT_ALL_TESTS
> + depends on REMOTEPROC && KUNIT=y
> + default KUNIT_ALL_TESTS
> + help
> + Enable KUnit tests for the remoteproc core, covering internal
> + helpers such as carveout/address-translation logic.
> +
> + If unsure, say N.
> +
> endif # REMOTEPROC
>
> endmenu
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index 1ed406714849..4f0ad28305d2 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -2839,3 +2839,7 @@ static void __exit remoteproc_exit(void)
> module_exit(remoteproc_exit);
>
> MODULE_DESCRIPTION("Generic Remote Processor Framework");
> +
> +#if IS_ENABLED(CONFIG_REMOTEPROC_KUNIT_TEST)
> +#include "tests/remoteproc_core-kunit.h"
> +#endif
> diff --git a/drivers/remoteproc/tests/remoteproc_core-kunit.h b/drivers/remoteproc/tests/remoteproc_core-kunit.h
> new file mode 100644
> index 000000000000..615a854fffbf
> --- /dev/null
> +++ b/drivers/remoteproc/tests/remoteproc_core-kunit.h
> @@ -0,0 +1,130 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _REMOTEPROC_CORE_KUNIT_H
> +#define _REMOTEPROC_CORE_KUNIT_H
> +
> +#include <kunit/test.h>
> +
> +/*
> + * Tests for rproc_check_carveout_da().
> + *
> + * The function is static, so this header is #include'd directly at the
> + * bottom of remoteproc_core.c (see CONFIG_REMOTEPROC_KUNIT_TEST), giving
> + * it access to the symbol despite the lack of external linkage.
> + *
> + * rproc_check_carveout_da() only touches &rproc->dev for dev_err() on
> + * its failure paths, so a zeroed struct rproc is sufficient here -- no
> + * device registration needed. dev_err()'s formatting chain
> + * (dev_driver_string() -> "" on NULL driver/bus/class, dev_name() ->
> + * NULL on an unregistered device, and the kernel's own %s NULL guard
> + * in lib/vsprintf.c) is safe end-to-end against a bare zeroed device.
> + */
> +
> +static void rproc_test_check_carveout_len_exceeds(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /* mem->len = 0x100; request len 0x200 with no da constraint */
> + mem.da = FW_RSC_ADDR_ANY;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, FW_RSC_ADDR_ANY, 0x200);
> + KUNIT_EXPECT_EQ(test, ret, -EINVAL);
> +}
> +
> +static void rproc_test_check_carveout_da_any_mismatch(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /* caller wants a specific da, but the carveout itself has none set */
> + mem.da = FW_RSC_ADDR_ANY;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, 0x1000, 0x50);
> + KUNIT_EXPECT_EQ(test, ret, -EINVAL);
> +}
> +
> +static void rproc_test_check_carveout_da_negative_delta(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /* requested da (0x1000) lies before the carveout's da (0x2000) */
> + mem.da = 0x2000;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, 0x1000, 0x10);
> + KUNIT_EXPECT_EQ(test, ret, -EINVAL);
> +}
> +
> +static void rproc_test_check_carveout_da_exceeds_end(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /* delta (0x80) + len (0x100) = 0x180, one byte over mem->len (0x100) -> 0x180 > 0x100 */
> + mem.da = 0x1000;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, 0x1080, 0x100);
> + KUNIT_EXPECT_EQ(test, ret, -EINVAL);
> +}
> +
> +static void rproc_test_check_carveout_da_exact_fit(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /*
> + * Boundary case: delta (0xf0) + len (0x10) == mem->len (0x100)
> + * exactly. The check is "> mem->len", so this must pass -- this
> + * is exactly the kind of edge an off-by-one would break.
> + */
> + mem.da = 0x1000;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, 0x10f0, 0x10);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +}
> +
> +static void rproc_test_check_carveout_da_any_skip_check(struct kunit *test)
> +{
> + struct rproc rproc = { 0 };
> + struct rproc_mem_entry mem = { 0 };
> + int ret;
> +
> + /*
> + * da == FW_RSC_ADDR_ANY -> address check is skipped entirely,
> + * only len is validated against mem->len.
> + */
> + mem.da = 0x1000;
> + mem.len = 0x100;
> +
> + ret = rproc_check_carveout_da(&rproc, &mem, FW_RSC_ADDR_ANY, 0x50);
> + KUNIT_EXPECT_EQ(test, ret, 0);
> +}
> +
> +static struct kunit_case rproc_core_test_cases[] = {
> + KUNIT_CASE(rproc_test_check_carveout_len_exceeds),
> + KUNIT_CASE(rproc_test_check_carveout_da_any_mismatch),
> + KUNIT_CASE(rproc_test_check_carveout_da_negative_delta),
> + KUNIT_CASE(rproc_test_check_carveout_da_exceeds_end),
> + KUNIT_CASE(rproc_test_check_carveout_da_exact_fit),
> + KUNIT_CASE(rproc_test_check_carveout_da_any_skip_check),
> + {}
> +};
> +
> +static struct kunit_suite rproc_core_test_suite = {
> + .name = "remoteproc_core",
> + .test_cases = rproc_core_test_cases,
> +};
> +
> +kunit_test_suite(rproc_core_test_suite);
> +
> +#endif /* _REMOTEPROC_CORE_KUNIT_H */
> --
> 2.34.1
>