Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/amd64/exception.S
Show First 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | X\l: | ||||
.endm | .endm | ||||
TRAP div, T_DIVIDE | TRAP div, T_DIVIDE | ||||
TRAP ofl, T_OFLOW | TRAP ofl, T_OFLOW | ||||
TRAP bnd, T_BOUND | TRAP bnd, T_BOUND | ||||
TRAP ill, T_PRIVINFLT | TRAP ill, T_PRIVINFLT | ||||
TRAP dna, T_DNA | TRAP dna, T_DNA | ||||
TRAP fpusegm, T_FPOPFLT | TRAP fpusegm, T_FPOPFLT | ||||
TRAP mchk, T_MCHK | |||||
TRAP rsvd, T_RESERVED | TRAP rsvd, T_RESERVED | ||||
TRAP fpu, T_ARITHTRAP | TRAP fpu, T_ARITHTRAP | ||||
TRAP xmm, T_XMMFLT | TRAP xmm, T_XMMFLT | ||||
/* This group of traps have tf_err already pushed by the cpu. */ | /* This group of traps have tf_err already pushed by the cpu. */ | ||||
.macro TRAP_ERR l, trapno | .macro TRAP_ERR l, trapno | ||||
PTI_ENTRY \l,X\l,has_err=1 | PTI_ENTRY \l,X\l,has_err=1 | ||||
.globl X\l | .globl X\l | ||||
▲ Show 20 Lines • Show All 411 Lines • ▼ Show 20 Lines | IDTVEC(nmi) | ||||
movq %rax,%cr3 | movq %rax,%cr3 | ||||
jmp nmi_calltrap | jmp nmi_calltrap | ||||
nmi_fromuserspace: | nmi_fromuserspace: | ||||
incl %ebx | incl %ebx | ||||
swapgs | swapgs | ||||
movq %cr3,%r13 | movq %cr3,%r13 | ||||
movq PCPU(KCR3),%rax | movq PCPU(KCR3),%rax | ||||
cmpq $~0,%rax | cmpq $~0,%rax | ||||
je 1f | je 1f | ||||
jhb: I think there is a bug here? Namely, if KCR3 is ~0, then we jmp to 1 which assumes that %rdi… | |||||
Not Done Inline ActionsI agree. kib: I agree. | |||||
movq %rax,%cr3 | movq %rax,%cr3 | ||||
movq PCPU(CURPCB),%rdi | 1: movq PCPU(CURPCB),%rdi | ||||
testq %rdi,%rdi | testq %rdi,%rdi | ||||
jz 3f | jz 3f | ||||
orl $PCB_FULL_IRET,PCB_FLAGS(%rdi) | orl $PCB_FULL_IRET,PCB_FLAGS(%rdi) | ||||
1: testb $CPUID_STDEXT_FSGSBASE,cpu_stdext_feature(%rip) | testb $CPUID_STDEXT_FSGSBASE,cpu_stdext_feature(%rip) | ||||
jz 3f | jz 3f | ||||
cmpw $KUF32SEL,TF_FS(%rsp) | cmpw $KUF32SEL,TF_FS(%rsp) | ||||
jne 2f | jne 2f | ||||
rdfsbase %rax | rdfsbase %rax | ||||
movq %rax,PCB_FSBASE(%rdi) | movq %rax,PCB_FSBASE(%rdi) | ||||
2: cmpw $KUG32SEL,TF_GS(%rsp) | 2: cmpw $KUG32SEL,TF_GS(%rsp) | ||||
jne 3f | jne 3f | ||||
movl $MSR_KGSBASE,%ecx | movl $MSR_KGSBASE,%ecx | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | outofnmi: | ||||
movq PCPU(CURTHREAD),%rdi /* thread */ | movq PCPU(CURTHREAD),%rdi /* thread */ | ||||
movq $PMC_FN_USER_CALLCHAIN,%rsi /* command */ | movq $PMC_FN_USER_CALLCHAIN,%rsi /* command */ | ||||
movq %rsp,%rdx /* frame */ | movq %rsp,%rdx /* frame */ | ||||
sti | sti | ||||
call *%rax | call *%rax | ||||
cli | cli | ||||
nocallchain: | nocallchain: | ||||
#endif | #endif | ||||
testl %ebx,%ebx | testl %ebx,%ebx /* %ebx == 0 => return to userland */ | ||||
jnz doreti_exit | jnz doreti_exit | ||||
nmi_kernelexit: | |||||
/* | /* | ||||
* Put back the preserved MSR_GSBASE value. | * Put back the preserved MSR_GSBASE value. | ||||
*/ | */ | ||||
movl $MSR_GSBASE,%ecx | movl $MSR_GSBASE,%ecx | ||||
movq %r12,%rdx | movq %r12,%rdx | ||||
movl %edx,%eax | movl %edx,%eax | ||||
shrq $32,%rdx | shrq $32,%rdx | ||||
wrmsr | wrmsr | ||||
movq %r13,%cr3 | movq %r13,%cr3 | ||||
nmi_restoreregs: | RESTORE_REGS | ||||
addq $TF_RIP,%rsp | |||||
jmp doreti_iret | |||||
/* | |||||
* MC# handling is similar to NMI. | |||||
* | |||||
* As with NMIs, machine check exceptions do not respect RFLAGS.IF and | |||||
* can occur at any time with a GS.base value that does not correspond | |||||
* to the privilege level in CS. | |||||
* | |||||
* Machine checks are not unblocked by iretq, but it is best to run | |||||
* the handler with interrupts disabled since the exception may have | |||||
* interrupted a critical section. | |||||
* | |||||
* The MC# handler runs on its own stack (tss_ist3). The canonical | |||||
* GS.base value for the processor is stored just above the bottom of | |||||
* its MC# stack. For exceptions taken from kernel mode, the current | |||||
* value in the processor's GS.base is saved at entry to C-preserved | |||||
* register %r12, the canonical value for GS.base is then loaded into | |||||
* the processor, and the saved value is restored at exit time. For | |||||
* exceptions taken from user mode, the cheaper 'SWAPGS' instructions | |||||
* are used for swapping GS.base. | |||||
*/ | |||||
IDTVEC(mchk) | |||||
subq $TF_RIP,%rsp | |||||
movl $(T_MCHK),TF_TRAPNO(%rsp) | |||||
movq $0,TF_ADDR(%rsp) | |||||
movq $0,TF_ERR(%rsp) | |||||
movq %rdi,TF_RDI(%rsp) | |||||
movq %rsi,TF_RSI(%rsp) | |||||
movq %rdx,TF_RDX(%rsp) | |||||
movq %rcx,TF_RCX(%rsp) | |||||
movq %r8,TF_R8(%rsp) | |||||
movq %r9,TF_R9(%rsp) | |||||
movq %rax,TF_RAX(%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_HASSEGS,TF_FLAGS(%rsp) | |||||
cld | |||||
xorl %ebx,%ebx | |||||
testb $SEL_RPL_MASK,TF_CS(%rsp) | |||||
jnz mchk_fromuserspace | |||||
/* | |||||
* We've interrupted the kernel. Preserve GS.base in %r12 | |||||
* and %cr3 in %r13. | |||||
*/ | |||||
movl $MSR_GSBASE,%ecx | |||||
rdmsr | |||||
movq %rax,%r12 | |||||
shlq $32,%rdx | |||||
orq %rdx,%r12 | |||||
/* Retrieve and load the canonical value for GS.base. */ | |||||
movq TF_SIZE(%rsp),%rdx | |||||
movl %edx,%eax | |||||
shrq $32,%rdx | |||||
wrmsr | |||||
movq %cr3,%r13 | |||||
movq PCPU(KCR3),%rax | |||||
cmpq $~0,%rax | |||||
je mchk_calltrap | |||||
movq %rax,%cr3 | |||||
jmp mchk_calltrap | |||||
mchk_fromuserspace: | |||||
incl %ebx | |||||
swapgs | |||||
movq %cr3,%r13 | |||||
movq PCPU(KCR3),%rax | |||||
cmpq $~0,%rax | |||||
je 1f | |||||
movq %rax,%cr3 | |||||
1: | |||||
/* Note: this label is also used by ddb and gdb: */ | |||||
mchk_calltrap: | |||||
FAKE_MCOUNT(TF_RIP(%rsp)) | |||||
movq %rsp,%rdi | |||||
call mca_intr | |||||
MEXITCOUNT | |||||
testl %ebx,%ebx /* %ebx == 0 => return to userland */ | |||||
jnz doreti_exit | |||||
/* | |||||
* Put back the preserved MSR_GSBASE value. | |||||
*/ | |||||
movl $MSR_GSBASE,%ecx | |||||
movq %r12,%rdx | |||||
movl %edx,%eax | |||||
shrq $32,%rdx | |||||
wrmsr | |||||
movq %r13,%cr3 | |||||
RESTORE_REGS | RESTORE_REGS | ||||
addq $TF_RIP,%rsp | addq $TF_RIP,%rsp | ||||
jmp doreti_iret | jmp doreti_iret | ||||
ENTRY(fork_trampoline) | ENTRY(fork_trampoline) | ||||
movq %r12,%rdi /* function */ | movq %r12,%rdi /* function */ | ||||
movq %rbx,%rsi /* arg1 */ | movq %rbx,%rsi /* arg1 */ | ||||
movq %rsp,%rdx /* trapframe pointer */ | movq %rsp,%rdx /* trapframe pointer */ | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | doreti_ast: | ||||
movq PCPU(CURTHREAD),%rax | movq PCPU(CURTHREAD),%rax | ||||
testl $TDF_ASTPENDING | TDF_NEEDRESCHED,TD_FLAGS(%rax) | testl $TDF_ASTPENDING | TDF_NEEDRESCHED,TD_FLAGS(%rax) | ||||
je doreti_exit | je doreti_exit | ||||
sti | sti | ||||
movq %rsp,%rdi /* pass a pointer to the trapframe */ | movq %rsp,%rdi /* pass a pointer to the trapframe */ | ||||
call ast | call ast | ||||
jmp doreti_ast | jmp doreti_ast | ||||
/* | /* | ||||
Done Inline ActionsI do not think that setting PCB_FULL_IRET and storing bases into pcb is strictly necessary there, because handler is not going to switch context. But keep it if it is simpler to keep the code similar to nmi. kib: I do not think that setting PCB_FULL_IRET and storing bases into pcb is strictly necessary… | |||||
Not Done Inline ActionsI can remove it easily enough. Is the same true of NMIs? I don't think those can trigger a context switch and return either? jhb: I can remove it easily enough. Is the same true of NMIs? I don't think those can trigger a… | |||||
Not Done Inline ActionsNMI handler releases the in-nmi state and enables interrupts when interrupted thread executed in usermode, allowing context switching, so save of bases is needed there. kib: NMI handler releases the in-nmi state and enables interrupts when interrupted thread executed… | |||||
* doreti_exit: pop registers, iret. | * doreti_exit: pop registers, iret. | ||||
* | * | ||||
* The segment register pop is a special case, since it may | * The segment register pop is a special case, since it may | ||||
* fault if (for example) a sigreturn specifies bad segment | * fault if (for example) a sigreturn specifies bad segment | ||||
* registers. The fault is handled in trap.c. | * registers. The fault is handled in trap.c. | ||||
*/ | */ | ||||
doreti_exit: | doreti_exit: | ||||
MEXITCOUNT | MEXITCOUNT | ||||
▲ Show 20 Lines • Show All 244 Lines • Show Last 20 Lines |
I think there is a bug here? Namely, if KCR3 is ~0, then we jmp to 1 which assumes that %rdi is valid, but we don't load %rdi (set to curpcb) until a few instructions below. I think this 1: label should be on the 'movq PCPU(curpcb),%rdi'? (I noticed this in the copy I had made in mchk).