feat(mm): add early page table mapping foundation

This commit is contained in:
Microindole 2026-05-09 10:59:10 +08:00
parent 5b69fe1f06
commit e3f34ac1fe
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
30 changed files with 589 additions and 17 deletions

View File

@ -6,6 +6,7 @@
- 正式设计、路线图、架构说明放在 `docs/` - 正式设计、路线图、架构说明放在 `docs/`
- 面向 agent 的任务、会话记录、检查单放在 `docs/agents/` - 面向 agent 的任务、会话记录、检查单放在 `docs/agents/`
- Linux 顶层目录映射见 `docs/agents/linux-layout.md`
## 工作规则 ## 工作规则

View File

@ -21,6 +21,7 @@ endif
include $(ARCH_MAKEFILE) include $(ARCH_MAKEFILE)
include $(ARCH_DIR)/boot/Makefile include $(ARCH_DIR)/boot/Makefile
include $(ARCH_DIR)/kernel/Makefile include $(ARCH_DIR)/kernel/Makefile
include $(ARCH_DIR)/mm/Makefile
include mm/Makefile include mm/Makefile
include kernel/Makefile include kernel/Makefile
@ -29,7 +30,7 @@ include kernel/Makefile
all: $(BOOT_EFI) $(KERNEL_ELF) all: $(BOOT_EFI) $(KERNEL_ELF)
dirs: dirs:
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/kernel $(BUILD_DIR)/mm $(EFI_DIR) mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/arch/mm $(BUILD_DIR)/kernel $(BUILD_DIR)/mm $(EFI_DIR)
$(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs $(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
@ -46,6 +47,10 @@ $(BUILD_DIR)/arch/kernel/%.o: $(ARCH_DIR)/kernel/%.S | dirs
$(BUILD_DIR)/arch/kernel/%.o: $(ARCH_DIR)/kernel/%.c include/tianole/arch.h | dirs $(BUILD_DIR)/arch/kernel/%.o: $(ARCH_DIR)/kernel/%.c include/tianole/arch.h | dirs
$(CC) $(KERNEL_CFLAGS) -c $< -o $@ $(CC) $(KERNEL_CFLAGS) -c $< -o $@
$(BUILD_DIR)/arch/mm/%.o: $(ARCH_DIR)/mm/%.c include/tianole/early_log.h include/tianole/mm.h | dirs
mkdir -p $(@D)
$(CC) $(KERNEL_CFLAGS) -c $< -o $@
$(BUILD_DIR)/kernel/%.o: kernel/%.c include/tianole/boot_info.h include/tianole/kernel_init.h include/tianole/arch.h include/tianole/early_log.h include/tianole/mm.h | dirs $(BUILD_DIR)/kernel/%.o: kernel/%.c include/tianole/boot_info.h include/tianole/kernel_init.h include/tianole/arch.h include/tianole/early_log.h include/tianole/mm.h | dirs
mkdir -p $(@D) mkdir -p $(@D)
$(CC) $(KERNEL_CFLAGS) -c $< -o $@ $(CC) $(KERNEL_CFLAGS) -c $< -o $@

View File

@ -1,15 +1,24 @@
# Tianole # Tianole
Tianole 是一个面向 `UEFI x86_64` 机器的自制操作系统项目 Tianole 是一个学习型自制操作系统项目,当前面向 `UEFI x86_64` 机器。
当前目标不是一次性做“大而全”的系统,而是按 Linux 风格的分层思路,逐步把启动链、内核基础设施、用户态和文件系统建立起来,最终能在真机上安全启动,并逐步具备运行本地 `git` 的能力。 这个项目的主要目的有三点:
- 通过亲手实现一个简化但不玩具化的 OS理解操作系统核心机制。
- 通过参考 Linux 的目录、分层和工程纪律,建立阅读 Linux 源码的上下文。
- 长期目标是在真机上安全启动,并逐步具备运行本地 `git` 的能力。
Tianole 不宣称自己是成熟 Linux 替代品。当前阶段更准确的定位是:用一个可运行、可验证、可持续演进的代码库,系统性学习 Linux 式内核工程。
## 当前状态 ## 当前状态
- 已跑通 `QEMU + OVMF` 下的最小 `UEFI` 启动链 - 已跑通 `QEMU + OVMF` 下的最小 `UEFI` 启动链。
- 已拆分为 `bootloader + kernel` - 已拆分为 `bootloader + kernel`,并能加载独立 `kernel.elf`
- `bootloader` 已能加载独立 `kernel.elf` - 已在进入 kernel 前调用 `ExitBootServices`
- `kernel_main()` 已能实际执行 - 已建立 early log 基础QEMU debug port 和 COM1 串口。
- 已建立 CPU exception 基础GDT/TSS/IDT、trap frame、invalid opcode -> panic。
- 已建立物理内存基础:从 boot memory map 初始化最小物理页 allocator。
- 还没有完成完整中断、页表、内核堆、调度、进程、VFS、用户态和 shell。
## 当前目录 ## 当前目录
@ -18,18 +27,44 @@ tianole/
AGENTS.md AGENTS.md
Makefile Makefile
arch/ arch/
block/
crypto/
docs/ docs/
drivers/
fs/
include/ include/
init/
ipc/
kernel/ kernel/
lib/
mm/ mm/
net/
scripts/ scripts/
security/
sound/
tools/
usr/
virt/
``` ```
当前还没有直接复制 Linux 的完整目录树,但已经开始按接近 Linux 的方式分层: 当前按接近 Linux 的方式分层,但不机械复制 Linux 的所有历史目录
- `arch/`:架构相关代码 - `arch/`:架构相关代码
- `block/`:通用块层
- `crypto/`:通用加密和哈希算法
- `drivers/`:设备驱动
- `fs/`VFS 和文件系统
- `init/`:初始化流程
- `ipc/`:进程间通信
- `kernel/`:通用内核主体逐步放这里 - `kernel/`:通用内核主体逐步放这里
- `lib/`:通用基础库
- `mm/`:架构无关内存管理 - `mm/`:架构无关内存管理
- `net/`:网络协议栈
- `security/`:安全模型
- `sound/`:音频子系统
- `tools/`:宿主机侧开发工具
- `usr/`:初始用户空间
- `virt/`:虚拟化支持
- `include/tianole/`:项目自有共享接口 - `include/tianole/`:项目自有共享接口
- `docs/`:正式设计和路线图 - `docs/`:正式设计和路线图
@ -79,4 +114,5 @@ GitHub Actions 会在 push 和 pull request 时运行同一个检查入口。具
- 路线图:[docs/roadmap.md](//wsl.localhost/Ubuntu/home/indole/tianole/docs/roadmap.md) - 路线图:[docs/roadmap.md](//wsl.localhost/Ubuntu/home/indole/tianole/docs/roadmap.md)
- 本机环境与真机启动约束:[docs/host-machine.md](//wsl.localhost/Ubuntu/home/indole/tianole/docs/host-machine.md) - 本机环境与真机启动约束:[docs/host-machine.md](//wsl.localhost/Ubuntu/home/indole/tianole/docs/host-machine.md)
- Linux 顶层目录映射:[docs/agents/linux-layout.md](//wsl.localhost/Ubuntu/home/indole/tianole/docs/agents/linux-layout.md)
- agent 入口:[AGENTS.md](//wsl.localhost/Ubuntu/home/indole/tianole/AGENTS.md) - agent 入口:[AGENTS.md](//wsl.localhost/Ubuntu/home/indole/tianole/AGENTS.md)

View File

@ -65,6 +65,14 @@ static uint64_t interrupted_rsp(const struct trap_frame *frame)
return (uint64_t)(uintptr_t)&frame->rflags + sizeof(frame->rflags); return (uint64_t)(uintptr_t)&frame->rflags + sizeof(frame->rflags);
} }
static uint64_t read_cr2(void)
{
uint64_t cr2;
__asm__ volatile("movq %%cr2, %0" : "=r"(cr2));
return cr2;
}
void arch_traps_init(void) void arch_traps_init(void)
{ {
gdt_init(); gdt_init();
@ -97,5 +105,11 @@ void trap_dispatch(struct trap_frame *frame)
log_u64_hex(frame->rflags); log_u64_hex(frame->rflags);
early_log_puts("\n"); early_log_puts("\n");
if (vector == 14) {
early_log_puts("fault_address=");
log_u64_hex(read_cr2());
early_log_puts("\n");
}
panic("unhandled CPU exception"); panic("unhandled CPU exception");
} }

5
arch/x86/mm/Makefile Normal file
View File

@ -0,0 +1,5 @@
ARCH_MM_SRCS := \
$(ARCH_DIR)/mm/page_table.c
ARCH_MM_OBJS := \
$(patsubst $(ARCH_DIR)/mm/%.c,$(BUILD_DIR)/arch/mm/%.o,$(ARCH_MM_SRCS))

305
arch/x86/mm/page_table.c Normal file
View File

@ -0,0 +1,305 @@
#include <stdint.h>
#include <tianole/arch.h>
#include <tianole/early_log.h>
#include <tianole/mm.h>
#define ENTRY_COUNT 512
#define PAGE_MASK 0x000ffffffffff000ull
#define PAGE_SIZE_FLAG (1ull << 7)
#define MAX_RESERVED_TABLE_PAGES 4096
#define TEST_VIRTUAL_PAGE 0xffffff0000000000ull
static phys_addr_t reserved_table_pages[MAX_RESERVED_TABLE_PAGES];
static uint64_t reserved_table_page_count;
static int reserved_table_pages_ready;
static uint64_t kernel_pml4[ENTRY_COUNT] __attribute__((aligned(PAGE_SIZE)));
static int page_tables_ready;
static uint64_t *active_pml4(void)
{
uint64_t cr3;
__asm__ volatile("movq %%cr3, %0" : "=r"(cr3));
return (uint64_t *)(uintptr_t)(cr3 & PAGE_MASK);
}
static void flush_tlb_page(virt_addr_t virt)
{
__asm__ volatile("invlpg (%0)" : : "r"((uintptr_t)virt) : "memory");
}
static void load_cr3(phys_addr_t root)
{
__asm__ volatile("movq %0, %%cr3" : : "r"(root) : "memory");
}
static uint64_t table_index(virt_addr_t virt, unsigned int shift)
{
return (virt >> shift) & 0x1ffu;
}
static void clear_page(phys_addr_t page)
{
uint64_t *words = (uint64_t *)(uintptr_t)page;
uint64_t index;
for (index = 0; index < PAGE_SIZE / sizeof(uint64_t); index++) {
words[index] = 0;
}
}
static uint64_t *entry_table(uint64_t entry)
{
return (uint64_t *)(uintptr_t)(entry & PAGE_MASK);
}
static void add_reserved_table_page(phys_addr_t page)
{
uint64_t index;
for (index = 0; index < reserved_table_page_count; index++) {
if (reserved_table_pages[index] == page) {
return;
}
}
if (reserved_table_page_count >= MAX_RESERVED_TABLE_PAGES) {
panic("too many active page table pages");
}
reserved_table_pages[reserved_table_page_count++] = page;
}
static void collect_active_page_tables(void)
{
uint64_t pml4_index;
uint64_t pdpt_index;
uint64_t pd_index;
uint64_t *pml4 = active_pml4();
add_reserved_table_page((phys_addr_t)(uintptr_t)pml4);
for (pml4_index = 0; pml4_index < ENTRY_COUNT; pml4_index++) {
uint64_t *pdpt;
if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
continue;
}
add_reserved_table_page(pml4[pml4_index] & PAGE_MASK);
pdpt = entry_table(pml4[pml4_index]);
for (pdpt_index = 0; pdpt_index < ENTRY_COUNT; pdpt_index++) {
uint64_t *pd;
if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0 ||
(pdpt[pdpt_index] & PAGE_SIZE_FLAG) != 0) {
continue;
}
add_reserved_table_page(pdpt[pdpt_index] & PAGE_MASK);
pd = entry_table(pdpt[pdpt_index]);
for (pd_index = 0; pd_index < ENTRY_COUNT; pd_index++) {
if ((pd[pd_index] & PAGE_PRESENT) == 0 ||
(pd[pd_index] & PAGE_SIZE_FLAG) != 0) {
continue;
}
add_reserved_table_page(
pd[pd_index] & PAGE_MASK);
}
}
}
}
int arch_page_table_uses_page(uint64_t page)
{
uint64_t index;
if ((page & (PAGE_SIZE - 1)) != 0) {
return 0;
}
if (reserved_table_pages_ready == 0) {
collect_active_page_tables();
reserved_table_pages_ready = 1;
}
for (index = 0; index < reserved_table_page_count; index++) {
if (reserved_table_pages[index] == page) {
return 1;
}
}
return 0;
}
static uint64_t make_table_entry(phys_addr_t table)
{
return table | PAGE_PRESENT | PAGE_WRITABLE;
}
static void page_tables_init(void)
{
uint64_t index;
uint64_t *firmware_pml4;
if (page_tables_ready != 0) {
return;
}
firmware_pml4 = active_pml4();
for (index = 0; index < ENTRY_COUNT; index++) {
kernel_pml4[index] = firmware_pml4[index];
}
load_cr3((phys_addr_t)(uintptr_t)kernel_pml4);
page_tables_ready = 1;
early_log_puts("kernel page table root active\n");
}
static int ensure_next_table(uint64_t *table, uint64_t index, uint64_t **next)
{
phys_addr_t page;
if ((table[index] & PAGE_PRESENT) != 0) {
*next = entry_table(table[index]);
return 0;
}
page = alloc_page();
if (page == 0) {
return -1;
}
clear_page(page);
table[index] = make_table_entry(page);
*next = (uint64_t *)(uintptr_t)page;
return 0;
}
static int page_entry(virt_addr_t virt, int create, uint64_t **entry)
{
uint64_t *pml4 = active_pml4();
uint64_t *pdpt;
uint64_t *pd;
uint64_t *pt;
uint64_t pml4_index = table_index(virt, 39);
uint64_t pdpt_index = table_index(virt, 30);
uint64_t pd_index = table_index(virt, 21);
uint64_t pt_index = table_index(virt, 12);
if (create != 0) {
if (ensure_next_table(pml4, pml4_index, &pdpt) != 0 ||
ensure_next_table(pdpt, pdpt_index, &pd) != 0 ||
ensure_next_table(pd, pd_index, &pt) != 0) {
return -1;
}
} else {
if ((pml4[pml4_index] & PAGE_PRESENT) == 0) {
return -1;
}
pdpt = entry_table(pml4[pml4_index]);
if ((pdpt[pdpt_index] & PAGE_PRESENT) == 0) {
return -1;
}
pd = entry_table(pdpt[pdpt_index]);
if ((pd[pd_index] & PAGE_PRESENT) == 0) {
return -1;
}
pt = entry_table(pd[pd_index]);
}
*entry = &pt[pt_index];
return 0;
}
int map_page(virt_addr_t virt, phys_addr_t phys, uint64_t flags)
{
uint64_t *entry;
if ((virt & (PAGE_SIZE - 1)) != 0 || (phys & (PAGE_SIZE - 1)) != 0) {
return -1;
}
if (page_entry(virt, 1, &entry) != 0) {
return -1;
}
if ((*entry & PAGE_PRESENT) != 0) {
return -1;
}
*entry = (phys & PAGE_MASK) | flags | PAGE_PRESENT;
flush_tlb_page(virt);
return 0;
}
int unmap_page(virt_addr_t virt)
{
uint64_t *entry;
if ((virt & (PAGE_SIZE - 1)) != 0) {
return -1;
}
if (page_entry(virt, 0, &entry) != 0 || (*entry & PAGE_PRESENT) == 0) {
return -1;
}
*entry = 0;
flush_tlb_page(virt);
return 0;
}
int virt_to_phys(virt_addr_t virt, phys_addr_t *phys)
{
uint64_t *entry;
if (phys == 0 || page_entry(virt, 0, &entry) != 0 ||
(*entry & PAGE_PRESENT) == 0) {
return -1;
}
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 1));
return 0;
}
void page_table_selftest(void)
{
phys_addr_t page = alloc_page();
phys_addr_t resolved;
volatile uint64_t *mapped =
(volatile uint64_t *)(uintptr_t)TEST_VIRTUAL_PAGE;
if (page == 0) {
panic("page table selftest allocation failed");
}
page_tables_init();
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
panic("page table selftest map failed");
}
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
resolved != page) {
panic("page table selftest resolve failed");
}
*mapped = 0x54494f4c45504d4dull;
if (*mapped != 0x54494f4c45504d4dull) {
panic("page table selftest access failed");
}
if (unmap_page(TEST_VIRTUAL_PAGE) != 0) {
panic("page table selftest unmap failed");
}
free_page(page);
early_log_puts("page table selftest ok\n");
}

9
block/README.md Normal file
View File

@ -0,0 +1,9 @@
# block
块设备层目录。
后续用于放置块设备抽象、请求队列、缓存写回入口,以及连接 `drivers/`
`fs/` 的通用块 I/O 层。
当前阶段暂不放代码。

8
crypto/README.md Normal file
View File

@ -0,0 +1,8 @@
# crypto
通用加密算法和哈希算法目录。
后续用于支持文件校验、签名、随机数、网络协议和用户态工具需要的基础算法。
当前阶段暂不放代码。

View File

@ -7,6 +7,7 @@
建议内容: 建议内容:
- `code-style.md`agent 修改代码时必须遵守的代码组织和风格规则。 - `code-style.md`agent 修改代码时必须遵守的代码组织和风格规则。
- `linux-layout.md`Tianole 和 Linux 顶层目录的映射与取舍。
- `session-notes/`:实现日志和阶段结论。 - `session-notes/`:实现日志和阶段结论。
- `tasks/`:适合 agent 执行的边界清晰任务,按路线阶段拆分。 - `tasks/`:适合 agent 执行的边界清晰任务,按路线阶段拆分。
- `prompts/`:可复用提示词和约束。 - `prompts/`:可复用提示词和约束。
@ -17,6 +18,7 @@
当前任务入口: 当前任务入口:
- `code-style.md`:代码风格和文件组织规则。 - `code-style.md`:代码风格和文件组织规则。
- `linux-layout.md`Linux 顶层目录映射。
- `tasks/README.md`agent 任务索引。 - `tasks/README.md`agent 任务索引。
- `tasks/02-cpu-interrupts.md`下一阶段GDT/TSS/IDT、异常入口和 trap frame。 - `tasks/02-cpu-interrupts.md`下一阶段GDT/TSS/IDT、异常入口和 trap frame。
- `../roadmap.md`:总路线和阶段依赖。 - `../roadmap.md`:总路线和阶段依赖。

View File

@ -26,6 +26,7 @@
- 按职责、生命周期、所有权和调用边界拆分。 - 按职责、生命周期、所有权和调用边界拆分。
- `kernel/main.c`、`boot/main.c` 这类入口文件应保持短,只串联阶段。 - `kernel/main.c`、`boot/main.c` 这类入口文件应保持短,只串联阶段。
- `mm/` 是顶层内存管理子系统目录,不放在 `kernel/mm/` 下。 - `mm/` 是顶层内存管理子系统目录,不放在 `kernel/mm/` 下。
- 顶层目录参考 `docs/agents/linux-layout.md`;可以预留目录,但不要放临时代码。
## 命名 ## 命名

View File

@ -0,0 +1,41 @@
# Linux 顶层目录映射
这个文档记录 Tianole 参考 Linux 顶层目录时的取舍。目标是学习 Linux 的工程分层,
而不是机械复制 Linux 的全部历史目录。
## 已采用或已预留
- `arch/`:对应 Linux `arch/`,放架构相关代码。
- `block/`:对应 Linux `block/`,后续放通用块层。
- `crypto/`:对应 Linux `crypto/`,后续放通用加密和哈希算法。
- `docs/`:对应 Linux `Documentation/`Tianole 使用小写 `docs/`
- `drivers/`:对应 Linux `drivers/`,后续放设备驱动和总线驱动。
- `fs/`:对应 Linux `fs/`,后续放 VFS 和具体文件系统。
- `include/`:对应 Linux `include/`,放公共头文件。
- `init/`:对应 Linux `init/`,后续放初始化流程。
- `ipc/`:对应 Linux `ipc/`,后续放 IPC、signal、pipe、futex 等能力。
- `kernel/`:对应 Linux `kernel/`,放调度、进程和核心内核逻辑。
- `lib/`:对应 Linux `lib/`,后续放通用基础库。
- `mm/`:对应 Linux `mm/`,放内存管理。
- `net/`:对应 Linux `net/`,后续放网络协议栈。
- `scripts/`:对应 Linux `scripts/`,放构建、检查和维护脚本。
- `security/`:对应 Linux `security/`,后续放权限和安全策略。
- `sound/`:对应 Linux `sound/`,后续放音频子系统。
- `tools/`:对应 Linux `tools/`,后续放宿主机侧开发工具。
- `usr/`:对应 Linux `usr/`,后续放初始用户空间。
- `virt/`:对应 Linux `virt/`,后续放虚拟化相关支持。
## 暂不采用
- `certs/`:证书和签名链路还不是当前阶段目标,后续可按安全启动需求加入。
- `io_uring/`:这是 Linux 的特定异步 I/O 子系统Tianole 早期不引入。
- `rust/`:当前内核实现语言是 C/ASM暂不引入 Rust 子树。
- `samples/`:示例代码暂不放顶层,后续可按教学或测试需要决定。
## 目录使用原则
- 可以预留目录,但不要在目录中放临时代码。
- 只有当一个目录对应的子系统真正开始实现时,才添加 `Makefile` 和源文件。
- 目录 README 用来说明长期职责,不替代正式设计文档。
- 如果后续发现目录边界不合理,优先更新本文件和 `docs/agents/code-style.md`

View File

@ -52,14 +52,16 @@
- 已建立最小物理页 free-list allocator。 - 已建立最小物理页 free-list allocator。
- 已提供 `alloc_page()``free_page()` - 已提供 `alloc_page()``free_page()`
- 已加入物理页分配/释放 selftest。 - 已加入物理页分配/释放 selftest。
- `scripts/check.sh` 已验证 `physical pages free=` 和 selftest 日志。 - 已切换到内核自有 PML4不再直接修改固件页表。
- 已提供最小 `map_page()`、`unmap_page()` 和 `virt_to_phys()` 接口。
- 已加入页表 map/unmap/query selftest。
- `scripts/check.sh` 已验证 `physical pages free=`、物理页 selftest、页表根切换和页表 selftest 日志。
后续扩展: 后续扩展:
- 长期内存区域模型,不直接依赖 UEFI memory type。 - 长期内存区域模型,不直接依赖 UEFI memory type。
- page metadata。 - page metadata。
- buddy allocator。 - buddy allocator。
- 页表 map/unmap。
- page fault 专门处理。 - page fault 专门处理。
- 最小内核堆。 - 最小内核堆。
- slab/slub 或等价小对象缓存。 - slab/slub 或等价小对象缓存。
@ -68,7 +70,9 @@
- 正常启动日志包含 `physical pages free=` - 正常启动日志包含 `physical pages free=`
- 正常启动日志包含 `physical page allocator selftest ok` - 正常启动日志包含 `physical page allocator selftest ok`
- 正常启动日志包含 `kernel page table root active`
- 正常启动日志包含 `page table selftest ok`
下一阶段: 下一阶段:
- 继续在 `03-memory.md` 内推进页表管理和内核堆。 - 继续在 `03-memory.md` 内推进 page fault 专门处理和最小内核堆。

View File

@ -21,6 +21,23 @@
9. `09-driver-expansion.md`PCI/ACPI/更完整设备驱动,音频等后期设备。 9. `09-driver-expansion.md`PCI/ACPI/更完整设备驱动,音频等后期设备。
10. `10-real-machine.md`:真机启动、安全验证、硬件差异处理。 10. `10-real-machine.md`:真机启动、安全验证、硬件差异处理。
## 当前进度
| 阶段 | 状态 | 说明 |
| --- | --- | --- |
| `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 策略和内核堆未完成。 |
| `04-time-scheduler.md` | 未开始 | 需要 timer、kernel thread、调度和等待队列。 |
| `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 |
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
| `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 |
| `08-shell-tools.md` | 未开始 | 需要 init、shell、最小用户态工具和 libc 基础。 |
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
当前最合适的下一步仍在 `03-memory.md` 内:先补 page fault 专门诊断,再接最小内核堆。
## Linux 级能力缺口路由 ## Linux 级能力缺口路由
这些能力不一定马上实现,但必须在路线中占位,避免后续走偏。 这些能力不一定马上实现,但必须在路线中占位,避免后续走偏。

View File

@ -19,6 +19,8 @@
- trap 验证invalid opcode 已能进入 trap dispatch 并最终进入 panic。 - trap 验证invalid opcode 已能进入 trap dispatch 并最终进入 panic。
- 物理内存基础:已从 boot memory map 建立最小物理页 allocator。 - 物理内存基础:已从 boot memory map 建立最小物理页 allocator。
- 物理页验证:`alloc_page/free_page` selftest 已接入启动检查。 - 物理页验证:`alloc_page/free_page` selftest 已接入启动检查。
- 页表基础:已切换到内核自有 PML4并提供最小 `map_page/unmap_page/virt_to_phys`
- 页表验证:页表 map/unmap/query selftest 已接入启动检查。
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。 - 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径GitHub Actions 已接入。 - `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径GitHub Actions 已接入。
- `.clang-format` 已用于强制当前 C 代码风格。 - `.clang-format` 已用于强制当前 C 代码风格。
@ -27,7 +29,7 @@
- 完整日志体系printk、log level、ring buffer、oops、符号化、crash dump。 - 完整日志体系printk、log level、ring buffer、oops、符号化、crash dump。
- 完整中断体系PIC/APIC、外部 IRQ、timer interrupt。 - 完整中断体系PIC/APIC、外部 IRQ、timer interrupt。
- 内存管理扩展:长期内存区域模型、页表、page fault 策略、内核堆。 - 内存管理扩展长期内存区域模型、page fault 策略、内核堆。
- 后续主线:调度、进程、文件系统、用户态和 shell。 - 后续主线:调度、进程、文件系统、用户态和 shell。
## 当前下一步 ## 当前下一步
@ -38,7 +40,6 @@
目标: 目标:
- 建立内核页表管理。
- 建立最小内核堆。 - 建立最小内核堆。
- 让 page fault 能进入现有异常路径并输出有效诊断。 - 让 page fault 能进入现有异常路径并输出有效诊断。

9
drivers/README.md Normal file
View File

@ -0,0 +1,9 @@
# drivers
设备驱动目录。
后续用于放置总线、平台设备和具体硬件驱动,例如 PCI、ACPI、串口、输入设备、
块设备、网络设备和显示设备。
当前 early serial log 仍属于早期调试 backend不迁入正式驱动模型。

8
fs/README.md Normal file
View File

@ -0,0 +1,8 @@
# fs
文件系统目录。
后续用于放置 VFS、路径解析、具体文件系统和虚拟文件系统接口。
当前阶段暂不放代码。

View File

@ -1,9 +1,12 @@
#ifndef TIANOLE_ARCH_H #ifndef TIANOLE_ARCH_H
#define TIANOLE_ARCH_H #define TIANOLE_ARCH_H
#include <stdint.h>
void arch_early_log_init(void); void arch_early_log_init(void);
void arch_early_log_putc(char ch); void arch_early_log_putc(char ch);
void arch_halt_forever(void) __attribute__((noreturn)); void arch_halt_forever(void) __attribute__((noreturn));
int arch_page_table_uses_page(uint64_t page);
void arch_traps_init(void); void arch_traps_init(void);
#endif #endif

View File

@ -8,9 +8,18 @@
#define PAGE_SIZE 4096u #define PAGE_SIZE 4096u
typedef uint64_t phys_addr_t; typedef uint64_t phys_addr_t;
typedef uint64_t virt_addr_t;
#define PAGE_PRESENT (1ull << 0)
#define PAGE_WRITABLE (1ull << 1)
#define PAGE_NO_EXECUTE (1ull << 63)
void mm_init(const boot_info_t *boot_info); void mm_init(const boot_info_t *boot_info);
phys_addr_t alloc_page(void); phys_addr_t alloc_page(void);
void free_page(phys_addr_t page); void free_page(phys_addr_t page);
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 page_table_selftest(void);
#endif #endif

8
init/README.md Normal file
View File

@ -0,0 +1,8 @@
# init
系统初始化目录。
后续用于放置内核初始化流程和第一个用户态 `init` 相关的入口组织。
当前 `kernel/main.c` 仍只负责早期启动串联。

8
ipc/README.md Normal file
View File

@ -0,0 +1,8 @@
# ipc
进程间通信目录。
后续用于放置信号、pipe、event、futex、共享内存等 IPC 基础设施。
当前阶段暂不放代码。

View File

@ -5,5 +5,6 @@ KERNEL_SRCS := \
KERNEL_OBJS := \ KERNEL_OBJS := \
$(ARCH_KERNEL_OBJS) \ $(ARCH_KERNEL_OBJS) \
$(ARCH_MM_OBJS) \
$(patsubst kernel/%.c,$(BUILD_DIR)/kernel/%.o,$(KERNEL_SRCS)) \ $(patsubst kernel/%.c,$(BUILD_DIR)/kernel/%.o,$(KERNEL_SRCS)) \
$(MM_OBJS) $(MM_OBJS)

8
lib/README.md Normal file
View File

@ -0,0 +1,8 @@
# lib
通用库代码目录。
后续用于放置字符串、位图、链表、红黑树、校验和等可被多个子系统复用的基础设施。
当前阶段暂不放代码。

View File

@ -1,5 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <tianole/arch.h>
#include <tianole/early_log.h> #include <tianole/early_log.h>
#include <tianole/mm.h> #include <tianole/mm.h>
@ -58,6 +59,10 @@ static int page_is_reserved(const boot_info_t *boot_info, uint64_t page)
return 1; return 1;
} }
if (arch_page_table_uses_page(page)) {
return 1;
}
return 0; return 0;
} }
@ -74,13 +79,24 @@ static void add_conventional_range(
const boot_info_t *boot_info, uint64_t start, uint64_t pages) const boot_info_t *boot_info, uint64_t start, uint64_t pages)
{ {
uint64_t page; uint64_t page;
uint64_t first = align_up(start, PAGE_SIZE);
uint64_t end = start + pages * PAGE_SIZE; uint64_t end = start + pages * PAGE_SIZE;
uint64_t last;
for (page = align_up(start, PAGE_SIZE); page + PAGE_SIZE <= end; if (first + PAGE_SIZE > end) {
page += PAGE_SIZE) { return;
}
last = align_down(end - PAGE_SIZE, PAGE_SIZE);
for (page = last;; page -= PAGE_SIZE) {
if (!page_is_reserved(boot_info, page)) { if (!page_is_reserved(boot_info, page)) {
add_free_page(page); add_free_page(page);
} }
if (page == first) {
break;
}
} }
} }
@ -161,4 +177,5 @@ void mm_init(const boot_info_t *boot_info)
early_log_puts("\n"); early_log_puts("\n");
page_allocator_selftest(); page_allocator_selftest();
page_table_selftest();
} }

8
net/README.md Normal file
View File

@ -0,0 +1,8 @@
# net
网络协议栈目录。
后续用于放置 socket API、协议栈、路由和网络缓冲区等网络核心代码。
当前本地 `git` 目标不要求网络,暂不放代码。

View File

@ -18,7 +18,9 @@ check_lines build/debug.log \
"memory map descriptors=" \ "memory map descriptors=" \
"conventional memory pages=" \ "conventional memory pages=" \
"physical pages free=" \ "physical pages free=" \
"physical page allocator selftest ok" "physical page allocator selftest ok" \
"kernel page table root active" \
"page table selftest ok"
check_lines build/serial.log \ check_lines build/serial.log \
"kernel_main entered" \ "kernel_main entered" \
@ -28,6 +30,8 @@ check_lines build/serial.log \
"memory map descriptors=" \ "memory map descriptors=" \
"conventional memory pages=" \ "conventional memory pages=" \
"physical pages free=" \ "physical pages free=" \
"physical page allocator selftest ok" "physical page allocator selftest ok" \
"kernel page table root active" \
"page table selftest ok"
cat build/debug.log cat build/debug.log

8
security/README.md Normal file
View File

@ -0,0 +1,8 @@
# security
安全模型目录。
后续用于放置权限、capability、隔离模型和安全策略扩展点。
当前阶段暂不放代码。

8
sound/README.md Normal file
View File

@ -0,0 +1,8 @@
# sound
音频子系统目录。
后续用于放置音频核心、设备模型和具体声卡驱动。
音频不是早期目标,当前阶段暂不放代码。

8
tools/README.md Normal file
View File

@ -0,0 +1,8 @@
# tools
宿主机侧开发工具目录。
后续用于放置需要编译或较复杂的开发辅助工具。简单脚本继续放在 `scripts/`
当前阶段暂不放代码。

8
usr/README.md Normal file
View File

@ -0,0 +1,8 @@
# usr
用户空间构建和初始用户程序目录。
后续用于放置 initramfs 内容、最小用户态程序、shell 和基础工具。
当前阶段暂不放代码。

8
virt/README.md Normal file
View File

@ -0,0 +1,8 @@
# virt
虚拟化支持目录。
后续如果需要支持虚拟化相关接口、虚拟设备框架或 guest/host 特性,可以放在这里。
当前阶段暂不放代码。