Add a test to validate emulation support for extended GPRs (EGPRs). APX extends the x86 general-purpose register set with R16-R31. The test follows the pattern established by existing AVX tests: it performs register-to-memory and register-to-register moves across various combinations, and cross-checks emulated execution against native results. For convenience, make available only when built with a compiler that supports APX. Otherwise, raw bytecode is ugly and inefficient, and even macrofied bytecode looks to be an overkill. Signed-off-by: Chang S. Bae --- Note: As suggested by Paolo [1], I also think this piece of testing can go to the KVM test tree. Additional (new) cases like testing KVM ABI and EGPR state management will be included to the KVM series (from next revision). [1]: https://lore.kernel.org/lkml/072ad56c-24d5-425a-9faf-c1a3ba732005@redhat.com/ --- lib/linux/compiler.h | 5 +++ lib/x86/processor.h | 1 + x86/xsave.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/lib/linux/compiler.h b/lib/linux/compiler.h index 8e62aae02b5e..e9df821d50fd 100644 --- a/lib/linux/compiler.h +++ b/lib/linux/compiler.h @@ -13,6 +13,11 @@ + __GNUC_PATCHLEVEL__) #ifdef __clang__ + +#define CLANG_VERSION (__clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__) + #if __has_builtin(__builtin_add_overflow) && \ __has_builtin(__builtin_sub_overflow) && \ __has_builtin(__builtin_mul_overflow) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 42dd2d2a4787..85e96741a6bf 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -198,6 +198,7 @@ static inline u64 get_non_canonical(u64 addr, u64 mask) #define XFEATURE_MASK_LBR BIT_ULL(15) #define XFEATURE_MASK_XTILE_CFG BIT_ULL(17) #define XFEATURE_MASK_XTILE_DATA BIT_ULL(18) +#define XFEATURE_MASK_APX BIT_ULL(19) #define XFEATURE_MASK_FP_SSE (XFEATURE_MASK_FP | XFEATURE_MASK_SSE) diff --git a/x86/xsave.c b/x86/xsave.c index 254f9fde6976..a1e57dea18db 100644 --- a/x86/xsave.c +++ b/x86/xsave.c @@ -1,6 +1,11 @@ #include "libcflat.h" #include "desc.h" #include "processor.h" +#include "linux/compiler.h" + +#if GCC_VERSION >= 140000 || CLANG_VERSION >= 180000 +#define TEST_EGPRS +#endif char __attribute__((aligned(32))) v32_1[32]; char __attribute__((aligned(32))) v32_2[32]; @@ -43,6 +48,78 @@ do { \ __TEST_VMOVDQA(ymm##r1, ymm##r2, KVM_FEP); \ } while (0) +#ifdef TEST_EGPRS + +char __attribute__((aligned(8))) r8_1[8]; +char __attribute__((aligned(8))) r8_2[8]; +char __attribute__((aligned(8))) r8_3[8]; + +static void initialize_gpr_buffers(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(r8_1); i++) + r8_1[i] = (char)rdtsc(); + + memset(r8_2, 0, sizeof(r8_2)); + memset(r8_3, 0, sizeof(r8_3)); +} + +#define __TEST_MOV_GPRS(reg1, reg2, FEP) \ +do { \ + asm volatile(FEP "mov r8_1(%%rip), %%" #reg1 "\n" \ + FEP "mov %%" #reg1 ", %%" #reg2 "\n" \ + FEP "mov %%" #reg2 ", r8_2(%%rip)\n" \ + "mov %%" #reg2 ", r8_3(%%rip)\n" \ + ::: "memory", #reg1, #reg2); \ + \ + report(!memcmp(r8_1, r8_2, sizeof(r8_1)), \ + "%s MOV using " #reg1 " and " #reg2, \ + strlen(FEP) ? "Emulated" : "Native"); \ + report(!memcmp(r8_1, r8_3, sizeof(r8_1)), \ + "%s MOV using " #reg1 " and " #reg2, \ + strlen(FEP) ? "Emulated+Native" : "Native"); \ +} while (0) + +#define TEST_MOV_GPRS(r1, r2) \ +do { \ + initialize_gpr_buffers(); \ + \ + __TEST_MOV_GPRS(r##r1, r##r2, ""); \ + \ + if (is_fep_available) \ + __TEST_MOV_GPRS(r##r1, r##r2, KVM_FEP); \ +} while (0) + +static __attribute__((target("apxf"))) void test_mov_egprs(void) +{ + TEST_MOV_GPRS(16, 31); + TEST_MOV_GPRS(17, 30); + TEST_MOV_GPRS(18, 29); + TEST_MOV_GPRS(19, 28); + TEST_MOV_GPRS(20, 27); + TEST_MOV_GPRS(21, 26); + TEST_MOV_GPRS(22, 25); + TEST_MOV_GPRS(23, 24); + TEST_MOV_GPRS(24, 23); + TEST_MOV_GPRS(25, 22); + TEST_MOV_GPRS(26, 21); + TEST_MOV_GPRS(27, 20); + TEST_MOV_GPRS(28, 19); + TEST_MOV_GPRS(29, 18); + TEST_MOV_GPRS(30, 17); + TEST_MOV_GPRS(31, 16); +} + +#else + +static test_mov_egprs(void) +{ + report_skip("Skip EGPR MOV tests: compiler does not support APX"); +} + +#endif /* TEST_EGPRS */ + static void test_write_xcr0(u64 val) { u64 xcr0_alias = rdtsc() << 32, cur; @@ -93,6 +170,12 @@ static __attribute__((target("avx"))) void test_avx_vmovdqa(void) TEST_VMOVDQA(15, 1); } +static void test_apx(void) +{ + test_write_xcr0(XFEATURE_MASK_FP_SSE | XFEATURE_MASK_APX); + test_mov_egprs(); +} + static void test_unsupported_xcrs(void) { u64 ign; @@ -151,6 +234,9 @@ static void test_xsave(void) if (supported_xcr0 & XFEATURE_MASK_YMM) test_avx_vmovdqa(); + if (supported_xcr0 & XFEATURE_MASK_APX) + test_apx(); + report(write_xcr0_safe(0) == GP_VECTOR, "Write XCR0 = 0 - expect #GP"); -- 2.51.0