* [kvm-unit-tests PATCH] riscv: Add Zicboz CBO.ZERO tests
@ 2026-08-08 5:50 Sainath Grandhi
0 siblings, 0 replies; only message in thread
From: Sainath Grandhi @ 2026-08-08 5:50 UTC (permalink / raw)
To: kvm; +Cc: kvm-riscv, Andrew Jones, Sainath Grandhi
Add a test for CBO.ZERO when Zicboz is advertised. Read the cache-block
size from riscv,cboz-block-size and allocate three aligned blocks.
Execute CBO.ZERO at multiple offsets in the middle block and verify that
the containing block is zeroed while the adjacent blocks remain
unchanged.
Skip the test when Zicboz is unavailable.
Signed-off-by: Sainath Grandhi <sainathg@systemssoftware.dev>
---
riscv/Makefile | 1 +
riscv/isa-zicboz.c | 168 ++++++++++++++++++++++++++++++++++++++++++++
riscv/unittests.cfg | 4 ++
3 files changed, 173 insertions(+)
create mode 100644 riscv/isa-zicboz.c
diff --git a/riscv/Makefile b/riscv/Makefile
index 348a30a7..f4a97575 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -15,6 +15,7 @@ tests += $(TEST_DIR)/sbi.$(exe)
tests += $(TEST_DIR)/selftest.$(exe)
tests += $(TEST_DIR)/sieve.$(exe)
tests += $(TEST_DIR)/isa-dbltrp.$(exe)
+tests += $(TEST_DIR)/isa-zicboz.$(exe)
all: $(tests)
diff --git a/riscv/isa-zicboz.c b/riscv/isa-zicboz.c
new file mode 100644
index 00000000..76a2d209
--- /dev/null
+++ b/riscv/isa-zicboz.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Zicboz cache-block-zero instruction tests
+ *
+ * Copyright (C) 2026, Sainath Grandhi <sainathg@systemssoftware.dev>
+ */
+#include <alloc.h>
+#include <devicetree.h>
+#include <libcflat.h>
+#include <string.h>
+
+#include <asm/isa.h>
+#include <asm/processor.h>
+
+#define GUARD_BEFORE_PATTERN 0xa5
+#define TARGET_PATTERN 0x5a
+#define GUARD_AFTER_PATTERN 0x3c
+
+struct zicboz_info {
+ u64 hartid;
+ u32 block_size;
+ int property_len;
+ bool found;
+};
+
+static void find_zicboz_block_size(int cpu_node, u64 hartid, void *data)
+{
+ struct zicboz_info *info = data;
+ const fdt32_t *property;
+ int len;
+
+ if (hartid != info->hartid)
+ return;
+
+ property = fdt_getprop(dt_fdt(), cpu_node,
+ "riscv,cboz-block-size", &len);
+ if (!property)
+ return;
+
+ info->found = true;
+ info->property_len = len;
+ if (len == sizeof(*property))
+ info->block_size = fdt32_to_cpu(*property);
+}
+
+static bool get_zicboz_block_size(size_t *block_size)
+{
+ struct zicboz_info info = {
+ .hartid = current_thread_info()->hartid,
+ };
+ int ret;
+
+ ret = dt_for_each_cpu_node(find_zicboz_block_size, &info);
+ if (ret) {
+ report_fail("failed to walk CPU nodes: %s", fdt_strerror(ret));
+ return false;
+ }
+
+ if (!info.found) {
+ report_fail("riscv,cboz-block-size is missing");
+ return false;
+ }
+
+ if (info.property_len != sizeof(fdt32_t)) {
+ report_fail("riscv,cboz-block-size has invalid length %d",
+ info.property_len);
+ return false;
+ }
+
+ if (!is_power_of_2(info.block_size)) {
+ report_fail("invalid CBO.ZERO block size %u", info.block_size);
+ return false;
+ }
+
+ *block_size = info.block_size;
+ return true;
+}
+
+static inline void cbo_zero(void *address)
+{
+ asm volatile(".option push\n"
+ ".option arch, +zicboz\n"
+ "cbo.zero 0(%0)\n"
+ ".option pop\n"
+ :
+ : "r" (address)
+ : "memory");
+}
+
+static bool block_has_value(const u8 *block, size_t block_size, u8 value)
+{
+ size_t i;
+
+ for (i = 0; i < block_size; i++) {
+ if (block[i] != value)
+ return false;
+ }
+
+ return true;
+}
+
+static void test_cbo_zero_at_offset(u8 *area, size_t block_size,
+ size_t offset, const char *name)
+{
+ u8 *guard_before = area;
+ u8 *target = guard_before + block_size;
+ u8 *guard_after = target + block_size;
+
+ report_prefix_push(name);
+
+ memset(guard_before, GUARD_BEFORE_PATTERN, block_size);
+ memset(target, TARGET_PATTERN, block_size);
+ memset(guard_after, GUARD_AFTER_PATTERN, block_size);
+
+ cbo_zero(target + offset);
+ asm volatile("fence rw, rw" ::: "memory");
+
+ report(block_has_value(target, block_size, 0),
+ "target block was zeroed");
+ report(block_has_value(guard_before, block_size,
+ GUARD_BEFORE_PATTERN),
+ "preceding block was preserved");
+ report(block_has_value(guard_after, block_size,
+ GUARD_AFTER_PATTERN),
+ "following block was preserved");
+
+ report_prefix_pop();
+}
+
+int main(int argc, char **argv)
+{
+ size_t block_size;
+ u8 *area;
+
+ report_prefix_push("zicboz");
+
+ if (!dt_available()) {
+ report_skip("device tree is not available");
+ goto out;
+ }
+
+ if (!has_ext("zicboz")) {
+ report_skip("Zicboz is not available");
+ goto out;
+ }
+
+ if (!get_zicboz_block_size(&block_size))
+ goto out;
+
+ report_info("CBO.ZERO block size is %lu bytes",
+ (unsigned long)block_size);
+
+ area = memalign(block_size, 3 * block_size);
+
+ /* Every address within a block must select the same containing block. */
+ test_cbo_zero_at_offset(area, block_size, 0, "block-start");
+ test_cbo_zero_at_offset(area, block_size, 1, "second-byte");
+ test_cbo_zero_at_offset(area, block_size, block_size / 2,
+ "block-middle");
+ test_cbo_zero_at_offset(area, block_size, block_size - 1,
+ "block-end");
+
+ free(area);
+
+out:
+ report_prefix_pop();
+ return report_summary();
+}
diff --git a/riscv/unittests.cfg b/riscv/unittests.cfg
index 9e9f58d9..30594fe5 100644
--- a/riscv/unittests.cfg
+++ b/riscv/unittests.cfg
@@ -22,3 +22,7 @@ groups = sbi
[dbltrp]
file = isa-dbltrp.flat
groups = isa sbi
+
+[zicboz]
+file = isa-zicboz.flat
+groups = isa
--
2.53.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-08-08 5:51 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-08 5:50 [kvm-unit-tests PATCH] riscv: Add Zicboz CBO.ZERO tests Sainath Grandhi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox