diff --git a/Makefile b/Makefile index 3fb4cb0..12f73cb 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,12 @@ FONT_SRC = src/font/font8x16.c CONSOLE_SRC = src/drivers/console/console.c GDT_SRC = src/arch/x86/gdt.c GDT_FLUSH_SRC = src/arch/x86/gdt_flush.asm +IDT_SRC = src/arch/x86/idt.c +IDT_FLUSH_SRC = src/arch/x86/idt_flush.asm +ISR_SRC = src/arch/x86/isr.asm +IRQ_SRC = src/arch/x86/irq.asm +MMAP_SRC = src/mem/mmap.c +PMM_SRC = src/mem/pmm.c # Object files BOOT_OBJ = $(BUILD_DIR)/boot.o @@ -33,9 +39,15 @@ FONT_OBJ = $(BUILD_DIR)/font8x16.o CONSOLE_OBJ = $(BUILD_DIR)/console.o GDT_OBJ = $(BUILD_DIR)/gdt.o GDT_FLUSH_OBJ = $(BUILD_DIR)/gdt_flush.o +IDT_OBJ = $(BUILD_DIR)/idt.o +IDT_FLUSH_OBJ = $(BUILD_DIR)/idt_flush.o +ISR_OBJ = $(BUILD_DIR)/isr.o +IRQ_OBJ = $(BUILD_DIR)/irq.o +MMAP_OBJ = $(BUILD_DIR)/mmap.o +PMM_OBJ = $(BUILD_DIR)/pmm.o # All object files -OBJS = $(BOOT_OBJ) $(KERNEL_OBJ) $(VIDEO_OBJ) $(FONT_OBJ) $(CONSOLE_OBJ) $(GDT_OBJ) $(GDT_FLUSH_OBJ) +OBJS = $(BOOT_OBJ) $(KERNEL_OBJ) $(VIDEO_OBJ) $(FONT_OBJ) $(CONSOLE_OBJ) $(GDT_OBJ) $(GDT_FLUSH_OBJ) $(IDT_OBJ) $(IDT_FLUSH_OBJ) $(ISR_OBJ) $(IRQ_OBJ) $(MMAP_OBJ) $(PMM_OBJ) # Output files KERNEL_ELF = $(BUILD_DIR)/kernel.elf @@ -113,6 +125,42 @@ $(GDT_FLUSH_OBJ): $(GDT_FLUSH_SRC) @echo "Compiling GDT flush..." $(NASM) $(NASMFLAGS) $(GDT_FLUSH_SRC) -o $(GDT_FLUSH_OBJ) +# Compile IDT +$(IDT_OBJ): $(IDT_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling IDT..." + $(CC) $(CFLAGS) -c $(IDT_SRC) -o $(IDT_OBJ) + +# Compile IDT flush (assembly) +$(IDT_FLUSH_OBJ): $(IDT_FLUSH_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling IDT flush..." + $(NASM) $(NASMFLAGS) $(IDT_FLUSH_SRC) -o $(IDT_FLUSH_OBJ) + +# Compile ISR (assembly) +$(ISR_OBJ): $(ISR_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling ISR..." + $(NASM) $(NASMFLAGS) $(ISR_SRC) -o $(ISR_OBJ) + +# Compile IRQ (assembly) +$(IRQ_OBJ): $(IRQ_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling IRQ..." + $(NASM) $(NASMFLAGS) $(IRQ_SRC) -o $(IRQ_OBJ) + +# Compile MMAP +$(MMAP_OBJ): $(MMAP_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling MMAP..." + $(CC) $(CFLAGS) -c $(MMAP_SRC) -o $(MMAP_OBJ) + +# Compile PMM +$(PMM_OBJ): $(PMM_SRC) + @mkdir -p $(BUILD_DIR) + @echo "Compiling PMM..." + $(CC) $(CFLAGS) -c $(PMM_SRC) -o $(PMM_OBJ) + # Clean build artifacts clean: @echo "Cleaning build directory..." diff --git a/include/arch/x86/idt.h b/include/arch/x86/idt.h new file mode 100644 index 0000000..4567b4b --- /dev/null +++ b/include/arch/x86/idt.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void idt_init(void); +void idt_set_gate(uint8_t num, uint32_t base, uint16_t selector, uint8_t flags); + diff --git a/include/drivers/video/video.h b/include/drivers/video/video.h index 4386b5c..a601ada 100644 --- a/include/drivers/video/video.h +++ b/include/drivers/video/video.h @@ -25,4 +25,5 @@ struct multiboot_tag_framebuffer { void video_init(void* mbinfo); void video_clear_screen(void); void video_draw_char(int cx, int cy, char c); +void video_scroll_up(void); diff --git a/include/mem/mmap.h b/include/mem/mmap.h new file mode 100644 index 0000000..1ec19ce --- /dev/null +++ b/include/mem/mmap.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void mmap_dump(uint32_t magic, void* mbinfo); \ No newline at end of file diff --git a/include/mem/pmm.h b/include/mem/pmm.h new file mode 100644 index 0000000..8087a2e --- /dev/null +++ b/include/mem/pmm.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +#define PAGE_SIZE 4096u + +void pmm_init(uint32_t magic, void* mbinfo); +void* pmm_alloc_page(void); +void pmm_free_page(void* page); + +uint32_t pmm_total_pages(void); +uint32_t pmm_free_pages(void); + diff --git a/linker.ld b/linker.ld index 620ee92..94760b5 100644 --- a/linker.ld +++ b/linker.ld @@ -4,6 +4,8 @@ ENTRY(_start) SECTIONS { . = 1M; + kernel_start = .; + .multiboot : { *(.multiboot) } @@ -24,4 +26,7 @@ SECTIONS { *(COMMON) *(.bss*) } + + kernel_end = .; + } diff --git a/src/arch/x86/gdt.c b/src/arch/x86/gdt.c index d2930df..9838d89 100644 --- a/src/arch/x86/gdt.c +++ b/src/arch/x86/gdt.c @@ -1,5 +1,4 @@ #include "arch/x86/gdt.h" -#include "drivers/console/console.h" // gdt entry 8byte struct gdt_entry { @@ -11,7 +10,7 @@ struct gdt_entry { uint8_t base_high; // base [24:31] } __attribute__((packed)); // do not add padding in struct -// gdtr pointer struct +// gdtr pointer sturct struct gdt_ptr { uint16_t limit; // gdt size uint32_t base; // gdt arr base address @@ -20,7 +19,7 @@ struct gdt_ptr { static struct gdt_entry gdt[3]; static struct gdt_ptr gp; -// Implemented in assembly (see gdt_flush.asm) +// extern implement from assembly extern void gdt_flush(uint32_t); // gdt_entry constructor @@ -62,17 +61,4 @@ void gdt_init(void) { // flush gdt info to cpu gdt_flush((uint32_t)&gp); - - // Verify GDT base is not zero - struct gdt_ptr_dump { - uint16_t limit; - uint32_t base; - } __attribute__((packed)); - - struct gdt_ptr_dump gdtr; - __asm__ __volatile__("sgdt %0" : "=m"(gdtr)); - - if (gdtr.base != 0) { - console_puts("GDT OK\n"); - } } \ No newline at end of file diff --git a/src/arch/x86/idt.c b/src/arch/x86/idt.c new file mode 100644 index 0000000..9f0b8ec --- /dev/null +++ b/src/arch/x86/idt.c @@ -0,0 +1,209 @@ +#include "arch/x86/idt.h" +#include "drivers/console/console.h" + +// IDT entry structure (8 bytes) +struct idt_entry { + uint16_t base_low; // offset bits 0-15 + uint16_t selector; // code segment selector + uint8_t zero; // reserved, always 0 + uint8_t flags; // flags: P, DPL, type + uint16_t base_high; // offset bits 16-31 +} __attribute__((packed)); + +// IDTR structure (6 bytes) +struct idt_ptr { + uint16_t limit; // IDT size - 1 + uint32_t base; // IDT base address +} __attribute__((packed)); + +// IDT with 256 entries (x86 supports 256 interrupts) +static struct idt_entry idt[256]; +static struct idt_ptr idtp; + +// External assembly function to load IDTR +extern void idt_flush(uint32_t); + +// Interrupt handler stubs (will be called from assembly) +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(); + +// IRQ handlers +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(); + +// Interrupt stack frame structure +// Stack layout (from top to bottom after pusha and segment registers): +// GS, FS, ES, DS (pushed manually) +// EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX (pusha) +// int_no, err_code (pushed manually) +// EIP, CS, EFLAGS (pushed by CPU) +// (useresp, ss only in user mode) +struct interrupt_frame { + uint32_t gs, fs, es, ds; // Segment registers (top of stack) + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // pusha order + uint32_t int_no, err_code; // Interrupt number and error code + uint32_t eip, cs, eflags; // CPU pushed registers + uint32_t useresp, ss; // User mode stack (if applicable) +} __attribute__((packed)); + +// Generic interrupt handler (called from assembly stubs) +void idt_handler(struct interrupt_frame* frame) { + console_puts("[IDT] Exception occurred: "); + // Simple number output + if (frame->int_no < 10) { + console_putc('0' + frame->int_no); + } else { + console_putc('0' + (frame->int_no / 10)); + console_putc('0' + (frame->int_no % 10)); + } + console_puts("\n"); +} + +// IRQ handler (called from assembly stubs) +void irq_handler(struct interrupt_frame* frame) { + // Send EOI to PIC if IRQ >= 8 + if (frame->int_no >= 40) { + // Send EOI to slave PIC + __asm__ __volatile__("outb %%al, $0xA0" : : "a"(0x20)); + } + // Send EOI to master PIC + __asm__ __volatile__("outb %%al, $0x20" : : "a"(0x20)); + + // Handle the IRQ + console_puts("[IDT] IRQ occurred: "); + uint8_t irq = frame->int_no - 32; + if (irq < 10) { + console_putc('0' + irq); + } else { + console_putc('0' + (irq / 10)); + console_putc('0' + (irq % 10)); + } + console_puts("\n"); +} + +// Set an IDT gate +void idt_set_gate(uint8_t num, uint32_t base, uint16_t selector, uint8_t flags) { + idt[num].base_low = base & 0xFFFF; + idt[num].base_high = (base >> 16) & 0xFFFF; + idt[num].selector = selector; + idt[num].zero = 0; + idt[num].flags = flags; +} + +void idt_init(void) { + // Set up IDTR + idtp.limit = sizeof(idt) - 1; + idtp.base = (uint32_t)&idt; + + // Initialize all IDT entries to zero + for (int i = 0; i < 256; i++) { + idt_set_gate(i, 0, 0x08, 0); + } + + // Set up exception handlers (ISR 0-31) + // Flags: 0x8E = 10001110 + // Bit 7: Present (1) + // Bits 6-5: DPL = 00 (ring 0) + // Bit 4: Storage segment = 0 (interrupt gate) + // Bits 3-0: Type = 1110 (32-bit interrupt gate) + idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E); + idt_set_gate(1, (uint32_t)isr1, 0x08, 0x8E); + idt_set_gate(2, (uint32_t)isr2, 0x08, 0x8E); + idt_set_gate(3, (uint32_t)isr3, 0x08, 0x8E); + idt_set_gate(4, (uint32_t)isr4, 0x08, 0x8E); + idt_set_gate(5, (uint32_t)isr5, 0x08, 0x8E); + idt_set_gate(6, (uint32_t)isr6, 0x08, 0x8E); + idt_set_gate(7, (uint32_t)isr7, 0x08, 0x8E); + idt_set_gate(8, (uint32_t)isr8, 0x08, 0x8E); + idt_set_gate(9, (uint32_t)isr9, 0x08, 0x8E); + idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E); + idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E); + idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E); + idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E); + idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E); + idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E); + idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E); + idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E); + idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E); + idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E); + idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E); + idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E); + idt_set_gate(22, (uint32_t)isr22, 0x08, 0x8E); + idt_set_gate(23, (uint32_t)isr23, 0x08, 0x8E); + idt_set_gate(24, (uint32_t)isr24, 0x08, 0x8E); + idt_set_gate(25, (uint32_t)isr25, 0x08, 0x8E); + idt_set_gate(26, (uint32_t)isr26, 0x08, 0x8E); + idt_set_gate(27, (uint32_t)isr27, 0x08, 0x8E); + idt_set_gate(28, (uint32_t)isr28, 0x08, 0x8E); + idt_set_gate(29, (uint32_t)isr29, 0x08, 0x8E); + idt_set_gate(30, (uint32_t)isr30, 0x08, 0x8E); + idt_set_gate(31, (uint32_t)isr31, 0x08, 0x8E); + + // Set up IRQ handlers (IRQ 0-15 map to ISR 32-47) + idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); + idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E); + idt_set_gate(34, (uint32_t)irq2, 0x08, 0x8E); + idt_set_gate(35, (uint32_t)irq3, 0x08, 0x8E); + idt_set_gate(36, (uint32_t)irq4, 0x08, 0x8E); + idt_set_gate(37, (uint32_t)irq5, 0x08, 0x8E); + idt_set_gate(38, (uint32_t)irq6, 0x08, 0x8E); + idt_set_gate(39, (uint32_t)irq7, 0x08, 0x8E); + idt_set_gate(40, (uint32_t)irq8, 0x08, 0x8E); + idt_set_gate(41, (uint32_t)irq9, 0x08, 0x8E); + idt_set_gate(42, (uint32_t)irq10, 0x08, 0x8E); + idt_set_gate(43, (uint32_t)irq11, 0x08, 0x8E); + idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E); + idt_set_gate(45, (uint32_t)irq13, 0x08, 0x8E); + idt_set_gate(46, (uint32_t)irq14, 0x08, 0x8E); + idt_set_gate(47, (uint32_t)irq15, 0x08, 0x8E); + + // Load IDTR + idt_flush((uint32_t)&idtp); + + console_puts("[IDT] Initialized\n"); +} + diff --git a/src/arch/x86/idt_flush.asm b/src/arch/x86/idt_flush.asm new file mode 100644 index 0000000..5a8052b --- /dev/null +++ b/src/arch/x86/idt_flush.asm @@ -0,0 +1,9 @@ +[bits 32] + +; IDT flush function +global idt_flush +idt_flush: + mov eax, [esp + 4] ; param idt_ptr address + lidt [eax] ; load into IDTR + ret + diff --git a/src/arch/x86/irq.asm b/src/arch/x86/irq.asm new file mode 100644 index 0000000..b0cd94f --- /dev/null +++ b/src/arch/x86/irq.asm @@ -0,0 +1,71 @@ +[bits 32] + +; IRQ handler stubs +%macro IRQ 2 +global irq%1 +irq%1: + cli + push dword 0 ; dummy error code + push dword %2 ; interrupt number (IRQ 0-15 map to ISR 32-47) + jmp irq_common_stub +%endmacro + +; IRQ handlers (IRQ 0-15 map to ISR 32-47) +IRQ 0, 32 ; Timer +IRQ 1, 33 ; Keyboard +IRQ 2, 34 ; Cascade +IRQ 3, 35 ; COM2 +IRQ 4, 36 ; COM1 +IRQ 5, 37 ; LPT2 +IRQ 6, 38 ; Floppy +IRQ 7, 39 ; LPT1 +IRQ 8, 40 ; CMOS real-time clock +IRQ 9, 41 ; Free +IRQ 10, 42 ; Free +IRQ 11, 43 ; Free +IRQ 12, 44 ; PS/2 mouse +IRQ 13, 45 ; FPU +IRQ 14, 46 ; Primary ATA +IRQ 15, 47 ; Secondary ATA + +; External C handler +extern irq_handler + +; Common IRQ stub +irq_common_stub: + ; Save all registers + pusha + + ; Save segment registers + push ds + push es + push fs + push gs + + ; Load kernel data segment + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Call C handler + push esp ; pass pointer to stack frame + call irq_handler + add esp, 4 ; clean up stack + + ; Restore segment registers + pop gs + pop fs + pop es + pop ds + + ; Restore all registers + popa + + ; Clean up error code and interrupt number + add esp, 8 + + ; Return from interrupt + iret + diff --git a/src/arch/x86/isr.asm b/src/arch/x86/isr.asm new file mode 100644 index 0000000..a50faa2 --- /dev/null +++ b/src/arch/x86/isr.asm @@ -0,0 +1,96 @@ +[bits 32] + +; Common interrupt handler stub +; This pushes the interrupt number and jumps to the C handler +%macro ISR_NOERRCODE 1 +global isr%1 +isr%1: + cli + push dword 0 ; dummy error code + push dword %1 ; interrupt number + jmp isr_common_stub +%endmacro + +%macro ISR_ERRCODE 1 +global isr%1 +isr%1: + cli + push dword %1 ; interrupt number (error code already on stack) + jmp isr_common_stub +%endmacro + +; Exception handlers (ISR 0-31) +ISR_NOERRCODE 0 ; Division by zero +ISR_NOERRCODE 1 ; Debug +ISR_NOERRCODE 2 ; Non-maskable interrupt +ISR_NOERRCODE 3 ; Breakpoint +ISR_NOERRCODE 4 ; Overflow +ISR_NOERRCODE 5 ; Bound range exceeded +ISR_NOERRCODE 6 ; Invalid opcode +ISR_NOERRCODE 7 ; Device not available +ISR_ERRCODE 8 ; Double fault (has error code) +ISR_NOERRCODE 9 ; Coprocessor segment overrun +ISR_ERRCODE 10 ; Invalid TSS (has error code) +ISR_ERRCODE 11 ; Segment not present (has error code) +ISR_ERRCODE 12 ; Stack fault (has error code) +ISR_ERRCODE 13 ; General protection fault (has error code) +ISR_ERRCODE 14 ; Page fault (has error code) +ISR_NOERRCODE 15 ; Reserved +ISR_NOERRCODE 16 ; x87 floating-point exception +ISR_ERRCODE 17 ; Alignment check (has error code) +ISR_NOERRCODE 18 ; Machine check +ISR_NOERRCODE 19 ; SIMD floating-point exception +ISR_NOERRCODE 20 ; Virtualization exception +ISR_NOERRCODE 21 ; Reserved +ISR_NOERRCODE 22 ; Reserved +ISR_NOERRCODE 23 ; Reserved +ISR_NOERRCODE 24 ; Reserved +ISR_NOERRCODE 25 ; Reserved +ISR_NOERRCODE 26 ; Reserved +ISR_NOERRCODE 27 ; Reserved +ISR_NOERRCODE 28 ; Reserved +ISR_NOERRCODE 29 ; Reserved +ISR_NOERRCODE 30 ; Security exception +ISR_NOERRCODE 31 ; Reserved + +; External C handler +extern idt_handler + +; Common ISR stub +isr_common_stub: + ; Save all registers + pusha + + ; Save segment registers + push ds + push es + push fs + push gs + + ; Load kernel data segment + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ; Call C handler + push esp ; pass pointer to stack frame + call idt_handler + add esp, 4 ; clean up stack + + ; Restore segment registers + pop gs + pop fs + pop es + pop ds + + ; Restore all registers + popa + + ; Clean up error code and interrupt number + add esp, 8 + + ; Return from interrupt + iret + diff --git a/src/drivers/console/console.c b/src/drivers/console/console.c index 41c6f22..2d0fa55 100644 --- a/src/drivers/console/console.c +++ b/src/drivers/console/console.c @@ -33,10 +33,6 @@ void console_clear(void) { // Put a single character void console_putc(char c) { - // Check if cursor is out of bounds - if (g_cursor_y >= g_console_height) - return; - if (c == '\n') { // Newline: move to next line g_cursor_x = 0; @@ -64,6 +60,12 @@ void console_putc(char c) { g_cursor_y++; } } + + // Scroll if cursor goes beyond screen + if (g_cursor_y >= g_console_height) { + video_scroll_up(); + g_cursor_y = g_console_height - 1; + } } // Put a string diff --git a/src/drivers/video/video.c b/src/drivers/video/video.c index 22ba4cf..b3dcbef 100644 --- a/src/drivers/video/video.c +++ b/src/drivers/video/video.c @@ -109,3 +109,53 @@ void video_draw_char(int cx, int cy, char c) { } } } + +// Scroll screen up by one line +void video_scroll_up(void) { + if (g_use_vga_text) { + // VGA text mode: copy lines 1-24 to 0-23, clear line 24 + volatile uint16_t* vga = (volatile uint16_t*)0xB8000; + for (int y = 0; y < 24; y++) { + for (int x = 0; x < 80; x++) { + vga[y * 80 + x] = vga[(y + 1) * 80 + x]; + } + } + // Clear last line + for (int x = 0; x < 80; x++) { + vga[24 * 80 + x] = 0x0000; + } + } else { + // Framebuffer mode + if (!g_fb || g_fb->framebuffer_bpp != 32) + return; + + uint32_t width = g_fb->framebuffer_width; + uint32_t height = g_fb->framebuffer_height; + uint32_t pitch = g_fb->framebuffer_pitch; + uint32_t* buffer = (uint32_t*)(uintptr_t)g_fb->framebuffer_addr; + uint32_t line_height = FONT8X16_HEIGHT; + uint32_t chars_per_line = width / FONT8X16_WIDTH; + uint32_t lines_per_screen = height / line_height; + + // Copy lines 1 to (lines_per_screen-1) to lines 0 to (lines_per_screen-2) + for (uint32_t line = 0; line < lines_per_screen - 1; line++) { + uint32_t src_y = (line + 1) * line_height; + uint32_t dst_y = line * line_height; + for (uint32_t row = 0; row < line_height; row++) { + uint32_t* src_row = (uint32_t*)((uint8_t*)buffer + pitch * (src_y + row)); + uint32_t* dst_row = (uint32_t*)((uint8_t*)buffer + pitch * (dst_y + row)); + for (uint32_t x = 0; x < width; x++) { + dst_row[x] = src_row[x]; + } + } + } + // Clear last line + uint32_t last_line_y = (lines_per_screen - 1) * line_height; + for (uint32_t row = 0; row < line_height; row++) { + uint32_t* last_row = (uint32_t*)((uint8_t*)buffer + pitch * (last_line_y + row)); + for (uint32_t x = 0; x < width; x++) { + last_row[x] = 0xFF000000; // black + } + } + } +} diff --git a/src/kernel.c b/src/kernel.c index c7fc76d..892a826 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,6 +1,9 @@ #include #include "drivers/console/console.h" +#include "mem/mmap.h" +#include "mem/pmm.h" #include "arch/x86/gdt.h" +#include "arch/x86/idt.h" #define MB2_MAGIC 0x36d76289 @@ -8,6 +11,25 @@ static void hlt_loop(void) { for(;;) __asm__ __volatile__("hlt"); } +static void console_putu32(uint32_t v) { + char buf[11]; + int idx = 0; + + if (v == 0) { + console_putc('0'); + return; + } + + while (v > 0 && idx < 10) { + buf[idx++] = (char)('0' + (v % 10)); + v /= 10; + } + + while (idx--) { + console_putc(buf[idx]); + } +} + void kernel_main(uint32_t magic, void* mbinfo) { if (magic != MB2_MAGIC) hlt_loop(); @@ -18,6 +40,39 @@ void kernel_main(uint32_t magic, void* mbinfo) { // Initialize GDT (will print "GDT OK" if successful) gdt_init(); + + // Initialize IDT + idt_init(); + + mmap_dump(magic, mbinfo); + + // Initialize PMM + console_puts("\n[PMM] Initializing...\n"); + pmm_init(magic, mbinfo); + + // Test: Allocate and free pages + console_puts("[PMM] Testing page allocation...\n"); + void* page1 = pmm_alloc_page(); + void* page2 = pmm_alloc_page(); + void* page3 = pmm_alloc_page(); + + if (page1 && page2 && page3) { + console_puts("[PMM] Allocated 3 pages successfully\n"); + console_puts("[PMM] Free pages after allocation: "); + console_putu32(pmm_free_pages()); + console_puts("\n"); + + pmm_free_page(page1); + pmm_free_page(page2); + pmm_free_page(page3); + + console_puts("[PMM] Freed 3 pages\n"); + console_puts("[PMM] Free pages after free: "); + console_putu32(pmm_free_pages()); + console_puts("\n"); + } else { + console_puts("[PMM] Failed to allocate pages\n"); + } // Test: Put string console_puts("Hello World!\n"); diff --git a/src/mem/mmap.c b/src/mem/mmap.c new file mode 100644 index 0000000..5a318c4 --- /dev/null +++ b/src/mem/mmap.c @@ -0,0 +1,112 @@ +#include "mem/mmap.h" +#include "drivers/console/console.h" + +#include +#include + +// Multiboot2 bootloader magic (GRUB가 EAX에 넣어줌) +#define MB2_MAGIC 0x36d76289 + +// Multiboot2 tag types +#define MB2_TAG_TYPE_END 0 +#define MB2_TAG_TYPE_MMAP 6 + +struct multiboot_tag { + uint32_t type; + uint32_t size; +}; + +// MMAP tag header (tag type 6) +struct multiboot_tag_mmap { + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; + // entries follow +}; + +// One mmap entry +struct multiboot_mmap_entry { + uint64_t addr; + uint64_t len; + uint32_t type; // 1 = available + uint32_t zero; +}; + +static struct multiboot_tag_mmap* find_mmap_tag(void* mbinfo) { + // Multiboot2 info starts with: + // u32 total_size, u32 reserved + uint8_t* p = (uint8_t*)mbinfo + 8; + + while (1) { + struct multiboot_tag* tag = (struct multiboot_tag*)p; + + if (tag->type == MB2_TAG_TYPE_END) return NULL; + if (tag->type == MB2_TAG_TYPE_MMAP) return (struct multiboot_tag_mmap*)tag; + + // tags are 8-byte aligned + p += (tag->size + 7) & ~7u; + } +} + +static void console_putu32(uint32_t v) { + char buf[11]; + int idx = 0; + + if (v == 0) { + console_putc('0'); + return; + } + + while (v > 0 && idx < 10) { + buf[idx++] = (char)('0' + (v % 10)); + v /= 10; + } + + while (idx--) { + console_putc(buf[idx]); + } +} + +static void console_puthex64(uint64_t v) { + console_puts("0x"); + for (int i = 60; i >= 0; i -= 4) { + uint8_t nibble = (uint8_t)((v >> i) & 0xFULL); + char c = (nibble < 10) ? (char)('0' + nibble) : (char)('a' + (nibble - 10)); + console_putc(c); + } +} + +void mmap_dump(uint32_t magic, void* mbinfo) { + if (magic != MB2_MAGIC) { + console_puts("[MMAP] invalid magic\n"); + return; + } + + struct multiboot_tag_mmap* mm = find_mmap_tag(mbinfo); + if (!mm) { + console_puts("[MMAP] no mmap tag\n"); + return; + } + + console_puts("[MMAP] found\n"); + + uint8_t* start = (uint8_t*)mm + sizeof(*mm); + uint8_t* end = (uint8_t*)mm + mm->size; + + for (uint8_t* p = start; p < end; p += mm->entry_size) { + struct multiboot_mmap_entry* e = (struct multiboot_mmap_entry*)p; + + console_puts(" type="); + console_putu32(e->type); + + console_puts(" addr="); + console_puthex64(e->addr); + + console_puts(" len="); + console_puthex64(e->len); + + if (e->type == 1) console_puts(" (usable)"); + console_putc('\n'); + } +} \ No newline at end of file diff --git a/src/mem/pmm.c b/src/mem/pmm.c new file mode 100644 index 0000000..51710bb --- /dev/null +++ b/src/mem/pmm.c @@ -0,0 +1,370 @@ +#include "mem/pmm.h" +#include "mem/mmap.h" +#include "drivers/console/console.h" +#include +#include +#include + +#define MB2_MAGIC 0x36d76289 +#define MB2_TAG_TYPE_END 0 +#define MB2_TAG_TYPE_MMAP 6 + +struct multiboot_tag { + uint32_t type; + uint32_t size; +}; + +struct multiboot_tag_mmap { + uint32_t type; + uint32_t size; + uint32_t entry_size; + uint32_t entry_version; +}; + +struct multiboot_mmap_entry { + uint64_t addr; + uint64_t len; + uint32_t type; + uint32_t zero; +}; + +static uint32_t total_pages = 0; +static uint32_t free_pages = 0; +static uint8_t g_bitmap[128 * 1024]; +static uint8_t* bitmap = NULL; +static uint32_t bitmap_size = 0; +static uint64_t memory_start = 0; +static uint64_t memory_end = 0; + +extern char kernel_start; +extern char kernel_end; +static inline void bitmap_set(uint32_t page_idx) { + if (!bitmap || page_idx >= total_pages) return; + uint32_t byte_idx = page_idx / 8; + uint32_t bit_idx = page_idx % 8; + if (byte_idx >= bitmap_size) return; + bitmap[byte_idx] |= (1u << bit_idx); +} + +static inline void bitmap_clear(uint32_t page_idx) { + if (!bitmap || page_idx >= total_pages) return; + uint32_t byte_idx = page_idx / 8; + uint32_t bit_idx = page_idx % 8; + if (byte_idx >= bitmap_size) return; + bitmap[byte_idx] &= ~(1u << bit_idx); +} + +static inline bool bitmap_get(uint32_t page_idx) { + if (!bitmap || page_idx >= total_pages) return true; + uint32_t byte_idx = page_idx / 8; + uint32_t bit_idx = page_idx % 8; + if (byte_idx >= bitmap_size) return true; + return (bitmap[byte_idx] & (1u << bit_idx)) != 0; +} + +static struct multiboot_tag_mmap* find_mmap_tag(void* mbinfo) { + uint8_t* p = (uint8_t*)mbinfo + 8; + + while (1) { + struct multiboot_tag* tag = (struct multiboot_tag*)p; + + if (tag->type == MB2_TAG_TYPE_END) return NULL; + if (tag->type == MB2_TAG_TYPE_MMAP) return (struct multiboot_tag_mmap*)tag; + + p += (tag->size + 7) & ~7u; + } +} + +void pmm_init(uint32_t magic, void* mbinfo) { + console_puts("[PMM] Starting initialization...\n"); + + if (magic != MB2_MAGIC) { + console_puts("[PMM] Invalid magic\n"); + return; + } + + struct multiboot_tag_mmap* mm = find_mmap_tag(mbinfo); + if (!mm) { + console_puts("[PMM] No memory map tag found\n"); + return; + } + + console_puts("[PMM] Memory map found\n"); + + uint8_t* start = (uint8_t*)mm + sizeof(*mm); + uint8_t* end = (uint8_t*)mm + mm->size; + + uint64_t max_addr = 0; + uint64_t min_addr = 0xFFFFFFFFFFFFFFFFULL; + + console_puts("[PMM] Scanning memory map...\n"); + + uint32_t entry_size = mm->entry_size; + if (entry_size == 0 || entry_size > 64) { + console_puts("[PMM] Invalid entry size\n"); + return; + } + + uint8_t* p = start; + uint32_t entry_count = 0; + + while (p < end && entry_count < 64) { + if (p + sizeof(struct multiboot_mmap_entry) > end) { + break; + } + + struct multiboot_mmap_entry* e = (struct multiboot_mmap_entry*)p; + + if (e->type == 1) { + uint32_t addr_low = (uint32_t)e->addr; + uint32_t addr_high = (uint32_t)(e->addr >> 32); + uint32_t len_low = (uint32_t)e->len; + uint32_t len_high = (uint32_t)(e->len >> 32); + + // Only process 32-bit addresses + if (addr_high == 0 && len_high == 0) { + uint32_t entry_start_32 = addr_low; + uint32_t entry_end_32 = addr_low + len_low; + uint64_t entry_start = (uint64_t)entry_start_32; + uint64_t entry_end = (uint64_t)entry_end_32; + + if (min_addr == 0xFFFFFFFFFFFFFFFFULL || entry_start < min_addr) { + min_addr = entry_start; + } + if (entry_end > max_addr) { + max_addr = entry_end; + } + } + } + + p += entry_size; + entry_count++; + } + + console_puts("[PMM] Memory scan complete\n"); + + if (min_addr == 0xFFFFFFFFFFFFFFFFULL || max_addr == 0) { + console_puts("[PMM] No usable memory found\n"); + return; + } + + console_puts("[PMM] Aligning memory...\n"); + + uint32_t min_addr_32 = (uint32_t)min_addr; + uint32_t max_addr_32 = (uint32_t)max_addr; + uint32_t page_mask_32 = ~(PAGE_SIZE - 1); + + uint32_t mem_start_32 = (min_addr_32 + PAGE_SIZE - 1) & page_mask_32; + + // Exclude 0~1MB from PMM management (standard approach) + const uint32_t PMM_MIN_ADDR = 0x100000; + if (mem_start_32 < PMM_MIN_ADDR) { + mem_start_32 = PMM_MIN_ADDR; + } + + uint32_t mem_end_32 = max_addr_32 & page_mask_32; + + if (mem_end_32 <= mem_start_32) { + console_puts("[PMM] Invalid memory range\n"); + return; + } + + memory_start = (uint64_t)mem_start_32; + memory_end = (uint64_t)mem_end_32; + + uint32_t mem_diff_32 = mem_end_32 - mem_start_32; + total_pages = mem_diff_32 >> 12; + + if (total_pages > 0x100000) { + total_pages = 0x100000; + } + + console_puts("[PMM] Page count calculated\n"); + + bitmap_size = (total_pages + 7) / 8; + + console_puts("[PMM] Memory range calculated\n"); + + console_puts("[PMM] Calculating kernel region...\n"); + + uint32_t kernel_start_addr_32 = (uint32_t)&kernel_start; + uint32_t kernel_end_addr_32 = (uint32_t)&kernel_end; + uint64_t kernel_start_addr = (uint64_t)kernel_start_addr_32; + uint64_t kernel_end_addr = (uint64_t)kernel_end_addr_32; + + uint64_t kernel_start_page = 0; + uint64_t kernel_end_page = 0; + + if (kernel_start_addr >= memory_start && kernel_start_addr < memory_end) { + uint64_t kernel_offset = kernel_start_addr - memory_start; + kernel_start_page = kernel_offset >> 12; + + uint64_t kernel_end_offset = kernel_end_addr - memory_start; + kernel_end_page = (kernel_end_offset + PAGE_SIZE - 1) >> 12; + if (kernel_end_page > (uint64_t)total_pages) { + kernel_end_page = total_pages; + } + } + + console_puts("[PMM] Kernel region calculated\n"); + + bitmap = g_bitmap; + + uint32_t max_bitmap_size = sizeof(g_bitmap); + if (bitmap_size > max_bitmap_size) { + console_puts("[PMM] Bitmap size exceeds static allocation\n"); + bitmap_size = max_bitmap_size; + total_pages = max_bitmap_size * 8; + } + + console_puts("[PMM] Bitmap initialized (static allocation)\n"); + + // Initialize bitmap to 0xFF (all pages marked as used) + // BSS initializes to 0, but 0 means free in our semantics, so we need to set all to used first + if (bitmap != NULL && bitmap_size > 0) { + uint8_t* bmp = bitmap; + uint32_t size = bitmap_size; + + __asm__ __volatile__ ( + "cld\n\t" + "rep stosb\n\t" + : "+c" (size), "+D" (bmp) + : "a" (0xFF) + : "memory" + ); + } + console_puts("[PMM] Bitmap set to all used (0xFF)\n"); + + console_puts("[PMM] Marking free pages...\n"); + + free_pages = 0; + p = start; + entry_count = 0; + uint32_t memory_start_32 = (uint32_t)memory_start; + uint32_t memory_end_32 = (uint32_t)memory_end; + + while (p < end && entry_count < 64) { + if (p + sizeof(struct multiboot_mmap_entry) > end) { + break; + } + + struct multiboot_mmap_entry* e = (struct multiboot_mmap_entry*)p; + + if (e->type == 1) { + uint32_t addr_low = (uint32_t)e->addr; + uint32_t addr_high = (uint32_t)(e->addr >> 32); + uint32_t len_low = (uint32_t)e->len; + uint32_t len_high = (uint32_t)(e->len >> 32); + + if (addr_high == 0 && len_high == 0) { + uint32_t entry_start_32 = addr_low; + uint32_t entry_end_32 = addr_low + len_low; + uint32_t entry_start_aligned = (entry_start_32 + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + uint32_t entry_end_aligned = entry_end_32 & ~(PAGE_SIZE - 1); + + if (entry_end_aligned > entry_start_aligned) { + if (entry_start_aligned < memory_start_32) entry_start_aligned = memory_start_32; + if (entry_end_aligned > memory_end_32) entry_end_aligned = memory_end_32; + + uint32_t page_start_offset = entry_start_aligned - memory_start_32; + uint32_t page_end_offset = entry_end_aligned - memory_start_32; + uint32_t page_start = page_start_offset >> 12; + uint32_t page_end = page_end_offset >> 12; + + for (uint32_t page_idx = page_start; page_idx < page_end && page_idx < total_pages; page_idx++) { + if (kernel_start_page > 0 && kernel_end_page > 0 && + page_idx >= (uint32_t)kernel_start_page && page_idx < (uint32_t)kernel_end_page) { + continue; + } + + if (bitmap != NULL) { + bitmap_clear(page_idx); + free_pages++; + } + } + } + } + } + + p += entry_size; + entry_count++; + } + + console_puts("[PMM] Initialized: "); + if (total_pages > 0) { + uint32_t t = total_pages; + char buf[12]; + int idx = 0; + while (t > 0 && idx < 11) { + buf[idx++] = (char)('0' + (t % 10)); + t /= 10; + } + while (idx--) console_putc(buf[idx]); + } + console_puts(" total pages, "); + if (free_pages > 0) { + uint32_t f = free_pages; + char buf[12]; + int idx = 0; + while (f > 0 && idx < 11) { + buf[idx++] = (char)('0' + (f % 10)); + f /= 10; + } + while (idx--) console_putc(buf[idx]); + } + console_puts(" free pages\n"); +} + +void* pmm_alloc_page(void) { + if (!bitmap || free_pages == 0) { + return NULL; + } + + for (uint32_t i = 0; i < total_pages; i++) { + if (!bitmap_get(i)) { + bitmap_set(i); + free_pages--; + return (void*)(memory_start + (uint64_t)i * PAGE_SIZE); + } + } + + return NULL; +} + +void pmm_free_page(void* page) { + if (!bitmap || !page) { + return; + } + + uint64_t page_addr = (uint64_t)page; + + if (page_addr < memory_start || page_addr >= memory_end) { + return; + } + + if ((page_addr & 0xFFF) != 0) { + return; + } + + uint32_t page_offset = (uint32_t)(page_addr - memory_start); + uint32_t page_idx = page_offset >> 12; + + if (page_idx >= total_pages) { + return; + } + + if (!bitmap_get(page_idx)) { + return; + } + + bitmap_clear(page_idx); + free_pages++; +} + +uint32_t pmm_total_pages(void) { + return total_pages; +} + +uint32_t pmm_free_pages(void) { + return free_pages; +}