summaryrefslogtreecommitdiff
path: root/kernel/cpu
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-30 22:12:14 +0100
committerAnton Kling <anton@kling.gg>2023-10-31 00:18:38 +0100
commit8a9208612eec8ddae4c418485d848ecfa0613699 (patch)
tree2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/cpu
parentca76600acc8bf7a02346efa5bd8f17072210ec01 (diff)
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel/cpu')
-rw-r--r--kernel/cpu/gdt.c72
-rw-r--r--kernel/cpu/gdt.h76
-rw-r--r--kernel/cpu/idt.c195
-rw-r--r--kernel/cpu/idt.h70
-rw-r--r--kernel/cpu/int_syscall.s20
-rw-r--r--kernel/cpu/io.h15
-rw-r--r--kernel/cpu/io.s147
-rw-r--r--kernel/cpu/reload_gdt.s17
-rw-r--r--kernel/cpu/spinlock.c2
-rw-r--r--kernel/cpu/spinlock.h5
-rw-r--r--kernel/cpu/syscall.c183
-rw-r--r--kernel/cpu/syscall.h59
12 files changed, 861 insertions, 0 deletions
diff --git a/kernel/cpu/gdt.c b/kernel/cpu/gdt.c
new file mode 100644
index 0000000..28853cf
--- /dev/null
+++ b/kernel/cpu/gdt.c
@@ -0,0 +1,72 @@
+#include "gdt.h"
+
+extern void flush_tss(void);
+extern void load_gdt(void *);
+
+typedef struct tss_entry_struct tss_entry_t;
+
+tss_entry_t tss_entry;
+
+typedef union {
+ struct GDT_Entry s;
+ uint64_t raw;
+} GDT_Entry;
+
+GDT_Entry gdt_entries[6] = {0};
+GDT_Pointer gdtr;
+
+extern uint32_t inital_esp;
+void write_tss(struct GDT_Entry *gdt_entry) {
+ uint32_t base = (uint32_t)&tss_entry;
+ uint32_t limit = sizeof(tss_entry);
+
+ gdt_entry->limit_low = limit;
+ gdt_entry->base_low = base;
+ gdt_entry->accessed = 1;
+ gdt_entry->read_write = 0;
+ gdt_entry->conforming_expand_down = 0;
+ gdt_entry->code = 1;
+ gdt_entry->code_data_segment = 0;
+ gdt_entry->DPL = 0;
+ gdt_entry->present = 1;
+ gdt_entry->limit_high = limit >> 16;
+ gdt_entry->available = 0;
+ gdt_entry->long_mode = 0;
+ gdt_entry->big = 0;
+ gdt_entry->gran = 0;
+ gdt_entry->base_high = (base & ((uint32_t)0xff << 24)) >> 24;
+
+ memset(&tss_entry, 0, sizeof tss_entry);
+ tss_entry.ss0 = GDT_KERNEL_DATA_SEGMENT * GDT_ENTRY_SIZE;
+ register uint32_t esp asm("esp");
+ tss_entry.esp0 = esp;
+}
+
+void gdt_init() {
+ gdt_entries[GDT_NULL_SEGMENT].raw = 0x0;
+ gdt_entries[GDT_KERNEL_CODE_SEGMENT].raw =
+ 0xCF9A000000FFFF; // Kernel code segment
+ gdt_entries[GDT_KERNEL_DATA_SEGMENT].raw =
+ 0xCF92000000FFFF; // Kernel data segment
+
+ // Usermode code segment
+ memcpy(&gdt_entries[GDT_USERMODE_CODE_SEGMENT],
+ &gdt_entries[GDT_KERNEL_CODE_SEGMENT], GDT_ENTRY_SIZE);
+
+ // Usermode data segment
+ memcpy(&gdt_entries[GDT_USERMODE_DATA_SEGMENT],
+ &gdt_entries[GDT_KERNEL_DATA_SEGMENT], GDT_ENTRY_SIZE);
+
+ // Set DPL to 3 to indicate that the segment is in ring 3
+ gdt_entries[GDT_USERMODE_CODE_SEGMENT].s.DPL = 3;
+ gdt_entries[GDT_USERMODE_DATA_SEGMENT].s.DPL = 3;
+
+ write_tss((struct GDT_Entry *)&gdt_entries[GDT_TSS_SEGMENT]);
+
+ gdtr.offset = (uint32_t)&gdt_entries;
+ gdtr.size = sizeof(gdt_entries) - 1;
+
+ asm("cli");
+ load_gdt(&gdtr);
+ flush_tss();
+}
diff --git a/kernel/cpu/gdt.h b/kernel/cpu/gdt.h
new file mode 100644
index 0000000..a490dea
--- /dev/null
+++ b/kernel/cpu/gdt.h
@@ -0,0 +1,76 @@
+#ifndef GDT_H
+#define GDT_H
+#include <stdint.h>
+#include <string.h>
+
+#define GDT_ENTRY_SIZE 0x8
+#define GDT_NULL_SEGMENT 0x0
+#define GDT_KERNEL_CODE_SEGMENT 0x1
+#define GDT_KERNEL_DATA_SEGMENT 0x2
+#define GDT_USERMODE_CODE_SEGMENT 0x3
+#define GDT_USERMODE_DATA_SEGMENT 0x4
+#define GDT_TSS_SEGMENT 0x5
+
+struct GDT_Entry
+{
+ uint16_t limit_low;
+ uint32_t base_low : 24;
+ uint32_t accessed : 1;
+ uint32_t read_write : 1; // readable for code, writable for data
+ uint32_t conforming_expand_down : 1; // conforming for code, expand down for data
+ uint32_t code : 1; // 1 for code, 0 for data
+ uint32_t code_data_segment : 1; // should be 1 for everything but TSS and LDT
+ uint32_t DPL : 2; // privilege level
+ uint32_t present : 1;
+ uint32_t limit_high : 4;
+ uint32_t available : 1; // only used in software; has no effect on hardware
+ uint32_t long_mode : 1;
+ uint32_t big : 1; // 32-bit opcodes for code, uint32_t stack for data
+ uint32_t gran : 1; // 1 to use 4k page addressing, 0 for byte addressing
+ uint8_t base_high;
+}__attribute__((packed));
+
+typedef struct GDT_Pointer
+{
+ uint16_t size;
+ uint32_t offset;
+}__attribute__((packed)) GDT_Pointer;
+
+struct tss_entry_struct
+{
+ uint32_t prev_tss; // The previous TSS - with hardware task switching these form a kind of backward linked list.
+ uint32_t esp0; // The stack pointer to load when changing to kernel mode.
+ uint32_t ss0; // The stack segment to load when changing to kernel mode.
+ // Everything below here is unused.
+ uint32_t esp1; // esp and ss 1 and 2 would be used when switching to rings 1 or 2.
+ uint32_t ss1;
+ uint32_t esp2;
+ uint32_t ss2;
+ uint32_t cr3;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t es;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t fs;
+ uint32_t gs;
+ uint32_t ldt;
+ uint16_t trap;
+ uint16_t iomap_base;
+} __attribute__((packed));
+
+void gdt_init();
+
+uint8_t gen_access_byte(uint8_t priv, uint8_t s, uint8_t ex, uint8_t dc, uint8_t rw);
+uint64_t gen_gdt_entry(uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flag);
+uint8_t gen_flag(uint8_t gr, uint8_t sz);
+#endif
diff --git a/kernel/cpu/idt.c b/kernel/cpu/idt.c
new file mode 100644
index 0000000..abcafad
--- /dev/null
+++ b/kernel/cpu/idt.c
@@ -0,0 +1,195 @@
+#include <cpu/idt.h>
+#include <sched/scheduler.h>
+#include <stdio.h>
+
+#define MASTER_PIC_COMMAND_PORT 0x20
+#define MASTER_PIC_DATA_PORT 0x21
+#define SLAVE_PIC_COMMAND_PORT 0xA0
+#define SLAVE_PIC_DATA_PORT 0xA1
+#define KEYBOARD_DATA_PORT 0x60
+#define KEYBOARD_STATUS_PORT 0x64
+#define KERNEL_CODE_SEGMENT_OFFSET GDT_ENTRY_SIZE *GDT_KERNEL_CODE_SEGMENT
+
+#define IDT_MAX_ENTRY 256
+
+struct IDT_Descriptor {
+ uint16_t low_offset;
+ uint16_t code_segment_selector;
+ uint8_t zero; // Always should be zero
+ uint8_t type_attribute;
+ uint16_t high_offset;
+} __attribute__((packed)) __attribute__((aligned(4)));
+
+struct IDT_Pointer {
+ uint16_t size;
+ struct IDT_Descriptor **interrupt_table;
+} __attribute__((packed));
+
+struct IDT_Descriptor IDT_Entry[IDT_MAX_ENTRY];
+struct IDT_Pointer idtr;
+
+extern void load_idtr(void *idtr);
+
+void format_descriptor(uint32_t offset, uint16_t code_segment,
+ uint8_t type_attribute,
+ struct IDT_Descriptor *descriptor) {
+ descriptor->low_offset = offset & 0xFFFF;
+ descriptor->high_offset = offset >> 16;
+ descriptor->type_attribute = type_attribute;
+ descriptor->code_segment_selector = code_segment;
+ descriptor->zero = 0;
+}
+
+void install_handler(void (*handler_function)(), uint16_t type_attribute,
+ uint8_t entry) {
+ format_descriptor((uint32_t)handler_function, KERNEL_CODE_SEGMENT_OFFSET,
+ type_attribute, &IDT_Entry[entry]);
+}
+
+__attribute__((no_caller_saved_registers)) void EOI(uint8_t irq) {
+ if (irq > 7)
+ outb(SLAVE_PIC_COMMAND_PORT, 0x20);
+
+ outb(MASTER_PIC_COMMAND_PORT, 0x20);
+}
+
+__attribute__((interrupt)) void general_protection_fault(registers_t *regs) {
+ klog("General Protetion Fault", 0x1);
+ kprintf(" Error Code: %x\n", regs->error_code);
+ kprintf("Instruction Pointer: %x\n", regs->eip);
+ dump_backtrace(12);
+ asm("hlt");
+ for (;;)
+ ;
+ EOI(0xD - 8);
+}
+
+__attribute__((interrupt)) void double_fault(registers_t *regs) {
+ (void)regs;
+ klog("DOUBLE FAULT", LOG_ERROR);
+ asm("hlt");
+ for (;;)
+ ;
+}
+__attribute__((interrupt)) void page_fault(registers_t *regs) {
+ if (0xFFFFDEAD == regs->eip) {
+ asm("sti");
+ for (;;)
+ switch_task();
+ }
+ klog("Page Fault", LOG_ERROR);
+ if (get_current_task()) {
+ kprintf("PID: %x\n", get_current_task()->pid);
+ kprintf("Name: %s\n", get_current_task()->program_name);
+ }
+ kprintf("Error Code: %x\n", regs->error_code);
+ kprintf("Instruction Pointer: %x\n", regs->eip);
+
+ if (regs->error_code & (1 << 0))
+ kprintf("page-protection violation\n");
+ else
+ kprintf("non-present page\n");
+
+ if (regs->error_code & (1 << 1))
+ kprintf("write access\n");
+ else
+ kprintf("read access\n");
+
+ if (regs->error_code & (1 << 2))
+ kprintf("CPL = 3\n");
+
+ if (regs->error_code & (1 << 4))
+ kprintf("Attempted instruction fetch\n");
+
+ dump_backtrace(5);
+ asm("hlt");
+ for (;;)
+ ;
+}
+
+static inline void io_wait(void) { outb(0x80, 0); }
+
+#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
+#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
+#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
+#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
+#define ICW1_INIT 0x10 /* Initialization - required! */
+
+#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
+#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
+#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
+#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
+#define ICW4_SFNM 0x10 /* Special fully nested (not) */
+
+void PIC_remap(int offset) {
+ unsigned char a1, a2;
+ a1 = inb(MASTER_PIC_DATA_PORT);
+ a2 = inb(SLAVE_PIC_DATA_PORT);
+
+ // Send ICW1 and tell the PIC that we will issue a ICW4
+ // Since there is no ICW1_SINGLE sent to indicate it is cascaded
+ // it means we will issue a ICW3.
+ outb(MASTER_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4);
+ io_wait();
+ outb(SLAVE_PIC_COMMAND_PORT, ICW1_INIT | ICW1_ICW4);
+ io_wait();
+
+ // As a part of ICW2 this sends the offsets(the position to put IRQ0) of the
+ // vector tables.
+ outb(MASTER_PIC_DATA_PORT, offset);
+ io_wait();
+ outb(SLAVE_PIC_DATA_PORT, offset + 0x8);
+ io_wait();
+
+ // This tells the master on which lines it is having slaves
+ outb(MASTER_PIC_DATA_PORT, 4);
+ io_wait();
+ // This tells the slave the cascading identity.
+ outb(SLAVE_PIC_DATA_PORT, 2);
+ io_wait();
+
+ outb(MASTER_PIC_DATA_PORT, ICW4_8086);
+ io_wait();
+ outb(SLAVE_PIC_DATA_PORT, ICW4_8086);
+ io_wait();
+
+ outb(MASTER_PIC_DATA_PORT, a1);
+ outb(SLAVE_PIC_DATA_PORT, a2);
+}
+
+void IRQ_set_mask(unsigned char IRQline) {
+ uint16_t port;
+ uint8_t value;
+ port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT;
+ if (IRQline >= 8)
+ IRQline -= 8;
+ value = inb(port) | (1 << IRQline);
+ outb(port, value);
+}
+
+void IRQ_clear_mask(unsigned char IRQline) {
+ uint16_t port;
+ uint8_t value;
+ port = (IRQline < 8) ? MASTER_PIC_DATA_PORT : SLAVE_PIC_DATA_PORT;
+ if (IRQline >= 8) {
+ IRQline -= 8;
+ }
+ value = inb(port) & ~(1 << IRQline);
+ outb(port, value);
+}
+
+void idt_init(void) {
+ install_handler(page_fault, INT_32_INTERRUPT_GATE(0x0), 0xE);
+ install_handler(double_fault, INT_32_INTERRUPT_GATE(0x0), 0x8);
+ install_handler(general_protection_fault, INT_32_INTERRUPT_GATE(0x0), 0xD);
+
+ PIC_remap(0x20);
+ IRQ_clear_mask(0xb);
+ IRQ_set_mask(0xe);
+ IRQ_set_mask(0xf);
+ IRQ_clear_mask(2);
+
+ idtr.interrupt_table = (struct IDT_Descriptor **)&IDT_Entry;
+ idtr.size = (sizeof(struct IDT_Descriptor) * IDT_MAX_ENTRY) - 1;
+ load_idtr(&idtr);
+}
diff --git a/kernel/cpu/idt.h b/kernel/cpu/idt.h
new file mode 100644
index 0000000..025ba75
--- /dev/null
+++ b/kernel/cpu/idt.h
@@ -0,0 +1,70 @@
+typedef struct kernel_registers kernel_registers_t;
+typedef struct registers registers_t;
+#ifndef IDT_H
+#define IDT_H
+#include <cpu/gdt.h>
+#include <cpu/io.h>
+#include <log.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*
+ * the type_attribute in the IDT_Entry struct
+ * is divded like this
+ * 7 0
+ * +---+---+---+---+---+---+---+---+
+ * | P | DPL | S | GateType |
+ * +---+---+---+---+---+---+---+---+
+ * It is 8 bits(1 byte) long
+ *
+ * P
+ * Present bit. Should be zero for unused
+ * interrupts.
+ *
+ * DPL
+ * Specifices the maximum ring(0 to 3) the
+ * interrupt can be called from.
+ *
+ * S
+ * Storage segment. This should be set to
+ * zero for all interrupt and trap gates.
+ *
+ * GateType
+ * Possible IDT gate types:
+ * 0b0101 0x5 5 80386 32 bit task gate
+ * 0b0110 0x6 6 80286 16-bit interrupt gate
+ * 0b0111 0x7 7 80286 16-bit trap gate
+ * 0b1110 0xE 14 80386 32-bit interrupt gate
+ * 0b1111 0xF 15 80386 32-bit trap gate
+ */
+
+// This enables the present bit.
+#define INT_PRESENT 0x80 /* 0b10000000 */
+
+#define INT_32_TASK_GATE(min_privlege) \
+ (INT_PRESENT | 0x05 | (min_privlege << 5))
+#define INT_16_INTERRUPT_GATE(min_privlege) \
+ (INT_PRESENT | 0x06 | (min_privlege << 5))
+#define INT_16_TRAP_GATE(min_privlege) \
+ (INT_PRESENT | 0x07 | (min_privlege << 5))
+#define INT_32_INTERRUPT_GATE(min_privlege) \
+ (INT_PRESENT | 0x0E | (min_privlege << 5))
+#define INT_32_TRAP_GATE(min_privlege) \
+ (INT_PRESENT | 0x0F | (min_privlege << 5))
+
+struct interrupt_frame;
+
+struct registers {
+ uint32_t error_code;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t eflags;
+ uint32_t esp;
+ uint32_t ss;
+};
+
+void idt_init(void);
+__attribute__((no_caller_saved_registers)) void EOI(unsigned char irq);
+void install_handler(void (*handler_function)(), uint16_t type_attribute,
+ uint8_t entry);
+#endif
diff --git a/kernel/cpu/int_syscall.s b/kernel/cpu/int_syscall.s
new file mode 100644
index 0000000..8c3c25f
--- /dev/null
+++ b/kernel/cpu/int_syscall.s
@@ -0,0 +1,20 @@
+.intel_syntax noprefix
+.global int_syscall
+.extern syscall_function_handler
+int_syscall:
+ push ebp
+ push edi
+ push esi
+ push edx
+ push ecx
+ push ebx
+ push eax
+ call syscall_function_handler
+ add esp, 4
+ pop ebx
+ pop ecx
+ pop edx
+ pop esi
+ pop edi
+ pop ebp
+ iretd
diff --git a/kernel/cpu/io.h b/kernel/cpu/io.h
new file mode 100644
index 0000000..38858a4
--- /dev/null
+++ b/kernel/cpu/io.h
@@ -0,0 +1,15 @@
+#include <stdint.h>
+
+extern void outsw(uint16_t, uint32_t);
+extern void outb(uint16_t, uint8_t);
+extern void outw(uint16_t, uint16_t);
+extern void outl(uint16_t, uint32_t);
+
+extern uint32_t inl(uint16_t);
+extern uint16_t inw(uint16_t);
+extern uint16_t inb(uint16_t);
+
+extern void rep_outsw(uint16_t count, uint16_t port, volatile void *addy);
+__attribute__((no_caller_saved_registers)) extern void
+rep_insw(uint16_t count, uint16_t port, volatile void *addy);
+extern void jump_usermode(void (*address)(), uint32_t stack_pointer);
diff --git a/kernel/cpu/io.s b/kernel/cpu/io.s
new file mode 100644
index 0000000..31e9df0
--- /dev/null
+++ b/kernel/cpu/io.s
@@ -0,0 +1,147 @@
+.intel_syntax noprefix
+.global outsw
+.global outb
+.global outw
+.global outl
+.global inb
+.global inw
+.global inl
+.global rep_outsw
+.global rep_insw
+.global flush_tss
+.global load_idtr
+
+# ebx, esi, edi, ebp, and esp;
+outsw:
+ push ebp
+ mov ebp, esp
+ push esi
+ mov dx, [ebp + 4+4]
+ mov esi, [ebp + 8+4]
+ outsw
+ pop esi
+ mov esp, ebp
+ pop ebp
+ ret
+
+outl:
+ mov eax, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, eax
+ ret
+
+outb:
+ mov al, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, al
+ ret
+
+outw:
+ mov ax, [esp + 8]
+ mov dx, [esp + 4]
+ out dx, ax
+ ret
+
+inl:
+ mov dx, [esp + 4]
+ in eax, dx
+ ret
+
+inw:
+ mov dx, [esp + 4]
+ in ax, dx
+ ret
+
+inb:
+ mov dx, [esp + 4]
+ in al, dx
+ ret
+
+rep_outsw:
+ push ebp
+ mov ebp, esp
+ push edi
+ mov ecx, [ebp + 4+4] #ECX is counter for OUTSW
+ mov edx, [ebp + 8+4] #Data port, in and out
+ mov edi, [ebp + 12+4] #Memory area
+ rep outsw #in to [RDI]
+ pop edi
+ mov esp, ebp
+ pop ebp
+ ret
+
+rep_insw:
+ push ebp
+ mov ebp, esp
+ push edi
+ mov ecx, [ebp + 4+4] #ECX is counter for INSW
+ mov edx, [ebp + 8+4] #Data port, in and out
+ mov edi, [ebp + 12+4] #Memory area
+ rep insw #in to [RDI]
+ pop edi
+ mov esp, ebp
+ pop ebp
+ ret
+
+flush_tss:
+ mov ax, 40
+ ltr ax
+ ret
+
+load_idtr:
+ mov edx, [esp + 4]
+ lidt [edx]
+ ret
+
+test_user_function:
+ mov eax, 0x00
+ int 0x80
+ mov ecx, 0x03
+ mov ebx, 0x04
+ mov eax, 0x02
+ int 0x80
+ mov ebx, eax
+ mov eax, 0x01
+ int 0x80
+loop:
+ jmp loop
+ ret
+
+.global spin_lock
+.global spin_unlock
+.extern locked
+
+spin_lock:
+ mov eax, 1
+ xchg eax, ebx
+ test eax, eax
+ jnz spin_lock
+ ret
+
+spin_unlock:
+ xor eax, eax
+ xchg eax, ebx
+ ret
+
+
+.global jump_usermode
+jump_usermode:
+ mov ax, (4 * 8) | 3 # user data segment with RPL 3
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax # sysexit sets SS
+
+ # setup wrmsr inputs
+ xor edx, edx # not necessary; set to 0
+ mov eax, 0x100008 # SS=0x10+0x10=0x20, CS=0x8+0x10=0x18
+ mov ecx, 0x174 # MSR specifier: IA32_SYSENTER_CS
+ wrmsr # set sysexit segments
+
+ # setup sysexit inputs
+ mov edx, [esp + 4] # to be loaded into EIP
+ mov ecx, [esp + 8] # to be loaded into ESP
+ mov esp, ecx
+ mov ebp, ecx
+ sti
+ sysexit
diff --git a/kernel/cpu/reload_gdt.s b/kernel/cpu/reload_gdt.s
new file mode 100644
index 0000000..3a0119b
--- /dev/null
+++ b/kernel/cpu/reload_gdt.s
@@ -0,0 +1,17 @@
+.section .text
+.global load_gdt
+
+load_gdt:
+ mov 4(%esp), %eax
+ lgdt (%eax)
+
+ mov $0x10, %eax
+ mov %eax, %ds
+ mov %eax, %es
+ mov %eax, %fs
+ mov %eax, %gs
+ mov %eax, %ss
+ jmp $0x8, $.long_jump
+.long_jump:
+ ret
+
diff --git a/kernel/cpu/spinlock.c b/kernel/cpu/spinlock.c
new file mode 100644
index 0000000..3f87423
--- /dev/null
+++ b/kernel/cpu/spinlock.c
@@ -0,0 +1,2 @@
+#include <stdint.h>
+uint8_t locked;
diff --git a/kernel/cpu/spinlock.h b/kernel/cpu/spinlock.h
new file mode 100644
index 0000000..93290c4
--- /dev/null
+++ b/kernel/cpu/spinlock.h
@@ -0,0 +1,5 @@
+#ifndef SPINLOCK_H
+#define SPINLOCK_H
+void spin_lock(int *l);
+void spin_unlock(int *l);
+#endif
diff --git a/kernel/cpu/syscall.c b/kernel/cpu/syscall.c
new file mode 100644
index 0000000..4978732
--- /dev/null
+++ b/kernel/cpu/syscall.c
@@ -0,0 +1,183 @@
+// FIXME: Make sure that the args variabel actually points to something
+// valid.
+#include <assert.h>
+#include <cpu/syscall.h>
+#include <drivers/pst.h>
+#include <errno.h>
+#include <fs/tmpfs.h>
+#include <fs/vfs.h>
+#include <kmalloc.h>
+#include <scalls/accept.h>
+#include <scalls/bind.h>
+#include <scalls/ftruncate.h>
+#include <scalls/kill.h>
+#include <scalls/mkdir.h>
+#include <scalls/mmap.h>
+#include <scalls/msleep.h>
+#include <scalls/ppoll.h>
+#include <scalls/recvfrom.h>
+#include <scalls/sendto.h>
+#include <scalls/shm.h>
+#include <scalls/socket.h>
+#include <scalls/stat.h>
+#include <scalls/uptime.h>
+#include <scalls/sigaction.h>
+#include <stdint.h>
+#include <string.h>
+
+#pragma GCC diagnostic ignored "-Wpedantic"
+
+int syscall_open(SYS_OPEN_PARAMS *args) {
+ char file[256];
+ strcpy(file, args->file);
+ // const char *file = copy_and_allocate_user_string(args->file);
+ int flags = args->flags;
+ int mode = args->mode;
+ int rc = vfs_open(file, flags, mode);
+ // kfree((void *)file);
+ return rc;
+}
+
+int syscall_exec(SYS_EXEC_PARAMS *args) {
+ const char *filename = copy_and_allocate_user_string(args->path);
+
+ int argc = 0;
+ for (; args->argv[argc];) {
+ argc++;
+ }
+
+ char **new_argv = kallocarray(argc + 1, sizeof(char *));
+ for (int i = 0; i < argc; i++)
+ new_argv[i] = copy_and_allocate_user_string(args->argv[i]);
+
+ new_argv[argc] = NULL;
+
+ exec(filename, new_argv);
+ kfree((void *)filename);
+ for (int i = 0; i < argc; i++)
+ kfree(new_argv[i]);
+ kfree(new_argv);
+ return -1;
+}
+
+int syscall_pipe(int fd[2]) {
+ pipe(fd); // FIXME: Error checking
+ return 0;
+}
+
+int syscall_pread(SYS_PREAD_PARAMS *args) {
+ return vfs_pread(args->fd, args->buf, args->count, args->offset);
+}
+
+int syscall_read(SYS_READ_PARAMS *args) {
+ if (!get_vfs_fd(args->fd))
+ return -EBADF;
+ int rc = vfs_pread(args->fd, args->buf, args->count,
+ get_current_task()->file_descriptors[args->fd]->offset);
+ get_current_task()->file_descriptors[args->fd]->offset += rc;
+ return rc;
+}
+
+int syscall_pwrite(SYS_PWRITE_PARAMS *args) {
+ return vfs_pwrite(args->fd, args->buf, args->count, args->offset);
+}
+
+int syscall_write(int fd, const char *buf, size_t count) {
+ if (!get_vfs_fd(fd))
+ return -EBADF;
+ int rc = vfs_pwrite(fd, (char *)buf, count,
+ get_current_task()->file_descriptors[fd]->offset);
+ get_current_task()->file_descriptors[fd]->offset += rc;
+ return rc;
+}
+
+int syscall_dup2(SYS_DUP2_PARAMS *args) {
+ return vfs_dup2(args->org_fd, args->new_fd);
+}
+
+void syscall_exit(int status) {
+ exit(status);
+ assert(0);
+}
+
+void syscall_wait(int *status) {
+ asm("cli");
+ if (!get_current_task()->child) {
+ if (status)
+ *status = -1;
+ return;
+ }
+ if (get_current_task()->child->dead) {
+ if (status)
+ *status = get_current_task()->child_rc;
+ return;
+ }
+ get_current_task()->halts[WAIT_CHILD_HALT] = 1;
+ switch_task();
+ if (status)
+ *status = get_current_task()->child_rc;
+}
+
+int syscall_fork(void) { return fork(); }
+
+int syscall_getpid(void) { return get_current_task()->pid; }
+
+void *align_page(void *a);
+
+int syscall_brk(void *addr) {
+ void *end = get_current_task()->data_segment_end;
+ if (!mmu_allocate_region(end, addr - end, MMU_FLAG_RW, NULL))
+ return -ENOMEM;
+ get_current_task()->data_segment_end = align_page(addr);
+ return 0;
+}
+
+void *syscall_sbrk(uintptr_t increment) {
+ asm("cli");
+ void *rc = get_current_task()->data_segment_end;
+ void *n =
+ (void *)((uintptr_t)(get_current_task()->data_segment_end) + increment);
+ int rc2;
+ if (0 > (rc2 = syscall_brk(n)))
+ return (void *)rc2;
+ return rc;
+}
+
+int syscall_close(int fd) { return vfs_close(fd); }
+
+int syscall_openpty(SYS_OPENPTY_PARAMS *args) {
+ assert(is_valid_userpointer(args, sizeof(SYS_OPENPTY_PARAMS)));
+ return openpty(args->amaster, args->aslave, args->name, args->termp,
+ args->winp);
+}
+
+void (*syscall_functions[])() = {
+ (void(*))syscall_open, (void(*))syscall_read,
+ (void(*))syscall_write, (void(*))syscall_pread,
+ (void(*))syscall_pwrite, (void(*))syscall_fork,
+ (void(*))syscall_exec, (void(*))syscall_getpid,
+ (void(*))syscall_exit, (void(*))syscall_wait,
+ (void(*))syscall_brk, (void(*))syscall_sbrk,
+ (void(*))syscall_pipe, (void(*))syscall_dup2,
+ (void(*))syscall_close, (void(*))syscall_openpty,
+ (void(*))syscall_poll, (void(*))syscall_mmap,
+ (void(*))syscall_accept, (void(*))syscall_bind,
+ (void(*))syscall_socket, (void(*))syscall_shm_open,
+ (void(*))syscall_ftruncate, (void(*))syscall_stat,
+ (void(*))syscall_msleep, (void(*))syscall_uptime,
+ (void(*))syscall_mkdir, (void(*))syscall_recvfrom,
+ (void(*))syscall_sendto, (void(*))syscall_kill,
+ (void(*))syscall_sigaction,
+};
+
+void syscall_function_handler(uint32_t eax, uint32_t arg1, uint32_t arg2,
+ uint32_t arg3, uint32_t arg4, uint32_t arg5) {
+ assert(eax < sizeof(syscall_functions) / sizeof(syscall_functions[0]));
+ syscall_functions[eax](arg1, arg2, arg3, arg4, arg5);
+}
+
+extern void int_syscall(void);
+
+void syscalls_init(void) {
+ install_handler(int_syscall, INT_32_INTERRUPT_GATE(0x3), 0x80);
+}
diff --git a/kernel/cpu/syscall.h b/kernel/cpu/syscall.h
new file mode 100644
index 0000000..51d50f2
--- /dev/null
+++ b/kernel/cpu/syscall.h
@@ -0,0 +1,59 @@
+#include "idt.h"
+#include <stddef.h>
+#include <stdint.h>
+
+void syscalls_init(void);
+
+typedef struct SYS_OPEN_PARAMS {
+ char *file;
+ int flags;
+ int mode;
+} __attribute__((packed)) SYS_OPEN_PARAMS;
+
+typedef struct SYS_PREAD_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PREAD_PARAMS;
+
+typedef struct SYS_READ_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_READ_PARAMS;
+
+typedef struct SYS_PWRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+ size_t offset;
+} __attribute__((packed)) SYS_PWRITE_PARAMS;
+
+typedef struct SYS_WRITE_PARAMS {
+ int fd;
+ void *buf;
+ size_t count;
+} __attribute__((packed)) SYS_WRITE_PARAMS;
+
+typedef struct SYS_EXEC_PARAMS {
+ char *path;
+ char **argv;
+} __attribute__((packed)) SYS_EXEC_PARAMS;
+
+typedef struct SYS_WAIT_PARAMS {
+ int *status;
+} __attribute__((packed)) SYS_WAIT_PARAMS;
+
+typedef struct SYS_DUP2_PARAMS {
+ int org_fd;
+ int new_fd;
+} __attribute__((packed)) SYS_DUP2_PARAMS;
+
+typedef struct SYS_OPENPTY_PARAMS {
+ int *amaster;
+ int *aslave;
+ char *name;
+ /*const struct termios*/ void *termp;
+ /*const struct winsize*/ void *winp;
+} __attribute__((packed)) SYS_OPENPTY_PARAMS;