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