feat(boot): bootstrap minimal UEFI startup and document roadmap

This commit is contained in:
Microindole 2026-05-08 16:29:40 +08:00
parent 5fc902d56d
commit 9a71252f91
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
7 changed files with 412 additions and 0 deletions

70
Makefile Normal file
View File

@ -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)

32
boot/main.c Normal file
View File

@ -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);
}
}

12
docs/agents/README.md Normal file
View File

@ -0,0 +1,12 @@
# Agent 文档
这个目录专门放面向 AI / agent 的项目材料,不和内核设计文档混放。
建议内容:
- `session-notes/`:实现日志和阶段结论。
- `tasks/`:适合 agent 执行的边界清晰任务。
- `prompts/`:可复用提示词和约束。
- `checklists/`:启动、验证、回归检查单。
正式架构决策仍然放在 `docs/`,然后在 agent 任务文档里引用它们。

179
docs/roadmap.md Normal file
View File

@ -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
- 建立物理页分配器
- 建立基础异常处理

110
include/efi.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef TIANOLE_EFI_H
#define TIANOLE_EFI_H
#include <stdint.h>
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

4
kernel/main.c Normal file
View File

@ -0,0 +1,4 @@
/*
* Kernel code starts in the next stage.
* The initial milestone is a verified UEFI boot path.
*/

5
scripts/run-qemu.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
make run