aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/Makefile43
-rw-r--r--kernel/config.h14
-rw-r--r--kernel/drivers/cpu.c69
-rw-r--r--kernel/drivers/ide.c41
-rw-r--r--kernel/drivers/interrupts.asm142
-rw-r--r--kernel/drivers/interrupts.c231
-rw-r--r--kernel/drivers/keyboard.c68
-rw-r--r--kernel/drivers/serial.c32
-rw-r--r--kernel/drivers/timer.c39
-rw-r--r--kernel/drivers/vesa.c40
-rw-r--r--kernel/entry.asm470
-rw-r--r--kernel/features/fs.c184
-rw-r--r--kernel/features/gui.c68
-rw-r--r--kernel/features/load.c19
-rw-r--r--kernel/features/proc.asm25
-rw-r--r--kernel/features/proc.c104
-rw-r--r--kernel/features/psf.c58
-rw-r--r--kernel/features/syscall.c25
-rw-r--r--kernel/inc/boot.h10
-rw-r--r--kernel/inc/cpu.h29
-rw-r--r--kernel/inc/fs.h102
-rw-r--r--kernel/inc/gui.h22
-rw-r--r--kernel/inc/ide.h40
-rw-r--r--kernel/inc/interrupts.h95
-rw-r--r--kernel/inc/keyboard.h8
-rw-r--r--kernel/inc/load.h10
-rw-r--r--kernel/inc/proc.h30
-rw-r--r--kernel/inc/psf.h48
-rw-r--r--kernel/inc/serial.h9
-rw-r--r--kernel/inc/syscall.h8
-rw-r--r--kernel/inc/timer.h11
-rw-r--r--kernel/inc/vesa.h57
-rw-r--r--kernel/main.c50
33 files changed, 2201 insertions, 0 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644
index 0000000..bbf13af
--- /dev/null
+++ b/kernel/Makefile
@@ -0,0 +1,43 @@
+# MIT License, Copyright (c) 2020 Marvin Borner
+
+COBJS = main.o \
+ drivers/vesa.o \
+ drivers/cpu.o \
+ drivers/serial.o \
+ drivers/interrupts.o \
+ drivers/interrupts_asm.o \
+ drivers/keyboard.o \
+ drivers/ide.o \
+ drivers/timer.o \
+ features/fs.o \
+ features/psf.o \
+ features/gui.o \
+ features/load.o \
+ features/proc.o \
+ features/proc_asm.o \
+ features/syscall.o
+CC = ../cross/opt/bin/i686-elf-gcc
+LD = ../cross/opt/bin/i686-elf-ld
+OC = ../cross/opt/bin/i686-elf-objcopy
+AS = nasm
+
+# Flags to make the binary smaller TODO: Remove after indirect pointer support!
+CSFLAGS = -mpreferred-stack-boundary=2 -fno-asynchronous-unwind-tables -Os
+
+CFLAGS = $(CSFLAGS) -Wall -Wextra -nostdlib -nostdinc -ffreestanding -fno-builtin -mgeneral-regs-only -std=c99 -m32 -pedantic-errors -Wl,-ekernel_main -I../lib/inc/ -Iinc/
+
+ASFLAGS = -f elf32 -O3
+
+all: compile
+
+%.o: %.c
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+%_asm.o: %.asm
+ @$(AS) $(ASFLAGS) $< -o $@
+
+compile: $(COBJS)
+ @mkdir -p ../build/
+ @$(AS) -f bin entry.asm -o ../build/boot.bin
+ @$(LD) -N -ekernel_main -Ttext 0x00050000 -o ../build/kernel.bin -L../build/ $+ -lc --oformat binary
+ @$(CC) $(CFLAGS) -o ../build/debug.o -L../build/ $+ -lc
diff --git a/kernel/config.h b/kernel/config.h
new file mode 100644
index 0000000..29c0e23
--- /dev/null
+++ b/kernel/config.h
@@ -0,0 +1,14 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// General kernel config
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+int MELVIX_VERSION = 0;
+
+#define USERNAME "Neo"
+//#define FONT_PATH "/font/ter-p32n.psf"
+#define FONT_PATH "/font/spleen-16x32.psfu"
+#define NETWORK "rtl8139"
+
+#endif
diff --git a/kernel/drivers/cpu.c b/kernel/drivers/cpu.c
new file mode 100644
index 0000000..5c27c51
--- /dev/null
+++ b/kernel/drivers/cpu.c
@@ -0,0 +1,69 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// This file is a wrapper around some CPU asm calls
+
+#include <def.h>
+
+u8 inb(u16 port)
+{
+ u8 value;
+ __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+u16 inw(u16 port)
+{
+ u16 value;
+ __asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+u32 inl(u16 port)
+{
+ u32 value;
+ __asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+void insl(u16 port, void *addr, int n)
+{
+ __asm__ volatile("cld; rep insl"
+ : "=D"(addr), "=c"(n)
+ : "d"(port), "0"(addr), "1"(n)
+ : "memory", "cc");
+}
+
+void outb(u16 port, u8 data)
+{
+ __asm__ volatile("outb %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void outw(u16 port, u16 data)
+{
+ __asm__ volatile("outw %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void outl(u16 port, u32 data)
+{
+ __asm__ volatile("outl %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void cli()
+{
+ __asm__ volatile("cli");
+}
+
+void sti()
+{
+ __asm__ volatile("sti");
+}
+
+void hlt()
+{
+ __asm__ volatile("hlt");
+}
+
+void idle()
+{
+ while (1)
+ hlt();
+}
diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c
new file mode 100644
index 0000000..fe3955c
--- /dev/null
+++ b/kernel/drivers/ide.c
@@ -0,0 +1,41 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <def.h>
+#include <ide.h>
+
+int ide_wait(int check)
+{
+ char r;
+
+ // Wait while drive is busy. Once just ready is set, exit the loop
+ while (((r = (char)inb(IDE_IO | IDE_CMD)) & (IDE_BUSY | IDE_READY)) != IDE_READY)
+ ;
+
+ // Check for errors
+ if (check && (r & (IDE_DRIVE_FAULT | IDE_ERROR)) != 0)
+ return 0xF;
+ return 0;
+}
+
+void *ide_read(void *b, u32 block)
+{
+ int sector_per_block = BLOCK_SIZE / SECTOR_SIZE; // 2
+ int sector = block * sector_per_block;
+
+ ide_wait(0);
+ outb(IDE_IO | IDE_SECTOR_COUNT, sector_per_block); // Number of sectors
+ outb(IDE_IO | IDE_LOW, LBA_LOW(sector));
+ outb(IDE_IO | IDE_MID, LBA_MID(sector));
+ outb(IDE_IO | IDE_HIGH, LBA_HIGH(sector));
+
+ // Slave/Master << 4 and last 4 bits
+ outb(IDE_IO | IDE_HEAD, 0xE0 | (1 << 4) | LBA_LAST(sector));
+ outb(IDE_IO | IDE_CMD, IDE_CMD_READ);
+ ide_wait(0);
+
+ // Read-only
+ insl(IDE_IO, b, BLOCK_SIZE / 4);
+
+ return b;
+}
diff --git a/kernel/drivers/interrupts.asm b/kernel/drivers/interrupts.asm
new file mode 100644
index 0000000..59c323c
--- /dev/null
+++ b/kernel/drivers/interrupts.asm
@@ -0,0 +1,142 @@
+; MIT License, Copyright (c) 2020 Marvin Borner
+
+; IRQ
+
+%macro IRQ 2
+ global irq%1
+ irq%1:
+ cli
+ push byte 0
+ push byte %2
+ jmp irq_common_stub
+%endmacro
+
+IRQ 0, 32
+IRQ 1, 33
+IRQ 2, 34
+IRQ 3, 35
+IRQ 4, 36
+IRQ 5, 37
+IRQ 6, 38
+IRQ 7, 39
+IRQ 8, 40
+IRQ 9, 41
+IRQ 10, 42
+IRQ 11, 43
+IRQ 12, 44
+IRQ 13, 45
+IRQ 14, 46
+IRQ 15, 47
+
+extern irq_handler
+irq_common_stub:
+ pusha
+
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ cld
+
+ push esp
+ call irq_handler
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ add esp, 8
+ sti
+ iret
+
+; ISR
+
+%macro ISR_NOERRCODE 1
+ global isr%1
+ isr%1:
+ cli
+ push byte 0
+ push %1
+ jmp isr_common_stub
+%endmacro
+
+%macro ISR_ERRCODE 1
+ global isr%1
+ isr%1:
+ cli
+ push byte %1
+ jmp isr_common_stub
+%endmacro
+
+ISR_NOERRCODE 0
+ISR_NOERRCODE 1
+ISR_NOERRCODE 2
+ISR_NOERRCODE 3
+ISR_NOERRCODE 4
+ISR_NOERRCODE 5
+ISR_NOERRCODE 6
+ISR_NOERRCODE 7
+ISR_ERRCODE 8
+ISR_NOERRCODE 9
+ISR_ERRCODE 10
+ISR_ERRCODE 11
+ISR_ERRCODE 12
+ISR_ERRCODE 13
+ISR_ERRCODE 14
+ISR_NOERRCODE 15
+ISR_NOERRCODE 16
+ISR_NOERRCODE 17
+ISR_NOERRCODE 18
+ISR_NOERRCODE 19
+ISR_NOERRCODE 20
+ISR_NOERRCODE 21
+ISR_NOERRCODE 22
+ISR_NOERRCODE 23
+ISR_NOERRCODE 24
+ISR_NOERRCODE 25
+ISR_NOERRCODE 26
+ISR_NOERRCODE 27
+ISR_NOERRCODE 28
+ISR_NOERRCODE 29
+ISR_NOERRCODE 30
+ISR_NOERRCODE 31
+ISR_NOERRCODE 128
+
+extern isr_handler
+isr_common_stub:
+ pusha
+
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ cld
+
+ push esp
+ call isr_handler
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ add esp, 8
+ sti
+ iret
diff --git a/kernel/drivers/interrupts.c b/kernel/drivers/interrupts.c
new file mode 100644
index 0000000..0b94208
--- /dev/null
+++ b/kernel/drivers/interrupts.c
@@ -0,0 +1,231 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// TODO: Remove some magic numbers
+
+#include <cpu.h>
+#include <def.h>
+#include <interrupts.h>
+#include <mem.h>
+#include <print.h>
+#include <serial.h>
+
+/**
+ * IDT
+ */
+
+void idt_set_gate(u8 num, u32 base, u16 sel, u8 flags)
+{
+ // Specify the interrupt routine's base address
+ idt[num].base_low = (u16)(base & 0xFFFF);
+ idt[num].base_high = (u16)((base >> 16) & 0xFFFF);
+
+ // Set selector/segment of IDT entry
+ idt[num].sel = sel;
+ idt[num].always0 = 0;
+ idt[num].flags = (u8)(flags | 0x60);
+}
+
+// Install IDT
+void idt_install()
+{
+ // Set IDT pointer and limit
+ idt_ptr.limit = (sizeof(struct idt_entry) * 256) - 1;
+ idt_ptr.base = &idt;
+
+ // Clear IDT by setting memory cells to 0
+ memset(&idt, 0, sizeof(struct idt_entry) * 256);
+
+ __asm__("lidt %0" : : "m"(idt_ptr));
+}
+
+/**
+ * IRQ
+ */
+
+void (*irq_routines[16])(struct regs *) = { 0 };
+
+// Install IRQ handler
+void irq_install_handler(int irq, void (*handler)(struct regs *r))
+{
+ irq_routines[irq] = handler;
+}
+
+// Remove IRQ handler
+void irq_uninstall_handler(int irq)
+{
+ irq_routines[irq] = 0;
+}
+
+// Remap the IRQ table
+void irq_remap()
+{
+ outb(0x20, 0x11);
+ outb(0xA0, 0x11);
+ outb(0x21, 0x20);
+ outb(0xA1, 0x28);
+ outb(0x21, 0x04);
+ outb(0xA1, 0x02);
+ outb(0x21, 0x01);
+ outb(0xA1, 0x01);
+ outb(0x21, 0x00);
+ outb(0xA1, 0x00);
+}
+
+// Handle IRQ ISRs
+void irq_handler(struct regs *r)
+{
+ void (*handler)(struct regs * r);
+
+ // Execute custom handler if exists
+ handler = irq_routines[r->int_no - 32];
+ if (handler)
+ handler(r);
+
+ // Send EOI to second (slave) PIC
+ if (r->int_no >= 40)
+ outb(0xA0, 0x20);
+
+ // Send EOI to master PIC
+ outb(0x20, 0x20);
+}
+
+// Map ISRs to the correct entries in the IDT
+void irq_install()
+{
+ irq_remap();
+
+ idt_set_gate(32, (u32)irq0, 0x08, 0x8E);
+ idt_set_gate(33, (u32)irq1, 0x08, 0x8E);
+ idt_set_gate(34, (u32)irq2, 0x08, 0x8E);
+ idt_set_gate(35, (u32)irq3, 0x08, 0x8E);
+ idt_set_gate(36, (u32)irq4, 0x08, 0x8E);
+ idt_set_gate(37, (u32)irq5, 0x08, 0x8E);
+ idt_set_gate(38, (u32)irq6, 0x08, 0x8E);
+ idt_set_gate(39, (u32)irq7, 0x08, 0x8E);
+
+ idt_set_gate(40, (u32)irq8, 0x08, 0x8E);
+ idt_set_gate(41, (u32)irq9, 0x08, 0x8E);
+ idt_set_gate(42, (u32)irq10, 0x08, 0x8E);
+ idt_set_gate(43, (u32)irq11, 0x08, 0x8E);
+ idt_set_gate(44, (u32)irq12, 0x08, 0x8E);
+ idt_set_gate(45, (u32)irq13, 0x08, 0x8E);
+ idt_set_gate(46, (u32)irq14, 0x08, 0x8E);
+ idt_set_gate(47, (u32)irq15, 0x08, 0x8E);
+}
+
+/**
+ * ISR
+ */
+
+void (*isr_routines[256])(struct regs *) = { 0 };
+
+const char *isr_exceptions[32] = { "Division By Zero",
+ "Debug",
+ "Non Maskable Interrupt",
+ "Breakpoint",
+ "Into Detected Overflow",
+ "Out of Bounds",
+ "Invalid Opcode",
+ "No Coprocessor",
+
+ "Double Fault",
+ "Coprocessor Segment Overrun",
+ "Bad TSS",
+ "Segment Not Present",
+ "Stack Fault",
+ "General Protection Fault",
+ "Page Fault",
+ "Unknown Interrupt",
+
+ "Coprocessor Fault",
+ "Alignment Check",
+ "Machine Check",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved" };
+
+void isr_install_handler(int isr, void (*handler)(struct regs *r))
+{
+ isr_routines[isr] = handler;
+}
+
+void isr_uninstall_handler(int isr)
+{
+ isr_routines[isr] = 0;
+}
+
+void isr_handler(struct regs *r)
+{
+ void (*handler)(struct regs * r);
+
+ // Execute fault handler if exists
+ handler = isr_routines[r->int_no];
+ if (handler) {
+ handler(r);
+ } else if (r->int_no <= 32) {
+ cli();
+ printf("\n%s Exception, halting!\n", isr_exceptions[r->int_no]);
+ printf("Error code: %d\n", r->err_code);
+ while (1) {
+ };
+ }
+}
+
+void isr_install()
+{
+ idt_set_gate(0, (u32)isr0, 0x08, 0x8E);
+ idt_set_gate(1, (u32)isr1, 0x08, 0x8E);
+ idt_set_gate(2, (u32)isr2, 0x08, 0x8E);
+ idt_set_gate(3, (u32)isr3, 0x08, 0x8E);
+ idt_set_gate(4, (u32)isr4, 0x08, 0x8E);
+ idt_set_gate(5, (u32)isr5, 0x08, 0x8E);
+ idt_set_gate(6, (u32)isr6, 0x08, 0x8E);
+ idt_set_gate(7, (u32)isr7, 0x08, 0x8E);
+
+ idt_set_gate(8, (u32)isr8, 0x08, 0x8E);
+ idt_set_gate(9, (u32)isr9, 0x08, 0x8E);
+ idt_set_gate(10, (u32)isr10, 0x08, 0x8E);
+ idt_set_gate(11, (u32)isr11, 0x08, 0x8E);
+ idt_set_gate(12, (u32)isr12, 0x08, 0x8E);
+ idt_set_gate(13, (u32)isr13, 0x08, 0x8E);
+ idt_set_gate(14, (u32)isr14, 0x08, 0x8E);
+ idt_set_gate(15, (u32)isr15, 0x08, 0x8E);
+
+ idt_set_gate(16, (u32)isr16, 0x08, 0x8E);
+ idt_set_gate(17, (u32)isr17, 0x08, 0x8E);
+ idt_set_gate(18, (u32)isr18, 0x08, 0x8E);
+ idt_set_gate(19, (u32)isr19, 0x08, 0x8E);
+ idt_set_gate(20, (u32)isr20, 0x08, 0x8E);
+ idt_set_gate(21, (u32)isr21, 0x08, 0x8E);
+ idt_set_gate(22, (u32)isr22, 0x08, 0x8E);
+ idt_set_gate(23, (u32)isr23, 0x08, 0x8E);
+
+ idt_set_gate(24, (u32)isr24, 0x08, 0x8E);
+ idt_set_gate(25, (u32)isr25, 0x08, 0x8E);
+ idt_set_gate(26, (u32)isr26, 0x08, 0x8E);
+ idt_set_gate(27, (u32)isr27, 0x08, 0x8E);
+ idt_set_gate(28, (u32)isr28, 0x08, 0x8E);
+ idt_set_gate(29, (u32)isr29, 0x08, 0x8E);
+ idt_set_gate(30, (u32)isr30, 0x08, 0x8E);
+ idt_set_gate(31, (u32)isr31, 0x08, 0x8E);
+}
+
+/**
+ * Combined
+ */
+void interrupts_install()
+{
+ idt_install();
+ isr_install();
+ irq_install();
+}
diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c
new file mode 100644
index 0000000..38ff1f7
--- /dev/null
+++ b/kernel/drivers/keyboard.c
@@ -0,0 +1,68 @@
+#include <cpu.h>
+#include <def.h>
+#include <gui.h>
+#include <interrupts.h>
+
+char keymap[128];
+
+// TODO: Use keyboard as event and move logic to other file
+void keyboard_handler()
+{
+ u8 scan_code = inb(0x60);
+
+ if (scan_code > 128)
+ return;
+
+ if ((scan_code & 0x80) == 0) { // PRESS
+ gui_term_write_char(keymap[scan_code]);
+ }
+}
+
+void keyboard_acknowledge()
+{
+ while (inb(0x60) != 0xfa)
+ ;
+}
+
+void keyboard_rate()
+{
+ outb(0x60, 0xF3);
+ keyboard_acknowledge();
+ outb(0x60, 0x0); // Rate{00000} Delay{00} 0
+}
+
+void keyboard_install()
+{
+ //keyboard_rate(); TODO: Fix keyboard rate?
+ irq_install_handler(1, keyboard_handler);
+}
+
+char keymap[128] = {
+ 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',
+ '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
+ '\n', 17, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
+ 14, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 14, '*',
+ 0, // Alt key
+ ' ', // Space bar
+ 15, // Caps lock
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F keys
+ 0, // Num lock
+ 0, // Scroll lock
+ 0, // Home key
+ 0, // Up arrow
+ 0, // Page up
+ '-',
+ 0, // Left arrow
+ 0,
+ 0, // Right arrow
+ '+',
+ 0, // End key
+ 0, // Down arrow
+ 0, // Page down
+ 0, // Insert key
+ 0, // Delete key
+ 0, 0, 0,
+ 0, // F11
+ 0, // F12
+ 0, // Other keys
+};
diff --git a/kernel/drivers/serial.c b/kernel/drivers/serial.c
new file mode 100644
index 0000000..dcee4dd
--- /dev/null
+++ b/kernel/drivers/serial.c
@@ -0,0 +1,32 @@
+#include <cpu.h>
+#include <def.h>
+#include <str.h>
+
+void serial_install()
+{
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x80);
+ outb(0x3f8 + 0, 0x03);
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x03);
+ outb(0x3f8 + 2, 0xC7);
+ outb(0x3f8 + 4, 0x0B);
+}
+
+int is_transmit_empty()
+{
+ return inb(0x3f8 + 5) & 0x20;
+}
+
+void serial_put(char ch)
+{
+ while (is_transmit_empty() == 0)
+ ;
+ outb(0x3f8, (u8)ch);
+}
+
+void serial_print(const char *data)
+{
+ for (u32 i = 0; i < strlen(data); i++)
+ serial_put(data[i]);
+}
diff --git a/kernel/drivers/timer.c b/kernel/drivers/timer.c
new file mode 100644
index 0000000..a3b4137
--- /dev/null
+++ b/kernel/drivers/timer.c
@@ -0,0 +1,39 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <def.h>
+#include <interrupts.h>
+
+static u32 timer_ticks = 0;
+
+void timer_phase(int hz)
+{
+ int divisor = 3579545 / 3 / hz;
+ outb(0x43, 0x36); // 01 10 11 0b // CTR, RW, MODE, BCD
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, divisor >> 8);
+}
+
+// Executed 1000 times per second
+void timer_handler()
+{
+ timer_ticks++;
+}
+
+// "Delay" function with CPU sleep
+void timer_wait(u32 ticks)
+{
+ u32 eticks;
+
+ eticks = timer_ticks + ticks;
+ while (timer_ticks < eticks) {
+ __asm__("sti//hlt//cli");
+ }
+}
+
+// Install timer handler into IRQ0
+void timer_install()
+{
+ timer_phase(1000);
+ irq_install_handler(0, timer_handler);
+}
diff --git a/kernel/drivers/vesa.c b/kernel/drivers/vesa.c
new file mode 100644
index 0000000..9402664
--- /dev/null
+++ b/kernel/drivers/vesa.c
@@ -0,0 +1,40 @@
+#include <def.h>
+#include <vesa.h>
+
+void vesa_draw_rectangle(int x1, int y1, int x2, int y2, const u32 color[3])
+{
+ int pos1 = x1 * vbe_bpl + y1 * vbe_pitch;
+ u8 *draw = &fb[pos1];
+ for (int i = 0; i <= y2 - y1; i++) {
+ for (int j = 0; j <= x2 - x1; j++) {
+ draw[vbe_bpl * j] = color[2];
+ draw[vbe_bpl * j + 1] = color[1];
+ draw[vbe_bpl * j + 2] = color[0];
+ }
+ draw += vbe_pitch;
+ }
+}
+
+void vesa_set_pixel(u16 x, u16 y, const u32 color[3])
+{
+ u8 pos = x * vbe_bpl + y * vbe_pitch;
+ u8 *draw = &fb[pos];
+ draw[pos] = (char)color[2];
+ draw[pos + 1] = (char)color[1];
+ draw[pos + 2] = (char)color[0];
+}
+
+void vesa_fill(const u32 color[3])
+{
+ vesa_draw_rectangle(0, 0, vbe->width - 1, vbe->height - 1, color);
+}
+
+void vesa_init(struct vbe *info)
+{
+ vbe = info;
+ vbe_height = vbe->height;
+ vbe_width = vbe->width;
+ vbe_bpl = vbe->bpp >> 3;
+ vbe_pitch = vbe->pitch;
+ fb = (u8 *)vbe->framebuffer;
+}
diff --git a/kernel/entry.asm b/kernel/entry.asm
new file mode 100644
index 0000000..ac70091
--- /dev/null
+++ b/kernel/entry.asm
@@ -0,0 +1,470 @@
+; Melvin's awesome ext2 bootloader
+; I'm not really good in assembly, there are MANY ways to improve this!
+; MIT License, Copyright (c) 2020 Marvin Borner
+
+; Definitions
+
+; General configurations
+; TODO: Find out why 2560x1600 doesn't work
+%define VIDEO_WIDTH 1920
+%define VIDEO_HEIGHT 1200
+%define VIDEO_BPP 4
+
+; Boot constants
+%define LOCATION 0x7c00 ; Bootloader location
+%define SECTOR_END 0xaa55 ; Bootsector end signature
+%define SECTOR_SIZE 510 ; 512 bytes minus signature
+
+; Interrupts
+%define VIDEO_INT 0x10 ; Video BIOS Interrupt
+%define DISK_INT 0x13 ; Disk BIOS Interrupt
+%define MISC_INT 0x15 ; Miscellaneous services BIOS Interrupt
+
+; Characters
+%define NEWLINE 0x0A ; Newline character (\n)
+%define RETURN 0x0D ; Return character (\r)
+%define NULL 0x00 ; NULL character (\0)
+
+; Video commands (VGA)
+%define VIDEO_CLEAR 0x03 ; Clear screen command
+%define VIDEO_OUT 0x0e ; Teletype output command
+
+; Disk commands
+%define DISK_EXT_CHECK 0x41 ; Disk extension check command
+%define DISK_EXT_CHECK_SIG1 0x55aa ; First extension check signature
+%define DISK_EXT_CHECK_SIG2 0xaa55 ; Second extension check signature
+%define DISK_ZERO 0x80 ; First disk - TODO: Disk detection
+%define DISK_READ 0x42 ; Disk extended read command
+
+; EXT2 constants
+%define EXT2_SB_SIZE 0x400 ; Superblock size
+%define EXT2_SIG_OFFSET 0x38 ; Signature offset in superblock
+%define EXT2_TABLE_OFFSET 0x08 ; Inode table offset after superblock
+%define EXT2_INODE_TABLE_LOC 0x1000 ; New inode table location in memory
+%define EXT2_KERNEL_INODE 0x05 ; Kernel inode
+%define EXT2_INODE_SIZE 0x80 ; Single inode size
+%define EXT2_GET_ADDRESS(inode) (EXT2_INODE_TABLE_LOC + (inode - 1) * EXT2_INODE_SIZE)
+%define EXT2_COUNT_OFFSET 0x1c ; Inode offset of number of data blocks
+%define EXT2_POINTER_OFFSET 0x28 ; Inode offset of first data pointer
+%define EXT2_IND_POINTER_OFFSET 0x2c ; Inode offset of singly indirect data pointer
+%define EXT2_DIRECT_POINTER_COUNT 0x0c ; Direct pointer count
+%define EXT2_SIG 0xef53 ; Signature
+
+; Video constants (VESA)
+%define VESA_START 0x2000 ; Struct starts at 0x2000
+%define VESA_END 0x3000 ; Struct ends at 0x3000
+%define VESA_GET_MODES 0x4f00 ; Get video modes (via 10h)
+%define VESA_GET_INFO 0x4f01 ; Get video mode info (via 10h)
+%define VESA_SET_MODE 0x4f02 ; Set video mode (via 10h)
+%define VESA_SUCCESS_SIG 0x004f ; Returns if VBE call succeeded
+%define VESA_MODE_OFFSET 0xe ; Offset to mode pointer
+%define VESA_MODE_SEGMENT 0x10 ; Mode pointer segment
+%define VESA_LIST_END 0xffff ; End of mode list
+%define VESA_PITCH_OFFSET 0x10 ; Pitch offset in mode info
+%define VESA_WIDTH_OFFSET 0x12 ; Width offset in mode info
+%define VESA_HEIGHT_OFFSET 0x14 ; Height offset in mode info
+%define VESA_BPP_OFFSET 0x19 ; Bytes Per Pixel (BPP) offset in mode info
+%define VESA_FRAMEBUFFER_OFFSET 0x2a ; Framebuffer offset in mode info
+%define VESA_LFB_FLAG 0x4000 ; Enable LFB flag
+
+; A20 constants
+%define A20_GATE 0x92 ; Fast A20 gate
+%define A20_ENABLED 0b10 ; Bit 1 defines whether A20 is enabled
+%define A20_EXCLUDE_BIT 0xfe ; Bit 0 may be write-only, causing a crash
+
+; GDT constants (bitmap)
+%define GDT_MAX_LIMIT 0xffff ; I just use the max limit lel
+%define GDT_PRESENT 0b10000000 ; Is present
+%define GDT_RING3 0b01100000 ; Privilege level 3
+%define GDT_DESCRIPTOR 0b00010000 ; Descriptor type, set for code/data
+%define GDT_EXECUTABLE 0b00001000 ; Can be executed
+%define GDT_READWRITE 0b00000010 ; Read/write access for code/data
+%define GDT_ACCESSED 0b00000001 ; Whether segment is accessed
+%define GDT_GRANULARITY (0x80 | 0x00) ; Page granularity (4KiB)
+%define GDT_SIZE (0x40 | 0x00) ; Use 32 bit selectors
+%define GDT_DATA_OFFSET 0x10 ; Offset to GDT data segment
+
+; Kernel constants
+%define STACK_POINTER 0x00900000 ; The initial stack pointer in kernel mode
+%define KERNEL_POSITION 0x00050000 ; Loaded kernel position in protected mode (* 0x10)
+
+; ENOUGH, let's go!
+
+bits 16
+org LOCATION
+
+; This is the first stage. It prints some things, checks some things
+; and jumps to the second stage. Nothing special.
+global _start
+_start:
+ ; Clear screen
+ mov ax, VIDEO_CLEAR
+ int VIDEO_INT
+
+ ; Check LBA support
+ mov ah, DISK_EXT_CHECK
+ mov bx, DISK_EXT_CHECK_SIG1
+ int DISK_INT
+ jc lba_error
+ cmp bx, DISK_EXT_CHECK_SIG2
+ jnz lba_error
+
+ ; Check disk and move dl
+ and dl, DISK_ZERO ; Use disk 0
+ jz disk_error
+ mov [drive], dl
+
+ ; Load stage two
+ mov bx, stage_two
+ mov [dest], bx
+ call disk_read
+
+ ; JUMP
+ jmp stage_two
+
+print:
+ push bx
+ push ax
+ mov ah, VIDEO_OUT
+ xor bh, bh
+ print_ch:
+ lodsb
+ test al, al
+ jz print_end
+ int VIDEO_INT
+ jmp print_ch
+ print_end:
+ pop ax
+ pop bx
+ ret
+
+disk_read:
+ mov si, packet ; Address of dap
+ mov ah, DISK_READ ; Extended read
+ mov dl, [drive] ; Drive number
+ int DISK_INT
+ jc disk_error
+ ret
+
+; Errors
+disk_error:
+ mov si, disk_error_msg
+ call print
+ jmp $
+lba_error:
+ mov si, lba_error_msg
+ call print
+ jmp $
+
+; Now put some data and routines just before the end of the boot sector because
+; we've still got some space left :)
+
+; Video map routine
+video_map:
+ mov bx, VESA_START ; Set load address
+ mov di, bx
+ mov ax, VESA_GET_MODES ; Get video modes
+ int VIDEO_INT ; Ask BIOS for data!
+
+ cmp ax, VESA_SUCCESS_SIG ; Check VBE support in response
+ jne .error ; Not supported :(
+
+ mov si, [bx + VESA_MODE_OFFSET] ; Mode pointer offset
+ mov ax, [bx + VESA_MODE_SEGMENT] ; Mode pointer segment
+ mov es, ax
+
+ mov di, VESA_END ; End of VBE struct
+.loop:
+ mov bx, [es:si] ; Load bx with video mode
+ cmp bx, VESA_LIST_END ; Is this the end?
+ jae .done ; Yes, there aren't any modes left
+
+ add si, 2
+ mov [.mode], bx
+
+ mov ax, VESA_GET_INFO ; Get mode information
+ mov cx, [.mode] ; Save in here
+ int VIDEO_INT ; BIOS interrupt!
+ cmp ax, VESA_SUCCESS_SIG ; Check if call succeeded
+ jne .error ; Nope, jump to error!
+
+ mov ax, [es:di + VESA_FRAMEBUFFER_OFFSET] ; Save framebuffer
+ mov [.framebuffer], ax ; Move fb address to struct
+
+ mov ax, [es:di + VESA_PITCH_OFFSET] ; Save pitch
+ mov bx, [es:di + VESA_WIDTH_OFFSET] ; Save width
+ mov cx, [es:di + VESA_HEIGHT_OFFSET] ; Save height
+ mov dx, [es:di + VESA_BPP_OFFSET] ; Save BPP
+
+ mov [.bpp], dx ; Move bpp to struct (bigger bpp is always desired)
+ add di, 0x100
+
+ cmp ax, [.pitch] ; Compare with desired pitch
+ jne .loop ; Not equal, continue search!
+ cmp bx, [.width] ; Compare with desired height
+ jne .loop ; Not equal, continue search!
+ cmp cx, [.height] ; Compare with desired height
+ jne .loop ; Not equal, continue search!
+
+ lea ax, [es:di - 0x100]
+ mov [vid_info.array], ax
+.set_mode:
+ mov ax, VESA_SET_MODE ; Set VBE mode
+ mov bx, [.mode] ; Set mode address
+ mov [vid_info], bx ; Move mode information to array
+ or bx, VESA_LFB_FLAG ; Enable LFB
+ int VIDEO_INT ; SET!
+ cmp ax, VESA_SUCCESS_SIG ; Check if set succeeded
+ jne .error ; Nope, jump to error!
+.done:
+ ret ; Finished loop and set!
+.error: ; Something failed - print message and loop!
+ mov si, video_error_msg
+ call print
+ jmp $
+
+; Video default data
+.mode dw 0
+.width dw VIDEO_WIDTH
+.height dw VIDEO_HEIGHT
+.pitch dw (VIDEO_WIDTH * VIDEO_BPP)
+.bpp dw VIDEO_BPP
+.framebuffer dd 0
+
+; Variables
+disk_error_msg db "Disk error!", NEWLINE, RETURN, NULL
+lba_error_msg db "LBA error!", NEWLINE, RETURN, NULL
+video_error_msg db "Video error!", NEWLINE, RETURN, NULL
+drive db 0
+
+; Video info struct
+vid_info:
+.mode dd 0 ; Mode info pointer
+.array dd 0 ; Mode array pointer
+
+; Data
+packet:
+ db 0x10 ; Packet size
+ db 0 ; Always 0
+count:
+ dw 4 ; Number of sectors to transfer
+dest:
+ dw 0 ; Destination offset
+ dw 0 ; Destination segment
+lba:
+ dd 1 ; LBA number
+ dd 0 ; More storage bytes
+
+; End of boot sector
+times SECTOR_SIZE - ($ - $$) db 0
+dw SECTOR_END
+
+; This is the second stage. It tries to load the kernel (inode 5) into memory.
+; To do this, it first checks the integrity of the ext2 fs. Then it has to find
+; the address of the fifth inode and load its contents into memory.
+; After this is finished, the stage can jump into the protected mode, enable the
+; A20 line and finally jump to the kernel! ez
+stage_two:
+ ; Verify signature
+ mov ax, [superblock + EXT2_SIG_OFFSET]
+ cmp ax, EXT2_SIG
+ jne disk_error
+
+ ; Load inode table
+ mov ax, [superblock + EXT2_SB_SIZE + EXT2_TABLE_OFFSET] ; Inode table
+ shl ax, 1 ; Multiply ax by 2
+ mov [lba], ax ; Sector
+ mov ax, 2
+ mov [count], ax ; Read 1024 bytes
+ mov bx, EXT2_INODE_TABLE_LOC ; Copy data to 0x1000
+ mov [dest], bx
+ call disk_read
+
+ ; Load kernel
+ mov bx, EXT2_GET_ADDRESS(EXT2_KERNEL_INODE) ; First block
+ mov cx, [bx + EXT2_COUNT_OFFSET] ; Number of blocks for inode
+ lea di, [bx + EXT2_POINTER_OFFSET] ; Address of first block pointer
+ mov bx, 0x5000 ; Load to this address
+ mov [dest + 2], bx
+ mov bx, 0 ; Inode location = 0xF0000
+ mov [dest], bx
+ call kernel_load
+
+ ; Set video mode
+ call video_map
+
+ jmp protected_mode_enter
+
+kernel_load:
+ ; TODO: Add singly pointer support (until ~12KiB)
+ ;cmp cx, EXT2_DIRECT_POINTER_COUNT ; Indirect pointer needed?
+ ;jge .indirect ; Singly indirect pointer
+
+ mov ax, [di] ; Set ax = block pointer
+ shl ax, 1 ; Multiply ax by 2
+ mov [lba], ax
+ mov [dest], bx
+ call disk_read
+ jmp .end
+
+;.indirect:
+; push di
+; push bx
+; push cx
+;
+; ; Read singly indirect pointer
+; mov bx, EXT2_GET_ADDRESS(EXT2_KERNEL_INODE) ; First block
+; lea di, [bx + EXT2_IND_POINTER_OFFSET] ; Address of singly indirect pointer
+; mov bx, 0x3000 ; Arbitrary address
+; mov ax, [di] ; Set ax = block pointer
+; shl ax, 1 ; Multiply ax by 2
+; mov [lba], ax
+; mov [dest], bx
+; call disk_read
+;
+; ; Read data
+; sub cx, EXT2_DIRECT_POINTER_COUNT
+; lea di, [ebx + 4 * ecx]
+; mov bx, 0x4000 ; Arbitrary address
+; mov ax, [di]
+; shl ax, 1
+; ;sub bx, 0x400
+; mov [lba], ax
+; mov [dest], bx
+; call disk_read
+;
+; pop cx
+; pop bx
+; pop di
+
+.end:
+ add bx, 0x400 ; 1kb increase
+ add di, 0x4 ; Move to next block pointer
+ sub cx, 0x2 ; Read 2 blocks
+ jnz kernel_load
+ ret
+
+protected_mode_enter:
+ cli ; Turn off interrupts
+
+ ; TODO: Check A20 support?
+ ; TODO: 0x92 method may not work on every device
+ in al, A20_GATE
+ test al, A20_ENABLED
+ jnz .a20_enabled
+ or al, A20_ENABLED
+ and al, A20_EXCLUDE_BIT
+ out A20_GATE, al
+ .a20_enabled:
+
+ lgdt [gdt_desc] ; Load GDT
+
+ ; Set protected mode via cr0
+ mov eax, cr0
+ or eax, 1 ; Set bit 0
+ mov cr0, eax
+
+ jmp (gdt_code - gdt):protected_mode ; JUMP!
+
+bits 32 ; Woah, so big!
+protected_mode:
+ mov ax, GDT_DATA_OFFSET ; Data segment offset of GDT
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax ; Stack segment
+
+ mov esp, STACK_POINTER ; Move stack pointer
+
+ mov ax, (gdt_tss - gdt) | 0b11 ; Load TSS in ring 3
+ ltr ax
+
+ mov eax, vid_info ; Pass VBE struct to kernel
+ push eax ; Push as second kernel parameter
+
+ mov edx, KERNEL_POSITION
+ lea eax, [edx]
+ call eax
+
+; GDT
+align 32
+gdt: ; GDTs start
+gdt_null: ; Must be null
+ dd 0
+ dd 0
+gdt_code: ; Code segment
+ dw GDT_MAX_LIMIT ; Limit
+ dw 0 ; First base
+ db 0 ; Second base
+ db (GDT_PRESENT | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE) ; Configuration
+ db (GDT_GRANULARITY | GDT_SIZE) ; Flags
+ db 0 ; Third base
+gdt_data: ; Data segment
+ dw GDT_MAX_LIMIT ; Limit
+ dw 0 ; First base
+ db 0 ; Second base
+ db (GDT_PRESENT | GDT_DESCRIPTOR | GDT_READWRITE) ; Configuration
+ db (GDT_GRANULARITY | GDT_SIZE) ; Flags
+ db 0 ; Third base
+gdt_user_code: ; User code segment
+ dw GDT_MAX_LIMIT ; Limit
+ dw 0 ; First base
+ db 0 ; Second base
+ db (GDT_PRESENT | GDT_RING3 | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_READWRITE) ; Configuration
+ db (GDT_GRANULARITY | GDT_SIZE) ; Flags
+ db 0 ; Third base
+gdt_user_data: ; Data segment
+ dw GDT_MAX_LIMIT ; Limit
+ dw 0 ; First base
+ db 0 ; Second base
+ db (GDT_PRESENT | GDT_RING3 | GDT_DESCRIPTOR | GDT_READWRITE) ; Configuration
+ db (GDT_GRANULARITY | GDT_SIZE) ; Flags
+ db 0 ; Third base
+gdt_tss: ; TSS segment
+ dw tss_entry + (tss_entry_end - tss_entry) ; Limit
+ dw tss_entry ; First base
+ db 0 ; Second base
+ db (GDT_PRESENT | GDT_RING3 | GDT_EXECUTABLE | GDT_ACCESSED) ; Configuration
+ db GDT_SIZE ; Flags
+ db 0 ; Third base
+gdt_end:
+gdt_desc:
+ dw gdt_end - gdt - 1
+ dd gdt
+
+; TSS
+tss_entry:
+ dd 0 ; Previous TSS
+ dd STACK_POINTER ; esp0
+ dd gdt_data - gdt ; ss0 (data offset)
+ dd 0 ; esp1
+ dd 0 ; ss1
+ dd 0 ; esp2
+ dd 0 ; ss2
+ dd 0 ; cr3
+ dd 0 ; eip
+ dd 0 ; eflags
+ dd 0 ; eax
+ dd 0 ; ecx
+ dd 0 ; edx
+ dd 0 ; ebx
+ dd 0 ; esp
+ dd 0 ; ebp
+ dd 0 ; esi
+ dd 0 ; edi
+ dd 0 ; es
+ dd 0 ; cs
+ dd 0 ; ss
+ dd 0 ; ds
+ dd 0 ; fs
+ dd 0 ; gs
+ dd 0 ; ldt
+ dw 0 ; trap
+ dw 0 ; iomap base
+tss_entry_end:
+
+times 1024 - ($ - $$) db 0
+
+; Start at LBA 2
+superblock:
diff --git a/kernel/features/fs.c b/kernel/features/fs.c
new file mode 100644
index 0000000..ab2a6eb
--- /dev/null
+++ b/kernel/features/fs.c
@@ -0,0 +1,184 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// EXT2 based filesystem
+
+#include <assert.h>
+#include <def.h>
+#include <fs.h>
+#include <ide.h>
+#include <mem.h>
+#include <print.h>
+#include <str.h>
+
+void *buffer_read(int block)
+{
+ return ide_read(malloc(BLOCK_SIZE), block);
+}
+
+struct superblock *get_superblock()
+{
+ struct superblock *sb = buffer_read(EXT2_SUPER);
+ if (sb->magic != EXT2_MAGIC)
+ return NULL;
+ return sb;
+}
+
+struct bgd *get_bgd()
+{
+ return buffer_read(EXT2_SUPER + 1);
+}
+
+struct inode *get_inode(int i)
+{
+ struct superblock *s = get_superblock();
+ assert(s);
+ struct bgd *b = get_bgd();
+ assert(b);
+
+ int block_group = (i - 1) / s->inodes_per_group;
+ int index = (i - 1) % s->inodes_per_group;
+ int block = (index * INODE_SIZE) / BLOCK_SIZE;
+ b += block_group;
+
+ u32 *data = buffer_read(b->inode_table + block);
+ struct inode *in =
+ (struct inode *)((u32)data + (index % (BLOCK_SIZE / INODE_SIZE)) * INODE_SIZE);
+ return in;
+}
+
+u32 read_indirect(u32 indirect, u32 block_num)
+{
+ char *data = buffer_read(indirect);
+ return *(u32 *)((u32)data + block_num * 4);
+}
+
+void *read_inode(struct inode *in)
+{
+ assert(in);
+ if (!in)
+ return NULL;
+
+ int num_blocks = in->blocks / (BLOCK_SIZE / SECTOR_SIZE);
+
+ assert(num_blocks != 0);
+ if (!num_blocks)
+ return NULL;
+
+ u32 sz = BLOCK_SIZE * num_blocks;
+ void *buf = malloc(sz);
+ printf("Loading %dKiB\n", sz >> 10);
+ assert(buf != NULL);
+
+ int indirect;
+
+ int blocknum = 0;
+ char *data;
+ for (int i = 0; i < num_blocks; i++) {
+ if (i < 12) {
+ blocknum = in->block[i];
+ data = buffer_read(blocknum);
+ memcpy((u32 *)((u32)buf + i * BLOCK_SIZE), data, BLOCK_SIZE);
+ } else {
+ // TODO: Support doubly and triply pointers
+ indirect = in->block[12];
+ blocknum = read_indirect(indirect, i - 12);
+ data = buffer_read(blocknum);
+ memcpy((u32 *)((u32)buf + (i - 1) * BLOCK_SIZE), data, BLOCK_SIZE);
+ }
+ }
+
+ return buf;
+}
+
+void *read_file(char *path)
+{
+ if (path[0] != '/')
+ return 0;
+
+ path++;
+ u32 current_inode = EXT2_ROOT;
+
+ int i;
+ while (1) {
+ for (i = 0; path[i] != '/' && path[i] != '\0'; i++)
+ ;
+
+ if (path[i] == '\0')
+ break;
+
+ path[i] = '\0';
+ current_inode = find_inode(path, current_inode);
+ path[i] = '/';
+
+ if (current_inode == 0)
+ return 0;
+
+ path += i + 1;
+ }
+
+ u32 inode = find_inode(path, current_inode);
+ if (inode == 0)
+ return 0;
+
+ return read_inode(get_inode(inode));
+}
+
+int find_inode(const char *name, int dir_inode)
+{
+ if (!dir_inode)
+ return -1;
+
+ struct inode *i = get_inode(dir_inode);
+
+ char *buf = malloc(BLOCK_SIZE * i->blocks / 2);
+ memset(buf, 0, BLOCK_SIZE * i->blocks / 2);
+
+ for (u32 q = 0; q < i->blocks / 2; q++) {
+ char *data = buffer_read(i->block[q]);
+ memcpy((u32 *)((u32)buf + q * BLOCK_SIZE), data, BLOCK_SIZE);
+ }
+
+ struct dirent *d = (struct dirent *)buf;
+
+ u32 sum = 0;
+ do {
+ // Calculate the 4byte aligned size of each entry
+ sum += d->total_len;
+ if (strncmp((void *)d->name, name, d->name_len) == 0) {
+ free(buf);
+ return d->inode_num;
+ }
+ d = (struct dirent *)((u32)d + d->total_len);
+
+ } while (sum < (1024 * i->blocks / 2));
+ free(buf);
+ return -1;
+}
+
+void ls_root()
+{
+ struct inode *i = get_inode(2);
+
+ char *buf = malloc(BLOCK_SIZE * i->blocks / 2);
+
+ for (u32 q = 0; q < i->blocks / 2; q++) {
+ char *data = buffer_read(i->block[q]);
+ memcpy((u32 *)((u32)buf + q * BLOCK_SIZE), data, BLOCK_SIZE);
+ }
+
+ struct dirent *d = (struct dirent *)buf;
+
+ int sum = 0;
+ int calc = 0;
+ printf("\nRoot directory:\n");
+ do {
+ calc = (sizeof(struct dirent) + d->name_len + 4) & ~0x3;
+ sum += d->total_len;
+ printf("/%s\n", d->name);
+ if (d->total_len != calc && sum == 1024)
+ d->total_len = calc;
+
+ d = (struct dirent *)((u32)d + d->total_len);
+
+ } while (sum < 1024);
+ printf("\n");
+}
diff --git a/kernel/features/gui.c b/kernel/features/gui.c
new file mode 100644
index 0000000..d080cb1
--- /dev/null
+++ b/kernel/features/gui.c
@@ -0,0 +1,68 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// Some GUI functions
+
+#include <fs.h>
+#include <gui.h>
+#include <psf.h>
+#include <str.h>
+#include <vesa.h>
+
+struct font *font;
+
+void gui_write_char(int x, int y, const u32 c[3], char ch)
+{
+ int pos = x * vbe_bpl + y * vbe_pitch;
+ char *draw = (char *)&fb[pos];
+
+ u32 stride = font->char_size / font->height;
+ for (int cy = 0; cy < font->height; cy++) {
+ for (int cx = 0; cx < font->width; cx++) {
+ u8 bits = font->chars[ch * font->char_size + cy * stride + cx / 8];
+ u8 bit = bits >> (7 - cx % 8) & 1;
+ if (bit) {
+ draw[vbe_bpl * cx] = c[2];
+ draw[vbe_bpl * cx + 1] = c[1];
+ draw[vbe_bpl * cx + 2] = c[0];
+ }
+ }
+ draw += vbe_pitch;
+ }
+}
+
+void gui_write(int x, int y, const u32 c[3], char *text)
+{
+ for (u32 i = 0; i < strlen(text); i++) {
+ gui_write_char(x + i * font->width, y, c, text[i]);
+ }
+}
+
+// Abstraction
+int x, y = 0;
+const u32 c[3] = { 0xff, 0xff, 0xff };
+void gui_term_write_char(char ch)
+{
+ if (x + font->width > vbe_width) {
+ x = 0;
+ y += font->height;
+ }
+
+ if (ch >= ' ' && ch <= '~') {
+ gui_write_char(x, y, c, ch);
+ x += font->width;
+ } else if (ch == '\n') {
+ x = 0;
+ y += font->height;
+ }
+}
+
+void gui_term_write(char *text)
+{
+ for (u32 i = 0; i < strlen(text); i++) {
+ gui_term_write_char(text[i]);
+ }
+}
+
+void gui_init(char *font_path)
+{
+ font = psf_parse(read_file(font_path));
+}
diff --git a/kernel/features/load.c b/kernel/features/load.c
new file mode 100644
index 0000000..cc1a094
--- /dev/null
+++ b/kernel/features/load.c
@@ -0,0 +1,19 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <def.h>
+#include <fs.h>
+#include <load.h>
+#include <mem.h>
+#include <print.h>
+#include <proc.h>
+
+void bin_load(char *path, struct proc *proc)
+{
+ char *data = read_file(path);
+ u32 stack = (u32)malloc(0x1000) + 0x1000;
+
+ proc->regs.ebp = (u32)stack;
+ proc->regs.esp = (u32)stack;
+ proc->regs.useresp = (u32)stack;
+ proc->regs.eip = (u32)data;
+}
diff --git a/kernel/features/proc.asm b/kernel/features/proc.asm
new file mode 100644
index 0000000..3d6bbc4
--- /dev/null
+++ b/kernel/features/proc.asm
@@ -0,0 +1,25 @@
+%define USER_CODE_SEGMENT 0x18
+%define USER_DATA_SEGMENT 0x20
+%define RING3_MASK 0b11
+
+global proc_jump_userspace
+extern _esp
+extern _eip
+proc_jump_userspace:
+ mov ax, USER_DATA_SEGMENT | RING3_MASK
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ mov eax, dword [_esp]
+ push USER_DATA_SEGMENT | RING3_MASK
+ push eax
+ pushf
+
+ sti
+
+ push USER_CODE_SEGMENT | RING3_MASK
+ push dword [_eip]
+
+ iret
diff --git a/kernel/features/proc.c b/kernel/features/proc.c
new file mode 100644
index 0000000..a14aaea
--- /dev/null
+++ b/kernel/features/proc.c
@@ -0,0 +1,104 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <interrupts.h>
+#include <load.h>
+#include <mem.h>
+#include <print.h>
+#include <proc.h>
+#include <str.h>
+#include <timer.h>
+
+u32 pid = 0;
+struct proc *root;
+struct proc *current;
+struct proc *last;
+
+void scheduler(struct regs *regs)
+{
+ if (current)
+ memcpy(&current->regs, regs, sizeof(struct regs));
+
+ timer_handler();
+
+ if (current && current->next)
+ current = current->next;
+ else
+ current = root;
+
+ while (current->state == PROC_ASLEEP)
+ if (!current->next)
+ current = root;
+ else
+ current = current->next;
+
+ /* proc_print(); */
+ memcpy(regs, &current->regs, sizeof(struct regs));
+
+ if (regs->cs != GDT_USER_CODE_OFFSET) {
+ regs->gs = GDT_USER_DATA_OFFSET;
+ regs->fs = GDT_USER_DATA_OFFSET;
+ regs->es = GDT_USER_DATA_OFFSET;
+ regs->ds = GDT_USER_DATA_OFFSET;
+ regs->ss = GDT_USER_DATA_OFFSET;
+ regs->cs = GDT_USER_CODE_OFFSET;
+ regs->eflags = EFLAGS_ALWAYS | EFLAGS_INTERRUPTS;
+ }
+ printf("%d", current->pid);
+}
+
+void proc_print()
+{
+ struct proc *proc = root;
+
+ printf("\n");
+ while (proc) {
+ printf("Process %d [%s]: %s\n", proc->pid,
+ proc->state == PROC_RUNNING ? "running" : "sleeping", proc->name);
+ proc = proc->next;
+ }
+ printf("\n");
+}
+
+void proc_attach(struct proc *proc)
+{
+ if (!last->next) {
+ last->next = proc;
+ } else {
+ struct proc *save = last;
+ while (save->next)
+ save = save->next;
+ save->next = proc;
+ }
+}
+
+struct proc *proc_make()
+{
+ struct proc *proc = malloc(sizeof(*proc));
+ proc->pid = pid++;
+ proc->state = PROC_RUNNING;
+ proc->next = NULL;
+
+ if (current)
+ proc_attach(proc);
+ last = proc;
+ return proc;
+}
+
+extern void proc_jump_userspace();
+
+u32 _esp, _eip;
+void proc_init()
+{
+ cli();
+ irq_install_handler(0, scheduler);
+
+ root = proc_make();
+ bin_load("/init", root);
+ strcpy(root->name, "root");
+ proc_print();
+
+ _eip = root->regs.eip;
+ _esp = root->regs.esp;
+ proc_jump_userspace();
+}
diff --git a/kernel/features/psf.c b/kernel/features/psf.c
new file mode 100644
index 0000000..adf2aa2
--- /dev/null
+++ b/kernel/features/psf.c
@@ -0,0 +1,58 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// PSF parser
+
+#include <def.h>
+#include <gui.h>
+#include <mem.h>
+#include <print.h>
+#include <psf.h>
+#include <vesa.h>
+
+// Verifies the PSF magics
+// Returns the PSF version or 0
+int psf_verify(char *data)
+{
+ struct psf1_header *header1 = (struct psf1_header *)data;
+ struct psf2_header *header2 = (struct psf2_header *)data;
+
+ if (header1->magic[0] == PSF1_MAGIC_0 && header1->magic[1] == PSF1_MAGIC_1)
+ return 1;
+ else if (header2->magic[0] == PSF2_MAGIC_0 && header2->magic[1] == PSF2_MAGIC_1 &&
+ header2->magic[2] == PSF2_MAGIC_2 && header2->magic[3] == PSF2_MAGIC_3)
+ return 2;
+ else
+ return 0;
+}
+
+struct font *psf_parse(char *data)
+{
+ int version = psf_verify(data);
+
+ char *chars;
+ int height;
+ int width;
+ int char_size;
+
+ if (version == 1) {
+ chars = data + sizeof(struct psf1_header);
+ height = ((struct psf1_header *)data)->char_size;
+ width = 8;
+ char_size = ((struct psf1_header *)data)->char_size;
+ } else if (version == 2) {
+ chars = data + ((struct psf2_header *)data)->size;
+ height = ((struct psf2_header *)data)->height;
+ width = ((struct psf2_header *)data)->width;
+ char_size = ((struct psf2_header *)data)->char_size;
+ } else {
+ printf("Unknown font!\n");
+ return 0;
+ }
+
+ struct font *font = malloc(sizeof(*font));
+ font->chars = chars;
+ font->height = height;
+ font->width = width;
+ font->char_size = char_size;
+
+ return font;
+}
diff --git a/kernel/features/syscall.c b/kernel/features/syscall.c
new file mode 100644
index 0000000..3d012cf
--- /dev/null
+++ b/kernel/features/syscall.c
@@ -0,0 +1,25 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <interrupts.h>
+#include <load.h>
+#include <print.h>
+#include <proc.h>
+#include <str.h>
+
+int i = 0;
+void syscall_handler(struct regs *r)
+{
+ printf("[SYSCALL] %d\n", r->eax);
+
+ struct proc *a = proc_make();
+ bin_load(++i ? "/a" : "/b", a);
+ strcpy(a->name, "a");
+ proc_print();
+}
+
+void syscall_init()
+{
+ idt_set_gate(0x80, (u32)isr128, 0x08, 0x8E);
+ isr_install_handler(0x80, syscall_handler);
+}
diff --git a/kernel/inc/boot.h b/kernel/inc/boot.h
new file mode 100644
index 0000000..1a41c32
--- /dev/null
+++ b/kernel/inc/boot.h
@@ -0,0 +1,10 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// This file specifies the structs passed by the bootloader
+
+#include <def.h>
+#include <vesa.h>
+
+struct vid_info {
+ u32 mode;
+ struct vbe *info;
+};
diff --git a/kernel/inc/cpu.h b/kernel/inc/cpu.h
new file mode 100644
index 0000000..eb09291
--- /dev/null
+++ b/kernel/inc/cpu.h
@@ -0,0 +1,29 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef CPU_H
+#define CPU_H
+
+#include <def.h>
+
+u8 inb(u16 port);
+u16 inw(u16 port);
+u32 inl(u16 port);
+void insl(u16 port, void *addr, int n);
+
+void outb(u16 port, u8 data);
+void outw(u16 port, u16 data);
+void outl(u16 port, u32 data);
+void cli();
+void sti();
+void hlt();
+void idle();
+
+static inline void spinlock(int *ptr)
+{
+ int prev;
+ do
+ __asm__ volatile("lock xchgl %0,%1" : "=a"(prev) : "m"(*ptr), "a"(1));
+ while (prev);
+}
+
+#endif
diff --git a/kernel/inc/fs.h b/kernel/inc/fs.h
new file mode 100644
index 0000000..775a1ce
--- /dev/null
+++ b/kernel/inc/fs.h
@@ -0,0 +1,102 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// EXT2 based filesystem
+
+#ifndef FS_H
+#define FS_H
+
+#include <def.h>
+
+#define EXT2_BOOT 0
+#define EXT2_SUPER 1
+#define EXT2_ROOT 2
+#define EXT2_MAGIC 0x0000EF53
+
+struct superblock {
+ u32 total_inodes;
+ u32 total_blocks;
+ u32 su_res_blocks; // Superuser reserved
+ u32 free_blocks;
+ u32 free_inodes;
+ u32 superblock_block_num;
+ u32 log2_block_size;
+ u32 log2_frag_size;
+ u32 blocks_per_group;
+ u32 frags_per_group;
+ u32 inodes_per_group;
+ u32 last_mount_time;
+ u32 last_write_time;
+ u16 mounts_since_fsck;
+ u16 max_mounts_since_fsck;
+ u16 magic;
+ u16 state; // 1 clean; 2 errors
+ u16 error_action;
+ u16 minor_version;
+ u32 last_fsck_time;
+ u32 max_time_since_fsck;
+ u32 creator_os_id;
+ u32 major_version;
+ u16 res_block_uid;
+ u16 res_block_gid;
+};
+
+struct bgd {
+ u32 block_bitmap;
+ u32 inode_bitmap;
+ u32 inode_table;
+ u16 free_blocks;
+ u16 free_inodes;
+ u16 used_dirs;
+ u16 pad;
+ u8 bg_reserved[12];
+};
+
+struct inode {
+ u16 mode;
+ u16 uid;
+ u32 size;
+
+ u32 last_access_time;
+ u32 creation_time;
+ u32 last_modification_time;
+ u32 deletion_time;
+
+ u16 gid;
+ u16 link_count;
+ u32 blocks;
+ u32 flags;
+ u32 os_specific_val1;
+ u32 block[15];
+ u32 generation;
+
+ u32 reserved1;
+ u32 reserved2;
+
+ u32 fragment_addr;
+ u8 os_specific_val2[12];
+};
+
+#define INODE_SIZE (sizeof(struct inode))
+
+struct dirent {
+ u32 inode_num;
+ u16 total_len;
+ u8 name_len;
+ u8 type_indicator;
+ u8 name[];
+};
+
+struct file {
+ struct inode inode;
+ u32 pos;
+ u8 block_index;
+ u8 *buf;
+ u32 curr_block_pos;
+};
+
+int find_inode(const char *name, int dir_inode);
+struct inode *get_inode(int i);
+void *read_inode(struct inode *in);
+void *read_file(char *path);
+void ls_root(); // DEMO ;)
+
+#endif
diff --git a/kernel/inc/gui.h b/kernel/inc/gui.h
new file mode 100644
index 0000000..760bcb9
--- /dev/null
+++ b/kernel/inc/gui.h
@@ -0,0 +1,22 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// Some GUI functions
+
+#ifndef GUI_H
+#define GUI_H
+
+#include <def.h>
+
+// Generalized font struct
+struct font {
+ char *chars;
+ int height;
+ int width;
+ int char_size;
+};
+
+void gui_write(int x, int y, const u32 c[3], char *text);
+void gui_term_write_char(char ch);
+void gui_term_write(char *text);
+void gui_init(char *font_path);
+
+#endif
diff --git a/kernel/inc/ide.h b/kernel/inc/ide.h
new file mode 100644
index 0000000..9d753ae
--- /dev/null
+++ b/kernel/inc/ide.h
@@ -0,0 +1,40 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef IDE_H
+#define IDE_H
+
+#include <def.h>
+
+#define BLOCK_SIZE 1024
+#define SECTOR_SIZE 512
+
+#define IDE_BUSY (1 << 7)
+#define IDE_READY (1 << 6)
+#define IDE_DRIVE_FAULT (1 << 5)
+#define IDE_ERROR (1 << 0)
+
+#define IDE_IO 0x1F0
+#define IDE_DATA 0x0
+#define IDE_FEATURES 0x1
+#define IDE_SECTOR_COUNT 0x2
+#define IDE_LOW 0x3
+#define IDE_MID 0x4
+#define IDE_HIGH 0x5
+#define IDE_HEAD 0x6
+#define IDE_CMD 0x7
+#define IDE_ALTERNATE 0x3F6
+
+#define LBA_LOW(c) ((u8)(c & 0xFF))
+#define LBA_MID(c) ((u8)(c >> 8) & 0xFF)
+#define LBA_HIGH(c) ((u8)(c >> 16) & 0xFF)
+#define LBA_LAST(c) ((u8)(c >> 24) & 0xF)
+
+#define IDE_CMD_READ (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x20 : 0xC4
+#define IDE_CMD_WRITE (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x30 : 0xC5
+#define IDE_CMD_READ_MUL 0xC4
+#define IDE_CMD_WRITE_MUL 0xC5
+
+int ide_wait(int check);
+void *ide_read(void *b, u32 block);
+
+#endif
diff --git a/kernel/inc/interrupts.h b/kernel/inc/interrupts.h
new file mode 100644
index 0000000..e943e5a
--- /dev/null
+++ b/kernel/inc/interrupts.h
@@ -0,0 +1,95 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef IDT_H
+#define IDT_H
+
+#include <def.h>
+
+struct regs {
+ u32 gs, fs, es, ds;
+ u32 edi, esi, ebp, esp, ebx, edx, ecx, eax;
+ u32 int_no, err_code;
+ u32 eip, cs, eflags, useresp, ss;
+};
+
+struct idt_entry {
+ u16 base_low;
+ u16 sel; // Kernel segment
+ u8 always0; // Always 0
+ u8 flags;
+ u16 base_high;
+} __attribute__((packed));
+
+struct idt_ptr {
+ u16 limit;
+ void *base;
+} __attribute__((packed));
+
+struct idt_entry idt[256];
+struct idt_ptr idt_ptr;
+
+void idt_set_gate(u8 num, u32 base, u16 sel, u8 flags);
+
+void irq_install_handler(int irq, void (*handler)(struct regs *r));
+void irq_uninstall_handler(int irq);
+
+void isr_install_handler(int isr, void (*handler)(struct regs *r));
+void isr_uninstall_handler(int isr);
+
+void interrupts_install();
+
+// External handlers (ASM)
+
+extern void isr0();
+extern void isr1();
+extern void isr2();
+extern void isr3();
+extern void isr4();
+extern void isr5();
+extern void isr6();
+extern void isr7();
+extern void isr8();
+extern void isr9();
+extern void isr10();
+extern void isr11();
+extern void isr12();
+extern void isr13();
+extern void isr14();
+extern void isr15();
+extern void isr16();
+extern void isr17();
+extern void isr18();
+extern void isr19();
+extern void isr20();
+extern void isr21();
+extern void isr22();
+extern void isr23();
+extern void isr24();
+extern void isr25();
+extern void isr26();
+extern void isr27();
+extern void isr28();
+extern void isr29();
+extern void isr30();
+extern void isr31();
+extern void isr128();
+
+extern void irq0();
+extern void irq1();
+extern void irq2();
+extern void irq3();
+extern void irq4();
+extern void irq5();
+extern void irq6();
+extern void irq7();
+extern void irq8();
+extern void irq9();
+extern void irq10();
+extern void irq11();
+extern void irq12();
+extern void irq13();
+extern void irq14();
+extern void irq15();
+extern void irq128();
+
+#endif
diff --git a/kernel/inc/keyboard.h b/kernel/inc/keyboard.h
new file mode 100644
index 0000000..f0effc7
--- /dev/null
+++ b/kernel/inc/keyboard.h
@@ -0,0 +1,8 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+
+void keyboard_install();
+
+#endif
diff --git a/kernel/inc/load.h b/kernel/inc/load.h
new file mode 100644
index 0000000..60fecf9
--- /dev/null
+++ b/kernel/inc/load.h
@@ -0,0 +1,10 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef LOAD_H
+#define LOAD_H
+
+#include <proc.h>
+
+void bin_load(char *path, struct proc *proc);
+
+#endif
diff --git a/kernel/inc/proc.h b/kernel/inc/proc.h
new file mode 100644
index 0000000..39ba704
--- /dev/null
+++ b/kernel/inc/proc.h
@@ -0,0 +1,30 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef PROC_H
+#define PROC_H
+
+#include <def.h>
+#include <interrupts.h>
+
+#define EFLAGS_ALWAYS 0x2 // Always one
+#define EFLAGS_INTERRUPTS 0x200 // Enable interrupts
+
+#define GDT_USER_CODE_OFFSET 0x1b // User code segment offset in GDT (with ring3 mask)
+#define GDT_USER_DATA_OFFSET 0x23 // User data segment offset in GDT (with ring3 mask)
+
+enum state { PROC_RUNNING, PROC_ASLEEP };
+
+struct proc {
+ u32 pid;
+ enum state state;
+ char name[32];
+ struct regs regs;
+ /* struct proc *parent; */
+ struct proc *next;
+};
+
+void proc_init();
+void proc_print();
+struct proc *proc_make();
+
+#endif
diff --git a/kernel/inc/psf.h b/kernel/inc/psf.h
new file mode 100644
index 0000000..63a3d1e
--- /dev/null
+++ b/kernel/inc/psf.h
@@ -0,0 +1,48 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// PSF parser
+
+#ifndef PSF_H
+#define PSF_H
+
+#include <def.h>
+
+/**
+ * PSF version 1
+ */
+
+#define PSF1_MAGIC_0 0x36
+#define PSF1_MAGIC_1 0x04
+#define PSF1_MODE_256 0
+#define PSF1_MODE_512 1
+#define PSF1_MODE_256_UNICODE 2
+#define PSF1_MODE_512_UNICODE 3
+
+struct psf1_header {
+ u8 magic[2];
+ u8 mode;
+ u8 char_size;
+};
+
+/**
+ * PSF version 2
+ */
+
+#define PSF2_MAGIC_0 0x72
+#define PSF2_MAGIC_1 0xb5
+#define PSF2_MAGIC_2 0x4a
+#define PSF2_MAGIC_3 0x86
+
+struct psf2_header {
+ u8 magic[4];
+ u32 version;
+ u32 size;
+ u32 flags;
+ u32 glyph_count;
+ u32 char_size;
+ u32 height;
+ u32 width;
+};
+
+struct font *psf_parse(char *data);
+
+#endif
diff --git a/kernel/inc/serial.h b/kernel/inc/serial.h
new file mode 100644
index 0000000..6511952
--- /dev/null
+++ b/kernel/inc/serial.h
@@ -0,0 +1,9 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef SERIAL_H
+#define SERIAL_H
+
+void serial_install();
+void serial_print(const char *data);
+
+#endif
diff --git a/kernel/inc/syscall.h b/kernel/inc/syscall.h
new file mode 100644
index 0000000..3f89365
--- /dev/null
+++ b/kernel/inc/syscall.h
@@ -0,0 +1,8 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef SYSCALL_H
+#define SYSCALL_H
+
+void syscall_init();
+
+#endif
diff --git a/kernel/inc/timer.h b/kernel/inc/timer.h
new file mode 100644
index 0000000..7c4f077
--- /dev/null
+++ b/kernel/inc/timer.h
@@ -0,0 +1,11 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <def.h>
+
+void timer_install();
+void timer_handler(); // For scheduler
+
+#endif
diff --git a/kernel/inc/vesa.h b/kernel/inc/vesa.h
new file mode 100644
index 0000000..e34cdc0
--- /dev/null
+++ b/kernel/inc/vesa.h
@@ -0,0 +1,57 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#ifndef VBE_H
+#define VBE_H
+
+#include <def.h>
+
+struct vbe {
+ u16 attributes;
+ u8 window_a;
+ u8 window_b;
+ u16 granularity;
+ u16 window_size;
+ u16 segment_a;
+ u16 segment_b;
+ u32 win_func_ptr;
+ u16 pitch;
+ u16 width;
+ u16 height;
+ u8 w_char;
+ u8 y_char;
+ u8 planes;
+ u8 bpp;
+ u8 banks;
+ u8 memory_model;
+ u8 bank_size;
+ u8 image_pages;
+ u8 reserved0;
+
+ u8 red_mask;
+ u8 red_position;
+ u8 green_mask;
+ u8 green_position;
+ u8 blue_mask;
+ u8 blue_position;
+ u8 reserved_mask;
+ u8 reserved_position;
+ u8 direct_color_attributes;
+
+ u32 framebuffer;
+ u32 off_screen_mem_off;
+ u16 off_screen_mem_size;
+ u8 reserved1[206];
+};
+
+struct vbe *vbe;
+int vbe_width;
+int vbe_height;
+int vbe_bpl;
+int vbe_pitch;
+u8 *fb;
+
+void vesa_fill(const u32 color[3]);
+void vesa_set_pixel(u16 x, u16 y, const u32 color[3]);
+void vesa_init(struct vbe *info);
+
+#endif
diff --git a/kernel/main.c b/kernel/main.c
new file mode 100644
index 0000000..17d6267
--- /dev/null
+++ b/kernel/main.c
@@ -0,0 +1,50 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include "config.h"
+#include <boot.h>
+#include <cpu.h>
+#include <def.h>
+#include <fs.h>
+#include <gui.h>
+#include <interrupts.h>
+#include <keyboard.h>
+#include <load.h>
+#include <print.h>
+#include <serial.h>
+#include <syscall.h>
+#include <timer.h>
+
+u32 HEAP = 0x00200000;
+u32 HEAP_START;
+
+void kernel_main(struct vid_info *vid_info)
+{
+ HEAP_START = HEAP; // For malloc function
+
+ // Initialize VESA video
+ vesa_init(vid_info->info);
+ u32 terminal_background[3] = { 0, 0, 0 };
+ vesa_fill(terminal_background);
+
+ // Serial connection
+ serial_install();
+ serial_print("\nConnected.\n");
+
+ // Install drivers
+ interrupts_install();
+ timer_install();
+ keyboard_install();
+
+ // Enable drivers
+ sti();
+
+ ls_root();
+ gui_init(FONT_PATH);
+
+ gui_term_write("Wake up, " USERNAME "...\n");
+
+ syscall_init();
+ proc_init();
+
+ idle();
+}