From 651c1fde6e3c790465532e719f7e6490ff572599 Mon Sep 17 00:00:00 2001 From: Person2random Date: Fri, 6 Mar 2026 01:32:55 +0200 Subject: [PATCH] This is a long one.. So we first added complete 4kb paging, Then we added a new wait command to test the wait functionality and then made it so that wait (Now renamed to kwait) waits more efficeintly, I certainly was waiting for this./myos Now the features added will help with implementing schedulers and a simple VM Note: using waitmode 0 is decaprecated --- Makefile | 8 ++-- console.c | 6 +++ isr.c | 31 +++++++++--- kernel.c | 31 +++++++----- kstd.c | 20 +++----- kstd.h | 2 +- linker.ld | 72 ++++++++++------------------ multiboot.h | 26 ++++++++++ paging.c | 7 ++- paging.h | 5 +- pmm.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++ pmm.h | 6 +++ 12 files changed, 260 insertions(+), 89 deletions(-) create mode 100644 multiboot.h create mode 100644 pmm.c create mode 100644 pmm.h diff --git a/Makefile b/Makefile index a64cd7b..dde53e8 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,10 @@ build: i686-elf-gcc -c kstd.c -o kstd.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra i686-elf-gcc -c console.c -o console.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra i686-elf-gcc -c paging.c -o paging.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra - + i686-elf-gcc -c pmm.c -o pmm.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra i686-elf-gcc -T linker.ld -o myos -ffreestanding -O2 -nostdlib \ - boot.o kernel.o terminal.o gdt.o gdtflush.o loadidt.o isr0.o isr.o idt.o irq.o kstd.o console.o paging.o -lgcc + boot.o kernel.o terminal.o gdt.o gdtflush.o loadidt.o isr0.o isr.o idt.o irq.o kstd.o console.o paging.o pmm.o -lgcc mkdir -p isodir/boot/grub cp myos isodir/boot/myos @@ -30,6 +30,6 @@ build: grub-mkrescue -o myos.iso isodir run: - qemu-system-i386 -kernel myos + qemu-system-i386 -cdrom myos.iso clean: - rm -rf boot.o isodir kernel.o myos myos.iso terminal.o gdt.o gdtflush.o loadidt.o isr0.o idt.o isr.o irq.o kstd.o console.o paging.o + rm -rf boot.o isodir kernel.o myos myos.iso terminal.o gdt.o gdtflush.o loadidt.o isr0.o idt.o isr.o irq.o kstd.o console.o paging.o pmm.o diff --git a/console.c b/console.c index f22ef14..e6f9aff 100644 --- a/console.c +++ b/console.c @@ -1,5 +1,6 @@ #include "kstd.h" #include "vga.h" +#include "paging.h" extern uint8_t _kernel_end[]; extern uint8_t _kernel_start[]; @@ -51,6 +52,11 @@ void handle_shell(char *input){ return; } + if (count > 1 && strcmp(tokens[0], "wait")){ + int time = stoia(tokens[1]); + kwait(time); + return; + } terminal_writestring("Invalid command\n"); diff --git a/isr.c b/isr.c index 3638175..80fc5ed 100644 --- a/isr.c +++ b/isr.c @@ -3,7 +3,7 @@ #include "gdt.h" // if needed #include "kstd.h" #include "paging.h" - +#include "pmm.h" // Declare all 32 ISR stubs from your assembly file extern void isr0(void); extern void isr1(void); @@ -46,6 +46,13 @@ static void (*isr_routines[32])(void) = { isr24, isr25, isr26, isr27, isr28, isr29, isr30, isr31 }; +static inline void flush_tlb(void) +{ + uint32_t cr3; + __asm__ volatile("mov %%cr3, %0" : "=r"(cr3)); + __asm__ volatile("mov %0, %%cr3" :: "r"(cr3)); +} + // Initialize IDT entries 0–31 void isr_init(void) { for (uint8_t i = 0; i < 32; i++) { @@ -76,20 +83,30 @@ void isr_handler(int num, uint32_t err) { terminal_writestring((err & 0x4) ? "|U" : "|S"); terminal_writestring((err & 0x8) ? "|RSVD" : ""); terminal_writestring((err & 0x10) ? "|IF" : ""); - terminal_writestring("]"); + terminal_writestring("]\n"); if (cr2 >= HEAP_BASE && cr2 < HEAP_LIMIT) { uint32_t fault_addr = cr2; + uint32_t pde_index = fault_addr >> 22; uint32_t pte_index = (fault_addr >> 12) & 0x3FF; - uint32_t phys = alloc_4mb_chunk(); - page_directory[pde_index] = phys | 0x83; + if (!(page_directory[pde_index] & 1)) { + uint32_t pt_phys = pmm_alloc_frame(); + memset((void*)pt_phys, 0, 4096); + + page_directory[pde_index] = pt_phys | 0x3; + } + + uint32_t* page_table = + (uint32_t*)(page_directory[pde_index] & 0xFFFFF000); - // Flush TLB - __asm__ volatile("mov %%cr3, %%eax; mov %%eax, %%cr3" ::: "eax"); + if (!(page_table[pte_index] & 1)) { + uint32_t page_phys = pmm_alloc_frame(); + page_table[pte_index] = page_phys | 0x3; + } - terminal_writestring(" Heap page mapped\n"); + flush_tlb(); return; } } diff --git a/kernel.c b/kernel.c index 93501d6..e8f02ce 100644 --- a/kernel.c +++ b/kernel.c @@ -1,3 +1,4 @@ +#include "pmm.h" #include #include #include @@ -8,18 +9,25 @@ #include "kstd.h" #include "paging.h" #include "console.h" - +#include "multiboot.h" extern uint8_t kernel_stack[]; // GDT flush expects this symbol; reserve a 16 KiB kernel stack. uint8_t kernel_stack[16384] __attribute__((aligned(16))); void kernel_main(uint32_t magic, void* mb_info){ - if (magic != 0x2BADB002) { - terminal_writestring("Not booted with multiboot2!\n"); - while (1); - } - uint64_t max[400000]; terminal_initialize(); - femboysay("Welcome to eOS, Epstein OS\n"); + if (magic != 0x2BADB002 && magic != 0x36D76289) { + terminal_writestring("Unknown boot magic, halting.\n"); + while (1) __asm__ volatile("hlt"); + } + pmm_init((uint32_t)mb_info); + + uint32_t a = pmm_alloc_frame(); + uint32_t b = pmm_alloc_frame(); + uint32_t c = pmm_alloc_frame(); + + + pmm_free_frame(b); + uint32_t d = pmm_alloc_frame(); femboysay("Will init GDT\n"); gdt_init(); femboysay("GDT initialized\n"); @@ -43,15 +51,14 @@ void kernel_main(uint32_t magic, void* mb_info){ femboysay("Accurate timing implemented\n"); femboysay("Will enable interrupts\n"); __asm__ volatile("sti"); - wait(1); + kwait(1); femboysay("Interrupts enabled\n"); femboysay("Will init paging\n"); init_paging(); femboysay("Paging init\n"); - volatile uint32_t *x = (uint32_t*)0x00300000; - *x = 0xDEADBEEF; - terminal_writestring("4KB identity mapping works\n"); - kmalloc(400000); + uint32_t* heap_test = (uint32_t*)0x40000000; + *heap_test = 1234; + terminal_writestring("Heap page allocated\n"); changeout(handle_shell,0); while (1) { diff --git a/kstd.c b/kstd.c index d709351..645bbee 100644 --- a/kstd.c +++ b/kstd.c @@ -216,27 +216,19 @@ void* memcpy(void* dst, const void* src, size_t n) { //Depends on waitmode. //If waitmode is set to 0 it will count assuming the speed is 18.2 Hz //If waitmode is set to 1,The int passed = number of milliseconds to wait -void wait(int seconds){ +void kwait (int seconds){ if(waitmode == 0){ - uint64_t current = ticks; - uint64_t target = 18.2*seconds + current; - while (1) - { - if(ticks >= target){ - break; - } - } + terminal_writestring("Waiting using waitmode 0 is decaprecated"); + return; } else if (waitmode == 1) { uint64_t current = ticks; uint64_t target = current+seconds; - while (1) + while (ticks < target) { - if (ticks >= target) - { - break; - } + + __asm__ volatile ("sti;hlt"); } diff --git a/kstd.h b/kstd.h index 8f50e71..f9df653 100644 --- a/kstd.h +++ b/kstd.h @@ -10,7 +10,7 @@ size_t readbuf(uint8_t *buf); void append_ibuf(uint8_t c); void* memset(void* dst, int v, size_t n); void* memcpy(void* dst, const void* src, size_t n); -void wait(int seconds); +void kwait(int seconds); void itoa(int value, char* str, int base); void outb(uint16_t port, uint8_t val); uint8_t inb(uint16_t port); diff --git a/linker.ld b/linker.ld index d75546f..d51fcba 100644 --- a/linker.ld +++ b/linker.ld @@ -1,58 +1,36 @@ /* The bootloader will look at this image and start execution at the symbol designated as the entry point. */ + ENTRY(_start) -/* Tell where the various sections of the object files will be put in the final - kernel image. */ SECTIONS { - /* It used to be universally recommended to use 1M as a start offset, - as it was effectively guaranteed to be available under BIOS systems. - However, UEFI has made things more complicated, and experimental data - strongly suggests that 2M is a safer place to load. In 2016, a new - feature was introduced to the multiboot2 spec to inform bootloaders - that a kernel can be loaded anywhere within a range of addresses and - will be able to relocate itself to run from such a loader-selected - address, in order to give the loader freedom in selecting a span of - memory which is verified to be available by the firmware, in order to - work around this issue. This does not use that feature, so 2M was - chosen as a safer option than the traditional 1M. */ - //. = 2M; + /* Load kernel at 2MB */ + . = 2M; _kernel_start = .; - /* First put the multiboot header, as it is required to be put very early - in the image or the bootloader won't recognize the file format. - Next we'll put the .text section. */ - .text BLOCK(4K) : ALIGN(4K) - { - *(.multiboot) - *(.text) - } - - /* Read-only data. */ - .rodata BLOCK(4K) : ALIGN(4K) - { - *(.rodata) - } - - /* Read-write data (initialized) */ - .data BLOCK(4K) : ALIGN(4K) - { - *(.data) - } + /* First loadable section */ + .text ALIGN(4K) : + { + /* Multiboot header must be first thing in first loadable segment */ + KEEP(*(.multiboot)) + *(.text*) + } - /* Read-write data (uninitialized) and stack */ - .bss BLOCK(4K) : ALIGN(4K) - { - *(COMMON) - *(.bss) - } + .rodata ALIGN(4K) : + { + *(.rodata*) + } - /* The compiler may produce other sections, by default it will put them in - a segment with the same name. Simply add stuff here as needed. */ + .data ALIGN(4K) : + { + *(.data*) + } - /* - Linker flag for kernel end - */ + .bss ALIGN(4K) : + { + *(COMMON) + *(.bss*) + } - _kernel_end = .; -} + _kernel_end = .; +} \ No newline at end of file diff --git a/multiboot.h b/multiboot.h new file mode 100644 index 0000000..081bc38 --- /dev/null +++ b/multiboot.h @@ -0,0 +1,26 @@ +#ifndef MULTIBOOT_H +#define MULTIBOOT_H + +#include + +typedef struct { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + uint32_t syms[4]; + uint32_t mmap_length; + uint32_t mmap_addr; +} multiboot_info_t; + +typedef struct { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} multiboot_memory_map_t; + +#endif \ No newline at end of file diff --git a/paging.c b/paging.c index 5f94831..7e6f3b2 100644 --- a/paging.c +++ b/paging.c @@ -8,10 +8,15 @@ uint32_t first_page_table[1024] __attribute__((aligned(4096))); static uint32_t placement = 0; static uint32_t heap_brk = HEAP_BASE; +void* kmalloc(uint32_t size) +{ + size = (size + 7) & ~7; -void* kmalloc(uint32_t size) { uint32_t addr = heap_brk; heap_brk += size; + + if (heap_brk + size >= HEAP_LIMIT) panic("heap exhausted"); + return (void*)addr; } diff --git a/paging.h b/paging.h index b1f1336..22cc89d 100644 --- a/paging.h +++ b/paging.h @@ -1,8 +1,7 @@ #ifndef PAGING_H #define PAGING_H - -#define HEAP_BASE 0x01000000 // 16MB -#define HEAP_LIMIT 0x04000000 +#define HEAP_BASE 0x40000000 +#define HEAP_LIMIT 0x80000000 extern uint32_t page_directory[1024] __attribute__((aligned(4096))); uint32_t alloc_4mb_chunk(); void init_paging(void); diff --git a/pmm.c b/pmm.c new file mode 100644 index 0000000..7874c59 --- /dev/null +++ b/pmm.c @@ -0,0 +1,135 @@ +#include +#include +#include "multiboot.h" +#include "pmm.h" + +#define FRAME_SIZE 0x1000 +#define ALIGN_UP(x,a) (((x) + ((a)-1)) & ~((a)-1)) +#define ALIGN_DOWN(x,a) ((x) & ~((a)-1)) + +extern uint8_t _kernel_end[]; // linker symbol + +static uint32_t *pmm_bitmap = 0; +static uint32_t pmm_frames = 0; // number of frames tracked + +static inline void bit_set(uint32_t frame) { pmm_bitmap[frame>>5] |= (1u << (frame & 31)); } +static inline void bit_clear(uint32_t frame) { pmm_bitmap[frame>>5] &= ~(1u << (frame & 31)); } +static inline uint32_t bit_test(uint32_t frame){ return pmm_bitmap[frame>>5] & (1u << (frame & 31)); } + +static void mark_range_used(uint32_t base, uint32_t end) { + base = ALIGN_DOWN(base, FRAME_SIZE); + end = ALIGN_UP(end, FRAME_SIZE); + for (uint32_t a = base; a < end; a += FRAME_SIZE) bit_set(a / FRAME_SIZE); +} + +static void mark_range_free(uint32_t base, uint32_t end) { + base = ALIGN_UP(base, FRAME_SIZE); + end = ALIGN_DOWN(end, FRAME_SIZE); + for (uint32_t a = base; a < end; a += FRAME_SIZE) bit_clear(a / FRAME_SIZE); +} + +void pmm_init(uint32_t mb_info_ptr) +{ + multiboot_info_t *mbi = (multiboot_info_t*)mb_info_ptr; + + // Need mmap + if (!(mbi->flags & (1u << 6))) { + // No mmap => can't safely PMM. Hard halt. + for (;;) __asm__ volatile("hlt"); + } + + // 1) Find top of usable RAM below 4GB + uint32_t top = 0; + multiboot_memory_map_t *m = (multiboot_memory_map_t*)mbi->mmap_addr; + uint32_t end = mbi->mmap_addr + mbi->mmap_length; + + while ((uint32_t)m < end) { + if (m->type == 1) { + uint64_t base = m->base_addr; + uint64_t len = m->length; + uint64_t e64 = base + len; + + if (base < 0x100000000ULL) { + if (e64 > 0x100000000ULL) e64 = 0x100000000ULL; // clamp to 4GB + if ((uint32_t)e64 > top) top = (uint32_t)e64; + } + } + m = (multiboot_memory_map_t*)((uint32_t)m + m->size + 4); + } + + // frames we track + pmm_frames = top / FRAME_SIZE; + + // 2) Place bitmap right after kernel, aligned + uint32_t bitmap_bytes = (pmm_frames + 7) / 8; + uint32_t bitmap_base = ALIGN_UP((uint32_t)_kernel_end, FRAME_SIZE); + uint32_t bitmap_end = ALIGN_UP(bitmap_base + bitmap_bytes, FRAME_SIZE); + pmm_bitmap = (uint32_t*)bitmap_base; + + // 3) Mark all USED initially (1s) + uint32_t bitmap_words = ALIGN_UP(bitmap_bytes, 4) / 4; + for (uint32_t i = 0; i < bitmap_words; i++) pmm_bitmap[i] = 0xFFFFFFFFu; + + // 4) Free only type==1 regions + m = (multiboot_memory_map_t*)mbi->mmap_addr; + while ((uint32_t)m < end) { + if (m->type == 1) { + uint64_t base = m->base_addr; + uint64_t len = m->length; + uint64_t e64 = base + len; + + if (base < 0x100000000ULL) { + if (e64 > 0x100000000ULL) e64 = 0x100000000ULL; + mark_range_free((uint32_t)base, (uint32_t)e64); + } + } + m = (multiboot_memory_map_t*)((uint32_t)m + m->size + 4); + } + + // 5) Re-reserve critical regions + // Null page + low BIOSy area (optional but wise): keep first 1MB used + mark_range_used(0x00000000, 0x00100000); + + // Kernel is loaded at 2MB in your linker script + mark_range_used(0x00200000, (uint32_t)_kernel_end); + + // Bitmap itself must be used + mark_range_used(bitmap_base, bitmap_end); + + // Multiboot info structure lives somewhere in RAM; reserve at least its first page. + // (Later you can reserve more precisely; this is safe and small.) + mark_range_used(mb_info_ptr, mb_info_ptr + FRAME_SIZE); + + // Modules, if any + if (mbi->flags & (1u << 3)) { // mods_* valid + // multiboot modules struct is 16 bytes each in MB1 + mark_range_used(mbi->mods_addr, mbi->mods_addr + mbi->mods_count * 16); + } +} + +uint32_t pmm_alloc_frame(void) +{ + if (!pmm_bitmap) return 0; + + uint32_t words = (pmm_frames + 31) / 32; + for (uint32_t i = 0; i < words; i++) { + uint32_t w = pmm_bitmap[i]; + if (w != 0xFFFFFFFFu) { + for (uint32_t b = 0; b < 32; b++) { + uint32_t frame = i * 32 + b; + if (frame >= pmm_frames) return 0; + if (!bit_test(frame)) { + bit_set(frame); + return frame * FRAME_SIZE; + } + } + } + } + return 0; +} + +void pmm_free_frame(uint32_t phys_addr) +{ + uint32_t frame = phys_addr / FRAME_SIZE; + if (frame < pmm_frames) bit_clear(frame); +} \ No newline at end of file diff --git a/pmm.h b/pmm.h new file mode 100644 index 0000000..7d04f99 --- /dev/null +++ b/pmm.h @@ -0,0 +1,6 @@ +#pragma once +#include + +void pmm_init(uint32_t mb_info_ptr); +uint32_t pmm_alloc_frame(void); // returns physical addr (4KB aligned) or 0 +void pmm_free_frame(uint32_t phys_addr); // phys_addr must be 4KB aligned \ No newline at end of file