diff options
Diffstat (limited to 'kernel')
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(¤t->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, ¤t->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(); +} |