diff options
| author | Konstantin Belousov <kib@FreeBSD.org> | 2026-02-13 11:24:40 +0000 |
|---|---|---|
| committer | Konstantin Belousov <kib@FreeBSD.org> | 2026-07-20 21:51:02 +0000 |
| commit | 6e93f5e4d6932c423b89dff8fc08d86f8bdeb7b9 (patch) | |
| tree | 82ca6a5c617fd9a19549dfe56245b3cc45317e19 | |
| parent | 0e5b1384df10e9b9700047cb79c347874212d2a9 (diff) | |
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
| -rw-r--r-- | sys/amd64/amd64/cpu_switch.S | 73 | ||||
| -rw-r--r-- | sys/amd64/amd64/db_trace.c | 5 | ||||
| -rw-r--r-- | sys/amd64/amd64/exception.S | 282 | ||||
| -rw-r--r-- | sys/amd64/amd64/genassym.c | 15 | ||||
| -rw-r--r-- | sys/amd64/amd64/initcpu.c | 43 | ||||
| -rw-r--r-- | sys/amd64/amd64/machdep.c | 104 | ||||
| -rw-r--r-- | sys/amd64/amd64/mp_machdep.c | 76 | ||||
| -rw-r--r-- | sys/amd64/amd64/trap.c | 558 | ||||
| -rw-r--r-- | sys/amd64/amd64/vm_machdep.c | 10 | ||||
| -rw-r--r-- | sys/amd64/include/md_var.h | 2 | ||||
| -rw-r--r-- | sys/amd64/include/pcb.h | 8 | ||||
| -rw-r--r-- | sys/amd64/vmm/intel/vmx.c | 2 | ||||
| -rw-r--r-- | sys/amd64/vmm/vmm.c | 2 | ||||
| -rw-r--r-- | sys/amd64/vmm/vmm_lapic.c | 5 | ||||
| -rw-r--r-- | sys/amd64/vmm/vmm_lapic.h | 2 | ||||
| -rw-r--r-- | sys/dev/hyperv/vmbus/x86/vmbus_x86.c | 3 | ||||
| -rw-r--r-- | sys/i386/i386/machdep.c | 2 | ||||
| -rw-r--r-- | sys/kern/kern_thread.c | 2 | ||||
| -rw-r--r-- | sys/x86/include/apicvar.h | 3 | ||||
| -rw-r--r-- | sys/x86/include/frame.h | 1 | ||||
| -rw-r--r-- | sys/x86/include/x86_var.h | 3 | ||||
| -rw-r--r-- | sys/x86/x86/local_apic.c | 38 |
22 files changed, 1155 insertions, 84 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 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. */ - 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 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. */ - 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 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; + } - /* 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; + 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) { + fi(&frame->tf_idt); + lapic_eoi(); + } else { + panic("DYN IPI %d without FRED handler", vec); + } + break; + } +#ifdef DEV_ISA +#ifdef DEV_ATPIC + if (vec >= IDT_IO_INTS && vec < IDT_IO_INTS + NUM_ISA_IRQS) { + atpic_handle_intr(vec - IDT_IO_INTS, &frame->tf_idt); + break; + } +#endif +#endif + /* apic ioint */ + lapic_handle_intr(vec, &frame->tf_idt); + break; + } +} + +void fred_out_of_nmi(void); + +static const int fred_ev_to_trapno_table[] = { + [IDT_DE] = T_DIVIDE, + [IDT_DB] = T_TRCTRAP, + [IDT_NMI] = T_RESERVED, + [IDT_BP] = T_BPTFLT, + [IDT_OF] = T_OFLOW, + [IDT_BR] = T_BOUND, + [IDT_UD] = T_PRIVINFLT, + [IDT_NM] = T_DNA, + [IDT_DF] = T_DOUBLEFLT, + [IDT_FPUGP] = T_FPOPFLT, + [IDT_TS] = T_TSSFLT, + [IDT_NP] = T_SEGNPFLT, + [IDT_SS] = T_STKFLT, + [IDT_GP] = T_PROTFLT, + [IDT_PF] = T_PAGEFLT, + [15] = T_RESERVED, + [IDT_MF] = T_ARITHTRAP, + [IDT_AC] = T_ALIGNFLT, + [IDT_MC] = T_MCHK, + [IDT_XF] = T_XMMFLT, + [20] = T_RESERVED, + [21] = T_RESERVED, + [22] = T_RESERVED, + [23] = T_RESERVED, + [24] = T_RESERVED, + [25] = T_RESERVED, + [26] = T_RESERVED, + [27] = T_RESERVED, + [28] = T_RESERVED, + [29] = T_RESERVED, + [30] = T_RESERVED, + [31] = T_RESERVED, +}; + +static void +trap_fred_ev_to_trapno(struct trapframe_fred *frame, u_int exc) +{ + frame->tf_idt.tf_trapno = fred_ev_to_trapno_table[exc]; +} + +static void +trap_fred_handle_u_exc(struct thread *td, struct trapframe_fred *frame, + u_int exc, ksiginfo_t *ksi, bool *do_trapsig) +{ + struct proc *p; + register_t addr; + int pf, signo, ucode; + + addr = frame->tf_idt.tf_rip; + signo = 0; + ucode = 0; + frame->tf_idt.tf_addr = 0; + trap_check_intr_user(td, &frame->tf_idt); + + switch (exc) { + case IDT_DE: + ucode = FPE_INTDIV; + signo = SIGFPE; + break; + case IDT_DB: + signo = SIGTRAP; + ucode = TRAP_TRACE; + if ((frame->tf_fred_evdata & TF_FRED_EVDATA_BS) != 0) + trap_clear_step(td, &frame->tf_idt); + break; + case IDT_BP: +#ifdef KDTRACE_HOOKS + if (trap_user_dtrace(&frame->tf_idt, &dtrace_pid_probe_ptr)) + return; +#endif + signo = SIGTRAP; + ucode = TRAP_BRKPT; + break; + case IDT_OF: + ucode = FPE_INTOVF; + signo = SIGFPE; + break; + case IDT_BR: + ucode = FPE_FLTSUB; + signo = SIGFPE; + break; + case IDT_UD: + signo = SIGILL; + ucode = ILL_PRVOPC; + break; + case IDT_NM: + KASSERT(PCB_USER_FPU(td->td_pcb), + ("kernel FPU ctx has leaked")); + fpudna(); + return; + case IDT_DF: + signo = SIGBUS; + ucode = BUS_OBJERR; + break; + case IDT_FPUGP: + ucode = ILL_COPROC; + signo = SIGILL; + break; + case IDT_TS: + signo = SIGBUS; + ucode = BUS_OBJERR; + break; + case IDT_NP: + signo = SIGBUS; + ucode = BUS_ADRERR; + break; + case IDT_SS: + signo = SIGBUS; + ucode = BUS_ADRERR; + break; + case IDT_GP: + signo = SIGBUS; + ucode = BUS_OBJERR; + break; + case IDT_PF: + p = td->td_proc; + frame->tf_idt.tf_addr = addr = frame->tf_fred_evdata; + if (*p->p_sysent->sv_trap != NULL && + (*p->p_sysent->sv_trap)(td) == 0) + return; + pf = trap_pfault(&frame->tf_idt, true, &signo, &ucode); + if (pf == -1 || pf == 0) + return; + break; + case IDT_MF: + ucode = fputrap_x87(); + if (ucode == -1) + return; + signo = SIGFPE; + break; + case IDT_AC: + signo = SIGBUS; + ucode = BUS_ADRALN; + break; + case IDT_MC: + mca_intr(); + return; + case IDT_XF: + ucode = fputrap_sse(); + if (ucode == -1) + return; + signo = SIGFPE; + break; + default: + panic("missed FRED handler for exception %d", exc); + } + + ksiginfo_init_trap(ksi); + ksi->ksi_signo = signo; + ksi->ksi_code = ucode; + ksi->ksi_trapno = frame->tf_idt.tf_trapno; + ksi->ksi_addr = (void *)addr; + trap_uprintf_signal(td, &frame->tf_idt, addr, signo, ucode); + KASSERT((read_rflags() & PSL_I) != 0, ("interrupts disabled")); + *do_trapsig = true; +} + +static u_int +fred_inst_len(const struct trapframe_fred *frame) +{ + return ((frame->tf_idt.tf_fred_evinfo2 & TF_FRED_EVINFO2_INSTLENMASK) >> + TF_FRED_EVINFO2_INSTLENSHIFT); +} + +int fred_verbose_events; + +void trap_fred_u(struct trapframe_fred *frame); +void +trap_fred_u(struct trapframe_fred *frame) +{ + struct thread *td; + ksiginfo_t ksi; + u_int type, vec; + bool do_trapsig; + + td = curthread; + type = frame->tf_idt.tf_fred_evinfo2 & TF_FRED_EVINFO2_TYPEMASK; + vec = frame->tf_idt.tf_fred_evinfo2 & TF_FRED_EVINFO2_VECMASK; + do_trapsig = false; + if (fred_verbose_events) { + printf("pid %d tid %d type %d vec %d rax %#lx\n", + td->td_proc->p_pid, td->td_tid, + (frame->tf_idt.tf_fred_evinfo2 & + TF_FRED_EVINFO2_TYPEMASK) >> 16, + vec, frame->tf_idt.tf_rax); + } + if (__predict_true((td->td_proc->p_sysent->sv_flags & + (SV_ABI_MASK | SV_LP64)) == (SV_ABI_FREEBSD | SV_LP64) && + (type == TF_FRED_EVINFO2_TYPE_SYSCALL || + type == TF_FRED_EVINFO2_TYPE_EXTINT || + (type == TF_FRED_EVINFO2_TYPE_EXC && vec == IDT_PF)))) + clear_pcb_flags(td->td_pcb, PCB_FULL_IRET); + else + set_pcb_flags(td->td_pcb, PCB_FULL_IRET); + switch (type) { + case TF_FRED_EVINFO2_TYPE_EXTINT: + trap_fred_extint(frame, vec); + break; + case TF_FRED_EVINFO2_TYPE_NMI: + frame->tf_idt.tf_trapno = T_NMI; + nmi_handle_intr(&frame->tf_idt); +#ifdef HWPMC_HOOKS + if ((td->td_pflags & TDP_CALLCHAIN) != 0) { + int (*ph)(struct thread *, int, void *); + + fred_out_of_nmi(); + ph = atomic_load_ptr(&pmc_hook); + if (ph != NULL) { + enable_intr(); + ph(td, PMC_FN_USER_CALLCHAIN, &frame->tf_idt); + } + } +#endif + break; + case TF_FRED_EVINFO2_TYPE_INT1: + case TF_FRED_EVINFO2_TYPE_INT3: + case TF_FRED_EVINFO2_TYPE_EXC: + enable_intr(); + trap_fred_ev_to_trapno(frame, vec); +#ifdef KDTRACE_HOOKS + if (dtrace_trap_func != NULL && (*dtrace_trap_func)( + &frame->tf_idt, frame->tf_idt.tf_trapno) != 0) + return; +#endif + td->td_pticks = 0; + td->td_frame = &frame->tf_idt; + if (td->td_cowgen != atomic_load_int(&td->td_proc->p_cowgen)) + thread_cow_update(td); + trap_fred_handle_u_exc(td, frame, vec, &ksi, &do_trapsig); + break; + case TF_FRED_EVINFO2_TYPE_INTn: + enable_intr(); +#ifdef COMPAT_FREEBSD32 + if (vec == IDT_SYSCALL) { + frame->tf_idt.tf_err = fred_inst_len(frame); + ia32_syscall(&frame->tf_idt); + break; + } +#endif + frame->tf_idt.tf_trapno = vec; + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGBUS; + ksi.ksi_code = BUS_OBJERR; + ksi.ksi_trapno = frame->tf_idt.tf_trapno; + ksi.ksi_addr = (void *)frame->tf_idt.tf_rip; + do_trapsig = true; + break; + case TF_FRED_EVINFO2_TYPE_SYSCALL: + /* Excessive */ + enable_intr(); + if (vec == TF_FRED_EVINFO2_VEC_SYSCALL) { + frame->tf_idt.tf_err = fred_inst_len(frame); + td->td_frame = &frame->tf_idt; + amd64_syscall(td, (frame->tf_idt.tf_rflags & PSL_T) != + 0); + } else { + /* SYSENTER returns without action */ + } + break; + default: + __unreachable(); + } + if (do_trapsig) + trapsignal(td, &ksi); + userret(td, &frame->tf_idt); + KASSERT(PCB_USER_FPU(td->td_pcb), + ("Return from trap with kernel FPU ctx leaked")); +} + +extern const char fred_ld_fs[]; +extern const char fred_ld_fs_fault[]; +extern const char fred_ld_fsbase[]; +extern const char fred_ld_fsbase_fault[]; +extern const char fred_lkgs[]; +extern const char fred_lkgs_fault[]; +extern const char fred_ld_kgsbase[]; +extern const char fred_ld_kgsbase_fault[]; +extern const char fred_ld_es[]; +extern const char fred_ld_es_fault[]; +extern const char fred_ld_ds[]; +extern const char fred_ld_ds_fault[]; +extern const char fred_eretu[]; +extern const char fred_syscall_eretu[]; +extern const char fred_eretu_fault[]; + +static const struct sfhandler fred_sfhandlers[] = { + { + .faddr = (uintptr_t)fred_ld_fs, + .fhandler = (uintptr_t)fred_ld_fs_fault, + }, + { + .faddr = (uintptr_t)fred_ld_fsbase, + .fhandler = (uintptr_t)fred_ld_fsbase_fault, + }, + { + .faddr = (uintptr_t)fred_lkgs, + .fhandler = (uintptr_t)fred_lkgs_fault, + }, + { + .faddr = (uintptr_t)fred_ld_kgsbase, + .fhandler = (uintptr_t)fred_ld_kgsbase_fault, + }, + { + .faddr = (uintptr_t)fred_ld_es, + .fhandler = (uintptr_t)fred_ld_es_fault, + }, + { + .faddr = (uintptr_t)fred_ld_ds, + .fhandler = (uintptr_t)fred_ld_ds_fault, + }, + { + .faddr = (uintptr_t)fred_eretu, + .fhandler = (uintptr_t)fred_eretu_fault, + }, + { + .faddr = (uintptr_t)fred_syscall_eretu, + .fhandler = (uintptr_t)fred_eretu_fault, + }, +}; + +static void +trap_fred_handle_k_exc(struct thread *td, struct trapframe_fred *frame, + u_int exc) +{ + register_t addr; + int i; + + addr = frame->tf_idt.tf_rip; + frame->tf_idt.tf_addr = 0; + KASSERT(cold || td->td_ucred != NULL, + ("kernel trap doesn't have ucred")); + + /* + * FRED exception handler runs with the interrupts disabled, + * unlike the IDT case. Only for page faults the interrupts + * are enabled, since this is the only legitimate exception + * from the kernel mode. Anything else results in panic, so + * there is no sense to enable interrupts. + */ + + if (exc != IDT_PF && trap_check_pcb_onfault(td, &frame->tf_idt)) + return; + + switch (exc) { + case IDT_DE: + break; + case IDT_DB: + if (user_dbreg_trap(frame->tf_fred_evdata)) + return; + /* FALLTHROUGH */ + case IDT_BP: +#ifdef KDB + if (kdb_trap(frame->tf_idt.tf_trapno, frame->tf_fred_evdata, + &frame->tf_idt)) + return; +#endif + break; + case IDT_OF: + break; + case IDT_BR: + break; + case IDT_UD: + break; + case IDT_NM: + if (PCB_USER_FPU(td->td_pcb)) + panic("Unregistered use of FPU in kernel"); + fpudna(); + return; + case IDT_DF: + dblfault_handler(&frame->tf_idt); + break; + case IDT_FPUGP: + break; + case IDT_TS: + /* + * PSL_NT is cleared automatically by FRED entry. + */ + break; + case IDT_NP: + case IDT_SS: + case IDT_GP: + if (td->td_intr_nesting_level != 0) + break; + for (i = 0; i < nitems(fred_sfhandlers); i++) { + if (frame->tf_idt.tf_rip == fred_sfhandlers[i].faddr) { + KASSERT((read_rflags() & PSL_I) == 0, + ("interrupts enabled")); + frame->tf_idt.tf_rip = fred_sfhandlers[i]. + fhandler; + return; + } + } + if (curpcb->pcb_onfault != NULL) { + frame->tf_idt.tf_rip = (long)curpcb->pcb_onfault; + return; + } + break; + case IDT_PF: + frame->tf_idt.tf_addr = addr = frame->tf_fred_evdata; + if ((frame->tf_idt.tf_rflags & PSL_I) != 0) + enable_intr(); + trap_pfault(&frame->tf_idt, false, NULL, NULL); + return; + case IDT_MF: + break; + case IDT_AC: + break; + case IDT_MC: + mca_intr(); + return; + case IDT_XF: + break; + default: + printf("Missing FRED handler for exception %d", exc); + trap_fatal(&frame->tf_idt, 0); + } + trap_fatal(&frame->tf_idt, 0); +} + +void trap_fred_k(struct trapframe_fred *frame); +void +trap_fred_k(struct trapframe_fred *frame) +{ + struct thread *td; + u_int vec; + + td = curthread; + vec = frame->tf_idt.tf_fred_evinfo2 & TF_FRED_EVINFO2_VECMASK; + switch (frame->tf_idt.tf_fred_evinfo2 & TF_FRED_EVINFO2_TYPEMASK) { + case TF_FRED_EVINFO2_TYPE_EXTINT: + trap_fred_extint(frame, vec); + break; + case TF_FRED_EVINFO2_TYPE_NMI: + frame->tf_idt.tf_trapno = T_NMI; + nmi_handle_intr(&frame->tf_idt); + break; + case TF_FRED_EVINFO2_TYPE_INT1: + case TF_FRED_EVINFO2_TYPE_INT3: + case TF_FRED_EVINFO2_TYPE_EXC: + trap_fred_ev_to_trapno(frame, vec); + trap_fred_handle_k_exc(td, frame, vec); + break; + case TF_FRED_EVINFO2_TYPE_INTn: + panic("INT%d from kernel", vec); + break; + case TF_FRED_EVINFO2_TYPE_SYSCALL: + panic("syscall from kernel"); + break; + default: + __unreachable(); + } +} diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index 1de891680f94..58af1cb8665c 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -378,7 +378,15 @@ void cpu_thread_new_kstack(struct thread *td) { td->td_md.md_stack_base = td_kstack_top(td); - td->td_frame = (struct trapframe *)td->td_md.md_stack_base - 1; + if (fred) { + struct trapframe_fred *f; + + f = (struct trapframe_fred *)td->td_md.md_stack_base - 1; + td->td_frame = &f->tf_idt; + } else { + td->td_frame = (struct trapframe *)td->td_md. + md_stack_base - 1; + } } void diff --git a/sys/amd64/include/md_var.h b/sys/amd64/include/md_var.h index 0e8fe916490b..55aa4843705e 100644 --- a/sys/amd64/include/md_var.h +++ b/sys/amd64/include/md_var.h @@ -65,12 +65,14 @@ struct sysentvec; struct trapframe; void amd64_conf_fast_syscall(void); +void amd64_cpu_init_fred(void); void amd64_db_resume_dbreg(void); vm_paddr_t amd64_loadaddr(void); void amd64_lower_shared_page(struct sysentvec *); void amd64_bsp_pcpu_init1(struct pcpu *pc); void amd64_bsp_pcpu_init2(uint64_t rsp0); void amd64_bsp_ist_init(struct pcpu *pc); +void amd64_bsp_fred_csl_init(struct pcpu *pc); void amd64_syscall(struct thread *td, int traced); void amd64_syscall_ret_flush_l1d(int error); void amd64_syscall_ret_flush_l1d_recalc(void); diff --git a/sys/amd64/include/pcb.h b/sys/amd64/include/pcb.h index 27e1dce08ee1..d1deb8ac9674 100644 --- a/sys/amd64/include/pcb.h +++ b/sys/amd64/include/pcb.h @@ -102,11 +102,17 @@ struct pcb { register_t pcb_lstar; register_t pcb_cstar; register_t pcb_sfmask; + /* FRED MSRs */ + register_t pcb_fred_rsp0; + register_t pcb_fred_rsp1; + register_t pcb_fred_rsp2; + register_t pcb_fred_rsp3; + register_t pcb_fred_stklvls; + register_t pcb_fred_config; struct savefpu *pcb_save; register_t pcb_tlsbase; /* not same as pcb_fsbase */ - uint64_t pcb_pad[4]; }; /* Per-CPU state saved during suspend and resume. */ diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index 7a2d0de6beff..65a6d9603caf 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -894,7 +894,7 @@ vmx_modinit(int ipinum) &tmp); if (error == 0) { pirvec = lapic_ipi_alloc(pti ? &IDTVEC(justreturn1_pti) : - &IDTVEC(justreturn)); + &IDTVEC(justreturn), vmm_justreturn); if (pirvec < 0) { if (bootverbose) { printf("vmx_modinit: unable to " diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c index 050cc93d2605..4dedb5da94a3 100644 --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -287,7 +287,7 @@ vmm_modinit(void) vmm_host_state_init(); vmm_ipinum = lapic_ipi_alloc(pti ? &IDTVEC(justreturn1_pti) : - &IDTVEC(justreturn)); + &IDTVEC(justreturn), vmm_justreturn); if (vmm_ipinum < 0) vmm_ipinum = IPI_AST; diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c index 44bae5da31e5..ce3e73e55b3c 100644 --- a/sys/amd64/vmm/vmm_lapic.c +++ b/sys/amd64/vmm/vmm_lapic.c @@ -237,3 +237,8 @@ lapic_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size, error = vlapic_read(vlapic, 1, off, rval, arg); return (error); } + +void +vmm_justreturn(struct trapframe *frame) +{ +} diff --git a/sys/amd64/vmm/vmm_lapic.h b/sys/amd64/vmm/vmm_lapic.h index 1c0e17b15c18..b96d2002d99c 100644 --- a/sys/amd64/vmm/vmm_lapic.h +++ b/sys/amd64/vmm/vmm_lapic.h @@ -71,4 +71,6 @@ int lapic_set_local_intr(struct vm *vm, struct vcpu *vcpu, int vector); int lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg); +void vmm_justreturn(struct trapframe *); + #endif diff --git a/sys/dev/hyperv/vmbus/x86/vmbus_x86.c b/sys/dev/hyperv/vmbus/x86/vmbus_x86.c index 0ed3b1b5f9d9..130ea5bf556b 100644 --- a/sys/dev/hyperv/vmbus/x86/vmbus_x86.c +++ b/sys/dev/hyperv/vmbus/x86/vmbus_x86.c @@ -151,7 +151,8 @@ vmbus_setup_intr1(struct vmbus_softc *sc) * free IDT vector for Hyper-V ISR and set it up. */ sc->vmbus_idtvec = lapic_ipi_alloc( - pti ? IDTVEC(vmbus_isr_pti) : IDTVEC(vmbus_isr)); + pti ? IDTVEC(vmbus_isr_pti) : IDTVEC(vmbus_isr), + vmbus_handle_intr); if (sc->vmbus_idtvec < 0) { #if defined(__amd64__) && defined(KLD_MODULE) pmap_pti_remove_kva(VMBUS_ISR_ADDR, VMBUS_ISR_ADDR + PAGE_SIZE); diff --git a/sys/i386/i386/machdep.c b/sys/i386/i386/machdep.c index 71992f18e8fb..872041785cc3 100644 --- a/sys/i386/i386/machdep.c +++ b/sys/i386/i386/machdep.c @@ -187,6 +187,8 @@ struct mtx icu_lock; struct mem_range_softc mem_range_softc; +int fred; + extern char start_exceptions[], end_exceptions[]; extern struct sysentvec elf32_freebsd_sysvec; diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index ebfbfa159c5a..094fd0e0a0f4 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -92,7 +92,7 @@ _Static_assert(offsetof(struct thread, td_pflags) == 0x114, "struct thread KBI td_pflags"); _Static_assert(offsetof(struct thread, td_frame) == 0x4e8, "struct thread KBI td_frame"); -_Static_assert(offsetof(struct thread, td_emuldata) == 0x6f0, +_Static_assert(offsetof(struct thread, td_emuldata) == 0x700, "struct thread KBI td_emuldata"); _Static_assert(offsetof(struct proc, p_flag) == 0xb8, "struct proc KBI p_flag"); diff --git a/sys/x86/include/apicvar.h b/sys/x86/include/apicvar.h index 00ec651be3d3..a6116591eb83 100644 --- a/sys/x86/include/apicvar.h +++ b/sys/x86/include/apicvar.h @@ -253,7 +253,8 @@ lapic_ipi_vectored(u_int vector, int dest) } int lapic_ipi_wait(int delay); -int lapic_ipi_alloc(inthand_t *ipifunc); +int lapic_ipi_alloc(inthand_t *ipifunc, + void (*fred_ipifunc)(struct trapframe *)); void lapic_ipi_free(int vector); int lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked); int lapic_set_lvt_mode(u_int apic_id, u_int lvt, u_int32_t mode); diff --git a/sys/x86/include/frame.h b/sys/x86/include/frame.h index b8e090ff95d0..63a612ac5d43 100644 --- a/sys/x86/include/frame.h +++ b/sys/x86/include/frame.h @@ -200,7 +200,6 @@ struct trapframe_fred { #define TF_HASBASES 0x00000002 #define TF_HASFPXSTATE 0x00000004 #define TF_RESERV0 0x00000008 /* no tlsbase in the trapframe */ -#define TF_FRED 0x00000010 #endif /* __amd64__ */ #endif /* _MACHINE_FRAME_H_ */ diff --git a/sys/x86/include/x86_var.h b/sys/x86/include/x86_var.h index 40dd21107436..1711b13b6af6 100644 --- a/sys/x86/include/x86_var.h +++ b/sys/x86/include/x86_var.h @@ -97,6 +97,7 @@ extern int x86_rngds_mitg_enable; extern int zenbleed_enable; extern int cpu_amdc1e_bug; extern char bootmethod[16]; +extern int fred; struct pcb; struct thread; @@ -114,6 +115,8 @@ struct minidumpstate; */ typedef void alias_for_inthand_t(void); +extern void (*fred_ipi_handlers[])(struct trapframe *); + bool acpi_get_fadt_bootflags(uint16_t *flagsp); void *alloc_fpusave(int flags); u_int cpu_auxmsr(void); diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c index daac99fad753..0dac24ae3048 100644 --- a/sys/x86/x86/local_apic.c +++ b/sys/x86/x86/local_apic.c @@ -2394,6 +2394,8 @@ native_lapic_ipi_vectored(u_int vector, int dest) void (*ipi_vectored)(u_int, int) = &native_lapic_ipi_vectored; #endif /* SMP */ +void (*fred_ipi_handlers[IPI_DYN_LAST - IPI_DYN_FIRST])(struct trapframe *); + /* * Since the IDT is shared by all CPUs the IPI slot update needs to be globally * visible. @@ -2408,10 +2410,9 @@ void (*ipi_vectored)(u_int, int) = &native_lapic_ipi_vectored; * the IDT slot update is globally visible before the IPI is delivered. */ int -lapic_ipi_alloc(inthand_t *ipifunc) +lapic_ipi_alloc(inthand_t *ipifunc, void (*fred_ipifunc)(struct trapframe *)) { - struct gate_descriptor *ip; - long func; + void (*fip)(struct trapframe *); int idx, vector; KASSERT(ipifunc != &IDTVEC(rsvd) && ipifunc != &IDTVEC(rsvd_pti), @@ -2420,14 +2421,10 @@ lapic_ipi_alloc(inthand_t *ipifunc) vector = -1; mtx_lock_spin(&icu_lock); for (idx = IPI_DYN_FIRST; idx <= IPI_DYN_LAST; idx++) { - ip = &idt[idx]; - func = (ip->gd_hioffset << 16) | ip->gd_looffset; -#ifdef __i386__ - func -= setidt_disp; -#endif - if ((!pti && func == (uintptr_t)&IDTVEC(rsvd)) || - (pti && func == (uintptr_t)&IDTVEC(rsvd_pti))) { + fip = fred_ipi_handlers[idx - IPI_DYN_FIRST]; + if (fip == NULL) { vector = idx; + fred_ipi_handlers[idx - IPI_DYN_FIRST] = fred_ipifunc; setidt(vector, ipifunc, SDT_APIC, SEL_KPL, GSEL_APIC); break; } @@ -2446,15 +2443,20 @@ lapic_ipi_free(int vector) ("%s: invalid vector %d", __func__, vector)); mtx_lock_spin(&icu_lock); - ip = &idt[vector]; - func = (ip->gd_hioffset << 16) | ip->gd_looffset; + KASSERT(fred_ipi_handlers[vector - IPI_DYN_FIRST] != NULL, + ("NULL fred func %d", vector)); + fred_ipi_handlers[vector - IPI_DYN_FIRST] = NULL; + if (!fred) { + ip = &idt[vector]; + func = (ip->gd_hioffset << 16) | ip->gd_looffset; #ifdef __i386__ - func -= setidt_disp; + func -= setidt_disp; #endif - KASSERT(func != (uintptr_t)&IDTVEC(rsvd) && - func != (uintptr_t)&IDTVEC(rsvd_pti), - ("invalid idtfunc %#lx", func)); - setidt(vector, pti ? &IDTVEC(rsvd_pti) : &IDTVEC(rsvd), SDT_APIC, - SEL_KPL, GSEL_APIC); + KASSERT(func != (uintptr_t)&IDTVEC(rsvd) && + func != (uintptr_t)&IDTVEC(rsvd_pti), + ("invalid idtfunc %d %#lx", vector, func)); + setidt(vector, pti ? &IDTVEC(rsvd_pti) : &IDTVEC(rsvd), + SDT_APIC, SEL_KPL, GSEL_APIC); + } mtx_unlock_spin(&icu_lock); } |
