feat(mm): add minimal kernel heap
This commit is contained in:
parent
7df720c57b
commit
a2e6889932
@ -56,14 +56,15 @@
|
||||
- 已提供最小 `map_page()`、`unmap_page()` 和 `virt_to_phys()` 接口。
|
||||
- 已加入页表 map/unmap/query selftest。
|
||||
- 已拆出 x86 page fault 诊断路径,能输出 fault address、错误码、访问类型和权限来源。
|
||||
- `scripts/check.sh` 已验证 `physical pages free=`、物理页 selftest、页表根切换、页表 selftest 和 page fault 日志。
|
||||
- 已建立最小内核堆,提供 `kmalloc()` 和 `kfree()`,底层通过页表按需映射物理页。
|
||||
- 已加入内核堆分配、写入、释放、复用 selftest。
|
||||
- `scripts/check.sh` 已验证 `physical pages free=`、物理页 selftest、页表根切换、页表 selftest、内核堆 selftest 和 page fault 日志。
|
||||
|
||||
后续扩展:
|
||||
|
||||
- 长期内存区域模型,不直接依赖 UEFI memory type。
|
||||
- page metadata。
|
||||
- buddy allocator。
|
||||
- 最小内核堆。
|
||||
- slab/slub 或等价小对象缓存。
|
||||
|
||||
验收依据:
|
||||
@ -72,8 +73,11 @@
|
||||
- 正常启动日志包含 `physical page allocator selftest ok`。
|
||||
- 正常启动日志包含 `kernel page table root active`。
|
||||
- 正常启动日志包含 `page table selftest ok`。
|
||||
- 正常启动日志包含 `kernel heap initialized`。
|
||||
- 正常启动日志包含 `kernel heap selftest ok`。
|
||||
- page fault 测试日志包含 `page fault: address=` 和 `access=write mode=kernel reason=not-present`。
|
||||
|
||||
下一阶段:
|
||||
|
||||
- 继续在 `03-memory.md` 内推进最小内核堆。
|
||||
- `03-memory.md` 的最小基础已经闭环。
|
||||
- 下一阶段可以进入 timer/scheduler;如果继续打磨内存,则应补 page metadata、buddy allocator 和 slab/slub。
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
| --- | --- | --- |
|
||||
| `01-early-debug.md` | 基础完成 | 已有 early log 前端、QEMU debug port、COM1 串口和最小 `panic()`。 |
|
||||
| `02-cpu-interrupts.md` | 基础完成 | 已有 GDT/TSS/IDT、exception vector 0-31、`trap_frame` 和 invalid opcode 回归测试。 |
|
||||
| `03-memory.md` | 进行中 | 已有最小物理页 allocator、页表 map/unmap/query 和 page fault 诊断基础;内核堆未完成。 |
|
||||
| `03-memory.md` | 基础完成 | 已有最小物理页 allocator、页表 map/unmap/query、page fault 诊断和内核堆。 |
|
||||
| `04-time-scheduler.md` | 未开始 | 需要 timer、kernel thread、调度和等待队列。 |
|
||||
| `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 |
|
||||
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
|
||||
@ -36,7 +36,7 @@
|
||||
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
|
||||
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
|
||||
|
||||
当前最合适的下一步仍在 `03-memory.md` 内:继续做最小内核堆。
|
||||
当前最合适的下一步是 `04-time-scheduler.md`:先建立 timer interrupt,再引入最小 kernel thread 和调度循环。
|
||||
|
||||
## Linux 级能力缺口路由
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
- 页表基础:已切换到内核自有 PML4,并提供最小 `map_page/unmap_page/virt_to_phys`。
|
||||
- 页表验证:页表 map/unmap/query selftest 已接入启动检查。
|
||||
- page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。
|
||||
- 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。
|
||||
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
||||
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
||||
- `.clang-format` 已用于强制当前 C 代码风格。
|
||||
@ -30,18 +31,19 @@
|
||||
|
||||
- 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。
|
||||
- 完整中断体系:PIC/APIC、外部 IRQ、timer interrupt。
|
||||
- 内存管理扩展:长期内存区域模型、内核堆、后续完整缺页策略。
|
||||
- 内存管理扩展:长期内存区域模型、page metadata、buddy/slab、后续完整缺页策略。
|
||||
- 后续主线:调度、进程、文件系统、用户态和 shell。
|
||||
|
||||
## 当前下一步
|
||||
|
||||
下一步执行:
|
||||
|
||||
- `docs/agents/tasks/03-memory.md`
|
||||
- `docs/agents/tasks/04-time-scheduler.md`
|
||||
|
||||
目标:
|
||||
|
||||
- 建立最小内核堆。
|
||||
- 建立 timer interrupt 基础。
|
||||
- 建立最小 kernel thread 和调度循环。
|
||||
|
||||
## 任务路由
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#ifndef TIANOLE_MM_H
|
||||
#define TIANOLE_MM_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
@ -17,9 +18,12 @@ typedef uint64_t virt_addr_t;
|
||||
void mm_init(const boot_info_t *boot_info);
|
||||
phys_addr_t alloc_page(void);
|
||||
void free_page(phys_addr_t page);
|
||||
void *kmalloc(size_t size);
|
||||
void kfree(void *ptr);
|
||||
int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags);
|
||||
int unmap_page(virt_addr_t virt);
|
||||
int virt_to_phys(virt_addr_t virt, phys_addr_t *phys);
|
||||
void heap_init(void);
|
||||
void page_table_selftest(void);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
MM_SRCS := \
|
||||
mm/heap.c \
|
||||
mm/page_alloc.c
|
||||
|
||||
MM_OBJS := \
|
||||
|
||||
235
mm/heap.c
Normal file
235
mm/heap.c
Normal file
@ -0,0 +1,235 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
#define HEAP_BASE 0xffffff2000000000ull
|
||||
#define HEAP_ALIGNMENT 16u
|
||||
|
||||
struct heap_block {
|
||||
size_t size;
|
||||
uint8_t free;
|
||||
struct heap_block *prev;
|
||||
struct heap_block *next;
|
||||
};
|
||||
|
||||
static struct heap_block *heap_first;
|
||||
static struct heap_block *heap_last;
|
||||
static virt_addr_t heap_end = HEAP_BASE;
|
||||
static int heap_ready;
|
||||
|
||||
static size_t align_up_size(size_t value, size_t alignment)
|
||||
{
|
||||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static virt_addr_t align_up_addr(virt_addr_t value, uint64_t alignment)
|
||||
{
|
||||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static void block_insert_after(
|
||||
struct heap_block *block, struct heap_block *next)
|
||||
{
|
||||
next->prev = block;
|
||||
next->next = block->next;
|
||||
|
||||
if (block->next != 0) {
|
||||
block->next->prev = next;
|
||||
} else {
|
||||
heap_last = next;
|
||||
}
|
||||
|
||||
block->next = next;
|
||||
}
|
||||
|
||||
static void block_merge_next(struct heap_block *block)
|
||||
{
|
||||
struct heap_block *next = block->next;
|
||||
|
||||
if (next == 0 || next->free == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
block->size += sizeof(*next) + next->size;
|
||||
block->next = next->next;
|
||||
|
||||
if (next->next != 0) {
|
||||
next->next->prev = block;
|
||||
} else {
|
||||
heap_last = block;
|
||||
}
|
||||
}
|
||||
|
||||
static void block_split(struct heap_block *block, size_t size)
|
||||
{
|
||||
struct heap_block *next;
|
||||
|
||||
if (block->size < size + sizeof(*next) + HEAP_ALIGNMENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
next = (struct heap_block *)((uint8_t *)(block + 1) + size);
|
||||
next->size = block->size - size - sizeof(*next);
|
||||
next->free = 1;
|
||||
|
||||
block->size = size;
|
||||
block_insert_after(block, next);
|
||||
}
|
||||
|
||||
static int map_heap_range(virt_addr_t start, size_t bytes)
|
||||
{
|
||||
virt_addr_t current;
|
||||
virt_addr_t end = start + bytes;
|
||||
|
||||
for (current = start; current < end; current += PAGE_SIZE) {
|
||||
phys_addr_t page = alloc_page();
|
||||
|
||||
if (page == 0 ||
|
||||
map_page(current,
|
||||
page,
|
||||
PAGE_WRITABLE | PAGE_NO_EXECUTE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int heap_extend(size_t min_size)
|
||||
{
|
||||
size_t bytes =
|
||||
align_up_size(min_size + sizeof(struct heap_block), PAGE_SIZE);
|
||||
struct heap_block *block = (struct heap_block *)(uintptr_t)heap_end;
|
||||
|
||||
if (map_heap_range(heap_end, bytes) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
heap_end += bytes;
|
||||
|
||||
block->size = bytes - sizeof(*block);
|
||||
block->free = 1;
|
||||
block->prev = heap_last;
|
||||
block->next = 0;
|
||||
|
||||
if (heap_last != 0) {
|
||||
heap_last->next = block;
|
||||
} else {
|
||||
heap_first = block;
|
||||
}
|
||||
|
||||
heap_last = block;
|
||||
|
||||
if (block->prev != 0 && block->prev->free != 0) {
|
||||
block_merge_next(block->prev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct heap_block *find_free_block(size_t size)
|
||||
{
|
||||
struct heap_block *block;
|
||||
|
||||
for (block = heap_first; block != 0; block = block->next) {
|
||||
if (block->free != 0 && block->size >= size) {
|
||||
return block;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *kmalloc(size_t size)
|
||||
{
|
||||
struct heap_block *block;
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = align_up_size(size, HEAP_ALIGNMENT);
|
||||
block = find_free_block(size);
|
||||
|
||||
if (block == 0) {
|
||||
if (heap_extend(size) != 0) {
|
||||
return 0;
|
||||
}
|
||||
block = find_free_block(size);
|
||||
}
|
||||
|
||||
if (block == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
block_split(block, size);
|
||||
block->free = 0;
|
||||
|
||||
return block + 1;
|
||||
}
|
||||
|
||||
void kfree(void *ptr)
|
||||
{
|
||||
struct heap_block *block;
|
||||
|
||||
if (ptr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
block = (struct heap_block *)ptr - 1;
|
||||
block->free = 1;
|
||||
|
||||
block_merge_next(block);
|
||||
if (block->prev != 0 && block->prev->free != 0) {
|
||||
block_merge_next(block->prev);
|
||||
}
|
||||
}
|
||||
|
||||
static void heap_selftest(void)
|
||||
{
|
||||
uint64_t *first = kmalloc(sizeof(*first));
|
||||
uint8_t *second = kmalloc(128);
|
||||
uint64_t *third;
|
||||
|
||||
if (first == 0 || second == 0) {
|
||||
panic("kernel heap selftest allocation failed");
|
||||
}
|
||||
|
||||
*first = 0x54494f4c45484541ull;
|
||||
second[0] = 0x31;
|
||||
second[127] = 0x32;
|
||||
|
||||
if (*first != 0x54494f4c45484541ull || second[0] != 0x31 ||
|
||||
second[127] != 0x32) {
|
||||
panic("kernel heap selftest memory corrupted");
|
||||
}
|
||||
|
||||
kfree(first);
|
||||
third = kmalloc(sizeof(*third));
|
||||
if (third == 0) {
|
||||
panic("kernel heap selftest reuse failed");
|
||||
}
|
||||
|
||||
kfree(third);
|
||||
kfree(second);
|
||||
|
||||
early_log_puts("kernel heap selftest ok\n");
|
||||
}
|
||||
|
||||
void heap_init(void)
|
||||
{
|
||||
if (heap_ready != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
heap_end = align_up_addr(heap_end, PAGE_SIZE);
|
||||
if (heap_extend(PAGE_SIZE - sizeof(struct heap_block)) != 0) {
|
||||
panic("kernel heap initialization failed");
|
||||
}
|
||||
|
||||
heap_ready = 1;
|
||||
early_log_puts("kernel heap initialized\n");
|
||||
heap_selftest();
|
||||
}
|
||||
@ -178,4 +178,5 @@ void mm_init(const boot_info_t *boot_info)
|
||||
|
||||
page_allocator_selftest();
|
||||
page_table_selftest();
|
||||
heap_init();
|
||||
}
|
||||
|
||||
@ -20,7 +20,9 @@ check_lines build/debug.log \
|
||||
"physical pages free=" \
|
||||
"physical page allocator selftest ok" \
|
||||
"kernel page table root active" \
|
||||
"page table selftest ok"
|
||||
"page table selftest ok" \
|
||||
"kernel heap initialized" \
|
||||
"kernel heap selftest ok"
|
||||
|
||||
check_lines build/serial.log \
|
||||
"kernel_main entered" \
|
||||
@ -32,6 +34,8 @@ check_lines build/serial.log \
|
||||
"physical pages free=" \
|
||||
"physical page allocator selftest ok" \
|
||||
"kernel page table root active" \
|
||||
"page table selftest ok"
|
||||
"page table selftest ok" \
|
||||
"kernel heap initialized" \
|
||||
"kernel heap selftest ok"
|
||||
|
||||
cat build/debug.log
|
||||
|
||||
@ -12,6 +12,7 @@ check_lines build/debug.log \
|
||||
"traps initialized" \
|
||||
"kernel page table root active" \
|
||||
"page table selftest ok" \
|
||||
"kernel heap selftest ok" \
|
||||
"exception: page fault" \
|
||||
"vector=14 error=0x0000000000000002" \
|
||||
"page fault: address=0xffffff1000000000 error=0x0000000000000002" \
|
||||
|
||||
Loading…
Reference in New Issue
Block a user