Skip to content

Commit 7dbd26d

Browse files
brooniectmarinas
authored andcommitted
kselftest/arm64: Add FPMR coverage to fp-ptrace
Add coverage for FPMR to fp-ptrace. FPMR can be available independently of SVE and SME, if SME is supported then FPMR is cleared by entering and exiting streaming mode. As with other registers we generate random values to load into the register, we restrict these to bitfields which are always defined. We also leave bitfields where the valid values are affected by the set of supported FP8 formats zero to reduce complexity, it is unlikely that specific bitfields will be affected by ptrace issues. Signed-off-by: Mark Brown <broonie@kernel.org> Link: https://lore.kernel.org/r/20241112-arm64-fp-ptrace-fpmr-v2-3-250b57c61254@kernel.org [catalin.marinas@arm.com: use REG_FPMR instead of FPMR] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent 7e9c5b0 commit 7dbd26d

File tree

4 files changed

+146
-7
lines changed

4 files changed

+146
-7
lines changed

tools/testing/selftests/arm64/fp/fp-ptrace-asm.S

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,12 @@ check_sm_in:
7171
tbz x7, #SVCR_SM_SHIFT, check_sve_in
7272

7373
// Load FFR if we have FA64
74-
mov x4, #0
75-
tbz x0, #HAVE_FA64_SHIFT, load_sve
76-
mov x4, #1
74+
ubfx x4, x0, #HAVE_FA64_SHIFT, #1
7775
b load_sve
7876

7977
// SVE?
8078
check_sve_in:
81-
tbz x0, #HAVE_SVE_SHIFT, wait_for_writes
79+
tbz x0, #HAVE_SVE_SHIFT, check_fpmr_in
8280
mov x4, #1
8381

8482
load_sve:
@@ -143,6 +141,13 @@ load_sve:
143141
ldr p14, [x7, #14, MUL VL]
144142
ldr p15, [x7, #15, MUL VL]
145143

144+
// This has to come after we set PSTATE.SM
145+
check_fpmr_in:
146+
tbz x0, #HAVE_FPMR_SHIFT, wait_for_writes
147+
adrp x7, fpmr_in
148+
ldr x7, [x7, :lo12:fpmr_in]
149+
msr REG_FPMR, x7
150+
146151
wait_for_writes:
147152
// Wait for the parent
148153
brk #0
@@ -166,6 +171,12 @@ wait_for_writes:
166171
stp q28, q29, [x7, #16 * 28]
167172
stp q30, q31, [x7, #16 * 30]
168173

174+
tbz x0, #HAVE_FPMR_SHIFT, check_sme_out
175+
mrs x7, REG_FPMR
176+
adrp x6, fpmr_out
177+
str x7, [x6, :lo12:fpmr_out]
178+
179+
check_sme_out:
169180
tbz x0, #HAVE_SME_SHIFT, check_sve_out
170181

171182
rdsvl 11, 1
@@ -197,9 +208,7 @@ check_sm_out:
197208
tbz x7, #SVCR_SM_SHIFT, check_sve_out
198209

199210
// Do we have FA64 and FFR?
200-
mov x4, #0
201-
tbz x0, #HAVE_FA64_SHIFT, read_sve
202-
mov x4, #1
211+
ubfx x4, x0, #HAVE_FA64_SHIFT, #1
203212
b read_sve
204213

205214
// SVE?

tools/testing/selftests/arm64/fp/fp-ptrace.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131

3232
#include "fp-ptrace.h"
3333

34+
#include <linux/bits.h>
35+
36+
#define FPMR_LSCALE2_MASK GENMASK(37, 32)
37+
#define FPMR_NSCALE_MASK GENMASK(31, 24)
38+
#define FPMR_LSCALE_MASK GENMASK(22, 16)
39+
#define FPMR_OSC_MASK GENMASK(15, 15)
40+
#define FPMR_OSM_MASK GENMASK(14, 14)
41+
3442
/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
3543
#ifndef NT_ARM_SVE
3644
#define NT_ARM_SVE 0x405
@@ -48,11 +56,22 @@
4856
#define NT_ARM_ZT 0x40d
4957
#endif
5058

59+
#ifndef NT_ARM_FPMR
60+
#define NT_ARM_FPMR 0x40e
61+
#endif
62+
5163
#define ARCH_VQ_MAX 256
5264

5365
/* VL 128..2048 in powers of 2 */
5466
#define MAX_NUM_VLS 5
5567

68+
/*
69+
* FPMR bits we can set without doing feature checks to see if values
70+
* are valid.
71+
*/
72+
#define FPMR_SAFE_BITS (FPMR_LSCALE2_MASK | FPMR_NSCALE_MASK | \
73+
FPMR_LSCALE_MASK | FPMR_OSC_MASK | FPMR_OSM_MASK)
74+
5675
#define NUM_FPR 32
5776
__uint128_t v_in[NUM_FPR];
5877
__uint128_t v_expected[NUM_FPR];
@@ -78,6 +97,8 @@ char zt_in[ZT_SIG_REG_BYTES];
7897
char zt_expected[ZT_SIG_REG_BYTES];
7998
char zt_out[ZT_SIG_REG_BYTES];
8099

100+
uint64_t fpmr_in, fpmr_expected, fpmr_out;
101+
81102
uint64_t sve_vl_out;
82103
uint64_t sme_vl_out;
83104
uint64_t svcr_in, svcr_expected, svcr_out;
@@ -128,6 +149,11 @@ static bool fa64_supported(void)
128149
return getauxval(AT_HWCAP2) & HWCAP2_SME_FA64;
129150
}
130151

152+
static bool fpmr_supported(void)
153+
{
154+
return getauxval(AT_HWCAP2) & HWCAP2_FPMR;
155+
}
156+
131157
static bool compare_buffer(const char *name, void *out,
132158
void *expected, size_t size)
133159
{
@@ -233,6 +259,8 @@ static void run_child(struct test_config *config)
233259
flags |= HAVE_SME2;
234260
if (fa64_supported())
235261
flags |= HAVE_FA64;
262+
if (fpmr_supported())
263+
flags |= HAVE_FPMR;
236264

237265
load_and_save(flags);
238266

@@ -321,6 +349,14 @@ static void read_child_regs(pid_t child)
321349
iov_child.iov_len = sizeof(zt_out);
322350
read_one_child_regs(child, "ZT", &iov_parent, &iov_child);
323351
}
352+
353+
if (fpmr_supported()) {
354+
iov_parent.iov_base = &fpmr_out;
355+
iov_parent.iov_len = sizeof(fpmr_out);
356+
iov_child.iov_base = &fpmr_out;
357+
iov_child.iov_len = sizeof(fpmr_out);
358+
read_one_child_regs(child, "FPMR", &iov_parent, &iov_child);
359+
}
324360
}
325361

326362
static bool continue_breakpoint(pid_t child,
@@ -595,6 +631,26 @@ static bool check_ptrace_values_zt(pid_t child, struct test_config *config)
595631
return compare_buffer("initial ZT", buf, zt_in, ZT_SIG_REG_BYTES);
596632
}
597633

634+
static bool check_ptrace_values_fpmr(pid_t child, struct test_config *config)
635+
{
636+
uint64_t val;
637+
struct iovec iov;
638+
int ret;
639+
640+
if (!fpmr_supported())
641+
return true;
642+
643+
iov.iov_base = &val;
644+
iov.iov_len = sizeof(val);
645+
ret = ptrace(PTRACE_GETREGSET, child, NT_ARM_FPMR, &iov);
646+
if (ret != 0) {
647+
ksft_print_msg("Failed to read initial FPMR: %s (%d)\n",
648+
strerror(errno), errno);
649+
return false;
650+
}
651+
652+
return compare_buffer("initial FPMR", &val, &fpmr_in, sizeof(val));
653+
}
598654

599655
static bool check_ptrace_values(pid_t child, struct test_config *config)
600656
{
@@ -629,6 +685,9 @@ static bool check_ptrace_values(pid_t child, struct test_config *config)
629685
if (!check_ptrace_values_zt(child, config))
630686
pass = false;
631687

688+
if (!check_ptrace_values_fpmr(child, config))
689+
pass = false;
690+
632691
return pass;
633692
}
634693

@@ -832,11 +891,18 @@ static void set_initial_values(struct test_config *config)
832891
{
833892
int vq = __sve_vq_from_vl(vl_in(config));
834893
int sme_vq = __sve_vq_from_vl(config->sme_vl_in);
894+
bool sm_change;
835895

836896
svcr_in = config->svcr_in;
837897
svcr_expected = config->svcr_expected;
838898
svcr_out = 0;
839899

900+
if (sme_supported() &&
901+
(svcr_in & SVCR_SM) != (svcr_expected & SVCR_SM))
902+
sm_change = true;
903+
else
904+
sm_change = false;
905+
840906
fill_random(&v_in, sizeof(v_in));
841907
memcpy(v_expected, v_in, sizeof(v_in));
842908
memset(v_out, 0, sizeof(v_out));
@@ -883,6 +949,21 @@ static void set_initial_values(struct test_config *config)
883949
memset(zt_expected, 0, ZT_SIG_REG_BYTES);
884950
memset(zt_out, 0, sizeof(zt_out));
885951
}
952+
953+
if (fpmr_supported()) {
954+
fill_random(&fpmr_in, sizeof(fpmr_in));
955+
fpmr_in &= FPMR_SAFE_BITS;
956+
957+
/* Entering or exiting streaming mode clears FPMR */
958+
if (sm_change)
959+
fpmr_expected = 0;
960+
else
961+
fpmr_expected = fpmr_in;
962+
} else {
963+
fpmr_in = 0;
964+
fpmr_expected = 0;
965+
fpmr_out = 0;
966+
}
886967
}
887968

888969
static bool check_memory_values(struct test_config *config)
@@ -933,6 +1014,12 @@ static bool check_memory_values(struct test_config *config)
9331014
if (!compare_buffer("saved ZT", zt_out, zt_expected, ZT_SIG_REG_BYTES))
9341015
pass = false;
9351016

1017+
if (fpmr_out != fpmr_expected) {
1018+
ksft_print_msg("Mismatch in saved FPMR: %lx != %lx\n",
1019+
fpmr_out, fpmr_expected);
1020+
pass = false;
1021+
}
1022+
9361023
return pass;
9371024
}
9381025

@@ -1010,6 +1097,36 @@ static void fpsimd_write(pid_t child, struct test_config *test_config)
10101097
strerror(errno), errno);
10111098
}
10121099

1100+
static bool fpmr_write_supported(struct test_config *config)
1101+
{
1102+
if (!fpmr_supported())
1103+
return false;
1104+
1105+
if (!sve_sme_same(config))
1106+
return false;
1107+
1108+
return true;
1109+
}
1110+
1111+
static void fpmr_write_expected(struct test_config *config)
1112+
{
1113+
fill_random(&fpmr_expected, sizeof(fpmr_expected));
1114+
fpmr_expected &= FPMR_SAFE_BITS;
1115+
}
1116+
1117+
static void fpmr_write(pid_t child, struct test_config *config)
1118+
{
1119+
struct iovec iov;
1120+
int ret;
1121+
1122+
iov.iov_len = sizeof(fpmr_expected);
1123+
iov.iov_base = &fpmr_expected;
1124+
ret = ptrace(PTRACE_SETREGSET, child, NT_ARM_FPMR, &iov);
1125+
if (ret != 0)
1126+
ksft_print_msg("Failed to write FPMR: %s (%d)\n",
1127+
strerror(errno), errno);
1128+
}
1129+
10131130
static void sve_write_expected(struct test_config *config)
10141131
{
10151132
int vl = vl_expected(config);
@@ -1266,6 +1383,12 @@ static struct test_definition base_test_defs[] = {
12661383
.set_expected_values = fpsimd_write_expected,
12671384
.modify_values = fpsimd_write,
12681385
},
1386+
{
1387+
.name = "FPMR write",
1388+
.supported = fpmr_write_supported,
1389+
.set_expected_values = fpmr_write_expected,
1390+
.modify_values = fpmr_write,
1391+
},
12691392
};
12701393

12711394
static struct test_definition sve_test_defs[] = {
@@ -1475,6 +1598,9 @@ int main(void)
14751598
if (fa64_supported())
14761599
ksft_print_msg("FA64 supported\n");
14771600

1601+
if (fpmr_supported())
1602+
ksft_print_msg("FPMR supported\n");
1603+
14781604
ksft_set_plan(tests);
14791605

14801606
/* Get signal handers ready before we start any children */

tools/testing/selftests/arm64/fp/fp-ptrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#define HAVE_SME_SHIFT 1
1515
#define HAVE_SME2_SHIFT 2
1616
#define HAVE_FA64_SHIFT 3
17+
#define HAVE_FPMR_SHIFT 4
1718

1819
#define HAVE_SVE (1 << HAVE_SVE_SHIFT)
1920
#define HAVE_SME (1 << HAVE_SME_SHIFT)
2021
#define HAVE_SME2 (1 << HAVE_SME2_SHIFT)
2122
#define HAVE_FA64 (1 << HAVE_FA64_SHIFT)
23+
#define HAVE_FPMR (1 << HAVE_FPMR_SHIFT)
2224

2325
#endif

tools/testing/selftests/arm64/fp/sme-inst.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef SME_INST_H
66
#define SME_INST_H
77

8+
#define REG_FPMR S3_3_C4_C4_2
9+
810
/*
911
* RDSVL X\nx, #\imm
1012
*/

0 commit comments

Comments
 (0)