IDT、SYSCALL 与 FRED:三条内核入口,同一份 pt_regs?

中断走 IDT、syscall 走 MSR——这两句在默认路径上大体成立;FRED 打开之后,它们就不再是两套并行模型了。

讨论 x86-64 Linux 怎么进内核时,常听到四句:

  1. 硬件中断要查 IDT
  2. syscall 直接用 MSR
  3. 进内核后都要构建 pt_regs
  4. FRED 把上面这些都简化了

它们在非 FRED路径上部分成立,但不能混为一谈,更不能直接推广到 FRED。下面先给出可正确复述的限定版,再对照 Intel SDM、FRED 规范与当前 Linux arch/x86 展开。


一、四句常见说法:对不对?

1.1 可正确复述的四句(带出处)

若要保证说法正确,宜写成:

  1. 硬件中断 / CPU 异常:默认走 IDT;FRED 启用后走 FRED event delivery,不再查 IDT。
    「查 IDT」指 CPU 按向量读一项门描述符(SDM Vol 3A 约 §6.14)1,不是内核扫整张表。Linux 在启用 FRED 时调用 idt_invalidate() 清空 IDTR,并在注释中写明 Any further IDT use is a bugfred.c)。之后由软件按 type × vector 两级分发(内核文档 Software based event dispatching2,实现见 fred_entry_from_user()

  2. 64 位 syscall:默认经 STAR/LSTAR/FMASK 入核、不进 IDT;FRED 启用后同一条指令改由 FRED 投递,再落到 do_syscall_64
    非 FRED 时:SYSCALL 不查 IDT,入口 RIP 来自 IA32_LSTAR(SDM 约 §5.8.8;指令级语义见3)。Linux 在 idt_syscall_init()MSR_LSTAR 写成 entry_SYSCALL_64。FRED 规范 §3 / §7.4:启用 FRED 后,SYSCALL/SYSENTER 改用 FRED event delivery,返回应走 ERETU 而非 SYSRET4。Linux 在 fred_other() 识别 FRED_SYSCALL 后调用 do_syscall_64。另:int $0x80 等仍属中断类路径,勿与 64 位 syscall 混谈。

  3. C 层几乎都拿 pt_regs *,但帧怎么来的不同。
    • IDT:硬件压 IRET 帧,再由入口汇编补全(error_entry / PUSH_AND_CLEAR_REGS 等)。
    • 传统 SYSCALL:硬件不压完整栈帧;entry_SYSCALL_64 手工 push 用户态部分再 PUSH_AND_CLEAR_REGS
    • FRED:硬件建立 FRED 栈帧(自 orig_ax 起,见 ptrace.h 注释);FRED_ENTER 再补通用寄存器。
      结果同为 struct pt_regs *,构建过程不可互换叙述。
  4. FRED 统一的是事件投递与返回,不是删掉 pt_regs、MSR 或软件分发表。
    FRED 规范 §3:凡原会走 IDT 的事件改走 FRED event delivery;SYSCALL/SYSENTER 亦并入;返回用 ERETS/ERETU4。内核文档强调两级软件分发与完整上下文,而非「去掉所有表」2。Linux 仍保留扩展后的 pt_regs、更多 FRED MSR(cpu_init_fred_exceptions()),以及 sysvec_table 等软件表。引导参数 fred=on|offon 为默认)见5

压成一句:

非 FRED:中断/异常查 IDT,64 位 syscall 走 MSR,两边都落到(不同构建方式的)pt_regs。** FRED:**统一投递与返回——IDT 作废、SYSCALL 也进 FRED;pt_regs 仍在,MSR 与软件分发仍在。

1.2 常见说法 ↔ 路径对照

# 常见说法 传统路径(IDT + SYSCALL) FRED 路径
1 硬件中断要查 IDT 外部 IRQ 与 CPU 异常由 CPU 按向量号查 IDT 投递 idt_invalidate() 后不再走 IDT;改 FRED event delivery,软件按 type/vector 分发
2 syscall 直接用 MSR 64 位 syscallMSR_LSTAR/STAR/FMASK,不进 IDT SYSCALL 变为 FRED 事件(EVENT_TYPE_OTHER),经 FRED 入口到 do_syscall_64()
3 进内核后都要构建 pt_regs C 层 handler 几乎都拿 struct pt_regs * 仍有 pt_regs,帧含 fred_cs/fred_ss/edata 扩展
4 FRED 简化了上面所有 统一了事件投递与返回;不是删掉 IDT、MSR、pt_regs
flowchart TB
    subgraph Traditional["传统路径"]
        IRQ1["外部 IRQ / 异常"] --> IDT["IDT 门描述符"]
        SYS1["用户 syscall"] --> MSR["LSTAR / STAR / FMASK"]
        IDT --> PT1["软件补全 pt_regs"]
        MSR --> PT1
    end

    subgraph FredPath["FRED 路径"]
        EVT["任意事件含 SYSCALL"] --> FRED["FRED event delivery"]
        FRED --> DISP["type × vector 软件分发"]
        DISP --> PT2["FRED 帧 + FRED_ENTER"]
    end

二、传统路径:IDT 与 SYSCALL

2.1 硬件中断 / CPU 异常 → IDT

按 SDM Vol 3A 对 64-bit 中断投递的描述,大致是:

  1. 中断控制器(APIC 等)给出向量号;
  2. CPU 用 IDTR → IDT[vector] 读门描述符;
  3. 硬件压 IRET 帧(SS、RSP、RFLAGS、CS、RIP;异常可能还有 error code);
  4. 若门描述符 IST ≠ 0,切换到 TSS 中对应 IST 栈。

「查 IDT」是 CPU 按向量索引一项门描述符,不是内核遍历整张表。

外部 IRQ 在 Linux 里的软件链可以写成:

APIC/MSI → 向量 N
    └─ IDT[N] → irq_entries_start + offset(idtentry.h)
        └─ push 向量号(占 orig_ax 槽)→ asm_common_interrupt
            └─ error_entry → PUSH_AND_CLEAR_REGS
                └─ common_interrupt(regs, vector)(arch/x86/kernel/irq.c)

参见:idtentry.h · entry_64.S · irq.c

外部 IRQ 与 #PF/#DE 等共用 IDT 机制,但 stub 宏不同(idtentry_irq vs idtentry)。用户态触发的 IRQ/异常,常经 error_entry + sync_regs 把帧落到线程内核栈上的标准 pt_regs 槽位。

2.2 64 位 SYSCALL → MSR(不进 IDT)

SYSCALL 不查 IDT。硬件把 RIP 换成 MSR_LSTAR、用 RCX/R11 保住返回契约、按 MSR_STAR 推导 CS/SS,并用 IA32_FMASK 掩 RFLAGS——不保存 RSP,也不压完整 IRET 帧13

Linux 在每 CPU 初始化时把 MSR_LSTAR 指到 entry_SYSCALL_64

// arch/x86/kernel/cpu/common.c:2268-2270
static inline void idt_syscall_init(void)
{
	wrmsrq(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);

入口汇编自己切栈、手工搭 pt_regs

// arch/x86/entry/entry_64.S:87-121
SYM_CODE_START(entry_SYSCALL_64)
	UNWIND_HINT_ENTRY
	ENDBR

	swapgs
	/* tss.sp2 is scratch space. */
	movq	%rsp, PER_CPU_VAR(cpu_tss_rw + TSS_sp2)
	SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp
	movq	PER_CPU_VAR(cpu_current_top_of_stack), %rsp
	...
	PUSH_AND_CLEAR_REGS rax=$-ENOSYS
	...
	call	do_syscall_64		/* returns with IRQs disabled */

调用链:

用户 syscall 指令
    └─ MSR_LSTAR → entry_SYSCALL_64(entry_64.S:87)
        └─ swapgs;用户 RSP → TSS.sp2;RSP ← cpu_current_top_of_stack
            └─ 手工 push 用户态部分 + PUSH_AND_CLEAR_REGS
                └─ do_syscall_64(regs, nr)(syscall_64.c)

MSR 只负责跳到入口;完整 pt_regsentry_SYSCALL_64 软件搭建。这不是「MSR 一步到位进 C 函数」。

其他系统调用入口仍走 IDT:int $0x80(向量 0x80)、ia32 sysenter 等。

2.3 传统路径对照

维度 IDT(IRQ / 异常 / int 0x80) 64 位 SYSCALL
硬件查表 IDT[vector] MSR_LSTAR
硬件压栈 IRET 帧(± error code) 仅 RCX/R11 等,无完整 IRET 帧
软件压栈 error_entry + PUSH_AND_CLEAR_REGS entry_SYSCALL_64 手工 push + PUSH_AND_CLEAR_REGS
典型 C 入口 common_interrupt / exc_* do_syscall_64
返用户 多为 iretq sysretq 快路径或 iretq 慢路径
sequenceDiagram
    participant U as 用户态
    participant CPU as CPU
    participant K as 内核入口

    Note over U,K: IDT 路径
    U->>CPU: 异常或外部 IRQ
    CPU->>CPU: 查 IDT 门, 压 IRET 帧
    CPU->>K: 跳门描述符入口
    K->>K: error_entry / PUSH_AND_CLEAR_REGS

    Note over U,K: SYSCALL 路径
    U->>CPU: syscall
    CPU->>CPU: 读 LSTAR 等 MSR, 不压栈帧
    CPU->>K: entry_SYSCALL_64
    K->>K: 切栈并手工构造 pt_regs

三、pt_regs:结果相同,构建方式不同

三条路径的 C ABI 目标一致:handler 收到 struct pt_regs *。但谁压什么不同:

路径 硬件贡献 软件贡献
IDT IRQ/异常 SS/RSP/RFLAGS/CS/RIP(+ error code) PUSH_AND_CLEAR_REGS;用户态常 sync_regs 到线程栈
SYSCALL 无 IRET 帧;RCX/R11 等由硬件改写 全程手工 push 用户态部分 + PUSH_AND_CLEAR_REGS
FRED FRED 栈帧(自 orig_ax 起,含 type/vector/edata) FRED_ENTERPUSH_AND_CLEAR_REGS

常见误读:

struct pt_regs 布局见 ptrace.h。注释写明:FRED 栈帧从 orig_ax 开始,且始终带 error code;cs/ss 槽在 FRED 下可解读为 fred_cs / fred_ss

// arch/x86/include/asm/ptrace.h:127-163
	/*
	 * orig_ax is used on entry for:
	 * - the syscall number (syscall, sysenter, int80)
	 * - error_code stored by the CPU on traps and exceptions
	 * - the interrupt number for device interrupts
	 *
	 * A FRED stack frame starts here:
	 *   1) It _always_ includes an error code;
	 *   ...
	 */
	unsigned long orig_ax;
	...
	union {
		u16		cs;
		u64		csx;
		struct fred_cs	fred_cs;
	};
	...
	union {
		u16		ss;
		u64		ssx;
		struct fred_ss	fred_ss;
	};

四、FRED:统一了什么,没删掉什么

FRED(Flexible Return and Event Delivery)用事件投递 + ERETS/ERETU 替代传统 IDT + IRET 模型42。规范写明设计目标包括:用更低延迟的转移替换 IDT 投递与 IRET;事件投递原子建立完整 supervisor 上下文、返回时建立完整 user 上下文。

启用 FRED 后,凡原先会走 IDT 的事件改走 FRED event delivery;SYSCALL / SYSENTER 的既有操作也被替换为 FRED 投递。返回侧用 ERETU(回 ring 3)与 ERETS(仍在 ring 0)4

4.1 Linux 如何启用

// arch/x86/kernel/fred.c:28-69
void cpu_init_fred_exceptions(void)
{
	...
	wrmsrq(MSR_IA32_FRED_CONFIG,
	       FRED_CONFIG_REDZONE |
	       FRED_CONFIG_INT_STKLVL(0) |
	       FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user));
	...
	cr4_set_bits(X86_CR4_FRED);
	/* Any further IDT use is a bug */
	idt_invalidate();

	/* Use int $0x80 for 32-bit system calls in FRED mode */
	setup_clear_cpu_cap(X86_FEATURE_SYSFAST32);
	setup_clear_cpu_cap(X86_FEATURE_SYSCALL32);
}

idt_invalidate() 把 IDTR 载成空表:

// arch/x86/kernel/idt.c:337-344
void idt_invalidate(void)
{
	static const struct desc_ptr idt = { .address = 0, .size = 0 };

	load_idt(&idt);
}

4.2 事件分发:替代 IDT 查表 + 部分 MSR 入口

入口由 MSR_IA32_FRED_CONFIG 指向 asm_fred_entrypoint_user / _kernel。用户态入口:

// arch/x86/entry/entry_64_fred.S:18-45
.macro FRED_ENTER
	UNWIND_HINT_END_OF_STACK
	ANNOTATE_NOENDBR
	PUSH_AND_CLEAR_REGS
	movq	%rsp, %rdi	/* %rdi -> pt_regs */
.endm
...
SYM_CODE_START_NOALIGN(asm_fred_entrypoint_user)
	FRED_ENTER
	call	fred_entry_from_user
...
1:	ERETU

fred_entry_from_user()regs->fred_ss.type 做第一级分发:

// arch/x86/entry/entry_fred.c:238-267
__visible noinstr void fred_entry_from_user(struct pt_regs *regs)
{
	unsigned long error_code = regs->orig_ax;
	regs->orig_ax = -1;

	switch (regs->fred_ss.type) {
	case EVENT_TYPE_EXTINT:
		return fred_extint(regs);
	case EVENT_TYPE_NMI:
		...
	case EVENT_TYPE_HWEXC:
		return fred_hwexc(regs, error_code);
	case EVENT_TYPE_SWINT:
		return fred_intx(regs);
	...
	case EVENT_TYPE_OTHER:
		return fred_other(regs);
	default: break;
	}
	return fred_bad_type(regs, error_code);
}
事件类型 含义 Linux 处理
EVENT_TYPE_EXTINT 外部中断 fred_extintsysvec_table[] / common_interrupt
EVENT_TYPE_HWEXC 硬件异常 fred_hwexcexc_*
EVENT_TYPE_NMI NMI fred_exc_nmi
EVENT_TYPE_OTHER 含 SYSCALL fred_otherdo_syscall_64
软中断 / INT n 等 fred_intx / fred_swexc  

64 位 syscall 在 FRED 下的落点:

// arch/x86/entry/entry_fred.c:78-85
static __always_inline void fred_other(struct pt_regs *regs)
{
	if (likely(regs->fred_ss.vector == FRED_SYSCALL && regs->fred_ss.l)) {
		regs->orig_ax = regs->ax;
		regs->ax = -ENOSYS;
		do_syscall_64(regs, regs->orig_ax);
		return;
	}
	...
}

FRED 规范对 SYSCALL 的修改写得很直白:当 CR4.FRED = 1 时,用 FRED event delivery 替换既有 SYSCALL 操作;返回应使用 ERETU 而非 SYSRET(FRED 下 SYSRET#UD4

因此 FRED 下:

flowchart TD
    A["事件发生"] --> B["FRED event delivery"]
    B --> C["asm_fred_entrypoint_user"]
    C --> D["fred_entry_from_user"]
    D --> E{"fred_ss.type"}
    E -->|EXTINT| F["fred_extint"]
    E -->|HWEXC| G["fred_hwexc"]
    E -->|OTHER| H["fred_other"]
    E -->|NMI 等| I["对应 handler"]
    H -->|FRED_SYSCALL| J["do_syscall_64"]
    F --> K["sysvec_table 或 common_interrupt"]

内核文档把这套分发概括为两级:先按事件类型,再按向量2

4.3 FRED 并没有「简化掉」的东西

机制 FRED 后
pt_regs 仍在;布局扩展(fred_cs/fred_ss,另有 fred_info.edata)——不是「不用 pt_regs
MSR 更多(FRED_CONFIGFRED_RSP*FRED_STKLVLS
栈级别 / 独立栈 IST 模型 → FRED stack level 模型
返回 ERETS / ERETU 替代 IRET / SYSRET
SWAPGS FRED 启用时非法(#UD);改 LKGS / 硬件 GS 交换42

FRED 的价值是统一事件投递与返回语义(更低延迟、原子上下文、edata 替代 CR2/DR6 瞬态等),不是删除所有内核数据结构。C 入口如 fred_entry_from_user(struct pt_regs *regs)do_syscall_64(regs, …) 照旧吃 pt_regs

4.4 硬件统一了什么;软件仍要做什么

投递与返回这一层是硬件替代;内核仍要做 type × vector 软件分发

原先(分轨) FRED 启用后(硬件)
IDT event delivery(中断/异常/INT n 等查门) FRED event delivery:不读 IDT,按 FRED_CONFIG 等 MSR 建上下文与栈帧
SYSCALL / SYSENTER 各自的 MSR 快路径 同一条 FRED event delivery(规范 §3 / §7.4)4
IRET / SYSRET / SYSEXIT 返回 ERETU(回 ring 3)/ ERETS(仍在 ring 0);FRED 下 SYSRET/SYSEXIT/SWAPGS 等为 #UD
IST(IDT 门 IST + TSS.ist[]) stack level 0–3 + FRED_RSP0–3 / FRED_STKLVLS
入核后常靠 SWAPGS 切 GS 投递 / ERETU 时硬件交换 GS 基址;属性用 LKGS

内核侧仍须软件完成:

一句话:FRED 用硬件统一「怎么进 ring 0 / 怎么回」(含原 IDT 事件 + SYSCALL/SYSENTER,以及返回与栈级别/GS);「进核后调哪个 handler」仍是内核软件表

4.5 TSS.RSP0 / IST:只属于 IDT 路径

TSS.RSP0 / IST 是 IDT(中断/异常/INT n)投递时的硬件切栈机制,传统 64 位 syscall 不走

  IDT 路径 64 位 SYSCALL
是否查 IDT
硬件切栈 用户→ring 0 且门 IST=0 → TSS.RSP0;IST≠0 → TSS.IST[] 按门做 RSP0/IST;硬件不保存、不切换 RSP
Linux 做法 常经 entry trampoline / sync_regs 等到线程栈 entry_SYSCALL_64:用户 RSP 可暂存 TSS.sp2(scratch),再接到 cpu_current_top_of_stack

SYSCALL 入口里出现的 TSS 字段(如 sp2)只是内核拿 TSS 当 scratch,不是 IDT 那套「硬件按 RSP0/IST 自动切栈」。FRED 启用后,切栈改由 FRED stack level + FRED_RSP*,与旧 IST/RSP0 模型并列替代,而非「给 SYSCALL 补上 RSP0」。


五、FRED 省了哪些步骤;快在哪里

FRED 不是把 syscall / IDT 路径整段删掉,而是把「进核 / 回用户」换成另一套硬件原语。省下的是旧机制里若干段,不是 do_syscall_64 / common_interrupt 之后的内核业务。

5.1 相对 IDT:少做 / 换掉的

旧步骤 FRED 怎么处理
读 IDTR、按向量取 IDT 门(内存查表) 不查 IDT;按已配好的 FRED_CONFIG 等进入口
门上的类型 / DPL 等硬件检查 投递侧不做这套;INT n 等需 软件按 type/vector 自己拦4
TSS.RSP0 / IST 切栈 改用 stack level + FRED_RSP*
IRET 帧(SS/RSP/RFLAGS/CS/RIP ± error code) FRED 帧(含 type/vector/edata 等,且始终带 error code 槽)
入核后常要 SWAPGS 投递 / ERETU硬件交换 GS 基址
IRET 返回(语义重,且与无条件放开 NMI 等纠缠) ERETU / ERETS
#PFCR2、调试相关读 DR6 的瞬时窗口 附加信息进帧上的 edata2

5.2 相对传统 SYSCALL:少做 / 换掉的

旧步骤 FRED 怎么处理
另一套 LSTAR/STAR/FMASK 快路径(与 IDT 并行 SYSCALL/SYSENTER 并入 FRED event delivery
硬件 几乎不压栈,入口里 整段手工搭 pt_regs 硬件先建 FRED 帧;软件主要补通用寄存器(FRED_ENTER
入/出核 swapgs + 自己切栈 GS / 栈级别多由 FRED 硬件处理
快路径 SYSRET,不行再退 IRET 统一用 ERETU(FRED 下 SYSRET#UD

do_syscall_64 本身还在,只是前面从 entry_SYSCALL_64 换成 fred_other → 同一条 C 分发。

5.3 并没有「省掉」的

5.4 延迟:对标谁、别指望什么

规范与内核文档写明的目标是:用更低延迟的转移替换 IDT event deliveryIRET42

对比对象 含义
相对 IDT + IRET 预期赢家:少 IDT 门访存、用更轻的返回原语替代 IRET、少 SWAPGS/ESPFIX/CR2/DR6/NMI 边角软件。具体周期数依赖微架构与场景,无统一公开数字。
相对传统 SYSCALL+SYSRET 不是主打「再砍一刀 syscall」。旧 syscall 本就不走 IDT;FRED 把 SYSCALL 收进统一投递,换来一致性和更完整硬件帧。纯 64 位 syscall 热路径上,旧 SYSRET 往往仍很强。
flowchart LR
    subgraph Win["FRED 延迟目标主要对标"]
        IDT["IDT 查门"] --> IRET["IRET 返回"]
    end

    subgraph Unify["统一进同一模型"]
        SYS["SYSCALL / SYSENTER"]
        IRQ["IRQ / 异常"]
    end

    IDT -.->|替换| FRED["FRED delivery + ERETU/ERETS"]
    IRET -.->|替换| FRED
    SYS --> FRED
    IRQ --> FRED

一句话:快在「替代 IDT 查门 + 替代笨重 IRET(及连带 SWAPGS/IST/边角软件)」这类通用事件进/出;syscall 并入同一硬件投递,主要换一致性和完整性,延迟要和旧 SYSRET 路径分开评价。


六、三路总览

类型 传统:怎么进内核 传统:pt_regs FRED:怎么进内核 FRED:pt_regs
外部 IRQ IDT[vector] → irq stub HW IRET 帧 + SW 补全 FRED EXTINT → fred_extint HW FRED 帧 + FRED_ENTER
CPU 异常 #PF 等 IDT → asm_exc_* 同上 FRED HWEXC → fred_hwexc 同上;fault 地址用 fred_event_data(regs)
64 位 syscall MSR_LSTAR → entry_SYSCALL_64 全软件 push FRED OTHER → fred_otherdo_syscall_64 FRED 帧 + FRED_ENTER
int $0x80 IDT 向量 0x80 IDT 路径 FRED 软件中断分支 依 FRED 实现
#DF / NMI IDT + IST IST 栈 FRED stack level 3/2 等 FRED_RSP*

七、常见误读

与 §1.1、§4.4–§5.4 对照,下列压缩说法仍易踩坑:

  1. 「硬件中断 = 软件扫 IDT 表」 → CPU 按向量索引查一项门描述符1
  2. 「syscall 完全靠 MSR,与中断无关」 → 传统 64 位对;int 0x80 仍走 IDT;FRED 下 syscall 是 FRED 事件4
  3. 「两种方式进内核后 pt_regs 构建相同」 → 结果都是 pt_regs *,过程不同(HW IRET vs 全软件 push vs FRED 帧)。
  4. 「FRED 让 IDT、MSR、pt_regs 都消失」 / 「FRED 不用 pt_regs」 → 仅统一投递;pt_regs 仍在,MSR 更多,IDT 作废但软件 dispatch 表(sysvec_table)仍在2
  5. 「TSS.RSP0 / IST 也管 SYSCALL」 → 否;那是 IDT 切栈。传统 syscall 不硬件切 RSP0/IST(§4.5)。
  6. 「FRED 一定比旧 SYSCALL+SYSRET 更快」 → 规范主要对标 IDT+IRET;syscall 并入统一模型,延迟须分开评价(§5.4)。
  7. 「生产环境已在用 FRED」 → 需 CPU 支持 + CONFIG_X86_FRED;主线默认在支持时启用,也可用 fred=off 关闭5。多数发行版与实机是否暴露该 feature,仍取决于硬件与发行配置。

References

  1. Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A — System Programming Guide, Part 1(约 §5.8.8 SYSCALL/SYSRET、§6.14 Interrupt 64-Bit Mode)。官方入口:https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html  2 3

  2. Linux 内核文档,Documentation/arch/x86/x86_64/fred.rstSoftware based event dispatching:先按 event type、再按 vector 的 two-level dispatch;以及完整 supervisor/user context、LKGS / SWAPGS  2 3 4 5 6 7 8

  3. x86 Instruction Reference — SYSCALLRCX/R11LSTARFMASK、不保存 RSP 等指令级语义  2

  4. Intel,Flexible Return and Event Delivery (FRED) Specification,Document Number 346446-004US,Revision 4.0(May 2023)。§3 Overview:「Any event that would normally cause IDT event delivery … The SYSCALL and SYSENTER instructions will also use FRED event delivery」;§7.4 规定 CR4.FRED=1SYSCALL 改走 FRED,返回应使用 ERETU  2 3 4 5 6 7 8 9 10

  5. Linux 内核参数文档,Documentation/admin-guide/kernel-parameters.txtfred= 条目)— Format { on | off }on 为默认  2 3