[PATCH 10/11] test: self: add MMU remapping self test

Ahmad Fatoum a.fatoum at pengutronix.de
Sun May 21 22:28:34 PDT 2023


ARM mmu code in barebox has recently seen some changes to enhance
remapping. Add a self test that exercises this a bit.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 test/self/Kconfig  |   6 ++
 test/self/Makefile |   1 +
 test/self/mmu.c    | 233 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 240 insertions(+)
 create mode 100644 test/self/mmu.c

diff --git a/test/self/Kconfig b/test/self/Kconfig
index ce5048c70ec9..c130209748aa 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -36,6 +36,7 @@ config SELFTEST_ENABLE_ALL
 	imply SELFTEST_FS_RAMFS
 	imply SELFTEST_TFTP
 	imply SELFTEST_JSON
+	imply SELFTEST_MMU
 	help
 	  Selects all self-tests compatible with current configuration
 
@@ -69,4 +70,9 @@ config SELFTEST_JSON
 	bool "JSON selftest"
 	depends on JSMN
 
+config SELFTEST_MMU
+	bool "MMU remapping selftest"
+	select MEMTEST
+	depends on MMU
+
 endif
diff --git a/test/self/Makefile b/test/self/Makefile
index 98ebd1fd66c1..8c816c4299f6 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SELFTEST_OF_MANIPULATION) += of_manipulation.o of_manipulation.dtb.
 obj-$(CONFIG_SELFTEST_ENVIRONMENT_VARIABLES) += envvar.o
 obj-$(CONFIG_SELFTEST_FS_RAMFS) += ramfs.o
 obj-$(CONFIG_SELFTEST_JSON) += json.o
+obj-$(CONFIG_SELFTEST_MMU) += mmu.o
 
 clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z
 clean-files += *.dtbo *.dtbo.S .*.dtso
diff --git a/test/self/mmu.c b/test/self/mmu.c
new file mode 100644
index 000000000000..ee6c1cd45e7e
--- /dev/null
+++ b/test/self/mmu.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-onl
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <mmu.h>
+#include <memtest.h>
+#include <abort.h>
+#include <zero_page.h>
+#include <linux/sizes.h>
+
+BSELFTEST_GLOBALS();
+
+#define __expect(ret, cond, fmt, ...) do { \
+	bool __cond = (cond); \
+	int __ret = (ret); \
+	total_tests++; \
+	\
+	if (!__cond) { \
+		failed_tests++; \
+		printf("%s:%d error %pe: " fmt "\n", \
+		       __func__, __LINE__, ERR_PTR(__ret), ##__VA_ARGS__); \
+	} \
+} while (0)
+
+#define expect_success(ret, ...) __expect((ret), ((ret) >= 0), __VA_ARGS__)
+
+static void memtest(void __iomem *start, size_t size, const char *desc)
+{
+	int ret;
+
+	ret = mem_test_bus_integrity((resource_size_t)start,
+				     (resource_size_t)start + size - 1, 0);
+	expect_success(ret, "%s bus test", desc);
+
+	ret = mem_test_moving_inversions((resource_size_t)start,
+					 (resource_size_t)start + size - 1, 0);
+	expect_success(ret, "%s moving inverstions test", desc);
+}
+
+static inline int __check_mirroring(void __iomem *a, void __iomem *b, bool is_mirror,
+				    const char *func, int line)
+{
+	if ((readl(a) == readl(b)) == (is_mirror))
+		return 0;
+
+	printf("%s:%d: mirroring unexpectedly %s: (*%p = 0x%x) %s (*%p = 0x%x)\n", func, line,
+	       is_mirror ? "failed" : "succeeded",
+	       a, readl(a), is_mirror ? "!=" : "==", b, readl(b));
+
+	mmuinfo(a);
+	mmuinfo(b);
+
+	return -EILSEQ;
+}
+
+#define check_mirroring(a, b, is_mirror) \
+	__check_mirroring((a), (b), (is_mirror), __func__, __LINE__)
+
+static void test_remap(void)
+{
+	u8 __iomem *buffer = NULL, *mirror = NULL;
+	phys_addr_t buffer_phys;
+	int i, ret;
+
+	buffer = memalign(SZ_2M, SZ_8M);
+	if (WARN_ON(!buffer))
+		goto out;
+
+	buffer_phys = virt_to_phys(buffer);
+
+	mirror = memalign(SZ_2M, SZ_8M);
+	if (WARN_ON(!mirror))
+		goto out;
+
+	pr_debug("allocated buffer = 0x%p, mirror = 0x%p\n", buffer, mirror);
+
+	memtest(buffer, SZ_8M, "cached buffer");
+	memtest(mirror, SZ_8M, "cached mirror");
+
+	if (__is_defined(ARCH_HAS_REMAP)) {
+		skipped_tests += 10;
+		goto out;
+	}
+
+	ret = remap_range(buffer, SZ_8M, MAP_UNCACHED);
+	memtest(buffer, SZ_8M, "uncached buffer");
+
+	ret = remap_range(mirror, SZ_8M, MAP_UNCACHED);
+	memtest(mirror, SZ_8M, "uncached mirror");
+
+	for (i = 0; i < SZ_8M; i += sizeof(u32)) {
+		int m = i, b = i;
+		writel(0xDEADBEEF, &mirror[m]);
+		writel(i, &buffer[b]);
+		ret = check_mirroring(&mirror[m], &buffer[b], false);
+		if (ret)
+			break;
+	}
+
+	expect_success(ret, "asserting no mirror before remap");
+
+	ret = arch_remap_range(mirror, buffer_phys, SZ_8M, MAP_UNCACHED);
+	expect_success(ret, "remapping with mirroring");
+
+	for (i = 0; i < SZ_8M; i += sizeof(u32)) {
+		int m = i, b = i;
+		writel(0xDEADBEEF, &mirror[m]);
+		writel(i, &buffer[b]);
+		ret = check_mirroring(&mirror[m], &buffer[b], true);
+		if (ret)
+			break;
+	}
+
+	expect_success(ret, "asserting mirroring after remap");
+
+	ret = arch_remap_range(mirror, buffer_phys + SZ_4K, SZ_4M, MAP_UNCACHED);
+	expect_success(ret, "remapping with mirroring (phys += 4K)");
+
+	for (i = 0; i < SZ_4M; i += sizeof(u32)) {
+		int m = i, b = i + SZ_4K;
+		writel(0xDEADBEEF, &mirror[m]);
+		writel(i, &buffer[b]);
+		ret = check_mirroring(&mirror[m], &buffer[b], true);
+		if (ret)
+			break;
+	}
+
+	expect_success(ret, "asserting mirroring after remap (phys += 4K)");
+
+	ret = arch_remap_range(mirror + SZ_4K, buffer_phys, SZ_4M, MAP_UNCACHED);
+	expect_success(ret, "remapping with mirroring (virt += 4K)");
+
+	for (i = 0; i < SZ_4M; i += sizeof(u32)) {
+		int m = i + SZ_4K, b = i;
+		writel(0xDEADBEEF, &mirror[m]);
+		writel(i, &buffer[b]);
+		ret = check_mirroring(&mirror[m], &buffer[b], true);
+		if (ret)
+			break;
+	}
+
+	expect_success(ret, "asserting mirroring after remap (virt += 4K)");
+	return;
+
+	ret = remap_range(buffer, SZ_8M, MAP_DEFAULT);
+	expect_success(ret, "remapping buffer with default attrs");
+	memtest(buffer, SZ_8M, "newly cached buffer");
+
+	ret = remap_range(mirror, SZ_8M, MAP_DEFAULT);
+	expect_success(ret, "remapping mirror with default attrs");
+	memtest(mirror, SZ_8M, "newly cached mirror");
+
+	for (i = 0; i < SZ_8M; i += sizeof(u32)) {
+		int m = i, b = i;
+		writel(0xDEADBEEF, &mirror[m]);
+		writel(i, &buffer[b]);
+		ret = check_mirroring(&mirror[m], &buffer[b], false);
+		if (ret)
+			break;
+	}
+
+	expect_success(ret, "asserting no mirror after remap restore");
+out:
+	free(buffer);
+	free(mirror);
+}
+
+static void test_zero_page(void)
+{
+	void __iomem *null = NULL;
+
+	total_tests += 3;
+
+	if (!IS_ENABLED(CONFIG_ARCH_HAS_DATA_ABORT_MASK)) {
+		pr_info("skipping %s because %s=n\n",
+			"CONFIG_ARCH_HAS_DATA_ABORT_MASK", __func__);
+		skipped_tests += 3;
+		return;
+	}
+
+	OPTIMIZER_HIDE_VAR(null);
+
+	/* Check if *NULL traps and data_abort_mask works */
+
+	data_abort_mask();
+
+	(void)readl(null);
+
+	if (!data_abort_unmask()) {
+		printf("%s: NULL pointer access did not trap\n", __func__);
+		failed_tests++;
+	}
+
+	if (!IS_ENABLED(CONFIG_ARCH_HAS_ZERO_PAGE)) {
+		pr_info("skipping %s because %s=n\n",
+			"CONFIG_ARCH_HAS_ZERO_PAGE", __func__);
+		skipped_tests += 2;
+		return;
+	}
+
+	/* Check if zero_page_access() works */
+
+	data_abort_mask();
+
+	zero_page_access();
+	(void)readl(null);
+	zero_page_faulting();
+
+	if (data_abort_unmask()) {
+		printf("%s: unexpected fault on zero page access\n", __func__);
+		failed_tests++;
+	}
+
+	/* Check if zero_page_faulting() works */
+
+	data_abort_mask();
+
+	(void)readl(null);
+
+	if (!data_abort_unmask()) {
+		printf("%s NULL pointer access did not trap\n", __func__);
+		failed_tests++;
+	}
+}
+
+static void test_mmu(void)
+{
+	test_zero_page();
+	test_remap();
+}
+bselftest(core, test_mmu);
-- 
2.39.2




More information about the barebox mailing list