Memory heap and allocator
This commit is contained in:
7
Makefile
7
Makefile
@@ -38,7 +38,7 @@ TARGET = BOOTX64.EFI
|
|||||||
TARGET_SO = bootx64.so
|
TARGET_SO = bootx64.so
|
||||||
OBJ = $(BUILD_DIR)/main.o
|
OBJ = $(BUILD_DIR)/main.o
|
||||||
KERNEL_TARGET = kernel.elf
|
KERNEL_TARGET = kernel.elf
|
||||||
KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/isr.o
|
KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/isr.o $(BUILD_DIR)/memory.o
|
||||||
KERNEL_LD = kernel.ld
|
KERNEL_LD = kernel.ld
|
||||||
|
|
||||||
# QEMU settings
|
# QEMU settings
|
||||||
@@ -121,6 +121,11 @@ $(BUILD_DIR)/isr.o: $(SRC_DIR)/isr.S
|
|||||||
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
||||||
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/memory.o: $(SRC_DIR)/memory.c
|
||||||
|
@echo "Compiling memory.c..."
|
||||||
|
$(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \
|
||||||
|
-mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
# Link kernel ELF
|
# Link kernel ELF
|
||||||
$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD)
|
$(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD)
|
||||||
@echo "Linking kernel ELF..."
|
@echo "Linking kernel ELF..."
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ typedef struct {
|
|||||||
EFI_STATUS (*set_attribute)(UINTN Attribute);
|
EFI_STATUS (*set_attribute)(UINTN Attribute);
|
||||||
EFI_STATUS (*read_key)(EFI_INPUT_KEY *Key);
|
EFI_STATUS (*read_key)(EFI_INPUT_KEY *Key);
|
||||||
void (*shutdown)(void);
|
void (*shutdown)(void);
|
||||||
|
EFI_STATUS (*alloc_pages)(UINTN pages, EFI_PHYSICAL_ADDRESS *addr);
|
||||||
|
EFI_STATUS (*free_pages)(EFI_PHYSICAL_ADDRESS addr, UINTN pages);
|
||||||
} BootInfo;
|
} BootInfo;
|
||||||
|
|
||||||
typedef void (*KernelEntryFn)(BootInfo *Boot);
|
typedef void (*KernelEntryFn)(BootInfo *Boot);
|
||||||
|
|||||||
15
commands.c
15
commands.c
@@ -1,6 +1,7 @@
|
|||||||
#include <efi.h>
|
#include <efi.h>
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "string_utils.h"
|
#include "string_utils.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
#define SAFE_PRINT(Boot, ...) \
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
do { \
|
do { \
|
||||||
@@ -15,6 +16,7 @@ static void cmd_help(BootInfo *Boot, CHAR16 *Args);
|
|||||||
static void cmd_man(BootInfo *Boot, CHAR16 *Args);
|
static void cmd_man(BootInfo *Boot, CHAR16 *Args);
|
||||||
static void cmd_clear(BootInfo *Boot, CHAR16 *Args);
|
static void cmd_clear(BootInfo *Boot, CHAR16 *Args);
|
||||||
static void cmd_about(BootInfo *Boot, CHAR16 *Args);
|
static void cmd_about(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
static void cmd_mem(BootInfo *Boot, CHAR16 *Args);
|
||||||
|
|
||||||
// Command registry
|
// Command registry
|
||||||
static Command commands[] = {
|
static Command commands[] = {
|
||||||
@@ -48,6 +50,12 @@ static Command commands[] = {
|
|||||||
L"Usage: about\n\r Shows information about this operating system.",
|
L"Usage: about\n\r Shows information about this operating system.",
|
||||||
cmd_about
|
cmd_about
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
L"mem",
|
||||||
|
L"Display memory statistics",
|
||||||
|
L"Usage: mem\n\r Shows physical memory, heap, and paging information.",
|
||||||
|
cmd_mem
|
||||||
|
},
|
||||||
{NULL, NULL, NULL, NULL} // Sentinel
|
{NULL, NULL, NULL, NULL} // Sentinel
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -144,12 +152,19 @@ static void cmd_about(BootInfo *Boot, CHAR16 *Args)
|
|||||||
SAFE_PRINT(Boot, L" - UEFI Boot\n\r");
|
SAFE_PRINT(Boot, L" - UEFI Boot\n\r");
|
||||||
SAFE_PRINT(Boot, L" - Console I/O\n\r");
|
SAFE_PRINT(Boot, L" - Console I/O\n\r");
|
||||||
SAFE_PRINT(Boot, L" - ELF64 Kernel Loader\n\r");
|
SAFE_PRINT(Boot, L" - ELF64 Kernel Loader\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" - Memory Management (PMM + Heap)\n\r");
|
||||||
SAFE_PRINT(Boot, L" - Interactive Command Interface\n\r");
|
SAFE_PRINT(Boot, L" - Interactive Command Interface\n\r");
|
||||||
SAFE_PRINT(Boot, L"\n\r");
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
SAFE_PRINT(Boot, L"Type 'help' for available commands.\n\r");
|
SAFE_PRINT(Boot, L"Type 'help' for available commands.\n\r");
|
||||||
SAFE_PRINT(Boot, L"\n\r");
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cmd_mem(BootInfo *Boot, CHAR16 *Args)
|
||||||
|
{
|
||||||
|
(void)Args;
|
||||||
|
memory_print_stats(Boot);
|
||||||
|
}
|
||||||
|
|
||||||
void show_help(BootInfo *Boot)
|
void show_help(BootInfo *Boot)
|
||||||
{
|
{
|
||||||
UINTN i = 0;
|
UINTN i = 0;
|
||||||
|
|||||||
2
kernel.c
2
kernel.c
@@ -3,6 +3,7 @@
|
|||||||
#include "boot_info.h"
|
#include "boot_info.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
#define SAFE_PRINT(Boot, ...) \
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
do { \
|
do { \
|
||||||
@@ -36,6 +37,7 @@ void kmain(BootInfo *Boot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
idt_init(Boot);
|
idt_init(Boot);
|
||||||
|
memory_init(Boot);
|
||||||
|
|
||||||
SAFE_PRINT(Boot, L"================================================\n\r");
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
SAFE_PRINT(Boot, L" Welcome to Simple UEFI Operating System!\n\r");
|
SAFE_PRINT(Boot, L" Welcome to Simple UEFI Operating System!\n\r");
|
||||||
|
|||||||
13
main.c
13
main.c
@@ -207,6 +207,17 @@ static void loader_shutdown(void)
|
|||||||
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static EFI_STATUS loader_alloc_pages(UINTN pages, EFI_PHYSICAL_ADDRESS *addr)
|
||||||
|
{
|
||||||
|
return uefi_call_wrapper(BS->AllocatePages, 4,
|
||||||
|
AllocateAnyPages, EfiLoaderData, pages, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static EFI_STATUS loader_free_pages(EFI_PHYSICAL_ADDRESS addr, UINTN pages)
|
||||||
|
{
|
||||||
|
return uefi_call_wrapper(BS->FreePages, 2, addr, pages);
|
||||||
|
}
|
||||||
|
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
EFIAPI
|
EFIAPI
|
||||||
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||||
@@ -241,6 +252,8 @@ efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
|||||||
Boot.set_attribute = loader_set_attribute;
|
Boot.set_attribute = loader_set_attribute;
|
||||||
Boot.read_key = loader_read_key;
|
Boot.read_key = loader_read_key;
|
||||||
Boot.shutdown = loader_shutdown;
|
Boot.shutdown = loader_shutdown;
|
||||||
|
Boot.alloc_pages = loader_alloc_pages;
|
||||||
|
Boot.free_pages = loader_free_pages;
|
||||||
|
|
||||||
EntryFn = (KernelEntryFn)(UINTN)KernelEntry;
|
EntryFn = (KernelEntryFn)(UINTN)KernelEntry;
|
||||||
EntryFn(&Boot);
|
EntryFn(&Boot);
|
||||||
|
|||||||
490
memory.c
Normal file
490
memory.c
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
#define SAFE_PRINT(Boot, ...) \
|
||||||
|
do { \
|
||||||
|
if ((Boot) != NULL && (Boot)->print != NULL) { \
|
||||||
|
(Boot)->print(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Physical Memory Manager – bitmap-based page-frame allocator
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static UINT64 pmm_pool_base = 0;
|
||||||
|
static UINTN pmm_total_pages = 0;
|
||||||
|
static UINTN pmm_free_count = 0;
|
||||||
|
static UINT8 pmm_bitmap[PMM_POOL_PAGES / 8];
|
||||||
|
static BOOLEAN pmm_ready = FALSE;
|
||||||
|
|
||||||
|
static void pmm_set_bit(UINTN idx)
|
||||||
|
{
|
||||||
|
pmm_bitmap[idx / 8] |= (UINT8)(1U << (idx % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmm_clear_bit(UINTN idx)
|
||||||
|
{
|
||||||
|
pmm_bitmap[idx / 8] &= (UINT8)~(1U << (idx % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOLEAN pmm_test_bit(UINTN idx)
|
||||||
|
{
|
||||||
|
return (pmm_bitmap[idx / 8] & (1U << (idx % 8))) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_init(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
EFI_PHYSICAL_ADDRESS pool_addr = 0;
|
||||||
|
UINTN i;
|
||||||
|
|
||||||
|
/* Zero the bitmap – all pages start free */
|
||||||
|
for (i = 0; i < sizeof(pmm_bitmap); i++) {
|
||||||
|
pmm_bitmap[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Boot == NULL || Boot->alloc_pages == NULL) {
|
||||||
|
SAFE_PRINT(Boot, L"PMM: page allocator unavailable\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = Boot->alloc_pages(PMM_POOL_PAGES, &pool_addr);
|
||||||
|
if (EFI_ERROR(Status)) {
|
||||||
|
SAFE_PRINT(Boot, L"PMM: failed to allocate pool (%d pages): %r\n\r",
|
||||||
|
(UINTN)PMM_POOL_PAGES, Status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmm_pool_base = (UINT64)pool_addr;
|
||||||
|
pmm_total_pages = PMM_POOL_PAGES;
|
||||||
|
pmm_free_count = PMM_POOL_PAGES;
|
||||||
|
pmm_ready = TRUE;
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L" PMM : %d pages (%d KB) at 0x%lx\n\r",
|
||||||
|
pmm_total_pages,
|
||||||
|
(pmm_total_pages * PAGE_SIZE) / 1024,
|
||||||
|
pmm_pool_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 pmm_alloc_page(void)
|
||||||
|
{
|
||||||
|
UINTN i;
|
||||||
|
|
||||||
|
if (!pmm_ready || pmm_free_count == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pmm_total_pages; i++) {
|
||||||
|
if (!pmm_test_bit(i)) {
|
||||||
|
pmm_set_bit(i);
|
||||||
|
pmm_free_count--;
|
||||||
|
return pmm_pool_base + ((UINT64)i * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free_page(UINT64 phys_addr)
|
||||||
|
{
|
||||||
|
UINTN idx;
|
||||||
|
|
||||||
|
if (!pmm_ready) return;
|
||||||
|
if (phys_addr < pmm_pool_base) return;
|
||||||
|
|
||||||
|
idx = (UINTN)((phys_addr - pmm_pool_base) / PAGE_SIZE);
|
||||||
|
if (idx >= pmm_total_pages) return;
|
||||||
|
if (!pmm_test_bit(idx)) return; /* already free */
|
||||||
|
|
||||||
|
pmm_clear_bit(idx);
|
||||||
|
pmm_free_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 pmm_alloc_pages(UINTN count)
|
||||||
|
{
|
||||||
|
UINTN i, j;
|
||||||
|
BOOLEAN found;
|
||||||
|
|
||||||
|
if (!pmm_ready || count == 0 || count > pmm_total_pages
|
||||||
|
|| pmm_free_count < count) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i + count <= pmm_total_pages; i++) {
|
||||||
|
found = TRUE;
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
if (pmm_test_bit(i + j)) {
|
||||||
|
found = FALSE;
|
||||||
|
i += j; /* skip past the used page */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
pmm_set_bit(i + j);
|
||||||
|
}
|
||||||
|
pmm_free_count -= count;
|
||||||
|
return pmm_pool_base + ((UINT64)i * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmm_free_pages(UINT64 phys_addr, UINTN count)
|
||||||
|
{
|
||||||
|
UINTN i;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
pmm_free_page(phys_addr + ((UINT64)i * PAGE_SIZE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINTN pmm_get_free_pages(void) { return pmm_free_count; }
|
||||||
|
UINTN pmm_get_total_pages(void) { return pmm_total_pages; }
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Paging – manipulate the live 4-level x86-64 page tables
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static UINT64 read_cr3(void)
|
||||||
|
{
|
||||||
|
UINT64 cr3;
|
||||||
|
__asm__ __volatile__("mov %%cr3, %0" : "=r"(cr3));
|
||||||
|
return cr3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void invlpg(UINT64 addr)
|
||||||
|
{
|
||||||
|
__asm__ __volatile__("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT64 *get_pml4(void)
|
||||||
|
{
|
||||||
|
return (UINT64 *)(UINTN)(read_cr3() & PTE_ADDR_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk one level of the page table hierarchy.
|
||||||
|
* If `create` is TRUE and the entry is missing, a fresh zeroed page is
|
||||||
|
* allocated from the PMM and installed.
|
||||||
|
*/
|
||||||
|
static UINT64 *paging_walk_level(UINT64 *table, UINTN index, BOOLEAN create)
|
||||||
|
{
|
||||||
|
UINT64 *next;
|
||||||
|
UINTN i;
|
||||||
|
UINT64 page;
|
||||||
|
|
||||||
|
if (table[index] & PTE_PRESENT) {
|
||||||
|
return (UINT64 *)(UINTN)(table[index] & PTE_ADDR_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!create) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
page = pmm_alloc_page();
|
||||||
|
if (page == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero the freshly-allocated page table */
|
||||||
|
next = (UINT64 *)(UINTN)page;
|
||||||
|
for (i = 0; i < PAGE_SIZE / sizeof(UINT64); i++) {
|
||||||
|
next[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table[index] = page | PTE_PRESENT | PTE_WRITABLE;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paging_init(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
SAFE_PRINT(Boot, L" Page: CR3 = 0x%lx (UEFI identity-mapped)\n\r",
|
||||||
|
read_cr3());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN paging_map_page(UINT64 virt, UINT64 phys, UINT64 flags)
|
||||||
|
{
|
||||||
|
UINT64 *pml4, *pdpt, *pd, *pt;
|
||||||
|
UINTN pml4i, pdpti, pdi, pti;
|
||||||
|
|
||||||
|
pml4i = (virt >> 39) & 0x1FF;
|
||||||
|
pdpti = (virt >> 30) & 0x1FF;
|
||||||
|
pdi = (virt >> 21) & 0x1FF;
|
||||||
|
pti = (virt >> 12) & 0x1FF;
|
||||||
|
|
||||||
|
pml4 = get_pml4();
|
||||||
|
|
||||||
|
pdpt = paging_walk_level(pml4, pml4i, TRUE);
|
||||||
|
if (pdpt == NULL) return FALSE;
|
||||||
|
|
||||||
|
/* 1 GB huge page – cannot carve a 4 KB mapping inside it */
|
||||||
|
if (pdpt[pdpti] & PTE_HUGE) return FALSE;
|
||||||
|
|
||||||
|
pd = paging_walk_level(pdpt, pdpti, TRUE);
|
||||||
|
if (pd == NULL) return FALSE;
|
||||||
|
|
||||||
|
/* 2 MB huge page – cannot carve a 4 KB mapping inside it */
|
||||||
|
if (pd[pdi] & PTE_HUGE) return FALSE;
|
||||||
|
|
||||||
|
pt = paging_walk_level(pd, pdi, TRUE);
|
||||||
|
if (pt == NULL) return FALSE;
|
||||||
|
|
||||||
|
pt[pti] = (phys & PTE_ADDR_MASK) | flags | PTE_PRESENT;
|
||||||
|
invlpg(virt);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paging_unmap_page(UINT64 virt)
|
||||||
|
{
|
||||||
|
UINT64 *pml4, *pdpt, *pd, *pt;
|
||||||
|
UINTN pml4i, pdpti, pdi, pti;
|
||||||
|
|
||||||
|
pml4i = (virt >> 39) & 0x1FF;
|
||||||
|
pdpti = (virt >> 30) & 0x1FF;
|
||||||
|
pdi = (virt >> 21) & 0x1FF;
|
||||||
|
pti = (virt >> 12) & 0x1FF;
|
||||||
|
|
||||||
|
pml4 = get_pml4();
|
||||||
|
|
||||||
|
pdpt = paging_walk_level(pml4, pml4i, FALSE);
|
||||||
|
if (pdpt == NULL) return;
|
||||||
|
if (pdpt[pdpti] & PTE_HUGE) return;
|
||||||
|
|
||||||
|
pd = paging_walk_level(pdpt, pdpti, FALSE);
|
||||||
|
if (pd == NULL) return;
|
||||||
|
if (pd[pdi] & PTE_HUGE) return;
|
||||||
|
|
||||||
|
pt = paging_walk_level(pd, pdi, FALSE);
|
||||||
|
if (pt == NULL) return;
|
||||||
|
|
||||||
|
pt[pti] = 0;
|
||||||
|
invlpg(virt);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT64 paging_get_phys(UINT64 virt)
|
||||||
|
{
|
||||||
|
UINT64 *pml4, *pdpt, *pd, *pt;
|
||||||
|
UINTN pml4i, pdpti, pdi, pti;
|
||||||
|
|
||||||
|
pml4i = (virt >> 39) & 0x1FF;
|
||||||
|
pdpti = (virt >> 30) & 0x1FF;
|
||||||
|
pdi = (virt >> 21) & 0x1FF;
|
||||||
|
pti = (virt >> 12) & 0x1FF;
|
||||||
|
|
||||||
|
pml4 = get_pml4();
|
||||||
|
|
||||||
|
if (!(pml4[pml4i] & PTE_PRESENT)) return 0;
|
||||||
|
pdpt = (UINT64 *)(UINTN)(pml4[pml4i] & PTE_ADDR_MASK);
|
||||||
|
|
||||||
|
if (!(pdpt[pdpti] & PTE_PRESENT)) return 0;
|
||||||
|
if (pdpt[pdpti] & PTE_HUGE) {
|
||||||
|
/* 1 GB page */
|
||||||
|
return (pdpt[pdpti] & 0x000FFFFFC0000000ULL) | (virt & 0x3FFFFFFFULL);
|
||||||
|
}
|
||||||
|
pd = (UINT64 *)(UINTN)(pdpt[pdpti] & PTE_ADDR_MASK);
|
||||||
|
|
||||||
|
if (!(pd[pdi] & PTE_PRESENT)) return 0;
|
||||||
|
if (pd[pdi] & PTE_HUGE) {
|
||||||
|
/* 2 MB page */
|
||||||
|
return (pd[pdi] & 0x000FFFFFFFE00000ULL) | (virt & 0x1FFFFFULL);
|
||||||
|
}
|
||||||
|
pt = (UINT64 *)(UINTN)(pd[pdi] & PTE_ADDR_MASK);
|
||||||
|
|
||||||
|
if (!(pt[pti] & PTE_PRESENT)) return 0;
|
||||||
|
return (pt[pti] & PTE_ADDR_MASK) | (virt & 0xFFFULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Heap Allocator – first-fit free-list with coalescing
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static HeapBlock *heap_start = NULL;
|
||||||
|
static BOOLEAN heap_ready = FALSE;
|
||||||
|
|
||||||
|
static UINTN align_up(UINTN val, UINTN align)
|
||||||
|
{
|
||||||
|
return (val + align - 1) & ~(align - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void heap_init(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
UINT64 phys;
|
||||||
|
UINTN heap_size;
|
||||||
|
|
||||||
|
phys = pmm_alloc_pages(HEAP_INITIAL_PAGES);
|
||||||
|
if (phys == 0) {
|
||||||
|
SAFE_PRINT(Boot, L" Heap: failed to allocate pages\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_size = HEAP_INITIAL_PAGES * PAGE_SIZE;
|
||||||
|
heap_start = (HeapBlock *)(UINTN)phys;
|
||||||
|
|
||||||
|
heap_start->magic = HEAP_BLOCK_MAGIC;
|
||||||
|
heap_start->state = HEAP_BLOCK_FREE;
|
||||||
|
heap_start->size = heap_size - sizeof(HeapBlock);
|
||||||
|
heap_start->next = NULL;
|
||||||
|
heap_start->prev = NULL;
|
||||||
|
|
||||||
|
heap_ready = TRUE;
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L" Heap: %d KB at 0x%lx\n\r",
|
||||||
|
heap_size / 1024, phys);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *kmalloc(UINTN size)
|
||||||
|
{
|
||||||
|
HeapBlock *block, *split;
|
||||||
|
UINTN aligned;
|
||||||
|
|
||||||
|
if (!heap_ready || size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned = align_up(size, HEAP_ALIGN);
|
||||||
|
|
||||||
|
for (block = heap_start; block != NULL; block = block->next) {
|
||||||
|
if (block->magic != HEAP_BLOCK_MAGIC) {
|
||||||
|
return NULL; /* heap corruption */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block->state != HEAP_BLOCK_FREE || block->size < aligned) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to split if there is room for another header + 16 bytes */
|
||||||
|
if (block->size >= aligned + sizeof(HeapBlock) + HEAP_ALIGN) {
|
||||||
|
split = (HeapBlock *)((UINT8 *)block + sizeof(HeapBlock) + aligned);
|
||||||
|
split->magic = HEAP_BLOCK_MAGIC;
|
||||||
|
split->state = HEAP_BLOCK_FREE;
|
||||||
|
split->size = block->size - aligned - sizeof(HeapBlock);
|
||||||
|
split->next = block->next;
|
||||||
|
split->prev = block;
|
||||||
|
|
||||||
|
if (block->next != NULL) {
|
||||||
|
block->next->prev = split;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->next = split;
|
||||||
|
block->size = aligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->state = HEAP_BLOCK_USED;
|
||||||
|
return (void *)((UINT8 *)block + sizeof(HeapBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL; /* out of heap memory */
|
||||||
|
}
|
||||||
|
|
||||||
|
void kfree(void *ptr)
|
||||||
|
{
|
||||||
|
HeapBlock *block;
|
||||||
|
|
||||||
|
if (ptr == NULL || !heap_ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
block = (HeapBlock *)((UINT8 *)ptr - sizeof(HeapBlock));
|
||||||
|
|
||||||
|
if (block->magic != HEAP_BLOCK_MAGIC || block->state != HEAP_BLOCK_USED) {
|
||||||
|
return; /* bad pointer or double-free */
|
||||||
|
}
|
||||||
|
|
||||||
|
block->state = HEAP_BLOCK_FREE;
|
||||||
|
|
||||||
|
/* Coalesce with next neighbour */
|
||||||
|
if (block->next != NULL
|
||||||
|
&& block->next->magic == HEAP_BLOCK_MAGIC
|
||||||
|
&& block->next->state == HEAP_BLOCK_FREE) {
|
||||||
|
block->size += sizeof(HeapBlock) + block->next->size;
|
||||||
|
block->next = block->next->next;
|
||||||
|
if (block->next != NULL) {
|
||||||
|
block->next->prev = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coalesce with previous neighbour */
|
||||||
|
if (block->prev != NULL
|
||||||
|
&& block->prev->magic == HEAP_BLOCK_MAGIC
|
||||||
|
&& block->prev->state == HEAP_BLOCK_FREE) {
|
||||||
|
block->prev->size += sizeof(HeapBlock) + block->size;
|
||||||
|
block->prev->next = block->next;
|
||||||
|
if (block->next != NULL) {
|
||||||
|
block->next->prev = block->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void heap_get_stats(UINTN *total, UINTN *used, UINTN *free_mem,
|
||||||
|
UINTN *num_blocks)
|
||||||
|
{
|
||||||
|
HeapBlock *b;
|
||||||
|
|
||||||
|
*total = 0; *used = 0; *free_mem = 0; *num_blocks = 0;
|
||||||
|
|
||||||
|
if (!heap_ready) return;
|
||||||
|
|
||||||
|
for (b = heap_start; b != NULL && b->magic == HEAP_BLOCK_MAGIC;
|
||||||
|
b = b->next) {
|
||||||
|
(*num_blocks)++;
|
||||||
|
*total += b->size;
|
||||||
|
if (b->state == HEAP_BLOCK_USED) {
|
||||||
|
*used += b->size;
|
||||||
|
} else {
|
||||||
|
*free_mem += b->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* Top-level helpers
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void memory_init(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
SAFE_PRINT(Boot, L"Initializing memory management...\n\r");
|
||||||
|
pmm_init(Boot);
|
||||||
|
paging_init(Boot);
|
||||||
|
heap_init(Boot);
|
||||||
|
SAFE_PRINT(Boot, L"Memory management ready.\n\r\n\r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_print_stats(BootInfo *Boot)
|
||||||
|
{
|
||||||
|
UINTN h_total, h_used, h_free, h_blocks;
|
||||||
|
UINTN p_total, p_free, p_used;
|
||||||
|
|
||||||
|
p_total = pmm_get_total_pages();
|
||||||
|
p_free = pmm_get_free_pages();
|
||||||
|
p_used = p_total - p_free;
|
||||||
|
|
||||||
|
heap_get_stats(&h_total, &h_used, &h_free, &h_blocks);
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"Memory Statistics\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"================================================\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"Physical Memory Manager:\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" Pool Base: 0x%lx\n\r", pmm_pool_base);
|
||||||
|
SAFE_PRINT(Boot, L" Total Pages: %d (%d KB)\n\r",
|
||||||
|
p_total, (p_total * PAGE_SIZE) / 1024);
|
||||||
|
SAFE_PRINT(Boot, L" Used Pages: %d (%d KB)\n\r",
|
||||||
|
p_used, (p_used * PAGE_SIZE) / 1024);
|
||||||
|
SAFE_PRINT(Boot, L" Free Pages: %d (%d KB)\n\r",
|
||||||
|
p_free, (p_free * PAGE_SIZE) / 1024);
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"Heap Allocator:\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" Total: %d bytes\n\r", h_total);
|
||||||
|
SAFE_PRINT(Boot, L" Used: %d bytes\n\r", h_used);
|
||||||
|
SAFE_PRINT(Boot, L" Free: %d bytes\n\r", h_free);
|
||||||
|
SAFE_PRINT(Boot, L" Blocks: %d\n\r", h_blocks);
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
|
||||||
|
SAFE_PRINT(Boot, L"Paging:\n\r");
|
||||||
|
SAFE_PRINT(Boot, L" CR3: 0x%lx\n\r", read_cr3());
|
||||||
|
SAFE_PRINT(Boot, L" Mode: 4-level (PML4)\n\r");
|
||||||
|
SAFE_PRINT(Boot, L"\n\r");
|
||||||
|
}
|
||||||
75
memory.h
Normal file
75
memory.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#ifndef MEMORY_H
|
||||||
|
#define MEMORY_H
|
||||||
|
|
||||||
|
#include <efi.h>
|
||||||
|
#include "boot_info.h"
|
||||||
|
|
||||||
|
/* Page size constants */
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define PAGE_SHIFT 12
|
||||||
|
#define PAGE_MASK (~(UINT64)(PAGE_SIZE - 1))
|
||||||
|
|
||||||
|
/* Page table entry flags */
|
||||||
|
#define PTE_PRESENT (1ULL << 0)
|
||||||
|
#define PTE_WRITABLE (1ULL << 1)
|
||||||
|
#define PTE_USER (1ULL << 2)
|
||||||
|
#define PTE_WRITETHROUGH (1ULL << 3)
|
||||||
|
#define PTE_NOCACHE (1ULL << 4)
|
||||||
|
#define PTE_ACCESSED (1ULL << 5)
|
||||||
|
#define PTE_DIRTY (1ULL << 6)
|
||||||
|
#define PTE_HUGE (1ULL << 7)
|
||||||
|
#define PTE_GLOBAL (1ULL << 8)
|
||||||
|
#define PTE_NX (1ULL << 63)
|
||||||
|
|
||||||
|
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000ULL
|
||||||
|
|
||||||
|
/* PMM pool: 16 MB managed by the kernel */
|
||||||
|
#define PMM_POOL_SIZE (16ULL * 1024 * 1024)
|
||||||
|
#define PMM_POOL_PAGES (PMM_POOL_SIZE / PAGE_SIZE)
|
||||||
|
|
||||||
|
/* Heap constants */
|
||||||
|
#define HEAP_INITIAL_PAGES 64 /* 256 KB initial heap */
|
||||||
|
#define HEAP_BLOCK_MAGIC 0xDEADBEEFU
|
||||||
|
#define HEAP_BLOCK_FREE 0
|
||||||
|
#define HEAP_BLOCK_USED 1
|
||||||
|
#define HEAP_ALIGN 16
|
||||||
|
|
||||||
|
/* -------- Physical Memory Manager -------- */
|
||||||
|
|
||||||
|
void pmm_init(BootInfo *Boot);
|
||||||
|
UINT64 pmm_alloc_page(void);
|
||||||
|
void pmm_free_page(UINT64 phys_addr);
|
||||||
|
UINT64 pmm_alloc_pages(UINTN count);
|
||||||
|
void pmm_free_pages(UINT64 phys_addr, UINTN count);
|
||||||
|
UINTN pmm_get_free_pages(void);
|
||||||
|
UINTN pmm_get_total_pages(void);
|
||||||
|
|
||||||
|
/* -------- Paging -------- */
|
||||||
|
|
||||||
|
void paging_init(BootInfo *Boot);
|
||||||
|
BOOLEAN paging_map_page(UINT64 virt, UINT64 phys, UINT64 flags);
|
||||||
|
void paging_unmap_page(UINT64 virt);
|
||||||
|
UINT64 paging_get_phys(UINT64 virt);
|
||||||
|
|
||||||
|
/* -------- Heap Allocator -------- */
|
||||||
|
|
||||||
|
typedef struct HeapBlock {
|
||||||
|
UINT32 magic;
|
||||||
|
UINT32 state;
|
||||||
|
UINTN size; /* usable bytes (excludes header) */
|
||||||
|
struct HeapBlock *next;
|
||||||
|
struct HeapBlock *prev;
|
||||||
|
} HeapBlock;
|
||||||
|
|
||||||
|
void heap_init(BootInfo *Boot);
|
||||||
|
void *kmalloc(UINTN size);
|
||||||
|
void kfree(void *ptr);
|
||||||
|
void heap_get_stats(UINTN *total, UINTN *used, UINTN *free_mem,
|
||||||
|
UINTN *num_blocks);
|
||||||
|
|
||||||
|
/* -------- Top-level helpers -------- */
|
||||||
|
|
||||||
|
void memory_init(BootInfo *Boot);
|
||||||
|
void memory_print_stats(BootInfo *Boot);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user