fcbcfe99a546ff480695fe8ae5edf58918910c17
[monolithium.git] / kernel / src / interrupt.c
1 /*
2  * interrupt.c
3  *
4  * Copyright (C) 2013 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <interrupt.h>
21 #include <segments.h>
22 #include <sync.h>
23
24 static byte_t isr_stubs[IDT_NUM_INTERRUPTS * ISR_STUB_SIZE];
25 static idt_entry_t idt[IDT_NUM_INTERRUPTS];
26 static interrupt_handler_t handlers[IDT_NUM_INTERRUPTS];
27
28 static inline void lidt(dword_t base, dword_t size)
29 {
30     volatile dword_t idtr[2];
31     idtr[0] = size << 16;
32     idtr[1] = base;
33     asm volatile ("lidt (%0)" :: "r"((byte_t*)idtr + 2));
34 }
35
36 static void idt_main_handler(byte_t interrupt_num, registers_t regs)
37 {
38     regs.esp += 16;
39     if (handlers[interrupt_num].procedure == NULL) return;
40
41     if (handlers[interrupt_num].interrupts) enable_ints();
42     handlers[interrupt_num].procedure(&regs, interrupt_num);
43     if (handlers[interrupt_num].interrupts) disable_ints();
44 }
45
46 dword_t set_int_handler(byte_t interrupt_num, isr_proc_t proc, bool_t interrupts, bool_t usermode)
47 {
48     dword_t ret = ERR_SUCCESS;
49     critical_t critical;
50
51     enter_critical(&critical);
52
53     if (handlers[interrupt_num].procedure != NULL)
54     {
55         ret = ERR_EXISTS;
56         goto done;
57     }
58
59     handlers[interrupt_num].procedure = proc;
60     handlers[interrupt_num].interrupts = interrupts;
61     idt[interrupt_num].type = usermode ? IDT_GATE_USER : IDT_GATE_KERNEL;
62
63 done:
64     leave_critical(&critical);
65     return ret;
66 }
67
68 dword_t remove_int_handler(byte_t interrupt_num)
69 {
70     dword_t ret = ERR_SUCCESS;
71     critical_t critical;
72
73     enter_critical(&critical);
74
75     if (handlers[interrupt_num].procedure == NULL)
76     {
77         ret = ERR_NOTFOUND;
78         goto done;
79     }
80
81     handlers[interrupt_num].procedure = NULL;
82     idt[interrupt_num].type = IDT_GATE_KERNEL;
83
84 done:
85     leave_critical(&critical);
86     return ret;
87 }
88
89 void interrupt_init(void)
90 {
91     dword_t i, offset = 0;
92
93     for (i = 0; i < IDT_NUM_INTERRUPTS; i++)
94     {
95         idt[i].offset = (dword_t)&isr_stubs[offset] & 0xFFFF;
96         idt[i].offset_high = (dword_t)&isr_stubs[offset] >> 16;
97         idt[i].selector = get_kernel_code_selector();
98         idt[i].type = IDT_GATE_KERNEL;
99         idt[i].zero = 0;
100
101         if (!HAS_ERROR_CODE(i))
102         {
103             isr_stubs[offset++] = 0x6A; // push 0
104             isr_stubs[offset++] = 0x00;
105         }
106
107         isr_stubs[offset++] = 0x60; // pushad
108
109         isr_stubs[offset++] = 0x1E; // push ds
110
111         isr_stubs[offset++] = 0x6A; // push get_kernel_data_selector()
112         isr_stubs[offset++] = get_kernel_data_selector();
113
114         isr_stubs[offset++] = 0x58; // pop eax
115
116         isr_stubs[offset++] = 0x8E; // mov ds, ax
117         isr_stubs[offset++] = 0xD8;
118
119         isr_stubs[offset++] = 0x8E; // mov es, ax
120         isr_stubs[offset++] = 0xC0;
121
122         isr_stubs[offset++] = 0x8E; // mov fs, ax
123         isr_stubs[offset++] = 0xE0;
124
125         isr_stubs[offset++] = 0x8E; // mov gs, ax
126         isr_stubs[offset++] = 0xE8;
127
128         isr_stubs[offset++] = 0x6A; // push i
129         isr_stubs[offset++] = (byte_t) i;
130
131         isr_stubs[offset++] = 0xE8; // call idt_main_handler
132         dword_t rel_addr = (dword_t)idt_main_handler - (dword_t)&isr_stubs[offset + 4];
133         isr_stubs[offset++] = rel_addr & 0xFF;
134         isr_stubs[offset++] = (rel_addr >> 8) & 0xFF;
135         isr_stubs[offset++] = (rel_addr >> 16) & 0xFF;
136         isr_stubs[offset++] = (rel_addr >> 24) & 0xFF;
137
138         isr_stubs[offset++] = 0x58; // pop eax
139
140         if (!HAS_ERROR_CODE(i))
141         {
142             isr_stubs[offset++] = 0x81; // cmp dword [esp + 0x24], CONTEXT_SWITCH_MAGIC
143             isr_stubs[offset++] = 0x7C;
144             isr_stubs[offset++] = 0x24;
145             isr_stubs[offset++] = 0x24;
146             isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC & 0xFF;
147             isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 8) & 0xFF;
148             isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 16) & 0xFF;
149             isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC >> 24;
150
151             isr_stubs[offset++] = 0x75; // jnz +0x04
152             isr_stubs[offset++] = 0x04;
153
154             isr_stubs[offset++] = 0x8B; // mov esp, [esp + 0x10]
155             isr_stubs[offset++] = 0x64;
156             isr_stubs[offset++] = 0x24;
157             isr_stubs[offset++] = 0x10;
158         }
159
160         isr_stubs[offset++] = 0x58; // pop eax
161
162         isr_stubs[offset++] = 0x8E; // mov ds, ax
163         isr_stubs[offset++] = 0xD8;
164
165         isr_stubs[offset++] = 0x8E; // mov es, ax
166         isr_stubs[offset++] = 0xC0;
167
168         isr_stubs[offset++] = 0x8E; // mov fs, ax
169         isr_stubs[offset++] = 0xE0;
170
171         isr_stubs[offset++] = 0x8E; // mov gs, ax
172         isr_stubs[offset++] = 0xE8;
173
174         isr_stubs[offset++] = 0x61; // popad
175
176         isr_stubs[offset++] = 0x83; // add esp, 4
177         isr_stubs[offset++] = 0xC4;
178         isr_stubs[offset++] = 0x04;
179
180         isr_stubs[offset++] = 0xCF; // iret
181     }
182
183     lidt((dword_t)&idt, sizeof(idt));
184 }