102 lines
4.0 KiB
Markdown
102 lines
4.0 KiB
Markdown
# 01 早期调试输出
|
||
|
||
## 目标
|
||
|
||
建立 firmware 之外的稳定日志能力,作为后续 CPU、内存、调度和驱动开发的基础。
|
||
|
||
## 前置条件
|
||
|
||
- bootloader 已能进入 kernel。
|
||
- kernel 已在 `ExitBootServices` 后运行。
|
||
- 当前已有 QEMU debug port 输出。
|
||
|
||
## 建议边界
|
||
|
||
- `arch/x86/`:COM1、I/O port、架构相关 early log backend。
|
||
- `kernel/`:通用 early log 前端和 panic/oops 入口。
|
||
- `scripts/`:QEMU 串口参数和日志验证。
|
||
|
||
## 实现内容
|
||
|
||
- 初始化 COM1。
|
||
- 增加串口输出 backend。
|
||
- 保留 QEMU debug port backend。
|
||
- 统一 `early_log_puts()` 一类前端接口。
|
||
- 增加最小 `panic()`,panic 后停止 CPU。
|
||
- 为后续 printk、oops、内核符号和 crash dump 预留接口边界。
|
||
|
||
### 1. Early console backend
|
||
|
||
- 建立通用 early log 前端,后端可以是 QEMU debug port、COM1、framebuffer console 或后续平台 console。
|
||
- x86 I/O port 访问集中在 arch 层,通用日志代码不能直接写端口。
|
||
- early 阶段不能依赖动态内存、锁、线程或 VFS。
|
||
- 后续切到正式 console 后,early backend 仍可作为 panic fallback。
|
||
|
||
### 2. Panic 与 halt 路径
|
||
|
||
- `panic()` 必须能在 allocator、scheduler、interrupt 都不可靠时输出最小信息。
|
||
- panic 路径要尽量避免递归 panic,至少保证第二次进入时直接 halt。
|
||
- arch 层提供 `arch_halt_forever()`,通用 panic 不直接写 `hlt` 循环细节。
|
||
- 后续 oops 和 panic 要区分:可恢复异常进入 oops,不可恢复错误进入 panic。
|
||
|
||
### 3. Log buffer 与正式 printk 预留
|
||
|
||
- early log 前端后续应能迁移到 ring buffer,而不是让所有代码直接写串口。
|
||
- 预留 log level、CPU id、timestamp、caller/source 信息。
|
||
- 预留多 consumer:serial、debug port、framebuffer console、内存 buffer。
|
||
- 预留 crash dump 或重启后读取日志的路径。
|
||
|
||
## Linux 参考原则
|
||
|
||
- 参考 Linux `printk`/console 分层:调用方写通用日志接口,console driver 负责具体输出设备。
|
||
- 参考 Linux early console 思路:早期输出简单可靠,正式 console 接管后仍保留诊断价值。
|
||
- 参考 Linux panic/oops 分离:panic 是不可恢复停机,oops 是异常诊断和策略入口。
|
||
- 参考 Linux ring buffer 思路:日志先进入内核缓冲,再由不同 console 消费,避免业务代码绑定输出硬件。
|
||
|
||
## 非玩具化约束
|
||
|
||
- 日志前端不能直接绑定 COM1。
|
||
- COM1 端口号要集中定义,不能散落在业务代码。
|
||
- panic 路径不能依赖动态内存。
|
||
- 输出接口要允许后续接 framebuffer console。
|
||
- 日志接口不能要求 scheduler、heap 或 interrupt 已经初始化。
|
||
- panic/oops 策略不能写进异常入口汇编。
|
||
- 正式 printk 出现后,early log 调用方不能被迫大规模改名或换接口。
|
||
- 任何新增 backend 都不能影响其他 backend 的可用性。
|
||
|
||
## 验收方式
|
||
|
||
- QEMU 日志中能看到 bootloader 和 kernel 输出。
|
||
- 串口或 debug log 中能看到 `boot services exited`。
|
||
- 人为触发 panic 时能看到 panic 信息并停止。
|
||
- 禁用某一个输出 backend 时,其他 backend 仍可输出关键日志。
|
||
- panic 路径在 heap 未初始化前也能输出。
|
||
- `scripts/check.sh` 至少验证 debug log 和 serial log 两条路径。
|
||
|
||
## 当前状态
|
||
|
||
基础完成:
|
||
|
||
- kernel early log 已拆成通用前端和 x86 backend。
|
||
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
||
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
||
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
||
|
||
后续扩展:
|
||
|
||
- printk/log level/ring buffer。
|
||
- oops 格式。
|
||
- 异常栈输出。
|
||
- 内核符号化输出。
|
||
- framebuffer console。
|
||
- crash dump。
|
||
- early log 到 printk 的接管流程。
|
||
- panic 后保留最后日志的内存 buffer。
|
||
- 多 CPU 后的日志串行化规则。
|
||
|
||
验收依据:
|
||
|
||
- 正常启动日志能进入 QEMU debug log。
|
||
- kernel 日志能进入 COM1 serial log。
|
||
- `panic()` 已被 `02-cpu-interrupts.md` 的 invalid opcode 测试覆盖。
|