summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/gdt.c121
-rw-r--r--src/kernel/gdt.h15
-rw-r--r--src/kernel/idt.c75
-rw-r--r--src/kernel/idt.h3
-rw-r--r--src/kernel/isr.c24
-rw-r--r--src/kernel/isr.h15
-rw-r--r--src/kernel/main.c33
-rw-r--r--src/kernel/mem.c19
-rw-r--r--src/kernel/mem.h12
-rw-r--r--src/kernel/panic.h14
-rw-r--r--src/kernel/proc.c19
-rw-r--r--src/kernel/proc.h13
-rw-r--r--src/kernel/tty.c38
-rw-r--r--src/kernel/tty.h8
-rw-r--r--src/kernel/util.c9
-rw-r--r--src/kernel/util.h4
16 files changed, 422 insertions, 0 deletions
diff --git a/src/kernel/gdt.c b/src/kernel/gdt.c
new file mode 100644
index 0000000..b0d6320
--- /dev/null
+++ b/src/kernel/gdt.c
@@ -0,0 +1,121 @@
+#include <kernel/gdt.h>
+#include <kernel/util.h>
+#include <stdint.h>
+
+extern void stack_top; // platform/boot.s
+
+struct gdt_entry {
+ uint64_t limit_low : 16;
+ uint64_t base_low : 24;
+ uint64_t accessed : 1; // set by the processor
+ // CODE | DATA
+ uint64_t rw : 1; // readable? | writeable?
+ uint64_t conforming : 1; // conforming? | expands down?
+ uint64_t code : 1; // 1 | 0
+
+ uint64_t codeordata : 1; // 1 for everything other than TSS and LDT
+ uint64_t ring : 2;
+ uint64_t present : 1; // always 1
+ uint64_t limit_high : 4;
+ uint64_t available : 1; // ???
+ uint64_t long_mode : 1;
+ uint64_t x32 : 1;
+ uint64_t gran : 1; // 1 - 4kb, 0 - 1b
+ uint64_t base_high : 8;
+} __attribute__((packed));
+
+struct tss_entry {
+ uint32_t prev_tss; // unused
+ uint32_t esp0; // kernel mode stack pointer
+ uint32_t ss0; // kernel mode stack segment
+ // total size = 0x68 (?) - 3 * sizeof(uint32_t) = 5c
+ uint8_t _unused[0x5c];
+} __attribute__((packed));
+
+struct lgdt_arg {
+ uint16_t limit;
+ uint32_t base;
+} __attribute__((packed));
+
+static struct gdt_entry GDT[SEG_end];
+static struct tss_entry TSS;
+static struct lgdt_arg lgdt_arg; // probably doesn't need to be global
+
+static void gdt_prepare();
+static void gdt_load();
+
+
+static void gdt_prepare() {
+ GDT[SEG_null].present = 0;
+
+ GDT[SEG_r0data].limit_low = 0xFFFF;
+ GDT[SEG_r0data].limit_high = 0xF;
+ GDT[SEG_r0data].gran = 1; // 4KB * 0xFFFFF = (almost) 4GB
+ GDT[SEG_r0data].base_low = 0;
+ GDT[SEG_r0data].base_high = 0;
+ GDT[SEG_r0data].accessed = 0;
+ GDT[SEG_r0data].rw = 1;
+ GDT[SEG_r0data].conforming = 0;
+ GDT[SEG_r0data].code = 0;
+ GDT[SEG_r0data].codeordata = 1;
+ GDT[SEG_r0data].ring = 0;
+ GDT[SEG_r0data].present = 1;
+ GDT[SEG_r0data].long_mode = 0; // ???
+ GDT[SEG_r0data].available = 1; // ???
+ GDT[SEG_r0data].x32 = 1;
+
+ // copy to r0 code
+ GDT[SEG_r0code] = GDT[SEG_r0data];
+ GDT[SEG_r0code].code = 1;
+
+ GDT[SEG_r3data] = GDT[SEG_r0data];
+ GDT[SEG_r3data].ring = 3;
+ GDT[SEG_r3code] = GDT[SEG_r0code];
+ GDT[SEG_r3code].ring = 3;
+
+ // tss
+ memset(&TSS, 0, sizeof(TSS));
+ TSS.ss0 = SEG_r0data << 3; // kernel data segment
+ TSS.esp0 = (uint32_t) &stack_top;
+
+ GDT[SEG_TSS].limit_low = sizeof(TSS);
+ GDT[SEG_TSS].base_low = (uint32_t) &TSS;
+ GDT[SEG_TSS].accessed = 1; // 1 for TSS
+ GDT[SEG_TSS].rw = 0; // 1 busy / 0 not busy
+ GDT[SEG_TSS].conforming = 0; // 0 for TSS
+ GDT[SEG_TSS].code = 1; // 32bit
+ GDT[SEG_TSS].codeordata = 0; // is a system entry
+ GDT[SEG_TSS].ring = 3;
+ GDT[SEG_TSS].present = 1;
+ GDT[SEG_TSS].limit_high = (sizeof(TSS) >> 16) & 0xf;
+ GDT[SEG_TSS].available = 0; // 0 for TSS
+ GDT[SEG_TSS].long_mode = 0;
+ GDT[SEG_TSS].x32 = 0; // idk
+ GDT[SEG_TSS].gran = 0;
+ GDT[SEG_TSS].base_high = (((uint32_t) &TSS) >> 24) & 0xff;
+}
+
+void change_cs(int seg); // temporary
+
+static void gdt_load() {
+ lgdt_arg.limit = sizeof(GDT) - 1;
+ lgdt_arg.base = (uint32_t) &GDT;
+ asm("lgdt (%0)"
+ : : "r" (&lgdt_arg) : "memory");
+ asm("ltr %%ax"
+ : : "a" (SEG_TSS << 3 | 3) : "memory");
+
+ // update all segment registers
+ change_cs(SEG_r0code << 3);
+ asm("mov %0, %%ds;"
+ "mov %0, %%ss;"
+ "mov %0, %%es;"
+ "mov %0, %%fs;"
+ "mov %0, %%gs;"
+ : : "r" (SEG_r0data << 3) : "memory");
+}
+
+void gdt_init() {
+ gdt_prepare();
+ gdt_load();
+}
diff --git a/src/kernel/gdt.h b/src/kernel/gdt.h
new file mode 100644
index 0000000..9e5a6a5
--- /dev/null
+++ b/src/kernel/gdt.h
@@ -0,0 +1,15 @@
+#pragma once
+
+enum {
+ SEG_null,
+ // order dictated by SYSENTER
+ SEG_r0code,
+ SEG_r0data,
+ SEG_r3code,
+ SEG_r3data,
+ SEG_TSS,
+
+ SEG_end
+};
+
+void gdt_init();
diff --git a/src/kernel/idt.c b/src/kernel/idt.c
new file mode 100644
index 0000000..65f511f
--- /dev/null
+++ b/src/kernel/idt.c
@@ -0,0 +1,75 @@
+#include <kernel/gdt.h>
+#include <kernel/idt.h>
+#include <kernel/isr.h>
+#include <kernel/panic.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+struct idt_entry {
+ uint16_t offset_low ;
+ uint16_t code_seg ;
+ uint8_t zero ; // unused, has to be 0
+ uint8_t type : 4; // 16/32 bit, task/interrupt/task gate
+ uint8_t storage : 1; // 0 for interrupt/trap gates
+ uint8_t ring : 2;
+ uint8_t present : 1;
+ uint16_t offset_high ;
+} __attribute__((packed));
+
+// is exactly the same as lgdt_arg, i should combine them into a single struct
+// later
+struct lidt_arg {
+ uint16_t limit;
+ uint32_t base;
+} __attribute__((packed));
+
+
+static struct idt_entry IDT[256];
+static struct lidt_arg lidt_arg;
+
+static inline void idt_add(uint8_t num, bool user, void (*isr));
+static void idt_prepare();
+static void idt_load();
+static void idt_test();
+
+
+static inline void idt_add(uint8_t num, bool user, void (*isr)) {
+ uintptr_t offset = (uintptr_t) isr;
+
+ IDT[num] = (struct idt_entry) {
+ .offset_low = offset,
+ .offset_high = offset >> 16,
+ .code_seg = SEG_r0code << 3,
+ .zero = 0,
+ .present = 1,
+ .ring = user ? 3 : 0,
+ .storage = 0,
+ .type = 0xE, // 32-bit interrupt gate
+ };
+}
+
+static void idt_prepare() {
+ for (int i = 0; i < 256; i++)
+ IDT[i].present = 0;
+
+ idt_add(0x08, false, isr_double_fault);
+ idt_add(0x0d, false, isr_general_protection_fault);
+ idt_add(0x34, false, isr_test_interrupt);
+}
+
+static void idt_load() {
+ lidt_arg.limit = sizeof(IDT) - 1;
+ lidt_arg.base = (uintptr_t) &IDT;
+ asm("lidt (%0)" : : "r" (&lidt_arg) : "memory");
+}
+
+static void idt_test() {
+ asm("int $0x34" : : : "memory");
+ if (!isr_test_interrupt_called) panic();
+}
+
+void idt_init() {
+ idt_prepare();
+ idt_load();
+ idt_test();
+}
diff --git a/src/kernel/idt.h b/src/kernel/idt.h
new file mode 100644
index 0000000..5627657
--- /dev/null
+++ b/src/kernel/idt.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void idt_init();
diff --git a/src/kernel/isr.c b/src/kernel/isr.c
new file mode 100644
index 0000000..b715de0
--- /dev/null
+++ b/src/kernel/isr.c
@@ -0,0 +1,24 @@
+#include <kernel/isr.h>
+#include <kernel/panic.h>
+#include <kernel/tty.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+bool isr_test_interrupt_called = false;
+
+__attribute__((interrupt))
+void isr_double_fault(struct interrupt_frame *frame) {
+ tty_const("#DF");
+ panic();
+}
+
+__attribute__((interrupt))
+void isr_general_protection_fault(struct interrupt_frame *frame) {
+ tty_const("#GP");
+ panic();
+}
+
+__attribute__((interrupt))
+void isr_test_interrupt(struct interrupt_frame *frame) {
+ isr_test_interrupt_called = true;
+}
diff --git a/src/kernel/isr.h b/src/kernel/isr.h
new file mode 100644
index 0000000..150fc46
--- /dev/null
+++ b/src/kernel/isr.h
@@ -0,0 +1,15 @@
+#pragma once
+#include <stdbool.h>
+
+struct interrupt_frame;
+
+extern bool isr_test_interrupt_called; // used in the self-test in idt.c
+
+__attribute__((interrupt))
+void isr_double_fault(struct interrupt_frame *frame);
+
+__attribute__((interrupt))
+void isr_general_protection_fault(struct interrupt_frame *frame);
+
+__attribute__((interrupt))
+void isr_test_interrupt(struct interrupt_frame *frame);
diff --git a/src/kernel/main.c b/src/kernel/main.c
new file mode 100644
index 0000000..0193514
--- /dev/null
+++ b/src/kernel/main.c
@@ -0,0 +1,33 @@
+#include <kernel/gdt.h>
+#include <kernel/idt.h>
+#include <kernel/mem.h>
+#include <kernel/panic.h>
+#include <kernel/proc.h>
+#include <kernel/tty.h>
+#include <platform/asm.h>
+
+void r3_test();
+
+void kmain()
+{
+ tty_clear();
+ tty_const("gdt...");
+ gdt_init();
+ tty_const("idt...");
+ idt_init();
+ tty_const("sysenter...");
+ sysenter_setup();
+ tty_const("mem...");
+ mem_init();
+
+ tty_const("creating process...");
+ struct process *proc = process_new(r3_test);
+ tty_const("switching...");
+ process_switch(proc);
+}
+
+void r3_test() {
+ tty_const("ok");
+ asm("cli");
+ panic();
+}
diff --git a/src/kernel/mem.c b/src/kernel/mem.c
new file mode 100644
index 0000000..08eb285
--- /dev/null
+++ b/src/kernel/mem.c
@@ -0,0 +1,19 @@
+#include <kernel/mem.h>
+
+extern void *_kernel_end;
+static void *highest_page;
+
+void mem_init() {
+ highest_page = &_kernel_end;
+}
+
+void *page_alloc(size_t pages) {
+ void *bottom = highest_page;
+ highest_page += pages * PAGE_SIZE;
+ return bottom;
+}
+
+// frees `pages` consecutive pages starting from *first
+void page_free(void *first, size_t pages) {
+ // not implemented
+}
diff --git a/src/kernel/mem.h b/src/kernel/mem.h
new file mode 100644
index 0000000..e8e6a4f
--- /dev/null
+++ b/src/kernel/mem.h
@@ -0,0 +1,12 @@
+#pragma once
+#include <stddef.h>
+
+#define PAGE_SIZE 4096
+
+void mem_init();
+
+// allocates `pages` consecutive pages
+void *page_alloc(size_t pages);
+
+// frees `pages` consecutive pages starting from *first
+void page_free(void *first, size_t pages);
diff --git a/src/kernel/panic.h b/src/kernel/panic.h
new file mode 100644
index 0000000..7d2ea5f
--- /dev/null
+++ b/src/kernel/panic.h
@@ -0,0 +1,14 @@
+#pragma once
+#include <kernel/tty.h>
+#include <platform/asm.h>
+
+// dumb c shit
+#define panic_tostr2(x) #x
+#define panic_tostr(x) panic_tostr2(x)
+
+#define panic() do { \
+ tty_const(" PANIC! at the "); \
+ tty_const(__func__); \
+ tty_const(" (" __FILE__ ":" panic_tostr(__LINE__) ") "); \
+ halt_cpu(); \
+} while (0)
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
new file mode 100644
index 0000000..776238c
--- /dev/null
+++ b/src/kernel/proc.c
@@ -0,0 +1,19 @@
+#include <kernel/mem.h>
+#include <kernel/proc.h>
+#include <platform/asm.h>
+
+struct process *process_current;
+
+struct process *process_new(void *eip) {
+ struct process *proc;
+ proc = page_alloc(1); // TODO kmalloc
+ proc->stack_top = proc->esp = page_alloc(1) + 1 * PAGE_SIZE;
+ proc->eip = eip;
+
+ return proc;
+}
+
+void process_switch(struct process *proc) {
+ process_current = proc;
+ sysexit(proc->eip, proc->esp);
+}
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
new file mode 100644
index 0000000..976b275
--- /dev/null
+++ b/src/kernel/proc.h
@@ -0,0 +1,13 @@
+#pragma once
+
+struct process {
+ void *stack_top;
+ void *esp;
+
+ void *eip;
+};
+
+extern struct process *process_current;
+
+struct process *process_new(void *eip);
+void process_switch(struct process *proc);
diff --git a/src/kernel/tty.c b/src/kernel/tty.c
new file mode 100644
index 0000000..5c4c47d
--- /dev/null
+++ b/src/kernel/tty.c
@@ -0,0 +1,38 @@
+/* will be moved to userspace later on */
+
+#include <kernel/tty.h>
+
+struct vga_cell {
+ unsigned char c;
+ unsigned char style;
+} __attribute__((__packed__));
+
+static const size_t vga_len = 80 * 25;
+static struct vga_cell *vga = (void*) 0xB8000;
+static size_t vga_pos = 0;
+
+static void tty_scroll() {
+ for (int i = 0; i < vga_len - 80; i++) {
+ vga[i] = vga[i + 80];
+ }
+ vga_pos -= 80;
+}
+
+void tty_putchar(char c)
+{
+ if (vga_pos >= vga_len - 80) tty_scroll();
+ vga[vga_pos++].c = c;
+}
+
+void tty_write(const char *buf, size_t len)
+{
+ for (size_t i = 0; i < len; i++) {
+ tty_putchar(buf[i]);
+ }
+}
+
+void tty_clear() {
+ for (size_t i = 0; i < vga_len; i++)
+ vga[i].c = ' ';
+ vga_pos = 0;
+}
diff --git a/src/kernel/tty.h b/src/kernel/tty.h
new file mode 100644
index 0000000..3dc1525
--- /dev/null
+++ b/src/kernel/tty.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <stddef.h>
+
+void tty_putchar(char c);
+void tty_write(const char *buf, size_t len);
+void tty_clear();
+
+#define tty_const(str) tty_write(str, sizeof(str) - 1)
diff --git a/src/kernel/util.c b/src/kernel/util.c
new file mode 100644
index 0000000..da3ac9d
--- /dev/null
+++ b/src/kernel/util.c
@@ -0,0 +1,9 @@
+#include <kernel/util.h>
+#include <stdint.h>
+
+void *memset(void *s, int c, size_t n) {
+ uint8_t *s2 = s;
+ for (size_t i = 0; i < n; n++)
+ s2[i] = c;
+ return s;
+}
diff --git a/src/kernel/util.h b/src/kernel/util.h
new file mode 100644
index 0000000..688ac63
--- /dev/null
+++ b/src/kernel/util.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <stddef.h>
+
+void *memset(void *s, int c, size_t n);