diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu
index e465438..6e7a366 100644
--- a/arch/x86/Kconfig.cpu
+++ b/arch/x86/Kconfig.cpu
@@ -204,10 +204,21 @@ config CC_HAS_MARCH_NATIVE
 	# usage warnings that only appear wth '-march=native'.
 	depends on CC_IS_GCC || CLANG_VERSION >= 190100
 
+config RUSTC_HAS_APXF
+	# The kernel isn't ready for in-kernel APX instructions. Without
+	# explicit frontend gating of APX, the backend may emit those
+	# instructions in native builds.
+	#
+	# Rust 1.88 added the `apxf` feature option, but versions before 1.93
+	# emit an `apxf` target attribute that only LLVM 23+ can interpret.
+	def_bool (RUSTC_VERSION >= 108800 && RUSTC_LLVM_MAJOR_VERSION >= 23) || \
+		 RUSTC_VERSION >= 109300
+
 config X86_NATIVE_CPU
 	bool "Build and optimize for local/native CPU"
 	depends on X86_64
 	depends on CC_HAS_MARCH_NATIVE
+	depends on !RUST || RUSTC_HAS_APXF
 	help
 	  Optimize for the current CPU used to compile the kernel.
 	  Use this option if you intend to build the kernel for your
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 598f178..8af6b80 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -161,6 +161,11 @@ else
 
 ifdef CONFIG_X86_NATIVE_CPU
         KBUILD_CFLAGS += -march=native
+        # Prevent the compiler from generating EGPR use. The kernel is
+        # not yet prepared for general in-kernel use.
+        KBUILD_CFLAGS += $(call cc-option,-mno-apx-features=egpr)
+
+        # generate_rust_target.rs handles Rust APX gating.
         KBUILD_RUSTFLAGS += -Ctarget-cpu=native
 else
         KBUILD_CFLAGS += -march=x86-64 -mtune=generic
diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c
index fb3594d..854899b 100644
--- a/arch/x86/entry/entry_fred.c
+++ b/arch/x86/entry/entry_fred.c
@@ -10,6 +10,7 @@
 #include <asm/desc.h>
 #include <asm/fred.h>
 #include <asm/idtentry.h>
+#include <asm/processor-flags.h>
 #include <asm/syscall.h>
 #include <asm/trapnr.h>
 #include <asm/traps.h>
@@ -71,7 +72,15 @@ static noinstr void fred_intx(struct pt_regs *regs)
 #endif
 
 	default:
-		return exc_general_protection(regs, 0);
+		/*
+		 * Reconstruct the #GP fault state that IDT delivery would produce.
+		 * Clear the software event flag so ERETU with TF set does not trap
+		 * before the resumed instruction. See prevent_single_step_upon_eretu().
+		 */
+		regs->ip -= regs->fred_ss.insnlen;
+		regs->flags |= X86_EFLAGS_RF;
+		regs->fred_ss.swevent = 0;
+		return exc_general_protection(regs, (regs->fred_ss.vector << 3) | 2);
 	}
 }
 
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 1142183..9f09d38 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -309,6 +309,26 @@ static void save_microcode_patch(struct microcode_intel *patch)
 		pr_err("Unable to allocate microcode memory size: %u\n", size);
 }
 
+static bool revision_is_safe(struct cpu_signature *sig, u32 rev)
+{
+	u32 vfm = IFM(x86_family(sig->sig), x86_model(sig->sig));
+
+	/*
+	 * Erratum GNR98 can cause #MCs if "jumping over" revision 0x1000405.
+	 * Avoid the jumps.
+	 */
+	if (vfm == INTEL_GRANITERAPIDS_X &&
+	    x86_stepping(sig->sig) == 1 &&
+	    sig->pf & 0x95 &&
+	    sig->rev < 0x1000405 &&
+	    rev > 0x1000405) {
+		pr_err_once("Erratum GNR98: skipping revision 0x%x.\n", rev);
+		return false;
+	}
+
+	return true;
+}
+
 /* Scan blob for microcode matching the boot CPUs family, model, stepping */
 static __init struct microcode_intel *scan_microcode(void *data, size_t size,
 						     struct ucode_cpu_info *uci,
@@ -330,6 +350,9 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
 		if (!intel_find_matching_signature(data, &uci->cpu_sig))
 			continue;
 
+		if (!revision_is_safe(&uci->cpu_sig, mc_header->rev))
+			continue;
+
 		/*
 		 * For saving the early microcode, find the matching revision which
 		 * was loaded on the BSP.
@@ -878,6 +901,9 @@ static enum ucode_state parse_microcode_blobs(int cpu, struct iov_iter *iter)
 		if (!intel_find_matching_signature(mc, &uci->cpu_sig))
 			continue;
 
+		if (!revision_is_safe(&uci->cpu_sig, mc_header.rev))
+			continue;
+
 		is_safe = ucode_validate_minrev(&mc_header);
 		if (force_minrev && !is_safe)
 			continue;
diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs
index 3bf2965..7687b0d 100644
--- a/scripts/generate_rust_target.rs
+++ b/scripts/generate_rust_target.rs
@@ -224,6 +224,11 @@ fn main() {
             features += ",+harden-sls-ijmp";
             features += ",+harden-sls-ret";
         }
+        if cfg.has("X86_NATIVE_CPU") {
+            // Prevent the backend from generating APX instructions. The kernel is not yet prepared
+            // for general in-kernel EGPR use.
+            features += ",-apxf";
+        }
         ts.push("features", features);
         ts.push("llvm-target", "x86_64-linux-gnu");
         ts.push("supported-sanitizers", ["kcfi", "kernel-address"]);
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 4340652..d478b13 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -13,7 +13,7 @@ CAN_BUILD_WITH_NOPIE := $(shell ./check_cc.sh "$(CC)" trivial_program.c -no-pie)
 TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt test_mremap_vdso \
 			check_initial_reg_state sigreturn iopl ioperm \
 			test_vsyscall mov_ss_trap sigtrap_loop \
-			syscall_arg_fault fsgsbase_restore sigaltstack
+			syscall_arg_fault fsgsbase_restore sigaltstack int_signal
 TARGETS_C_BOTHBITS += nx_stack
 TARGETS_C_32BIT_ONLY := entry_from_vm86 test_syscall_vdso unwind_vdso \
 			test_FCMOV test_FCOMI test_FISTTP \
diff --git a/tools/testing/selftests/x86/int_signal.c b/tools/testing/selftests/x86/int_signal.c
new file mode 100644
index 0000000..22676da
--- /dev/null
+++ b/tools/testing/selftests/x86/int_signal.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Check the signal context for INT instructions with IDT and FRED entry. */
+#define _GNU_SOURCE
+
+#include <cpuid.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <ucontext.h>
+
+#include "helpers.h"
+
+#ifdef __x86_64__
+#define REG_IP REG_RIP
+#define USER_IP rip
+#define STACK_PTR "%rsp"
+#else
+#define REG_IP REG_EIP
+#define USER_IP eip
+#define STACK_PTR "%esp"
+#endif
+
+/*
+ * Each instruction has normal and single-step entry points. Resume at the
+ * NOP after handling its signal, then expect a trace trap after that NOP
+ * when TF is set. Explicit labels avoid assuming the kernel's saved IP.
+ */
+#define PROBE(name, insn) \
+	extern void name(void); \
+	extern void name##_tf(void); \
+	extern const char name##_end[], name##_step[]; \
+	asm(".pushsection .text\n" \
+	    ".globl " #name "_tf\n" \
+	    ".type " #name "_tf, @function\n" \
+	    #name "_tf:\n" \
+	    "pushf\n" \
+	    "orl $0x100, (" STACK_PTR ")\n" \
+	    "popf\n" \
+	    ".globl " #name "\n" \
+	    ".type " #name ", @function\n" \
+	    #name ":\n" insn "\n" \
+	    ".globl " #name "_end\n" \
+	    #name "_end:\nnop\n" \
+	    ".globl " #name "_step\n" \
+	    #name "_step:\nret\n" \
+	    ".size " #name ", .-" #name "\n" \
+	    ".size " #name "_tf, .-" #name "_tf\n" \
+	    ".popsection\n")
+
+PROBE(int1, ".byte 0xcd, 0x01");
+PROBE(int29, ".byte 0xcd, 0x29");
+PROBE(int2c, ".byte 0xcd, 0x2c");
+PROBE(int2d, ".byte 0xcd, 0x2d");
+PROBE(prefixed_int2d, ".byte 0x66, 0xcd, 0x2d");
+PROBE(long_int2d, ".fill 13, 1, 0x2e\n.byte 0xcd, 0x2d");
+PROBE(int81, ".byte 0xcd, 0x81");
+PROBE(intff, ".byte 0xcd, 0xff");
+PROBE(short_int3, ".byte 0xcc");
+PROBE(long_int3, ".byte 0xcd, 0x03");
+PROBE(int4, ".byte 0xcd, 0x04");
+PROBE(ud2, ".byte 0x0f, 0x0b");
+PROBE(hlt, ".byte 0xf4");
+
+struct test {
+	const char *name;
+	void (*run)(void);
+	void (*run_tf)(void);
+	const char *end, *step;
+	int signo, trap, error, ip_offset, flags, code;
+};
+
+#define TEST(name, sig, trap, error, offset, flags, code) \
+	{ #name, name, name##_tf, name##_end, name##_step, \
+	  sig, trap, error, offset, flags, code }
+
+#define GP(name, error) \
+	TEST(name, SIGSEGV, 13, error, 0, X86_EFLAGS_RF, SI_KERNEL)
+
+static const struct test tests[] = {
+	GP(int1, 0x00a),
+	GP(int29, 0x14a),
+	GP(int2c, 0x162),
+	GP(int2d, 0x16a),
+	GP(prefixed_int2d, 0x16a),
+	GP(long_int2d, 0x16a),
+	GP(int81, 0x40a),
+	GP(intff, 0x7fa),
+	GP(hlt, 0),
+	TEST(short_int3, SIGTRAP, 3, 0, 1, 0, SI_KERNEL),
+	TEST(long_int3, SIGTRAP, 3, 0, 2, 0, SI_KERNEL),
+	TEST(int4, SIGSEGV, 4, 0, 2, 0, SI_KERNEL),
+	TEST(ud2, SIGILL, 6, 0, 0, X86_EFLAGS_RF, ILL_ILLOPN),
+};
+
+static const struct test *active;
+static volatile sig_atomic_t seen, signo, trap, error, ip_offset, flags;
+static volatile sig_atomic_t code, addr_ok, single_step, stepped, step_ok;
+
+static void handler(int sig, siginfo_t *info, void *context)
+{
+	ucontext_t *uc = context;
+	uintptr_t ip = uc->uc_mcontext.gregs[REG_IP];
+	uintptr_t start = (uintptr_t)active->run;
+	uintptr_t end = (uintptr_t)active->end;
+
+	if (seen && single_step && sig == SIGTRAP) {
+		if (stepped++) {
+			ksft_print_msg("%s: second trace trap at %#lx\n",
+				       active->name, (unsigned long)ip);
+			_exit(KSFT_FAIL);
+		}
+		step_ok = ip == (uintptr_t)active->step &&
+			uc->uc_mcontext.gregs[REG_TRAPNO] == 1 &&
+			info->si_code == TRAP_TRACE;
+		uc->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
+		return;
+	}
+
+	if (seen || ip < start || ip > end) {
+		ksft_print_msg("%s: unexpected signal %d at %#lx\n",
+			       active->name, sig, (unsigned long)ip);
+		_exit(KSFT_FAIL);
+	}
+
+	signo = sig;
+	trap = uc->uc_mcontext.gregs[REG_TRAPNO];
+	error = uc->uc_mcontext.gregs[REG_ERR];
+	ip_offset = ip - start;
+	flags = uc->uc_mcontext.gregs[REG_EFL] & (X86_EFLAGS_RF | X86_EFLAGS_TF);
+	code = info->si_code;
+	/* force_sig() reports no address, force_sig_fault() reports the IP. */
+	addr_ok = info->si_addr == (code == SI_KERNEL ? NULL : (void *)ip);
+	seen = 1;
+	uc->uc_mcontext.gregs[REG_IP] = end;
+}
+
+static void wait_for_child(pid_t child, int *status)
+{
+	pid_t ret;
+
+	do {
+		ret = waitpid(child, status, 0);
+	} while (ret < 0 && errno == EINTR);
+	if (ret != child)
+		ksft_exit_fail_perror("waitpid");
+}
+
+/* Resume the tracee and check where the next stop lands. */
+static bool resume_to(pid_t child, int *status, int request, int sig,
+		      const void *ip, const char *what)
+{
+	struct user_regs_struct regs;
+
+	if (ptrace(request, child, 0, 0))
+		return false;
+	wait_for_child(child, status);
+	if (!WIFSTOPPED(*status)) {
+		ksft_print_msg("%s: tracee did not stop\n", what);
+		return false;
+	}
+	if (WSTOPSIG(*status) != sig) {
+		ksft_print_msg("%s: stopped with signal %d, expected %d\n",
+			       what, WSTOPSIG(*status), sig);
+		return false;
+	}
+	if (ptrace(PTRACE_GETREGS, child, 0, &regs))
+		return false;
+	if ((unsigned long)regs.USER_IP != (unsigned long)ip) {
+		ksft_print_msg("%s: stopped at %#lx, expected %#lx\n", what,
+			       (unsigned long)regs.USER_IP, (unsigned long)ip);
+		return false;
+	}
+	return true;
+}
+
+static bool set_ip(pid_t child, const void *ip, bool tf)
+{
+	struct user_regs_struct regs;
+
+	if (ptrace(PTRACE_GETREGS, child, 0, &regs))
+		return false;
+	regs.USER_IP = (unsigned long)ip;
+	if (tf)
+		regs.eflags |= X86_EFLAGS_TF;
+	return !ptrace(PTRACE_SETREGS, child, 0, &regs);
+}
+
+/*
+ * Exercise the tracer paths that resume through the fault frame rather than
+ * sigreturn. A stale FRED software event flag on that frame traps before the
+ * NOP executes instead of after it.
+ */
+static void test_ptrace(void)
+{
+	bool into = false, step = false, cont = false;
+	pid_t child;
+	int status;
+
+	child = fork();
+	if (child < 0)
+		ksft_exit_fail_perror("fork");
+	if (!child) {
+		if (ptrace(PTRACE_TRACEME, 0, 0, 0))
+			_exit(KSFT_FAIL);
+		/* Start from a breakpoint frame, not the syscall frame of raise(). */
+		asm volatile("int3");
+		_exit(KSFT_FAIL);
+	}
+
+	wait_for_child(child, &status);
+	if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
+		goto out;
+	if (ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_EXITKILL))
+		goto out;
+
+	/* Single-step into the INT. The fault must report the INT's address. */
+	if (!set_ip(child, int2d, false))
+		goto out;
+	into = resume_to(child, &status, PTRACE_SINGLESTEP, SIGSEGV, int2d,
+			 "single-step into INT");
+	if (!into)
+		goto out;
+
+	/* Suppress SIGSEGV and single-step the NOP. */
+	if (!set_ip(child, int2d_end, false))
+		goto out;
+	step = resume_to(child, &status, PTRACE_SINGLESTEP, SIGTRAP, int2d_step,
+			 "single-step after INT");
+	if (!step)
+		goto out;
+
+	/* Fault again, then suppress SIGSEGV and continue with TF set. */
+	if (!set_ip(child, int2d, false))
+		goto out;
+	if (!resume_to(child, &status, PTRACE_CONT, SIGSEGV, int2d,
+		       "continue to INT"))
+		goto out;
+	if (!set_ip(child, int2d_end, true))
+		goto out;
+	cont = resume_to(child, &status, PTRACE_CONT, SIGTRAP, int2d_step,
+			 "continue with TF after INT");
+out:
+	if (WIFSTOPPED(status)) {
+		kill(child, SIGKILL);
+		wait_for_child(child, &status);
+	}
+	ksft_test_result(into, "ptrace single-step into INT faults at the INT\n");
+	ksft_test_result(step, "ptrace single-step after suppressing SIGSEGV\n");
+	ksft_test_result(cont, "ptrace continue with TF after suppressing SIGSEGV\n");
+}
+
+static bool cpu_has_fred(void)
+{
+	unsigned int eax, ebx, ecx, edx;
+
+	if (__get_cpuid_max(0, NULL) < 7)
+		return false;
+	__cpuid_count(7, 1, eax, ebx, ecx, edx);
+	return eax & (1 << 17);
+}
+
+int main(void)
+{
+	unsigned int i, tf;
+	int expected_flags, ok;
+
+	ksft_print_header();
+	ksft_set_plan(2 * ARRAY_SIZE(tests) + 3);
+	ksft_print_msg("CPU %s FRED\n", cpu_has_fred() ? "supports" : "lacks");
+	sethandler(SIGSEGV, handler, 0);
+	sethandler(SIGTRAP, handler, 0);
+	sethandler(SIGILL, handler, 0);
+
+	for (tf = 0; tf < 2; tf++) {
+		for (i = 0; i < ARRAY_SIZE(tests); i++) {
+			active = &tests[i];
+			single_step = tf;
+			seen = signo = trap = error = ip_offset = flags = 0;
+			code = addr_ok = stepped = step_ok = 0;
+			expected_flags = active->flags | (tf ? X86_EFLAGS_TF : 0);
+			if (tf)
+				active->run_tf();
+			else
+				active->run();
+
+			ok = seen && signo == active->signo && trap == active->trap &&
+				error == active->error && ip_offset == active->ip_offset &&
+				flags == expected_flags && code == active->code && addr_ok &&
+				(!tf || (stepped && step_ok));
+			ksft_test_result(ok, "%s%s\n", active->name, tf ? " with TF" : "");
+			if (!ok) {
+				ksft_print_msg("got signal=%d trap=%d error=%#x ip=%d\n",
+					       signo, trap, error, ip_offset);
+				ksft_print_msg("got flags=%#x code=%d addr_ok=%d step_ok=%d\n",
+					       flags, code, addr_ok, step_ok);
+				ksft_print_msg("expected signal=%d trap=%d error=%#x ip=%d\n",
+					       active->signo, active->trap, active->error,
+					       active->ip_offset);
+				ksft_print_msg("expected flags=%#x code=%d\n",
+					       expected_flags, active->code);
+			}
+		}
+	}
+	test_ptrace();
+	ksft_finished();
+}
