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