aboutsummaryrefslogtreecommitdiff
path: root/arch/x86/64/exceptions.S
blob: e66c53ceeaed8ca0798db6ddf81511f493f903b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
 * 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 <lk/asm.h>
#include <arch/x86/descriptor.h>

#define NUM_INT 0x100
#define NUM_EXC 0x14

.text

/* interrupt service routine stubs */

/*
 * pushq $i occupies 5 bytes when i >= 0x80 compare to
 * 2 bytes when i < 0x80, use align to fill the gap
 * to make sure isr_stub_len correct for each interrupts
 */
_isr:
.set i, 0
.rept NUM_INT

.set isr_stub_start, .

.if i == 8 || (i >= 10 && i <= 14) || i == 17
.align 16
    nop             /* error code pushed by exception */
    nop             /* 2 nops are the same length as push byte */
    pushq $i        /* interrupt number */
    jmp interrupt_common
.align 16
.else
.align 16
    pushq $0        /* fill in error code in iframe */
    pushq $i        /* interrupt number */
    jmp interrupt_common
.align 16
.endif

/* figure out the length of a single isr stub (usually 6 or 9 bytes) */
.set isr_stub_len, . - isr_stub_start

.set i, i + 1
.endr

/* annoying, but force AS to use the same (longer) encoding of jmp for all of the stubs */
.fill 256

interrupt_common:

    /* clear the direction bit */
    cld

    /* save general purpose registers */
    pushq %r15
    pushq %r14
    pushq %r13
    pushq %r12
    pushq %r11
    pushq %r10
    pushq %r9
    pushq %r8
    pushq %rax
    pushq %rcx
    pushq %rdx
    pushq %rbx
    pushq %rbp
    pushq %rsi
    pushq %rdi

    /* pass the  iframe using rdi */
    movq %rsp, %rdi

    call x86_exception_handler

    /* restore general purpose registers */
    popq %rdi
    popq %rsi
    popq %rbp
    popq %rbx
    popq %rdx
    popq %rcx
    popq %rax
    popq %r8
    popq %r9
    popq %r10
    popq %r11
    popq %r12
    popq %r13
    popq %r14
    popq %r15

    /* drop vector number and error code*/
    addq $16, %rsp
    iretq

FUNCTION(setup_idt)
    /* setup isr stub descriptors in the idt */
    mov  $_isr, %rsi
    mov  $_idt, %rdi
    movl $NUM_INT, %ecx

.Lloop:
    mov  %rsi, %rbx
    movw %bx, (%rdi)        /* offset [0:15] in IDT(n).low */
    shr  $16, %rbx
    movw %bx, 6(%rdi)       /* offset [16:31] in IDT(n).high */
    shr  $16, %rbx
    movl %ebx, 8(%rdi)      /* offset [32:63] */

    add  $isr_stub_len, %rsi    /* index the next ISR stub */
    add  $16, %rdi          /* index the next IDT entry */

    loop .Lloop

    lidt _idtr

    ret

.data

.align 8
DATA(_idtr)
    .short _idt_end - _idt - 1  /* IDT limit */
    .quad _idt
.fill 8

.align 8
/* interrupt descriptor table (IDT) */
DATA(_idt)

.set i, 0
.rept NUM_INT
    .short 0        /* low 16 bits of ISR offset (_isr#i & 0FFFFh) */
    .short CODE_64_SELECTOR   /* selector */
    .byte  0
    .byte  0x8e     /* present, ring 0, 64-bit interrupt gate */
    .short  0       /* high 16 bits of ISR offset (_isr#i / 65536) */
    .short  0       /* ISR offset */
    .short  0       /* ISR offset */
    .short  0       /* 32bits Reserved */
    .short  0       /* 32bits Reserved */


.set i, i + 1
.endr

.global _idt_end
_idt_end: