1 /* $OpenBSD */ 2 3 #include <sys/types.h> 4 #include <sys/ptrace.h> 5 #include <sys/wait.h> 6 7 #include <err.h> 8 #include <signal.h> 9 #include <stdint.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 struct cpuid { 16 uint32_t a, b, c, d; 17 }; 18 19 struct xstate { 20 struct { 21 uint8_t buf[1024]; 22 uint32_t size; 23 } area; 24 25 struct { 26 uint32_t supported; 27 uint32_t offset; 28 uint32_t size; 29 } components[3]; 30 #define XSTATE_COMPONENT_X87 0 31 #define XSTATE_COMPONENT_SSE 1 32 #define XSTATE_COMPONENT_AVX 2 33 }; 34 35 struct u128 { 36 uint64_t v[2]; 37 } __attribute__((packed)); 38 39 struct ymm { 40 struct u128 xmm; 41 struct u128 ymm; 42 } __attribute__((packed)); 43 44 extern void ymm_write(void); 45 extern void ymm_read(struct ymm[16]); 46 47 static inline void 48 cpuid(uint32_t leaf, uint32_t subleaf, struct cpuid *out) 49 { 50 __asm__("cpuid" 51 : "=a" (out->a), "=b" (out->b), "=c" (out->c), "=d" (out->d) 52 : "a" (leaf), "c" (subleaf)); 53 } 54 55 static int 56 xstate_init(struct xstate *xstate, pid_t pid) 57 { 58 #define CPUID_01_C_XSAVE_MASK (1 << 26) 59 #define XCR0_XMM_MASK (1 << 1) 60 #define XCR0_YMM_MASK (1 << 2) 61 62 struct cpuid leaf; 63 struct ptrace_xstate_info info; 64 65 cpuid(0x1, 0, &leaf); 66 if ((leaf.c & CPUID_01_C_XSAVE_MASK) == 0) { 67 printf("SKIPPED: XSAVE not enumerated"); 68 return 1; 69 } 70 71 memset(xstate, 0, sizeof(*xstate)); 72 73 if (ptrace(PT_GETXSTATE_INFO, pid, 74 (caddr_t)&info, sizeof(info)) == -1) 75 err(1, "ptrace: PT_GETXSTATE_INFO"); 76 if (info.xsave_len > sizeof(xstate->area.buf)) 77 errx(1, "xstate buffer too small"); 78 xstate->area.size = info.xsave_len; 79 80 if ((info.xsave_mask & XCR0_XMM_MASK) == 0 || 81 (info.xsave_mask & XCR0_YMM_MASK) == 0) { 82 printf("SKIPPED: SSE/AVX disabled in XCR0\n"); 83 return 1; 84 } 85 86 xstate->components[XSTATE_COMPONENT_SSE].supported = 1; 87 /* Part of legacy region in XSAVE area. */ 88 xstate->components[XSTATE_COMPONENT_SSE].offset = 160; 89 xstate->components[XSTATE_COMPONENT_SSE].size = 256; 90 91 cpuid(0xd, XSTATE_COMPONENT_AVX, &leaf); 92 xstate->components[XSTATE_COMPONENT_AVX].supported = 1; 93 xstate->components[XSTATE_COMPONENT_AVX].offset = leaf.b; 94 xstate->components[XSTATE_COMPONENT_AVX].size = leaf.a; 95 96 return 0; 97 } 98 99 static void 100 xstate_ymm_read(struct xstate *xstate, int regno, struct ymm *rd) 101 { 102 struct u128 *xmm = (struct u128 *)(xstate->area.buf + 103 xstate->components[XSTATE_COMPONENT_SSE].offset); 104 struct u128 *ymm = (struct u128 *)(xstate->area.buf + 105 xstate->components[XSTATE_COMPONENT_AVX].offset); 106 107 rd->xmm = xmm[regno]; 108 rd->ymm = ymm[regno]; 109 } 110 111 static void 112 xstate_ymm_write(struct xstate *xstate, int regno, struct ymm *wr) 113 { 114 struct u128 *xmm = (struct u128 *)(xstate->area.buf + 115 xstate->components[XSTATE_COMPONENT_SSE].offset); 116 struct u128 *ymm = (struct u128 *)(xstate->area.buf + 117 xstate->components[XSTATE_COMPONENT_AVX].offset); 118 119 xmm[regno] = wr->xmm; 120 ymm[regno] = wr->ymm; 121 } 122 123 static void 124 wait_until_stopped(pid_t pid) 125 { 126 int status; 127 128 if (waitpid(pid, &status, 0) == -1) 129 err(1, "waitpid"); 130 if (!WIFSTOPPED(status)) 131 errx(1, "expected traced process to be stopped"); 132 } 133 134 static int 135 check_ymm(const struct ymm ymm[16]) 136 { 137 int error = 0; 138 int i; 139 140 for (i = 0; i < 16; i++) { 141 struct ymm exp; 142 143 memset(&exp, (i << 4) | i, 32); 144 if (memcmp(&exp, &ymm[i], 32) == 0) 145 continue; 146 147 warnx("ymm%d: expected %016llx%016llx%016llx%016llx," 148 " got %016llx%016llx%016llx%016llx", i, 149 exp.ymm.v[1], exp.ymm.v[0], 150 exp.xmm.v[1], exp.xmm.v[0], 151 ymm[i].ymm.v[1], ymm[i].ymm.v[0], 152 ymm[i].xmm.v[1], ymm[i].xmm.v[0]); 153 error = 1; 154 } 155 156 return error; 157 } 158 159 static int 160 test_ymm_get(struct xstate *xstate) 161 { 162 struct ymm ymm[16]; 163 pid_t pid; 164 int i; 165 166 pid = fork(); 167 if (pid == 0) { 168 ptrace(PT_TRACE_ME, 0, 0, 0); 169 ymm_write(); 170 raise(SIGSTOP); 171 /* UNREACHABLE */ 172 } 173 174 wait_until_stopped(pid); 175 176 if (xstate_init(xstate, pid)) 177 return 0; 178 179 if (ptrace(PT_GETXSTATE, pid, 180 xstate->area.buf, xstate->area.size) == -1) 181 err(1, "ptrace: PT_GETXSTATE"); 182 for (i = 0; i < 16; i++) 183 xstate_ymm_read(xstate, i, &ymm[i]); 184 return check_ymm(ymm); 185 } 186 187 static int 188 test_ymm_set(struct xstate *xstate) 189 { 190 pid_t pid; 191 int i, status; 192 193 pid = fork(); 194 if (pid == 0) { 195 struct ymm ymm[16]; 196 197 ptrace(PT_TRACE_ME, 0, 0, 0); 198 raise(SIGSTOP); 199 ymm_read(ymm); 200 _exit(check_ymm(ymm)); 201 } 202 203 wait_until_stopped(pid); 204 205 if (xstate_init(xstate, pid)) 206 return 0; 207 208 if (ptrace(PT_GETXSTATE, pid, 209 xstate->area.buf, xstate->area.size) == -1) 210 err(1, "ptrace: PT_GETXSTATE"); 211 for (i = 0; i < 16; i++) { 212 struct ymm ymm; 213 214 memset(&ymm, (i << 4) | i, 32); 215 xstate_ymm_write(xstate, i, &ymm); 216 } 217 218 if (ptrace(PT_SETXSTATE, pid, 219 xstate->area.buf, xstate->area.size) == -1) 220 err(1, "ptrace: PT_SETXSTATE"); 221 222 if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) == -1) 223 err(1, "ptrace: PT_CONTINUE"); 224 if (waitpid(pid, &status, 0) == -1) 225 err(1, "waitpid"); 226 return WIFEXITED(status) && WEXITSTATUS(status) == 0 ? 0 : 1; 227 } 228 229 static void __attribute__((noreturn)) 230 usage(void) 231 { 232 fprintf(stderr, "usage: xstate test-case\n"); 233 exit(1); 234 } 235 236 int 237 main(int argc, char *argv[]) 238 { 239 struct { 240 const char *name; 241 int (*test)(struct xstate *); 242 } tests[] = { 243 { "xstate-ymm-get", test_ymm_get }, 244 { "xstate-ymm-set", test_ymm_set }, 245 }; 246 struct xstate xstate; 247 unsigned int i; 248 249 if (argc != 2) 250 usage(); 251 252 for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { 253 if (strcmp(argv[1], tests[i].name) == 0) 254 return tests[i].test(&xstate); 255 } 256 257 warnx("no such test case"); 258 return 1; 259 } 260