| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // Copyright (c) 2015 Intel Corporation |
| // Copyright (c) 2016 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <asm.h> |
| #include <arch/x86/descriptor.h> |
| |
| #define NUM_INT 256 |
| |
| /* |
| * Please note that the macro for generating interrupt routine stubs relies |
| * on the macro execution counter \@ which is shared by all invocations across |
| * this compilation unit. Be careful when adding additional macros to this |
| * file. In particular: |
| * 1) No macros can be executed before def_isr (so \@ starts at zero). |
| * 2) def_isr cannot have any macros (so \@ increments by one for each |
| * def_isr invocation). |
| */ |
| |
| .text |
| |
| /* interrupt service routine stubs */ |
| _isr: |
| .macro def_isr |
| .pushsection .text |
| FUNCTION_LABEL(_isr_\@) |
| .cfi_startproc simple |
| .cfi_signal_frame |
| /* Set CFA for an interrupt frame. */ |
| .if \@ == 8 || (\@ >= 10 && \@ <= 14) || \@ == 17 |
| .cfi_def_cfa %rsp, (8 * 6) |
| .else |
| .cfi_def_cfa %rsp, (8 * 5) |
| .endif |
| .cfi_offset %rip, -(5 * 8) |
| /* Mark each reg as having the same value as from the "calling" frame. |
| This is the default state for callee-saved registers, but for completeness |
| sake we do this for all of them. */ |
| ALL_CFI_SAME_VALUE |
| /* Clear the AC flag to prevent ring 0 from performing data accesses to |
| * ring 3 if SMAP is available. If it was set, it will get restored by |
| * iretd. DO NOT REMOVE THIS CLAC, code in idt.c assumes it is here. |
| * It MUST be the first instruction of this function. */ |
| clac |
| /* We can't use push_value here: it is a macro invocation and using it |
| * will screw up tracking of \@ == isr number. Instead we inline the .cfi |
| * directives. */ |
| .if \@ == 8 || (\@ >= 10 && \@ <= 14) || \@ == 17 |
| /* error code pushed by exception */ |
| pushq $\@ /* interrupt number */ |
| .cfi_adjust_cfa_offset 8 |
| jmp interrupt_common |
| .else |
| pushq $0 /* fill in error code in iframe */ |
| .cfi_adjust_cfa_offset 8 |
| pushq $\@ /* interrupt number */ |
| .cfi_adjust_cfa_offset 8 |
| jmp interrupt_common |
| .endif |
| END_FUNCTION(_isr_\@) |
| .popsection |
| .pushsection .rodata.isr |
| .quad _isr_\@ |
| .popsection |
| .endm |
| |
| .pushsection .rodata.isr |
| /* build a table of isr entry points */ |
| .balign 8 |
| DATA(_isr_table) |
| .popsection |
| .rept NUM_INT |
| def_isr |
| .endr |
| |
| FUNCTION_LABEL(interrupt_common) |
| .cfi_startproc simple |
| .cfi_signal_frame |
| /* Set CFA for an interrupt frame. */ |
| .cfi_def_cfa %rsp, 7 * 8 /* hw + _isr_* push this many values */ |
| .cfi_offset %rip, -(5 * 8) |
| /* Mark each reg as having the same value as from the "calling" frame. |
| This is the default state for callee-saved registers, but for completeness |
| sake we do this for all of them. */ |
| ALL_CFI_SAME_VALUE |
| |
| /* Clear the direction flag. Without this, uses of string |
| instructions, e.g. REP MOVS in memcpy() or inlined by the compiler, |
| can go wrong and copy in the wrong direction, since this code may |
| assume that the direction flag is unset. */ |
| cld |
| |
| /* Check to see if we came from user space by testing the PL of the |
| * CS register that was saved on the stack automatically. Check for != 0. |
| */ |
| testb $3, 0x18(%rsp) |
| jz 1f |
| |
| /* swap gs to kernel space */ |
| swapgs |
| |
| 1: |
| /* save general purpose registers */ |
| push_reg %r15 |
| push_reg %r14 |
| push_reg %r13 |
| push_reg %r12 |
| push_reg %r11 |
| push_reg %r10 |
| push_reg %r9 |
| push_reg %r8 |
| push_reg %rax |
| push_reg %rcx |
| push_reg %rdx |
| push_reg %rbx |
| push_reg %rbp |
| push_reg %rsi |
| push_reg %rdi |
| |
| movq %rsp, %rdi /* pass the iframe using rdi */ |
| |
| call x86_exception_handler |
| |
| /* A label to assist gdb's backtracing through kernel exceptions. |
| When gdb sees this as the return address it knows it can fetch |
| x86_iframe_t from $rsp. See scripts/zircon.elf-gdb.py. */ |
| interrupt_common_iframe_set_up_for_debugger: |
| |
| /* restore general purpose registers */ |
| pop_reg %rdi |
| pop_reg %rsi |
| pop_reg %rbp |
| pop_reg %rbx |
| pop_reg %rdx |
| pop_reg %rcx |
| pop_reg %rax |
| pop_reg %r8 |
| pop_reg %r9 |
| pop_reg %r10 |
| pop_reg %r11 |
| pop_reg %r12 |
| pop_reg %r13 |
| pop_reg %r14 |
| pop_reg %r15 |
| |
| /* check if we're returning to user space as per before */ |
| testb $3, 0x18(%rsp) |
| jz 1f |
| |
| /* swap gs back to user space */ |
| swapgs |
| |
| 1: |
| /* drop vector number and error code*/ |
| add_to_sp 16 |
| |
| iretq |
| END_FUNCTION(interrupt_common) |
| |
| /* Call external interrupt handler manually without actually issuing interrupt. |
| * |
| * For external interrupts CPU doesn't store error code on stack so we use 0. We |
| * additionally use CODE_64_SELECTOR as CS, 0 as SS, RFLAGS value and current |
| * stack. |
| */ |
| FUNCTION(x86_call_external_interrupt_handler) |
| /* save current RFLAGS value */ |
| pushfq |
| popq %r10 |
| |
| /* save current RSP value */ |
| movq %rsp, %r11 |
| |
| /* calculate exit address */ |
| leaq .Lexit(%rip), %rax |
| |
| /* prepare interrupt stack frame in the from interrupt_common expects to see */ |
| sub_from_sp 0x38 |
| movq %rdi, 0x00(%rsp) // rdi holds vector number |
| movq $0, 0x08(%rsp) // error code |
| movq %rax, 0x10(%rsp) // RIP (return address) |
| movq $CODE_64_SELECTOR, 0x18(%rsp) // CS |
| movq %r10, 0x20(%rsp) // RFLAGS |
| movq %r11, 0x28(%rsp) // RSP |
| movq $0, 0x30(%rsp) // SS |
| |
| /* we can actually avoid this jump if we put this code above |
| * interrupt_common and just fall through, but benefits of doing this are |
| * not obvious so for now for the sake of clarity keep this jump |
| */ |
| jmp interrupt_common |
| |
| .Lexit: |
| ret |
| END_FUNCTION(x86_call_external_interrupt_handler) |