Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Jul 2026 21:51:38 +0000
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 6e93f5e4d693 - main - amd64: FRED support
Message-ID:  <6a5e986a.22164.2ca88691@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=6e93f5e4d6932c423b89dff8fc08d86f8bdeb7b9

commit 6e93f5e4d6932c423b89dff8fc08d86f8bdeb7b9
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-02-13 11:24:40 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-20 21:51:02 +0000

    amd64: FRED support
    
    FRED support as defined starting from the SDM rev. 90, requires a new
    'events' entry point to receive user and kernel mode exceptions and
    interrupts notifications from the hardware.  A minimal asm trampoline is
    enough, rest can be implemented in C due to the clean FRED organization
    of the event reporting.
    
    The syscall entry is handled by a microptimized assembly path, directly
    calling into the amd64_syscall() handler, instead of the generic events
    entry point.
    
    Tested by:      emaste
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D55829
---
 sys/amd64/amd64/cpu_switch.S         |  73 ++++-
 sys/amd64/amd64/db_trace.c           |   5 +-
 sys/amd64/amd64/exception.S          | 282 ++++++++++++++++++
 sys/amd64/amd64/genassym.c           |  15 +
 sys/amd64/amd64/initcpu.c            |  43 +++
 sys/amd64/amd64/machdep.c            | 104 +++++--
 sys/amd64/amd64/mp_machdep.c         |  82 +++--
 sys/amd64/amd64/trap.c               | 558 +++++++++++++++++++++++++++++++++++
 sys/amd64/amd64/vm_machdep.c         |  10 +-
 sys/amd64/include/md_var.h           |   2 +
 sys/amd64/include/pcb.h              |   8 +-
 sys/amd64/vmm/intel/vmx.c            |   2 +-
 sys/amd64/vmm/vmm.c                  |   2 +-
 sys/amd64/vmm/vmm_lapic.c            |   5 +
 sys/amd64/vmm/vmm_lapic.h            |   2 +
 sys/dev/hyperv/vmbus/x86/vmbus_x86.c |   3 +-
 sys/i386/i386/machdep.c              |   2 +
 sys/kern/kern_thread.c               |   2 +-
 sys/x86/include/apicvar.h            |   3 +-
 sys/x86/include/frame.h              |   1 -
 sys/x86/include/x86_var.h            |   3 +
 sys/x86/x86/local_apic.c             |  38 +--
 22 files changed, 1158 insertions(+), 87 deletions(-)

diff --git a/sys/amd64/amd64/cpu_switch.S b/sys/amd64/amd64/cpu_switch.S
index 21e55e64d74a..774098d7def5 100644
--- a/sys/amd64/amd64/cpu_switch.S
+++ b/sys/amd64/amd64/cpu_switch.S
@@ -187,14 +187,22 @@ do_kthread:
 	cmpq	%rax,%rdx
 	jne	do_tss
 done_tss:
+	movq	%r8,PCPU(CURPCB)
+	cmpb	$0,fred
+	jne	1f
 	movq	TD_MD_STACK_BASE(%r12),%r9
 	movq	%r9,PCPU(RSP0)
-	movq	%r8,PCPU(CURPCB)
 	movq	PCPU(PTI_RSP0),%rax
 	cmpq	$~0,PCPU(UCR3)
 	cmove	%r9,%rax
 	movq	%rax,TSS_RSP0(%rdx)
-	movq	%r12,PCPU(CURTHREAD)		/* into next thread */
+	jmp	2f
+1:	movq	TD_MD_STACK_BASE(%r12),%rdx
+	movl	%edx,%eax
+	shrq	$32,%rdx
+	movl	$MSR_FRED_RSP0,%ecx
+	wrmsr
+2:	movq	%r12,PCPU(CURTHREAD)		/* into next thread */
 
 	/* Test if debug registers should be restored. */
 	testl	$PCB_DBREGS,PCB_FLAGS(%r8)
@@ -365,8 +373,36 @@ ENTRY(savectx)
 	movl	%eax,PCB_SFMASK(%rdi)
 	movl	%edx,PCB_SFMASK+4(%rdi)
 
-	sgdt	PCB_GDT(%rdi)
-	sidt	PCB_IDT(%rdi)
+	cmpb	$0, fred
+	je	1f
+	movl	$MSR_FRED_RSP0,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_RSP0(%rdi)
+	movl	%edx,PCB_FRED_RSP0+4(%rdi)
+	movl	$MSR_FRED_RSP1,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_RSP1(%rdi)
+	movl	%edx,PCB_FRED_RSP1+4(%rdi)
+	movl	$MSR_FRED_RSP2,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_RSP2(%rdi)
+	movl	%edx,PCB_FRED_RSP2+4(%rdi)
+	movl	$MSR_FRED_RSP3,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_RSP3(%rdi)
+	movl	%edx,PCB_FRED_RSP3+4(%rdi)
+	movl	$MSR_FRED_STKLVLS,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_STKLVLS(%rdi)
+	movl	%edx,PCB_FRED_STKLVLS+4(%rdi)
+	movl	$MSR_FRED_CONFIG,%ecx
+	rdmsr
+	movl	%eax,PCB_FRED_CONFIG(%rdi)
+	movl	%edx,PCB_FRED_CONFIG+4(%rdi)
+	jmp	2f
+
+1:	sidt	PCB_IDT(%rdi)
+2:	sgdt	PCB_GDT(%rdi)
 	sldt	PCB_LDT(%rdi)
 	str	PCB_TR(%rdi)
 
@@ -442,8 +478,35 @@ ENTRY(resumectx)
 	movq	%rax,%cr3
 
 	/* Restore descriptor tables. */
+	cmpb	$0,fred
+	jne	1f
 	lidt	PCB_IDT(%rdi)
-	lldt	PCB_LDT(%rdi)
+	jmp	2f
+1:	movl	$MSR_FRED_RSP0,%ecx
+	movl	PCB_FRED_RSP0(%rdi),%eax
+	movl	4 + PCB_FRED_RSP0(%rdi),%edx
+	wrmsr
+	movl	$MSR_FRED_RSP1,%ecx
+	movl	PCB_FRED_RSP1(%rdi),%eax
+	movl	4 + PCB_FRED_RSP1(%rdi),%edx
+	wrmsr
+	movl	$MSR_FRED_RSP2,%ecx
+	movl	PCB_FRED_RSP2(%rdi),%eax
+	movl	4 + PCB_FRED_RSP2(%rdi),%edx
+	wrmsr
+	movl	$MSR_FRED_RSP3,%ecx
+	movl	PCB_FRED_RSP3(%rdi),%eax
+	movl	4 + PCB_FRED_RSP3(%rdi),%edx
+	wrmsr
+	movl	$MSR_FRED_STKLVLS,%ecx
+	movl	PCB_FRED_STKLVLS(%rdi),%eax
+	movl	4 + PCB_FRED_STKLVLS(%rdi),%edx
+	wrmsr
+	movl	$MSR_FRED_CONFIG,%ecx
+	movl	PCB_FRED_CONFIG(%rdi),%eax
+	movl	4 + PCB_FRED_CONFIG(%rdi),%edx
+	wrmsr
+2:	lldt	PCB_LDT(%rdi)
 
 #define	SDT_SYSTSS	9
 #define	SDT_SYSBSY	11
diff --git a/sys/amd64/amd64/db_trace.c b/sys/amd64/amd64/db_trace.c
index 5f751906b7e6..e14cd55978de 100644
--- a/sys/amd64/amd64/db_trace.c
+++ b/sys/amd64/amd64/db_trace.c
@@ -167,7 +167,10 @@ db_nextframe(db_addr_t *fp, db_addr_t *ip, struct thread *td)
 		    strcmp(name, "fork_trampoline") == 0 ||
 		    strcmp(name, "mchk_calltrap") == 0 ||
 		    strcmp(name, "nmi_calltrap") == 0 ||
-		    strcmp(name, "Xdblfault") == 0)
+		    strcmp(name, "Xdblfault") == 0 ||
+		    strcmp(name, "fred_events_handlers") == 0 ||
+		    strcmp(name, "user_fred_syscall") == 0 ||
+		    strcmp(name, "user_fred_non_syscall") == 0)
 			frame_type = TRAP;
 		else if (strncmp(name, "Xatpic_intr", 11) == 0 ||
 		    strncmp(name, "Xapic_isr", 9) == 0 ||
diff --git a/sys/amd64/amd64/exception.S b/sys/amd64/amd64/exception.S
index 4d58e07caf4b..6dfb8a431fe7 100644
--- a/sys/amd64/amd64/exception.S
+++ b/sys/amd64/amd64/exception.S
@@ -1063,6 +1063,8 @@ ENTRY(fork_trampoline)
 	movq	%rbx,%rsi		/* arg1 */
 	movq	%rsp,%rdx		/* trapframe pointer */
 	call	fork_exit
+	cmpb	$0, fred
+	jne	fred_checkast
 	jmp	doreti			/* Handle any ASTs */
 
 /*
@@ -1401,6 +1403,286 @@ gsbase_load_fault:
 	movq	$0,PCB_GSBASE(%r8)
 	jmp	doreti
 
+/*
+ * FRED guarantees that on entry, %rflags is set to '2', which in
+ * particular means that interrupts are disabled, and DF/AC are clear.
+ * Interrupts are re-enabled by the C handler, if appropriate.
+ */
+	ALIGN_TEXT
+user_fred_syscall:
+	movq	%rdx,TF_RDX(%rsp)
+	movq	%rcx,TF_RCX(%rsp)
+	SAVE_SEGS
+	movq	PCPU(CURPCB),%r11
+	andl	$~PCB_FULL_IRET,PCB_FLAGS(%r11)
+	sti
+	movw	$KUDSEL,TF_SS(%rsp)
+	movw	$KUCSEL,TF_CS(%rsp)
+	movq	%r8,TF_R8(%rsp)
+	movq	%r9,TF_R9(%rsp)
+	movq	%rbx,TF_RBX(%rsp)
+	movq	%rbp,TF_RBP(%rsp)
+	movq	%r12,TF_R12(%rsp)
+	movq	%r13,TF_R13(%rsp)
+	movq	%r14,TF_R14(%rsp)
+	movq	%r15,TF_R15(%rsp)
+	movl	$TF_HASSEGS,TF_FLAGS(%rsp)
+	andl	$TF_FRED_EVINFO2_INSTLENMASK,%eax
+	shrl	$TF_FRED_EVINFO2_INSTLENSHIFT,%eax
+	movq	%rax,TF_ERR(%rsp)
+	movq	PCPU(CURTHREAD),%rdi
+	movl	TF_RFLAGS(%rsp),%esi
+	andl	$PSL_T,%esi
+	call	amd64_syscall
+	movq	PCPU(CURPCB),%rax
+	cli
+	testl	$PCB_FULL_IRET,PCB_FLAGS(%rax)
+	jnz	fred_checkast
+	movq	PCPU(CURTHREAD),%rax
+	cmpl	$0,TD_AST(%rax)
+	jnz	fred_checkast
+	movq	TF_RDI(%rsp),%rdi
+	movq	TF_RSI(%rsp),%rsi
+	movq	TF_RDX(%rsp),%rdx
+	movq	TF_RAX(%rsp),%rax
+	xorl	%r8d,%r8d
+	xorl	%r9d,%r9d
+	xorl	%r10d,%r10d
+	xorl	%r11d,%r11d
+	addq	$TF_ERR,%rsp
+	.globl	fred_syscall_eretu
+fred_syscall_eretu:
+	eretu
+
+	ALIGN_TEXT
+user_fred_non_syscall:
+	movq	%rdx,TF_RDX(%rsp)
+	movq	%rcx,TF_RCX(%rsp)
+	movq	%r8,TF_R8(%rsp)
+	movq	%r9,TF_R9(%rsp)
+	movq	%rbx,TF_RBX(%rsp)
+	movq	%rbp,TF_RBP(%rsp)
+	movq	%r10,TF_R10(%rsp)
+	movq	%r11,TF_R11(%rsp)
+	movq	%r12,TF_R12(%rsp)
+	movq	%r13,TF_R13(%rsp)
+	movq	%r14,TF_R14(%rsp)
+	movq	%r15,TF_R15(%rsp)
+	SAVE_SEGS
+	movq	PCPU(CURTHREAD),%r9
+	movq	TD_PCB(%r9),%r8
+	rdfsbase %rax
+	movq	%rax,PCB_FSBASE(%r8)
+	rdgsbase %rax
+	movq	%rax,PCB_GSBASE(%r8)
+	movl	$TF_HASSEGS,TF_FLAGS(%rsp)
+	KMSAN_ENTER
+	movq	%rsp, %rdi
+	call	trap_fred_u
+	KMSAN_LEAVE
+fred_checkast:
+	cli
+	movq	PCPU(CURTHREAD),%rax
+	movq	TD_PCB(%rax),%r8
+	cmpl	$0,TD_AST(%rax)
+	je	fred_gouser
+	sti
+	movq	%rsp,%rdi	/* pass a pointer to the trapframe */
+	call	ast
+	jmp	fred_checkast
+fred_gouser:
+	testl	$PCB_FULL_IRET,PCB_FLAGS(%r8)
+	jz	fred_u_regs
+	andl	$~PCB_FULL_IRET,PCB_FLAGS(%r8)
+	testl	$TF_HASSEGS,TF_FLAGS(%rsp)
+	jne	fred_do_segs
+	movw	$KUDSEL,%ax
+	movw	%ax,TF_DS(%rsp)
+	movw	%ax,TF_ES(%rsp)
+	movw	$KUF32SEL,TF_FS(%rsp)
+	movw	$KUG32SEL,TF_GS(%rsp)
+fred_do_segs:
+	/* Restore %fs and fsbase */
+	movw	TF_FS(%rsp),%ax
+	.globl	fred_ld_fs
+fred_ld_fs:
+	movw	%ax,%fs
+	testl	$PCB_32BIT,PCB_FLAGS(%r8)
+	jnz	after_fred_fs
+	movq	PCB_FSBASE(%r8),%rax
+	.globl	fred_ld_fsbase
+fred_ld_fsbase:
+	wrfsbase %rax
+after_fred_fs:
+	/* Restore %gs and kgsbase */
+	movw	TF_GS(%rsp),%ax
+	.globl	fred_lkgs
+fred_lkgs:
+	lkgs	%ax
+	testl	$PCB_32BIT,PCB_FLAGS(%r8)
+	jnz	after_fred_gs
+	movl	PCB_GSBASE(%r8),%eax
+	movl	PCB_GSBASE+4(%r8),%edx
+	movl	$MSR_KGSBASE,%ecx
+	.globl	fred_ld_kgsbase
+fred_ld_kgsbase:
+	wrmsr
+after_fred_gs:
+	.globl	fred_ld_es
+fred_ld_es:
+	movw	TF_ES(%rsp),%es
+	.globl	fred_ld_ds
+fred_ld_ds:
+	movw	TF_DS(%rsp),%ds
+fred_u_regs:
+	RESTORE_REGS
+	addq	$TF_ERR,%rsp
+	.globl	fred_eretu
+fred_eretu:
+	eretu
+
+	.globl	fred_ld_fs_fault
+fred_ld_fs_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movw	$KUF32SEL,TF_FS(%rsp)
+	jmp	fred_checkast
+
+	.globl	fred_ld_fsbase_fault
+fred_ld_fsbase_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movq	PCPU(CURTHREAD),%r9
+	movq	TD_PCB(%r9),%r8
+	movq	$0,PCB_FSBASE(%r8)
+	jmp	fred_checkast
+
+	.globl	fred_lkgs_fault
+fred_lkgs_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movw	$KUG32SEL,TF_GS(%rsp)
+	jmp	fred_checkast
+
+	.globl	fred_ld_kgsbase_fault
+fred_ld_kgsbase_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movq	PCPU(CURTHREAD),%r9
+	movq	TD_PCB(%r9),%r8
+	movq	$0,PCB_GSBASE(%r8)
+	jmp	fred_checkast
+
+	.globl	fred_ld_es_fault
+fred_ld_es_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movw	$KUDSEL,TF_ES(%rsp)
+	jmp	fred_checkast
+
+	.globl	fred_ld_ds_fault
+fred_ld_ds_fault:
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_NP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp,%rdi
+	call	trap_fred_u
+	movw	$KUDSEL,TF_DS(%rsp)
+	jmp	fred_checkast
+
+	.globl	fred_eretu_fault
+fred_eretu_fault:
+	subq	$TF_ERR,%rsp
+	movq	%rdi,TF_RDI(%rsp)
+	movq	%rdx,TF_RDX(%rsp)
+	movq	%rcx,TF_RCX(%rsp)
+	movq	%rsi,TF_RSI(%rsp)
+	movq	%r8,TF_R8(%rsp)
+	movq	%r9,TF_R9(%rsp)
+	movq	%rbx,TF_RBX(%rsp)
+	movq	%rbp,TF_RBP(%rsp)
+	movq	%r10,TF_R10(%rsp)
+	movq	%r11,TF_R11(%rsp)
+	movq	%r12,TF_R12(%rsp)
+	movq	%r13,TF_R13(%rsp)
+	movq	%r14,TF_R14(%rsp)
+	movq	%r15,TF_R15(%rsp)
+	SAVE_SEGS
+	movl	$(TF_FRED_EVINFO2_TYPE_EXC | IDT_GP), TF_FRED_EVINFO2(%rsp)
+	movq	%rsp, %rdi
+	KMSAN_ENTER
+	call	trap_fred_u
+	KMSAN_LEAVE
+	jmp	fred_checkast
+
+/*
+ * Content for the FRED entries page.
+*/
+
+	.p2align 12,0x90
+	.globl	fred_events_handlers, fred_events_handlers_end
+fred_events_handlers:
+	subq	$TF_ERR,%rsp
+	movq	%rax,TF_RAX(%rsp)
+	movq	%rdi,TF_RDI(%rsp)
+	movq	%rsi,TF_RSI(%rsp)
+	movl	TF_FRED_EVINFO2(%rsp),%edi
+	movl	%edi,%eax
+	andl	$TF_FRED_EVINFO2_TYPEMASK,%edi
+	cmpl	$TF_FRED_EVINFO2_TYPE_SYSCALL,%edi
+	je	user_fred_syscall
+	jmp	user_fred_non_syscall
+
+	. = fred_events_handlers + 256
+	subq	$TF_ERR,%rsp
+	movq	%rax,TF_RAX(%rsp)
+	movq	%rdi,TF_RDI(%rsp)
+	movq	%rdx,TF_RDX(%rsp)
+	movq	%rcx,TF_RCX(%rsp)
+	movq	%rsi,TF_RSI(%rsp)
+	movq	%r8,TF_R8(%rsp)
+	movq	%r9,TF_R9(%rsp)
+	movq	%rbx,TF_RBX(%rsp)
+	movq	%rbp,TF_RBP(%rsp)
+	movq	%r10,TF_R10(%rsp)
+	movq	%r11,TF_R11(%rsp)
+	movq	%r12,TF_R12(%rsp)
+	movq	%r13,TF_R13(%rsp)
+	movq	%r14,TF_R14(%rsp)
+	movq	%r15,TF_R15(%rsp)
+	movq	%rsp, %rdi
+	KMSAN_ENTER
+	call	trap_fred_k
+	KMSAN_LEAVE
+	RESTORE_REGS
+	addq	$TF_ERR,%rsp
+	erets
+fred_events_handlers_end:
+
+	.globl	fred_out_of_nmi
+fred_out_of_nmi:
+	pushq	%rbp
+	movq	%rsp,%rbp
+	xorl	%eax,%eax
+	pushq	$0
+	pushq	$0
+	movw	%ss,%ax
+	orl	$(1<<18),%eax	/* NMI block release for ERETS */
+	pushq	%rax
+	pushq	%rbp
+	pushfq
+	xorl	%eax,%eax
+	movw	%cs,%ax
+	pushq	%rax
+	pushq	$1f
+	pushq	$0
+	erets
+1:	popq	%rbp
+	retq
+
 #ifdef HWPMC_HOOKS
 	ENTRY(end_exceptions)
 #endif
diff --git a/sys/amd64/amd64/genassym.c b/sys/amd64/amd64/genassym.c
index 875c536166d1..9a5a74b3f6d6 100644
--- a/sys/amd64/amd64/genassym.c
+++ b/sys/amd64/amd64/genassym.c
@@ -146,6 +146,12 @@ ASSYM(PCB_STAR, offsetof(struct pcb, pcb_star));
 ASSYM(PCB_LSTAR, offsetof(struct pcb, pcb_lstar));
 ASSYM(PCB_CSTAR, offsetof(struct pcb, pcb_cstar));
 ASSYM(PCB_SFMASK, offsetof(struct pcb, pcb_sfmask));
+ASSYM(PCB_FRED_RSP0, offsetof(struct pcb, pcb_fred_rsp0));
+ASSYM(PCB_FRED_RSP1, offsetof(struct pcb, pcb_fred_rsp1));
+ASSYM(PCB_FRED_RSP2, offsetof(struct pcb, pcb_fred_rsp2));
+ASSYM(PCB_FRED_RSP3, offsetof(struct pcb, pcb_fred_rsp3));
+ASSYM(PCB_FRED_STKLVLS, offsetof(struct pcb, pcb_fred_stklvls));
+ASSYM(PCB_FRED_CONFIG, offsetof(struct pcb, pcb_fred_config));
 ASSYM(PCB_SIZE, sizeof(struct pcb));
 ASSYM(PCB_FULL_IRET, PCB_FULL_IRET);
 ASSYM(PCB_DBREGS, PCB_DBREGS);
@@ -180,9 +186,15 @@ ASSYM(TF_DS, offsetof(struct trapframe, tf_ds));
 ASSYM(TF_ES, offsetof(struct trapframe, tf_es));
 ASSYM(TF_FS, offsetof(struct trapframe, tf_fs));
 ASSYM(TF_GS, offsetof(struct trapframe, tf_gs));
+ASSYM(TF_FRED_EVINFO2, offsetof(struct trapframe, tf_fred_evinfo2));
 ASSYM(TF_FLAGS, offsetof(struct trapframe, tf_flags));
 ASSYM(TF_SIZE, sizeof(struct trapframe));
 ASSYM(TF_HASSEGS, TF_HASSEGS);
+ASSYM(TF_FRED_EVINFO2_TYPE_EXC, TF_FRED_EVINFO2_TYPE_EXC);
+ASSYM(TF_FRED_EVINFO2_TYPE_SYSCALL, TF_FRED_EVINFO2_TYPE_SYSCALL);
+ASSYM(TF_FRED_EVINFO2_TYPEMASK, TF_FRED_EVINFO2_TYPEMASK);
+ASSYM(TF_FRED_EVINFO2_INSTLENMASK, TF_FRED_EVINFO2_INSTLENMASK);
+ASSYM(TF_FRED_EVINFO2_INSTLENSHIFT, TF_FRED_EVINFO2_INSTLENSHIFT);
 
 ASSYM(PTI_RDX, offsetof(struct pti_frame, pti_rdx));
 ASSYM(PTI_RAX, offsetof(struct pti_frame, pti_rax));
@@ -272,6 +284,9 @@ ASSYM(TSSSEL, GSEL(GPROC0_SEL, SEL_KPL));
 ASSYM(LDTSEL, GSEL(GUSERLDT_SEL, SEL_KPL));
 ASSYM(SEL_RPL_MASK, SEL_RPL_MASK);
 
+ASSYM(IDT_NP, IDT_NP);
+ASSYM(IDT_GP, IDT_GP);
+
 ASSYM(__FreeBSD_version, __FreeBSD_version);
 
 #ifdef	HWPMC_HOOKS
diff --git a/sys/amd64/amd64/initcpu.c b/sys/amd64/amd64/initcpu.c
index 7161cf631508..5b615be4b8dc 100644
--- a/sys/amd64/amd64/initcpu.c
+++ b/sys/amd64/amd64/initcpu.c
@@ -397,3 +397,46 @@ initializecpucache(void)
 		cpu_stdext_feature &= ~CPUID_STDEXT_CLFLUSHOPT;
 	}
 }
+
+extern const char fred_events_handlers[], fred_events_handlers_end[];
+
+void
+amd64_cpu_init_fred(void)
+{
+	MPASS((((uintptr_t)fred_events_handlers) & PAGE_MASK) == 0);
+	/*
+	 * 128b = 0x2 * 64b redzone
+	 * SLC 0 for EXTINT
+	 * SLC 0 current
+	 */
+	wrmsr(MSR_FRED_CONFIG, (uintptr_t)fred_events_handlers |
+	    (0x2 << IA32_FRED_CONFIG_REDZONESZ_SHIFT) |
+	    (0 << IA32_FRED_CONFIG_EXTINT_SLC_SHIFT) | 0);
+
+	/*
+	 * With FRED, there is no need for workarounds requiring
+	 * dedicated stack for #DB.  Also, there is no need to have
+	 * separate stacks for #NM and #MC, because CSL rules ensure
+	 * that interrupted frame is safe even if NMI was interrupted
+	 * by machine check or vice versa.
+	 *
+	 * Still, we need to provide a stack for events that can occur
+	 * with the interrupts disabled, to avoid caring about the
+	 * ordering of the context update in cpu_switch.S.  Use CSL 1
+	 * for #NM and #MC.
+	 *
+	 * Since double fault handler is used to handle stack
+	 * overflow, it gets the dedicated stack, of the highest used
+	 * CSL 2.
+	 *
+	 * CSL 3 is unused, and we have some space to re-arrange this
+	 * if a need in the future appears.
+	 */
+	wrmsr(MSR_FRED_STKLVLS, (1ul << (IDT_NM * 2)) |
+	    (1ul << (IDT_MC * 2)) | (2ul << (IDT_DF * 2)));
+	wrmsr(MSR_FRED_RSP3, 0);
+	load_cr4(rcr4() | CR4_FRED);
+	if (IS_BSP() && bootverbose)
+		printf("FRED enabled\n");
+
+}
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index ab9edfe2c68f..c6a77c5ef852 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -206,10 +206,14 @@ long realmem = 0;
 int late_console = 1;
 int lass_enabled = 0;
 
+int __read_frequently fred = 0;
+SYSCTL_INT(_hw, OID_AUTO, fred, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
+    &fred, 0,
+    "FRED is used");
+
 struct kva_md_info kmi;
 
 struct region_descriptor r_idt;
-
 struct pcpu *__pcpu;
 struct pcpu temp_bsp_pcpu;
 
@@ -347,9 +351,9 @@ cpu_setregs(void)
 static struct gate_descriptor idt0[NIDT];
 struct gate_descriptor *idt = &idt0[0];	/* interrupt descriptor table */
 
-static char dblfault_stack[DBLFAULT_STACK_SIZE] __aligned(16);
+static char dblfault_stack[DBLFAULT_STACK_SIZE] __aligned(64);
 static char mce0_stack[MCE_STACK_SIZE] __aligned(16);
-static char nmi0_stack[NMI_STACK_SIZE] __aligned(16);
+static char nmi0_stack[NMI_STACK_SIZE] __aligned(64);
 static char dbg0_stack[DBG_STACK_SIZE] __aligned(16);
 CTASSERT(sizeof(struct nmi_pcpu) == 16);
 
@@ -485,6 +489,9 @@ setidt(int idx, inthand_t *func, int typ, int dpl, int ist)
 {
 	struct gate_descriptor *ip;
 
+	if (fred)
+		return;
+
 	ip = idt + idx;
 	ip->gd_looffset = (uintptr_t)func;
 	ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL);
@@ -1186,9 +1193,11 @@ amd64_conf_fast_syscall(void)
 
 	msr = rdmsr(MSR_EFER) | EFER_SCE;
 	wrmsr(MSR_EFER, msr);
-	wrmsr(MSR_LSTAR, pti ? (u_int64_t)IDTVEC(fast_syscall_pti) :
-	    (u_int64_t)IDTVEC(fast_syscall));
-	wrmsr(MSR_CSTAR, (u_int64_t)IDTVEC(fast_syscall32));
+	if (!fred) {
+		wrmsr(MSR_LSTAR, pti ? (u_int64_t)IDTVEC(fast_syscall_pti) :
+		    (u_int64_t)IDTVEC(fast_syscall));
+		wrmsr(MSR_CSTAR, (u_int64_t)IDTVEC(fast_syscall32));
+	}
 	msr = ((u_int64_t)GSEL(GCODE_SEL, SEL_KPL) << 32) |
 	    ((u_int64_t)GSEL(GUCODE32_SEL, SEL_UPL) << 48);
 	wrmsr(MSR_STAR, msr);
@@ -1230,33 +1239,49 @@ amd64_bsp_ist_init(struct pcpu *pc)
 
 	tssp = &pc->pc_common_tss;
 
-	/* doublefault stack space, runs on ist1 */
-	np = ((struct nmi_pcpu *)&dblfault_stack[sizeof(dblfault_stack)]) - 1;
-	np->np_pcpu = (register_t)pc;
-	tssp->tss_ist1 = (long)np;
+	/* Doublefault stack space, runs on ist1 for IDT. */
+	if (fred) {
+		wrmsr(MSR_FRED_RSP2, (uint64_t)&dblfault_stack[
+		    sizeof(dblfault_stack)]);
+	} else {
+		np = ((struct nmi_pcpu *)&dblfault_stack[sizeof(
+		    dblfault_stack)]) - 1;
+		np->np_pcpu = (register_t)pc;
+		tssp->tss_ist1 = (long)np;
+	}
 
 	/*
-	 * NMI stack, runs on ist2.  The pcpu pointer is stored just
-	 * above the start of the ist2 stack.
+	 * NMI stack.
 	 */
-	np = ((struct nmi_pcpu *)&nmi0_stack[sizeof(nmi0_stack)]) - 1;
-	np->np_pcpu = (register_t)pc;
-	tssp->tss_ist2 = (long)np;
+	if (fred) {
+		wrmsr(MSR_FRED_RSP1, (uint64_t)&nmi0_stack[
+		    sizeof(nmi0_stack)]);
+	} else {
+		/*
+		 * Runs on ist2 for IDT.  The pcpu pointer is stored
+		 * just above the start of the ist2 stack.
+		 */
+		np = ((struct nmi_pcpu *)&nmi0_stack[sizeof(nmi0_stack)]) - 1;
+		np->np_pcpu = (register_t)pc;
+		tssp->tss_ist2 = (long)np;
+	}
 
-	/*
-	 * MC# stack, runs on ist3.  The pcpu pointer is stored just
-	 * above the start of the ist3 stack.
-	 */
-	np = ((struct nmi_pcpu *)&mce0_stack[sizeof(mce0_stack)]) - 1;
-	np->np_pcpu = (register_t)pc;
-	tssp->tss_ist3 = (long)np;
+	if (!fred) {
+		/*
+		 * MC# stack for IDT, runs on ist3.  The pcpu pointer
+		 * is stored just above the start of the ist3 stack.
+		 */
+		np = ((struct nmi_pcpu *)&mce0_stack[sizeof(mce0_stack)]) - 1;
+		np->np_pcpu = (register_t)pc;
+		tssp->tss_ist3 = (long)np;
 
-	/*
-	 * DB# stack, runs on ist4.
-	 */
-	np = ((struct nmi_pcpu *)&dbg0_stack[sizeof(dbg0_stack)]) - 1;
-	np->np_pcpu = (register_t)pc;
-	tssp->tss_ist4 = (long)np;
+		/*
+		 * DB# stack for IDT, runs on ist4.
+		 */
+		np = ((struct nmi_pcpu *)&dbg0_stack[sizeof(dbg0_stack)]) - 1;
+		np->np_pcpu = (register_t)pc;
+		tssp->tss_ist4 = (long)np;
+	}
 }
 
 /*
@@ -1359,6 +1384,13 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
 		TUNABLE_INT_FETCH("hw.use_xsave", &use_xsave);
 	}
 
+	if ((cpu_stdext_feature4 & (CPUID_STDEXT4_FRED | CPUID_STDEXT4_LKGS)) ==
+	    (CPUID_STDEXT4_FRED | CPUID_STDEXT4_LKGS) &&
+	    (cpu_stdext_feature & CPUID_STDEXT_FSGSBASE) != 0 && !pti) {
+		fred = 1;
+		TUNABLE_INT_FETCH("hw.fred", &fred);
+	}
+
 	sched_instance_select();
 
 	link_elf_ireloc();
@@ -1473,9 +1505,11 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
 	setidt(IDT_EVTCHN, pti ? &IDTVEC(xen_intr_upcall_pti) :
 	    &IDTVEC(xen_intr_upcall), SDT_SYSIGT, SEL_KPL, 0);
 #endif
-	r_idt.rd_limit = sizeof(idt0) - 1;
-	r_idt.rd_base = (long) idt;
-	lidt(&r_idt);
+	if (!fred) {
+		r_idt.rd_limit = sizeof(idt0) - 1;
+		r_idt.rd_base = (long) idt;
+		lidt(&r_idt);
+	}
 
 	TUNABLE_INT_FETCH("hw.ibrs_disable", &hw_ibrs_disable);
 	TUNABLE_INT_FETCH("machdep.mitigations.ibrs.disable", &hw_ibrs_disable);
@@ -1564,6 +1598,9 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
 	if (getenv_is_true("debug.dump_modinfo_at_boot"))
 		preload_dump();
 
+	if (fred)
+		amd64_cpu_init_fred();
+
 #ifdef DEV_ISA
 #ifdef DEV_ATPIC
 	elcr_probe();
@@ -1828,6 +1865,11 @@ clear_pcb_flags(struct pcb *pcb, const u_int flags)
 
 extern const char wrmsr_early_safe_gp_handler[];
 
+/*
+ * What about FRED?  wrmsr_early_safe_start() is used before we
+ * switched CPU to the FRED mode.  We use IDT to catch #GP from MSR
+ * write even if BSP is switched to the FRED mode later.
+ */
 void
 wrmsr_early_safe_start(void)
 {
diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c
index 69490b8e275f..81a24a4f4df5 100644
--- a/sys/amd64/amd64/mp_machdep.c
+++ b/sys/amd64/amd64/mp_machdep.c
@@ -240,25 +240,51 @@ init_secondary(void)
 	    IOPERM_BITMAP_SIZE;
 	pc->pc_common_tss.tss_rsp0 = 0;
 
-	/* The doublefault stack runs on IST1. */
-	np = ((struct nmi_pcpu *)&doublefault_stack[DBLFAULT_STACK_SIZE]) - 1;
-	np->np_pcpu = (register_t)pc;
-	pc->pc_common_tss.tss_ist1 = (long)np;
-
-	/* The NMI stack runs on IST2. */
-	np = ((struct nmi_pcpu *)&nmi_stack[NMI_STACK_SIZE]) - 1;
-	np->np_pcpu = (register_t)pc;
-	pc->pc_common_tss.tss_ist2 = (long)np;
-
-	/* The MC# stack runs on IST3. */
-	np = ((struct nmi_pcpu *)&mce_stack[MCE_STACK_SIZE]) - 1;
-	np->np_pcpu = (register_t)pc;
-	pc->pc_common_tss.tss_ist3 = (long)np;
-
-	/* The DB# stack runs on IST4. */
-	np = ((struct nmi_pcpu *)&dbg_stack[DBG_STACK_SIZE]) - 1;
-	np->np_pcpu = (register_t)pc;
-	pc->pc_common_tss.tss_ist4 = (long)np;
+	/*
+	 * The doublefault stack.
+	 * Runs on IST1 for IDT.
+	 * Uses CSL 2 for FRED.
+	 */
+	if (fred) {
+		wrmsr(MSR_FRED_RSP2, (uint64_t)&doublefault_stack[
+		    DBLFAULT_STACK_SIZE]);
+	} else {
+		np = ((struct nmi_pcpu *)&doublefault_stack[
+		    DBLFAULT_STACK_SIZE]) - 1;
+		np->np_pcpu = (register_t)pc;
+		pc->pc_common_tss.tss_ist1 = (long)np;
+	}
+
+	/*
+	 * The NMI stack.
+	 * Runs on IST2 for IDT.
+	 * Uses CSL 1 for FRED.
+	 */
+	if (fred) {
+		wrmsr(MSR_FRED_RSP1, (uint64_t)&nmi_stack[NMI_STACK_SIZE]);
+	} else {
+		np = ((struct nmi_pcpu *)&nmi_stack[NMI_STACK_SIZE]) - 1;
+		np->np_pcpu = (register_t)pc;
+		pc->pc_common_tss.tss_ist2 = (long)np;
+	}
+
+	/*
+	 * The MC# stack.
+	 * Runs on IST3 for IDT.
+	 * Shares CSL 1 with NMI for FRED.
+	 */
+	if (!fred) {
+		np = ((struct nmi_pcpu *)&mce_stack[MCE_STACK_SIZE]) - 1;
+		np->np_pcpu = (register_t)pc;
+		pc->pc_common_tss.tss_ist3 = (long)np;
+	}
+
+	if (!fred) {
+		/* The DB# stack, used for for IDT, runs on IST4. */
+		np = ((struct nmi_pcpu *)&dbg_stack[DBG_STACK_SIZE]) - 1;
+		np->np_pcpu = (register_t)pc;
+		pc->pc_common_tss.tss_ist4 = (long)np;
+	}
 
 	/* Prepare private GDT */
 	gdt_segs[GPROC0_SEL].ssd_base = (long)&pc->pc_common_tss;
@@ -278,7 +304,10 @@ init_secondary(void)
 	wrmsr(MSR_KGSBASE, 0);		/* User value */
 	fix_cpuid();
 
-	lidt(&r_idt);
+	if (fred)
+		amd64_cpu_init_fred();
+	else
+		lidt(&r_idt);
 
 	gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
 	ltr(gsel_tss);
@@ -430,12 +459,17 @@ start_all_aps(void)
 		    M_WAITOK | M_ZERO);
 		doublefault_stack = kmem_malloc(DBLFAULT_STACK_SIZE,
 		    M_WAITOK | M_ZERO);
-		mce_stack = kmem_malloc(MCE_STACK_SIZE,
-		    M_WAITOK | M_ZERO);
+		if (!fred) {
+			mce_stack = kmem_malloc(MCE_STACK_SIZE,
+			    M_WAITOK | M_ZERO);
+		}
 		nmi_stack = kmem_malloc_domainset(
 		    DOMAINSET_PREF(domain), NMI_STACK_SIZE, M_WAITOK | M_ZERO);
-		dbg_stack = kmem_malloc_domainset(
-		    DOMAINSET_PREF(domain), DBG_STACK_SIZE, M_WAITOK | M_ZERO);
+		if (!fred) {
+			dbg_stack = kmem_malloc_domainset(
+			    DOMAINSET_PREF(domain), DBG_STACK_SIZE,
+			    M_WAITOK | M_ZERO);
+		}
 		dpcpu = kmem_malloc_domainset(DOMAINSET_PREF(domain),
 		    DPCPU_SIZE, M_WAITOK | M_ZERO);
 
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
index df9cc44bcbb2..815d356217f9 100644
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/trap.c
@@ -41,6 +41,7 @@
  * AMD64 Trap and System call handling
  */
 
+#include "opt_atpic.h"
 #include "opt_clock.h"
 #include "opt_cpu.h"
 #include "opt_hwpmc_hooks.h"
@@ -61,6 +62,7 @@
 #include <sys/mutex.h>
 #include <sys/resourcevar.h>
 #include <sys/signalvar.h>
+#include <sys/smp.h>
 #include <sys/syscall.h>
 #include <sys/sysctl.h>
 #include <sys/sysent.h>
@@ -83,6 +85,8 @@ PMC_SOFT_DEFINE( , , page_fault, write);
 
 #include <machine/cpu.h>
 #include <machine/intr_machdep.h>
+#include <x86/apicreg.h>
+#include <x86/apicvar.h>
 #include <x86/mca.h>
 #include <machine/md_var.h>
 #include <machine/pcb.h>
@@ -90,6 +94,7 @@ PMC_SOFT_DEFINE( , , page_fault, write);
 #include <machine/stack.h>
 #include <machine/trap.h>
 #include <machine/tss.h>
+#include <x86/isa/icu.h>
 
 #ifdef KDTRACE_HOOKS
 #include <sys/dtrace_bsd.h>
@@ -995,6 +1000,14 @@ trap_diag(struct trapframe *frame, vm_offset_t eva, const char *type_str)
 	    frame->tf_r11, frame->tf_r12);
 	printf("r13: %016lx r14: %016lx r15: %016lx\n", frame->tf_r13,
 	    frame->tf_r14, frame->tf_r15);
+	if (fred) {
+		struct trapframe_fred *f;
+
+		f = __containerof(frame, struct trapframe_fred, tf_idt);
+		printf("evdata %016lx evinfo1 %04x evinfo2 %08x\n",
+		    f->tf_fred_evdata, frame->tf_fred_evinfo1,
+		    frame->tf_fred_evinfo2);
+	}
 }
 
 static void
@@ -1069,6 +1082,14 @@ dblfault_handler(struct trapframe *frame)
 	    frame->tf_cs, frame->tf_ss, frame->tf_ds, frame->tf_es,
 	    frame->tf_fs, frame->tf_gs,
 	    rdmsr(MSR_FSBASE), rdmsr(MSR_GSBASE), rdmsr(MSR_KGSBASE));
+	if (fred) {
+		struct trapframe_fred *f;
+
+		f = __containerof(frame, struct trapframe_fred, tf_idt);
+		printf("evdata %016lx evinfo1 %04x evinfo2 %08x\n",
+		    f->tf_fred_evdata, frame->tf_fred_evinfo1,
+		    frame->tf_fred_evinfo2);
+	}
 	/* Print these separately in case pcpu accesses trap. */
 	printf("cpuid = %d; apic id = %02x\n", PCPU_GET(cpuid),
 	    PCPU_GET(apic_id));
@@ -1295,3 +1316,540 @@ amd64_syscall(struct thread *td, int traced)
 
 	amd64_syscall_ret_flush_l1d_check_inline(td->td_errno);
 }
+
+static void
+trap_fred_extint(struct trapframe_fred *frame, u_int vec)
+{
+	switch (vec) {
+	case IPI_RENDEZVOUS:
+#ifdef COUNT_IPIS
+		ipi_rendezvous_counts[PCPU_GET(cpuid)]++;
+#endif
+		smp_rendezvous_action();
+		lapic_eoi();
+		break;
+	case IPI_INVLOP:
+		invlop_handler();
+		lapic_eoi();
+		break;
+	case IPI_BITMAP_VECTOR:
+		lapic_eoi();
+		ipi_bitmap_handler(&frame->tf_idt);
+		break;
+	case IPI_STOP:
+		lapic_eoi();
+		cpustop_handler();
+		break;
+	case IPI_SUSPEND:
+		cpususpend_handler();
+		lapic_eoi();
+		break;
+	case IPI_SWI:
+		lapic_eoi();
+		ipi_swi_handler(&frame->tf_idt);
+		break;
+	case IPI_OFF:
+		cpuoff_handler();
+		lapic_eoi();
+		break;
+	case APIC_SPURIOUS_INT:
+		break;
+	case APIC_TIMER_INT:
+		lapic_handle_timer(&frame->tf_idt);
+		break;
+	case APIC_CMC_INT:
+		lapic_handle_cmc();
+		break;
+	case APIC_ERROR_INT:
+		lapic_handle_error();
+		break;
+	case APIC_THERMAL_INT:
+		lapic_handle_thermal();
+		break;
+#ifdef XENHVM
+	case IDT_EVTCHN:
+		xen_arch_intr_handle_upcall(&frame->tf_idt);
+		break;
+#endif
+	default:
+		if (vec >= IPI_DYN_FIRST && vec <= IPI_DYN_LAST) {
+			void (*fi)(struct trapframe *);
+
+			fi = fred_ipi_handlers[vec - IPI_DYN_FIRST];
+			if (fi != NULL) {
*** 749 LINES SKIPPED ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a5e986a.22164.2ca88691>