1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2009 Corey Tabaka
3// Copyright (c) 2015 Intel Corporation
4// Copyright (c) 2016 Travis Geiselbrecht
5//
6// Use of this source code is governed by a MIT-style
7// license that can be found in the LICENSE file or at
8// https://opensource.org/licenses/MIT
9
10#include <asm.h>
11#include <arch/x86/descriptor.h>
12
13#define NUM_INT 256
14
15/*
16 * Please note that the macro for generating interrupt routine stubs relies
17 * on the macro execution counter \@ which is shared by all invocations across
18 * this compilation unit. Be careful when adding additional macros to this
19 * file. In particular:
20 * 1) No macros can be executed before def_isr (so \@ starts at zero).
21 * 2) def_isr cannot have any macros (so \@ increments by one for each
22 *    def_isr invocation).
23 */
24
25.text
26
27/* interrupt service routine stubs */
28_isr:
29.macro def_isr
30.pushsection .text
31FUNCTION_LABEL(_isr_\@)
32    .cfi_startproc simple
33    .cfi_signal_frame
34    /* Set CFA for an interrupt frame. */
35.if \@ == 8 || (\@ >= 10 && \@ <= 14) || \@ == 17
36    .cfi_def_cfa %rsp, (8 * 6)
37.else
38    .cfi_def_cfa %rsp, (8 * 5)
39.endif
40    .cfi_offset %rip, -(5 * 8)
41    /* Mark each reg as having the same value as from the "calling" frame.
42       This is the default state for callee-saved registers, but for completeness
43       sake we do this for all of them. */
44    ALL_CFI_SAME_VALUE
45    /* Clear the AC flag to prevent ring 0 from performing data accesses to
46     * ring 3 if SMAP is available.  If it was set, it will get restored by
47     * iretd.  DO NOT REMOVE THIS CLAC, code in idt.c assumes it is here.
48     * It MUST be the first instruction of this function. */
49    clac
50    /* We can't use push_value here: it is a macro invocation and using it
51     * will screw up tracking of \@ == isr number. Instead we inline the .cfi
52     * directives. */
53.if \@ == 8 || (\@ >= 10 && \@ <= 14) || \@ == 17
54    /* error code pushed by exception */
55    pushq $\@              /* interrupt number */
56    .cfi_adjust_cfa_offset 8
57    jmp interrupt_common
58.else
59    pushq $0               /* fill in error code in iframe */
60    .cfi_adjust_cfa_offset 8
61    pushq $\@              /* interrupt number */
62    .cfi_adjust_cfa_offset 8
63    jmp interrupt_common
64.endif
65END_FUNCTION(_isr_\@)
66.popsection
67.pushsection .rodata.isr
68.quad _isr_\@
69.popsection
70.endm
71
72.pushsection .rodata.isr
73/* build a table of isr entry points */
74.balign 8
75DATA(_isr_table)
76.popsection
77.rept NUM_INT
78def_isr
79.endr
80
81FUNCTION_LABEL(interrupt_common)
82    .cfi_startproc simple
83    .cfi_signal_frame
84    /* Set CFA for an interrupt frame. */
85    .cfi_def_cfa %rsp, 7 * 8 /* hw + _isr_* push this many values */
86    .cfi_offset %rip, -(5 * 8)
87    /* Mark each reg as having the same value as from the "calling" frame.
88       This is the default state for callee-saved registers, but for completeness
89       sake we do this for all of them. */
90    ALL_CFI_SAME_VALUE
91
92    /* Clear the direction flag.  Without this, uses of string
93       instructions, e.g. REP MOVS in memcpy() or inlined by the compiler,
94       can go wrong and copy in the wrong direction, since this code may
95       assume that the direction flag is unset. */
96    cld
97
98    /* Check to see if we came from user space by testing the PL of the
99     * CS register that was saved on the stack automatically. Check for != 0.
100     */
101    testb $3, 0x18(%rsp)
102    jz    1f
103
104    /* swap gs to kernel space */
105    swapgs
106
1071:
108    /* save general purpose registers */
109    push_reg %r15
110    push_reg %r14
111    push_reg %r13
112    push_reg %r12
113    push_reg %r11
114    push_reg %r10
115    push_reg %r9
116    push_reg %r8
117    push_reg %rax
118    push_reg %rcx
119    push_reg %rdx
120    push_reg %rbx
121    push_reg %rbp
122    push_reg %rsi
123    push_reg %rdi
124
125    movq %rsp, %rdi     /* pass the  iframe using rdi */
126
127    call x86_exception_handler
128
129/* A label to assist gdb's backtracing through kernel exceptions.
130   When gdb sees this as the return address it knows it can fetch
131   x86_iframe_t from $rsp. See scripts/zircon.elf-gdb.py. */
132interrupt_common_iframe_set_up_for_debugger:
133
134    /* restore general purpose registers */
135    pop_reg %rdi
136    pop_reg %rsi
137    pop_reg %rbp
138    pop_reg %rbx
139    pop_reg %rdx
140    pop_reg %rcx
141    pop_reg %rax
142    pop_reg %r8
143    pop_reg %r9
144    pop_reg %r10
145    pop_reg %r11
146    pop_reg %r12
147    pop_reg %r13
148    pop_reg %r14
149    pop_reg %r15
150
151    /* check if we're returning to user space as per before */
152    testb $3, 0x18(%rsp)
153    jz    1f
154
155    /* swap gs back to user space */
156    swapgs
157
1581:
159    /* drop vector number and error code*/
160    add_to_sp 16
161
162    iretq
163END_FUNCTION(interrupt_common)
164
165/* Call external interrupt handler manually without actually issuing interrupt.
166 *
167 * For external interrupts CPU doesn't store error code on stack so we use 0. We
168 * additionally use CODE_64_SELECTOR as CS, 0 as SS, RFLAGS value and current
169 * stack.
170 */
171FUNCTION(x86_call_external_interrupt_handler)
172    /* save current RFLAGS value */
173    pushfq
174    popq %r10
175
176    /* save current RSP value */
177    movq %rsp, %r11
178
179    /* calculate exit address */
180    leaq .Lexit(%rip), %rax
181
182    /* prepare interrupt stack frame in the from interrupt_common expects to see */
183    sub_from_sp 0x38
184    movq %rdi, 0x00(%rsp)              // rdi holds vector number
185    movq $0, 0x08(%rsp)                // error code
186    movq %rax, 0x10(%rsp)              // RIP (return address)
187    movq $CODE_64_SELECTOR, 0x18(%rsp) // CS
188    movq %r10, 0x20(%rsp)              // RFLAGS
189    movq %r11, 0x28(%rsp)              // RSP
190    movq $0, 0x30(%rsp)                // SS
191
192    /* we can actually avoid this jump if we put this code above
193     * interrupt_common and just fall through, but benefits of doing this are
194     * not obvious so for now for the sake of clarity keep this jump
195     */
196    jmp    interrupt_common
197
198.Lexit:
199    ret
200END_FUNCTION(x86_call_external_interrupt_handler)
201