1 // SPDX-License-Identifier: GPL-2.0-only
2 #define _GNU_SOURCE
3 #include <stdio.h>
4 #include <signal.h>
5 #include <string.h>
6 #include <sys/ucontext.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <cpuid.h>
11 #include <unistd.h>
12 #include <sys/syscall.h>
13 #include <stddef.h>
14 #include <setjmp.h>
15
16 #include "helpers.h"
17 #include "xstate.h"
18
19 #ifndef FP_XSTATE_MAGIC2_SIZE
20 #define FP_XSTATE_MAGIC2_SIZE sizeof(FP_XSTATE_MAGIC2)
21 #endif
22
23 /*
24 * This test verifies the FPU portability and consistency of the signal frame.
25 *
26 * - test_valid_shrunk_xstate_size:
27 * Verifies that the kernel restores state from a frame with xstate_size
28 * shrunk to only include active features.
29 *
30 * - test_invalid_shrunk_xstate_size:
31 * Verifies that the kernel rejects a frame if xstate_size is too small for
32 * the features enabled in xfeatures.
33 */
34
35 #define SIGFRAME_XSTATE_HDR_OFFSET 512
36 #define XSTATE_SSE_ONLY_SIZE (SIGFRAME_XSTATE_HDR_OFFSET + XSAVE_HDR_SIZE)
37 #define XFEATURE_MASK_FPSSE ((1 << XFEATURE_FP) | (1 << XFEATURE_SSE))
38
39 static uint32_t ymm_offset;
40 static uint32_t xstate_size_ymm;
41 static pid_t self_pid;
42
43 /*
44 * Load %ymm0 from @v, invoke SYS_kill to deliver @sig, and store the
45 * restored %ymm0 state back into @v within a single inline assembly
46 * block so the compiler cannot clobber %xmm0/%ymm0 between steps.
47 */
48 __attribute__((target("avx")))
raise_with_ymm0(int sig,uint64_t * v)49 static void raise_with_ymm0(int sig, uint64_t *v)
50 {
51 register long rax asm("rax") = SYS_kill;
52 register long rdi asm("rdi") = self_pid;
53 register long rsi asm("rsi") = sig;
54
55 asm volatile ("vmovdqu %0, %%ymm0\n\t"
56 "syscall\n\t"
57 "vmovdqu %%ymm0, %0"
58 : "+m" (*(char (*)[32])v), "+r" (rax)
59 : "r" (rdi), "r" (rsi)
60 : "rcx", "r11", "ymm0", "memory");
61 }
62
63 /*
64 * Avoid using printf() in signal handlers as it is not
65 * async-signal-safe.
66 */
67 #define SIGNAL_BUF_LEN 1024
68 static char sig_err_buf[SIGNAL_BUF_LEN];
69
sig_print(const char * msg)70 static void sig_print(const char *msg)
71 {
72 int left = SIGNAL_BUF_LEN - strlen(sig_err_buf) - 1;
73
74 strncat(sig_err_buf, msg, left);
75 }
76
check_avx_support(void)77 static void check_avx_support(void)
78 {
79 uint32_t eax, ebx, ecx, edx;
80 struct xstate_info xstate;
81
82 /* Check CPUID.01H:ECX.OSXSAVE[bit 27] before calling xgetbv to avoid #UD */
83 __cpuid(1, eax, ebx, ecx, edx);
84 if (!(ecx & (1 << 27)))
85 ksft_exit_skip("OSXSAVE not enabled by OS\n");
86
87 /* Check XCR0[2] (YMM) is enabled by OS */
88 if (!(xgetbv(0) & (1 << XFEATURE_YMM)))
89 ksft_exit_skip("AVX (YMM) not enabled in XCR0\n");
90
91 xstate = get_xstate_info(XFEATURE_YMM);
92 if (!xstate.size)
93 ksft_exit_skip("AVX not supported by hardware\n");
94
95 ymm_offset = xstate.xbuf_offset;
96 xstate_size_ymm = xstate.xbuf_offset + xstate.size;
97 }
98
99 #define TEST_YMMH_VAL (0x5656565656565656UL)
100
__handle_shrunk_xstate_size(int sig,siginfo_t * si,void * ucp,bool valid_size)101 static void __handle_shrunk_xstate_size(int sig, siginfo_t *si, void *ucp, bool valid_size)
102 {
103 uint64_t xfeatures, *ymmh_p;
104 struct xsave_buffer *xbuf;
105 struct _fpx_sw_bytes *sw;
106 ucontext_t *uc = ucp;
107 void *fp;
108
109 fp = uc->uc_mcontext.fpregs;
110 if (!fp) {
111 sig_print("fpregs is NULL\n");
112 return;
113 }
114
115 sw = get_fpx_sw_bytes(fp);
116 if (sw->magic1 != FP_XSTATE_MAGIC1) {
117 sig_print("magic1 is not valid\n");
118 return;
119 }
120
121 xbuf = (struct xsave_buffer *)fp;
122
123 /*
124 * Both test cases shrink the frame to contain only AVX (FP + SSE + YMM).
125 * If valid_size is true, set xstate_size to match the enabled features.
126 * If valid_size is false, set xstate_size too small (SSE only), which
127 * the kernel must reject.
128 */
129 if (valid_size)
130 sw->xstate_size = xstate_size_ymm;
131 else
132 sw->xstate_size = XSTATE_SSE_ONLY_SIZE;
133
134 xfeatures = get_xstatebv(xbuf);
135 xfeatures &= XFEATURE_MASK_FPSSE | (1 << XFEATURE_YMM);
136 set_xstatebv(xbuf, xfeatures);
137 set_fpx_sw_bytes_features(fp, xfeatures);
138
139 *(uint32_t *)(fp + sw->xstate_size) = FP_XSTATE_MAGIC2;
140
141 if (valid_size) {
142 ymmh_p = (uint64_t *)(fp + ymm_offset);
143 ymmh_p[0] = TEST_YMMH_VAL;
144 ymmh_p[1] = TEST_YMMH_VAL + 1;
145 }
146
147 /* clear everything after MAGIC2. */
148 if (sw->xstate_size + FP_XSTATE_MAGIC2_SIZE < sw->extended_size)
149 memset(fp + sw->xstate_size + FP_XSTATE_MAGIC2_SIZE, 0,
150 sw->extended_size - sw->xstate_size - FP_XSTATE_MAGIC2_SIZE);
151 }
152
handle_valid_shrunk_xstate_size(int sig,siginfo_t * si,void * ucp)153 static void handle_valid_shrunk_xstate_size(int sig, siginfo_t *si, void *ucp)
154 {
155 __handle_shrunk_xstate_size(sig, si, ucp, true);
156 }
157
handle_invalid_shrunk_xstate_size(int sig,siginfo_t * si,void * ucp)158 static void handle_invalid_shrunk_xstate_size(int sig, siginfo_t *si, void *ucp)
159 {
160 __handle_shrunk_xstate_size(sig, si, ucp, false);
161 }
162
test_valid_shrunk_xstate_size(void)163 static void test_valid_shrunk_xstate_size(void)
164 {
165 uint64_t v[4];
166
167 sig_err_buf[0] = 0;
168 sethandler(SIGUSR1, handle_valid_shrunk_xstate_size, 0);
169
170 v[0] = 0x1111111111111111ULL;
171 v[1] = 0x2222222222222222ULL;
172 v[2] = 0x3333333333333333ULL;
173 v[3] = 0x4444444444444444ULL;
174 raise_with_ymm0(SIGUSR1, v);
175
176 if (sig_err_buf[0])
177 ksft_test_result_fail("%s\n", sig_err_buf);
178 else if (v[2] == TEST_YMMH_VAL && v[3] == (TEST_YMMH_VAL + 1))
179 ksft_test_result_pass("YMM state restored correctly from shrunk frame\n");
180 else
181 ksft_test_result_fail(
182 "Got upper bits: 0x%lx 0x%lx (expected %lx %lx)\n",
183 v[2], v[3], TEST_YMMH_VAL, TEST_YMMH_VAL + 1);
184
185 clearhandler(SIGUSR1);
186 }
187
188 static sigjmp_buf segv_jmpbuf;
189
handle_segv(int sig,siginfo_t * si,void * ucp)190 static void handle_segv(int sig, siginfo_t *si, void *ucp)
191 {
192 siglongjmp(segv_jmpbuf, 1);
193 }
194
test_invalid_shrunk_xstate_size(void)195 static void test_invalid_shrunk_xstate_size(void)
196 {
197 uint64_t v[4];
198
199 sig_err_buf[0] = 0;
200 sethandler(SIGUSR1, handle_invalid_shrunk_xstate_size, 0);
201 sethandler(SIGSEGV, handle_segv, 0);
202
203 if (sigsetjmp(segv_jmpbuf, 1) == 0) {
204 v[0] = 0x1111111111111111ULL;
205 v[1] = 0x2222222222222222ULL;
206 v[2] = 0x3333333333333333ULL;
207 v[3] = 0x4444444444444444ULL;
208 raise_with_ymm0(SIGUSR1, v);
209 sig_print("Inconsistent size was NOT rejected\n");
210 }
211
212 clearhandler(SIGUSR1);
213 clearhandler(SIGSEGV);
214
215 if (sig_err_buf[0])
216 ksft_test_result_fail("%s\n", sig_err_buf);
217 else
218 ksft_test_result_pass("Inconsistent size correctly rejected\n");
219 }
220
main(void)221 int main(void)
222 {
223 ksft_print_header();
224 ksft_set_plan(2);
225
226 self_pid = getpid();
227
228 check_avx_support();
229
230 test_valid_shrunk_xstate_size();
231 test_invalid_shrunk_xstate_size();
232
233 ksft_finished();
234 return 0;
235 }
236