diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index a9e452b65ba2..049f8ffb2742 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -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 xsave_signal_handle
+			corrupt_xstate_header amx xsave_signal_handle xsave_fork_test
 # Some selftests require 32bit support enabled also on 64bit systems
 TARGETS_C_32BIT_NEEDED := ldt_gdt ptrace_syscall
 
@@ -106,3 +106,4 @@ $(OUTPUT)/test_syscall_vdso_32: thunks_32.S
 $(OUTPUT)/check_initial_reg_state_32: CFLAGS += -Wl,-ereal_start -static
 $(OUTPUT)/check_initial_reg_state_64: CFLAGS += -Wl,-ereal_start -static
 $(OUTPUT)/xsave_signal_handle_64: CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-avx -mno-pku
+$(OUTPUT)/xsave_fork_test_64: CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-avx -mno-pku
diff --git a/tools/testing/selftests/x86/xsave_fork_test.c b/tools/testing/selftests/x86/xsave_fork_test.c
new file mode 100644
index 000000000000..507334e25eba
--- /dev/null
+++ b/tools/testing/selftests/x86/xsave_fork_test.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * It's used for XSAVE test with process switching.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sched.h>
+#include <sys/syscall.h>
+
+#include "xsave_common.h"
+
+void *aligned_alloc(size_t alignment, size_t size);
+static unsigned char *xsave_buf0, *xsave_buf1, *xsave_buf2;
+static int xsave_size;
+
+void prepare_environment(void)
+{
+	xsave_size = get_xsave_size();
+	printf("XSAVE_TEST_MASK:0x%x, xsave size:0x%x\n",
+		XSAVE_TEST_MASK, xsave_size);
+	check_cpu_capability();
+
+	/* SDM XSAVE: misalignment to a 64-byte boundary will result in #GP */
+	xsave_buf0 = aligned_alloc(64, xsave_size);
+	if (!xsave_buf0)
+		execution_failed("aligned_alloc xsave_buf0 failed\n");
+	xsave_buf1 = aligned_alloc(64, xsave_size);
+	if (!xsave_buf1)
+		execution_failed("aligned_alloc xsave_buf1 failed\n");
+	xsave_buf2 = aligned_alloc(64, xsave_size);
+	if (!xsave_buf2)
+		execution_failed("aligned_alloc xsave_buf2 failed\n");
+}
+
+/* Use fork to create pid and trigger process switch test */
+int test_xsave_fork(void)
+{
+	pid_t child, grandchild;
+	int status, result[2];
+	const char *test_xsave_child = "Child xstate was same as parent";
+	const char *test_process_switch = "Xstate after the process switch didn't change";
+	uint32_t ui32_change = 0xffff0000;
+
+	populate_xstate_regs();
+	xsave(xsave_buf0, XSAVE_TEST_MASK);
+	child = syscall(SYS_fork);
+	if (child < 0)
+		execution_failed("fork failed\n");
+	if (child == 0) {
+		xsave(xsave_buf1, XSAVE_TEST_MASK);
+		result[0] = compare_xsave_buf(xsave_buf0, xsave_buf1, xsave_size,
+			test_xsave_child, NO_CHANGE);
+
+		/*
+		 * If above case is failed and prints some failed reason, in
+		 * order to avoid libc printf change and clean up some xstates,
+		 * populate xstates again for next test
+		 */
+		populate_xstate_regs();
+		xsave(xsave_buf1, XSAVE_TEST_MASK);
+
+		/* fork grandchild will trigger process switching in child */
+		grandchild = syscall(SYS_fork);
+		if (grandchild == 0) {
+			printf("Grandchild pid:%d change it's own xstates\n", getpid());
+			change_xstate(ui32_change);
+			return 0;
+		}
+		if (grandchild) {
+			if (waitpid(grandchild, &status, 0) != grandchild || !WIFEXITED(status))
+				printf("[FAIL]:Grandchild exit with error, status:0x%x\n",
+					status);
+		}
+		/* After switch back to child process and check xstate */
+		xsave(xsave_buf2, XSAVE_TEST_MASK);
+		result[1] = compare_xsave_buf(xsave_buf1, xsave_buf2, xsave_size,
+			test_process_switch, NO_CHANGE);
+		printf("Child pid:%d check xstate after swtich back\n",
+			getpid());
+
+		check_result(result[0], test_xsave_child);
+		check_result(result[1], test_process_switch);
+		printf("Xstate in process switch test pass[%d/%d], err_num:%d\n",
+			pass_num, case_num, err_num);
+
+		return 0;
+	}
+
+	if (child) {
+		if (waitpid(child, &status, 0) != child || !WIFEXITED(status))
+			printf("[FAIL]:Child exit with error, status:0x%x\n",
+				status);
+	}
+
+	return 0;
+}
+
+int main(void)
+{
+	cpu_set_t set;
+
+	case_num = 2;
+	CPU_ZERO(&set);
+	CPU_SET(0, &set);
+	sched_setaffinity(getpid(), sizeof(set), &set);
+
+	prepare_environment();
+	test_xsave_fork();
+
+	return 0;
+}
