# 逐步实验手册

## 实验规范

每个实验必须创建独立目录或 Git commit，并使用 `templates/EXPERIMENT_RECORD.md`。如果实际命令与本文不同，以当前 checkout 的 README/Make help 为准，并把差异写入记录。

统一证据：

- 构建命令和退出码。
- 版本 commit。
- 配置文件。
- 完整串口日志。
- 关键断点或寄存器输出。
- PASS/FAIL 判断。

---

## Lab 00：环境与版本锁定

### 目标

建立可重复实验环境，证明 QEMU 和 GDB 可以工作。

### 步骤

1. 完成 `00_BASELINE.md` 的工具检查。
2. 克隆 ArceOS 和 AxVisor 官方仓库并初始化子模块。
3. 写入 `versions.lock`。
4. 找到一个最小 AArch64 示例并构建。
5. 在 QEMU 命令中加入 `-S -s`。
6. 使用 GDB 加载带符号 ELF，连接 `:1234`。
7. 在入口设置断点，记录 PC、SP、CurrentEL。

### 验收

- [ ] 构建可重复成功。
- [ ] GDB 在入口停止。
- [ ] PC 落在预期镜像区间。
- [ ] 保存版本和终端日志。

### 常见问题

- QEMU 直接退出：检查镜像格式和 `-kernel`/loader 用法。
- 断点不命中：检查 GDB 加载的 ELF 与实际 binary 是否来自同一次构建。
- 地址错位：检查链接地址和 QEMU 装载方式。

---

## Lab 01：ArceOS Hello World 启动追踪

### 目标

从架构入口追踪到应用第一条日志。

### 步骤

1. 使用当前仓库官方命令运行 hello world。
2. 保存完整构建命令和 QEMU 命令。
3. 找链接脚本和入口符号。
4. 在架构入口、Rust 运行时入口、应用主函数设置断点。
5. 记录每个断点的 PC、SP、调用栈和 CurrentEL。
6. 为内存、驱动、任务初始化增加临时阶段日志。
7. 画出入口到应用的调用链。

### 验收

- [ ] 调用链包含真实文件和函数。
- [ ] 能说明 BSS、栈和 MMU 何时准备。
- [ ] 能说明第一条 UART 日志经过哪些抽象层。

---

## Lab 02：ArceOS 多任务与同步

### 目标

编写两个任务，理解调度、timer 和同步原语。

### 任务设计

- Task A 每 100 ms 增加共享计数器。
- Task B 每 500 ms 读取计数器并打印。
- 使用 mutex/spinlock 或当前 ArceOS 提供的同步原语保护数据。

### 步骤

1. 从最小多任务示例创建新应用。
2. 启用需要的 task/timer feature。
3. 创建共享状态和两个任务。
4. 加入任务 ID、CPU ID、tick 日志。
5. 故意去掉锁，观察不一致或解释为什么当前实验难以复现竞态。
6. 恢复同步并运行至少 60 秒。

### 验收

- [ ] 两个任务频率基本符合设计。
- [ ] 能定位 timer 驱动到调度唤醒的路径。
- [ ] 能解释当前锁在单核和多核下的差异。

---

## Lab 03：ArceOS 启动阶段故障注入

### 目标

训练“最后成功边界”调试法。

### 故障 A：错误 UART 地址

在实验分支中修改平台 UART 地址，观察第一条日志消失。使用 GDB 验证程序仍在执行，并观察 MMIO 写地址。

### 故障 B：跳过内存初始化

只在可安全回滚的实验分支中跳过或延迟堆初始化，观察首次动态分配的失败位置。

### 验收

- [ ] 每个故障都有预期、实际、证据和修复。
- [ ] 不把“无串口输出”直接等同于“CPU 没运行”。

---

## Lab 04：AxVisor 首次启动与 VM 配置

### 目标

运行官方最小 VM 示例，建立 VM 生命周期地图。

### 步骤

1. 按当前 AxVisor README 构建 AArch64 QEMU 示例。
2. 记录真实 VM 配置文件和客体镜像来源。
3. 在配置解析、VM create、image load、vCPU create 和 first run 增加日志。
4. 日志统一包含 VM ID 和 vCPU ID。
5. 保存一份成功串口日志并标注阶段。

### 验收

```text
[vm-create]
[image-load]
[stage2-ready]
[vcpu-create]
[vcpu-enter]
[guest-first-output]
```

- [ ] 能把每条日志对应到真实源码。
- [ ] 能解释 guest entry 和 image load address。

---

## Lab 05：VM Exit 解码器

### 目标

把模糊的异常日志升级为可定位信息。

### 实现要求

为 lower EL sync 路径增加结构化输出：

```text
[vm-exit] vm=<id> vcpu=<id> ec=<hex> iss=<hex>
          elr=<hex> spsr=<hex> far=<hex> hpfar=<hex>
```

至少对以下类型提供可读名称：

- HVC。
- WFI/WFE 或系统寄存器陷入。
- Instruction Abort。
- Data Abort。
- Unknown/Unsupported。

### 实验

1. 触发一次已有 HVC 或合法 Exit。
2. 触发一次访问未映射 IPA。
3. 比较 ESR_EL2 的 EC/ISS。
4. 证明 handler 是否推进 ELR_EL2。

### 验收

- [ ] 两种 Exit 能被正确区分。
- [ ] 日志足以定位客体 PC 和访问地址。
- [ ] 未知类型不会静默忽略。

---

## Lab 06：Stage-2 映射检查器

### 目标

在 VM 启动前发现对齐、重叠和权限错误。

### 功能要求

遍历 VM region，检查：

- IPA/PA/size 页对齐。
- size 非零。
- VM 内 IPA region 不冲突。
- 所有 VM 的 PA region 不冲突。
- MMIO 使用 Device 属性。
- RAM 权限符合预期。

### 故障注入

1. 让 Linux 和 RTOS PA region 重叠一页。
2. 让一个 region 长度不对齐。
3. 把 UART MMIO 标记为 Normal Memory。

### 验收

- [ ] 前两类错误在进入客体前被拒绝。
- [ ] 错误输出包含 VM 和具体 region。
- [ ] 能打印一条 IPA→PA 样例映射。

---

## Lab 07：Linux 客体启动到 shell

### 目标

准备 Linux Image、DTB 和最小 initramfs，让 Linux 进入 shell。

### 步骤

1. 选择与 AxVisor 示例兼容的 AArch64 Linux 版本。
2. 使用官方 defconfig 或项目推荐配置构建 `Image` 和 DTB。
3. 准备最小 initramfs，确保 `/init` 可执行。
4. 在 DTB `/chosen` 设置 console、earlycon 和 initrd 范围。
5. 核对 Linux guest IPA memory 与 DTB `/memory`。
6. 核对 vCPU `x0` 指向 DTB IPA。
7. 先启动单 vCPU Linux。
8. 将日志分为 early entry、memory、driver、rootfs、init 五段。

### 验收

- [ ] 出现 earlycon。
- [ ] Linux 识别预期 RAM 和 CPU 数。
- [ ] initramfs 挂载成功。
- [ ] `/init` 执行并进入 shell。

### 典型失败点

- 无 earlycon：UART 节点、bootargs 或 MMIO。
- `Bad Linux ARM64 Image magic`：镜像格式/地址错误。
- `No working init found`：权限、架构、动态库或路径错误。

---

## Lab 08：Linux 自动启动两个程序

### 目标

使用配套 `app-a.c`、`app-b.c` 和 `/init`，在 Linux 启动后自动运行两个程序。

### 步骤

1. 使用 AArch64 交叉编译器静态编译：

```bash
aarch64-linux-gnu-gcc -O2 -static -o app-a app-a.c
aarch64-linux-gnu-gcc -O2 -static -o app-b app-b.c
file app-a app-b
```

2. 将程序放入 initramfs `/bin`。
3. 使用配套 `/init`，设置可执行权限。
4. 重新生成 cpio archive。
5. 启动 Linux 并运行 5 分钟。

### 验收

- [ ] `/init` 输出 `[PASS] linux apps started`。
- [ ] App A 和 App B PID 不同。
- [ ] 两者持续心跳。
- [ ] 一个程序退出时另一个仍运行。

---

## Lab 09：RTOS 客体启动与双任务

### 目标

启动当前 AxVisor 支持的 RTOS/unikernel 客体，再实现两个周期任务。

### 步骤

1. 先运行官方 RTOS/unikernel 示例，不做平台迁移。
2. 记录镜像格式、load address、entry、RAM、UART、timer 和 IRQ。
3. 追踪 entry 到第一条日志。
4. 追踪 timer 初始化和第一个 tick。
5. 创建 100 ms 与 500 ms 周期任务。
6. 输出 task ID、tick 和周期计数。
7. 运行 10 分钟并统计周期误差。

### 验收

- [ ] RTOS 调度器启动。
- [ ] 两个任务持续运行。
- [ ] timer/vIRQ 路径可以解释。
- [ ] 任务周期无明显长期漂移。

---

## Lab 10：Linux + RTOS 双客体

### 目标

同时运行 Linux 和 RTOS，完成静态 CPU、内存、IRQ 和设备分区。

### 步骤

1. 复制 `resource-map.example.toml` 并替换为实际配置。
2. 为每个 VM 分配不重叠 PA region。
3. 为 RTOS 固定一个物理 CPU；Linux 使用其余 CPU。
4. 检查 VMID、VTTBR 和 IRQ owner。
5. 同时启动两台 VM。
6. 日志增加 `[linux]`、`[rtos]`、`vm=<id>` 前缀。
7. 连续运行至少 30 分钟。

### 验收

- [ ] Linux App A、App B 心跳持续。
- [ ] RTOS Task 1、Task 2 心跳持续。
- [ ] 无 PA/IRQ/设备重叠。
- [ ] 30 分钟内无未处理 Exit。

---

## Lab 11：故障隔离

### 目标

证明一个 VM 的故障不破坏另一个 VM。

### 测试矩阵

| 故障 | 预期 |
|---|---|
| 杀死 Linux App A | App B 和 RTOS 继续运行 |
| Linux kernel panic | RTOS 继续运行，AxVisor 标记 Linux VM 失败 |
| RTOS task panic | Linux 继续运行 |
| RTOS 访问未映射 IPA | AxVisor 报告 RTOS VM fault，Linux 继续运行 |

### 证据

每次故障记录注入时刻、Exit 信息、VM 状态变化，以及另一个 VM 在故障前后至少 30 秒的连续心跳。

### 验收

- [ ] AxVisor 不发生自身 panic。
- [ ] 故障归属到正确 VM。
- [ ] 非故障 VM 不停止、不重启。

---

## Lab 12：一键构建与自动验收

### 目标

让陌生人根据 README 在干净环境中复现实验。

### 脚本职责

`build-all.sh`：

- 检查工具。
- 输出版本。
- 构建 AxVisor、Linux apps、initramfs、RTOS。
- 失败立即退出。

`run.sh`：

- 启动 QEMU。
- 保存串口日志。
- 支持超时退出。

`smoke-test.sh`：

- 调用 `run.sh`。
- 使用 `check-serial-log.sh` 检查关键标志。
- 输出 PASS/FAIL。

### 最终验收

- [ ] 干净环境完成构建。
- [ ] 30 分钟稳定运行。
- [ ] 自动日志检查通过。
- [ ] 故障注入测试通过。
- [ ] README 包含版本、架构图和已知限制。
