diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a0af820 --- /dev/null +++ b/Makefile @@ -0,0 +1,70 @@ +SHELL := /bin/bash + +CC := clang +LD := lld-link + +BUILD_DIR := build +EFI_DIR := $(BUILD_DIR)/image/EFI/BOOT +BOOT_EFI := $(EFI_DIR)/BOOTX64.EFI +BOOT_OBJ := $(BUILD_DIR)/boot/main.obj +DEBUG_LOG := $(BUILD_DIR)/debug.log +OVMF_CODE ?= /usr/share/OVMF/OVMF_CODE_4M.fd +OVMF_VARS_TEMPLATE ?= /usr/share/OVMF/OVMF_VARS_4M.fd +OVMF_VARS := $(BUILD_DIR)/OVMF_VARS.fd + +CFLAGS := \ + -target x86_64-unknown-windows \ + -ffreestanding \ + -fshort-wchar \ + -mno-red-zone \ + -fno-stack-protector \ + -fno-builtin \ + -Wall \ + -Wextra \ + -Werror \ + -Iinclude + +LDFLAGS := \ + /subsystem:efi_application \ + /entry:efi_main \ + /nodefaultlib \ + /dll \ + /machine:x64 \ + /out:$(BOOT_EFI) + +.PHONY: all clean run run-headless dirs + +all: $(BOOT_EFI) + +dirs: + mkdir -p $(BUILD_DIR)/boot $(EFI_DIR) + +$(BOOT_OBJ): boot/main.c include/efi.h | dirs + $(CC) $(CFLAGS) -c $< -o $@ + +$(BOOT_EFI): $(BOOT_OBJ) | dirs + $(LD) $(LDFLAGS) $< + +$(OVMF_VARS): | dirs + cp $(OVMF_VARS_TEMPLATE) $@ + +run: $(BOOT_EFI) $(OVMF_VARS) + qemu-system-x86_64 \ + -drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \ + -drive if=pflash,format=raw,file=$(OVMF_VARS) \ + -drive format=raw,file=fat:rw:$(BUILD_DIR)/image + +run-headless: $(BOOT_EFI) $(OVMF_VARS) + rm -f $(DEBUG_LOG) + qemu-system-x86_64 \ + -display none \ + -nodefaults \ + -no-reboot \ + -drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \ + -drive if=pflash,format=raw,file=$(OVMF_VARS) \ + -drive format=raw,file=fat:rw:$(BUILD_DIR)/image \ + -debugcon file:$(DEBUG_LOG) \ + -global isa-debugcon.iobase=0xe9 + +clean: + rm -rf $(BUILD_DIR) diff --git a/boot/main.c b/boot/main.c new file mode 100644 index 0000000..04af903 --- /dev/null +++ b/boot/main.c @@ -0,0 +1,32 @@ +#include "efi.h" + +static efi_char16_t banner_text[] = { + 'T', 'i', 'a', 'n', 'o', 'l', 'e', ' ', + 'b', 'o', 'o', 't', ' ', 's', 't', 'u', + 'b', ' ', 'l', 'o', 'a', 'd', 'e', 'd', + '.', '\r', '\n', 0 +}; + +static inline void debug_putc(char ch) { + __asm__ volatile("outb %0, $0xe9" : : "a"(ch)); +} + +static void debug_puts(const char *text) { + while (*text != '\0') { + if (*text == '\n') { + debug_putc('\r'); + } + debug_putc(*text++); + } +} + +efi_status EFIAPI efi_main(efi_handle image_handle, efi_system_table_t *system_table) { + (void)image_handle; + + system_table->con_out->output_string(system_table->con_out, banner_text); + debug_puts("Tianole boot stub loaded.\n"); + + for (;;) { + system_table->boot_services->stall(1000 * 1000); + } +} diff --git a/docs/agents/README.md b/docs/agents/README.md new file mode 100644 index 0000000..81fc783 --- /dev/null +++ b/docs/agents/README.md @@ -0,0 +1,12 @@ +# Agent 文档 + +这个目录专门放面向 AI / agent 的项目材料,不和内核设计文档混放。 + +建议内容: + +- `session-notes/`:实现日志和阶段结论。 +- `tasks/`:适合 agent 执行的边界清晰任务。 +- `prompts/`:可复用提示词和约束。 +- `checklists/`:启动、验证、回归检查单。 + +正式架构决策仍然放在 `docs/`,然后在 agent 任务文档里引用它们。 diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..f58bb16 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,179 @@ +# Tianole 路线图 + +## 第 0 阶段 + +- 建立最小 `UEFI` 启动程序。 +- 在 `QEMU + OVMF` 下运行。 +- 在屏幕和 `debug.log` 中输出可见的启动标记。 +- 保证启动后常驻,便于后续调试。 + +## 第 1 阶段 + +- 把当前单体启动程序拆成 `boot/` 与 `kernel/`。 +- 定义 `boot_info` 交接结构。 +- 由 `bootloader` 加载独立 `kernel` 映像。 +- 跳转到 `kernel` 入口并建立稳定栈。 + +## 第 2 阶段 + +- 获取 `UEFI memory map`。 +- 正确执行 `ExitBootServices`。 +- 建立最小 framebuffer 控制台。 +- 开始做物理内存管理。 + +## 第 3 阶段 + +- 建立 `IDT` 和基础异常处理。 +- 增加定时器来源。 +- 开始早期 `ACPI` 解析。 +- 逐步过渡到接近 Linux 风格的 `arch/`、`mm/`、`kernel/` 边界。 + +## 总体流程 + +这个项目要按“可中断、可回坑、每一阶段都能单独验收”的方式推进。最终目标不是只在虚拟机里亮屏,而是要在这台 `UEFI x86_64` 笔记本上独立启动,并逐步具备运行本地 `git` 的能力。 + +建议的长期阶段如下: + +### 阶段 A:启动链 + +- 跑通 `UEFI app -> bootloader -> kernel` +- 保证 `kernel_main()` 真正开始执行 +- 保证屏幕或串口可见输出 + +完成标志: + +- `bootloader` 成功把控制权交给独立 `kernel` +- `kernel` 能输出第一行字并停住 + +### 阶段 B:早期内核基础 + +- 定义 `boot_info` +- 获取 `memory map` +- 正确执行 `ExitBootServices` +- 建立 early console +- 建立最小 panic / assert / log + +完成标志: + +- 退出 UEFI 后内核仍能稳定运行 +- 崩溃时能给出可定位信息 + +### 阶段 C:内存与异常 + +- 建立物理页分配器 +- 建立内核堆分配器 +- 建立 `GDT/IDT` +- 处理基础异常 +- 增加定时器 + +完成标志: + +- 能稳定分配页和堆内存 +- 关键异常能进入处理路径 + +### 阶段 D:任务模型 + +- 建立内核线程 +- 建立调度器 +- 建立上下文切换 +- 增加睡眠/唤醒与同步原语 + +完成标志: + +- 可以同时运行多个内核线程 +- 调度由时钟驱动 + +### 阶段 E:用户态基础 + +- 建立用户地址空间 +- 增加系统调用入口 +- 增加 ELF 加载 +- 增加进程创建和等待 + +完成标志: + +- 能加载并运行最小用户程序 +- 用户程序能调用系统调用输出 + +### 阶段 F:存储与文件系统 + +- 建立块设备层 +- 建立 `VFS` +- 接入一个实际文件系统 +- 实现路径解析、文件读写、目录操作 + +完成标志: + +- 能挂载文件系统 +- 能打开、读取、写入文件 + +### 阶段 G:最小用户空间 + +- 增加 `init` +- 增加简单 shell +- 增加基础命令 +- 补最小 `libc` 能力 + +完成标志: + +- 启动后能进入 shell +- 能执行几个基础程序 + +### 阶段 H:面向 git 的补齐 + +- 优先支持本地 `git`,不先碰网络 +- 补足 `git` 依赖的文件、进程、时间、路径等接口 +- 先以 `git init / status / add / commit` 为目标 + +完成标志: + +- 能运行本地仓库的基础 `git` 操作 + +### 阶段 I:真机落地 + +- 先 `QEMU + OVMF` +- 再 `U 盘 UEFI` 启动 +- 最后再考虑加入本机启动项 + +完成标志: + +- 不破坏现有 `Windows 11 + Linux Mint + GRUB` +- 能在真机上独立启动并进入系统 + +## 当前所处阶段 + +当前只完成了第 0 阶段的最小成果: + +- `UEFI` 启动程序可构建 +- `QEMU + OVMF` 能执行它 +- 屏幕和 `debug.log` 有可见输出 +- 程序可常驻,便于调试 + +当前还没有完成: + +- 独立 `kernel` 接管 +- `bootloader -> kernel` 交接 +- `memory map` +- `ExitBootServices` +- 真正的内核子系统 + +## 接下来最近三个里程碑 + +### 里程碑 1:独立 kernel 接管 + +- 让 `bootloader` 加载独立 `kernel` +- 建立 `boot_info` +- 跳到 `kernel_main()` +- 由 `kernel` 自己输出第一行字 + +### 里程碑 2:退出 UEFI + +- 获取并保存 `memory map` +- 正确执行 `ExitBootServices` +- `kernel` 在退出固件服务后继续稳定运行 + +### 里程碑 3:早期调试和内存基础 + +- 增加 early serial / framebuffer console +- 建立物理页分配器 +- 建立基础异常处理 diff --git a/include/efi.h b/include/efi.h new file mode 100644 index 0000000..b7ae32e --- /dev/null +++ b/include/efi.h @@ -0,0 +1,110 @@ +#ifndef TIANOLE_EFI_H +#define TIANOLE_EFI_H + +#include + +typedef uint64_t efi_status; +typedef void *efi_handle; +typedef uint16_t efi_char16_t; +typedef uint64_t efi_uintn_t; + +#define EFI_SUCCESS 0 +#define EFI_RESET_SHUTDOWN 2 + +#if defined(__x86_64__) +#define EFIAPI __attribute__((ms_abi)) +#else +#define EFIAPI +#endif + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t header_size; + uint32_t crc32; + uint32_t reserved; +} efi_table_header_t; + +typedef struct efi_simple_text_output_protocol efi_simple_text_output_protocol_t; +typedef struct efi_runtime_services efi_runtime_services_t; +typedef struct efi_boot_services efi_boot_services_t; + +struct efi_simple_text_output_protocol { + void *reset; + efi_status(EFIAPI *output_string)( + efi_simple_text_output_protocol_t *self, + efi_char16_t *string + ); +}; + +struct efi_runtime_services { + efi_table_header_t hdr; + void *get_time; + void *set_time; + void *get_wakeup_time; + void *set_wakeup_time; + void *set_virtual_address_map; + void *convert_pointer; + void *get_variable; + void *get_next_variable_name; + void *set_variable; + void *get_next_high_mono_count; + void(EFIAPI *reset_system)( + uint32_t reset_type, + efi_status reset_status, + efi_uintn_t data_size, + void *reset_data + ); +}; + +struct efi_boot_services { + efi_table_header_t hdr; + void *raise_tpl; + void *restore_tpl; + void *allocate_pages; + void *free_pages; + void *get_memory_map; + void *allocate_pool; + void *free_pool; + void *create_event; + void *set_timer; + void *wait_for_event; + void *signal_event; + void *close_event; + void *check_event; + void *install_protocol_interface; + void *reinstall_protocol_interface; + void *uninstall_protocol_interface; + void *handle_protocol; + void *reserved; + void *register_protocol_notify; + void *locate_handle; + void *locate_device_path; + void *install_configuration_table; + void *load_image; + void *start_image; + void *exit; + void *unload_image; + void *exit_boot_services; + void *get_next_monotonic_count; + void(EFIAPI *stall)(efi_uintn_t microseconds); + void *set_watchdog_timer; +}; + +typedef struct { + efi_table_header_t hdr; + efi_char16_t *firmware_vendor; + uint32_t firmware_revision; + efi_handle console_in_handle; + void *con_in; + efi_handle console_out_handle; + efi_simple_text_output_protocol_t *con_out; + efi_handle standard_error_handle; + efi_simple_text_output_protocol_t *std_err; + efi_runtime_services_t *runtime_services; + efi_boot_services_t *boot_services; + uint64_t number_of_table_entries; + void *configuration_table; +} efi_system_table_t; + +#endif diff --git a/kernel/main.c b/kernel/main.c new file mode 100644 index 0000000..49c6059 --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,4 @@ +/* + * Kernel code starts in the next stage. + * The initial milestone is a verified UEFI boot path. + */ diff --git a/scripts/run-qemu.sh b/scripts/run-qemu.sh new file mode 100755 index 0000000..593c751 --- /dev/null +++ b/scripts/run-qemu.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")/.." +make run