From 0dadf5e19743cc9951c0f9e9fe9b8bc8d5432d89 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Thu, 9 Apr 2026 22:02:00 +0800 Subject: [PATCH] Stage 2 --- Makefile | 2 + clks/include/clks/boot.h | 1 + clks/include/clks/heap.h | 19 ++++ clks/include/clks/limine.h | 36 +++++++ clks/include/clks/pmm.h | 21 ++++ clks/kernel/heap.c | 176 ++++++++++++++++++++++++++++++++++ clks/kernel/kmain.c | 38 +++++++- clks/kernel/limine_requests.c | 17 ++++ clks/kernel/pmm.c | 105 ++++++++++++++++++++ docs/stage2.md | 35 +++++++ 10 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 clks/include/clks/heap.h create mode 100644 clks/include/clks/pmm.h create mode 100644 clks/kernel/heap.c create mode 100644 clks/kernel/pmm.c create mode 100644 docs/stage2.md diff --git a/Makefile b/Makefile index 1e90ae4..e67df43 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,8 @@ SOURCES := \ clks/kernel/log.c \ clks/kernel/limine_requests.c \ clks/kernel/tty.c \ + clks/kernel/pmm.c \ + clks/kernel/heap.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ diff --git a/clks/include/clks/boot.h b/clks/include/clks/boot.h index 6b4ba60..49b350f 100644 --- a/clks/include/clks/boot.h +++ b/clks/include/clks/boot.h @@ -6,5 +6,6 @@ clks_bool clks_boot_base_revision_supported(void); const struct limine_framebuffer *clks_boot_get_framebuffer(void); +const struct limine_memmap_response *clks_boot_get_memmap(void); #endif \ No newline at end of file diff --git a/clks/include/clks/heap.h b/clks/include/clks/heap.h new file mode 100644 index 0000000..32ee45f --- /dev/null +++ b/clks/include/clks/heap.h @@ -0,0 +1,19 @@ +#ifndef CLKS_HEAP_H +#define CLKS_HEAP_H + +#include + +struct clks_heap_stats { + usize total_bytes; + usize used_bytes; + usize free_bytes; + u64 alloc_count; + u64 free_count; +}; + +void clks_heap_init(void); +void *clks_kmalloc(usize size); +void clks_kfree(void *ptr); +struct clks_heap_stats clks_heap_get_stats(void); + +#endif \ No newline at end of file diff --git a/clks/include/clks/limine.h b/clks/include/clks/limine.h index a981e28..44015c2 100644 --- a/clks/include/clks/limine.h +++ b/clks/include/clks/limine.h @@ -23,6 +23,24 @@ 0xa3148604f6fab11bULL \ } +#define LIMINE_MEMMAP_REQUEST \ + { \ + LIMINE_COMMON_MAGIC, \ + LIMINE_REQUEST_MAGIC, \ + 0x67cf3d9d378a806fULL, \ + 0xe304acdfc50c3c62ULL \ + } + +#define LIMINE_MEMMAP_USABLE 0ULL +#define LIMINE_MEMMAP_RESERVED 1ULL +#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2ULL +#define LIMINE_MEMMAP_ACPI_NVS 3ULL +#define LIMINE_MEMMAP_BAD_MEMORY 4ULL +#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5ULL +#define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6ULL +#define LIMINE_MEMMAP_FRAMEBUFFER 7ULL +#define LIMINE_MEMMAP_RESERVED_MAPPED 8ULL + struct limine_framebuffer { void *address; u64 width; @@ -53,4 +71,22 @@ struct limine_framebuffer_request { struct limine_framebuffer_response *response; }; +struct limine_memmap_entry { + u64 base; + u64 length; + u64 type; +}; + +struct limine_memmap_response { + u64 revision; + u64 entry_count; + struct limine_memmap_entry **entries; +}; + +struct limine_memmap_request { + u64 id[4]; + u64 revision; + struct limine_memmap_response *response; +}; + #endif \ No newline at end of file diff --git a/clks/include/clks/pmm.h b/clks/include/clks/pmm.h new file mode 100644 index 0000000..bf91078 --- /dev/null +++ b/clks/include/clks/pmm.h @@ -0,0 +1,21 @@ +#ifndef CLKS_PMM_H +#define CLKS_PMM_H + +#include +#include + +#define CLKS_PAGE_SIZE 4096ULL + +struct clks_pmm_stats { + u64 managed_pages; + u64 free_pages; + u64 used_pages; + u64 dropped_pages; +}; + +void clks_pmm_init(const struct limine_memmap_response *memmap); +u64 clks_pmm_alloc_page(void); +void clks_pmm_free_page(u64 phys_addr); +struct clks_pmm_stats clks_pmm_get_stats(void); + +#endif \ No newline at end of file diff --git a/clks/kernel/heap.c b/clks/kernel/heap.c new file mode 100644 index 0000000..02ae290 --- /dev/null +++ b/clks/kernel/heap.c @@ -0,0 +1,176 @@ +#include +#include +#include + +#define CLKS_HEAP_ARENA_SIZE (1024ULL * 1024ULL) +#define CLKS_HEAP_ALIGN 16ULL +#define CLKS_HEAP_MAGIC 0x434C454F4E4F534FULL + +struct clks_heap_block { + usize size; + clks_bool is_free; + struct clks_heap_block *next; + struct clks_heap_block *prev; + u64 magic; +}; + +static u8 clks_heap_arena[CLKS_HEAP_ARENA_SIZE] __attribute__((aligned(16))); +static struct clks_heap_block *clks_heap_head = CLKS_NULL; +static clks_bool clks_heap_ready = CLKS_FALSE; + +static usize clks_heap_used_bytes = 0; +static u64 clks_heap_alloc_count = 0; +static u64 clks_heap_free_count = 0; + +static usize clks_heap_align_up(usize value) { + return (value + (CLKS_HEAP_ALIGN - 1ULL)) & ~(CLKS_HEAP_ALIGN - 1ULL); +} + +static void clks_heap_split_block(struct clks_heap_block *block, usize need_size) { + usize min_tail_size = sizeof(struct clks_heap_block) + CLKS_HEAP_ALIGN; + + if (block->size < (need_size + min_tail_size)) { + return; + } + + { + u8 *new_block_addr = (u8 *)block + sizeof(struct clks_heap_block) + need_size; + struct clks_heap_block *new_block = (struct clks_heap_block *)new_block_addr; + + new_block->size = block->size - need_size - sizeof(struct clks_heap_block); + new_block->is_free = CLKS_TRUE; + new_block->next = block->next; + new_block->prev = block; + new_block->magic = CLKS_HEAP_MAGIC; + + if (block->next != CLKS_NULL) { + block->next->prev = new_block; + } + + block->next = new_block; + block->size = need_size; + } +} + +static void clks_heap_merge_next(struct clks_heap_block *block) { + struct clks_heap_block *next = block->next; + + if (next == CLKS_NULL) { + return; + } + + if (next->is_free == CLKS_FALSE) { + return; + } + + if (next->magic != CLKS_HEAP_MAGIC) { + return; + } + + block->size += sizeof(struct clks_heap_block) + next->size; + block->next = next->next; + + if (next->next != CLKS_NULL) { + next->next->prev = block; + } +} + +void clks_heap_init(void) { + clks_memset(clks_heap_arena, 0, sizeof(clks_heap_arena)); + + clks_heap_head = (struct clks_heap_block *)clks_heap_arena; + clks_heap_head->size = CLKS_HEAP_ARENA_SIZE - sizeof(struct clks_heap_block); + clks_heap_head->is_free = CLKS_TRUE; + clks_heap_head->next = CLKS_NULL; + clks_heap_head->prev = CLKS_NULL; + clks_heap_head->magic = CLKS_HEAP_MAGIC; + + clks_heap_used_bytes = 0; + clks_heap_alloc_count = 0; + clks_heap_free_count = 0; + clks_heap_ready = CLKS_TRUE; +} + +void *clks_kmalloc(usize size) { + struct clks_heap_block *current; + usize aligned_size; + + if (clks_heap_ready == CLKS_FALSE) { + return CLKS_NULL; + } + + if (size == 0) { + return CLKS_NULL; + } + + aligned_size = clks_heap_align_up(size); + current = clks_heap_head; + + while (current != CLKS_NULL) { + if (current->magic != CLKS_HEAP_MAGIC) { + return CLKS_NULL; + } + + if (current->is_free == CLKS_TRUE && current->size >= aligned_size) { + clks_heap_split_block(current, aligned_size); + current->is_free = CLKS_FALSE; + clks_heap_used_bytes += current->size; + clks_heap_alloc_count++; + return (void *)((u8 *)current + sizeof(struct clks_heap_block)); + } + + current = current->next; + } + + return CLKS_NULL; +} + +void clks_kfree(void *ptr) { + struct clks_heap_block *block; + + if (clks_heap_ready == CLKS_FALSE) { + return; + } + + if (ptr == CLKS_NULL) { + return; + } + + block = (struct clks_heap_block *)((u8 *)ptr - sizeof(struct clks_heap_block)); + + if (block->magic != CLKS_HEAP_MAGIC) { + return; + } + + if (block->is_free == CLKS_TRUE) { + return; + } + + block->is_free = CLKS_TRUE; + + if (clks_heap_used_bytes >= block->size) { + clks_heap_used_bytes -= block->size; + } else { + clks_heap_used_bytes = 0; + } + + clks_heap_free_count++; + + clks_heap_merge_next(block); + + if (block->prev != CLKS_NULL && block->prev->is_free == CLKS_TRUE) { + clks_heap_merge_next(block->prev); + } +} + +struct clks_heap_stats clks_heap_get_stats(void) { + struct clks_heap_stats stats; + + stats.total_bytes = CLKS_HEAP_ARENA_SIZE - sizeof(struct clks_heap_block); + stats.used_bytes = clks_heap_used_bytes; + stats.free_bytes = stats.total_bytes - stats.used_bytes; + stats.alloc_count = clks_heap_alloc_count; + stats.free_count = clks_heap_free_count; + + return stats; +} \ No newline at end of file diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 0911a20..8addd11 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -1,14 +1,20 @@ #include #include #include +#include #include #include +#include #include #include #include void clks_kernel_main(void) { const struct limine_framebuffer *boot_fb; + const struct limine_memmap_response *boot_memmap; + struct clks_pmm_stats pmm_stats; + struct clks_heap_stats heap_stats; + void *heap_probe = CLKS_NULL; clks_serial_init(); @@ -24,7 +30,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE1 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE2 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -41,6 +47,36 @@ void clks_kernel_main(void) { clks_log(CLKS_LOG_INFO, "ARCH", "AARCH64 ONLINE"); #endif + boot_memmap = clks_boot_get_memmap(); + + if (boot_memmap == CLKS_NULL) { + clks_log(CLKS_LOG_ERROR, "MEM", "NO LIMINE MEMMAP RESPONSE"); + clks_cpu_halt_forever(); + } + + clks_pmm_init(boot_memmap); + pmm_stats = clks_pmm_get_stats(); + + clks_log_hex(CLKS_LOG_INFO, "PMM", "MANAGED_PAGES", pmm_stats.managed_pages); + clks_log_hex(CLKS_LOG_INFO, "PMM", "FREE_PAGES", pmm_stats.free_pages); + clks_log_hex(CLKS_LOG_INFO, "PMM", "USED_PAGES", pmm_stats.used_pages); + clks_log_hex(CLKS_LOG_INFO, "PMM", "DROPPED_PAGES", pmm_stats.dropped_pages); + + clks_heap_init(); + heap_stats = clks_heap_get_stats(); + + clks_log_hex(CLKS_LOG_INFO, "HEAP", "TOTAL_BYTES", heap_stats.total_bytes); + clks_log_hex(CLKS_LOG_INFO, "HEAP", "FREE_BYTES", heap_stats.free_bytes); + + heap_probe = clks_kmalloc(128); + + if (heap_probe == CLKS_NULL) { + clks_log(CLKS_LOG_ERROR, "HEAP", "KMALLOC SELFTEST FAILED"); + } else { + clks_log(CLKS_LOG_INFO, "HEAP", "KMALLOC SELFTEST OK"); + clks_kfree(heap_probe); + } + clks_log(CLKS_LOG_INFO, "TTY", "VIRTUAL TTY0 READY"); clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); diff --git a/clks/kernel/limine_requests.c b/clks/kernel/limine_requests.c index 2a0999b..102620f 100644 --- a/clks/kernel/limine_requests.c +++ b/clks/kernel/limine_requests.c @@ -14,6 +14,13 @@ CLKS_USED static volatile struct limine_framebuffer_request limine_framebuffer_r .response = CLKS_NULL, }; +CLKS_USED static volatile struct limine_memmap_request limine_memmap_request + __attribute__((section(".limine_requests"))) = { + .id = LIMINE_MEMMAP_REQUEST, + .revision = 0, + .response = CLKS_NULL, + }; + CLKS_USED static volatile u64 limine_requests_end[] __attribute__((section(".limine_requests_end"))) = LIMINE_REQUESTS_END_MARKER; @@ -33,4 +40,14 @@ const struct limine_framebuffer *clks_boot_get_framebuffer(void) { } return request->response->framebuffers[0]; +} + +const struct limine_memmap_response *clks_boot_get_memmap(void) { + volatile struct limine_memmap_request *request = &limine_memmap_request; + + if (request->response == CLKS_NULL) { + return CLKS_NULL; + } + + return request->response; } \ No newline at end of file diff --git a/clks/kernel/pmm.c b/clks/kernel/pmm.c new file mode 100644 index 0000000..830ed65 --- /dev/null +++ b/clks/kernel/pmm.c @@ -0,0 +1,105 @@ +#include +#include +#include + +#define CLKS_PMM_MAX_TRACKED_PAGES 262144ULL +#define CLKS_PMM_MIN_USABLE_ADDR 0x100000ULL + +static u64 clks_pmm_free_stack[CLKS_PMM_MAX_TRACKED_PAGES]; +static u64 clks_pmm_free_top = 0; +static u64 clks_pmm_managed_pages = 0; +static u64 clks_pmm_dropped_pages = 0; + +static u64 clks_align_up_u64(u64 value, u64 alignment) { + return (value + alignment - 1ULL) & ~(alignment - 1ULL); +} + +static u64 clks_align_down_u64(u64 value, u64 alignment) { + return value & ~(alignment - 1ULL); +} + +void clks_pmm_init(const struct limine_memmap_response *memmap) { + u64 i; + + clks_pmm_free_top = 0; + clks_pmm_managed_pages = 0; + clks_pmm_dropped_pages = 0; + clks_memset(clks_pmm_free_stack, 0, sizeof(clks_pmm_free_stack)); + + if (memmap == CLKS_NULL) { + return; + } + + for (i = 0; i < memmap->entry_count; i++) { + const struct limine_memmap_entry *entry = memmap->entries[i]; + u64 start; + u64 end; + u64 addr; + + if (entry == CLKS_NULL) { + continue; + } + + if (entry->type != LIMINE_MEMMAP_USABLE) { + continue; + } + + start = clks_align_up_u64(entry->base, CLKS_PAGE_SIZE); + end = clks_align_down_u64(entry->base + entry->length, CLKS_PAGE_SIZE); + + if (end <= start) { + continue; + } + + for (addr = start; addr < end; addr += CLKS_PAGE_SIZE) { + if (addr < CLKS_PMM_MIN_USABLE_ADDR) { + continue; + } + + if (clks_pmm_free_top < CLKS_PMM_MAX_TRACKED_PAGES) { + clks_pmm_free_stack[clks_pmm_free_top] = addr; + clks_pmm_free_top++; + clks_pmm_managed_pages++; + } else { + clks_pmm_dropped_pages++; + } + } + } +} + +u64 clks_pmm_alloc_page(void) { + if (clks_pmm_free_top == 0) { + return 0ULL; + } + + clks_pmm_free_top--; + return clks_pmm_free_stack[clks_pmm_free_top]; +} + +void clks_pmm_free_page(u64 phys_addr) { + if (phys_addr == 0ULL) { + return; + } + + if ((phys_addr & (CLKS_PAGE_SIZE - 1ULL)) != 0ULL) { + return; + } + + if (clks_pmm_free_top >= CLKS_PMM_MAX_TRACKED_PAGES) { + return; + } + + clks_pmm_free_stack[clks_pmm_free_top] = phys_addr; + clks_pmm_free_top++; +} + +struct clks_pmm_stats clks_pmm_get_stats(void) { + struct clks_pmm_stats stats; + + stats.managed_pages = clks_pmm_managed_pages; + stats.free_pages = clks_pmm_free_top; + stats.used_pages = clks_pmm_managed_pages - clks_pmm_free_top; + stats.dropped_pages = clks_pmm_dropped_pages; + + return stats; +} \ No newline at end of file diff --git a/docs/stage2.md b/docs/stage2.md new file mode 100644 index 0000000..62eeafa --- /dev/null +++ b/docs/stage2.md @@ -0,0 +1,35 @@ +# CLeonOS Stage2 + +## Stage Goal +- Introduce foundational memory management for CLKS. +- Initialize physical memory manager (PMM) from Limine memmap. +- Add kernel heap allocator (`clks_kmalloc`/`clks_kfree`) without external libc. +- Emit memory statistics via kernel log during boot. + +## Acceptance Criteria +- Kernel receives valid Limine memmap response. +- PMM initializes and prints managed/free/used/dropped page counts. +- Heap initializes and prints total/free bytes. +- `clks_kmalloc` self-test allocates and frees successfully. +- Kernel still reaches idle loop without panic. + +## Build Targets +- `make setup` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- `NO LIMINE MEMMAP RESPONSE`: + - Check Limine protocol entry in `configs/limine.conf` and ensure `protocol: limine` is used. +- PMM managed pages are 0: + - Validate `LIMINE_MEMMAP_REQUEST` ID and section placement in `limine_requests.c`. +- Heap self-test failed: + - Inspect `heap.c` block split/merge logic and verify allocator init was called. +- Limine panic about ELF segment permissions: + - Ensure linker sections are page-aligned and segment permissions do not share pages. +- Limine artifacts missing during ISO packaging: + - Verify `make setup` completed and `limine/bin/*` files exist. \ No newline at end of file