From 8f5e2eae3ec18a6b805cc44f49a8cb6853c32292 Mon Sep 17 00:00:00 2001 From: xamidev Date: Tue, 30 Dec 2025 21:33:38 +0100 Subject: [PATCH 1/7] First steps: getting memory map from Limine request and looking at it --- Makefile | 2 +- src/io/term/term.c | 5 +++++ src/kernel.h | 6 ++---- src/kmain.c | 14 +++++++++++-- src/mem/misc/utils.c | 46 +++++++++++++++++++++++++++++++++++++++++ src/mem/misc/utils.h | 2 ++ src/mem/paging/paging.h | 16 ++++++++++++++ src/mem/paging/pmm.c | 17 +++++++++++++++ src/string/string.c | 6 ++++++ src/string/string.h | 6 ++++++ 10 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 src/mem/paging/paging.h create mode 100644 src/mem/paging/pmm.c create mode 100644 src/string/string.c create mode 100644 src/string/string.h diff --git a/Makefile b/Makefile index 475a555..cf2e519 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCES = src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c +SOURCES = src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c build: rm -f *.o diff --git a/src/io/term/term.c b/src/io/term/term.c index 3a697e6..2fea23e 100644 --- a/src/io/term/term.c +++ b/src/io/term/term.c @@ -1,4 +1,9 @@ // Terminal output +/* +There are a couple of bugs here and there but for now I don't care too much +because this shitty implementation will be replaced one day by Flanterm +(once memory management is okay: paging & kernel malloc) +*/ #include #include diff --git a/src/kernel.h b/src/kernel.h index 217798f..e1cf0b3 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -13,9 +13,7 @@ enum ErrorCodes #include "io/serial/serial.h" #include "io/term/printf.h" -// Still lacks print formatting... -#define DEBUG(log, ...) \ - printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); \ - fctprintf((void*)&skputc, 0, "debug: [%s]: %s\n", __FILE__, log) +#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__) +// printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); #endif diff --git a/src/kmain.c b/src/kmain.c index 09b5a2f..1bff270 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -22,6 +22,13 @@ static volatile struct limine_framebuffer_request framebuffer_request = { .revision = 0 }; +// Memory map request +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_memmap_request memmap_request = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0 +}; + __attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER; @@ -51,17 +58,20 @@ void kmain() term_init(); serial_init(); + if (memmap_request.response == NULL) hcf(); + memmap_display(memmap_request.response); + CLEAR_INTERRUPTS; gdt_init(); idt_init(); timer_init(); SET_INTERRUPTS; - keyboard_init(FR); + //keyboard_init(FR); // Draw something printf("%s, %s!\n", "Hello", "world"); // Yoohoooooo! - DEBUG("kernel initialized successfully! hanging... wow=%d", 42); + //DEBUG("kernel initialized successfully! hanging... wow=%d", 42); hcf(); } diff --git a/src/mem/misc/utils.c b/src/mem/misc/utils.c index a27c898..adbf38e 100644 --- a/src/mem/misc/utils.c +++ b/src/mem/misc/utils.c @@ -1,5 +1,8 @@ #include #include +#include +#include "../../kernel.h" +#include "../../string/string.h" // We won't be linked to standard library, but still need the basic mem* functions // so everything goes allright with the compiler @@ -67,4 +70,47 @@ int memcmp(const void* s1, const void* s2, size_t n) } return 0; +} + +// Display the memmap so we see how the memory is laid out at handoff +void memmap_display(struct limine_memmap_response* response) +{ + DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count); + + for (size_t i=0; ientry_count; i++) + { + struct limine_memmap_entry* entry = response->entries[i]; + char type[32] = {0}; + switch(entry->type) + { + case LIMINE_MEMMAP_USABLE: + strcpy(type, "USABLE"); + break; + case LIMINE_MEMMAP_RESERVED: + strcpy(type, "RESERVED"); + break; + case LIMINE_MEMMAP_ACPI_RECLAIMABLE: + strcpy(type, "ACPI_RECLAIMABLE"); + break; + case LIMINE_MEMMAP_ACPI_NVS: + strcpy(type, "ACPI_NVS"); + break; + case LIMINE_MEMMAP_BAD_MEMORY: + strcpy(type, "BAD_MEMORY"); + break; + case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE: + strcpy(type, "BOOTLOADER_RECLAIMABLE"); + break; + case LIMINE_MEMMAP_KERNEL_AND_MODULES: + strcpy(type, "KERNEL_AND_MODULES"); + break; + case LIMINE_MEMMAP_FRAMEBUFFER: + strcpy(type, "FRAMEBUFFER"); + break; + default: + strcpy(type, "UNKNOWN"); + break; + } + DEBUG("entry %u: [%016x | %u bytes] - %s", i, entry->base, entry->length, type); + } } \ No newline at end of file diff --git a/src/mem/misc/utils.h b/src/mem/misc/utils.h index dd757f4..1b01ca3 100644 --- a/src/mem/misc/utils.h +++ b/src/mem/misc/utils.h @@ -8,4 +8,6 @@ void* memset(void* s, int c, size_t n); void* memmove(void *dest, const void* src, size_t n); int memcmp(const void* s1, const void* s2, size_t n); +void memmap_display(struct limine_memmap_response* response); + #endif \ No newline at end of file diff --git a/src/mem/paging/paging.h b/src/mem/paging/paging.h new file mode 100644 index 0000000..33da2aa --- /dev/null +++ b/src/mem/paging/paging.h @@ -0,0 +1,16 @@ +#ifndef PAGING_PMM_H +#define PAGING_PMM_H + +/* +We are going to use a bitmap, consisting of an array of uint64_t +to represent pages for the PMM (physical memory manager). + +Bit set (1) = page used +Bit clear (0) = page free + +*/ + +#define PAGE_SIZE 4096 +#define BITS_PER_ROW 64 + +#endif \ No newline at end of file diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c new file mode 100644 index 0000000..0028cda --- /dev/null +++ b/src/mem/paging/pmm.c @@ -0,0 +1,17 @@ +// OMG here we are. I'm cooked. + +/* +pmm - Physical Memory Manager +will manage 4kb pages physically +it will probably need to get some info from Limine, +to see which pages are used by kernel/bootloader/mmio/fb etc. +*/ + +#include "paging.h" + +/* +First we'll have to discover the physical memory layout, +and for that we can use a Limine request. +*/ + +uint64_t pages_bitmap[]; \ No newline at end of file diff --git a/src/string/string.c b/src/string/string.c new file mode 100644 index 0000000..1992eee --- /dev/null +++ b/src/string/string.c @@ -0,0 +1,6 @@ +char* strcpy(char *dest, const char *src) +{ + char *temp = dest; + while((*dest++ = *src++)); + return temp; +} \ No newline at end of file diff --git a/src/string/string.h b/src/string/string.h new file mode 100644 index 0000000..49a8ea0 --- /dev/null +++ b/src/string/string.h @@ -0,0 +1,6 @@ +#ifndef STRING_H +#define STRING_H + +char *strcpy(char *dest, const char *src); + +#endif \ No newline at end of file -- 2.49.1 From 05a862e97a50af6879bbf9fb42b4a0db11b8b3a1 Mon Sep 17 00:00:00 2001 From: xamidev Date: Wed, 31 Dec 2025 12:02:41 +0100 Subject: [PATCH 2/7] PMM: init (find biggest usable region) --- Makefile | 2 +- src/kernel.h | 2 +- src/kmain.c | 15 ++++++++++- src/mem/misc/utils.c | 8 +++++- src/mem/misc/utils.h | 1 + src/mem/paging/pmm.c | 60 +++++++++++++++++++++++++++++++++++++++++++- src/mem/paging/pmm.h | 16 ++++++++++++ 7 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 src/mem/paging/pmm.h diff --git a/Makefile b/Makefile index cf2e519..b2ccf57 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCES = src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c +SOURCES = src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c build: rm -f *.o diff --git a/src/kernel.h b/src/kernel.h index e1cf0b3..0daeeef 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -13,7 +13,7 @@ enum ErrorCodes #include "io/serial/serial.h" #include "io/term/printf.h" -#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__) +#define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__) // printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); #endif diff --git a/src/kmain.c b/src/kmain.c index 1bff270..1ea9f90 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -10,6 +10,7 @@ #include "kernel.h" #include "time/timer.h" #include "io/kbd/ps2.h" +#include "mem/paging/pmm.h" // Limine version used __attribute__((used, section(".limine_requests"))) @@ -29,6 +30,13 @@ static volatile struct limine_memmap_request memmap_request = { .revision = 0 }; +// Higher Half Direct Map +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_hhdm_request hhdm_request = { + .id = LIMINE_HHDM_REQUEST, + .revision = 0 +}; + __attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER; @@ -61,13 +69,18 @@ void kmain() if (memmap_request.response == NULL) hcf(); memmap_display(memmap_request.response); + if (hhdm_request.response == NULL) hcf(); + hhdm_display(hhdm_request.response); + + pmm_init(memmap_request.response, hhdm_request.response); + CLEAR_INTERRUPTS; gdt_init(); idt_init(); timer_init(); SET_INTERRUPTS; - //keyboard_init(FR); + keyboard_init(FR); // Draw something printf("%s, %s!\n", "Hello", "world"); diff --git a/src/mem/misc/utils.c b/src/mem/misc/utils.c index adbf38e..227dab0 100644 --- a/src/mem/misc/utils.c +++ b/src/mem/misc/utils.c @@ -111,6 +111,12 @@ void memmap_display(struct limine_memmap_response* response) strcpy(type, "UNKNOWN"); break; } - DEBUG("entry %u: [%016x | %u bytes] - %s", i, entry->base, entry->length, type); + DEBUG("entry %02u: [0x%016x | %016u bytes] - %s", i, entry->base, entry->length, type); } +} + +// Display the HHDM +void hhdm_display(struct limine_hhdm_response* hhdm) +{ + DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset); } \ No newline at end of file diff --git a/src/mem/misc/utils.h b/src/mem/misc/utils.h index 1b01ca3..169d88d 100644 --- a/src/mem/misc/utils.h +++ b/src/mem/misc/utils.h @@ -9,5 +9,6 @@ void* memmove(void *dest, const void* src, size_t n); int memcmp(const void* s1, const void* s2, size_t n); void memmap_display(struct limine_memmap_response* response); +void hhdm_display(struct limine_hhdm_response* hhdm); #endif \ No newline at end of file diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c index 0028cda..0f1e956 100644 --- a/src/mem/paging/pmm.c +++ b/src/mem/paging/pmm.c @@ -8,10 +8,68 @@ to see which pages are used by kernel/bootloader/mmio/fb etc. */ #include "paging.h" +#include +#include +#include +#include "../misc/utils.h" /* First we'll have to discover the physical memory layout, and for that we can use a Limine request. */ -uint64_t pages_bitmap[]; \ No newline at end of file +/* +We will look for the biggest usable physical memory region +and use this for the bitmap. The reserved memory will be ignored. +*/ + +struct usable_memory* usable_mem; +struct limine_memmap_entry* biggest_entry; + +uint64_t* bitmap; + +static void pmm_allocate_bitmap(struct limine_hhdm_response* hhdm) +{ + uint64_t pages = biggest_entry->length / PAGE_SIZE; + DEBUG("we need %u pages (bits) that will fit in %u uint64_t", pages, pages/64); + + bitmap = (uint64_t*)(biggest_entry->base + hhdm->offset); + DEBUG("bitmap will live at 0x%p", bitmap); + + // All pages are marked free since we're in a USABLE region + memset(bitmap, 0x00, pages/64); +} + +static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) +{ + // Max length of a usable memory region + uint64_t length_max = 0; + uint64_t offset = hhdm->offset; + + DEBUG("Usable Memory:"); + for (size_t i=0; ientry_count; i++) + { + struct limine_memmap_entry* entry = memmap->entries[i]; + + if (entry->type == LIMINE_MEMMAP_USABLE) + { + DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", entry->base, entry->base+entry->length, + entry->base+offset, entry->base+entry->length+offset); + if (entry->length > length_max) + { + length_max = entry->length; + biggest_entry = entry; + } + } + } + + DEBUG("Biggest usable memory region:"); + DEBUG("0x%p-0x%p mapped at 0x%p-0x%p", biggest_entry->base, biggest_entry->base + biggest_entry->length, + biggest_entry->base+offset, biggest_entry->base+biggest_entry->length+offset); +} + +void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) +{ + pmm_find_biggest_usable_region(memmap, hhdm); + pmm_allocate_bitmap(hhdm); +} \ No newline at end of file diff --git a/src/mem/paging/pmm.h b/src/mem/paging/pmm.h new file mode 100644 index 0000000..d2c722f --- /dev/null +++ b/src/mem/paging/pmm.h @@ -0,0 +1,16 @@ +#ifndef PAGING_PMM_H +#define PAGING_PMM_H + +#include + +void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm); + +// Might be upgraded to a freelist later. +// For now, we can take the biggest usable region and we will be fine. +struct usable_memory +{ + uint64_t base; // physical + uint64_t length; +}; + +#endif \ No newline at end of file -- 2.49.1 From 075058a958641b3c7067b6ba10c4847ec39382a1 Mon Sep 17 00:00:00 2001 From: xamidev Date: Wed, 31 Dec 2025 17:42:26 +0100 Subject: [PATCH 3/7] PMM: init with freelist --- src/mem/paging/pmm.c | 83 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c index 0f1e956..3ce044b 100644 --- a/src/mem/paging/pmm.c +++ b/src/mem/paging/pmm.c @@ -10,8 +10,10 @@ to see which pages are used by kernel/bootloader/mmio/fb etc. #include "paging.h" #include #include +#include #include #include "../misc/utils.h" +#include "pmm.h" /* First we'll have to discover the physical memory layout, @@ -25,20 +27,42 @@ and use this for the bitmap. The reserved memory will be ignored. struct usable_memory* usable_mem; struct limine_memmap_entry* biggest_entry; - +/* uint64_t* bitmap; +uint64_t* usable_memory_begin; +// Bitmap will be placed at the start of the biggest usable region +// 1 bitmap page = 4096 bytes = 32768 bits +// So 1 bitmap page can track up to 32768 pages of memory static void pmm_allocate_bitmap(struct limine_hhdm_response* hhdm) { uint64_t pages = biggest_entry->length / PAGE_SIZE; - DEBUG("we need %u pages (bits) that will fit in %u uint64_t", pages, pages/64); + uint64_t bitmap_items = (pages+BITS_PER_ROW-1)/BITS_PER_ROW; + DEBUG("we need %u pages (bits) that will fit in %u uint64_t", pages, bitmap_items); bitmap = (uint64_t*)(biggest_entry->base + hhdm->offset); DEBUG("bitmap will live at 0x%p", bitmap); + uint64_t bitmap_bytes = bitmap_items*sizeof(uint64_t); + // All pages are marked free since we're in a USABLE region - memset(bitmap, 0x00, pages/64); -} + memset(bitmap, 0x00, bitmap_bytes); + + uint64_t bitmap_pages = (bitmap_bytes + PAGE_SIZE - 1) / PAGE_SIZE; + + // The 1st memory page we can use is at bitmap + bitmap_pages + usable_memory_begin = (void*)((uintptr_t)bitmap + bitmap_pages*PAGE_SIZE); + + // Mark bitmap as allocated + for (uint64_t i=0; ibase+offset, biggest_entry->base+biggest_entry->length+offset); } +// Offset from Higher Half Direct Map +uint64_t hhdm_off; + +static uintptr_t g_freelist = 0; + +#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off)) +#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off) + +// Stole it +#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1)) + +uintptr_t pmm_alloc() +{ + if (!g_freelist) return 0; + uintptr_t addr = g_freelist; + g_freelist = *(uintptr_t*) PHYS_TO_VIRT(g_freelist); + return addr; +} + +void pmm_free(uintptr_t addr) +{ + *(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist; + g_freelist = addr; +} + +static void pmm_init_freelist() +{ + // We simply call pmm_free() on each page that is marked USABLE + // in our big memory region. + uint64_t base = ALIGN_UP(biggest_entry->base, PAGE_SIZE); + uint64_t end = ALIGN_DOWN(biggest_entry->base + biggest_entry->length, PAGE_SIZE); + + uint64_t page_count=0; + for (uint64_t addr = base; addr < end; addr += PAGE_SIZE) + { + pmm_free(addr); + //DEBUG("page %u lives at phys 0x%p (virt 0x%p)", page_count, addr, PHYS_TO_VIRT(addr)); + //page_count++; + } +} + void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) { + hhdm_off = hhdm->offset; pmm_find_biggest_usable_region(memmap, hhdm); - pmm_allocate_bitmap(hhdm); + //pmm_allocate_bitmap(hhdm); too complicated for my small brain + + // Now we have biggest USABLE region, + // so to populate the free list we just iterate through it + pmm_init_freelist(); + + DEBUG("Trying pmm_alloc gives us page at 0x%p", pmm_alloc()); } \ No newline at end of file -- 2.49.1 From bb5fb9db33af788c1cbd7710bb0e33e38a7d9b27 Mon Sep 17 00:00:00 2001 From: xamidev Date: Fri, 2 Jan 2026 11:24:24 +0100 Subject: [PATCH 4/7] Cleaner include paths + some paging definitions --- Makefile | 4 ++-- src/idt/idt.c | 4 ++-- src/io/kbd/ps2.c | 4 ++-- src/kmain.c | 2 ++ src/mem/gdt/gdt.c | 2 +- src/mem/misc/utils.c | 4 ++-- src/mem/paging/paging.c | 13 +++++++++++ src/mem/paging/paging.h | 35 ++++++++++++++++++++--------- src/mem/paging/pmm.c | 50 +++-------------------------------------- src/time/timer.c | 2 +- 10 files changed, 52 insertions(+), 68 deletions(-) create mode 100644 src/mem/paging/paging.c diff --git a/Makefile b/Makefile index b2ccf57..6c70425 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -SOURCES = src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c +SOURCES = src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c build: rm -f *.o - x86_64-elf-gcc -g -c -I src $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel + x86_64-elf-gcc -g -c -Isrc $(SOURCES) -Wall -Wextra -std=gnu99 -nostdlib -ffreestanding -fno-stack-protector -fno-stack-check -fno-PIC -ffunction-sections -fdata-sections -mcmodel=kernel objcopy -O elf64-x86-64 -B i386 -I binary zap-light16.psf zap-light16.o nasm -f elf64 src/idt/idt.S -o idt_stub.o x86_64-elf-ld -o pepperk -T linker.ld *.o diff --git a/src/idt/idt.c b/src/idt/idt.c index 77fa029..5e31e48 100644 --- a/src/idt/idt.c +++ b/src/idt/idt.c @@ -1,8 +1,8 @@ #include "idt.h" #include #include -#include "../io/serial/serial.h" -#include "../io/kbd/ps2.h" +#include "io/serial/serial.h" +#include "io/kbd/ps2.h" #include struct interrupt_descriptor idt[256]; diff --git a/src/io/kbd/ps2.c b/src/io/kbd/ps2.c index 314d197..d8b787a 100644 --- a/src/io/kbd/ps2.c +++ b/src/io/kbd/ps2.c @@ -1,9 +1,9 @@ // PS/2 Keyboard support -#include "../serial/serial.h" +#include "io/serial/serial.h" #include "ps2.h" #include -#include "../term/term.h" +#include "io/term/term.h" #include // The key status bitfield will be used to see if ALT, CONTROL, or SHIFT is pressed diff --git a/src/kmain.c b/src/kmain.c index 1ea9f90..52c8bdd 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -11,6 +11,7 @@ #include "time/timer.h" #include "io/kbd/ps2.h" #include "mem/paging/pmm.h" +#include "mem/paging/paging.h" // Limine version used __attribute__((used, section(".limine_requests"))) @@ -73,6 +74,7 @@ void kmain() hhdm_display(hhdm_request.response); pmm_init(memmap_request.response, hhdm_request.response); + paging_init(); CLEAR_INTERRUPTS; gdt_init(); diff --git a/src/mem/gdt/gdt.c b/src/mem/gdt/gdt.c index 052f18f..68961b5 100644 --- a/src/mem/gdt/gdt.c +++ b/src/mem/gdt/gdt.c @@ -1,6 +1,6 @@ #include "gdt.h" #include -#include "../../io/serial/serial.h" +#include "io/serial/serial.h" #include // Descriptors are 8-byte wide (64bits) diff --git a/src/mem/misc/utils.c b/src/mem/misc/utils.c index 227dab0..bb5ca8a 100644 --- a/src/mem/misc/utils.c +++ b/src/mem/misc/utils.c @@ -1,8 +1,8 @@ #include #include #include -#include "../../kernel.h" -#include "../../string/string.h" +#include "kernel.h" +#include "string/string.h" // We won't be linked to standard library, but still need the basic mem* functions // so everything goes allright with the compiler diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c new file mode 100644 index 0000000..39fdd30 --- /dev/null +++ b/src/mem/paging/paging.c @@ -0,0 +1,13 @@ +#include "paging.h" +#include + +void paging_init() +{ + uint64_t cr3; + asm volatile ("mov %%cr3, %0" : "=r"(cr3)); + + // Root directory address (cr3 without 12 less significant bits) + uint64_t* pml4 = PHYS_TO_VIRT(cr3 & ~0xFFF); + + DEBUG("pml4=0x%p", pml4); +} \ No newline at end of file diff --git a/src/mem/paging/paging.h b/src/mem/paging/paging.h index 33da2aa..6dad8c1 100644 --- a/src/mem/paging/paging.h +++ b/src/mem/paging/paging.h @@ -1,16 +1,29 @@ -#ifndef PAGING_PMM_H -#define PAGING_PMM_H - -/* -We are going to use a bitmap, consisting of an array of uint64_t -to represent pages for the PMM (physical memory manager). - -Bit set (1) = page used -Bit clear (0) = page free - -*/ +#ifndef PAGING_H +#define PAGING_H #define PAGE_SIZE 4096 #define BITS_PER_ROW 64 +#include + +void paging_init(); + +extern uint64_t hhdm_off; + +#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off)) +#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off) + +// Stole it +#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1)) + +// Page entry special bits +// Bits set on a parent (directory, table) fall back to their children +#define PTE_PRESENT (1ULL << 0) +#define PTE_WRITE (1ULL << 1) +#define PTE_USER (1ULL << 2) +#define PTE_HUGE (1ULL << 7) +#define PTE_NOEXEC (1ULL << 63) + + #endif \ No newline at end of file diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c index 3ce044b..df98430 100644 --- a/src/mem/paging/pmm.c +++ b/src/mem/paging/pmm.c @@ -12,7 +12,7 @@ to see which pages are used by kernel/bootloader/mmio/fb etc. #include #include #include -#include "../misc/utils.h" +#include "mem/misc/utils.h" #include "pmm.h" /* @@ -27,42 +27,6 @@ and use this for the bitmap. The reserved memory will be ignored. struct usable_memory* usable_mem; struct limine_memmap_entry* biggest_entry; -/* -uint64_t* bitmap; -uint64_t* usable_memory_begin; - -// Bitmap will be placed at the start of the biggest usable region -// 1 bitmap page = 4096 bytes = 32768 bits -// So 1 bitmap page can track up to 32768 pages of memory -static void pmm_allocate_bitmap(struct limine_hhdm_response* hhdm) -{ - uint64_t pages = biggest_entry->length / PAGE_SIZE; - uint64_t bitmap_items = (pages+BITS_PER_ROW-1)/BITS_PER_ROW; - DEBUG("we need %u pages (bits) that will fit in %u uint64_t", pages, bitmap_items); - - bitmap = (uint64_t*)(biggest_entry->base + hhdm->offset); - DEBUG("bitmap will live at 0x%p", bitmap); - - uint64_t bitmap_bytes = bitmap_items*sizeof(uint64_t); - - // All pages are marked free since we're in a USABLE region - memset(bitmap, 0x00, bitmap_bytes); - - uint64_t bitmap_pages = (bitmap_bytes + PAGE_SIZE - 1) / PAGE_SIZE; - - // The 1st memory page we can use is at bitmap + bitmap_pages - usable_memory_begin = (void*)((uintptr_t)bitmap + bitmap_pages*PAGE_SIZE); - - // Mark bitmap as allocated - for (uint64_t i=0; i -#include "../io/serial/serial.h" +#include "io/serial/serial.h" #include /* -- 2.49.1 From c065df6ff3af4aae6de506461c5e7ab286e4130c Mon Sep 17 00:00:00 2001 From: xamidev Date: Fri, 2 Jan 2026 13:40:44 +0100 Subject: [PATCH 5/7] Paging: mapped kernel, fb, early-mem, HHDM --- src/kmain.c | 20 +++++- src/mem/paging/paging.c | 155 ++++++++++++++++++++++++++++++++++++++-- src/mem/paging/paging.h | 26 +++++-- src/mem/paging/pmm.h | 2 + 4 files changed, 188 insertions(+), 15 deletions(-) diff --git a/src/kmain.c b/src/kmain.c index 52c8bdd..64b1ced 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -38,6 +38,13 @@ static volatile struct limine_hhdm_request hhdm_request = { .revision = 0 }; +// Executable Address/Kernel Address (find base phys/virt address of kernel) +__attribute__((used, section(".limine_requests"))) +static volatile struct limine_kernel_address_request kerneladdr_request = { + .id = LIMINE_KERNEL_ADDRESS_REQUEST, + .revision = 0 +}; + __attribute__((used, section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER; @@ -61,10 +68,12 @@ void kmain() if (!LIMINE_BASE_REVISION_SUPPORTED) hcf(); if (framebuffer_request.response == NULL || framebuffer_request.response->framebuffer_count < 1) hcf(); + // We should probably grab all the boot info in a boot context struct + // that would be a bit cleaner than this mess + // Get the first framebuffer from the response framebuffer = framebuffer_request.response->framebuffers[0]; - term_init(); serial_init(); if (memmap_request.response == NULL) hcf(); @@ -73,8 +82,8 @@ void kmain() if (hhdm_request.response == NULL) hcf(); hhdm_display(hhdm_request.response); - pmm_init(memmap_request.response, hhdm_request.response); - paging_init(); + if (kerneladdr_request.response == NULL) hcf(); + DEBUG("kernel: phys_base=0x%p virt_base=0x%p", kerneladdr_request.response->physical_base, kerneladdr_request.response->virtual_base); CLEAR_INTERRUPTS; gdt_init(); @@ -82,11 +91,16 @@ void kmain() timer_init(); SET_INTERRUPTS; + pmm_init(memmap_request.response, hhdm_request.response); + paging_init(kerneladdr_request.response, framebuffer); + keyboard_init(FR); + term_init(); // Draw something printf("%s, %s!\n", "Hello", "world"); // Yoohoooooo! //DEBUG("kernel initialized successfully! hanging... wow=%d", 42); + printf("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non justo a magna bibendum auctor viverra rutrum diam. In hac habitasse platea dictumst. Vestibulum suscipit ipsum eget tortor maximus lobortis. Donec vel ipsum id lacus fringilla bibendum id eget risus. Fusce vestibulum diam sit amet nunc ultricies, nec rutrum nibh congue. Donec fringilla a dui sit amet ullamcorper. Donec pharetra quis tortor id congue. Aliquam erat volutpat. Duis suscipit nulla vel ligula iaculis, in gravida mauris pellentesque. Vestibulum nunc nisl, posuere eu eros et, dictum molestie dolor. Donec posuere laoreet hendrerit. Suspendisse potenti. Proin fringilla vehicula malesuada. Quisque a dui est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nec aliquam lacus, at lacinia enim. "); hcf(); } diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c index 39fdd30..779c2a1 100644 --- a/src/mem/paging/paging.c +++ b/src/mem/paging/paging.c @@ -1,13 +1,156 @@ #include "paging.h" +#include "pmm.h" #include +#include +#include -void paging_init() +/* +Paging on x86 uses four different page table levels: +cr3 register contains the phys address for the PML4 (root directory) + +Each directory/table is made of 512 entries, each one uint64_t +Each of these entries have special bits (PRESENT/WRITEABLE/USER/etc.) +that dictates their attributes. Also these bits fall back on children tables. + +If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages +2MB huge pages: PML4 -> PDPT -> PD -> 2mb pages +4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages +*/ + +static inline void load_cr3(uint64_t value) { + asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory"); +} + +// To flush TLB +static inline void invlpg(void *addr) { - uint64_t cr3; - asm volatile ("mov %%cr3, %0" : "=r"(cr3)); + asm volatile("invlpg (%0)" :: "r"(addr) : "memory"); +} - // Root directory address (cr3 without 12 less significant bits) - uint64_t* pml4 = PHYS_TO_VIRT(cr3 & ~0xFFF); +// Allocates a 512-entry 64bit page table/directory/whatever (zeroed) +static uint64_t* alloc_page_table() +{ + uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc()); - DEBUG("pml4=0x%p", pml4); + for (size_t i=0; i<512; i++) + { + virt[i] = 0; + } + return virt; +} + +__attribute__((aligned(4096))) +static uint64_t *kernel_pml4; + +void map_page(uint64_t virt, uint64_t phys, uint64_t flags) +{ + virt = PAGE_ALIGN_DOWN(virt); + phys = PAGE_ALIGN_DOWN(phys); + + // Translate the virt address into page table indexes + uint64_t pml4_i = PML4_INDEX(virt); + uint64_t pdpt_i = PDPT_INDEX(virt); + uint64_t pd_i = PD_INDEX(virt); + uint64_t pt_i = PT_INDEX(virt); + + uint64_t *pdpt, *pd, *pt; + + // PML4 + // If the entry at index is not present, allocate enough space for it + // then populate the entry with correct addr + flags + if (!(kernel_pml4[pml4_i] & PTE_PRESENT)) + { + pdpt = alloc_page_table(); + kernel_pml4[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pdpt = (uint64_t *)PHYS_TO_VIRT(kernel_pml4[pml4_i] & ~0xFFFULL); + } + + // PDPT: same here + if (!(pdpt[pdpt_i] & PTE_PRESENT)) + { + pd = alloc_page_table(); + pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL); + } + + // PD: and here + if (!(pd[pd_i] & PTE_PRESENT)) + { + pt = alloc_page_table(); + pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL); + } + + // PT: finally, populate the page table entry + pt[pt_i] = phys | flags | PTE_PRESENT; + + // Flush TLB (apply changes) + invlpg((void *)virt); +} + +void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb) +{ + // We should map the kernel, GDT, IDT, stack, framebuffer. + // Optionally we could map ACPI tables (we can find them in the Limine memmap) + + uint64_t kernel_phys_base = kaddr->physical_base; + uint64_t kernel_virt_base = kaddr->virtual_base; + + kernel_pml4 = alloc_page_table(); + + // for debug + uint64_t page_count = 0; + + // First 16 MB identity-mapped (phys = virt) + // This is because there might be some leftover stuff in the lower phys addresses + // from boot/bios/acpi/... + for (uint64_t i=0; i<0x1000000; i += PAGE_SIZE) + { + map_page(i, i, PTE_WRITABLE); + page_count++; + } + DEBUG("Mapped %u pages for the identity-mapping of the first 16 MB", page_count); page_count = 0; + + // HHDM map first 1 GB using given offset + for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE) + { + map_page(i+hhdm_off, i, PTE_WRITABLE); + page_count++; + } + DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0; + + // Map the kernel (according to virt/phys_base given by Limine) + // SOME DAY when we want a safer kernel we should map .text as Read/Exec + // .rodata as Read and .data as Read/Write + // For now who gives a shit, let's RWX all kernel + for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE) + { + map_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE); + page_count++; + } + DEBUG("Mapped %u pages for kernel", page_count); page_count = 0; + + // Get the framebuffer phys/virt address, and size + uint64_t fb_virt = (uint64_t)fb->address; + uint64_t fb_phys = VIRT_TO_PHYS(fb_virt); + uint64_t fb_size = fb->pitch * fb->height; + uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE; + + // Map the framebuffer (with cache-disable & write-through) + for (uint64_t i=0; i +#include -void paging_init(); +void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb); extern uint64_t hhdm_off; @@ -16,14 +17,27 @@ extern uint64_t hhdm_off; // Stole it #define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) #define ALIGN_DOWN(x, align) ((x) & ~((align) - 1)) +#define PAGE_ALIGN_DOWN(x) ((x) & ~0xFFFULL) + +#define PML4_INDEX(x) (((x) >> 39) & 0x1FF) +#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF) +#define PD_INDEX(x) (((x) >> 21) & 0x1FF) +#define PT_INDEX(x) (((x) >> 12) & 0x1FF) // Page entry special bits // Bits set on a parent (directory, table) fall back to their children -#define PTE_PRESENT (1ULL << 0) -#define PTE_WRITE (1ULL << 1) -#define PTE_USER (1ULL << 2) -#define PTE_HUGE (1ULL << 7) -#define PTE_NOEXEC (1ULL << 63) +#define PTE_PRESENT (1ULL << 0) +#define PTE_WRITABLE (1ULL << 1) +#define PTE_USER (1ULL << 2) +#define PTE_PWT (1ULL << 3) +#define PTE_PCD (1ULL << 4) +#define PTE_HUGE (1ULL << 7) +#define PTE_NOEXEC (1ULL << 63) +// Specified in linker.ld +#define KERNEL_BASE 0xFFFFFFFF80000000ULL + +// 2 MB should be enough (as of now, the whole kernel ELF is around 75kb) +#define KERNEL_SIZE 0x200000 #endif \ No newline at end of file diff --git a/src/mem/paging/pmm.h b/src/mem/paging/pmm.h index d2c722f..30202de 100644 --- a/src/mem/paging/pmm.h +++ b/src/mem/paging/pmm.h @@ -4,6 +4,8 @@ #include void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm); +void pmm_free(uintptr_t addr); +uintptr_t pmm_alloc(); // Might be upgraded to a freelist later. // For now, we can take the biggest usable region and we will be fine. -- 2.49.1 From e18b73c8a037f25c04e85ecc58ec1e9bb77c0e7d Mon Sep 17 00:00:00 2001 From: xamidev Date: Sat, 3 Jan 2026 13:48:10 +0100 Subject: [PATCH 6/7] Small kernel heap for VMM internals, kmalloc/kfree --- Makefile | 4 +- src/kernel.h | 4 ++ src/kmain.c | 17 ++++++- src/mem/heap/kheap.c | 106 ++++++++++++++++++++++++++++++++++++++++ src/mem/heap/kheap.h | 26 ++++++++++ src/mem/paging/paging.c | 90 +++++++++++++++++++++++++++------- src/mem/paging/paging.h | 1 + src/mem/paging/pmm.c | 1 - src/mem/paging/pmm.h | 8 --- src/mem/paging/vmm.c | 66 +++++++++++++++++++++++++ src/mem/paging/vmm.h | 29 +++++++++++ 11 files changed, 322 insertions(+), 30 deletions(-) create mode 100644 src/mem/heap/kheap.c create mode 100644 src/mem/heap/kheap.h create mode 100644 src/mem/paging/vmm.c create mode 100644 src/mem/paging/vmm.h diff --git a/Makefile b/Makefile index 6c70425..a94d962 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SOURCES = src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c +SOURCES = src/mem/heap/kheap.c src/mem/paging/vmm.c src/mem/paging/paging.c src/mem/paging/pmm.c src/string/string.c src/io/kbd/ps2.c src/io/serial/serial.c src/io/term/printf.c src/io/term/term.c src/idt/idt.c src/mem/gdt/gdt.c src/mem/misc/utils.c src/time/timer.c src/kmain.c build: rm -f *.o @@ -30,7 +30,7 @@ build-iso: limine/limine build ./limine/limine bios-install pepper.iso debug: - qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot & + qemu-system-x86_64 -drive file=pepper.iso -s -S -d int -no-reboot -no-shutdown & gdb pepperk --command=debug.gdb run: build-iso diff --git a/src/kernel.h b/src/kernel.h index 0daeeef..bbbd46e 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -16,4 +16,8 @@ enum ErrorCodes #define DEBUG(log, ...) fctprintf((void*)&skputc, 0, "debug: [%s]: " log "\r\n", __FILE__, ##__VA_ARGS__) // printf("debug: [%s]: " log "\n", __FILE__, ##__VA_ARGS__); + +void hcf(); +#define assert(check) do { if(!(check)) hcf(); } while(0) + #endif diff --git a/src/kmain.c b/src/kmain.c index 64b1ced..13e50ae 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -12,6 +12,8 @@ #include "io/kbd/ps2.h" #include "mem/paging/pmm.h" #include "mem/paging/paging.h" +#include "mem/paging/vmm.h" +#include "mem/heap/kheap.h" // Limine version used __attribute__((used, section(".limine_requests"))) @@ -53,8 +55,8 @@ static volatile LIMINE_REQUESTS_END_MARKER; struct limine_framebuffer* framebuffer; -// Panic -static void hcf() +// Panic (should dump registers etc. in the future) +void hcf() { for (;;) { @@ -92,7 +94,18 @@ void kmain() SET_INTERRUPTS; pmm_init(memmap_request.response, hhdm_request.response); + + // Remap kernel , HHDM and framebuffer paging_init(kerneladdr_request.response, framebuffer); + + kheap_init(); + + void* ptr = kmalloc(10); DEBUG("(KMALLOC TEST) Allocated 10 bytes at 0x%p", ptr); + void* ptr2 = kmalloc(200); DEBUG("(KMALLOC TEST) Allocated 200 bytes at 0x%p", ptr2); + kfree(ptr); + void* ptr3 = kmalloc(5); DEBUG("(KMALLOC TEST) Allocated 5 bytes at 0x%p", ptr3); + + vmm_init(); keyboard_init(FR); diff --git a/src/mem/heap/kheap.c b/src/mem/heap/kheap.c new file mode 100644 index 0000000..aff1f48 --- /dev/null +++ b/src/mem/heap/kheap.c @@ -0,0 +1,106 @@ +#include "kheap.h" +#include "mem/paging/paging.h" +#include "mem/paging/pmm.h" +#include +#include + +extern uint64_t kernel_phys_base; +extern uint64_t kernel_virt_base; + +uintptr_t kheap_start; + +static struct heap_block_t* head = NULL; +static uintptr_t end; + +// Kernel root table (level 4) +extern uint64_t *kernel_pml4; + +static void kheap_map_page() +{ + uintptr_t phys = pmm_alloc(); + paging_map_page(kernel_pml4, end, phys, PTE_PRESENT | PTE_WRITABLE | PTE_NOEXEC); + end += PAGE_SIZE; + DEBUG("Mapped first kheap page"); +} + +void kheap_init() +{ + kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE); + end = kheap_start; + + // At least 1 page must be mapped for it to work + kheap_map_page(); + + // Give linked list head its properties + head = (struct heap_block_t*)kheap_start; + head->size = PAGE_SIZE - sizeof(struct heap_block_t); + head->free = true; + head->next = NULL; + DEBUG("kheap initialized, head=0x%p, size=%u", head, head->size); +} + +void* kmalloc(size_t size) +{ + // No size, no memory allocated! + if (!size) return NULL; + + struct heap_block_t* curr = head; + + while (curr) + { + // Is block free and big enough for us? + if (curr->free && curr->size >= size) + { + // We split the block if it is big enough + if (curr->size > size + sizeof(struct heap_block_t)) + { + struct heap_block_t* new_block = (struct heap_block_t*)((uintptr_t)curr + sizeof(struct heap_block_t) + size); + // We have to subtract the size of our block struct + new_block->size = curr->size - size - sizeof(struct heap_block_t); + new_block->free = true; + + // Then we chain up the block in the list + new_block->next = curr->next; + curr->next = new_block; + + curr->size = size; + } + + // Found a good block, we return it + curr->free = false; + return (void*)((uintptr_t)curr + sizeof(struct heap_block_t)); + } + // Continue browsing the list if nothing good was found yet + curr = curr->next; + } + + // If we're hear it means we didn't have enough memory + // for the block allocation. So we will allocate more.. + uintptr_t old_end = end; + kheap_map_page(); + + struct heap_block_t* block = (struct heap_block_t*)old_end; + block->size = PAGE_SIZE - sizeof(struct heap_block_t); + block->free = false; + block->next = NULL; + + // Put the block at the end of the list + curr = head; + while (curr->next) + { + curr = curr->next; + } + curr->next = block; + + return (void*)((uintptr_t)block + sizeof(struct heap_block_t)); +} + +void kfree(void* ptr) +{ + // Nothing to free + if (!ptr) return; + + // Set it free! + struct heap_block_t* block = (struct heap_block_t*)((uintptr_t)ptr - sizeof(struct heap_block_t)); + block->free = true; +} \ No newline at end of file diff --git a/src/mem/heap/kheap.h b/src/mem/heap/kheap.h new file mode 100644 index 0000000..f8bb8b5 --- /dev/null +++ b/src/mem/heap/kheap.h @@ -0,0 +1,26 @@ +#ifndef KHEAP_H +#define KHEAP_H + +// We need some kind of simple kernel heap to make our linked list +// for the VMM, as we need "malloc" and "free" for that data structure. +// When the kernel heap is ready, we can alloc our VM object linked list +// and then continue working on the VMM. + +// 16MB should be enough for some linked lists +#define KHEAP_SIZE (16*1024*1024) + +#include +#include + +struct heap_block_t +{ + size_t size; + bool free; + struct heap_block_t* next; +}; + +void kheap_init(); +void* kmalloc(size_t size); +void kfree(void* ptr); + +#endif \ No newline at end of file diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c index 779c2a1..4e50d2e 100644 --- a/src/mem/paging/paging.c +++ b/src/mem/paging/paging.c @@ -39,10 +39,14 @@ static uint64_t* alloc_page_table() return virt; } +// Kernel paging root table, that will be placed in cr3 __attribute__((aligned(4096))) -static uint64_t *kernel_pml4; +uint64_t *kernel_pml4; -void map_page(uint64_t virt, uint64_t phys, uint64_t flags) +// Will map a page ONLY according to the kernel_pml4 root table. +// For kernel initialization/mapping only +// Deprecated, will be removed +/* void paging_kmap_page(uint64_t virt, uint64_t phys, uint64_t flags) { virt = PAGE_ALIGN_DOWN(virt); phys = PAGE_ALIGN_DOWN(phys); @@ -90,37 +94,87 @@ void map_page(uint64_t virt, uint64_t phys, uint64_t flags) // PT: finally, populate the page table entry pt[pt_i] = phys | flags | PTE_PRESENT; + // Flush TLB (apply changes) + invlpg((void *)virt); +} */ + +// Same as above, only this one takes any root table (not only kernel) +// Duplicate code but don't worry about it, I'll refactor one day +void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags) +{ + virt = PAGE_ALIGN_DOWN(virt); + phys = PAGE_ALIGN_DOWN(phys); + + // Translate the virt address into page table indexes + uint64_t pml4_i = PML4_INDEX(virt); + uint64_t pdpt_i = PDPT_INDEX(virt); + uint64_t pd_i = PD_INDEX(virt); + uint64_t pt_i = PT_INDEX(virt); + + uint64_t *pdpt, *pd, *pt; + + // PML4 + // If the entry at index is not present, allocate enough space for it + // then populate the entry with correct addr + flags + if (!(root_table[pml4_i] & PTE_PRESENT)) + { + pdpt = alloc_page_table(); + root_table[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pdpt = (uint64_t *)PHYS_TO_VIRT(root_table[pml4_i] & ~0xFFFULL); + } + + // PDPT: same here + if (!(pdpt[pdpt_i] & PTE_PRESENT)) + { + pd = alloc_page_table(); + pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL); + } + + // PD: and here + if (!(pd[pd_i] & PTE_PRESENT)) + { + pt = alloc_page_table(); + pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE; + } + else { + pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL); + } + + // PT: finally, populate the page table entry + pt[pt_i] = phys | flags | PTE_PRESENT; + // Flush TLB (apply changes) invlpg((void *)virt); } +uint64_t kernel_phys_base; +uint64_t kernel_virt_base; + void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb) { // We should map the kernel, GDT, IDT, stack, framebuffer. // Optionally we could map ACPI tables (we can find them in the Limine memmap) - uint64_t kernel_phys_base = kaddr->physical_base; - uint64_t kernel_virt_base = kaddr->virtual_base; + kernel_phys_base = kaddr->physical_base; + kernel_virt_base = kaddr->virtual_base; + + DEBUG("Kernel lives at virt=0x%p phys=0x%p", kernel_virt_base, kernel_phys_base); kernel_pml4 = alloc_page_table(); // for debug uint64_t page_count = 0; - // First 16 MB identity-mapped (phys = virt) - // This is because there might be some leftover stuff in the lower phys addresses - // from boot/bios/acpi/... - for (uint64_t i=0; i<0x1000000; i += PAGE_SIZE) - { - map_page(i, i, PTE_WRITABLE); - page_count++; - } - DEBUG("Mapped %u pages for the identity-mapping of the first 16 MB", page_count); page_count = 0; - // HHDM map first 1 GB using given offset for (uint64_t i=0; i<0x40000000; i += PAGE_SIZE) { - map_page(i+hhdm_off, i, PTE_WRITABLE); + //paging_kmap_page(i+hhdm_off, i, PTE_WRITABLE); + paging_map_page(kernel_pml4, i+hhdm_off, i, PTE_WRITABLE); page_count++; } DEBUG("Mapped %u pages for first 1GB (HHDM)", page_count); page_count = 0; @@ -131,7 +185,8 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra // For now who gives a shit, let's RWX all kernel for (uint64_t i = 0; i < KERNEL_SIZE; i += PAGE_SIZE) { - map_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE); + //paging_kmap_page(kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE); + paging_map_page(kernel_pml4, kernel_virt_base+i, kernel_phys_base+i, PTE_WRITABLE); page_count++; } DEBUG("Mapped %u pages for kernel", page_count); page_count = 0; @@ -145,7 +200,8 @@ void paging_init(struct limine_kernel_address_response* kaddr, struct limine_fra // Map the framebuffer (with cache-disable & write-through) for (uint64_t i=0; i void paging_init(struct limine_kernel_address_response* kaddr, struct limine_framebuffer* fb); +void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags); extern uint64_t hhdm_off; diff --git a/src/mem/paging/pmm.c b/src/mem/paging/pmm.c index df98430..2128f88 100644 --- a/src/mem/paging/pmm.c +++ b/src/mem/paging/pmm.c @@ -25,7 +25,6 @@ We will look for the biggest usable physical memory region and use this for the bitmap. The reserved memory will be ignored. */ -struct usable_memory* usable_mem; struct limine_memmap_entry* biggest_entry; static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm) diff --git a/src/mem/paging/pmm.h b/src/mem/paging/pmm.h index 30202de..084c334 100644 --- a/src/mem/paging/pmm.h +++ b/src/mem/paging/pmm.h @@ -7,12 +7,4 @@ void pmm_init(struct limine_memmap_response* memmap, struct limine_hhdm_response void pmm_free(uintptr_t addr); uintptr_t pmm_alloc(); -// Might be upgraded to a freelist later. -// For now, we can take the biggest usable region and we will be fine. -struct usable_memory -{ - uint64_t base; // physical - uint64_t length; -}; - #endif \ No newline at end of file diff --git a/src/mem/paging/vmm.c b/src/mem/paging/vmm.c new file mode 100644 index 0000000..630a479 --- /dev/null +++ b/src/mem/paging/vmm.c @@ -0,0 +1,66 @@ +/* +The VMM (virtual memory manager) will have two roles: +- mapping pages +- unmapping pages +in a specified virtual space + +compared to the PMM which allocs/frees 4kb frames ("physical pages"). +*/ + +#include "vmm.h" +#include "paging.h" +#include +#include "pmm.h" +#include + +void* vmm_pt_root = 0; + +// Linked list head for virtual memory objects +struct vm_object* vm_objs = NULL; + + +uint64_t convert_x86_vm_flags(size_t flags) +{ + uint64_t value = 0; + if (flags & VM_FLAG_WRITE) + { + value |= PTE_WRITABLE; + } + if (flags & VM_FLAG_USER) + { + value |= PTE_USER; + } + if ((flags & VM_FLAG_EXEC) == 0) + { + value |= PTE_NOEXEC; + } + return value; +} + +extern uint64_t *kernel_pml4; + +void vmm_setup_pt_root() +{ + // We alloc a physical page (frame) for the pointer, then map it + // to virt (pointer) + uintptr_t phys = pmm_alloc(); + vmm_pt_root = (void*)kernel_pml4; + paging_map_page(kernel_pml4, (uint64_t)vmm_pt_root, phys, convert_x86_vm_flags(VM_FLAG_WRITE | VM_FLAG_EXEC)); + DEBUG("VMM setup: vmm_pt_root=0x%p (phys=0x%p)", vmm_pt_root, phys); +} + +void* vmm_alloc(size_t length, size_t flags) +{ + // We will try to allocate at least length bytes, which have to be rounded UP to + // the next page so its coherent with the PMM + size_t len = ALIGN_UP(length, PAGE_SIZE); + + // Some linked list shenanigans will be here + // but for now we'd need some kheap to kmalloc the linked list items + // else we can't do it +} + +void vmm_init() +{ + vmm_setup_pt_root(); +} \ No newline at end of file diff --git a/src/mem/paging/vmm.h b/src/mem/paging/vmm.h new file mode 100644 index 0000000..3f862be --- /dev/null +++ b/src/mem/paging/vmm.h @@ -0,0 +1,29 @@ +#ifndef VMM_H +#define VMM_H + +#include +#include + +/* +This will be our linked list of virtual memory objects. +Flags here aren't x86 flags, they are platform-agnostic +kernel-defined flags. +*/ + +struct vm_object +{ + uintptr_t base; + size_t length; + size_t flags; + struct vm_object* next; +}; + +// Flags bitfield +#define VM_FLAG_NONE 0 +#define VM_FLAG_WRITE (1 << 0) +#define VM_FLAG_EXEC (1 << 1) +#define VM_FLAG_USER (1 << 2) + +void vmm_init(); + +#endif \ No newline at end of file -- 2.49.1 From 923758a4ea160e09f9d3e43dd80d44ba919a9723 Mon Sep 17 00:00:00 2001 From: xamidev Date: Sun, 4 Jan 2026 09:24:25 +0100 Subject: [PATCH 7/7] Remove useless code/comments --- src/mem/paging/paging.c | 61 +++-------------------------------------- src/mem/paging/vmm.c | 6 ++-- 2 files changed, 7 insertions(+), 60 deletions(-) diff --git a/src/mem/paging/paging.c b/src/mem/paging/paging.c index 4e50d2e..ce07aa1 100644 --- a/src/mem/paging/paging.c +++ b/src/mem/paging/paging.c @@ -43,63 +43,10 @@ static uint64_t* alloc_page_table() __attribute__((aligned(4096))) uint64_t *kernel_pml4; -// Will map a page ONLY according to the kernel_pml4 root table. -// For kernel initialization/mapping only -// Deprecated, will be removed -/* void paging_kmap_page(uint64_t virt, uint64_t phys, uint64_t flags) -{ - virt = PAGE_ALIGN_DOWN(virt); - phys = PAGE_ALIGN_DOWN(phys); - - // Translate the virt address into page table indexes - uint64_t pml4_i = PML4_INDEX(virt); - uint64_t pdpt_i = PDPT_INDEX(virt); - uint64_t pd_i = PD_INDEX(virt); - uint64_t pt_i = PT_INDEX(virt); - - uint64_t *pdpt, *pd, *pt; - - // PML4 - // If the entry at index is not present, allocate enough space for it - // then populate the entry with correct addr + flags - if (!(kernel_pml4[pml4_i] & PTE_PRESENT)) - { - pdpt = alloc_page_table(); - kernel_pml4[pml4_i] = VIRT_TO_PHYS(pdpt) | PTE_PRESENT | PTE_WRITABLE; - } - else { - pdpt = (uint64_t *)PHYS_TO_VIRT(kernel_pml4[pml4_i] & ~0xFFFULL); - } - - // PDPT: same here - if (!(pdpt[pdpt_i] & PTE_PRESENT)) - { - pd = alloc_page_table(); - pdpt[pdpt_i] = VIRT_TO_PHYS(pd) | PTE_PRESENT | PTE_WRITABLE; - } - else { - pd = (uint64_t *)PHYS_TO_VIRT(pdpt[pdpt_i] & ~0xFFFULL); - } - - // PD: and here - if (!(pd[pd_i] & PTE_PRESENT)) - { - pt = alloc_page_table(); - pd[pd_i] = VIRT_TO_PHYS(pt) | PTE_PRESENT | PTE_WRITABLE; - } - else { - pt = (uint64_t *)PHYS_TO_VIRT(pd[pd_i] & ~0xFFFULL); - } - - // PT: finally, populate the page table entry - pt[pt_i] = phys | flags | PTE_PRESENT; - - // Flush TLB (apply changes) - invlpg((void *)virt); -} */ - -// Same as above, only this one takes any root table (not only kernel) -// Duplicate code but don't worry about it, I'll refactor one day +// Map a page, taking virt and phys address. This will go through the paging structures +// beginning at the given root table, translate the virtual address in indexes in +// page table/directories, and then mapping the correct page table entry with the +// given physical address + flags void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags) { virt = PAGE_ALIGN_DOWN(virt); diff --git a/src/mem/paging/vmm.c b/src/mem/paging/vmm.c index 630a479..ec507b6 100644 --- a/src/mem/paging/vmm.c +++ b/src/mem/paging/vmm.c @@ -55,9 +55,9 @@ void* vmm_alloc(size_t length, size_t flags) // the next page so its coherent with the PMM size_t len = ALIGN_UP(length, PAGE_SIZE); - // Some linked list shenanigans will be here - // but for now we'd need some kheap to kmalloc the linked list items - // else we can't do it + // Need to implement this (as linked list) + // but for now kernel heap is sufficient + // The VMM will prob be more useful when we have userspace } void vmm_init() -- 2.49.1