kselftest/arm64: Verify SME only ABI in syscall-abi
authorMark Brown <broonie@kernel.org>
Tue, 27 Dec 2022 13:06:37 +0000 (13:06 +0000)
committerCatalin Marinas <catalin.marinas@arm.com>
Thu, 12 Jan 2023 16:20:31 +0000 (16:20 +0000)
Currently syscall-abi only covers SME in the case where the system supports
SVE however it is architecturally valid to support SME without SVE. Update
the program to cover this case, this requires adjustments in the code to
check for SVCR.SM being set when deciding if we're handling the FPSIMD or
SVE registers and the addition of new test cases for the SME only case.

Note that in the SME only case we should not save the SVE registers after a
syscall since even if we were in streaming mode and therefore set them the
syscall should have exited streaming mode, we check that we have done so by
looking at SVCR.

Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20221223-arm64-syscall-abi-sme-only-v1-3-4fabfbd62087@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
tools/testing/selftests/arm64/abi/syscall-abi-asm.S
tools/testing/selftests/arm64/abi/syscall-abi.c

index acd5e9f3bc0b424b4dddbfb5de88913d2f49056b..cdfafc939a9ef5d60a584ba6ec7e83512fea9029 100644 (file)
@@ -92,8 +92,11 @@ do_syscall:
        str     x29, [x2], #8           // FP
        str     x30, [x2], #8           // LR
 
-       // Load FPRs if we're not doing SVE
+       // Load FPRs if we're not doing neither SVE nor streaming SVE
        cbnz    x0, 1f
+       ldr     x2, =svcr_in
+       tbnz    x2, #SVCR_SM_SHIFT, 1f
+
        ldr     x2, =fpr_in
        ldp     q0, q1, [x2]
        ldp     q2, q3, [x2, #16 * 2]
@@ -111,10 +114,11 @@ do_syscall:
        ldp     q26, q27, [x2, #16 * 26]
        ldp     q28, q29, [x2, #16 * 28]
        ldp     q30, q31, [x2, #16 * 30]
+
+       b       2f
 1:
 
        // Load the SVE registers if we're doing SVE/SME
-       cbz     x0, 1f
 
        ldr     x2, =z_in
        ldr     z0, [x2, #0, MUL VL]
@@ -155,9 +159,9 @@ do_syscall:
        ldr     x2, =ffr_in
        ldr     p0, [x2]
        ldr     x2, [x2, #0]
-       cbz     x2, 2f
+       cbz     x2, 1f
        wrffr   p0.b
-2:
+1:
 
        ldr     x2, =p_in
        ldr     p0, [x2, #0, MUL VL]
@@ -176,7 +180,7 @@ do_syscall:
        ldr     p13, [x2, #13, MUL VL]
        ldr     p14, [x2, #14, MUL VL]
        ldr     p15, [x2, #15, MUL VL]
-1:
+2:
 
        // Do the syscall
        svc     #0
index 45fdcbe3e909b77be7dd72d813ba757b52e9c849..7c9b6e9470409718b26498f6082d2ae337855a35 100644 (file)
@@ -88,6 +88,7 @@ static int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t s
 #define NUM_FPR 32
 uint64_t fpr_in[NUM_FPR * 2];
 uint64_t fpr_out[NUM_FPR * 2];
+uint64_t fpr_zero[NUM_FPR * 2];
 
 static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
                      uint64_t svcr)
@@ -102,7 +103,7 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
        int errors = 0;
        int i;
 
-       if (!sve_vl) {
+       if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
                for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
                        if (fpr_in[i] != fpr_out[i]) {
                                ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
@@ -114,6 +115,18 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
                }
        }
 
+       /*
+        * In streaming mode the whole register set should be cleared
+        * by the transition out of streaming mode.
+        */
+       if (svcr & SVCR_SM_MASK) {
+               if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {
+                       ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",
+                                      cfg->name);
+                       errors++;
+               }
+       }
+
        return errors;
 }
 
@@ -400,6 +413,24 @@ static void test_one_syscall(struct syscall_cfg *cfg)
                                         sme_vls[sme]);
                }
        }
+
+       for (sme = 0; sme < sme_vl_count; sme++) {
+               ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
+               if (ret == -1)
+                       ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
+                                                  strerror(errno), errno);
+
+               ksft_test_result(do_test(cfg, 0, sme_vls[sme],
+                                        SVCR_ZA_MASK | SVCR_SM_MASK),
+                                "%s SME VL %d SM+ZA\n",
+                                cfg->name, sme_vls[sme]);
+               ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),
+                                "%s SME VL %d SM\n",
+                                cfg->name, sme_vls[sme]);
+               ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),
+                                "%s SME VL %d ZA\n",
+                                cfg->name, sme_vls[sme]);
+       }
 }
 
 void sve_count_vls(void)
@@ -474,6 +505,7 @@ int main(void)
        sme_count_vls();
 
        tests += sve_vl_count;
+       tests += sme_vl_count * 3;
        tests += (sve_vl_count * sme_vl_count) * 3;
        ksft_set_plan(ARRAY_SIZE(syscalls) * tests);