diff --git a/AGENTS.md b/AGENTS.md index 7720574..ff5fdf2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,6 +6,7 @@ - 正式设计、路线图、架构说明放在 `docs/` - 面向 agent 的任务、会话记录、检查单放在 `docs/agents/` +- Linux 顶层目录映射见 `docs/agents/linux-layout.md` ## 工作规则 diff --git a/Makefile b/Makefile index 078eeb1..a26e56d 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ endif include $(ARCH_MAKEFILE) include $(ARCH_DIR)/boot/Makefile include $(ARCH_DIR)/kernel/Makefile +include $(ARCH_DIR)/mm/Makefile include mm/Makefile include kernel/Makefile @@ -29,7 +30,7 @@ include kernel/Makefile all: $(BOOT_EFI) $(KERNEL_ELF) 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 $(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 $(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 mkdir -p $(@D) $(CC) $(KERNEL_CFLAGS) -c $< -o $@ diff --git a/README.md b/README.md index 3e96814..c2216eb 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,24 @@ # Tianole -Tianole 是一个面向 `UEFI x86_64` 机器的自制操作系统项目。 +Tianole 是一个学习型自制操作系统项目,当前面向 `UEFI x86_64` 机器。 -当前目标不是一次性做“大而全”的系统,而是按 Linux 风格的分层思路,逐步把启动链、内核基础设施、用户态和文件系统建立起来,最终能在真机上安全启动,并逐步具备运行本地 `git` 的能力。 +这个项目的主要目的有三点: + +- 通过亲手实现一个简化但不玩具化的 OS,理解操作系统核心机制。 +- 通过参考 Linux 的目录、分层和工程纪律,建立阅读 Linux 源码的上下文。 +- 长期目标是在真机上安全启动,并逐步具备运行本地 `git` 的能力。 + +Tianole 不宣称自己是成熟 Linux 替代品。当前阶段更准确的定位是:用一个可运行、可验证、可持续演进的代码库,系统性学习 Linux 式内核工程。 ## 当前状态 -- 已跑通 `QEMU + OVMF` 下的最小 `UEFI` 启动链 -- 已拆分为 `bootloader + kernel` -- `bootloader` 已能加载独立 `kernel.elf` -- `kernel_main()` 已能实际执行 +- 已跑通 `QEMU + OVMF` 下的最小 `UEFI` 启动链。 +- 已拆分为 `bootloader + kernel`,并能加载独立 `kernel.elf`。 +- 已在进入 kernel 前调用 `ExitBootServices`。 +- 已建立 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 Makefile arch/ + block/ + crypto/ docs/ + drivers/ + fs/ include/ + init/ + ipc/ kernel/ + lib/ mm/ + net/ scripts/ + security/ + sound/ + tools/ + usr/ + virt/ ``` -当前还没有直接复制 Linux 的完整目录树,但已经开始按接近 Linux 的方式分层: +当前按接近 Linux 的方式分层,但不机械复制 Linux 的所有历史目录: - `arch/`:架构相关代码 +- `block/`:通用块层 +- `crypto/`:通用加密和哈希算法 +- `drivers/`:设备驱动 +- `fs/`:VFS 和文件系统 +- `init/`:初始化流程 +- `ipc/`:进程间通信 - `kernel/`:通用内核主体逐步放这里 +- `lib/`:通用基础库 - `mm/`:架构无关内存管理 +- `net/`:网络协议栈 +- `security/`:安全模型 +- `sound/`:音频子系统 +- `tools/`:宿主机侧开发工具 +- `usr/`:初始用户空间 +- `virt/`:虚拟化支持 - `include/tianole/`:项目自有共享接口 - `docs/`:正式设计和路线图 @@ -79,4 +114,5 @@ GitHub Actions 会在 push 和 pull request 时运行同一个检查入口。具 - 路线图:[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) +- 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) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 6d200b6..bfa40a3 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -65,6 +65,14 @@ static uint64_t interrupted_rsp(const struct trap_frame *frame) 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) { gdt_init(); @@ -97,5 +105,11 @@ void trap_dispatch(struct trap_frame *frame) log_u64_hex(frame->rflags); 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"); } diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile new file mode 100644 index 0000000..1d614b0 --- /dev/null +++ b/arch/x86/mm/Makefile @@ -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)) diff --git a/arch/x86/mm/page_table.c b/arch/x86/mm/page_table.c new file mode 100644 index 0000000..f265862 --- /dev/null +++ b/arch/x86/mm/page_table.c @@ -0,0 +1,305 @@ +#include + +#include +#include +#include + +#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"); +} diff --git a/block/README.md b/block/README.md new file mode 100644 index 0000000..9d23d60 --- /dev/null +++ b/block/README.md @@ -0,0 +1,9 @@ +# block + +块设备层目录。 + +后续用于放置块设备抽象、请求队列、缓存写回入口,以及连接 `drivers/` 和 +`fs/` 的通用块 I/O 层。 + +当前阶段暂不放代码。 + diff --git a/crypto/README.md b/crypto/README.md new file mode 100644 index 0000000..8008acf --- /dev/null +++ b/crypto/README.md @@ -0,0 +1,8 @@ +# crypto + +通用加密算法和哈希算法目录。 + +后续用于支持文件校验、签名、随机数、网络协议和用户态工具需要的基础算法。 + +当前阶段暂不放代码。 + diff --git a/docs/agents/README.md b/docs/agents/README.md index 359fab2..b71be7f 100644 --- a/docs/agents/README.md +++ b/docs/agents/README.md @@ -7,6 +7,7 @@ 建议内容: - `code-style.md`:agent 修改代码时必须遵守的代码组织和风格规则。 +- `linux-layout.md`:Tianole 和 Linux 顶层目录的映射与取舍。 - `session-notes/`:实现日志和阶段结论。 - `tasks/`:适合 agent 执行的边界清晰任务,按路线阶段拆分。 - `prompts/`:可复用提示词和约束。 @@ -17,6 +18,7 @@ 当前任务入口: - `code-style.md`:代码风格和文件组织规则。 +- `linux-layout.md`:Linux 顶层目录映射。 - `tasks/README.md`:agent 任务索引。 - `tasks/02-cpu-interrupts.md`:下一阶段,GDT/TSS/IDT、异常入口和 trap frame。 - `../roadmap.md`:总路线和阶段依赖。 diff --git a/docs/agents/code-style.md b/docs/agents/code-style.md index 8754023..807af78 100644 --- a/docs/agents/code-style.md +++ b/docs/agents/code-style.md @@ -26,6 +26,7 @@ - 按职责、生命周期、所有权和调用边界拆分。 - `kernel/main.c`、`boot/main.c` 这类入口文件应保持短,只串联阶段。 - `mm/` 是顶层内存管理子系统目录,不放在 `kernel/mm/` 下。 +- 顶层目录参考 `docs/agents/linux-layout.md`;可以预留目录,但不要放临时代码。 ## 命名 diff --git a/docs/agents/linux-layout.md b/docs/agents/linux-layout.md new file mode 100644 index 0000000..83c26be --- /dev/null +++ b/docs/agents/linux-layout.md @@ -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`。 + diff --git a/docs/agents/tasks/03-memory.md b/docs/agents/tasks/03-memory.md index 4320042..36990a1 100644 --- a/docs/agents/tasks/03-memory.md +++ b/docs/agents/tasks/03-memory.md @@ -52,14 +52,16 @@ - 已建立最小物理页 free-list allocator。 - 已提供 `alloc_page()` 和 `free_page()`。 - 已加入物理页分配/释放 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。 - page metadata。 - buddy allocator。 -- 页表 map/unmap。 - page fault 专门处理。 - 最小内核堆。 - slab/slub 或等价小对象缓存。 @@ -68,7 +70,9 @@ - 正常启动日志包含 `physical pages free=`。 - 正常启动日志包含 `physical page allocator selftest ok`。 +- 正常启动日志包含 `kernel page table root active`。 +- 正常启动日志包含 `page table selftest ok`。 下一阶段: -- 继续在 `03-memory.md` 内推进页表管理和内核堆。 +- 继续在 `03-memory.md` 内推进 page fault 专门处理和最小内核堆。 diff --git a/docs/agents/tasks/README.md b/docs/agents/tasks/README.md index 0f1e900..1e65da1 100644 --- a/docs/agents/tasks/README.md +++ b/docs/agents/tasks/README.md @@ -21,6 +21,23 @@ 9. `09-driver-expansion.md`:PCI/ACPI/更完整设备驱动,音频等后期设备。 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 级能力缺口路由 这些能力不一定马上实现,但必须在路线中占位,避免后续走偏。 diff --git a/docs/roadmap.md b/docs/roadmap.md index c02c6cc..3c2a45e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -19,6 +19,8 @@ - trap 验证:invalid opcode 已能进入 trap dispatch 并最终进入 panic。 - 物理内存基础:已从 boot memory map 建立最小物理页 allocator。 - 物理页验证:`alloc_page/free_page` selftest 已接入启动检查。 +- 页表基础:已切换到内核自有 PML4,并提供最小 `map_page/unmap_page/virt_to_phys`。 +- 页表验证:页表 map/unmap/query selftest 已接入启动检查。 - 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。 - `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。 - `.clang-format` 已用于强制当前 C 代码风格。 @@ -27,7 +29,7 @@ - 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。 - 完整中断体系:PIC/APIC、外部 IRQ、timer interrupt。 -- 内存管理扩展:长期内存区域模型、页表、page fault 策略、内核堆。 +- 内存管理扩展:长期内存区域模型、page fault 策略、内核堆。 - 后续主线:调度、进程、文件系统、用户态和 shell。 ## 当前下一步 @@ -38,7 +40,6 @@ 目标: -- 建立内核页表管理。 - 建立最小内核堆。 - 让 page fault 能进入现有异常路径并输出有效诊断。 diff --git a/drivers/README.md b/drivers/README.md new file mode 100644 index 0000000..ed2da4d --- /dev/null +++ b/drivers/README.md @@ -0,0 +1,9 @@ +# drivers + +设备驱动目录。 + +后续用于放置总线、平台设备和具体硬件驱动,例如 PCI、ACPI、串口、输入设备、 +块设备、网络设备和显示设备。 + +当前 early serial log 仍属于早期调试 backend,不迁入正式驱动模型。 + diff --git a/fs/README.md b/fs/README.md new file mode 100644 index 0000000..e81dbfe --- /dev/null +++ b/fs/README.md @@ -0,0 +1,8 @@ +# fs + +文件系统目录。 + +后续用于放置 VFS、路径解析、具体文件系统和虚拟文件系统接口。 + +当前阶段暂不放代码。 + diff --git a/include/tianole/arch.h b/include/tianole/arch.h index 146b3d9..2009320 100644 --- a/include/tianole/arch.h +++ b/include/tianole/arch.h @@ -1,9 +1,12 @@ #ifndef TIANOLE_ARCH_H #define TIANOLE_ARCH_H +#include + void arch_early_log_init(void); void arch_early_log_putc(char ch); void arch_halt_forever(void) __attribute__((noreturn)); +int arch_page_table_uses_page(uint64_t page); void arch_traps_init(void); #endif diff --git a/include/tianole/mm.h b/include/tianole/mm.h index b7b344b..cd2bc28 100644 --- a/include/tianole/mm.h +++ b/include/tianole/mm.h @@ -8,9 +8,18 @@ #define PAGE_SIZE 4096u 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); phys_addr_t alloc_page(void); 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 diff --git a/init/README.md b/init/README.md new file mode 100644 index 0000000..2b8cd1c --- /dev/null +++ b/init/README.md @@ -0,0 +1,8 @@ +# init + +系统初始化目录。 + +后续用于放置内核初始化流程和第一个用户态 `init` 相关的入口组织。 + +当前 `kernel/main.c` 仍只负责早期启动串联。 + diff --git a/ipc/README.md b/ipc/README.md new file mode 100644 index 0000000..09bbbca --- /dev/null +++ b/ipc/README.md @@ -0,0 +1,8 @@ +# ipc + +进程间通信目录。 + +后续用于放置信号、pipe、event、futex、共享内存等 IPC 基础设施。 + +当前阶段暂不放代码。 + diff --git a/kernel/Makefile b/kernel/Makefile index fef1332..615e89e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -5,5 +5,6 @@ KERNEL_SRCS := \ KERNEL_OBJS := \ $(ARCH_KERNEL_OBJS) \ + $(ARCH_MM_OBJS) \ $(patsubst kernel/%.c,$(BUILD_DIR)/kernel/%.o,$(KERNEL_SRCS)) \ $(MM_OBJS) diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..2fd3cdc --- /dev/null +++ b/lib/README.md @@ -0,0 +1,8 @@ +# lib + +通用库代码目录。 + +后续用于放置字符串、位图、链表、红黑树、校验和等可被多个子系统复用的基础设施。 + +当前阶段暂不放代码。 + diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c44b8f5..7d2ac81 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1,5 +1,6 @@ #include +#include #include #include @@ -58,6 +59,10 @@ static int page_is_reserved(const boot_info_t *boot_info, uint64_t page) return 1; } + if (arch_page_table_uses_page(page)) { + return 1; + } + return 0; } @@ -74,13 +79,24 @@ static void add_conventional_range( const boot_info_t *boot_info, uint64_t start, uint64_t pages) { uint64_t page; + uint64_t first = align_up(start, PAGE_SIZE); uint64_t end = start + pages * PAGE_SIZE; + uint64_t last; - for (page = align_up(start, PAGE_SIZE); page + PAGE_SIZE <= end; - page += PAGE_SIZE) { + if (first + PAGE_SIZE > end) { + return; + } + + last = align_down(end - PAGE_SIZE, PAGE_SIZE); + + for (page = last;; page -= PAGE_SIZE) { if (!page_is_reserved(boot_info, 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"); page_allocator_selftest(); + page_table_selftest(); } diff --git a/net/README.md b/net/README.md new file mode 100644 index 0000000..c2e43af --- /dev/null +++ b/net/README.md @@ -0,0 +1,8 @@ +# net + +网络协议栈目录。 + +后续用于放置 socket API、协议栈、路由和网络缓冲区等网络核心代码。 + +当前本地 `git` 目标不要求网络,暂不放代码。 + diff --git a/scripts/checks/boot.sh b/scripts/checks/boot.sh index 8899f0a..cca69ad 100755 --- a/scripts/checks/boot.sh +++ b/scripts/checks/boot.sh @@ -18,7 +18,9 @@ check_lines build/debug.log \ "memory map descriptors=" \ "conventional memory pages=" \ "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 \ "kernel_main entered" \ @@ -28,6 +30,8 @@ check_lines build/serial.log \ "memory map descriptors=" \ "conventional memory pages=" \ "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 diff --git a/security/README.md b/security/README.md new file mode 100644 index 0000000..6d3bd69 --- /dev/null +++ b/security/README.md @@ -0,0 +1,8 @@ +# security + +安全模型目录。 + +后续用于放置权限、capability、隔离模型和安全策略扩展点。 + +当前阶段暂不放代码。 + diff --git a/sound/README.md b/sound/README.md new file mode 100644 index 0000000..eca24d6 --- /dev/null +++ b/sound/README.md @@ -0,0 +1,8 @@ +# sound + +音频子系统目录。 + +后续用于放置音频核心、设备模型和具体声卡驱动。 + +音频不是早期目标,当前阶段暂不放代码。 + diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..728d691 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,8 @@ +# tools + +宿主机侧开发工具目录。 + +后续用于放置需要编译或较复杂的开发辅助工具。简单脚本继续放在 `scripts/`。 + +当前阶段暂不放代码。 + diff --git a/usr/README.md b/usr/README.md new file mode 100644 index 0000000..02a0ecd --- /dev/null +++ b/usr/README.md @@ -0,0 +1,8 @@ +# usr + +用户空间构建和初始用户程序目录。 + +后续用于放置 initramfs 内容、最小用户态程序、shell 和基础工具。 + +当前阶段暂不放代码。 + diff --git a/virt/README.md b/virt/README.md new file mode 100644 index 0000000..46fd308 --- /dev/null +++ b/virt/README.md @@ -0,0 +1,8 @@ +# virt + +虚拟化支持目录。 + +后续如果需要支持虚拟化相关接口、虚拟设备框架或 guest/host 特性,可以放在这里。 + +当前阶段暂不放代码。 +