xref: /openbsd-src/regress/sys/kern/ptrace/xstate/xstate.c (revision 664d632d7edbc89451a03affb2c37bcfdfaa78ac)
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