@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
*_32
*_64
+*.o
single_step_syscall
sysret_ss_attrs
syscall_nt
@@ -18,7 +18,7 @@ TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip syscall_numbering \
- corrupt_xstate_header amx
+ corrupt_xstate_header amx xstate
# Some selftests require 32bit support enabled also on 64bit systems
TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
@@ -69,7 +69,7 @@ all_32: $(BINARIES_32)
all_64: $(BINARIES_64)
-EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64)
+EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64) *.o
$(BINARIES_32): $(OUTPUT)/%_32: %.c helpers.h
$(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl -lm
@@ -109,3 +109,10 @@ $(OUTPUT)/test_syscall_vdso_32: thunks_32.S
# state.
$(OUTPUT)/check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static
$(OUTPUT)/check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static
+
+# xstate_64 is special: it needs xstate_helpers.o to prevent GCC from
+# generating any FP code by mistake and stdlib.h can't be used due to
+# "-mno-sse" parameter, so compile xstate_64 with the code file xstate.c
+# which can use stdlib.h and xstate_helpers.o which cannot use stdlib.h
+xstate_helpers.o: CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-avx -mno-pku
+$(OUTPUT)/xstate_64: xstate_helpers.o
new file mode 100644
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * xstate.c - test the XSAVE functionality with the following basic case.
+ *
+ * The XSAVE feature set supports the saving and restoring of state components.
+ * It tests "FP, SSE(XMM), AVX2(YMM), AVX512_OPMASK/AVX512_ZMM_Hi256/
+ * AVX512_Hi16_ZMM and PKRU parts" xstates with following cases:
+ * The contents of these xstates in the process should not change after the
+ * signal handling.
+ *
+ * The regions and reserved bytes of the components tested for XSAVE feature
+ * are as follows:
+ * x87(FP)/SSE (0 - 159 bytes)
+ * SSE(XMM part) (160-415 bytes)
+ * Reserved (416-511 bytes)
+ * Header_used (512-527 bytes; XSTATE BV(bitmap vector) mask:512-519 bytes)
+ * Header_reserved(528-575 bytes must be 00)
+ * YMM (Offset:CPUID.(EAX=0D,ECX=2).EBX Size:CPUID(EAX=0D,ECX=2).EAX)
+ * AVX512_OPMASK (Offset:CPUID.(EAX=0D,ECX=5).EBX Size:CPUID(EAX=0D,ECX=5).EAX)
+ * ZMM_Hi256 (Offset:CPUID.(EAX=0D,ECX=6).EBX Size:CPUID(EAX=0D,ECX=6).EAX)
+ * Hi16_ZMM (Offset:CPUID.(EAX=0D,ECX=7).EBX Size:CPUID(EAX=0D,ECX=7).EAX)
+ * PKRU (Offset:CPUID.(EAX=0D,ECX=9).EBX Size:CPUID(EAX=0D,ECX=9).EAX)
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <malloc.h>
+#include <stdlib.h>
+
+#include "xstate.h"
+#include "xstate_helpers.h"
+#include "../kselftest.h"
+
+#define NUM_TESTS 1
+#define xstate_test_array_init(idx, init_opt, fill_opt) \
+ do { \
+ xstate_tests[idx].init = init_opt; \
+ xstate_tests[idx].fill_xbuf = fill_opt; \
+ } while (0)
+
+static struct xsave_buffer *valid_xbuf, *compared_xbuf;
+static struct xstate_test xstate_tests[XFEATURE_MAX];
+static uint32_t xstate_size;
+
+static struct xsave_buffer *alloc_xbuf(uint32_t buf_size)
+{
+ struct xsave_buffer *xbuf;
+
+ /* XSAVE buffer should be 64B-aligned. */
+ xbuf = aligned_alloc(64, buf_size);
+ if (!xbuf)
+ ksft_exit_fail_msg("aligned_alloc() failed.\n");
+
+ return xbuf;
+}
+
+static void show_test_xfeatures(void)
+{
+ uint32_t xfeature_num;
+ const char *xfeature_name;
+
+ ksft_print_msg("[NOTE] Test following xstates with mask:%lx.\n",
+ xstate_info.mask);
+ for (xfeature_num = 0; xfeature_num < XFEATURE_MAX; xfeature_num++) {
+ if (!(xstate_info.mask & (1 << xfeature_num)))
+ continue;
+ xfeature_name = xfeature_names[xfeature_num];
+ ksft_print_msg("[NOTE] XSAVE feature num %02d: '%s'.\n",
+ xfeature_num, xfeature_name);
+ }
+}
+
+static void compare_buf_result(struct xsave_buffer *valid_buf,
+ struct xsave_buffer *compare_buf,
+ const char *case_name)
+{
+ if (memcmp(&valid_buf->bytes[0], &compare_buf->bytes[0], xstate_size))
+ ksft_test_result_fail("The case: %s.\n", case_name);
+ else
+ ksft_test_result_pass("The case: %s.\n", case_name);
+}
+
+static void test_xstate_sig_handle(void)
+{
+ const char *case_name1 = "xstate around process signal handling";
+
+ ksft_print_msg("[RUN] Check xstate around signal handling test.\n");
+
+ if (xstate_sig_handle(valid_xbuf, compared_xbuf, xstate_info.mask,
+ xstate_size)) {
+ ksft_print_msg("[NOTE] SIGUSR1 handling is done.\n");
+ } else {
+ ksft_test_result_error("%s: Didn't access SIGUSR1 handling.\n",
+ case_name1);
+ return;
+ }
+
+ compare_buf_result(valid_xbuf, compared_xbuf, case_name1);
+}
+
+static void prepare_xstate_test(void)
+{
+ xstate_test_array_init(XFEATURE_FP, init_legacy_info,
+ fill_fp_mxcsr_xstate_buf);
+ xstate_test_array_init(XFEATURE_SSE, init_legacy_info,
+ fill_xmm_xstate_buf);
+ xstate_test_array_init(XFEATURE_YMM, init_ymm_info,
+ fill_common_xstate_buf);
+ xstate_test_array_init(XFEATURE_OPMASK, init_avx512_info,
+ fill_common_xstate_buf);
+ xstate_test_array_init(XFEATURE_ZMM_Hi256, init_avx512_info,
+ fill_common_xstate_buf);
+ xstate_test_array_init(XFEATURE_Hi16_ZMM, init_avx512_info,
+ fill_common_xstate_buf);
+ xstate_test_array_init(XFEATURE_PKRU, init_pkru_info,
+ fill_pkru_xstate_buf);
+
+ xstate_tests[XSTATE_CASE_SIG].xstate_case = test_xstate_sig_handle;
+}
+
+static void test_xstate(void)
+{
+ uint32_t xfeature_num, case_num, eax, ebx, ecx, edx;
+
+ /*
+ * CPUID.(EAX=0DH, ECX=0H):EBX: maximum size(bytes, from the beginning
+ * of the XSAVE/XRSTOR save area) required by enabled features in XCR0.
+ */
+ __cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER, eax, ebx,
+ ecx, edx);
+ xstate_size = ebx;
+ valid_xbuf = alloc_xbuf(xstate_size);
+ compared_xbuf = alloc_xbuf(xstate_size);
+
+ for (xfeature_num = XFEATURE_FP; xfeature_num < XFEATURE_MAX;
+ xfeature_num++) {
+ /* If there is no the xfeature init function, will continue. */
+ if (xstate_tests[xfeature_num].init) {
+ /* If CPU doesn't support the xfeature, will continue */
+ if (!xstate_tests[xfeature_num].init(xfeature_num))
+ continue;
+ } else {
+ continue;
+ }
+
+ /* Fill xstate buffer. */
+ if (xfeature_num != XFEATURE_PKRU) {
+ xstate_tests[xfeature_num].fill_xbuf(valid_xbuf,
+ xfeature_num, XSTATE_TESTBYTE);
+ } else {
+ /*
+ * Bits 0-1 in first byte of PKRU must be 0 for
+ * RW access to linear address.
+ */
+ xstate_tests[xfeature_num].fill_xbuf(valid_xbuf,
+ xfeature_num, PKRU_TESTBYTE);
+ }
+ }
+
+ /*
+ * Fill xstate-component bitmap(512-519 bytes) into the beginning of
+ * xstate header. xstate header range is 512-575 bytes.
+ */
+ *(uint64_t *)(&valid_xbuf->header) = xstate_info.mask;
+
+ show_test_xfeatures();
+
+ for (case_num = XSTATE_CASE_SIG; case_num < XSTATE_CASE_MAX; case_num++)
+ xstate_tests[case_num].xstate_case();
+
+ free(valid_xbuf);
+ free(compared_xbuf);
+}
+
+int main(void)
+{
+ ksft_print_header();
+ ksft_set_plan(NUM_TESTS);
+
+ /* Check hardware availability for xsave at first. */
+ check_cpuid_xsave_availability();
+ prepare_xstate_test();
+ test_xstate();
+
+ ksft_exit(ksft_cnt.ksft_pass == ksft_plan);
+}
new file mode 100644
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include "../kselftest.h"
+
+#define XSTATE_TESTBYTE 0x8f
+/* Bits 0-1 in first byte of PKRU must be 0 for RW access to linear address. */
+#define PKRU_TESTBYTE 0xfc
+/* FP xstate(0-159 bytes) offset(0) and size(160 bytes) are fixed. */
+#define FP_SIZE 160
+/* XMM xstate(160-415 bytes) offset(byte 160) and size(256 bytes) are fixed. */
+#define XMM_OFFSET 160
+#define XMM_SIZE 256
+/*
+ * xstate 416-511 bytes are reserved, XSAVE header offset 512 bytes
+ * and header size 64 bytes are fixed.
+ */
+#define XSAVE_HDR_OFFSET 512
+#define XSAVE_HDR_SIZE 64
+
+#define CPUID_LEAF1_ECX_XSAVE_MASK (1 << 26) /* XSAVE instructions */
+#define CPUID_LEAF1_ECX_OSXSAVE_MASK (1 << 27) /* OSXSAVE flag */
+
+#define CPUID_LEAF7_EBX_AVX2_MASK (1U << 5) /* AVX2 instructions */
+#define CPUID_LEAF7_EBX_AVX512F_MASK (1U << 16) /* AVX-512 Foundation */
+
+#define CPUID_LEAF7_ECX_OSPKE_MASK (1U << 4) /* OS Protection Keys Enable */
+
+#define CPUID_LEAF_XSTATE 0xd
+#define CPUID_SUBLEAF_XSTATE_USER 0x0
+
+/* It's from arch/x86/kernel/fpu/xstate.c. */
+static const char * const xfeature_names[] = {
+ "x87 floating point registers",
+ "SSE registers",
+ "AVX registers",
+ "MPX bounds registers",
+ "MPX CSR",
+ "AVX-512 opmask",
+ "AVX-512 Hi256",
+ "AVX-512 ZMM_Hi256",
+ "Processor Trace (unused)",
+ "Protection Keys User registers",
+ "PASID state",
+ "unknown xstate feature",
+ "unknown xstate feature",
+ "unknown xstate feature",
+ "unknown xstate feature",
+ "unknown xstate feature",
+ "unknown xstate feature",
+ "AMX Tile config",
+ "AMX Tile data",
+ "unknown xstate feature",
+};
+
+/* List of XSAVE features Linux knows about. */
+enum xfeature {
+ XFEATURE_FP,
+ XFEATURE_SSE,
+ /*
+ * Values above here are "legacy states".
+ * Those below are "extended states".
+ */
+ XFEATURE_YMM,
+ XFEATURE_BNDREGS,
+ XFEATURE_BNDCSR,
+ XFEATURE_OPMASK,
+ XFEATURE_ZMM_Hi256,
+ XFEATURE_Hi16_ZMM,
+ XFEATURE_PT_UNIMPLEMENTED_SO_FAR,
+ XFEATURE_PKRU,
+ XFEATURE_PASID,
+ XFEATURE_RSRVD_COMP_11,
+ XFEATURE_RSRVD_COMP_12,
+ XFEATURE_RSRVD_COMP_13,
+ XFEATURE_RSRVD_COMP_14,
+ XFEATURE_LBR,
+ XFEATURE_RSRVD_COMP_16,
+ XFEATURE_XTILE_CFG,
+ XFEATURE_XTILE_DATA,
+ XFEATURE_MAX,
+};
+
+enum xstate_case {
+ XSTATE_CASE_SIG,
+ XSTATE_CASE_MAX,
+};
+
+struct xsave_buffer {
+ union {
+ struct {
+ char legacy[XSAVE_HDR_OFFSET];
+ char header[XSAVE_HDR_SIZE];
+ char extended[0];
+ };
+ char bytes[0];
+ };
+};
+
+struct {
+ uint64_t mask;
+ uint32_t size[XFEATURE_MAX];
+ uint32_t offset[XFEATURE_MAX];
+} xstate_info;
+
+struct xstate_test {
+ bool (*init)(uint32_t xfeature_num);
+ void (*fill_xbuf)(void *buf, uint32_t xfeature_num, uint8_t test_byte);
+ void (*xstate_case)(void);
+};
+
+static void check_cpuid_xsave_availability(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ /*
+ * CPUID.1:ECX.XSAVE[bit 26] enumerates general
+ * support for the XSAVE feature set, including
+ * XGETBV.
+ */
+ __cpuid_count(1, 0, eax, ebx, ecx, edx);
+ if (!(ecx & CPUID_LEAF1_ECX_XSAVE_MASK))
+ ksft_exit_skip("cpuid: CPU doesn't support xsave.\n");
+
+ if (!(ecx & CPUID_LEAF1_ECX_OSXSAVE_MASK))
+ ksft_exit_skip("cpuid: CPU doesn't support OS xsave.\n");
+}
+
+/* Retrieve the mask, offset and size of a specific xstate. */
+static void retrieve_xstate_mask_size_offset(uint32_t xfeature_num)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ xstate_info.mask |= 1 << xfeature_num;
+ /*
+ * The offset and size of xstate FP and SSE are not recorded by CPUID,
+ * the contents of FP (x87 state) and MXCSR register are mixed in
+ * bytes 0-511.
+ */
+ if (xfeature_num == XFEATURE_FP || xfeature_num == XFEATURE_SSE)
+ return;
+
+ __cpuid_count(CPUID_LEAF_XSTATE, xfeature_num, eax, ebx, ecx, edx);
+ /*
+ * CPUID.(EAX=0xd, ECX=xfeature_num), and output is as follow:
+ * eax: xfeature num state component size
+ * ebx: xfeature num state component offset in user buffer
+ */
+ if (!eax || !ebx)
+ ksft_exit_fail_msg("xfeature num:%d size/offset:%d/%d is 0.\n",
+ xfeature_num, eax, ebx);
+
+ xstate_info.size[xfeature_num] = eax;
+ xstate_info.offset[xfeature_num] = ebx;
+}
+
+/* Retrieve legacy FP and SSE xstate info. */
+static bool init_legacy_info(uint32_t xfeature_num)
+{
+ retrieve_xstate_mask_size_offset(xfeature_num);
+ return true;
+}
+
+static bool init_ymm_info(uint32_t xfeature_num)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ /* CPUID.7.0:EBX.AVX2[bit 5]: the support for AVX2 instructions. */
+ __cpuid_count(7, 0, eax, ebx, ecx, edx);
+ if (ebx & CPUID_LEAF7_EBX_AVX2_MASK) {
+ retrieve_xstate_mask_size_offset(xfeature_num);
+ return true;
+ }
+ return false;
+}
+
+static bool init_avx512_info(uint32_t xfeature_num)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ /* CPUID.7.0:EBX.AVX512F[bit 16]: support for AVX512F instructions. */
+ __cpuid_count(7, 0, eax, ebx, ecx, edx);
+ if (ebx & CPUID_LEAF7_EBX_AVX512F_MASK) {
+ retrieve_xstate_mask_size_offset(xfeature_num);
+ return true;
+ }
+ return false;
+}
+
+static bool init_pkru_info(uint32_t xfeature_num)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ /* CPUID.7.0:ECX.OSPKE[bit 4]: the support for OS set CR4.PKE. */
+ __cpuid_count(7, 0, eax, ebx, ecx, edx);
+ if (ecx & CPUID_LEAF7_ECX_OSPKE_MASK) {
+ retrieve_xstate_mask_size_offset(xfeature_num);
+ return true;
+ }
+ return false;
+}
+
+static void fill_xmm_xstate_buf(void *buf, uint32_t xfeature_num,
+ uint8_t test_byte)
+{
+ /*
+ * Fill test byte value into SSE XMM part xstate buffer(160-415 bytes).
+ * xstate 416-511 bytes are reserved as all 0.
+ */
+ memset((unsigned char *)buf + XMM_OFFSET, test_byte, XMM_SIZE);
+}
+
+static void fill_common_xstate_buf(void *buf, uint32_t xfeature_num,
+ uint8_t test_byte)
+{
+ memset((unsigned char *)buf + xstate_info.offset[xfeature_num],
+ test_byte, xstate_info.size[xfeature_num]);
+}
+
+static void fill_pkru_xstate_buf(void *buf, uint32_t xfeature_num,
+ uint8_t test_byte)
+{
+ /*
+ * Only 0-3 bytes of PKRU xstate are allowed to be written. 4-7
+ * bytes are reserved as all 0.
+ */
+ memset((unsigned char *)buf + xstate_info.offset[XFEATURE_PKRU],
+ test_byte, sizeof(uint32_t));
+}
new file mode 100644
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * xstate_helpers.c - xstate helpers to prevent GCC from generating any FP code.
+ *
+ * Because xstate like XMM will not be preserved across function calls, it uses
+ * assembly instruction to call a system call of fork or raise signal, and uses
+ * the inline functions to solve the problem.
+ * To prevent GCC from generating any FP/SSE(XMM)/AVX/PKRU code, add
+ * "-mno-sse -mno-mmx -mno-sse2 -mno-avx -mno-pku" compiler arguments. And it
+ * causes that stdlib.h can not be used in this test file due to GCC bug.
+ * And this code file will not use libc in stdlib.h like aligned_alloc() and
+ * satisfy xstate test requirements.
+ */
+
+#define _GNU_SOURCE
+#include <err.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+
+#include "xstate_helpers.h"
+
+/* err() exits and will not return. */
+#define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
+/* FP and SSE are legacy xstates with fixed masks in regions 0-511 bytes. */
+#define MASK_FP_SSE 3
+
+static bool sigusr1_done;
+
+static inline void __xsave(void *xbuf, uint64_t rfbm)
+{
+ uint32_t rfbm_lo = rfbm;
+ uint32_t rfbm_hi = rfbm >> 32;
+
+ asm volatile("xsave (%%rdi)"
+ : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
+ : "memory");
+}
+
+static inline void __xrstor(void *xbuf, uint64_t rfbm)
+{
+ uint32_t rfbm_lo = rfbm;
+ uint32_t rfbm_hi = rfbm >> 32;
+
+ asm volatile("xrstor (%%rdi)"
+ : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
+}
+
+inline void fill_fp_mxcsr_xstate_buf(void *buf, uint32_t xfeature_num,
+ uint8_t ui8_fp)
+{
+ /*
+ * Sets the FPU control, status, tag, instruction pointer, and
+ * data pointer registers to their default states.
+ */
+ asm volatile("finit");
+ /* Populate tested bytes onto FP registers stack ST0-7. */
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ asm volatile("fldl %0" : : "m" (ui8_fp));
+ /* Xsave the x87 FPU and SSE MXCSR(bytes 24-27) xstate into the buf. */
+ __xsave(buf, MASK_FP_SSE);
+}
+
+/*
+ * Because xstate like XMM, YMM registers are not preserved across function
+ * calls, so use inline function with assembly code only to raise signal.
+ */
+static inline long __raise(long pid_num, long sig_num)
+{
+ long ret, nr = SYS_kill;
+
+ asm volatile("movq %0, %%rdi" : : "r"(pid_num) : "%rdi");
+ asm volatile("movq %0, %%rsi" : : "r"(sig_num) : "%rsi");
+ asm volatile("syscall"
+ : "=a" (ret)
+ : "a" (nr)
+ : "rcx", "r11", "memory", "cc");
+
+ return ret;
+}
+
+static void sigusr1_handler(int signum, siginfo_t *info, void *__ctxp)
+{
+ sigusr1_done = true;
+}
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ fatal_error("sigaction");
+}
+
+static void clearhandler(int sig)
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ fatal_error("sigaction");
+}
+
+bool xstate_sig_handle(void *valid_xbuf, void *compared_xbuf, uint64_t mask,
+ uint32_t xstate_size)
+{
+ pid_t process_pid;
+
+ sigusr1_done = false;
+ memset(compared_xbuf, 0, xstate_size);
+ sethandler(SIGUSR1, sigusr1_handler, 0);
+ process_pid = getpid();
+ /*
+ * Xrstor the valid buf and call syscall assembly instruction, then
+ * save the xstate to compared buf after signal handling for comparison.
+ */
+ __xrstor(valid_xbuf, mask);
+ __raise(process_pid, SIGUSR1);
+ __xsave(compared_xbuf, mask);
+ clearhandler(SIGUSR1);
+
+ return sigusr1_done;
+}
new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <stdint.h>
+
+extern void fill_fp_mxcsr_xstate_buf(void *buf, uint32_t xfeature_num,
+ uint8_t ui8_fp);
+extern bool xstate_sig_handle(void *valid_xbuf, void *compared_xbuf,
+ uint64_t mask, uint32_t xstate_size);