e7dce9f22182a23968f368f96e85d1b4658c9e97
[monolithium.git] / kernel / src / interrupt.c
1 /*
2  * interrupt.c
3  *
4  * Copyright (C) 2018 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 <lock.h>
23 #include <thread.h>
24 #include <cpu.h>
25
26 static byte_t isr_stubs[IDT_NUM_INTERRUPTS * ISR_STUB_SIZE];
27 static idt_entry_t idt[IDT_NUM_INTERRUPTS];
28 static interrupt_handler_t handlers[IDT_NUM_INTERRUPTS];
29
30 static void idt_main_handler(byte_t interrupt_num, registers_t regs)
31 {
32     regs.esp += 16;
33     if (handlers[interrupt_num].procedure == NULL) return;
34
35     thread_t *thread = get_current_thread();
36
37     if (thread)
38     {
39         if (thread->in_kernel == 0) thread->last_context = &regs;
40         thread->in_kernel++;
41     }
42
43     if (handlers[interrupt_num].interrupts) cpu_enable_interrupts();
44     handlers[interrupt_num].procedure(&regs, interrupt_num);
45
46     if (thread)
47     {
48         ASSERT(thread->in_kernel > 0);
49
50         if (--thread->in_kernel == 0)
51         {
52             thread->last_context = NULL;
53             if (thread->terminating) thread->terminated = TRUE;
54             if (thread->terminated || thread->frozen > 0) syscall_yield_quantum();
55             ASSERT(!thread->terminated && (thread->frozen <= 0));
56         }
57     }
58
59     cpu_disable_interrupts();
60 }
61
62 dword_t set_int_handler(byte_t interrupt_num, isr_proc_t proc, bool_t interrupts, bool_t usermode)
63 {
64     dword_t ret = ERR_SUCCESS;
65     critical_t critical;
66
67     enter_critical(&critical);
68
69     if (handlers[interrupt_num].procedure != NULL)
70     {
71         ret = ERR_EXISTS;
72         goto done;
73     }
74
75     handlers[interrupt_num].procedure = proc;
76     handlers[interrupt_num].interrupts = interrupts;
77     idt[interrupt_num].type = usermode ? IDT_GATE_USER : IDT_GATE_KERNEL;
78
79 done:
80     leave_critical(&critical);
81     return ret;
82 }
83
84 dword_t remove_int_handler(byte_t interrupt_num)
85 {
86     dword_t ret = ERR_SUCCESS;
87     critical_t critical;
88
89     enter_critical(&critical);
90
91     if (handlers[interrupt_num].procedure == NULL)
92     {
93         ret = ERR_NOTFOUND;
94         goto done;
95     }
96
97     handlers[interrupt_num].procedure = NULL;
98     idt[interrupt_num].type = IDT_GATE_KERNEL;
99
100 done:
101     leave_critical(&critical);
102     return ret;
103 }
104
105 void interrupt_init(void)
106 {
107     dword_t i, offset = 0;
108
109     for (i = 0; i < IDT_NUM_INTERRUPTS; i++)
110     {
111         idt[i].offset = (dword_t)&isr_stubs[offset] & 0xFFFF;
112         idt[i].offset_high = (dword_t)&isr_stubs[offset] >> 16;
113         idt[i].selector = get_kernel_code_selector();
114         idt[i].type = IDT_GATE_KERNEL;
115         idt[i].zero = 0;
116
117         if (!HAS_ERROR_CODE(i))
118         {
119             isr_stubs[offset++] = 0x6A; // push 0
120             isr_stubs[offset++] = 0x00;
121         }
122
123         isr_stubs[offset++] = 0x60; // pushad
124
125         isr_stubs[offset++] = 0x1E; // push ds
126
127         isr_stubs[offset++] = 0x6A; // push get_kernel_data_selector()
128         isr_stubs[offset++] = get_kernel_data_selector();
129
130         isr_stubs[offset++] = 0x58; // pop eax
131
132         isr_stubs[offset++] = 0x8E; // mov ds, ax
133         isr_stubs[offset++] = 0xD8;
134
135         isr_stubs[offset++] = 0x8E; // mov es, ax
136         isr_stubs[offset++] = 0xC0;
137
138         isr_stubs[offset++] = 0x8E; // mov fs, ax
139         isr_stubs[offset++] = 0xE0;
140
141         isr_stubs[offset++] = 0x8E; // mov gs, ax
142         isr_stubs[offset++] = 0xE8;
143
144         isr_stubs[offset++] = 0x6A; // push i
145         isr_stubs[offset++] = (byte_t) i;
146
147         isr_stubs[offset++] = 0xE8; // call idt_main_handler
148         dword_t rel_addr = (dword_t)idt_main_handler - (dword_t)&isr_stubs[offset + 4];
149         isr_stubs[offset++] = rel_addr & 0xFF;
150         isr_stubs[offset++] = (rel_addr >> 8) & 0xFF;
151         isr_stubs[offset++] = (rel_addr >> 16) & 0xFF;
152         isr_stubs[offset++] = (rel_addr >> 24) & 0xFF;
153
154         isr_stubs[offset++] = 0x58; // pop eax
155
156         if (!HAS_ERROR_CODE(i))
157         {
158             isr_stubs[offset++] = 0x81; // cmp dword [esp + 0x24], CONTEXT_SWITCH_MAGIC
159             isr_stubs[offset++] = 0x7C;
160             isr_stubs[offset++] = 0x24;
161             isr_stubs[offset++] = 0x24;
162             isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC & 0xFF;
163             isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 8) & 0xFF;
164             isr_stubs[offset++] = (CONTEXT_SWITCH_MAGIC >> 16) & 0xFF;
165             isr_stubs[offset++] = CONTEXT_SWITCH_MAGIC >> 24;
166
167             isr_stubs[offset++] = 0x75; // jnz +0x04
168             isr_stubs[offset++] = 0x04;
169
170             isr_stubs[offset++] = 0x8B; // mov esp, [esp + 0x10]
171             isr_stubs[offset++] = 0x64;
172             isr_stubs[offset++] = 0x24;
173             isr_stubs[offset++] = 0x10;
174         }
175
176         isr_stubs[offset++] = 0x58; // pop eax
177
178         isr_stubs[offset++] = 0x8E; // mov ds, ax
179         isr_stubs[offset++] = 0xD8;
180
181         isr_stubs[offset++] = 0x8E; // mov es, ax
182         isr_stubs[offset++] = 0xC0;
183
184         isr_stubs[offset++] = 0x8E; // mov fs, ax
185         isr_stubs[offset++] = 0xE0;
186
187         isr_stubs[offset++] = 0x8E; // mov gs, ax
188         isr_stubs[offset++] = 0xE8;
189
190         isr_stubs[offset++] = 0x61; // popad
191
192         isr_stubs[offset++] = 0x83; // add esp, 4
193         isr_stubs[offset++] = 0xC4;
194         isr_stubs[offset++] = 0x04;
195
196         isr_stubs[offset++] = 0xCF; // iret
197     }
198
199     cpu_set_interrupt_table(idt, sizeof(idt));
200 }