diff --git a/.cache/clangd/index/kmain.c.9B04FE5F8A3537CE.idx b/.cache/clangd/index/kmain.c.9B04FE5F8A3537CE.idx index b59c42c..5da2d05 100644 Binary files a/.cache/clangd/index/kmain.c.9B04FE5F8A3537CE.idx and b/.cache/clangd/index/kmain.c.9B04FE5F8A3537CE.idx differ diff --git a/Makefile b/Makefile index 5622e1a..5cc40b7 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ KERNELIMG := $(BUILDDIR)/kernel.bin QEMUFLAGS = -d int -s \ -kernel $(KERNELIMG) \ + -m 512M \ -device piix3-ide,id=ide -drive id=disk,file=image.img,format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 \ .PHONY: all kernel qemu clean docs diff --git a/idt.c b/idt.c index 51f2456..e020b10 100644 --- a/idt.c +++ b/idt.c @@ -1,4 +1,6 @@ #include +#include +#include void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes) { idt[i].offset_lo = (uint16_t)(offset & 0xFFFF); @@ -7,3 +9,13 @@ void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t s idt[i].attributes = attributes; idt[i].offset_hi = (uint16_t)(offset >> 16); } + +void exception_handler(struct registers regs) { + clear_screen(); + + printf("Exception 0x%X (0x%X) has occurred\nEAX: 0x%08X\nECX: 0x%08X\nEDX: 0x%08X\nEBX: 0x%08X\nESI: 0x%08X\nEDI: 0x%08X\nEBP: 0x%08X\nESP: 0x%08X\n", + regs.int_no, regs.err_code, regs.eax, regs.ecx, regs.edx, regs.ebx, regs.esi, regs.edi, regs.ebp, regs.esp+24); + + printf("\nEIP: 0x%08X\nCS: 0x%08X\nEFLAGS: 0x%08X", + regs.eip, regs.cs, regs.eflags); +} diff --git a/image.img b/image.img deleted file mode 100644 index 0172adc..0000000 --- a/image.img +++ /dev/null @@ -1 +0,0 @@ -SKIBIDI ANDEW PINGAS BALLS ANDEW diff --git a/include/fat12.h b/include/fat12.h new file mode 100644 index 0000000..ff71f10 --- /dev/null +++ b/include/fat12.h @@ -0,0 +1,34 @@ +#ifndef RK_FAT12_H_ +#define RK_FAT12_H_ + +#include + +struct fat12_boot_sector { + uint8_t jump_boot[3]; // Jump instruction to boot code + uint8_t oem_name[8]; // OEM name and version + uint16_t bytes_per_sector; // Bytes per sector + uint8_t sectors_per_cluster; // Sectors per cluster + uint16_t reserved_sectors; // Reserved sectors + uint8_t num_fats; // Number of FATs + uint16_t root_entry_count; // Number of root directory entries + uint16_t total_sectors_16; // Total sectors (16-bit) + uint8_t media; // Media descriptor + uint16_t fat_size_16; // Sectors per FAT (16-bit) + uint16_t sectors_per_track; // Sectors per track (for BIOS) + uint16_t num_heads; // Number of heads (for BIOS) + uint32_t hidden_sectors; // Hidden sectors + uint32_t total_sectors_32; // Total sectors (32-bit) + + // Extended Boot Record (EBR) fields + uint8_t drive_number; // Drive number + uint8_t reserved1; // Reserved + uint8_t boot_signature; // Extended boot signature + uint32_t volume_id; // Volume ID (serial number) + uint8_t volume_label[11]; // Volume label + uint8_t file_system_type[8]; // File system type (e.g., "FAT12 ") + + uint8_t boot_code[448]; // Boot code + uint16_t boot_sector_signature; // Boot sector signature (0xAA55) +} __attribute__((packed)); + +#endif diff --git a/include/gdt.h b/include/gdt.h index d12b650..e6241e9 100644 --- a/include/gdt.h +++ b/include/gdt.h @@ -1,9 +1,50 @@ #ifndef RK_GDT_H_ #define RK_GDT_H_ +#include + #define GDT_NULL 0 #define GDT_KERNEL_CODE 1 #define GDT_KERNEL_DATA 2 #define GDT_SEGMENT_SELECTOR(i, p) ((i << 3) | p) +struct gdt_entry { + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; +} __attribute__((packed)); + +struct tss_entry { + uint32_t prev_tss; // The previous TSS - with hardware task switching this is used + uint32_t esp0; // The stack pointer to load when we change to kernel mode + uint32_t ss0; // The stack segment to load when we change to kernel mode + uint32_t esp1; // Unused... + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t es; // The value to load into ES when we change to kernel mode + uint32_t cs; // The value to load into CS when we change to kernel mode + uint32_t ss; // The value to load into SS when we change to kernel mode + uint32_t ds; // The value to load into DS when we change to kernel mode + uint32_t fs; // The value to load into FS when we change to kernel mode + uint32_t gs; // The value to load into GS when we change to kernel mode + uint32_t ldt; // Unused... + uint16_t trap; + uint16_t iomap_base; +} __attribute__((packed)); + #endif diff --git a/include/idt.h b/include/idt.h index d493ef4..ab00b62 100644 --- a/include/idt.h +++ b/include/idt.h @@ -36,7 +36,17 @@ enum IDT_ATTRIBUTES { INT_PRESENT = 0x80, }; +struct registers { + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ + uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */ + + uint32_t eip; + uint32_t cs; + uint32_t eflags; +}; + void encode_idt_entry(struct idt_entry idt[], int i, uint32_t offset, uint16_t segment_selector, uint8_t attributes); void flush_idt(uint32_t idt_pointer); +void exception_handler(struct registers regs); #endif diff --git a/include/kernel.h b/include/kernel.h new file mode 100644 index 0000000..044e6cd --- /dev/null +++ b/include/kernel.h @@ -0,0 +1,22 @@ +#ifndef RK_KERNEL_H_ +#define RK_KERNEL_H_ + +#include +#include + +struct kernel_context { + struct rsdp *rsdp; + struct xsdp *xsdp; + + struct rsdt *rsdt; + struct mcfg *mcfg; + + uint32_t multi_mmap_size; + struct multiboot_mmap_entry *multi_mmap; + + uint32_t available_bytes; + uint32_t mmap_size; + uint32_t *mmap; +}; + +#endif diff --git a/include/mem.h b/include/mem.h new file mode 100644 index 0000000..c628ae7 --- /dev/null +++ b/include/mem.h @@ -0,0 +1,20 @@ +#ifndef RK_MEM_H_ +#define RK_MEM_H_ + +#include +#include + +#define KERNEL_START 0x1000000 // 1 MiB +#define KERNEL_SIZE 0xC800 // 50 KiB +#define BLOCK_SIZE 4096 +#define BITS 32 + +uint8_t mmap_init(struct kernel_context *ctx); +void mmap_set_block(struct kernel_context *ctx, int block); +void mmap_free_block(struct kernel_context *ctx, int block); +void *mmap_find_first_free_block(struct kernel_context *ctx); +void *mmap_block_to_physical(struct kernel_context *ctx, int block); + +void *memset(void *s, int c, uint32_t len); + +#endif diff --git a/include/multiboot.h b/include/multiboot.h index c1e83f3..ebeb17f 100644 --- a/include/multiboot.h +++ b/include/multiboot.h @@ -28,4 +28,19 @@ struct multiboot_info { uint32_t vbeInterfaceLength; }; +struct multiboot_mmap_entry +{ + uint32_t size; + uint32_t addr_low; + uint32_t addr_high; + uint32_t len_low; + uint32_t len_high; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + uint32_t type; +} __attribute__((packed)); + #endif diff --git a/kmain.c b/kmain.c index 0340b55..952d712 100644 --- a/kmain.c +++ b/kmain.c @@ -1,4 +1,4 @@ -#include "ide.h" +#include #include #include #include @@ -9,33 +9,112 @@ #include #include #include - -struct kernel_context { - struct rsdp *rsdp; - struct xsdp *xsdp; - - struct rsdt *rsdt; -}; +#include +#include +#include struct kernel_context ctx = {0}; struct idt_entry g_idt[256] = {0}; struct idtr g_idtr = {0}; -extern void exception_handler(); +extern uint32_t isr_stub_table[32]; extern void ps2_isr(); #define MAGIC_BREAKPOINT __asm__ volatile("xchgw %bx, %bx"); +#define USER_DS 0x23 // User data segment selector +#define USER_CS 0x1B // User code segment selector + +extern struct gdt_entry gdt[6]; +struct tss_entry tss; + +void tss_flush(uint16_t tss_selector) { + asm volatile ( + "ltr %0" + : // no output + : "r" (tss_selector) + ); +} + +void test() { + printf("\n\nHello from user mode!\n"); + for (;;) {} +} + +void switch_to_user_mode(void *user_stack, void *user_entry) { + asm volatile ( + "movl $0x23, %%eax \n" // User data segment selector + "mov %%ax, %%ds \n" + "mov %%ax, %%es \n" + "mov %%ax, %%fs \n" + "mov %%ax, %%gs \n" + + "pushl $0x23 \n" // User stack segment + "pushl %0 \n" // User stack pointer + "pushfl \n" // Push EFLAGS + "popl %%eax \n" + "orl $0x200, %%eax \n" // Enable interrupts (set IF) + "pushl %%eax \n" + + "pushl $0x1B \n" // User code segment + "pushl %1 \n" // User entry point + + "iret \n" // Interrupt return (switches to user mode) + : + : "r"(user_stack), "r"(user_entry) + : "eax" + ); +} + +void write_tss(int num, uint16_t ss0, uint32_t esp0) { + uint32_t base = (uint32_t)&tss; + uint32_t limit = base + sizeof(tss); + + gdt[num].base_low = base & 0xFFFF; + gdt[num].base_middle = (base >> 16) & 0xFF; + gdt[num].base_high = (base >> 24) & 0xFF; + gdt[num].limit_low = limit & 0xFFFF; + gdt[num].granularity = (limit >> 16) & 0x0F; + gdt[num].granularity |= 0x40; + gdt[num].access = 0x89; + + memset(&tss, 0, sizeof(tss)); + tss.ss0 = ss0; + tss.esp0 = esp0; + tss.cs = 0x0b; + tss.ss = tss.ds = tss.es = tss.fs = tss.gs = 0x13; +} + void kmain(struct multiboot_info *info) { clear_screen(); set_color(VGA_COLOR_CYAN); // Check if the bootloader gave us the upper and lower memory if (!(info->flags & 0x1)) goto halt; + ctx.multi_mmap = (struct multiboot_mmap_entry *)info->memMapAddress; + ctx.multi_mmap_size = info->memMapLength / sizeof(struct multiboot_mmap_entry); + + for (uint32_t i = 0; i < ctx.multi_mmap_size; i++) { + struct multiboot_mmap_entry entry = ctx.multi_mmap[i]; + if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue; + + uint32_t len = (uint32_t)(entry.len_low | entry.len_high); + uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high); + printf("Region #%d: 0x%08X-0x%08X\n", i, addr, addr+len); + } + + if (!mmap_init(&ctx)) { + set_color(VGA_COLOR_RED); + printf("Failed to create memory map\n"); + goto halt; + } + + printf("Detected %d bytes of memory\n", ctx.available_bytes); + // Create and load an IDT for (int i = 0; i < 32; i++) { - encode_idt_entry(g_idt, i, (uint32_t)&exception_handler, GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), TRAP_GATE_32 | INT_RING0 | INT_PRESENT); + encode_idt_entry(g_idt, i, isr_stub_table[i], GDT_SEGMENT_SELECTOR(GDT_KERNEL_CODE, 0x00), TRAP_GATE_32 | INT_RING0 | INT_PRESENT); } g_idtr.size = (sizeof(struct idt_entry) * 256) - 1; @@ -119,7 +198,17 @@ void kmain(struct multiboot_info *info) { } set_color(VGA_COLOR_WHITE); - printf("\nYou are now being dropped into a kernel shell\n$ "); + + void *buf = mmap_find_first_free_block(&ctx); + ata_read_sector(ATA_PRIMARY, ATA_MASTER, 0, (uint8_t *)buf); +// struct fat12_boot_sector *boot_sect = (struct fat12_boot_sector *)buf; +// int fat_size = boot_sect->total_sectors_16 * boot_sect->bytes_per_sector; + + write_tss(5, 0x10, 0x0); + tss_flush(0x2B); + void *stack_bottom = mmap_find_first_free_block(&ctx); + void *stack_top = stack_bottom + 4096; + switch_to_user_mode(stack_top, buf); halt: for (;;) {} } diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..bd445a2 --- /dev/null +++ b/mem.c @@ -0,0 +1,121 @@ +#include +#include + +// TODO: Fix this, it wastes memory when switching regions because the previous offset isnt being subtracted +// Should work fine for now? +void *mmap_block_to_physical(struct kernel_context *ctx, int block) { + uint32_t offset = block * BLOCK_SIZE; + + for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) { + struct multiboot_mmap_entry entry = ctx->multi_mmap[i]; + if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue; + + uint32_t len = (uint32_t)(entry.len_low | entry.len_high); + uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high); + if (len > offset) + return (void *)(addr + offset); + } + + return 0; +} + +void *memset(void *s, int c, uint32_t len) { + unsigned char *dst = s; + while (len > 0) { + *dst = (unsigned char) c; + dst++; + len--; + } + return s; +} + + +uint8_t mmap_init(struct kernel_context *ctx) { + // Calculate the number of bytes available to us + for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) { + struct multiboot_mmap_entry entry = ctx->multi_mmap[i]; + if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue; + + uint32_t len = (uint32_t)(entry.len_low | entry.len_high); + ctx->available_bytes += len; + } + + ctx->mmap_size = ctx->available_bytes / BLOCK_SIZE / 8; + printf("Memory map size: %d bytes\n", ctx->mmap_size); + + // Loop again to find the first region with enough space to hold the memory map + int index = -1; + for (uint32_t i = 0; i < ctx->multi_mmap_size; i++) { + struct multiboot_mmap_entry entry = ctx->multi_mmap[i]; + if (entry.type != MULTIBOOT_MEMORY_AVAILABLE) continue; + + uint32_t len = (uint32_t)(entry.len_low | entry.len_high); + if (len > ctx->mmap_size) { + index = i; + break; + } + } + + if (index == -1) return 0; // Failed + + // Create map + struct multiboot_mmap_entry entry = ctx->multi_mmap[index]; + uint32_t addr = (uint32_t)(entry.addr_low | entry.addr_high); + ctx->mmap = (uint32_t *)addr; + // Zero the map + memset(ctx->mmap, 0, ctx->mmap_size); + // Reserve the blocks that hold the memory map + 1 + uint32_t blocks_to_set = ctx->mmap_size / BLOCK_SIZE + 1; + for (uint32_t i = 0; i < blocks_to_set; i++) + mmap_set_block(ctx, i); + // Reserve the kernel space + int bits = ctx->mmap_size * 8; + for (int i = 0; i < bits; i++) { + uint32_t phys = (uint32_t)mmap_block_to_physical(ctx, i); + if (phys > KERNEL_START && phys < KERNEL_START + KERNEL_SIZE) + mmap_set_block(ctx, i); + } + + return 1; +} + +void mmap_set_block(struct kernel_context *ctx, int block) { + int index = block / BITS; + int bit = block; + if (block >= BITS) + bit -= BITS; + + uint32_t mask = 1 << bit; + if (!(ctx->mmap[index] & mask)) + ctx->mmap[index] |= mask; +} + +void mmap_free_block(struct kernel_context *ctx, int block) { + int index = block / BITS; + int bit = block; + if (block >= BITS) + bit -= BITS; + + uint32_t mask = 1 << bit; + if (ctx->mmap[index] & mask) + ctx->mmap[index] ^= mask; +} + +void *mmap_find_first_free_block(struct kernel_context *ctx) { + int bits = ctx->mmap_size * 8; + for (int i = 0; i < bits; i++) { + int index = i / BITS; + int bit = i; + if (i >= BITS) + bit -= BITS; + + uint32_t mask = 1 << bit; + + uint32_t dword = ctx->mmap[index]; + if (!(dword & mask)) { + return mmap_block_to_physical(ctx, i); + } + } + + return 0; +} diff --git a/multiboot.asm b/multiboot.asm index 0d9d1a3..c5d9240 100644 --- a/multiboot.asm +++ b/multiboot.asm @@ -1,5 +1,5 @@ %define MAGIC 0x1BADB002 -%define FLAGS 0b100 +%define FLAGS 0b110 ; Multiboot v1 Specification ; https://www.gnu.org/software/grub/manual/multiboot/multiboot.html @@ -49,13 +49,74 @@ _start: halt: hlt jmp halt -global exception_handler -exception_handler: - add esp, 4 - mov byte [0xb8000], 'X' - iret + +extern exception_handler +%macro isr_err_stub 1 +isr_stub_%+%1: + cli + push %1 + pusha + call exception_handler + popa + add esp, 8 + iret +%endmacro +%macro isr_no_err_stub 1 +isr_stub_%+%1: + cli + push 0 + push %1 + pusha + call exception_handler + popa + add esp, 12 + iret +%endmacro + +isr_no_err_stub 0 +isr_no_err_stub 1 +isr_no_err_stub 2 +isr_no_err_stub 3 +isr_no_err_stub 4 +isr_no_err_stub 5 +isr_no_err_stub 6 +isr_no_err_stub 7 +isr_err_stub 8 +isr_no_err_stub 9 +isr_err_stub 10 +isr_err_stub 11 +isr_err_stub 12 +isr_err_stub 13 +isr_err_stub 14 +isr_no_err_stub 15 +isr_no_err_stub 16 +isr_err_stub 17 +isr_no_err_stub 18 +isr_no_err_stub 19 +isr_no_err_stub 20 +isr_no_err_stub 21 +isr_no_err_stub 22 +isr_no_err_stub 23 +isr_no_err_stub 24 +isr_no_err_stub 25 +isr_no_err_stub 26 +isr_no_err_stub 27 +isr_no_err_stub 28 +isr_no_err_stub 29 +isr_err_stub 30 +isr_no_err_stub 31 + +global isr_stub_table +isr_stub_table: +%assign i 0 +%rep 32 + dd isr_stub_%+i +%assign i i+1 +%endrep + global ps2_isr ps2_isr: + cli extern ps2_handler pushad cld @@ -66,7 +127,8 @@ ps2_isr: ; GDT for a flat memory layout ; We get access to all memory and can utilize paging -gdt_start: +global gdt +gdt: gdt_null: dq 0 gdt_kern_code: @@ -83,11 +145,27 @@ gdt_kern_data: db 0b10010010 db 0b11001111 db 0x00 +gdt_user_code: + dw 0xffff + dw 0x0000 + db 0x00 + db 0b11111010 + db 0b11001111 + db 0x00 +gdt_user_data: + dw 0xffff + dw 0x0000 + db 0x00 + db 0b11110010 + db 0b11001111 + db 0x00 +tss: + dq 0 gdt_end: gdtp: - dw gdt_end - gdt_start - 1 - dd gdt_start + dw gdt_end - gdt - 1 + dd gdt ; Reserve 16KiB of kernel stack space