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


Reply via email to