[feat] Memory manager
This commit is contained in:
@@ -1,18 +0,0 @@
|
||||
name: PR Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install build-essential qemu-system-x86 make ovmf -y
|
||||
- name: Build OS
|
||||
run: make all -j$(nproc)
|
||||
@@ -4,11 +4,11 @@ LDFLAGS = -shared -Bsymbolic -Lgnu-efi/x86_64/lib -Lgnu-efi/x86_64/gnuefi -Tgnu-
|
||||
LDLIBS = -lgnuefi -lefi --no-undefined
|
||||
|
||||
SRC_C = $(wildcard *.c)
|
||||
SRC_CPP = $(wildcard */*.cpp)
|
||||
SRC_CPP = $(wildcard */*.cpp) $(wildcard */*/*.cpp)
|
||||
OBJ = $(SRC_C:%.c=build/%.o) $(SRC_CPP:%.cpp=build/%.o)
|
||||
|
||||
_bd:
|
||||
@mkdir -p build/graphics build/kernel build/fonts
|
||||
@mkdir -p build/graphics build/kernel build/fonts build/kernel/memory
|
||||
|
||||
gnu-efi/x86_64/gnuefi/crt0-efi-x86_64.o:
|
||||
@echo "* Building gnu-efi..."
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
void init_heap();
|
||||
void* kmalloc(UINTN size);
|
||||
void kfree(void* ptr);
|
||||
void* kcalloc(UINTN num, UINTN size);
|
||||
void* krealloc(void* ptr, UINTN new_size);
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
#define PAGE_SHIFT 12
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1ULL))
|
||||
|
||||
typedef struct {
|
||||
UINT8* bitmap;
|
||||
UINTN bitmap_size;
|
||||
UINTN total_pages;
|
||||
UINTN free_pages;
|
||||
void* free_list_head;
|
||||
UINT64 base_addr;
|
||||
UINT64 max_addr;
|
||||
} pmm_t;
|
||||
|
||||
extern pmm_t g_pmm;
|
||||
|
||||
EFI_STATUS pmm_init();
|
||||
void* pmm_alloc_pages(UINTN n);
|
||||
void pmm_free_pages(void* addr, UINTN n);
|
||||
UINTN pmm_get_free_count();
|
||||
BOOLEAN pmm_is_page_free(void* addr);
|
||||
@@ -12,4 +12,5 @@ extern serial_context g_serial;
|
||||
void serial_init(EFI_SERIAL_IO_PROTOCOL *SerialIo); // 初始化串行驱动
|
||||
void serial_write(const char *str); // 往串行写string
|
||||
void serial_write_char(char c); // 往串行写char(不推荐使用)
|
||||
void serial_write_hex(UINTN val); // 往串行写十六进制数字
|
||||
char serial_read_char(); // 读串行
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <fonts/pixel_font.h>
|
||||
#include <serial.h>
|
||||
#include <common.h>
|
||||
#include <memory/pmm.h>
|
||||
#include <memory/heap.h>
|
||||
|
||||
inline void init_gop() {
|
||||
// 初始化 GOP
|
||||
@@ -51,6 +53,47 @@ extern "C" void kernel_main() {
|
||||
uefi_call_wrapper((void*)ST->ConOut->ClearScreen, 1, ST->ConOut);
|
||||
serial_write("\n\n"); // 防止和前面串了serial.log看不清
|
||||
|
||||
// init memory managers
|
||||
serial_write("Sylva: init PMM...\n");
|
||||
EFI_STATUS st = pmm_init();
|
||||
if (EFI_ERROR(st)) {
|
||||
serial_write("Sylva: PMM init FAILED!\n");
|
||||
} else {
|
||||
serial_write("Sylva: PMM init OK\n");
|
||||
serial_write("Sylva: free pages = ");
|
||||
serial_write_hex(pmm_get_free_count());
|
||||
serial_write("\n");
|
||||
}
|
||||
|
||||
serial_write("Sylva: init heap...\n");
|
||||
init_heap();
|
||||
|
||||
// test kmalloc/kfree
|
||||
serial_write("Sylva: kmalloc test...\n");
|
||||
void* p1 = kmalloc(64);
|
||||
void* p2 = kmalloc(128);
|
||||
void* p3 = kmalloc(256);
|
||||
serial_write("Sylva: p1 = ");
|
||||
serial_write_hex((UINTN)p1);
|
||||
serial_write(" p2 = ");
|
||||
serial_write_hex((UINTN)p2);
|
||||
serial_write(" p3 = ");
|
||||
serial_write_hex((UINTN)p3);
|
||||
serial_write("\n");
|
||||
|
||||
serial_write("Sylva: kfree test...\n");
|
||||
kfree(p2);
|
||||
kfree(p1);
|
||||
kfree(p3);
|
||||
|
||||
void* p4 = kmalloc(32);
|
||||
serial_write("Sylva: realloc p4 = ");
|
||||
serial_write_hex((UINTN)p4);
|
||||
serial_write("\n");
|
||||
kfree(p4);
|
||||
|
||||
serial_write("Sylva: memory init done.\n");
|
||||
|
||||
pf_print("Welcome to Sylva OS!\n");
|
||||
serial_write(" Kernel prepared well, start loop.\n");
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#include <memory/heap.h>
|
||||
#include <memory/pmm.h>
|
||||
#include <serial.h>
|
||||
#include <BUILD_INFOS.h>
|
||||
|
||||
struct heap_block {
|
||||
UINTN size; // includes header; bit 0 = 1 used, 0 free
|
||||
struct heap_block* next; // free list link (only valid when free)
|
||||
};
|
||||
|
||||
#define HEAP_ALIGN 16
|
||||
#define HEADER_SIZE ((UINTN)sizeof(struct heap_block))
|
||||
#define MIN_BLOCK_SIZE (HEADER_SIZE + HEAP_ALIGN)
|
||||
|
||||
#define HEAP_INIT_PAGES 4
|
||||
|
||||
#define BLOCK_SIZE(block) ((block)->size & ~(UINTN)1)
|
||||
#define IS_USED(block) ((block)->size & 1)
|
||||
#define IS_FREE(block) (!IS_USED(block))
|
||||
|
||||
static struct heap_block* g_heap_free_list = NULL;
|
||||
static void* g_heap_start = NULL;
|
||||
static void* g_heap_end = NULL;
|
||||
|
||||
static UINTN align_up(UINTN val, UINTN align) {
|
||||
return (val + align - 1) & ~(align - 1ULL);
|
||||
}
|
||||
|
||||
static struct heap_block* next_block(struct heap_block* block) {
|
||||
return (struct heap_block*)((UINT8*)block + BLOCK_SIZE(block));
|
||||
}
|
||||
|
||||
static void heap_expand(UINTN min_size) {
|
||||
UINTN pages = (min_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
void* mem = pmm_alloc_pages(pages);
|
||||
if (!mem) {
|
||||
serial_write("HEAP: expand failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct heap_block* new_block = (struct heap_block*)mem;
|
||||
new_block->size = pages * PAGE_SIZE;
|
||||
new_block->next = NULL;
|
||||
|
||||
// Add to free list (sorted by address for coalescing)
|
||||
struct heap_block** prev = &g_heap_free_list;
|
||||
while (*prev && (UINT8*)*prev < (UINT8*)new_block) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
new_block->next = *prev;
|
||||
*prev = new_block;
|
||||
|
||||
// Try to merge with the previous free block if adjacent
|
||||
if (prev != &g_heap_free_list) {
|
||||
struct heap_block* prev_block = g_heap_free_list;
|
||||
while (prev_block->next != new_block) {
|
||||
prev_block = prev_block->next;
|
||||
}
|
||||
if ((UINT8*)prev_block + BLOCK_SIZE(prev_block) == (UINT8*)new_block) {
|
||||
prev_block->size += new_block->size;
|
||||
prev_block->next = new_block->next;
|
||||
new_block = prev_block;
|
||||
}
|
||||
}
|
||||
|
||||
if ((UINT8*)new_block + BLOCK_SIZE(new_block) > (UINT8*)g_heap_end) {
|
||||
g_heap_end = (UINT8*)new_block + BLOCK_SIZE(new_block);
|
||||
}
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("HEAP: expanded by ");
|
||||
serial_write_hex(pages * PAGE_SIZE);
|
||||
serial_write(" bytes\n");
|
||||
}
|
||||
}
|
||||
|
||||
void init_heap() {
|
||||
void* mem = pmm_alloc_pages(HEAP_INIT_PAGES);
|
||||
if (!mem) {
|
||||
serial_write("HEAP: init failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
g_heap_start = mem;
|
||||
g_heap_end = (void*)((UINT8*)mem + HEAP_INIT_PAGES * PAGE_SIZE);
|
||||
|
||||
struct heap_block* initial = (struct heap_block*)mem;
|
||||
initial->size = HEAP_INIT_PAGES * PAGE_SIZE;
|
||||
initial->next = NULL;
|
||||
g_heap_free_list = initial;
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("HEAP: init OK, ");
|
||||
serial_write_hex(HEAP_INIT_PAGES * PAGE_SIZE);
|
||||
serial_write(" bytes @ ");
|
||||
serial_write_hex((UINTN)mem);
|
||||
serial_write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void* kmalloc(UINTN size) {
|
||||
if (size == 0) return NULL;
|
||||
|
||||
UINTN alloc_size = align_up(size + HEADER_SIZE, HEAP_ALIGN);
|
||||
if (alloc_size < MIN_BLOCK_SIZE) alloc_size = MIN_BLOCK_SIZE;
|
||||
|
||||
struct heap_block** prev = &g_heap_free_list;
|
||||
while (*prev) {
|
||||
UINTN block_sz = BLOCK_SIZE(*prev);
|
||||
if (block_sz >= alloc_size) {
|
||||
// Found a suitable block
|
||||
struct heap_block* block = *prev;
|
||||
|
||||
// Split if remaining space is useful
|
||||
if (block_sz >= alloc_size + MIN_BLOCK_SIZE) {
|
||||
struct heap_block* split = (struct heap_block*)((UINT8*)block + alloc_size);
|
||||
split->size = block_sz - alloc_size;
|
||||
// Insert split into free list
|
||||
split->next = block->next;
|
||||
block->size = alloc_size;
|
||||
*prev = split;
|
||||
} else {
|
||||
// Use the whole block
|
||||
*prev = block->next;
|
||||
block->size = block_sz | 1; // mark used
|
||||
}
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS && size > 1024) {
|
||||
serial_write("HEAP: kmalloc ");
|
||||
serial_write_hex(size);
|
||||
serial_write(" -> ");
|
||||
serial_write_hex((UINTN)(block + 1));
|
||||
serial_write("\n");
|
||||
}
|
||||
|
||||
return (void*)(block + 1);
|
||||
}
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
|
||||
// Out of memory in current heap — expand
|
||||
UINTN expand_size = alloc_size > PAGE_SIZE ? alloc_size : PAGE_SIZE;
|
||||
heap_expand(expand_size);
|
||||
|
||||
// Retry after expansion
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
void kfree(void* ptr) {
|
||||
if (!ptr) return;
|
||||
|
||||
struct heap_block* block = (struct heap_block*)ptr - 1;
|
||||
if (IS_FREE(block)) {
|
||||
serial_write("HEAP: double free detected!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as free
|
||||
block->size &= ~(UINTN)1;
|
||||
|
||||
// Merge with next block if it's free
|
||||
struct heap_block* next = next_block(block);
|
||||
if ((UINT8*)next < (UINT8*)g_heap_end) {
|
||||
if (IS_FREE(next)) {
|
||||
// Remove next from free list and merge
|
||||
block->size += next->size;
|
||||
struct heap_block** prev = &g_heap_free_list;
|
||||
while (*prev && *prev != next) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
if (*prev) *prev = next->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert block into free list
|
||||
struct heap_block** prev = &g_heap_free_list;
|
||||
while (*prev && (UINT8*)*prev < (UINT8*)block) {
|
||||
prev = &(*prev)->next;
|
||||
}
|
||||
block->next = *prev;
|
||||
*prev = block;
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("HEAP: kfree @ ");
|
||||
serial_write_hex((UINTN)ptr);
|
||||
serial_write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void* kcalloc(UINTN num, UINTN size) {
|
||||
UINTN total = num * size;
|
||||
void* ptr = kmalloc(total);
|
||||
if (ptr) {
|
||||
UINT8* p = (UINT8*)ptr;
|
||||
for (UINTN i = 0; i < total; i++) {
|
||||
p[i] = 0;
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* krealloc(void* ptr, UINTN new_size) {
|
||||
if (!ptr) return kmalloc(new_size);
|
||||
if (new_size == 0) {
|
||||
kfree(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct heap_block* block = (struct heap_block*)ptr - 1;
|
||||
UINTN old_size = BLOCK_SIZE(block) - HEADER_SIZE;
|
||||
|
||||
if (old_size >= new_size) {
|
||||
// Can we split the shrinkage?
|
||||
UINTN shrink = old_size - new_size;
|
||||
if (shrink >= MIN_BLOCK_SIZE) {
|
||||
block->size = (new_size + HEADER_SIZE) | 1;
|
||||
struct heap_block* split = (struct heap_block*)((UINT8*)ptr + new_size);
|
||||
split->size = shrink;
|
||||
kfree(split + 1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* new_ptr = kmalloc(new_size);
|
||||
if (new_ptr) {
|
||||
UINT8* src = (UINT8*)ptr;
|
||||
UINT8* dst = (UINT8*)new_ptr;
|
||||
for (UINTN i = 0; i < old_size; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
kfree(ptr);
|
||||
}
|
||||
return new_ptr;
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
#include <memory/pmm.h>
|
||||
#include <efilib.h>
|
||||
#include <serial.h>
|
||||
#include <BUILD_INFOS.h>
|
||||
|
||||
pmm_t g_pmm;
|
||||
|
||||
static inline void bitmap_set(UINTN idx) {
|
||||
g_pmm.bitmap[idx / 8] |= (1 << (idx % 8));
|
||||
}
|
||||
|
||||
static inline void bitmap_clear(UINTN idx) {
|
||||
g_pmm.bitmap[idx / 8] &= ~(1 << (idx % 8));
|
||||
}
|
||||
|
||||
static inline BOOLEAN bitmap_test(UINTN idx) {
|
||||
return (g_pmm.bitmap[idx / 8] >> (idx % 8)) & 1;
|
||||
}
|
||||
|
||||
// Clean stale entries from free list head
|
||||
static void clean_free_list() {
|
||||
while (g_pmm.free_list_head != NULL &&
|
||||
bitmap_test((UINTN)g_pmm.free_list_head / PAGE_SIZE)) {
|
||||
g_pmm.free_list_head = *(void**)g_pmm.free_list_head;
|
||||
}
|
||||
}
|
||||
|
||||
EFI_STATUS pmm_init() {
|
||||
UINTN map_size = 0;
|
||||
UINTN map_key;
|
||||
UINTN desc_size;
|
||||
UINT32 desc_version;
|
||||
|
||||
EFI_STATUS status = uefi_call_wrapper(
|
||||
(void*)ST->BootServices->GetMemoryMap, 5,
|
||||
&map_size, NULL, &map_key, &desc_size, &desc_version
|
||||
);
|
||||
|
||||
map_size += desc_size * 64;
|
||||
|
||||
EFI_MEMORY_DESCRIPTOR* mem_map = NULL;
|
||||
status = uefi_call_wrapper(
|
||||
(void*)ST->BootServices->AllocatePool, 3,
|
||||
EfiLoaderData, map_size, (void**)&mem_map
|
||||
);
|
||||
if (EFI_ERROR(status)) return status;
|
||||
|
||||
status = uefi_call_wrapper(
|
||||
(void*)ST->BootServices->GetMemoryMap, 5,
|
||||
&map_size, mem_map, &map_key, &desc_size, &desc_version
|
||||
);
|
||||
if (EFI_ERROR(status)) {
|
||||
uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map);
|
||||
return status;
|
||||
}
|
||||
|
||||
UINTN entry_count = map_size / desc_size;
|
||||
|
||||
// First pass: count total pages and find max physical address
|
||||
UINT64 max_addr = 0;
|
||||
UINT64 total_free = 0;
|
||||
for (UINTN i = 0; i < entry_count; i++) {
|
||||
EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)mem_map + i * desc_size);
|
||||
UINT64 end = desc->PhysicalStart + desc->NumberOfPages * PAGE_SIZE;
|
||||
if (end > max_addr) max_addr = end;
|
||||
if (desc->Type == EfiConventionalMemory) {
|
||||
total_free += desc->NumberOfPages * PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
g_pmm.base_addr = 0;
|
||||
g_pmm.max_addr = max_addr;
|
||||
|
||||
// How many pages does the bitmap cover?
|
||||
UINTN total_pages = (UINTN)(max_addr / PAGE_SIZE);
|
||||
g_pmm.total_pages = total_pages;
|
||||
|
||||
// Bitmap size in bytes, rounded up to page boundary
|
||||
g_pmm.bitmap_size = ((total_pages + 7) / 8);
|
||||
UINTN bitmap_pages = (g_pmm.bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
g_pmm.bitmap_size = bitmap_pages * PAGE_SIZE; // round to full pages
|
||||
|
||||
// Place bitmap at the end of the highest free conventional memory region
|
||||
UINT64 bitmap_addr = 0;
|
||||
for (UINTN i = 0; i < entry_count; i++) {
|
||||
EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)mem_map + i * desc_size);
|
||||
if (desc->Type == EfiConventionalMemory) {
|
||||
UINT64 region_bytes = desc->NumberOfPages * PAGE_SIZE;
|
||||
if (region_bytes >= g_pmm.bitmap_size) {
|
||||
UINT64 candidate = desc->PhysicalStart + region_bytes - g_pmm.bitmap_size;
|
||||
if (candidate > bitmap_addr) {
|
||||
bitmap_addr = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bitmap_addr == 0) {
|
||||
serial_write("PMM: ERROR - no space for bitmap!\n");
|
||||
uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
g_pmm.bitmap = (UINT8*)(UINTN)bitmap_addr;
|
||||
|
||||
// Init bitmap: mark ALL pages as used (0xFF)
|
||||
for (UINTN i = 0; i < g_pmm.bitmap_size; i++) {
|
||||
g_pmm.bitmap[i] = 0xFF;
|
||||
}
|
||||
|
||||
// Mark free pages (EfiConventionalMemory) as free in bitmap
|
||||
g_pmm.free_pages = 0;
|
||||
UINT64 bm_start_page = bitmap_addr / PAGE_SIZE;
|
||||
UINT64 bm_end_page = (bitmap_addr + g_pmm.bitmap_size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
|
||||
for (UINTN i = 0; i < entry_count; i++) {
|
||||
EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)mem_map + i * desc_size);
|
||||
if (desc->Type != EfiConventionalMemory) continue;
|
||||
|
||||
UINT64 start_page = desc->PhysicalStart / PAGE_SIZE;
|
||||
UINT64 end_page = start_page + desc->NumberOfPages;
|
||||
|
||||
for (UINT64 p = start_page; p < end_page; p++) {
|
||||
// Skip bitmap pages
|
||||
if (p >= bm_start_page && p < bm_end_page) continue;
|
||||
bitmap_clear((UINTN)p);
|
||||
g_pmm.free_pages++;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark bitmap pages as used
|
||||
for (UINT64 p = bm_start_page; p < bm_end_page; p++) {
|
||||
bitmap_set((UINTN)p);
|
||||
}
|
||||
|
||||
// Build free list by linking free pages
|
||||
g_pmm.free_list_head = NULL;
|
||||
void* prev = NULL;
|
||||
for (UINTN i = 0; i < entry_count; i++) {
|
||||
EFI_MEMORY_DESCRIPTOR* desc = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)mem_map + i * desc_size);
|
||||
if (desc->Type != EfiConventionalMemory) continue;
|
||||
|
||||
UINT64 start_page = desc->PhysicalStart / PAGE_SIZE;
|
||||
UINT64 end_page = start_page + desc->NumberOfPages;
|
||||
|
||||
for (UINT64 p = start_page; p < end_page; p++) {
|
||||
if (p >= bm_start_page && p < bm_end_page) continue;
|
||||
void* page = (void*)(UINTN)(p * PAGE_SIZE);
|
||||
if (prev) {
|
||||
*(void**)prev = page;
|
||||
} else {
|
||||
g_pmm.free_list_head = page;
|
||||
}
|
||||
prev = page;
|
||||
}
|
||||
}
|
||||
if (prev) *(void**)prev = NULL;
|
||||
|
||||
uefi_call_wrapper((void*)ST->BootServices->FreePool, 1, mem_map);
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("PMM: init OK, total ");
|
||||
serial_write_hex(total_pages);
|
||||
serial_write(" pages (");
|
||||
serial_write_hex(total_free / (1024*1024));
|
||||
serial_write(" MB free), bitmap ");
|
||||
serial_write_hex(bitmap_pages);
|
||||
serial_write(" pages @ ");
|
||||
serial_write_hex(bitmap_addr);
|
||||
serial_write("\n");
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
void* pmm_alloc_pages(UINTN n) {
|
||||
if (n == 0) return NULL;
|
||||
|
||||
clean_free_list();
|
||||
|
||||
if (n == 1) {
|
||||
if (g_pmm.free_list_head == NULL) {
|
||||
serial_write("PMM: OOM (no free pages)\n");
|
||||
return NULL;
|
||||
}
|
||||
void* page = g_pmm.free_list_head;
|
||||
g_pmm.free_list_head = *(void**)page;
|
||||
*(void**)page = NULL; // clear the next pointer
|
||||
|
||||
UINTN idx = (UINTN)page / PAGE_SIZE;
|
||||
bitmap_set(idx);
|
||||
g_pmm.free_pages--;
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("PMM: alloc 1 page @ ");
|
||||
serial_write_hex((UINTN)page);
|
||||
serial_write("\n");
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
// n > 1: scan bitmap for n consecutive free pages
|
||||
UINTN consecutive = 0;
|
||||
UINTN start_idx = 0;
|
||||
for (UINTN i = 0; i < g_pmm.total_pages; i++) {
|
||||
if (!bitmap_test(i)) {
|
||||
if (consecutive == 0) start_idx = i;
|
||||
consecutive++;
|
||||
if (consecutive == n) {
|
||||
// Found n consecutive free pages
|
||||
void* base = (void*)(UINTN)(start_idx * PAGE_SIZE);
|
||||
for (UINTN j = 0; j < n; j++) {
|
||||
bitmap_set(start_idx + j);
|
||||
}
|
||||
g_pmm.free_pages -= n;
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("PMM: alloc ");
|
||||
serial_write_hex(n);
|
||||
serial_write(" pages @ ");
|
||||
serial_write_hex((UINTN)base);
|
||||
serial_write("\n");
|
||||
}
|
||||
return base;
|
||||
}
|
||||
} else {
|
||||
consecutive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
serial_write("PMM: OOM (no contiguous free pages)\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pmm_free_pages(void* addr, UINTN n) {
|
||||
UINTN start_idx = (UINTN)addr / PAGE_SIZE;
|
||||
|
||||
// Add freed pages to free list
|
||||
for (UINTN i = 0; i < n; i++) {
|
||||
UINTN idx = start_idx + i;
|
||||
bitmap_clear(idx);
|
||||
g_pmm.free_pages++;
|
||||
|
||||
void* page = (void*)(UINTN)(idx * PAGE_SIZE);
|
||||
// Push to front of free list
|
||||
*(void**)page = g_pmm.free_list_head;
|
||||
g_pmm.free_list_head = page;
|
||||
}
|
||||
|
||||
if (ENABLE_SERIAL_PRINTS) {
|
||||
serial_write("PMM: free ");
|
||||
serial_write_hex(n);
|
||||
serial_write(" pages @ ");
|
||||
serial_write_hex((UINTN)addr);
|
||||
serial_write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
UINTN pmm_get_free_count() {
|
||||
return g_pmm.free_pages;
|
||||
}
|
||||
|
||||
BOOLEAN pmm_is_page_free(void* addr) {
|
||||
UINTN idx = (UINTN)addr / PAGE_SIZE;
|
||||
if (idx >= g_pmm.total_pages) return FALSE;
|
||||
return !bitmap_test(idx);
|
||||
}
|
||||
@@ -35,6 +35,18 @@ void serial_write(const char *str) {
|
||||
}
|
||||
}
|
||||
|
||||
void serial_write_hex(UINTN val) {
|
||||
char buf[19];
|
||||
buf[0] = '0'; buf[1] = 'x';
|
||||
for (int i = 17; i >= 2; i--) {
|
||||
UINTN digit = val & 0xF;
|
||||
buf[i] = digit < 10 ? '0' + digit : 'A' + digit - 10;
|
||||
val >>= 4;
|
||||
}
|
||||
buf[18] = '\0';
|
||||
serial_write(buf);
|
||||
}
|
||||
|
||||
char serial_read_char() {
|
||||
// 后面可能用的上,比如远程调试?
|
||||
if (!g_serial.SerialIo) return 0;
|
||||
|
||||
Reference in New Issue
Block a user