| /* See COPYRIGHT for copyright information. |
| * The two TRAP* macros (minus the .data parts) are from the JOS project. |
| * Everything else: |
| * Copyright (c) 2009, 2013 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| */ |
| #include <arch/mmu.h> |
| #include <arch/trap.h> |
| #include <arch/x86.h> |
| #include <ros/memlayout.h> |
| |
| ################################################################### |
| # exceptions/interrupts |
| ################################################################### |
| |
| /* The TRAPHANDLER macro defines a globally-visible function for handling |
| * a trap. It pushes a trap number onto the stack, then jumps to _alltraps. |
| * It also builds this traps portion of the trap_tbl. |
| * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. |
| */ |
| #define TRAPHANDLER(name, num) \ |
| .text; \ |
| .globl name; /* define global symbol for 'name' */ \ |
| .type name, @function; /* symbol type is function */ \ |
| .align 2; /* align function definition */ \ |
| name: /* function starts here */ \ |
| pushq $(num); \ |
| jmp _alltraps; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| /* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. |
| * It pushes a 0 in place of the error code, so the trap frame has the same |
| * format in either case. */ |
| #define TRAPHANDLER_NOEC(name, num) \ |
| .text; \ |
| .globl name; \ |
| .type name, @function; \ |
| .align 2; \ |
| name: \ |
| pushq $0; \ |
| pushq $(num); \ |
| jmp _alltraps; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| /* Same as NOEC, but for IRQs instead. num is the ISR number it is mapped to */ |
| #define IRQ_HANDLER(name, num) \ |
| .text; \ |
| .globl name; \ |
| .type name, @function; \ |
| .align 2; \ |
| name: \ |
| pushq $0; \ |
| pushq $(num); \ |
| jmp _allirqs; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| #define NMI_HANDLER(name, num) \ |
| .text; \ |
| .globl name; \ |
| .type name, @function; \ |
| .align 2; \ |
| name: \ |
| pushq $0; \ |
| pushq $(num); \ |
| jmp _nmi_entry; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| #define DOUBLEFAULT_HANDLER(name, num) \ |
| .text; \ |
| .globl name; \ |
| .type name, @function; \ |
| .align 2; \ |
| name: \ |
| pushq $(num); \ |
| jmp _dblf_entry; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| /* Bare minimum IRQ handler: send a LAPIC_EOI and immediately iret. */ |
| #define POKE_HANDLER(name, num) \ |
| .text; \ |
| .globl name; \ |
| .type name, @function; \ |
| .align 2; \ |
| name:; \ |
| pushq %rax; \ |
| pushq %rcx; \ |
| pushq %rdx; \ |
| movq $0, %rax; \ |
| movq $0, %rdx; \ |
| movq $(MSR_LAPIC_EOI), %rcx; \ |
| wrmsr; \ |
| popq %rdx; \ |
| popq %rcx; \ |
| popq %rax; \ |
| iretq; \ |
| .data; \ |
| .quad name; \ |
| .long num |
| |
| # Set data for the trap_tbl symbol. Set text for the entry_points. The various |
| # trap/irq handler macros will toggle data on and off as needed. |
| .data |
| .globl trap_tbl |
| trap_tbl: |
| .text |
| .globl __asm_entry_points_start |
| __asm_entry_points_start: |
| |
| /* Generate entry points for the different traps. Note that all of these bounce |
| * off the corresponding trap.c function, such as handle_irqs, and that the name |
| * e.g. ISR_divide_error is soley for the little stup that jumps to something |
| * like _alltraps. |
| * |
| * Technically, these HANDLER entries do not need to be in numeric order. |
| * trap.c will do a 'foreach (up to last-1), set the IDT for the number to point |
| * to the func' in the order in which they appear in the trap tbl, so the 'last |
| * one wins'. */ |
| TRAPHANDLER_NOEC(ISR_divide_error, T_DIVIDE) |
| TRAPHANDLER_NOEC(ISR_debug_exceptions, T_DEBUG) |
| NMI_HANDLER(ISR_NMI, T_NMI) |
| TRAPHANDLER_NOEC(ISR_breakpoint, T_BRKPT) |
| TRAPHANDLER_NOEC(ISR_overflow, T_OFLOW) |
| TRAPHANDLER_NOEC(ISR_bounds_check, T_BOUND) |
| TRAPHANDLER_NOEC(ISR_invalid_opcode, T_ILLOP) |
| TRAPHANDLER_NOEC(ISR_device_not_available, T_DEVICE) |
| DOUBLEFAULT_HANDLER(ISR_double_fault, T_DBLFLT) |
| /* 9 reserved */ |
| TRAPHANDLER(ISR_invalid_TSS, T_TSS) |
| TRAPHANDLER(ISR_segment_not_present, T_SEGNP) |
| TRAPHANDLER(ISR_stack_exception, T_STACK) |
| TRAPHANDLER(ISR_general_protection_fault, T_GPFLT) |
| TRAPHANDLER(ISR_page_fault, T_PGFLT) |
| /* 15 reserved */ |
| TRAPHANDLER_NOEC(ISR_floating_point_error, T_FPERR) |
| TRAPHANDLER(ISR_alignment_check, T_ALIGN) |
| TRAPHANDLER_NOEC(ISR_machine_check, T_MCHK) |
| TRAPHANDLER_NOEC(ISR_simd_error, T_SIMDERR) |
| /* 20 - 31 reserved */ |
| /* 32-47 are PIC/8259 IRQ vectors */ |
| IRQ_HANDLER(IRQ0, 32) |
| IRQ_HANDLER(IRQ1, 33) |
| IRQ_HANDLER(IRQ2, 34) |
| IRQ_HANDLER(IRQ3, 35) |
| IRQ_HANDLER(IRQ4, 36) |
| IRQ_HANDLER(IRQ5, 37) |
| IRQ_HANDLER(IRQ6, 38) |
| IRQ_HANDLER(IRQ7, 39) |
| IRQ_HANDLER(IRQ8, 40) |
| IRQ_HANDLER(IRQ9, 41) |
| IRQ_HANDLER(IRQ10, 42) |
| IRQ_HANDLER(IRQ11, 43) |
| IRQ_HANDLER(IRQ12, 44) |
| IRQ_HANDLER(IRQ13, 45) |
| IRQ_HANDLER(IRQ14, 46) |
| IRQ_HANDLER(IRQ15, 47) |
| TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL) |
| /* 49-223 are IOAPIC routing vectors (from IOAPIC to LAPIC) */ |
| IRQ_HANDLER(IRQ17, 49) |
| IRQ_HANDLER(IRQ18, 50) |
| IRQ_HANDLER(IRQ19, 51) |
| IRQ_HANDLER(IRQ20, 52) |
| IRQ_HANDLER(IRQ21, 53) |
| IRQ_HANDLER(IRQ22, 54) |
| IRQ_HANDLER(IRQ23, 55) |
| IRQ_HANDLER(IRQ24, 56) |
| IRQ_HANDLER(IRQ25, 57) |
| IRQ_HANDLER(IRQ26, 58) |
| IRQ_HANDLER(IRQ27, 59) |
| IRQ_HANDLER(IRQ28, 60) |
| IRQ_HANDLER(IRQ29, 61) |
| IRQ_HANDLER(IRQ30, 62) |
| IRQ_HANDLER(IRQ31, 63) |
| IRQ_HANDLER(IRQ32, 64) |
| IRQ_HANDLER(IRQ33, 65) |
| IRQ_HANDLER(IRQ34, 66) |
| IRQ_HANDLER(IRQ35, 67) |
| IRQ_HANDLER(IRQ36, 68) |
| IRQ_HANDLER(IRQ37, 69) |
| IRQ_HANDLER(IRQ38, 70) |
| IRQ_HANDLER(IRQ39, 71) |
| IRQ_HANDLER(IRQ40, 72) |
| IRQ_HANDLER(IRQ41, 73) |
| IRQ_HANDLER(IRQ42, 74) |
| IRQ_HANDLER(IRQ43, 75) |
| IRQ_HANDLER(IRQ44, 76) |
| IRQ_HANDLER(IRQ45, 77) |
| IRQ_HANDLER(IRQ46, 78) |
| IRQ_HANDLER(IRQ47, 79) |
| IRQ_HANDLER(IRQ48, 80) |
| IRQ_HANDLER(IRQ49, 81) |
| IRQ_HANDLER(IRQ50, 82) |
| IRQ_HANDLER(IRQ51, 83) |
| IRQ_HANDLER(IRQ52, 84) |
| IRQ_HANDLER(IRQ53, 85) |
| IRQ_HANDLER(IRQ54, 86) |
| IRQ_HANDLER(IRQ55, 87) |
| IRQ_HANDLER(IRQ56, 88) |
| IRQ_HANDLER(IRQ57, 89) |
| IRQ_HANDLER(IRQ58, 90) |
| IRQ_HANDLER(IRQ59, 91) |
| IRQ_HANDLER(IRQ60, 92) |
| IRQ_HANDLER(IRQ61, 93) |
| IRQ_HANDLER(IRQ62, 94) |
| IRQ_HANDLER(IRQ63, 95) |
| IRQ_HANDLER(IRQ64, 96) |
| IRQ_HANDLER(IRQ65, 97) |
| IRQ_HANDLER(IRQ66, 98) |
| IRQ_HANDLER(IRQ67, 99) |
| IRQ_HANDLER(IRQ68, 100) |
| IRQ_HANDLER(IRQ69, 101) |
| IRQ_HANDLER(IRQ70, 102) |
| IRQ_HANDLER(IRQ71, 103) |
| IRQ_HANDLER(IRQ72, 104) |
| IRQ_HANDLER(IRQ73, 105) |
| IRQ_HANDLER(IRQ74, 106) |
| IRQ_HANDLER(IRQ75, 107) |
| IRQ_HANDLER(IRQ76, 108) |
| IRQ_HANDLER(IRQ77, 109) |
| IRQ_HANDLER(IRQ78, 110) |
| IRQ_HANDLER(IRQ79, 111) |
| IRQ_HANDLER(IRQ80, 112) |
| IRQ_HANDLER(IRQ81, 113) |
| IRQ_HANDLER(IRQ82, 114) |
| IRQ_HANDLER(IRQ83, 115) |
| IRQ_HANDLER(IRQ84, 116) |
| IRQ_HANDLER(IRQ85, 117) |
| IRQ_HANDLER(IRQ86, 118) |
| IRQ_HANDLER(IRQ87, 119) |
| IRQ_HANDLER(IRQ88, 120) |
| IRQ_HANDLER(IRQ89, 121) |
| IRQ_HANDLER(IRQ90, 122) |
| IRQ_HANDLER(IRQ91, 123) |
| IRQ_HANDLER(IRQ92, 124) |
| IRQ_HANDLER(IRQ93, 125) |
| IRQ_HANDLER(IRQ94, 126) |
| IRQ_HANDLER(IRQ95, 127) |
| IRQ_HANDLER(IRQ96, 128) |
| IRQ_HANDLER(IRQ97, 129) |
| IRQ_HANDLER(IRQ98, 130) |
| IRQ_HANDLER(IRQ99, 131) |
| IRQ_HANDLER(IRQ100, 132) |
| IRQ_HANDLER(IRQ101, 133) |
| IRQ_HANDLER(IRQ102, 134) |
| IRQ_HANDLER(IRQ103, 135) |
| IRQ_HANDLER(IRQ104, 136) |
| IRQ_HANDLER(IRQ105, 137) |
| IRQ_HANDLER(IRQ106, 138) |
| IRQ_HANDLER(IRQ107, 139) |
| IRQ_HANDLER(IRQ108, 140) |
| IRQ_HANDLER(IRQ109, 141) |
| IRQ_HANDLER(IRQ110, 142) |
| IRQ_HANDLER(IRQ111, 143) |
| IRQ_HANDLER(IRQ112, 144) |
| IRQ_HANDLER(IRQ113, 145) |
| IRQ_HANDLER(IRQ114, 146) |
| IRQ_HANDLER(IRQ115, 147) |
| IRQ_HANDLER(IRQ116, 148) |
| IRQ_HANDLER(IRQ117, 149) |
| IRQ_HANDLER(IRQ118, 150) |
| IRQ_HANDLER(IRQ119, 151) |
| IRQ_HANDLER(IRQ120, 152) |
| IRQ_HANDLER(IRQ121, 153) |
| IRQ_HANDLER(IRQ122, 154) |
| IRQ_HANDLER(IRQ123, 155) |
| IRQ_HANDLER(IRQ124, 156) |
| IRQ_HANDLER(IRQ125, 157) |
| IRQ_HANDLER(IRQ126, 158) |
| IRQ_HANDLER(IRQ127, 159) |
| IRQ_HANDLER(IRQ128, 160) |
| IRQ_HANDLER(IRQ129, 161) |
| IRQ_HANDLER(IRQ130, 162) |
| IRQ_HANDLER(IRQ131, 163) |
| IRQ_HANDLER(IRQ132, 164) |
| IRQ_HANDLER(IRQ133, 165) |
| IRQ_HANDLER(IRQ134, 166) |
| IRQ_HANDLER(IRQ135, 167) |
| IRQ_HANDLER(IRQ136, 168) |
| IRQ_HANDLER(IRQ137, 169) |
| IRQ_HANDLER(IRQ138, 170) |
| IRQ_HANDLER(IRQ139, 171) |
| IRQ_HANDLER(IRQ140, 172) |
| IRQ_HANDLER(IRQ141, 173) |
| IRQ_HANDLER(IRQ142, 174) |
| IRQ_HANDLER(IRQ143, 175) |
| IRQ_HANDLER(IRQ144, 176) |
| IRQ_HANDLER(IRQ145, 177) |
| IRQ_HANDLER(IRQ146, 178) |
| IRQ_HANDLER(IRQ147, 179) |
| IRQ_HANDLER(IRQ148, 180) |
| IRQ_HANDLER(IRQ149, 181) |
| IRQ_HANDLER(IRQ150, 182) |
| IRQ_HANDLER(IRQ151, 183) |
| IRQ_HANDLER(IRQ152, 184) |
| IRQ_HANDLER(IRQ153, 185) |
| IRQ_HANDLER(IRQ154, 186) |
| IRQ_HANDLER(IRQ155, 187) |
| IRQ_HANDLER(IRQ156, 188) |
| IRQ_HANDLER(IRQ157, 189) |
| IRQ_HANDLER(IRQ158, 190) |
| IRQ_HANDLER(IRQ159, 191) |
| IRQ_HANDLER(IRQ160, 192) |
| IRQ_HANDLER(IRQ161, 193) |
| IRQ_HANDLER(IRQ162, 194) |
| IRQ_HANDLER(IRQ163, 195) |
| IRQ_HANDLER(IRQ164, 196) |
| IRQ_HANDLER(IRQ165, 197) |
| IRQ_HANDLER(IRQ166, 198) |
| IRQ_HANDLER(IRQ167, 199) |
| IRQ_HANDLER(IRQ168, 200) |
| IRQ_HANDLER(IRQ169, 201) |
| IRQ_HANDLER(IRQ170, 202) |
| IRQ_HANDLER(IRQ171, 203) |
| IRQ_HANDLER(IRQ172, 204) |
| IRQ_HANDLER(IRQ173, 205) |
| IRQ_HANDLER(IRQ174, 206) |
| IRQ_HANDLER(IRQ175, 207) |
| IRQ_HANDLER(IRQ176, 208) |
| IRQ_HANDLER(IRQ177, 209) |
| IRQ_HANDLER(IRQ178, 210) |
| IRQ_HANDLER(IRQ179, 211) |
| IRQ_HANDLER(IRQ180, 212) |
| IRQ_HANDLER(IRQ181, 213) |
| IRQ_HANDLER(IRQ182, 214) |
| IRQ_HANDLER(IRQ183, 215) |
| IRQ_HANDLER(IRQ184, 216) |
| IRQ_HANDLER(IRQ185, 217) |
| IRQ_HANDLER(IRQ186, 218) |
| IRQ_HANDLER(IRQ187, 219) |
| IRQ_HANDLER(IRQ188, 220) |
| IRQ_HANDLER(IRQ189, 221) |
| IRQ_HANDLER(IRQ190, 222) |
| IRQ_HANDLER(IRQ191, 223) |
| /* 224-239 are OS IPI vectors (0xe0-0xef) */ |
| IRQ_HANDLER(IRQ192, I_SMP_CALL0) |
| IRQ_HANDLER(IRQ193, I_SMP_CALL1) |
| IRQ_HANDLER(IRQ194, I_SMP_CALL2) |
| IRQ_HANDLER(IRQ195, I_SMP_CALL3) |
| IRQ_HANDLER(IRQ196, I_SMP_CALL4) |
| IRQ_HANDLER(IRQ197, 229) |
| IRQ_HANDLER(IRQ198, 230) |
| IRQ_HANDLER(IRQ199, 231) |
| IRQ_HANDLER(IRQ200, 232) |
| IRQ_HANDLER(IRQ201, 233) |
| IRQ_HANDLER(IRQ202, 234) |
| IRQ_HANDLER(IRQ203, 235) |
| IRQ_HANDLER(IRQ204, I_TESTING) |
| POKE_HANDLER(IRQ205, I_POKE_GUEST) |
| POKE_HANDLER(IRQ206, I_POKE_CORE) |
| IRQ_HANDLER(IRQ207, I_KERNEL_MSG) |
| /* 240-255 are LAPIC vectors (0xf0-0xff), hightest priority class */ |
| IRQ_HANDLER(IRQ208, 240) |
| IRQ_HANDLER(IRQ209, 241) |
| IRQ_HANDLER(IRQ210, 242) |
| IRQ_HANDLER(IRQ211, 243) |
| IRQ_HANDLER(IRQ212, 244) |
| IRQ_HANDLER(IRQ213, 245) |
| IRQ_HANDLER(IRQ214, 246) |
| IRQ_HANDLER(IRQ215, 247) |
| IRQ_HANDLER(IRQ216, 248) |
| IRQ_HANDLER(IRQ217, 249) |
| IRQ_HANDLER(IRQ218, 250) |
| IRQ_HANDLER(IRQ219, 251) |
| IRQ_HANDLER(IRQ220, 252) |
| IRQ_HANDLER(IRQ221, 253) |
| IRQ_HANDLER(IRQ222, 254) |
| IRQ_HANDLER(IRQ223, 255) |
| /* But make sure default is last!! */ |
| TRAPHANDLER_NOEC(ISR_default, T_DEFAULT) |
| |
| .data |
| .globl trap_tbl_end |
| trap_tbl_end: |
| |
| .text |
| _alltraps: |
| cld |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %r11 |
| pushq %r10 |
| pushq %r9 |
| pushq %r8 |
| pushq %rdi |
| pushq %rsi |
| pushq %rbp |
| pushq %rdx |
| pushq %rcx |
| pushq %rbx |
| pushq %rax |
| cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax |
| je trap_all_tf |
| # this is a user TF. we need to swapgs to get the kernel's gs and mark |
| # the context as partial |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| movl $0x1, 0xac(%rsp) # 0xac - diff btw tf_padding0 and tf_rax |
| trap_all_tf: |
| pushq $0 # fsbase space |
| pushq $0 # gsbase space |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdi |
| call trap |
| # the return paths are only used by the kernel |
| addq $0x10, %rsp # skip fs/gs base |
| popq %rax |
| popq %rbx |
| popq %rcx |
| popq %rdx |
| popq %rbp |
| popq %rsi |
| popq %rdi |
| popq %r8 |
| popq %r9 |
| popq %r10 |
| popq %r11 |
| popq %r12 |
| popq %r13 |
| popq %r14 |
| popq %r15 |
| addq $0x10, %rsp # skip trapno and err |
| iretq |
| |
| # might merge this with _alltraps |
| _allirqs: |
| cld |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %r11 |
| pushq %r10 |
| pushq %r9 |
| pushq %r8 |
| pushq %rdi |
| pushq %rsi |
| pushq %rbp |
| pushq %rdx |
| pushq %rcx |
| pushq %rbx |
| pushq %rax |
| cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax |
| je irq_all_tf |
| # this is a user TF. we need to swapgs to get the kernel's gs and mark |
| # the context as partial |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| movl $0x1, 0xac(%rsp) # 0xac - diff btw tf_padding0 and tf_rax |
| irq_all_tf: |
| pushq $0 # fsbase space |
| pushq $0 # gsbase space |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdi |
| call handle_irq |
| # the return paths are only used by the kernel |
| addq $0x10, %rsp # skip fs/gs base |
| popq %rax |
| popq %rbx |
| popq %rcx |
| popq %rdx |
| popq %rbp |
| popq %rsi |
| popq %rdi |
| popq %r8 |
| popq %r9 |
| popq %r10 |
| popq %r11 |
| popq %r12 |
| popq %r13 |
| popq %r14 |
| popq %r15 |
| addq $0x10, %rsp # skip trapno and err |
| iretq |
| |
| # Similar to the NMI handler, we come in on a special stack, but we can trust |
| # the contents of GS for kernel contexts. This is mostly true. If we double |
| # faulted in this file, for instance, then GS could be garbage. But that should |
| # never happen. Most double faults will be for running into a guard page on a |
| # stack. |
| # |
| # Since the double fault is so bad, we just want to get into the kernel's C code |
| # where we can run some diagnostics. We (currently) won't try to recover, since |
| # it's probably a kernel bug. |
| _dblf_entry: |
| cld |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %r11 |
| pushq %r10 |
| pushq %r9 |
| pushq %r8 |
| pushq %rdi |
| pushq %rsi |
| pushq %rbp |
| pushq %rdx |
| pushq %rcx |
| pushq %rbx |
| pushq %rax |
| cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax |
| je dblf_all_tf |
| # this is a user TF. we need to swapgs to get the kernel's gs and mark |
| # the context as partial |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| movl $0x1, 0xac(%rsp) # 0xac - diff btw tf_padding0 and tf_rax |
| dblf_all_tf: |
| pushq $0 # fsbase space |
| pushq $0 # gsbase space |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdi |
| call handle_double_fault |
| dblf_spin: |
| jmp dblf_spin |
| |
| # Unlike normal trap and IRQ handlers, both user and kernel TFs are handled the |
| # similarly. Both come in here and return from here. We cannot do anything |
| # fancy like proc_restartcore() from NMI context. |
| # |
| # All NMIs will come in fresh on the same stack (IST1, per-core). We don't need |
| # to find a stackpointer, but we do need to find GS. |
| # |
| # Regardless of whether or not the interrupted context was in the kernel, we |
| # can't trust GS. Basically we can never tell from looking at GS and KERN_GS |
| # whether swapgs occurred, since the user could have put kernel addrs in their |
| # GS. But we can use the stack for storage to bootstrap GS. %rsp at entry |
| # points to pcpui *. |
| # |
| # We can also tell if GS was set correctly or not and avoid the wrmsr calls. |
| # This isn't a huge deal. But note that we don't know whether or not the kernel |
| # actually set the gsbase. We just know if it was correct or not. The user |
| # could have set it and the kernel hadn't had a chance to swapgs yet. The NMI |
| # handler doesn't care, since all TFs come in and go out via this asm. |
| # |
| # If we want to be paranoid, we can completely ignore user TFs and just save and |
| # restore GS with the same mechanism we use for the kernel. But since we came |
| # in on a user TF, we can use swapgs. Note that even for nested NMI handlers, |
| # we have a user TF for only the first time, and only the last exit will swapgs |
| # back to the way it was initially. |
| _nmi_entry: |
| cld |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %r11 |
| pushq %r10 |
| pushq %r9 |
| pushq %r8 |
| pushq %rdi |
| pushq %rsi |
| pushq %rbp |
| pushq %rdx |
| pushq %rcx |
| pushq %rbx |
| pushq %rax |
| cmpw $GD_KT, 0x90(%rsp) # 0x90 - diff btw tf_cs and tf_rax |
| je nmi_kern_tf |
| # this is a user TF. we need to swapgs to get the kernel's gs and mark |
| # the context as partial |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| movl $0x1, 0xac(%rsp) # 0xac - diff btw tf_padding0 and tf_rax |
| pushq $0 # fsbase space |
| pushq $0 # gsbase space |
| jmp nmi_all_tf |
| nmi_kern_tf: |
| # this is a kernel TF. but we don't know if they set up gs yet, so |
| # we'll save and restore whatever they had loaded and use our own |
| pushq $0 # fsbase space |
| # Get the current GS base into rax |
| movl $MSR_GS_BASE, %ecx |
| rdmsr |
| shlq $32, %rdx |
| orq %rdx, %rax |
| # Get the real GS base from the top of the stack. This was set in |
| # smp_boot, and our rsp pointed to it when we entered the kernel. |
| movq 0xb8(%rsp), %rdx # 0xb8 from fs_base to the top |
| # Compare them. If they are the same, we can just push 0 for gsbase |
| # (which later will mean "no need to restore GS". |
| cmpq %rdx, %rax |
| je nmi_gs_ok |
| # They weren't the same. Save the old one and set the new one. |
| pushq %rax # gsbase space |
| movq %rdx, %rax |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| wrmsr |
| jmp nmi_all_tf |
| nmi_gs_ok: |
| pushq $0 # gsbase space |
| nmi_all_tf: |
| # At this point, GS is set correctly, either due to swapgs (user TF), |
| # wrmsr (kern TF with bad GS), or it was already fine (and gsbase in the |
| # TF = 0). |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdi |
| call handle_nmi |
| # Unlike in normal IRQs/Traps, both user and kernel contexts return via |
| # this path. |
| cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase |
| je nmi_kern_restore_gs |
| # User TF. Restore whatever was there with swapgs. We don't care what |
| # it was, nor do we care what was in the TF. |
| swapgs # user's GS is now in MSR_GS_BASE |
| addq $0x10, %rsp # skip gs/fs base |
| jmp nmi_popal |
| nmi_kern_restore_gs: |
| popq %rax # fetch saved gsbase |
| addq $0x08, %rsp # skip fs base |
| cmpq $0, %rax |
| je nmi_popal |
| # gsbase in the TF != 0, which means we need to restore that gsbase |
| movl $MSR_GS_BASE, %ecx |
| movq %rax, %rdx |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| wrmsr |
| nmi_popal: |
| popq %rax |
| popq %rbx |
| popq %rcx |
| popq %rdx |
| popq %rbp |
| popq %rsi |
| popq %rdi |
| popq %r8 |
| popq %r9 |
| popq %r10 |
| popq %r11 |
| popq %r12 |
| popq %r13 |
| popq %r14 |
| popq %r15 |
| addq $0x10, %rsp # skip trapno and err |
| iretq |
| |
| .globl __nmi_pop_ok_start; |
| .globl __nmi_pop_ok_end; |
| .globl __nmi_pop_fail_start; |
| .globl __nmi_pop_fail_end; |
| |
| # extern void nmi_try_to_pop(struct hw_trapframe *tf, int *status, |
| # int old_val, int new_val); |
| # |
| # __nmi_bottom_half calls this to atomically pop a hw_tf (%rdi) and set |
| # &pcpui->nmi_status (%rsi) with compare and swap to NMI_NORMAL_OPN (%ecx) given |
| # that it was NMI_IN_PROGRESS (%edx) |
| # |
| # (Careful, nmi_status is an int, not a long.) |
| # |
| # If the real NMI handler interrupts us, it'll move us to the fail section of |
| # the code. That code is identical to 'ok', up until ok's final statement. |
| # |
| # In that event, we'll need a little help returning: specifically to bootstrap |
| # pcpui and our current stackpointer. pcpui is already saved near the top of |
| # stack. We'll save rsp ourselves. |
| .globl nmi_try_to_pop; |
| .type nmi_try_to_pop, @function; |
| nmi_try_to_pop: |
| __nmi_pop_ok_start: |
| # careful only to use caller-saved or argument registers before saving |
| movl %edx, %eax # load old_val into eax for the CAS |
| cmpxchgl %ecx, (%rsi) # no need for LOCK, since an NMI would serialize |
| jz nmi_ok_cas_worked # ZF = 1 on successful CAS |
| ret |
| nmi_ok_cas_worked: |
| # save callee-saved regs (the pops below clobber them, and we might |
| # return) |
| pushq %rbp |
| pushq %rbx |
| pushq %r12 |
| pushq %r13 |
| pushq %r14 |
| pushq %r15 |
| # We need to save the current rsp into the scratch space at the top of |
| # the stack. This assumes we're within the top page of our stack, which |
| # should always be true. Careful not to use rdi, which still has an |
| # argument. |
| movq %rsp, %rbx |
| # Want to round rbx up to PGSIZE, then subtract 8, to get our slot. |
| movq $0xfff, %rax |
| notq %rax # rax = 0xfffffffffffff000 |
| andq %rax, %rbx # round down rbx |
| addq $0x1000, %rbx # add PGSIZE, assuming rsp was not page aligned |
| subq $0x8, %rbx # point to the scratch space |
| movq %rsp, (%rbx) # save rsp in the scratch space |
| # We jump our rsp to the base of the HW_TF. This is still on the same |
| # stack, just farther back than where our caller is. We need to be |
| # careful to not clobber the stack. Otherwise we'll have chaos. |
| movq %rdi, %rsp |
| # From here down is the same as the normal NMI exit path, but with 'ok' |
| # in the symbol names. |
| cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase |
| je nmi_ok_kern_restore_gs |
| # User TF. Restore whatever was there with swapgs. We don't care what |
| # it was, nor do we care what was in the TF. |
| swapgs # user's GS is now in MSR_GS_BASE |
| addq $0x10, %rsp # skip gs/fs base |
| jmp nmi_ok_popal |
| nmi_ok_kern_restore_gs: |
| popq %rax # fetch saved gsbase |
| addq $0x08, %rsp # skip fs base |
| cmpq $0, %rax |
| je nmi_ok_popal |
| # gsbase in the TF != 0, which means we need to restore that gsbase |
| movl $MSR_GS_BASE, %ecx |
| movq %rax, %rdx |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| wrmsr |
| nmi_ok_popal: |
| popq %rax |
| popq %rbx |
| popq %rcx |
| popq %rdx |
| popq %rbp |
| popq %rsi |
| popq %rdi |
| popq %r8 |
| popq %r9 |
| popq %r10 |
| popq %r11 |
| popq %r12 |
| popq %r13 |
| popq %r14 |
| popq %r15 |
| addq $0x10, %rsp # skip trapno and err |
| iretq |
| __nmi_pop_ok_end: |
| |
| # This is the 'fail' case. It is identical to the 'ok' case, up until the |
| # iretq, other than 'ok' replaced with 'fail'. In place of iretq, we undo the |
| # entire operation. |
| __nmi_pop_fail_start: |
| # careful only to use caller-saved or argument registers before saving |
| movl %edx, %eax # load old_val into eax for the CAS |
| cmpxchgl %ecx, (%rsi) # no need for LOCK, since an NMI would serialize |
| jz nmi_fail_cas_worked # ZF = 1 on successful CAS |
| ret |
| nmi_fail_cas_worked: |
| # save callee-saved regs (the pops below clobber them, and we might |
| # return) |
| pushq %rbp |
| pushq %rbx |
| pushq %r12 |
| pushq %r13 |
| pushq %r14 |
| pushq %r15 |
| # We need to save the current rsp into the scratch space at the top of |
| # the stack. This assumes we're within the top page of our stack, which |
| # should always be true. Careful not to use rdi, which still has an |
| # argument. |
| movq %rsp, %rbx |
| # Want to round rbx up to PGSIZE, then subtract 8, to get our slot. |
| movq $0xfff, %rax |
| notq %rax # rax = 0xfffffffffffff000 |
| andq %rax, %rbx # round down rbx |
| addq $0x1000, %rbx # add PGSIZE, assuming rsp was not page aligned |
| subq $0x8, %rbx # point to the scratch space |
| movq %rsp, (%rbx) # save rsp in the scratch space |
| # We jump our rsp to the base of the HW_TF. This is still on the same |
| # stack, just farther back than where our caller is. We need to be |
| # careful to not clobber the stack. Otherwise we'll have chaos. |
| movq %rdi, %rsp |
| # From here down is the same as the normal NMI exit path and the ok |
| # path, but with 'fail' in the symbol names. |
| cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase |
| je nmi_fail_kern_restore_gs |
| # User TF. Restore whatever was there with swapgs. We don't care what |
| # it was, nor do we care what was in the TF. |
| swapgs # user's GS is now in MSR_GS_BASE |
| addq $0x10, %rsp # skip gs/fs base |
| jmp nmi_fail_popal |
| nmi_fail_kern_restore_gs: |
| popq %rax # fetch saved gsbase |
| addq $0x08, %rsp # skip fs base |
| cmpq $0, %rax |
| je nmi_fail_popal |
| # gsbase in the TF != 0, which means we need to restore that gsbase |
| movl $MSR_GS_BASE, %ecx |
| movq %rax, %rdx |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| wrmsr |
| nmi_fail_popal: |
| popq %rax |
| popq %rbx |
| popq %rcx |
| popq %rdx |
| popq %rbp |
| popq %rsi |
| popq %rdi |
| popq %r8 |
| popq %r9 |
| popq %r10 |
| popq %r11 |
| popq %r12 |
| popq %r13 |
| popq %r14 |
| popq %r15 |
| addq $0x10, %rsp # skip trapno and err |
| # Here's is where we differ from OK. Time to undo everything and return |
| # rsp currently is pointing at tf->tf_rip. Remember that we don't want |
| # to write anything to the stack - everything in the TF is still the way |
| # it was when we started to pop. |
| # |
| # First off, let's get the stack addr of the pcpui pointer loaded |
| movq %rsp, %rbx |
| movq $0xfff, %rax |
| notq %rax # rax = 0xfffffffffffff000 |
| andq %rax, %rbx # round down rbx |
| addq $0x1000, %rbx # add PGSIZE, assuming rsp was not page aligned |
| subq $0x10, %rbx # point to the pcpui pointer |
| # Now let's start to unwind |
| subq $0x98, %rsp # jump from rip to tf_gsbase (top of hw_tf) |
| # Need to restore gs, just like on an NMI entry |
| cmpw $GD_KT, 0xa0(%rsp) # 0xa0 - diff btw tf_cs and tf_gsbase |
| je nmi_pop_fail_kern_tf |
| # This is a user TF. We need to swapgs to get the kernel's gs |
| # We don't need to mark the context as partial (we never do for NMIs, |
| # actually), and in general, we don't want to write anything on the |
| # stack. |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| jmp nmi_pop_fail_all_tf |
| nmi_pop_fail_kern_tf: |
| # Kernel TF. We basically need to do the same thing on entry, since we |
| # might have restored some weird GS base. We can tell based on |
| # tf_gsbase 0 for gsbase means we didn't need to change GS |
| cmpq $0, (%rsp) |
| je nmi_pop_fail_gs_fine |
| # rbx points to where pcpui* is stored |
| mov (%rbx), %rdx |
| movl $MSR_GS_BASE, %ecx |
| movq %rdx, %rax |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| wrmsr |
| nmi_pop_fail_gs_fine: |
| nmi_pop_fail_all_tf: |
| addq $0x8, %rbx # move to the scratch slot, holding rsp |
| mov (%rbx), %rsp |
| # restore callee-saved regs |
| popq %r15 |
| popq %r14 |
| popq %r13 |
| popq %r12 |
| popq %rbx |
| popq %rbp |
| ret |
| # sweet jeebus. |
| __nmi_pop_fail_end: |
| |
| |
| .globl sysenter_handler; |
| .type sysenter_handler, @function; |
| |
| sysenter_handler: |
| #ifndef CONFIG_NOFASTCALL_FSBASE |
| # Do a quick TLS / FS base change, never changing stacks. |
| # When rdi has the magic number, rsi has the new base |
| movabs $FASTCALL_SETFSBASE, %rax |
| cmp %rax, %rdi |
| jne normal_syscall # could profile this and jump differently |
| # need to check rsi, make sure it is canonical (will enfore below ULIM). |
| # need to either do this check, or handle the kernel GP fault on wrmsr. |
| movq %rsi, %rdi |
| shrq $47, %rdi |
| cmp $0, %rdi |
| jne fastcall_pop |
| # need to use cx, dx, and ax for the wrmsr. dx and ax are free. |
| movq %rcx, %rdi # save rcx, the retaddr |
| movq %rsi, %rdx |
| movq %rsi, %rax |
| shrq $32, %rdx |
| andl $0xffffffff, %eax |
| movl $MSR_FS_BASE, %ecx |
| wrmsr |
| movq %rdi, %rcx # restore retaddr |
| fastcall_pop: |
| rex.w sysret |
| normal_syscall: |
| #endif |
| # cld is handled by the SFMASK |
| swapgs # user's GS is now in MSR_KERNEL_GS_BASE |
| movq %gs:0, %rsp |
| # Saving the FPU callee-saved state for now. Might be able to have the |
| # preempt handler deal with it. |
| pushq $0 # space for mxcsr, fpucw, and padding0 |
| movw $0x1, 0x6(%rsp) # tf_padding0 = 1, partial context |
| fnstcw 0x4(%rsp) |
| stmxcsr (%rsp) |
| pushq %rdx # rsp, saved by userspace |
| pushq %rcx # rip, saved by hardware |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %rbp |
| pushq %rbx |
| pushq $0 # fsbase space |
| pushq $0 # gsbase space |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdx |
| # arg0, rdi: struct sysc*. arg1, rsi: count. arg2, rdx: sw_tf |
| call sysenter_callwrapper |
| # return via pop_tf, never this path |
| sysenter_spin: |
| jmp sysenter_spin |
| |
| .globl vmexit_handler; |
| .type vmexit_handler, @function; |
| vmexit_handler: |
| # rflags has all flags = 0, so cli and cld already. |
| # HOST_GS_BASE and RSP is set by the hardware |
| # Set default values. Most of these will be set in C later. |
| pushq $0 # guest_pa |
| pushq $0 # guest_va |
| pushq $0 # intrinfo2 and 1 |
| pushq $0 # exit_qual + exit_reason |
| pushq $0 # pad + trap_inject |
| pushq $0 # flags + guest_pcorid |
| pushq $0 # cr3 |
| pushq $0 # cr2 |
| pushq $0 # rsp |
| pushq $0 # rflags |
| pushq $0 # rip |
| # Save register state |
| pushq %r15 |
| pushq %r14 |
| pushq %r13 |
| pushq %r12 |
| pushq %r11 |
| pushq %r10 |
| pushq %r9 |
| pushq %r8 |
| pushq %rdi |
| pushq %rsi |
| pushq %rbp |
| pushq %rdx |
| pushq %rcx |
| pushq %rbx |
| pushq %rax |
| movq $0, %rbp # so we can backtrace to this point |
| movq %rsp, %rdi |
| call handle_vmexit |
| vmexit_spin: |
| jmp vmexit_spin |
| |
| .globl __asm_entry_points_end |
| __asm_entry_points_end: |