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
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];
7897char zt_expected [ZT_SIG_REG_BYTES ];
7998char zt_out [ZT_SIG_REG_BYTES ];
8099
100+ uint64_t fpmr_in , fpmr_expected , fpmr_out ;
101+
81102uint64_t sve_vl_out ;
82103uint64_t sme_vl_out ;
83104uint64_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+
131157static 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
326362static 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
599655static 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
888969static 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+
10131130static 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
12711394static 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 */
0 commit comments