LF OS
Hobby operating system for amd64 with high ambitions
Loading...
Searching...
No Matches
sc.c
Go to the documentation of this file.
1#include "sc.h"
2#include "msr.h"
3#include "string.h"
4#include "mm.h"
5#include "cpu.h"
6#include "scheduler.h"
7#include "pic.h"
8#include "panic.h"
9#include "vm.h"
10#include "mq.h"
11#include "flexarray.h"
12#include "errno.h"
14#define GDT_ACCESSED 0x01
15#define GDT_RW 0x02
16#define GDT_CONFORMING 0x04
17#define GDT_EXECUTE 0x08
18#define GDT_SYSTEM 0x10
19#define GDT_RING1 0x20
20#define GDT_RING2 0x40
21#define GDT_RING3 0x60
22#define GDT_PRESENT 0x80
23
33
42
47
69
70typedef struct {
72 struct tss tss;
74
75extern void sc_handle(cpu_state* cpu);
76
77extern void _syscall_handler(void);
78extern void reload_cs(void);
79
80extern void idt_entry_0(void);
81extern void idt_entry_1(void);
82extern void idt_entry_2(void);
83extern void idt_entry_3(void);
84extern void idt_entry_4(void);
85extern void idt_entry_5(void);
86extern void idt_entry_6(void);
87extern void idt_entry_7(void);
88extern void idt_entry_8(void);
89extern void idt_entry_9(void);
90extern void idt_entry_10(void);
91extern void idt_entry_11(void);
92extern void idt_entry_12(void);
93extern void idt_entry_13(void);
94extern void idt_entry_14(void);
95extern void idt_entry_15(void);
96extern void idt_entry_16(void);
97extern void idt_entry_17(void);
98extern void idt_entry_18(void);
99extern void idt_entry_19(void);
100extern void idt_entry_20(void);
101extern void idt_entry_21(void);
102extern void idt_entry_22(void);
103extern void idt_entry_23(void);
104extern void idt_entry_24(void);
105extern void idt_entry_25(void);
106extern void idt_entry_26(void);
107extern void idt_entry_27(void);
108extern void idt_entry_28(void);
109extern void idt_entry_29(void);
110extern void idt_entry_30(void);
111extern void idt_entry_31(void);
112extern void idt_entry_32(void);
113extern void idt_entry_33(void);
114extern void idt_entry_34(void);
115extern void idt_entry_35(void);
116extern void idt_entry_36(void);
117extern void idt_entry_37(void);
118extern void idt_entry_38(void);
119extern void idt_entry_39(void);
120extern void idt_entry_40(void);
121extern void idt_entry_41(void);
122extern void idt_entry_42(void);
123extern void idt_entry_43(void);
124extern void idt_entry_44(void);
125extern void idt_entry_45(void);
126extern void idt_entry_46(void);
127extern void idt_entry_47(void);
128
130static struct idt_entry _idt[256];
131
132static flexarray_t interrupt_queues[16] = { 0 };
133
134void set_iopb(struct vm_table* context, ptr_t new_iopb) {
135 static ptr_t originalPages[2] = {0, 0};
136
137 ptr_t iopb = (ptr_t)&_cpu0->tss + _cpu0->tss.iopb_offset;
138
139 if(!originalPages[0] && !originalPages[1]) {
141 originalPages[1] = vm_context_get_physical_for_virtual(VM_KERNEL_CONTEXT, iopb + 4*KiB);
142 }
143
144 if(!new_iopb) {
145 vm_context_map(context, ALLOCATOR_REGION_USER_IOPERM.start, originalPages[0], 0);
146 vm_context_map(context, ALLOCATOR_REGION_USER_IOPERM.start + 4*KiB, originalPages[1], 0);
147 }
148 else {
149 vm_context_map(context, ALLOCATOR_REGION_USER_IOPERM.start, new_iopb, 0);
150 vm_context_map(context, ALLOCATOR_REGION_USER_IOPERM.start + 4*KiB, new_iopb * 4*KiB, 0);
151 }
152}
153
154void init_gdt(void) {
156 memset(_cpu0, 0, 4*KiB);
157 memset((char*)_cpu0 + 4*KiB, 0xFF, 12*KiB);
158
160
161 _cpu0->tss._reserved1 = 0;
162 _cpu0->tss._reserved2 = 0;
163 _cpu0->tss._reserved3 = 0;
164 _cpu0->tss.iopb_offset = 0x1000 - ((ptr_t)&_cpu0->tss & 0xFFF);
165 _cpu0->tss.ist1 = _cpu0->tss.rsp0 = _cpu0->tss.rsp1 = _cpu0->tss.rsp2 = _cpu0->kernel_stack = kernel_stack;
166
167 static struct gdt_entry gdt[8];
168 memset((uint8_t*)gdt, 0, sizeof(gdt));
169
170 // kernel CS
172 gdt[1].size = 0xa0;
173
174 // kernel SS
175 gdt[2].type = GDT_SYSTEM | GDT_PRESENT | GDT_RW;
176 gdt[2].size = 0xa0;
177
178 // user CS (compatibility mode)
179 // XXX: make 32bit descriptor
181 gdt[3].size = 0xa0;
182
183 // user SS
185 gdt[4].size = 0xa0;
186
187 // user CS (long mode)
189 gdt[5].size = 0xa0;
190
191 // TSS
193 gdt[6].limitLow = sizeof(struct tss) + (8*KiB) + 1;
194 gdt[6].baseLow = ((ptr_t)&_cpu0->tss & 0xFFFF);
195 gdt[6].baseMid = ((ptr_t)&_cpu0->tss >> 16) & 0xFF;
196 gdt[6].baseHigh = ((ptr_t)&_cpu0->tss >> 24) & 0xFF;
197 gdt[7].type = 0;
198 gdt[7].limitLow = ((ptr_t)&_cpu0->tss >> 32) & 0xFFFF;
199 gdt[7].baseLow = ((ptr_t)&_cpu0->tss >> 48) & 0xFFFF;
200
201 struct table_pointer gdtp = {
202 .limit = sizeof(gdt) -1,
203 .base = (ptr_t)gdt,
204 };
205
206 asm("lgdt %0"::"m"(gdtp));
207 reload_cs();
208 asm("ltr %%ax"::"a"(6 << 3));
209}
210
212 flexarray_t array;
213 uint64_t idx;
214
215 if((array = interrupt_queues[interrupt]) && (idx = flexarray_find(array, &mq)) != -1ULL) {
216 flexarray_remove(array, idx);
217 }
218}
219
221 for(size_t i = 0; i < sizeof(interrupt_queues) / sizeof(interrupt_queues[0]); i++) {
223 }
224}
225
227 flexarray_t array;
228
229 if(!(array = interrupt_queues[interrupt])) {
230 array = new_flexarray(sizeof(uint64_t), 0, &kernel_alloc);
231 interrupt_queues[interrupt] = array;
232 }
233
234 int error;
236 logw("sc", "error adding message queue teardown notify while adding mq %llu to interrupt queues for interrupt %u: $llu", mq, (uint64_t)interrupt, error);
237 }
238 flexarray_append(array, &mq);
239}
240
241static void _set_idt_entry(int index, ptr_t base) {
242 _idt[index].baseLow = base & 0xFFFF;
243 _idt[index].baseMid = (base >> 16) & 0xFFFF;
244 _idt[index].baseHigh = base >> 32;
245 _idt[index].selector = 0x08;
246 _idt[index].flags = 0xEE;
247 _idt[index].ist = 1;
248}
249
250static void _setup_idt(void) {
251 memset((uint8_t*)_idt, 0, sizeof(_idt));
252
301
302 struct table_pointer idtp = {
303 .limit = sizeof(_idt) - 1,
304 .base = (ptr_t)_idt,
305 };
306 asm("lidt %0"::"m"(idtp));
307}
308
309void init_sc(void) {
310 _setup_idt();
311
312 asm("mov $0xC0000080, %%rcx\n"
313 "rdmsr\n"
314 "or $1, %%rax\n"
315 "wrmsr":::"rcx","rax");
316
317 write_msr(0xC0000081, 0x001B000800000000);
318 write_msr(0xC0000082, (ptr_t)_syscall_handler);
319 write_msr(0xC0000084, 0x200); // disable interrupts on syscall
320 write_msr(0xC0000102, (ptr_t)(_cpu0));
321}
322
323static void enable_iopb(struct vm_table* context) {
324 ptr_t iopb_pages[2] = {
327 };
328
329 ptr_t iopb = (ptr_t)&_cpu0->tss + _cpu0->tss.iopb_offset;
330
331 vm_context_map(context, iopb, iopb_pages[0], 0);
332 vm_context_map(context, iopb + 4*KiB, iopb_pages[1], 0);
333
334 asm("invlpg (%0)"::"r"(iopb));
335 asm("invlpg (%0)"::"r"(iopb + (4*KiB)));
336}
337
339 cpu_state* new_cpu = old_cpu; // for idle task we only change some fields,
340 // allocating a new cpu for that is ..
341 // correct but slow, so we just reuse the old one
342
343 struct vm_table* new_context;
344 schedule_next(&new_cpu, &new_context);
345 vm_context_activate(new_context);
346 enable_iopb(new_context);
347
348 return new_cpu;
349}
350
352 if(cpu->interrupt == 0x0e) {
353 ptr_t fault_address;
354 asm("mov %%cr2, %0":"=r"(fault_address));
355
356 if(scheduler_handle_pf(fault_address, cpu->error_code)) {
357 return true;
358 }
359 }
360
361 logw("sc", "Process %d caused exception %u for reason %u at 0x%x, cpu dump below", scheduler_current_process, cpu->interrupt,cpu->error_code, cpu->rip);
362 DUMP_CPU(cpu);
363
364 // exception in user space
365 if(cpu->interrupt == 14) {
367 }
368 else {
370 }
371
372 return true;
373}
374
375__attribute__ ((force_align_arg_pointer))
376cpu_state* interrupt_handler(cpu_state* cpu) {
378
379 if(cpu->interrupt < 32) {
380 if((cpu->rip & 0x0000800000000000) == 0) {
382 cpu_state* new_cpu = cpu;
383 struct vm_table* new_context = vm_current_context();
384
385 if(schedule_next_if_needed(&new_cpu, &new_context)) {
386 vm_context_activate(new_context);
387 }
388
389 return new_cpu;
390 }
391 }
392
393 panic_cpu(cpu);
394 }
395 else if(cpu->interrupt >= 32 && cpu->interrupt < 48) {
397
398 uint8_t irq = cpu->interrupt - 0x20;
399 if(interrupt_queues[irq]) {
400 size_t len = flexarray_length(interrupt_queues[irq]);
401 const uint64_t* queues = (uint64_t*)flexarray_getall(interrupt_queues[irq]);
402
403 size_t user_size = sizeof(struct HardwareInterruptUserData);
404 size_t size = sizeof(struct Message) + user_size;
405 struct Message* msg = vm_alloc(size);
406
407 msg->size = size;
408 msg->user_size = user_size;
410 msg->sender = -1;
412
413 for(size_t i = 0; i < len; ++i) {
414 mq_push(queues[i], msg);
415 }
416
417 vm_free(msg);
418 }
419 }
420
421 return schedule_process(cpu);
422}
423
424__attribute__ ((force_align_arg_pointer))
425cpu_state* syscall_handler(cpu_state* cpu) {
427 sc_handle(cpu);
429
430 cpu_state* new_cpu = cpu; // for idle task we only change some fields,
431 // allocating a new cpu for that is ..
432 // correct but slow, so we just reuse the old one
433 struct vm_table* new_context = vm_current_context();
434
435 if(schedule_next_if_needed(&new_cpu, &new_context)) {
436 vm_context_activate(new_context);
437 }
438
439 enable_iopb(new_context);
440 return new_cpu;
441}
allocator_t kernel_alloc
Definition vm.c:737
unsigned short uint16_t
Definition arch.h:8
uint64_t ptr_t
Definition arch.h:17
unsigned int uint32_t
Definition arch.h:11
unsigned long uint64_t
Definition arch.h:14
unsigned char uint8_t
Definition arch.h:5
#define DUMP_CPU(cpu)
Definition cpu.h:33
uint64_t interrupt
Definition cpu.h:24
uint64_t rip
Definition cpu.h:26
uint64_t error_code
Definition cpu.h:24
Definition cpu.h:7
#define EEXIST
Definition errno-defs.h:14
const void * flexarray_getall(flexarray_t array)
Definition flexarray.c:126
flexarray_t new_flexarray(size_t member_size, size_t initial_alloc, allocator_t *alloc)
Definition flexarray.c:14
void flexarray_remove(flexarray_t array, uint64_t idx)
Definition flexarray.c:78
uint64_t flexarray_find(flexarray_t array, void *data)
Will return -1 when not found.
Definition flexarray.c:92
uint64_t flexarray_append(flexarray_t array, void *data)
Definition flexarray.c:56
size_t flexarray_length(flexarray_t array)
Definition flexarray.c:102
#define ALLOCATOR_REGION_KERNEL_HEAP
Definition vm.h:25
#define ALLOCATOR_REGION_USER_IOPERM
Definition vm.h:20
void * memset(void *dest, int c, size_t size)
Definition string.c:72
#define logw(component, fmt,...)
Definition log.h:44
size_t user_size
Size of the user data.
struct Message::UserData::HardwareInterruptUserData HardwareInterrupt
size_t size
Size of the message, including metadata.
pid_t sender
Sender of the message.
union Message::UserData user_data
@ MT_HardwareInterrupt
size_t user_size
Size of the user data.
enum MessageType type
Type of the message.
#define KiB
Definition mm.h:6
uint64_t mq_push(uint64_t mq, struct Message *message)
Definition mq.c:144
uint64_t mq_notify_teardown(mq_id_t mq, mq_notifier notifier)
Definition mq.c:242
void write_msr(uint32_t msr, uint64_t value)
Definition msr.c:3
void panic_cpu(const cpu_state *cpu)
Definition panic.c:73
void pic_set_handled(int interrupt)
Definition pic.c:23
void idt_entry_35(void)
static cpu_local_data * _cpu0
Definition sc.c:129
void idt_entry_47(void)
uint32_t _reserved0
Definition sc.c:49
void set_iopb(struct vm_table *context, ptr_t new_iopb)
Definition sc.c:134
static struct idt_entry _idt[256]
Definition sc.c:130
static cpu_state * schedule_process(cpu_state *old_cpu)
Definition sc.c:338
void idt_entry_11(void)
void idt_entry_36(void)
uint16_t iopb_offset
Definition sc.c:67
uint8_t baseHigh
Definition sc.c:40
void _syscall_handler(void)
#define GDT_SYSTEM
Definition sc.c:18
uint32_t baseHigh
Definition sc.c:30
void idt_entry_19(void)
uint8_t type
Definition sc.c:38
struct tss tss
Definition sc.c:72
void reload_cs(void)
uint16_t limit
Definition sc.c:44
void idt_entry_33(void)
void idt_entry_2(void)
void idt_entry_31(void)
void idt_entry_37(void)
void idt_entry_46(void)
void idt_entry_7(void)
void idt_entry_40(void)
uint64_t rsp0
Definition sc.c:51
void idt_entry_30(void)
static void _setup_idt(void)
Definition sc.c:250
uint16_t selector
Definition sc.c:26
void idt_entry_41(void)
uint64_t ist6
Definition sc.c:62
void idt_entry_14(void)
void idt_entry_21(void)
uint64_t ist2
Definition sc.c:58
void idt_entry_8(void)
void idt_entry_29(void)
static void enable_iopb(struct vm_table *context)
Definition sc.c:323
void idt_entry_15(void)
void idt_entry_26(void)
uint16_t limitLow
Definition sc.c:35
uint8_t flags
Definition sc.c:28
static void _set_idt_entry(int index, ptr_t base)
Definition sc.c:241
uint64_t ist1
Definition sc.c:57
static bool handle_userspace_exception(cpu_state *cpu)
Definition sc.c:351
uint16_t _reserved3
Definition sc.c:66
uint64_t ist5
Definition sc.c:61
void idt_entry_24(void)
static flexarray_t interrupt_queues[16]
Definition sc.c:132
#define GDT_EXECUTE
Definition sc.c:17
void idt_entry_45(void)
void idt_entry_38(void)
void idt_entry_10(void)
void idt_entry_5(void)
void idt_entry_43(void)
void idt_entry_3(void)
void interrupt_del_queue(uint8_t interrupt, uint64_t mq)
Definition sc.c:211
#define GDT_PRESENT
Definition sc.c:22
void idt_entry_9(void)
ptr_t kernel_stack
Definition sc.c:71
void idt_entry_25(void)
uint64_t base
Definition sc.c:45
#define GDT_ACCESSED
Definition sc.c:14
#define GDT_RW
Definition sc.c:15
void interrupt_add_queue(uint8_t interrupt, uint64_t mq)
Definition sc.c:226
uint64_t _reserved1
Definition sc.c:55
uint64_t _reserved2
Definition sc.c:65
void idt_entry_1(void)
void idt_entry_20(void)
uint8_t size
Definition sc.c:39
void idt_entry_32(void)
void idt_entry_27(void)
static void interrupt_delall_queue(uint64_t mq)
Definition sc.c:220
void idt_entry_12(void)
void idt_entry_22(void)
void idt_entry_39(void)
void idt_entry_42(void)
typedef __attribute__
Definition sc.c:424
void sc_handle(cpu_state *cpu)
void idt_entry_6(void)
void idt_entry_18(void)
uint16_t baseLow
Definition sc.c:25
uint32_t _reserved
Definition sc.c:31
uint16_t baseMid
Definition sc.c:29
void init_sc(void)
Definition sc.c:309
void idt_entry_28(void)
uint8_t ist
Definition sc.c:27
void idt_entry_4(void)
uint64_t ist7
Definition sc.c:63
void idt_entry_17(void)
void idt_entry_34(void)
#define GDT_RING3
Definition sc.c:21
uint64_t rsp1
Definition sc.c:52
uint8_t size
Definition sc.c:4
void idt_entry_0(void)
uint64_t rsp2
Definition sc.c:53
uint64_t base
Definition sc.c:1
uint64_t ist4
Definition sc.c:60
void idt_entry_23(void)
void idt_entry_16(void)
void init_gdt(void)
Definition sc.c:154
void idt_entry_44(void)
uint16_t baseLow
Definition sc.c:36
void idt_entry_13(void)
uint64_t ist3
Definition sc.c:59
uint8_t baseMid
Definition sc.c:37
Definition sc.c:34
Definition sc.c:24
Definition sc.c:48
void scheduler_process_save(cpu_state *cpu)
Definition scheduler.c:155
void schedule_next(cpu_state **cpu, struct vm_table **context)
Definition scheduler.c:181
bool schedule_next_if_needed(cpu_state **cpu, struct vm_table **context)
Definition scheduler.c:221
bool scheduler_handle_pf(ptr_t fault_address, uint64_t error_code)
Definition scheduler.c:334
volatile pid_t scheduler_current_process
Definition scheduler.c:50
void scheduler_kill_current(enum kill_reason reason)
Definition scheduler.c:261
@ kill_reason_abort
Definition scheduler.h:10
@ kill_reason_segv
Definition scheduler.h:9
static bool size_t size_t uint64_t * mq
Definition syscalls.h:303
static uint16_t bool uint64_t * error
Definition syscalls.h:126
ptr_t vm_context_alloc_pages(struct vm_table *context, region_t region, size_t num)
Definition vm.c:560
struct vm_table * vm_current_context(void)
Definition vm.c:743
void vm_context_map(struct vm_table *pml4, ptr_t virtual, ptr_t physical, uint8_t pat)
Definition vm.c:406
ptr_t vm_context_get_physical_for_virtual(struct vm_table *context, ptr_t virtual)
Definition vm.c:475
void vm_context_activate(struct vm_table *context)
Definition vm.c:388
void * vm_alloc(size_t size)
Like malloc but allocates full pages only. 16 byte data overhead.
Definition vm.c:697
struct vm_table * VM_KERNEL_CONTEXT
Definition vm.c:16
void vm_free(void *ptr)
the matching free() like function for vm_alloc
Definition vm.c:707
A paging table, when this is a PML4 it may also be called context.
Definition vm.c:42