diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ee6304..8aca4c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(kernel idt.c isr.c pic.c + pmm.c interrupts.S kernel.c ) diff --git a/src/kernel.c b/src/kernel.c index 08e5405..842ac93 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -5,6 +5,7 @@ #include "idt.h" #include "pic.h" #include "port_io.h" +#include "pmm.h" void offset_print(const char *str) { @@ -46,6 +47,17 @@ void kernel_main(uint32_t magic, uint32_t addr) { init_pic(); offset_print("PIC initialized\n"); + + init_pmm(addr); + offset_print("PMM initialized\n"); + + phys_addr_t p1 = pmm_alloc_page(PMM_ZONE_NORMAL); + offset_print("Allocated page at: "); + print_hex(p1); + + phys_addr_t p2 = pmm_alloc_page(PMM_ZONE_DMA); + offset_print("Allocated DMA page at: "); + print_hex(p2); /* Enable interrupts */ asm volatile("sti"); diff --git a/src/linker.ld b/src/linker.ld index 678c08f..8180131 100644 --- a/src/linker.ld +++ b/src/linker.ld @@ -4,6 +4,8 @@ SECTIONS { . = 1M; + _kernel_start = .; + .text BLOCK(4K) : ALIGN(4K) { KEEP(*(.multiboot)) @@ -25,4 +27,6 @@ SECTIONS *(COMMON) *(.bss) } + + _kernel_end = .; } diff --git a/src/pmm.c b/src/pmm.c new file mode 100644 index 0000000..1cadbbc --- /dev/null +++ b/src/pmm.c @@ -0,0 +1,146 @@ +#include "pmm.h" +#include + +extern uint32_t _kernel_start; +extern uint32_t _kernel_end; + +#define MAX_PHYSICAL_MEMORY 0xFFFFFFFF // 4GB +#define BITMAP_SIZE (MAX_PHYSICAL_MEMORY / PAGE_SIZE / 32) +#define FRAMES_PER_INDEX 32 + +static uint32_t memory_bitmap[BITMAP_SIZE]; +static uint32_t memory_size = 0; +// static uint32_t used_frames = 0; // Unused for now + +static void set_frame(phys_addr_t frame_addr) { + uint32_t frame = frame_addr / PAGE_SIZE; + uint32_t idx = frame / FRAMES_PER_INDEX; + uint32_t off = frame % FRAMES_PER_INDEX; + memory_bitmap[idx] |= (1 << off); +} + +static void clear_frame(phys_addr_t frame_addr) { + uint32_t frame = frame_addr / PAGE_SIZE; + uint32_t idx = frame / FRAMES_PER_INDEX; + uint32_t off = frame % FRAMES_PER_INDEX; + memory_bitmap[idx] &= ~(1 << off); +} + +static uint32_t first_free_frame(size_t start_frame, size_t end_frame) { + for (size_t i = start_frame; i < end_frame; i++) { + uint32_t idx = i / FRAMES_PER_INDEX; + uint32_t off = i % FRAMES_PER_INDEX; + if (!(memory_bitmap[idx] & (1 << off))) { + return i; + } + } + return (uint32_t)-1; +} + +void init_pmm(uint32_t multiboot_addr) { + struct multiboot_tag *tag; + uint32_t addr = multiboot_addr; + + /* Initialize all memory as used (1) initially */ + for (int i = 0; i < BITMAP_SIZE; i++) { + memory_bitmap[i] = 0xFFFFFFFF; + } + + if (addr & 7) { + // offset_print("Multiboot alignment error\n"); // Need offset_print here? No, kernel handles prints + return; // Alignment issue + } + + uint32_t size = *(uint32_t *)addr; + + for (tag = (struct multiboot_tag *)(addr + 8); + tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7) & ~7))) { + + if (tag->type == MULTIBOOT_TAG_TYPE_BASIC_MEMINFO) { + struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag; + memory_size = meminfo->mem_upper; + } else if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) { + multiboot_memory_map_t *mmap; + struct multiboot_tag_mmap *tag_mmap = (struct multiboot_tag_mmap *)tag; + + for (mmap = tag_mmap->entries; + (multiboot_uint8_t *)mmap < (multiboot_uint8_t *)tag + tag->size; + mmap = (multiboot_memory_map_t *)((unsigned long)mmap + tag_mmap->entry_size)) { + + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + uint64_t start = mmap->addr; + uint64_t len = mmap->len; + uint64_t end = start + len; + + if (start % PAGE_SIZE != 0) { + start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1); + } + + for (uint64_t addr_iter = start; addr_iter < end; addr_iter += PAGE_SIZE) { + if (addr_iter + PAGE_SIZE <= end && addr_iter < MAX_PHYSICAL_MEMORY) { + clear_frame((phys_addr_t)addr_iter); + } + } + } + } + } + } + + uint32_t kstart = (uint32_t)&_kernel_start; + uint32_t kend = (uint32_t)&_kernel_end; + + kstart &= ~(PAGE_SIZE - 1); + kend = (kend + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + + for (uint32_t i = kstart; i < kend; i += PAGE_SIZE) { + set_frame(i); + } + + // Mark multiboot structure + uint32_t mb_start = multiboot_addr; + uint32_t mb_end = multiboot_addr + size; + mb_start &= ~(PAGE_SIZE - 1); + mb_end = (mb_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + + for (uint32_t i = mb_start; i < mb_end; i += PAGE_SIZE) { + set_frame(i); + } + + // Mark first page as used to avoid returning 0 (NULL) + set_frame(0); +} + +phys_addr_t pmm_alloc_page(pmm_zone_t zone) { + uint32_t start_frame = 0; + uint32_t end_frame = BITMAP_SIZE * FRAMES_PER_INDEX; + uint32_t frame = (uint32_t)-1; + + // 16MB boundary is at 16*1024*1024 / 4096 = 4096 frames + uint32_t dma_limit = 4096; + + if (zone == PMM_ZONE_DMA) { + end_frame = dma_limit; + frame = first_free_frame(start_frame, end_frame); + } else { + start_frame = dma_limit; + frame = first_free_frame(start_frame, end_frame); + + // Fallback to DMA if NORMAL failed + if (frame == (uint32_t)-1) { + frame = first_free_frame(0, dma_limit); + } + } + + if (frame != (uint32_t)-1) { + phys_addr_t addr = frame * PAGE_SIZE; + set_frame(addr); + return addr; + } + + return 0; // OOM +} + +void pmm_free_page(phys_addr_t addr) { + clear_frame(addr); +} diff --git a/src/pmm.h b/src/pmm.h new file mode 100644 index 0000000..e51fbb2 --- /dev/null +++ b/src/pmm.h @@ -0,0 +1,36 @@ +#ifndef PMM_H +#define PMM_H + +#include +#include + +#define PAGE_SIZE 4096 + +typedef uintptr_t phys_addr_t; + +typedef enum { + PMM_ZONE_DMA, // < 16 MB + PMM_ZONE_NORMAL, // Any other memory + PMM_ZONE_HIGHMEM // > 4 GB (if 64-bit or PAE, but we are 32-bit for now) - actually sticking to DMA and NORMAL is enough for 32-bit typically +} pmm_zone_t; + +/* + * Initialize the physical memory manager. + * @param multiboot_addr Address of the multiboot info structure + */ +void init_pmm(uint32_t multiboot_addr); + +/* + * Allocate a physical page. + * @param zone The preferred zone to allocate from. + * @return Physical address of the allocated page, or 0 if out of memory. + */ +phys_addr_t pmm_alloc_page(pmm_zone_t zone); + +/* + * Free a physical page. + * @param addr Physical address of the page to free. + */ +void pmm_free_page(phys_addr_t addr); + +#endif