mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge branch 'for-next/selftests' into for-next/core
* for-next/selftests: kselftest/arm64: Add 2025 dpISA coverage to hwcaps kselftest/arm64: Add tests for POR_EL0 save/reset/restore kselftest/arm64: Move/add POE helpers to test_signals_utils.h kselftest/arm64: Add POE as a feature in the signal tests selftests/mm: Fix resv_sz when parsing arm64 signal frame
This commit is contained in:
commit
68d619cbde
|
|
@ -108,6 +108,24 @@ static void f8mm8_sigill(void)
|
|||
asm volatile(".inst 0x6e80ec00");
|
||||
}
|
||||
|
||||
static void f16f32dot_sigill(void)
|
||||
{
|
||||
/* FDOT V0.2S, V0.4H, V0.2H[0] */
|
||||
asm volatile(".inst 0xf409000");
|
||||
}
|
||||
|
||||
static void f16f32mm_sigill(void)
|
||||
{
|
||||
/* FMMLA V0.4S, V0.8H, V0.8H */
|
||||
asm volatile(".inst 0x4e40ec00");
|
||||
}
|
||||
|
||||
static void f16mm_sigill(void)
|
||||
{
|
||||
/* FMMLA V0.8H, V0.8H, V0.8H */
|
||||
asm volatile(".inst 0x4ec0ec00");
|
||||
}
|
||||
|
||||
static void faminmax_sigill(void)
|
||||
{
|
||||
/* FAMIN V0.4H, V0.4H, V0.4H */
|
||||
|
|
@ -191,6 +209,12 @@ static void lut_sigill(void)
|
|||
asm volatile(".inst 0x4e801000");
|
||||
}
|
||||
|
||||
static void sve_lut6_sigill(void)
|
||||
{
|
||||
/* LUTI6 Z0.H, { Z0.H, Z1.H }, Z0[0] */
|
||||
asm volatile(".inst 0x4560ac00");
|
||||
}
|
||||
|
||||
static void mops_sigill(void)
|
||||
{
|
||||
char dst[1], src[1];
|
||||
|
|
@ -282,6 +306,18 @@ static void sme2p2_sigill(void)
|
|||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void sme2p3_sigill(void)
|
||||
{
|
||||
/* SMSTART SM */
|
||||
asm volatile("msr S0_3_C4_C3_3, xzr" : : : );
|
||||
|
||||
/* ADDQP Z0.B, Z0.B, Z0.B */
|
||||
asm volatile(".inst 0x4207800" : : : "z0");
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void sme_aes_sigill(void)
|
||||
{
|
||||
/* SMSTART SM */
|
||||
|
|
@ -378,6 +414,18 @@ static void smef8f32_sigill(void)
|
|||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smelut6_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
asm volatile("msr S0_3_C4_C7_3, xzr" : : : );
|
||||
|
||||
/* LUTI6 { Z0.B-Z3.B }, ZT0, { Z0-Z2 } */
|
||||
asm volatile(".inst 0xc08a0000" : : : );
|
||||
|
||||
/* SMSTOP */
|
||||
asm volatile("msr S0_3_C4_C6_3, xzr" : : : );
|
||||
}
|
||||
|
||||
static void smelutv2_sigill(void)
|
||||
{
|
||||
/* SMSTART */
|
||||
|
|
@ -486,6 +534,12 @@ static void sve2p2_sigill(void)
|
|||
asm volatile(".inst 0x4cea000" : : : "z0");
|
||||
}
|
||||
|
||||
static void sve2p3_sigill(void)
|
||||
{
|
||||
/* ADDQP Z0.B, Z0.B, Z0.B */
|
||||
asm volatile(".inst 0x4207800" : : : "z0");
|
||||
}
|
||||
|
||||
static void sveaes_sigill(void)
|
||||
{
|
||||
/* AESD z0.b, z0.b, z0.b */
|
||||
|
|
@ -504,6 +558,12 @@ static void sveb16b16_sigill(void)
|
|||
asm volatile(".inst 0x65000000" : : : );
|
||||
}
|
||||
|
||||
static void sveb16mm_sigill(void)
|
||||
{
|
||||
/* BFMMLA Z0.H, Z0.H, Z0.H */
|
||||
asm volatile(".inst 0x64e0e000" : : : );
|
||||
}
|
||||
|
||||
static void svebfscale_sigill(void)
|
||||
{
|
||||
/* BFSCALE Z0.H, P0/M, Z0.H, Z0.H */
|
||||
|
|
@ -729,6 +789,27 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "f8mm4",
|
||||
.sigill_fn = f8mm4_sigill,
|
||||
},
|
||||
{
|
||||
.name = "F16MM",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_F16MM,
|
||||
.cpuinfo = "f16mm",
|
||||
.sigill_fn = f16mm_sigill,
|
||||
},
|
||||
{
|
||||
.name = "F16F32DOT",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_F16F32DOT,
|
||||
.cpuinfo = "f16f32dot",
|
||||
.sigill_fn = f16f32dot_sigill,
|
||||
},
|
||||
{
|
||||
.name = "F16F32MM",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_F16F32MM,
|
||||
.cpuinfo = "f16f32mm",
|
||||
.sigill_fn = f16f32mm_sigill,
|
||||
},
|
||||
{
|
||||
.name = "FAMINMAX",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
|
|
@ -918,6 +999,13 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "sme2p2",
|
||||
.sigill_fn = sme2p2_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME 2.3",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_SME2P3,
|
||||
.cpuinfo = "sme2p3",
|
||||
.sigill_fn = sme2p3_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME AES",
|
||||
.at_hwcap = AT_HWCAP,
|
||||
|
|
@ -967,6 +1055,13 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "smef8f32",
|
||||
.sigill_fn = smef8f32_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME LUT6",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_SME_LUT6,
|
||||
.cpuinfo = "smelut6",
|
||||
.sigill_fn = smelut6_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SME LUTV2",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
|
|
@ -1052,6 +1147,13 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "sve2p2",
|
||||
.sigill_fn = sve2p2_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE 2.3",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_SVE2P3,
|
||||
.cpuinfo = "sve2p3",
|
||||
.sigill_fn = sve2p3_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE AES",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
|
|
@ -1066,6 +1168,13 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "sveaes2",
|
||||
.sigill_fn = sveaes2_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE B16MM",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_SVE_B16MM,
|
||||
.cpuinfo = "sveb16mm",
|
||||
.sigill_fn = sveb16mm_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE BFSCALE",
|
||||
.at_hwcap = AT_HWCAP,
|
||||
|
|
@ -1087,6 +1196,13 @@ static const struct hwcap_data {
|
|||
.cpuinfo = "svef16mm",
|
||||
.sigill_fn = svef16mm_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE_LUT6",
|
||||
.at_hwcap = AT_HWCAP3,
|
||||
.hwcap_bit = HWCAP3_SVE_LUT6,
|
||||
.cpuinfo = "svelut6",
|
||||
.sigill_fn = sve_lut6_sigill,
|
||||
},
|
||||
{
|
||||
.name = "SVE2 B16B16",
|
||||
.at_hwcap = AT_HWCAP2,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ enum {
|
|||
FSME_FA64_BIT,
|
||||
FSME2_BIT,
|
||||
FGCS_BIT,
|
||||
FPOE_BIT,
|
||||
FMAX_END
|
||||
};
|
||||
|
||||
|
|
@ -45,6 +46,7 @@ enum {
|
|||
#define FEAT_SME_FA64 (1UL << FSME_FA64_BIT)
|
||||
#define FEAT_SME2 (1UL << FSME2_BIT)
|
||||
#define FEAT_GCS (1UL << FGCS_BIT)
|
||||
#define FEAT_POE (1UL << FPOE_BIT)
|
||||
|
||||
/*
|
||||
* A descriptor used to describe and configure a test case.
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ static char const *const feats_names[FMAX_END] = {
|
|||
" FA64 ",
|
||||
" SME2 ",
|
||||
" GCS ",
|
||||
" POE ",
|
||||
};
|
||||
|
||||
#define MAX_FEATS_SZ 128
|
||||
|
|
@ -341,6 +342,8 @@ int test_init(struct tdescr *td)
|
|||
td->feats_supported |= FEAT_SME2;
|
||||
if (getauxval(AT_HWCAP) & HWCAP_GCS)
|
||||
td->feats_supported |= FEAT_GCS;
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_POE)
|
||||
td->feats_supported |= FEAT_POE;
|
||||
if (feats_ok(td)) {
|
||||
if (td->feats_required & td->feats_supported)
|
||||
fprintf(stderr,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,22 @@ static inline __attribute__((always_inline)) uint64_t get_gcspr_el0(void)
|
|||
return val;
|
||||
}
|
||||
|
||||
#define SYS_POR_EL0 "S3_3_C10_C2_4"
|
||||
|
||||
static inline uint64_t get_por_el0(void)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
asm volatile("mrs %0, " SYS_POR_EL0 "\n" : "=r"(val));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void set_por_el0(uint64_t val)
|
||||
{
|
||||
asm volatile("msr " SYS_POR_EL0 ", %0\n" :: "r"(val));
|
||||
}
|
||||
|
||||
static inline bool feats_ok(struct tdescr *td)
|
||||
{
|
||||
if (td->feats_incompatible & td->feats_supported)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2026 Arm Ltd
|
||||
*
|
||||
* Verify that the POR_EL0 register is left untouched on sigreturn if the
|
||||
* POE frame record is missing.
|
||||
*/
|
||||
|
||||
#include <asm/sigcontext.h>
|
||||
|
||||
#include "test_signals_utils.h"
|
||||
#include "testcases.h"
|
||||
|
||||
#define POR_EL0_INIT 0x07ul
|
||||
#define POR_EL0_CUSTOM 0x77ul
|
||||
|
||||
static bool failed_check;
|
||||
|
||||
static bool modify_por_el0(struct tdescr *td)
|
||||
{
|
||||
set_por_el0(POR_EL0_CUSTOM);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int signal_remove_poe_context(struct tdescr *td, siginfo_t *si,
|
||||
ucontext_t *uc)
|
||||
{
|
||||
struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uc);
|
||||
size_t resv_size = GET_UCP_RESV_SIZE(uc);
|
||||
struct _aarch64_ctx *poe_ctx_head;
|
||||
|
||||
poe_ctx_head = get_header(ctx, POE_MAGIC, resv_size, NULL);
|
||||
if (!poe_ctx_head) {
|
||||
fprintf(stderr, "Missing poe_context record\n");
|
||||
failed_check = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually removing the record would require moving down the next
|
||||
* records. An easier option is to turn it into an ESR record, which is
|
||||
* ignored by sigreturn().
|
||||
*/
|
||||
poe_ctx_head->magic = ESR_MAGIC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_por_el0_preserved(struct tdescr *td)
|
||||
{
|
||||
uint64_t por_el0 = get_por_el0();
|
||||
|
||||
if (por_el0 == POR_EL0_INIT) {
|
||||
fprintf(stderr, "POR_EL0 preserved\n");
|
||||
} else {
|
||||
fprintf(stderr, "POR_EL0 unexpectedly set to %lx\n", por_el0);
|
||||
failed_check = true;
|
||||
}
|
||||
|
||||
td->pass = !failed_check;
|
||||
}
|
||||
|
||||
struct tdescr tde = {
|
||||
.name = "POR_EL0 missing poe_context",
|
||||
.descr = "Remove poe_context record and check POR_EL0 is preserved",
|
||||
.feats_required = FEAT_POE,
|
||||
.timeout = 3,
|
||||
.sig_trig = SIGUSR1,
|
||||
.init = modify_por_el0,
|
||||
.run = signal_remove_poe_context,
|
||||
.check_result = check_por_el0_preserved,
|
||||
};
|
||||
64
tools/testing/selftests/arm64/signal/testcases/poe_restore.c
Normal file
64
tools/testing/selftests/arm64/signal/testcases/poe_restore.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2026 Arm Ltd
|
||||
*
|
||||
* Verify that the POR_EL0 register is saved and restored as expected on signal
|
||||
* entry/return.
|
||||
*/
|
||||
|
||||
#include <asm/sigcontext.h>
|
||||
|
||||
#include "test_signals_utils.h"
|
||||
#include "testcases.h"
|
||||
|
||||
#define POR_EL0_INIT 0x07ul
|
||||
#define POR_EL0_CUSTOM 0x77ul
|
||||
|
||||
static bool failed_check;
|
||||
|
||||
static bool modify_por_el0(struct tdescr *td)
|
||||
{
|
||||
set_por_el0(POR_EL0_CUSTOM);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int signal_check_por_el0_reset(struct tdescr *td, siginfo_t *si,
|
||||
ucontext_t *uc)
|
||||
{
|
||||
uint64_t signal_por_el0 = get_por_el0();
|
||||
|
||||
if (signal_por_el0 != POR_EL0_INIT) {
|
||||
fprintf(stderr, "POR_EL0 is %lx in signal handler (expected %lx)\n",
|
||||
signal_por_el0, POR_EL0_INIT);
|
||||
failed_check = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_por_el0_restored(struct tdescr *td)
|
||||
{
|
||||
uint64_t por_el0 = get_por_el0();
|
||||
|
||||
if (por_el0 == POR_EL0_CUSTOM) {
|
||||
fprintf(stderr, "POR_EL0 restored\n");
|
||||
} else {
|
||||
fprintf(stderr, "POR_EL0 was %lx but is now %lx\n",
|
||||
POR_EL0_CUSTOM, por_el0);
|
||||
failed_check = true;
|
||||
}
|
||||
|
||||
td->pass = !failed_check;
|
||||
}
|
||||
|
||||
struct tdescr tde = {
|
||||
.name = "POR_EL0 restore",
|
||||
.descr = "Validate that POR_EL0 is saved/restored on signal entry/return",
|
||||
.feats_required = FEAT_POE,
|
||||
.timeout = 3,
|
||||
.sig_trig = SIGUSR1,
|
||||
.init = modify_por_el0,
|
||||
.run = signal_check_por_el0_reset,
|
||||
.check_result = check_por_el0_restored,
|
||||
};
|
||||
|
|
@ -21,21 +21,6 @@ static union {
|
|||
char buf[1024 * 128];
|
||||
} context;
|
||||
|
||||
#define SYS_POR_EL0 "S3_3_C10_C2_4"
|
||||
|
||||
static uint64_t get_por_el0(void)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
asm volatile(
|
||||
"mrs %0, " SYS_POR_EL0 "\n"
|
||||
: "=r"(val)
|
||||
:
|
||||
: );
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int poe_present(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
|
||||
{
|
||||
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
|
||||
|
|
|
|||
|
|
@ -130,9 +130,10 @@ static inline u64 get_pkey_bits(u64 reg, int pkey)
|
|||
static inline void aarch64_write_signal_pkey(ucontext_t *uctxt, u64 pkey)
|
||||
{
|
||||
struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uctxt);
|
||||
size_t resv_size = GET_UCP_RESV_SIZE(uctxt);
|
||||
struct poe_context *poe_ctx =
|
||||
(struct poe_context *) get_header(ctx, POE_MAGIC,
|
||||
sizeof(uctxt->uc_mcontext), NULL);
|
||||
resv_size, NULL);
|
||||
if (poe_ctx)
|
||||
poe_ctx->por_el0 = pkey;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user