1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2023 Oxide Computer Company
14 */
15
16 #ifndef _XSAVE_UTIL_H
17 #define _XSAVE_UTIL_H
18
19 /*
20 * This file contains misc. pieces for use between our tests. This is
21 * implemented in both xsave_util.c and xsave_asm32.s and xsave_asm64.s.
22 */
23
24 #ifndef _ASM
25 #include <sys/types.h>
26 #include <stdint.h>
27 #include <ucontext.h>
28 #include <stdio.h>
29 #include <procfs.h>
30 #include <libproc.h>
31 #endif /* !_ASM */
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif
36
37 /*
38 * While we would prefer an enum, this is a macro so it can be shared with the
39 * assembler code.
40 */
41 #define XSU_XMM 0
42 #define XSU_YMM 1
43 #define XSU_ZMM 2
44
45 /*
46 * These are definitions that vary based on whether we're in an ILP32 or LP64
47 * environment because of how the ISA works.
48 */
49 #ifdef __amd64
50 #define XSU_MAX_XMM 16
51 #define XSU_MAX_YMM 16
52 #define XSU_MAX_ZMM 32
53 #else
54 #define XSU_MAX_XMM 8
55 #define XSU_MAX_YMM 8
56 #define XSU_MAX_ZMM 8
57 #endif
58
59 #define XSU_XMM_U32 4
60 #define XSU_YMM_U32 8
61 #define XSU_ZMM_U32 16
62
63 #ifndef _ASM
64
65 /*
66 * Redefine the xsave header here for the test cases so we can avoid exposing
67 * this out of the kernel. Right now the uc_xsave_t isn't defined under
68 * _KMEMUSER and we're trying to hold onto that (or a similar style solution)
69 * while we can.
70 */
71 #define UC_XSAVE_VERS (('u' << 24) | ('c' << 16) | 0x01)
72 typedef struct uc_xsave {
73 uint32_t ucx_vers;
74 uint32_t ucx_len;
75 uint64_t ucx_bv;
76 } uc_xsave_t;
77
78 /*
79 * This structure represents a generic AVX-512 FPU state that can hold up to 32
80 * registers. If only XMM or YMM are valid, then the first 8 and 16 bytes will
81 * be valid respectively and the latter 16 entries won't be used in 64-bit code.
82 * In 32-bit code only the valid FPU entries will present.
83 */
84 typedef struct xsu_fpu {
85 upad512_t xf_reg[32];
86 uint64_t xf_opmask[8];
87 } xsu_fpu_t;
88
89 extern uint32_t xsu_hwsupport(void);
90
91 /*
92 * This deterministically fills the contents of an FPU structure. It will zero
93 * the entire structure and then fill in the appropriate parts based on the type
94 * of FPU we have.
95 */
96 extern void xsu_fill(xsu_fpu_t *, uint32_t, uint32_t);
97
98 /*
99 * Dump the contents of the FPU in a deterministic fashion to a specified file.
100 */
101 extern void xsu_dump(FILE *, const xsu_fpu_t *, uint32_t);
102
103 /*
104 * These routines read and write the state of the FPU. This will cover the
105 * selected hardware bits.
106 */
107 extern void xsu_setfpu(const xsu_fpu_t *, uint32_t);
108 extern void xsu_getfpu(xsu_fpu_t *, uint32_t);
109
110 /*
111 * This is used to overwrite the contents of a ucontext_t with the resulting FPU
112 * state. The xc_xsave will be replaced with a pointer to something of our own
113 * sizing.
114 */
115 extern void xsu_overwrite_uctx(ucontext_t *, const xsu_fpu_t *, uint32_t);
116
117 /*
118 * Diff two different fpu sets and see if they're identical or not. Only the
119 * bytes that correspond to the indicated pieces will be checked.
120 */
121 extern boolean_t xsu_same(const xsu_fpu_t *, const xsu_fpu_t *, uint32_t);
122
123 /*
124 * This function allocates and sets up the prxregset_hdr_t and associated notes
125 * with their expected values based on the hardware support values.
126 */
127 extern void xsu_xregs_alloc(void **, size_t *, uint32_t);
128
129 /*
130 * This is a common function that just sleeps and is meant to be here for test
131 * programs that want a thread to mess with.
132 */
133 extern void *xsu_sleeper_thread(void *);
134
135 /*
136 * Convert a given xsu_fpu_t state into something that we can set via xregs.
137 */
138 extern void xsu_fpu_to_xregs(const xsu_fpu_t *, uint32_t, prxregset_t **,
139 size_t *);
140
141 typedef struct xsu_proc {
142 char *xp_prog;
143 char *xp_arg;
144 const char *xp_object;
145 const char *xp_symname;
146 struct ps_prochandle *xp_proc;
147 uintptr_t xp_addr;
148 ulong_t xp_instr;
149 int xp_wait;
150 } xsu_proc_t;
151
152 /*
153 * This pair of functions gives us a mini-debugging context. The first sets up a
154 * program that is ready and paused at a breakpoint.
155 */
156 extern void xsu_proc_bkpt(xsu_proc_t *);
157 extern void xsu_proc_finish(xsu_proc_t *);
158
159 /*
160 * Set the xmm portion of an fpregset based on a seed.
161 */
162 extern void xsu_fpregset_xmm_set(fpregset_t *, uint32_t);
163 extern void xsu_xregs_xmm_set(prxregset_t *, uint32_t);
164
165 /*
166 * Go through and see if the data in an fpregs section is equivalent to an xregs
167 * XMM section. This focuses on the control words and xmm data, we do not bother
168 * with the x87 registers themselves and not all of the 32-bit pieces.
169 */
170 extern boolean_t xsu_fpregs_cmp(const fpregset_t *, const prxregset_t *);
171
172 /*
173 * Given two xregs structures, check if the given component in them has
174 * identical data. Note, this assumes that the structure is valid-ish. That is,
175 * that all the info structures point to valid data.
176 */
177 extern boolean_t xsu_xregs_comp_equal(const prxregset_t *, const prxregset_t *,
178 uint32_t);
179
180 #endif /* !_ASM */
181
182 #ifdef __cplusplus
183 }
184 #endif
185
186 #endif /* _XSAVE_UTIL_H */