From: Frank Chang <[email protected]>
Cover scalar, floating-point, vector, and segmented misaligned accesses
with Zicclsm enabled and disabled. Clean up both generated test
binaries.
To build and run the tests:
make -C build/tests/tcg/riscv64-softmmu \
CC=riscv64-unknown-elf-gcc LD=riscv64-unknown-elf-ld \
test-zicclsm test-zicclsm-off
make -C build/tests/tcg/riscv64-softmmu \
run-test-zicclsm run-test-zicclsm-off
To clean the generated binaries and objects:
make -C build/tests/tcg/riscv64-softmmu clean
Signed-off-by: Frank Chang <[email protected]>
Reviewed-by: Daniel Henrique Barboza <[email protected]>
Message-ID: <[email protected]>
Signed-off-by: Alistair Francis <[email protected]>
---
tests/tcg/riscv64/Makefile.softmmu-target | 22 ++
tests/tcg/riscv64/test-zicclsm.S | 368 ++++++++++++++++++++++
2 files changed, 390 insertions(+)
create mode 100644 tests/tcg/riscv64/test-zicclsm.S
diff --git a/tests/tcg/riscv64/Makefile.softmmu-target
b/tests/tcg/riscv64/Makefile.softmmu-target
index 82be8a2c91..908fd008e4 100644
--- a/tests/tcg/riscv64/Makefile.softmmu-target
+++ b/tests/tcg/riscv64/Makefile.softmmu-target
@@ -41,5 +41,27 @@ comma:= ,
run-test-crc32: test-crc32
$(call run-test, $<, $(QEMU) -cpu rv64$(comma)xlrbr=true $(QEMU_OPTS)$<)
+# Zicclsm: misaligned load/store support. Assemble one source twice: the
+# default build expects every misaligned access to succeed (zicclsm=true),
+# the -DZICCLSM_DISABLED build expects every one to trap (zicclsm=false).
+ZICCLSM_MARCH = -march=rv64gcv_zfh
+CLEANFILES += test-zicclsm test-zicclsm-off
+
+test-zicclsm: test-zicclsm.S $(LINK_SCRIPT)
+ $(CC) $(CFLAGS) $(ZICCLSM_MARCH) $< -Wa,--noexecstack -c -o
test-zicclsm.o
+ $(LD) $(LDFLAGS) test-zicclsm.o -o $@
+
+test-zicclsm-off: test-zicclsm.S $(LINK_SCRIPT)
+ $(CC) $(CFLAGS) $(ZICCLSM_MARCH) -DZICCLSM_DISABLED $<
-Wa,--noexecstack -c -o test-zicclsm-off.o
+ $(LD) $(LDFLAGS) test-zicclsm-off.o -o $@
+
+EXTRA_RUNS += run-test-zicclsm
+run-test-zicclsm: test-zicclsm
+ $(call run-test, $<, $(QEMU) -cpu
rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=true $(QEMU_OPTS)$<)
+
+EXTRA_RUNS += run-test-zicclsm-off
+run-test-zicclsm-off: test-zicclsm-off
+ $(call run-test, $<, $(QEMU) -cpu
rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=false $(QEMU_OPTS)$<)
+
# We don't currently support the multiarch system tests
undefine MULTIARCH_TESTS
diff --git a/tests/tcg/riscv64/test-zicclsm.S b/tests/tcg/riscv64/test-zicclsm.S
new file mode 100644
index 0000000000..a2f217c0d1
--- /dev/null
+++ b/tests/tcg/riscv64/test-zicclsm.S
@@ -0,0 +1,368 @@
+/*
+ * Test the Zicclsm extension (misaligned load/store support).
+ *
+ * This single source is assembled twice:
+ * - test-zicclsm : run on a CPU with zicclsm=true. Every misaligned
+ * scalar integer, floating-point and vector
+ * load/store must complete WITHOUT raising a trap.
+ * - test-zicclsm-off : built with -DZICCLSM_DISABLED and run on a CPU with
+ * zicclsm=false. Every misaligned access must raise a
+ * misaligned load/store exception, with the correct
+ * mcause and mtval.
+ *
+ * Zicclsm governs all regular scalar loads/stores (integer and F/D/Zfh
+ * floating-point) as well as vector element loads/stores. Floating-point
+ * loads/stores (flh/flw/fld, fsh/fsw/fsd) are therefore exercised here.
+ *
+ * Atomic (A/Zacas/...) accesses are intentionally excluded: they always
+ * require natural alignment regardless of Zicclsm. Likewise cm.push/cm.pop
+ * (Zcmp) are excluded, as they are not regular loads/stores.
+ *
+ * Register conventions (persist across the whole test; the trap handler only
+ * clobbers t0-t4):
+ * s1 = expected mcause for the pending misaligned access
+ * s2 = expected mtval (the misaligned address)
+ * s3 = trap counter (incremented by the handler)
+ * s4 = base address of the aligned data buffer
+ * s5 = snapshot of s3 taken before an access, used to check the delta
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+ .option norelax
+ .option norvc
+
+/* RISC-V exception causes (see target/riscv/cpu_bits.h). */
+#define CAUSE_LOAD_MISALIGNED 0x4
+#define CAUSE_STORE_MISALIGNED 0x6
+
+/*
+ * EXPECT sets up the expectation for the access that immediately follows and
+ * snapshots the trap counter.
+ * \cause = expected mcause if the access traps
+ * \off = byte offset from the buffer base; also the expected mtval
+ */
+ .macro EXPECT cause, off
+ li s1, \cause
+ addi s2, s4, \off
+ mv s5, s3
+ .endm
+
+/*
+ * CHECK validates the outcome of the preceding access.
+ * - When Zicclsm is disabled, exactly one trap must have fired.
+ * - When Zicclsm is enabled, no trap must have fired.
+ */
+ .macro CHECK
+#ifdef ZICCLSM_DISABLED
+ addi s5, s5, 1
+ bne s3, s5, fail
+#else
+ bne s3, s5, fail
+#endif
+ .endm
+
+/* In the enabled case, also verify the value returned by scalar loads. */
+ .macro CHECK_VALUE value
+#ifndef ZICCLSM_DISABLED
+ li t0, \value
+ bne a2, t0, fail
+#endif
+ .endm
+
+/* Verify bytes written by an enabled scalar store. */
+ .macro CHECK_BYTE off, value
+#ifndef ZICCLSM_DISABLED
+ lbu t0, \off(s4)
+ li t1, \value
+ bne t0, t1, fail
+#endif
+ .endm
+
+ .text
+ .global _start
+_start:
+ /* Install the trap handler. */
+ lla t0, trap
+ csrw mtvec, t0
+
+ /* Enable the FP (FS) and Vector (VS) unit state so F/D/V instructions
+ * do not trap as illegal. 0x6600 = FS[14:13]=11 | VS[10:9]=11. */
+ li t0, 0x6600
+ csrs mstatus, t0
+
+ /* Initialise persistent state. */
+ li s3, 0 /* trap counter */
+ lla s4, buf /* aligned buffer base */
+
+ /*
+ * ---- Scalar integer loads ----
+ * lh/lhu need 2-byte alignment; lw/lwu 4-byte; ld 8-byte.
+ */
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ lh a2, 1(s4)
+ CHECK
+ CHECK_VALUE 0x2211
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ lhu a2, 1(s4)
+ CHECK
+ CHECK_VALUE 0x2211
+
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ lw a2, 1(s4)
+ CHECK
+ CHECK_VALUE 0x44332211
+ EXPECT CAUSE_LOAD_MISALIGNED, 3
+ lw a2, 3(s4)
+ CHECK
+ CHECK_VALUE 0x66554433
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ lwu a2, 1(s4)
+ CHECK
+ CHECK_VALUE 0x44332211
+
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ ld a2, 1(s4)
+ CHECK
+ CHECK_VALUE 0x8877665544332211
+ EXPECT CAUSE_LOAD_MISALIGNED, 3
+ ld a2, 3(s4)
+ CHECK
+ CHECK_VALUE 0xaa99887766554433
+ EXPECT CAUSE_LOAD_MISALIGNED, 7
+ ld a2, 7(s4)
+ CHECK
+ CHECK_VALUE 0xeeddccbbaa998877
+
+ /*
+ * ---- Scalar integer stores ----
+ */
+ li t6, 0x1122334455667788
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ sh t6, 1(s4)
+ CHECK
+ CHECK_BYTE 1, 0x88
+ CHECK_BYTE 2, 0x77
+
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ sw t6, 1(s4)
+ CHECK
+ CHECK_BYTE 1, 0x88
+ CHECK_BYTE 2, 0x77
+ CHECK_BYTE 3, 0x66
+ CHECK_BYTE 4, 0x55
+ EXPECT CAUSE_STORE_MISALIGNED, 3
+ sw t6, 3(s4)
+ CHECK
+ CHECK_BYTE 3, 0x88
+ CHECK_BYTE 4, 0x77
+ CHECK_BYTE 5, 0x66
+ CHECK_BYTE 6, 0x55
+
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ sd t6, 1(s4)
+ CHECK
+ CHECK_BYTE 1, 0x88
+ CHECK_BYTE 2, 0x77
+ CHECK_BYTE 3, 0x66
+ CHECK_BYTE 4, 0x55
+ CHECK_BYTE 5, 0x44
+ CHECK_BYTE 6, 0x33
+ CHECK_BYTE 7, 0x22
+ CHECK_BYTE 8, 0x11
+ EXPECT CAUSE_STORE_MISALIGNED, 7
+ sd t6, 7(s4)
+ CHECK
+ CHECK_BYTE 7, 0x88
+ CHECK_BYTE 8, 0x77
+ CHECK_BYTE 9, 0x66
+ CHECK_BYTE 10, 0x55
+ CHECK_BYTE 11, 0x44
+ CHECK_BYTE 12, 0x33
+ CHECK_BYTE 13, 0x22
+ CHECK_BYTE 14, 0x11
+
+ /*
+ * ---- Floating-point loads ----
+ * flh needs 2-byte alignment; flw 4-byte; fld 8-byte. Their alignment
+ * is governed by Zicclsm just like the scalar integer forms.
+ */
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ flh fa0, 1(s4)
+ CHECK
+
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ flw fa0, 1(s4)
+ CHECK
+ EXPECT CAUSE_LOAD_MISALIGNED, 3
+ flw fa0, 3(s4)
+ CHECK
+
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ fld fa0, 1(s4)
+ CHECK
+ EXPECT CAUSE_LOAD_MISALIGNED, 7
+ fld fa0, 7(s4)
+ CHECK
+
+ /*
+ * ---- Floating-point stores ----
+ */
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ fsh fa0, 1(s4)
+ CHECK
+
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ fsw fa0, 1(s4)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 3
+ fsw fa0, 3(s4)
+ CHECK
+
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ fsd fa0, 1(s4)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 7
+ fsd fa0, 7(s4)
+ CHECK
+
+ /*
+ * ---- Vector unit-stride loads / stores ----
+ * A base address that is not aligned to the element size (SEW) is
+ * misaligned for the first element access.
+ */
+ vsetvli t1, x0, e16, m1, ta, ma
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vle16.v v0, (a0)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ addi a0, s4, 1
+ vse16.v v0, (a0)
+ CHECK
+
+ vsetvli t1, x0, e32, m1, ta, ma
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vle32.v v0, (a0)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 3
+ addi a0, s4, 3
+ vse32.v v0, (a0)
+ CHECK
+
+ vsetvli t1, x0, e64, m1, ta, ma
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vle64.v v0, (a0)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 7
+ addi a0, s4, 7
+ vse64.v v0, (a0)
+ CHECK
+
+ /*
+ * ---- Vector strided loads / stores ----
+ */
+ vsetvli t1, x0, e32, m1, ta, ma
+ li a1, 8 /* stride in bytes */
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vlse32.v v0, (a0), a1
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ addi a0, s4, 1
+ vsse32.v v0, (a0), a1
+ CHECK
+
+ /* ---- Vector indexed loads / stores ---- */
+ /*
+ * Zero indices keep the first element at the deliberately misaligned
base.
+ */
+ vmv.v.i v1, 0
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vluxei32.v v0, (a0), v1
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ addi a0, s4, 1
+ vsuxei32.v v0, (a0), v1
+ CHECK
+
+ /* ---- Vector segmented loads / stores ---- */
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vlseg2e32.v v0, (a0)
+ CHECK
+ EXPECT CAUSE_STORE_MISALIGNED, 1
+ addi a0, s4, 1
+ vsseg2e32.v v0, (a0)
+ CHECK
+
+ /*
+ * ---- Vector whole-register load ----
+ * Only the whole-register *load* forms carry an element width
+ * (vl1re32.v => EEW=32), so only they enforce alignment when Zicclsm is
+ * off. The whole-register store form (vs1r.v) is defined as EEW=8
+ * (byte granular) and therefore never faults on misalignment, so it is
+ * not exercised here.
+ */
+ EXPECT CAUSE_LOAD_MISALIGNED, 1
+ addi a0, s4, 1
+ vl1re32.v v1, (a0)
+ CHECK
+
+ /* Success. */
+ li a0, 0
+ j _exit
+
+ /*
+ * Trap handler: validate mcause and mtval against the expectation, bump
+ * the trap counter, then skip past the faulting instruction. The
+ * instruction length is decoded from its low two bits (0b11 => 4 bytes,
+ * otherwise a 2-byte compressed instruction).
+ */
+ .balign 4
+trap:
+ csrr t0, mcause
+ bne t0, s1, fail
+ csrr t1, mtval
+ bne t1, s2, fail
+ addi s3, s3, 1
+
+ csrr t0, mepc
+ lhu t2, 0(t0)
+ andi t3, t2, 3
+ li t4, 3
+ bne t3, t4, 1f
+ addi t0, t0, 4 /* 32-bit instruction */
+ j 2f
+1:
+ addi t0, t0, 2 /* 16-bit compressed instruction */
+2:
+ csrw mepc, t0
+ mret
+
+fail:
+ li a0, 1
+_exit:
+ lla a1, semiargs
+ li t0, 0x20026 /* ADP_Stopped_ApplicationExit */
+ sd t0, 0(a1)
+ sd a0, 8(a1)
+ li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */
+ .balign 16
+ slli zero, zero, 0x1f
+ ebreak
+ srai zero, zero, 0x7
+ j .
+
+ .data
+ .balign 16
+semiargs:
+ .space 16
+ .balign 64
+buf:
+ .byte 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
+ .byte 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+ .space 240
--
2.54.0