This commit is contained in:
xyou
2026-07-31 13:53:49 +08:00
parent 5ea84fac97
commit f52157b7af
241 changed files with 26843 additions and 8155 deletions
+288 -39
View File
@@ -1,39 +1,288 @@
# Deshab Bug 记录
> 所有测试与开发过程中发现的 bug 统一登记于此。每个 bug 按 BUG-YYYYMMDD-NNN 编号。
> 测试用例文档位于 RE/test-cases/。
## 严重程度定义
- P0 阻断: 影响主链路启动或核心功能完全不可用(必须立即修复)
- P1 严重: 功能可降级但关键路径受损(本轮内修复)
- P2 一般: 非关键路径异常或体验问题(下一里程碑前修复)
- P3 提示: 日志/小问题,不影响功能(记录备查)
## Bug 条目格式
```
### BUG-YYYYMMDD-NNN [P级] [模块] 简述
- 现象:
- 复现步骤:
- 串口/日志证据:
- 根因分析:
- 修复方案:
- 状态: [OPEN/FIXING/FIXED/CLOSED]
- 修复提交: <commit hash 或 "待提交">
- 验证: <验证方法与结果>
```
---
## Bug 列表
<!-- 在此追加 bug 条目,最新放在顶部 -->
P2: 软件Files的窗口点击叉无法关闭
P3: 在第一页已经有了终端,所以在第四页的桌面上不用显示终端和CMD
P2: 在开发者模式中的shell输入命令并回车时文字消失没有反应
P3: 启动时的打字机效果再快3倍
P2: 桌面中第一页的shell中输入命令回车后,上一条命令输入的文字会跑到下一行
P2: 桌面中第一页的Files显示的不是SYSTEM/user中的文件夹内容
P2: Files中没有显示文件夹,直接显示了文件夹下的文件
P2: Files没有提供“..”来供用户返回上一级的文件夹
# Deshab Bug 记录
> 所有测试与开发过程中发现的 bug 统一登记于此。每个 bug 按 BUG-YYYYMMDD-NNN 编号。
> 测试用例文档位于 RE/test-cases/。
>
> 2026-07-29 文档治理:此前 8 条遗留纯文本 bug 已按模板补全(编号 001–008,
> 日期为桌面测试期的近似日期);同时补登 B5/B6 及 BSS NOBITS、go_up 条目。
## 严重程度定义
- P0 阻断: 影响主链路启动或核心功能完全不可用(必须立即修复)
- P1 严重: 功能可降级但关键路径受损(本轮内修复)
- P2 一般: 非关键路径异常或体验问题(下一里程碑前修复)
- P3 提示: 日志/小问题,不影响功能(记录备查)
## Bug 条目格式
```
### BUG-YYYYMMDD-NNN [P级] [模块] 简述
- 现象:
- 复现步骤:
- 串口/日志证据:
- 根因分析:
- 修复方案:
- 状态: [OPEN/FIXING/FIXED/CLOSED]
- 修复提交: <commit hash 或 "待提交">
- 验证: <验证方法与结果>
```
---
## Bug 列表
<!-- 在此追加 bug 条目,最新放在顶部 -->
### BUG-20260729-021 [P2] [DSK/kernel_api] 硬偏移读取 kernel_api.block 脆弱性
- 现象: DSK 通过 `*(u64*)(api + 0xA8)` 硬偏移读取 `dkm_kernel_api.block`,若结构体在 `block` 字段前插入新字段,DSK 将读取到错误指针 → 解引用 → 立即崩溃,无日志。
- 复现步骤: 修改 dkm.h 中 `dkm_kernel_api` 在 `block` 字段前增加任意字段 → 编译 DSK(不重新编译)→ 启动。
- 串口/日志证据: 安全审查Agent报告(bd89e1b9),4.1节。
- 根因分析: DSK 独立编译不含 `dkm_kernel_api` 结构体定义,无法在编译期校验偏移一致性;注释虽警告"新增字段只允许追加在末尾",但无编译期保障。
- 修复方案: ① DSK 端 include `dkm_kernel_api` 结构体定义并用字段名访问;② 或添加 `_Static_assert(offsetof(..., block) == 0xA8, ...)` 编译期校验。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/dsk/main.c + CODE/shell/main.c)
- 验证: DSK/shell 均添加 dsk_kernel_api_mirror/shell_kernel_api_mirror 镜像结构体 + _Static_assert 编译期校验 + 字段名访问,不再使用硬偏移。
### BUG-20260729-020 [P1] [UTSM/IDT] IOAPIC 中断路由可能绕过 PIC mask
- 现象: 实机通过 IOAPIC 路由中断时,`dsk_load_and_jump()` 中的 PIC mask 不影响 IOAPIC redirection entry,中断可能绕过 PIC mask 直接到达 CPU。
- 复现步骤: 在使用 IOAPIC 的实机上启动 → UTSM mask PIC → DSK 运行期间 IOAPIC 路由的 IRQ 到达 → IDT halt stub 触发(修复 BUG-013 后)→ 安全停机而非崩溃。
- 串口/日志证据: 系统日志Agent报告(aa2d3f5a),崩溃点B分析。
- 根因分析: `dsk_load_and_jump()` 仅操作 8259 PIC 的 IMR 寄存器,未处理 IOAPIC 的 24 个 redirection entry。实机(尤其是多核平台)中断路由通常经 IOAPIC 而非 PIC。
- 修复方案: ① 在 `dsk_load_and_jump()` 中 mask IOAPIC 全部 24 个 redirection entry(设 mask 位);② 或在 LAPIC 中设置 TPR=0xFF 屏蔽所有中断。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/UTSM/kernel/dsk_loader.c dsk_load_and_jump)
- 验证: 采用方案②:通过 IA32_APIC_BASE MSR 读取 LAPIC 基地址 + HHDM 偏移,写入 TPR=0xFF 屏蔽所有可屏蔽中断优先级。仅在 LAPIC 全局启用时(bit 11)执行。
### BUG-20260729-019 [P1] [UTSM+DSK/FAT32] FAT 表缓冲区在实机大容量磁盘上溢出
- 现象: FAT32 FAT 表缓冲区 `g_dsk_fat32_disk[131072]`(128KB/256扇区)在实机大容量磁盘上不足。2GB 以上分区的 FAT 表超过 128KB,FAT 链遍历在缓冲区边界处中断,文件读取截断或失败。
- 复现步骤: 在 ≥2GB FAT32 分区上启动 → UTSM 从 SATA 盘读取 deshab.elf → FAT 条目偏移超过 128KB → `fat_ent + 4 > sizeof(g_dsk_fat32_disk)` → 循环中断 → 文件未找到或数据截断。
- 串口/日志证据: 安全审查Agent报告(bd89e1b9),2.1节;计算表:2GB分区/32KB簇=256KB FAT(溢出),32GB分区/4KB簇=32MB FAT(严重溢出)。
- 根因分析: FAT 读取策略为"一次性预读 256 扇区",在 QEMU 4MB 测试镜像上够用,但实机磁盘分区远大于此。当文件起始簇在缓冲区内但后续簇不在时,ELF 数据不完整 → 加载后必然崩溃。
- 修复方案: 实现"按需 FAT 扇区读取":仅读取包含目标条目的单个 FAT 扇区(512字节),而非预读整个 FAT 表。需新增 `fat32_get_next_cluster()` 函数。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/UTSM/kernel/dsk_loader.c + CODE/dsk/main.c + CODE/shell/main.c)
- 验证: 三端均实现 fat32_get_next_cluster() 按需 FAT 扇区读取:条目在缓冲区内直接读取,超出则按需读单扇区(512B)。写入路径保留缓冲区内修改+回写逻辑不变。
### BUG-20260729-018 [P1] [DSK/loader] g_fdata 仅 1MB,与 UTSM 端 2MB 不一致
- 现象: DSK 端文件数据缓冲区 `g_fdata[1048576]` 仅 1MB,而 UTSM 端 `g_dsk_fat32_filedata[2097152]` 为 2MB。当前 desktop.elf 约 150KB 暂时够用,但增长后将突破 1MB 限制。
- 复现步骤: desktop.elf 增长至 >1MB → `fat32_read_root_file` 因 `found_size > sizeof(g_fdata)` 返回 -6 → desktop 无法加载。
- 串口/日志证据: 安全审查Agent报告(bd89e1b9),2.2节。
- 根因分析: UTSM 和 DSK 两端缓冲区大小定义不一致,DSK 端未与 UTSM 对齐。
- 修复方案: `g_fdata[1048576]` → `g_fdata[2097152]`,与 UTSM 端对齐。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/dsk/main.c L66)
- 验证: 编译通过,镜像生成成功。
### BUG-20260729-017 [P1] [UTSM/dsk_loader] FAT32 加载器 11 个返回点无日志
- 现象: `dsk_load_from_block_fat32()` 有 11 个 `return -1` 出口但无任何日志输出。实机 SATA 盘 FAT32 结构与 QEMU 不同时,函数静默失败回退 Limine boot module,用户无法从日志得知失败原因。
- 复现步骤: 实机启动 → FAT32 解析任意步骤失败 → 串口无相关日志 → 回退 Limine 或 halt。
- 串口/日志证据: 系统日志Agent报告(aa2d3f5a),缺口1分析。
- 根因分析: 函数编写时为早期最小实现,未添加错误路径日志。
- 修复方案: 在全部 11 个 `return -1` 前添加 `log_error` + 诊断 `log_hex64`,输出失败条件和关键参数值。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/UTSM/kernel/dsk_loader.c L182-317)
- 验证: 编译通过,日志覆盖完整。
### BUG-20260729-016 [P0] [DSK/loader] kernel_api / framebuffer 解引用无 NULL 验证
- 现象: DSK `dsk_spinner_and_check()` 直接解引用 `ctx->dkm_kernel_api` 和 `ctx->framebuffer_address`,无 NULL 检查。若 `api` 为 0 → `*(u64*)(0 + 0xA8)` 解引用地址 0xA8 → 页错误 → 三重故障重启。framebuffer 为 NULL 同理。
- 复现步骤: boot_context 传递错误(kernel_api=0 或 fb=0)→ DSK entry → 立即页错误。
- 串口/日志证据: 系统日志Agent报告(aa2d3f5a),缺口3/4分析。
- 根因分析: 早期实现假设 boot_context 始终有效,未添加防御性检查。
- 修复方案: ① `kernel_api` NULL 检查 + 诊断日志;② `framebuffer_address` NULL 检查;③ 输出 `block_api` / `block_read` / `block_write` 函数指针地址便于调试。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/dsk/main.c L866-878)
- 验证: 编译通过。
### BUG-20260729-015 [P0] [DKM/ahci] AHCI 写入超时使用固定循环 + PxCMD.W 违规写入
- 现象: `ahci_write_blocks_internal()` 使用 `for(i=0;i<5000000;i++)` 固定循环超时。实机 CPU 频率差异导致超时时间不可预测(快 CPU 可能过早超时,慢 CPU 可能等待过久)。同时代码写入 `PxCMD.W` 位(AHCI 规范中为只读位),违反 AHCI 1.3 规范。
- 复现步骤: 实机启动 → DSK 通过 AHCI 写入 FAT32 → 固定循环超时不足 → 写入超时失败 → FAT32 写操作失败 → 系统异常。
- 串口/日志证据: 驱动开发Agent报告;原代码 L356-358(PxCMD.W 写入)、L363-371(固定循环)。
- 根因分析: ① 写入路径复制了早期读取路径的固定循环模式,未跟随读取路径升级为 TSC deadline;② `AHCI_CMD_W` (bit 6) 在 AHCI 规范中为只读,硬件根据命令头 `flags.W` 自动设置。
- 修复方案: ① 写入超时替换为 TSC 5 秒 deadline(与读取路径一致);② 移除所有 PxCMD.W 读/写操作。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/DKM/ahci/ahci.c L355-371)
- 验证: 编译通过;QEMU 读取路径已验证 TSC 超时有效。
### BUG-20260729-014 [P0] [DSK/loader] dsk_load_elf 完全忽略 size 参数 + PIE 重定位无边界检查
- 现象: DSK 端 `dsk_load_elf()` 显式 `(void)size` 忽略文件大小参数,程序头、PT_LOAD 段、PT_DYNAMIC 段的读取均无边界检查。PIE 重定位中 `rela_off - min_vaddr` 和 `rel->offset - min_vaddr` 可能无符号整数下溢(若 < min_vaddr),导致从 `image` 基址偏移到巨大地址 → 随机内存写入 → 三重故障重启。UTSM 端有完整的 `range_ok` 检查,DSK 端完全缺失。
- 复现步骤: 加载损坏/恶意的 ELF → `rela_off < min_vaddr` → `rela_off - min_vaddr` 下溢为 ~0xFFFFFFFFFFFFFFFF → 读取/写入随机内存 → 崩溃。
- 串口/日志证据: 安全审查Agent报告(bd89e1b9),1.1/1.2/3.1节。
- 根因分析: DSK ELF 加载器为简化实现,省略了 UTSM 端已实现的安全检查。两端 ELF 加载逻辑不一致(UTSM 有 `range_ok`,DSK 忽略 `size`)。
- 修复方案: ① 移除 `(void)size`,添加 `size < sizeof(elf64_ehdr)` 检查;② 程序头范围验证 `phoff + phnum*phentsize <= size`;③ PT_LOAD `offset + filesz <= size` 检查;④ PT_DYNAMIC vaddr 范围检查;⑤ PIE `rela_off >= min_vaddr && rela_off < min_vaddr + isize` 边界检查;⑥ `rel->offset >= min_vaddr && rel->offset < min_vaddr + isize` 边界检查;⑦ entry 范围验证。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/dsk/main.c L721-824)
- 验证: 编译通过;边界检查逻辑与 UTSM 端 `dsk_load_elf_image` 一致。
### BUG-20260729-013 [P0] [UTSM/dsk_loader] UTSM→DSK 跳转前 cli 缺失 + IDT 未重置导致实机三重故障
- 现象: 实机从 UTSM 加载 DSK 后崩溃重启。`dsk_load_and_jump()` 中 PIC mask 操作前未执行 `cli`,两条 `outb`(mask PIC2 和 PIC1)之间存在中断竞争窗口。实机在此窗口内可能触发 spurious IRQ7/15 或 IOAPIC 中断 → 跳转到 UTSM 过期 IDT handler → 若 handler 内调用的内存被 DSK 覆盖 → 双重异常 → 三重故障 → 重启。同时,跳转后 IDT 仍指向 UTSM 的 ISR stub,DSK 运行期间任何 CPU 异常(PF/GP)会执行 UTSM 的 `idt_handler`(含 log_error + for(;;)cli;hlt),若发生双重异常则重启。
- 复现步骤: 实机启动 → UTSM 完成 DKM 加载 → `dsk_load_and_jump()` → PIC mask 两步操作之间中断到达 → 三重故障重启。或在 DSK 运行期间发生 CPU 异常 → UTSM handler → 可能双重异常 → 重启。
- 串口/日志证据: 系统日志Agent报告(aa2d3f5a),崩溃点A/B分析;安全审查Agent报告确认。QEMU 不复现(无 spurious IRQ、无 SMI/NMI)。
- 根因分析: ① `outb(0xA1,0xFF)` 和 `outb(0x21,0xFF)` 之间 IF=1,PIC2 IRQ 可通过 cascade 到 PIC1 触发中断;② IDT 未重置,DSK 阶段异常仍路由到 UTSM handler,handler 依赖的代码/数据可能已被覆盖。
- 修复方案: ① PIC mask 前添加 `__asm__ volatile("cli")` 消除中断窗口;② 新增 `idt_halt_all()` 函数将全部 256 个 IDT 条目替换为安全 halt stub(`cli; hlt` 循环),防止 DSK 运行期间过期 handler 被触发;③ DSK entry 前输出 context 关键字段诊断日志(magic/api/fb)。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/UTSM/kernel/dsk_loader.c L613-650, CODE/UTSM/arch/x86_64/idt.c L440-460, CODE/UTSM/include/utsm/idt.h L8-11)
- 验证: 编译通过;QEMU 启动正常;实机验证待用户执行。
### BUG-20260729-012 [P3] [tools/fileman] go_up 缺少前向声明导致编译错误
- 现象: fileman 编译失败,`open_selected()` 调用 `go_up()` 时函数尚未定义(隐式声明/未定义引用)。
- 复现步骤: 编译 `CODE/tools/fileman/`。
- 串口/日志证据: 编译器报错(implicit declaration of function 'go_up')。
- 根因分析: C 文件内函数定义顺序——`open_selected`(L259)在 `go_up`(L298)之前,缺少前向声明。
- 修复方案: `CODE/tools/fileman/main.c` L258 添加 `static void go_up(void); /* 前向声明:open_selected 调用 go_up */`。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——main.c L258 前向声明存在,编译通过。
### BUG-20260729-011 [P1] [build/linker] 全项目 linker.ld 未将 .bss 分离为 NOBITS 段导致 ELF 膨胀
- 现象: 多个用户态 ELF 体积异常膨胀:shell 773KB、cmd 499KB、fileman ~470KB,远超代码实际大小;fileman 因超出桌面宿主加载缓冲区而崩溃(见 BUG-20260728-009)。
- 复现步骤: 构建 shell/cmd/fileman 等 .elf,观察文件体积;反查 program header 的 filesz/memsz。
- 串口/日志证据: 无运行期日志;构建产物体积异常即为证据。
- 根因分析: linker.ld 将 `.bss` 与 `.data` 合并且未分离为独立 NOBITS 段,零初始化 BSS 数据(f32_disk/f32_data 等大缓冲)被计入 ELF 文件体积。
- 修复方案: 全项目 linker.ld 将 `.bss` 分离为独立段置于 `.dynamic` 之后(仍归 `:data` PT_LOAD,phdr filesz<memsz,NOBITS 不占文件空间)。修复后 shell 773KB→92KB、cmd 499KB→92KB、fileman ~470KB→46KB。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——`CODE/tools/fileman/linker.ld` L23–28 含分离注释与独立 .bss 段;shell/cmd 等 linker.ld 同构。
### BUG-20260729-010 [P1] [firstInit] FirstInit 设置向导按键崩溃(策划 B6)
- 现象: FirstInit 账户设置卡片中按键导致崩溃。原记录:"fb_text -O2 optimization issue"。
- 复现步骤: firstInit.txt=0 → 首次启动 → 进入 FirstInit 账户设置卡片 → 按任意键。
- 串口/日志证据: 调试会话 `debug-firstinit-key-crash.md`(状态 OPEN)记录 H1–H5 假设:① -O2 下 clang 生成 memset/memcpy 隐式调用或对齐 SSE 指令(freestanding 环境出错);② fb_text/fb_char 路径数组越界(g_ascii 95 项索引边界 / 18px 中文字模尺寸);③ 栈溢出(DSK 直接 call 入口共享同一栈,-O2 内联帧膨胀);④ 扫描码状态机(shift/release)越界写;⑤ linker.ld BSS NOBITS 分离后 rela 覆盖遗漏(指针数组未重定位)。已确认:所有模块 -O2 编译、无 -fno-strict-aliasing;DSK loader 已 memset 整个 image(BSS 安全)。
- 根因分析: 未定,待 QEMU 复现(monitor sendkey + 串口捕获异常向量/RIP)后 addr2line 定位。M1 里程碑确认 QEMU 未复现。
- 修复方案: 添加防御性边界检查:scan_to_ascii 调用前 sc>=58 提前跳过(防止越界);已确认 -fno-builtin 编译标志存在。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/firstInit/main.c read_field 防御性边界检查)
- 验证: M1 里程碑 QEMU 验证未复现;已添加 sc>=58 防御性跳过;-fno-builtin 已存在于编译标志。
### BUG-20260728-009 [P1] [desktop/fileman] fileman.elf 启动触发 QEMU exit(策划 B5)
- 现象: 桌面中打开 Files 应用(外部 .elf 全屏宿主)时 QEMU 进程直接退出,文件管理器不可用。
- 复现步骤: 构建含 fileman.elf 的镜像 → 启动至 desktop → 打开 fileman → QEMU exit。
- 串口/日志证据: fileman 启动后串口输出中断(当时未留存完整日志,退出码待补录)。
- 根因分析: fileman 的 linker.ld 未将 .bss 分离为 NOBITS 段(见 BUG-20260729-011),ELF 膨胀至 ~470KB,超出 desktop `dt_load_elf` 的 `g_elf_image` 256KB 静态缓冲区,加载越界/校验失败。
- 修复方案: ① 全项目 linker.ld .bss 分离 NOBITS(fileman ~470KB→46KB);② desktop `g_elf_image` 由 256KB 扩至 1MB(`CODE/desktop/main.c` L1802)。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——linker.ld NOBITS 分离 + g_elf_image=1048576 已确认;fileman 现可打开/浏览/关闭(QEMU 实机回归建议补做)。
### BUG-20260728-008 [P2] [desktop/fileman] Files 缺少 ".." 返回上级目录
- 现象: Files 进入子目录后没有 ".." 条目,用户无法返回上一级文件夹。
- 复现步骤: desktop 打开 Files → 进入任意子目录 → 列表中无返回入口(仅键盘 Backspace)。
- 串口/日志证据: 无(UI 行为问题)。
- 根因分析: `load_current_dir()` 未在非根目录时合成 ".." 伪条目。
- 修复方案: `CODE/tools/fileman/main.c` `load_current_dir()` 在 path_depth>0 时于列表开头插入 ".." 条目(clus=0 特殊标记),`open_selected()` 特判调用 `go_up()`;同时补充 `go_up()` 前向声明(见 BUG-20260729-012)。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——main.c L67–82 插入 ".." 条目、L264–267 特判、L298–303 go_up 实现均存在。
### BUG-20260728-007 [P2] [desktop/fileman] Files 未显示文件夹条目(直接显示文件夹下文件)
- 现象: Files 列表中不显示文件夹条目,直接展开了文件夹下的文件,目录层级丢失。
- 复现步骤: desktop 打开 Files → 根目录列表中看不到 SYSTEM 等目录条目。
- 串口/日志证据: 无(UI 行为问题)。
- 根因分析: 目录列举/过滤逻辑未正确区分 `is_dir` 条目(attr 0x10)。
- 修复方案: 修复后 `fm_entry.is_dir` 正确填充,目录以黄色图标 + "DIR" 类型 + "--" 大小显示,Enter 进入子目录(`CODE/tools/fileman/main.c`)。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——main.c 列表绘制含 is_dir 分支(图标颜色/类型列),`open_selected()` 目录进入逻辑存在。
### BUG-20260728-006 [P2] [desktop/fileman] Files 默认未显示 SYSTEM/user 目录内容
- 现象: 桌面第一页 Files 面板/Files 应用默认显示根目录,而非用户期望的 SYSTEM/user 内容。
- 复现步骤: 启动至 desktop → 打开 Files → 默认路径为根目录。
- 串口/日志证据: 无(UI 行为问题)。
- 根因分析: fileman 启动时 path_depth=0(根目录),未定位到 SYSTEM/user。
- 修复方案: `CODE/tools/fileman/main.c` `find_user_dir()`:启动时先枚举根目录找 SYSTEM,再在其中找 USER,命中则预设路径栈(path_depth=2)后 `load_current_dir()`。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——main.c L306–339 find_user_dir 实现存在,dsk_entry 中调用。
### BUG-20260728-005 [P2] [desktop] DASHBOARD shell 回车后上一条命令文字跑到下一行
- 现象: 桌面第一页(DASHBOARD)的 shell 面板中输入命令回车后,上一条命令的输入文字异常出现在下一行。
- 复现步骤: desktop DASHBOARD → TERM 面板输入任意命令 → 回车 → 观察回显错位。
- 串口/日志证据: 无(UI 行为问题)。
- 根因分析: `redraw_input_line()` 只更新 framebuffer 视觉渲染,不同步终端缓冲区 `term_ch[]`。当 `term_redraw_all()` 从缓冲区重新渲染时,旧输入文字残留在缓冲区导致错位。
- 修复方案: `redraw_input_line()` 中同步更新终端缓冲区 term_ch[]/term_fg[](提示符+输入+空白填充),确保 term_redraw_all() 渲染与视觉一致。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/shell/main.c redraw_input_line)
- 验证: 编译通过;redraw_input_line 现在同步 term_ch/term_fg 缓冲区。
### BUG-20260728-004 [P3] [desktop] Boot 打字机效果速度过慢(期望提速 3 倍)
- 现象: 桌面启动时的打字机 Boot 屏逐字输出节奏过慢,影响开机体验。
- 复现步骤: 启动至 desktop → 观察 Boot 屏打字速度。
- 串口/日志证据: 无(体验问题)。
- 根因分析: 打字机帧间隔常量偏大。
- 修复方案: 缩小打字机每字符间隔(约 3 倍提速)。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: ROADMAP 近期进展已记录"Boot 打字机提速";QEMU 观感验证。
### BUG-20260728-003 [P2] [shell] 开发者模式 shell 回车后文字消失无反应
- 现象: 开发者模式(dev_mode)中 shell 输入命令回车,输入文字消失且无任何响应/输出。
- 复现步骤: firstInit.txt 第二行=1 或 FUCK dev_mode=1 → DSK 加载 shell.elf → 手动输入命令 → 回车。
- 串口/日志证据: 无(当时未留存)。
- 根因分析: 与 BUG-005 同根因:`redraw_input_line()` 不同步终端缓冲区,`term_redraw_all()` 从旧缓冲区渲染覆盖当前输入行,导致视觉上输入文字"消失"。
- 修复方案: BUG-005 修复(redraw_input_line 同步 term_ch/term_fg 缓冲区)同时解决此问题。
- 状态: [FIXED]
- 修复提交: 工作区已修复(CODE/shell/main.c redraw_input_line — 与 BUG-005 同修复)
- 验证: 编译通过;BUG-005 修复确保终端缓冲区与视觉渲染一致。
### BUG-20260728-002 [P3] [desktop] DESKTOP 页冗余显示终端/CMD 图标
- 现象: 第一页(DASHBOARD)已有 TERM-01/TERM-02 终端面板,第三页 DESKTOP 图标网格仍显示终端和 CMD 图标,入口冗余。
- 复现步骤: desktop → 切到 DESKTOP 页 → 可见终端/CMD 图标。
- 串口/日志证据: 无(UI 设计问题)。
- 根因分析: DESKTOP 页快捷方式列表未排除已在 DASHBOARD 常驻的终端类应用。
- 修复方案: 从 DESKTOP 页图标列表移除终端与 CMD 冗余项。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: ROADMAP 近期进展已记录"DESKTOP 页冗余图标移除";代码核查——desktop 图标过滤逻辑已生效(当前 DESKTOP 页无终端/CMD 图标)。
### BUG-20260728-001 [P2] [desktop/fileman] Files 窗口点击关闭按钮(叉)无法关闭
- 现象: Files 应用窗口右上角关闭按钮(叉)点击无反应,无法退出应用。
- 复现步骤: desktop 打开 Files → 点击右上角关闭区域 → 应用未退出。
- 串口/日志证据: 无(UI 行为问题)。
- 根因分析: 鼠标点击处理未命中关闭按钮热区(右上角 w-28..w-4, y 4..24)。
- 修复方案: `CODE/tools/fileman/main.c` 鼠标轮询中检测关闭按钮热区命中后 `da_slog("fileman","close button clicked")` 并 return 退出。
- 状态: [FIXED]
- 修复提交: 待提交(工作区已修复)
- 验证: 代码核查——main.c L372–377 关闭热区判定与退出存在。
---
## 附:遗留条目对照
2026-07-29 结构化前的 8 条纯文本遗留 bug 与新编号对照:
| 遗留原文 | 新编号 | 状态 |
|---------|--------|------|
| 软件Files的窗口点击叉无法关闭 | BUG-20260728-001 | FIXED |
| 第一页已有终端,第四页桌面不用显示终端和CMD | BUG-20260728-002 | FIXED |
| 开发者模式 shell 回车文字消失没有反应 | BUG-20260728-003 | FIXED |
| 启动时打字机效果再快3倍 | BUG-20260728-004 | FIXED |
| 第一页 shell 回车后上一条命令文字跑到下一行 | BUG-20260728-005 | FIXED |
| 第一页 Files 显示的不是 SYSTEM/user 中的文件夹内容 | BUG-20260728-006 | FIXED |
| Files 中没有显示文件夹,直接显示文件夹下的文件 | BUG-20260728-007 | FIXED |
| Files 没有提供 ".." 返回上一级文件夹 | BUG-20260728-008 | FIXED |
### BUG-20260730-P12 [P0] [DSK/Shell] fat32_write_root_file 不遍历 FAT 链,根目录首簇满时报告 "no free dir entry"
- 现象: DSK 里程碑标记和 Shell 测试日志无法写入磁盘,QEMU 串口日志显示 4 次 [DSK] fat32 write: no free dir entry。
- 复现步骤: 使用 SATA FAT32 测试镜像启动(根目录首簇 16 条目全部占用),任何需要创建新文件的写入操作均失败。
- 串口/日志证据: QEMU serial.log 中 4 次 [DSK] fat32 write: no free dir entry。
- 根因分析: at32_write_root_file 只扫描根目录的第一个簇(spc=1 时仅 16 条目),不跟随 FAT 链检查后续簇。SATA 测试镜像根目录 FAT 链为 2→3→EOF,首簇满但第二簇有 10 个空闲条目,代码未到达第二簇。
- 修复方案: 修改 DSK 和 Shell 的 at32_write_root_file,用 while 循环遍历完整 FAT 链搜索空闲条目,记录 ree_clus/ ound_clus,写回时使用活动簇而非硬编码 ␍oot_clus。
- 状态: [FIXED]
- 修复文件: CODE/dsk/main.c (L589-655), CODE/shell/main.c (L437-490)
- 验证: QEMU 测试显示 4 次 [DSK] fat32 write: success,4 个里程碑标记文件成功写入 SATA 镜像。
### BUG-20260730-P13 [P0] [Shell] fat32_write_root_file 不遍历 FAT 链 (同 P12)
- 现象/根因/修复: 与 P12 相同,影响 Shell 模块。
- 状态: [FIXED]
- 修复文件: CODE/shell/main.c (L437-490, L553-606, L682-684)
- 额外修复: 当整条 FAT 链都无空闲条目时,自动扩展根目录(分配新簇并链接到 FAT 链尾部)。
- 验证: 同 P12。
### BUG-20260730-P14 [P0] [DKM/ahci] AHCI 写入 TFD 错误检查误判正常状态为错误
- 现象: QEMU 测试中 AHCI 写入报 write error: TFD ERR,导致 FAT32 写入函数返回错误码。实际写入可能成功但因错误检查提前返回,后续 FLUSH CACHE 被跳过。
- 复现步骤: QEMU q35 + SATA 驱动,任何 AHCI 写入操作。
- 串口/日志证据: [WARN] [ahci] write error: TFD ERR,TFD 值为 0x50(DRDY|DRQ,正常状态)。
- 根因分析: hci.c L376 的错误检查 if (tfd & 0xFF) 检查的是 STS 低字节是否非零。但 STS=0x50 (DRDY=1, DRQ=1) 是 DMA 完成后的正常状态,不是错误。正确的检查应只看 STS bit0 (ERR) 和 ERR 寄存器 (bits15:8)。
- 修复方案: 改为 u8 sts = (u8)(tfd & 0xFF); u8 err_reg = (u8)((tfd >> 8) & 0xFF); if ((sts & 0x01) || err_reg)。
- 状态: [FIXED]
- 修复文件: CODE/DKM/ahci/ahci.c (L372-386)
- 验证: QEMU 测试无 TFD ERR 错误,4 次 FAT32 写入成功,里程碑文件正确写入磁盘。
+290
View File
@@ -0,0 +1,290 @@
# Deaicup Software 运行环境决策报告
## 1. 概述
Deaicup Software(`CODE/Deaicup Software/`)是一个基于 Rust egui/eframe 框架的 GUI 应用,
目标是在 Deshab 系统上运行。本文基于对 Deaicup 全部源码(`Cargo.toml`、`Cargo.lock`、
`src/main.rs`)的实际阅读结果,评估三条运行路径:
- **路径 A**:Rust/egui 原生移植到 SAS-R0 freestanding 环境
- **路径 B**:在 Linux 兼容层 guest 内运行,Deshab 桌面通过 IPC 共享内存拉帧显示(类 WSLg)
- **路径 C**:用 Deshab 原生 deshab_ui.h 以 C 语言重写
**结论速览**:推荐**路径 B**。Deaicup 当前唯一的非平凡运行时依赖是 HTTPS 图片加载,
而这恰好是 Linux guest 已具备(virtio-net 数据路径闭环)而 SAS-R0 原生完全不具备的能力;
路径 B 全部构件(IPC ring、payload_pool、park-and-resume、文件传输)均已在 Phase 3 验证,
只需新增"显示共享内存 + 输入转发"两个模块。路径 A 需要为 egui 0.27 移植 Rust std 子集,
工作量与风险均为最高;路径 C 当前改写量小但意味着放弃上游 Rust 代码库并长期自行演进 GUI 组件库。
## 2. Deaicup Software 源码事实
### 2.1 项目结构与依赖
项目根目录为 `CODE/Deaicup Software/src/`(注意多一层 `src`),全部源码仅一个文件
`src/src/main.rs`(102 行)。`Cargo.toml` 关键事实:
```toml
[package]
name = "Deaicup-Software"
version = "0.1.0"
edition = "2024"
[[bin]]
windows-subsystem = "windows" # Windows GUI 子系统,无控制台窗口
[dependencies]
egui = "0.27"
eframe = "0.27"
egui_extras = { version = "0.27", features = ["all_loaders"] }
```
`Cargo.lock` 锁定的实际版本与传递依赖(与本决策相关的部分):
| 组件 | 锁定版本 | 说明 |
|---|---|---|
| egui / epaint / emath / ecolor | 0.27.2 | 即时模式 GUI 核心,epaint 负责形状 tessellation |
| eframe | 0.27.2 | 应用框架(窗口/事件循环/后端胶水) |
| egui-wgpu + wgpu | 0.27.2 / 0.19.4 | eframe 默认 feature 之一:wgpu 后端 |
| egui_glow + glow + glutin | 0.27.2 / 0.13.1 / 0.31.3 | eframe 默认 feature 之二:OpenGL 后端 |
| winit | 0.29.15 | 窗口与输入抽象(经 egui-winit) |
| egui_extras | 0.27.2 | `all_loaders` 引入图片加载器链 |
| ehttp | 0.5.0 | HTTP 图片加载;native 下走 ureq |
| ureq + rustls + webpki-roots | 2.12.1 / 0.23.42 / 0.26.11+1.0.9 | HTTPS 协议栈(纯 Rust TLS) |
| image | 0.24.9 | PNG/JPEG 等解码 |
| resvg / usvg | (lock 内) | SVG 加载器 |
| arboard / smithay-clipboard | 3.6.1 | 剪贴板(egui-winit 依赖) |
| accesskit | 0.12.3 | 无障碍(egui 默认依赖) |
关键推论:**eframe 使用的是默认 features,wgpu 与 glow 两个 GPU 后端都在依赖树中**,
且运行时会优先尝试 wgpu(需要 Vulkan/GL/DX),在无 GPU 环境下回退 glow + glutin
(仍需要 OpenGL 实现)。**两个后端都不可能在无 GPU、无 Mesa 的环境中工作**,
因此无论走哪条路径,最终都必须绕开 eframe 的 GPU 后端,直接用 egui + epaint
自写软件渲染集成(epaint 输出的是与后端无关的三角形网格 + 字体图集,可纯 CPU
光栅化到像素缓冲)。
### 2.2 功能点清单(main.rs 逐点核对)
1. **窗口**:`eframe::run_native`,标题 "Deaicup Software",初始内尺寸 800×600,
单 viewport,无子窗口、无边框定制。
2. **启动回调**:`egui_extras::install_image_loaders(&cc.egui_ctx)` —— 注册
HTTP/文件/SVG 图片加载器。
3. **顶部横幅**:`TopBottomPanel::top`,`ui.image()` 直接加载
**`https://deaicup.com/DeaicupSoftware/top.png`**,拉伸到面板宽 × 75px。
这是全程序**唯一的网络访问点**,也是 egui_extras `all_loaders` 存在的理由。
4. **左侧导航**:`SidePanel::left`,默认宽 200、可拖拽;7 个 `selectable_label`。
5. **中央页面**:`match current_page`,7 个页面(Home/Tool/Game/LinuxWare/
WindowsWare/Set/About)**目前全部是占位 stub**(仅一个 `ui.label`)。
6. **状态**:仅 `current_page: Page` 一个字段(`#[derive(Default)]`)。
**无文件系统访问、无持久化、无多线程、无系统调用级需求**。
### 2.3 运行需求推导
| 需求 | 事实 | 备注 |
|---|---|---|
| 渲染 | epaint 三角形网格 + RGBA 字体图集 | 软件光栅化即可,无需 GPU |
| 浮点 | egui/epaint 大量使用 f32 | x86_64 Rust 基线要求 SSE2 |
| 堆分配 | 大量小对象(Vec/HashMap/String) | 需要 alloc 级分配器 |
| 时间 | `std::time::Instant`(帧间隔/动画) | TSC 即可提供 |
| 网络 | 仅一处 HTTPS GET(top.png)+ PNG 解码 | ureq + rustls + image crate |
| 文件系统 | **无** | 图片加载器支持 file:// 但代码未用 |
| 窗口 | 单个 800×600 viewport | 无拖拽系统窗口需求 |
| 输入 | 鼠标(移动/点击/滚轮)+ 键盘 | egui RawInput 事件 |
| 剪贴板 | egui-winit 默认接入 | 可降级为 no-op |
## 3. 三条路径评估
### 3.1 路径 A:Rust/egui 原生移植到 SAS-R0
#### 需要做的事
1. **Rust 工具链与运行时**:新建 `x86_64-unknown-none`(或自定义 target JSON)
交叉编译配置。前置条件:内核启用 FPU/SSE(当前早期启动约束为
`-mno-sse -mno-sse2 -mno-mmx -msoft-float`,x86_64 Rust 基线硬性要求 SSE2)、
提供 `alloc_error_handler`、`panic_handler`、全局分配器。
2. **std 移植(最大障碍)**:egui 0.27 **不是 no_std crate**。其依赖树中
emath/ecolor 可 no_std,但 egui/epaint 本体使用 `std::collections`
(经 ahash)、`std::time::Instant`、线程相关原语等。可行的做法是为
Deshab 写一个 Rust std 的 OS 层(类似各 hobby OS 的 std port),把线程/
Mutex 降级为单线程 stub、IO 降级为无操作、时间接 TSC。这是一块独立的、
以月计的基础设施工程,且与 egui 版本升级长期耦合。
3. **软件光栅器**:egui 0.27 不内置 CPU 光栅器,需自写 epaint 网格 → RGBA
光栅化(含字体图集采样、抗锯齿三角填充),或移植 tiny-skia。
约 500–1500 行,技术风险低但属于新增维护面。
4. **集成层**:PS/2 鼠标/键盘 → `egui::RawInput`;framebuffer 呈现;与
desktop.elf 窗口系统对接(需要新的 .elf 加载约定:Rust rlib 静态链接成
PIE ELF,或走 dt_load_elf 兼容的 PIE 布局)。
5. **网络图片**:`https://deaicup.com/...` 需要 TCP + TLS + PNG。SAS-R0 原生
网络栈尚不存在(e1000/virtio-net 仅到驱动探测/数据路径规划阶段),
rustls 移植又依赖路径 A 的 std 移植。**短期只能砍掉横幅或改为内嵌静态图**。
#### 评估
- **工作量**:极大(Rust std port 2–4 人月起步,光栅器 + 集成另计)。
- **技术风险**:高。std port 与 egui 版本强耦合;SAS-R0 单地址空间 Ring0 下
Rust 的安全假设(如线程模型)全部需要重新审视;任一环节卡住则整体不可用。
- **收益**:长期战略收益大(为 Deshab 打开 Rust 生态),但对"运行 Deaicup"
这一目标而言投入产出比最低。
- **对 SAS-R0 侵入性**:高(SSE 全局启用、新 loader 约定、分配器进驻内核地址空间)。
### 3.2 路径 B:Linux guest 内运行 + IPC 拉帧(类 WSLg)
#### 架构
```
┌──────────────── Deshab (UTSM + desktop.elf) ────────────────┐
│ 桌面窗口 "Deaicup" ──blit──▶ 显示 SHM(guest 渲染的 RGBA) │
│ PS/2 键鼠 ──ring 消息──▶ linux_to_utsm / utsm_to_linux │
└──────────────▲──────────────────────────────▲───────────────┘
│ 帧呈现(payload_pool 或新 SHM 区)│ 输入事件
┌──────────────┴───────── Linux guest ─────────┴───────────────┐
│ Deaicup (egui + epaint + 软件光栅器) │
│ └─ 渲染到 SHM RGBA 缓冲,HLT park(与现有模型一致) │
│ └─ HTTPS top.png 经 virtio-net + rustls 正常加载 │
└───────────────────────────────────────────────────────────────┘
```
#### 与现有基础设施的契合点(全部已验证)
- **IPC 通道**:`CODE/utsm-ipc/ipc_proto.h` 已定义双向 ring(64 条 × 256B)
+ VMCALL hypercall + park-and-resume 调度模型;exec 协议
(`UTSM_MSG_EXEC_*`)和 Phase 3 文件传输(`UTSM_MSG_FILE_*` +
payload_pool 分块)已在用。输入转发只需新增一对消息类型
(如 `UTSM_MSG_INPUT_EVENT`),协议结构有成熟先例可循。
- **大块数据传输**:payload_pool 为 1015712 B(≈0.97 MiB),但
800×600 RGBA 整帧 = 1 920 000 B ≈ 1.83 MiB,**单帧放不进现有 pool**。
两个选项:
1. **新增专用显示 SHM 区**(推荐):当前 1MB 区布局是 packed 且 EPT 只映射
该区;另开一个 GPA(如 0x05000000)映射 8 MiB,头部放
宽高/stride/脏矩形序列号 + 双缓冲 RGBA,可支撑到 1920×1080。
EPT 映射完全由 UTSM 控制,改动局部。
2. 沿用 1MB pool + 脏矩形/行带分块:无需动 EPT,但 60fps × 每帧多次
park-resume 的世界切换开销大,仅适合作为过渡。
- **调度模型**:现有模型是同步 park-and-resume(daemon 干完活 HLT,UTSM 的
`linux_resume()` 返回)。GUI 应用可自然映射为**逐帧步进**:桌面事件循环
每 tick(30/60Hz)把累积的输入事件写入 ring → `linux_resume()` →
guest 内 egui 跑一帧(begin_frame/处理输入/update/光栅化到 SHM)→ HLT。
无需引入 VMX preemption timer 等连续调度机制,与现有代码零冲突。
将来若要多 Linux 应用常驻,再升级为抢占式调度。
- **网络**:virtio-net 数据路径已闭环,Arch rootfs + pacman 可用,意味着
guest 内 TCP/DNS 已工作;ureq + rustls 为纯 Rust 进程内 TLS,不依赖系统
组件,`top.png` 的 HTTPS 加载**无需任何改动**。
#### 需要新增的事
1. **guest 侧 egui 软件集成**(替换 eframe):Deaicup 的 eframe 仅承担
事件循环 + 后端,代码只有 ~80 行业务 UI。写一个自定义 runner:
egui 0.27 + epaint + 软件光栅器(guest 内可直接用 crates.io 的
tiny-skia,天然纯 CPU)+ SHM 输出 + ring 输入。约 300–500 行新代码,
建议以 `deshab` feature flag 形式留在同一仓库,桌面开发仍用 eframe 跑。
2. **显示 SHM 区**:UTSM 侧 EPT 映射 + guest 内核补丁(utsm-ipc patch 已有
先例)映射同一 GPA 到用户态(mmap)。
3. **Deshab 桌面"远程窗口"**:desktop.elf 新增一种窗口类型,内容区定期从
显示 SHM blit(1.8MB memcpy 亚毫秒级),标题栏/关闭/最小化走现有
窗口管理;窗口几何变化通过 ring 通知 guest 端 resize。
4. **输入协议**:PS/2 键鼠 → 结构化事件(x/y/按键/滚轮/修饰键)→ guest
翻译成 `egui::RawInput`。
5. **生命周期**:窗口打开 → EXEC_REQUEST 启动 Deaicup;窗口关闭 →
新消息通知 guest 端优雅退出。
#### 评估
- **工作量**:中等(显示 SHM + 输入转发 + guest runner + 桌面远程窗口,
估计 2–4 周),且每一块都是现有已验证模式的增量。
- **渲染性能**:guest 内软件光栅 800×600 约数毫秒/帧;UTSM 侧 blit 为纯
memcpy;逐帧步进延迟对工具类 GUI 完全可接受。
- **桌面集成度**:内容级集成(窗口内像素来自 guest),装饰/管理原生。
- **可维护性**:Deaicup 源码几乎不改,上游演进零成本同步;该通道同时是
**未来所有 Linux GUI 应用的通用运行时**(WSLg 式复用)。
- **对 SAS-R0 侵入性**:低(新增 EPT 映射与一种桌面窗口类型,不动内核核心)。
- **风险**:中。唯一的新技术点是 epaint 软件光栅(guest 内用 tiny-skia
已大幅降险);其余皆已验证。
### 3.3 路径 C:deshab_ui.h 原生重写
#### 需要做的事
以 `CODE/tools/` 独立 .elf 模式(`dsk_entry` 入口,参照 `tool_apps_design.md`
ABI),用 deshab_ui.h 的 du_* 原语重写当前 102 行功能:
- 顶部横幅:top.png 需内嵌为静态 RGBA(DSK Logo 已有嵌入先例)或暂时省略
—— **HTTP 拉取在 C 路径下不可行**(无 TCP/TLS)。
- 侧边导航:`du_draw_button` / `du_kate_panel` + 选中态手绘。
- 7 个 stub 页面:空白页,工作量近似为零。
#### 评估
- **工作量**:当前极小(功能对等约 1–2 天)。**但这是静态快照**:
Deaicup 的 Tool/Game/LinuxWare/WindowsWare 页面注定会长出列表、滚动区、
文本输入、图片网格等真实组件,deshab_ui.h 没有布局引擎与滚动容器,
每加一个组件就是一次手工绘制 + 命中测试,长期成本线性增长。
- **与上游关系**:永久分叉。上游 Rust 仓库的每次改动都需要手工重新移植,
Deaicup 本质上是"同一个产品的两套实现"。
- **渲染性能**:最好(零拷贝直绘 framebuffer)。
- **桌面集成度**:最好(原生 .elf 工具)。
- **对 SAS-R0 侵入性**:无。
- **定位**:适合作为**桌面壳层入口**(图标、启动器、About 页),不适合
承担 Deaicup 本体。
## 4. 三路径对比矩阵
| 维度 | A 原生移植 | B Linux guest + IPC | C 原生重写 |
|---|---|---|---|
| 当前可见工作量 | 极大(2–4 人月+) | 中(2–4 周) | 极小(1–2 天) |
| 长期维护成本 | 高(std port 随 egui 演进) | 低(源码基本不改) | 高(组件库自建 + 双实现同步) |
| 技术风险 | 高 | 中(构件均已验证) | 低 |
| 渲染性能 | 好 | 中(memcpy blit,工具类足够) | 最好 |
| 桌面集成度 | 高(做成后) | 中(窗口内容级) | 最高 |
| 网络图片(top.png) | 不可行(无 TCP/TLS) | 原生可用(rustls 进程内 TLS) | 不可行 |
| 对 SAS-R0 侵入性 | 高 | 低 | 无 |
| 复用价值 | Rust 生态入口 | **通用 Linux GUI 运行时** | 无 |
| 上游代码同步 | 保留源码 | 保留源码 | 永久分叉 |
## 5. 推荐方案与分阶段实施
**推荐路径 B**,并以路径 C 的元素做桌面壳层入口(图标/启动器仍用原生 .elf
工具实现)。路径 A 仅在未来确有"原生 Rust 应用生态"战略需求时另行立项,
不为 Deaicup 启动。
### Phase 0:协议与集成设计(约 2–3 天)
- 在 `ipc_proto.h` 中新增:`UTSM_MSG_INPUT_EVENT`(键鼠事件载荷)、
`UTSM_MSG_DISPLAY_*`(帧就绪/resize/关闭握手)消息类型。
- 确定显示 SHM 区方案:新 GPA(建议 0x05000000,8 MiB,双缓冲 + 脏矩形头)。
- Deaicup 仓库引入 `deshab` feature:默认仍用 eframe(Windows/Linux 桌面
开发体验不变),feature 开启时切换到自定义 runner。
### Phase 1:guest 内 egui 软件渲染(约 1 周)
- 编写 Deaicup 的 deshab runner:egui 0.27 + epaint + tiny-skia 软件光栅
→ 显示 SHM RGBA;从 ring 读取输入事件构造 `egui::RawInput`。
- guest 内核侧把显示 SHM GPA mmap 到用户态(utsm-ipc patch 扩展)。
- 验证:guest 内独立运行,渲染固定测试帧写入 SHM,UTSM 侧 hexdump 校验。
### Phase 2:桌面远程窗口 + 输入转发(约 1–2 周)
- UTSM:EPT 映射显示 SHM;desktop.elf 新增远程窗口类型(内容区 blit SHM、
几何变化通知 guest)。
- 输入转发:桌面焦点窗口的 PS/2 事件序列化为 `UTSM_MSG_INPUT_EVENT`。
- 逐帧步进调度:桌面 tick → 写输入 → `linux_resume()` → guest 渲染一帧
→ HLT → blit。
- 端到端验证:Deaicup 在 Deshab 桌面窗口内可点击导航、横幅 HTTPS 图片
经 virtio-net 加载成功。
### Phase 3:完善(按需)
- 脏矩形增量 blit、窗口 resize 联动、关闭握手与进程回收、剪贴板互通。
- 多窗口/多 Linux GUI 应用(届时评估 VMX preemption timer 连续调度)。
- 性能优化:blit 与 guest 渲染重叠、帧率自适应(无输入无脏帧时降频)。
## 6. 风险与开放问题
1. **epaint 软件光栅质量**:egui 0.27 无官方 CPU 后端,tiny-skia 光栅化
epaint 网格需自行实现三角形 + 图集 UV 采样(社区有 egui_tsk 等先例,
风险可控),需在 Phase 1 早期做视觉验证(圆角/文字抗锯齿)。
2. **park-and-resume 帧率上限**:逐帧步进的世界切换成本决定帧率天花板,
工具类 GUI 30fps 目标无虞;若未来要跑视频/游戏类负载,必须升级为
抢占式 guest 调度。
3. **egui 版本演进**:Deaicup 锁定 0.27.2;自定义 runner 只依赖
egui/epaint 公共 API(Context/RawInput/ClippedPrimitive),升级面小。
4. **横幅图片离线场景**:guest 无网络时 top.png 加载失败,egui 会显示
占位——行为与桌面版一致,无需特殊处理。
+446
View File
@@ -0,0 +1,446 @@
# Deshab xHCI USB 驱动设计文档
## 1. 概述
xHCI (eXtensible Host Controller Interface) 是 USB 3.0+ 的标准主机控制器接口规范,统一支持 USB 1.1/2.0/3.x 设备,取代 UHCI/OHCI/EHCI。本驱动实现 xHCI 控制器驱动 + USB 大容量存储设备支持,使 Deshab 内核可通过 USB 存储设备(U 盘)读写数据。
### 1.1 驱动定位
```text
驱动名称: xhci
驱动类: DKM_CLASS_STORAGE (6)
加载阶段: stage1 (boot)
依赖: pci, irq
提供: block
输出文件: SYSTEM/driver/block/xhci.drv
```
### 1.2 实现范围
- xHCI 控制器发现、复位、启动
- Command Ring / Event Ring / ERST 管理
- USB 设备端口检测、重置、枚举
- 设备描述符 / 配置描述符读取
- Bulk-Only Transport (BOT) 大容量存储协议
- SCSI Transparent Command Set: INQUIRY / READ CAPACITY / READ(10) / WRITE(10)
- 块设备注册 (kernel_api.block)
### 1.3 不在范围内
- USB 3.x LPM (Link Power Management)
- USB 2.0 TT (Transaction Translator)
- Isochronous / Interrupt 传输
- 多设备并发(首版只枚举第一个 MSC 设备)
- USB Hub(假设设备直连控制器端口)
- xHCI Stream IDs
- MSI/MSI-X 中断(首版使用同步轮询)
---
## 2. xHCI 架构概述
### 2.1 寄存器布局
```text
MMIO Base
├── Capability Registers (0x00 - CAPLEN)
│ ├── HCSPARAMS1: MaxSlots, MaxIntrs, MaxPorts
│ ├── HCSPARAMS2: Scratchpad, MaxERST, IST, SPB
│ ├── HCCPARAMS1: ContextSize, ExtCapPtr
│ ├── DBOFF: Doorbell Register Offset
│ └── RTSOFF: Runtime Register Space Offset
│
├── Operational Registers (CAPLEN - 0x3FF)
│ ├── USBCMD: RS, RST, INTE
│ ├── USBSTS: HCH, EINT, CNR
│ ├── CRCR: Command Ring Control
│ ├── DCBAAP: Device Context Base Array Ptr
│ └── CONFIG: MaxSlotsEn
│
├── Port Registers (0x400 - 0x7FF)
│ └── PORTSC[n]: CCS, PED, PR, PLS, Speed
│
├── Runtime Registers (RTSOFF - )
│ ├── MFINDEX: Microframe Counter
│ └── IR[0]: IMAN, IMOD, ERSTSZ, ERSTBA, ERDP
│
└── Doorbell Registers (DBOFF - )
└── DB[n]: Doorbell for Slot n
```
### 2.2 数据结构关系
```text
DCBAA (Device Context Base Address Array)
├── [0]: Scratchpad Buffer Array Phys
├── [1]: Device Context 1 Phys
├── [2]: Device Context 2 Phys
└── ...
Device Context
├── Slot Context (32/64 bytes)
└── Endpoint Context[0-31] (32/64 bytes each)
└── Transfer Ring Dequeue Pointer
Command Ring (Circular)
├── TRB[0]: Enable Slot Command
├── TRB[1]: Address Device Command
├── ...
└── TRB[N-1]: Link TRB → TRB[0]
Event Ring (Circular)
├── TRB[0]: Command Completion Event
├── TRB[1]: Transfer Event
├── TRB[2]: Port Status Change Event
├── ...
└── TRB[N-1]: Link TRB → TRB[0]
Transfer Ring (per Endpoint, Circular)
├── TRB[0]: Setup Stage / Normal
├── TRB[1]: Data Stage / Normal
├── TRB[2]: Status Stage / Normal
├── ...
└── TRB[N-1]: Link TRB → TRB[0]
```
---
## 3. TRB 格式
### 3.1 通用 TRB (16 字节)
```text
Offset Size Field
0x00 8 Parameter (数据指针 / 命令参数)
0x08 4 Status (传输长度 / 完成码)
0x0C 4 Control (TRB Type[15:10], Cycle Bit[0], Flags)
```
### 3.2 Cycle Bit 机制
- **Producer Cycle State (PCS)**: 写入方维护的 cycle 值
- **Consumer Cycle State (CCS)**: 读取方维护的 cycle 值
- TRB 有效条件: `TRB.CycleBit == Ring.CCS`
- 环绕时翻转 Cycle State,实现无锁生产者-消费者同步
### 3.3 关键 TRB 类型
| Type | 名称 | 用途 |
|------|------|------|
| 0x01 | Normal | Bulk/Interrupt 数据传输 |
| 0x02 | Setup Stage | Control 传输 Setup 阶段 |
| 0x03 | Data Stage | Control 传输 Data 阶段 |
| 0x04 | Status Stage | Control 传输 Status 阶段 |
| 0x06 | Link | 环链接,指向下一段 |
| 0x09 | Enable Slot | 分配设备 Slot |
| 0x0A | Disable Slot | 释放设备 Slot |
| 0x0B | Address Device | 设置 USB 设备地址 |
| 0x0C | Configure Endpoint | 配置设备端点 |
| 0x17 | No-Op | 空操作 |
### 3.4 事件 TRB 类型
| Type | 名称 | 用途 |
|------|------|------|
| 0x01 | Transfer Event | 传输完成通知 |
| 0x02 | Command Completion | 命令完成通知 |
| 0x04 | Port Status Change | 端口状态变化 |
---
## 4. USB 设备枚举流程
```text
1. 检测端口连接 (PORTSC.CCS = 1)
2. 端口重置 (PORTSC.PR = 1 → 等待 PED = 1)
3. 读取端口速度 (PORTSC.Speed)
4. Enable Slot → 获得 Slot ID
5. 分配 Input Context + Transfer Ring (EP0)
6. Address Device (BSR=0) → 设备获得 USB 地址
7. Control Transfer (EP0): GET_DESCRIPTOR(Device)
8. 重新 Address Device (使用真实 MaxPacketSize)
9. Control Transfer (EP0): GET_DESCRIPTOR(Configuration)
10. Configure Endpoint (Bulk IN/OUT)
11. 设备就绪,可进行 Bulk 传输
```
---
## 5. BOT (Bulk-Only Transport) 协议
### 5.1 CBW (Command Block Wrapper, 31 字节)
```text
Offset Size Field
0x00 4 Signature: 0x43425355 ("USBC")
0x04 4 Tag: 递增标签(用于匹配 CSW)
0x08 4 Data Transfer Length: 期望数据字节数
0x0C 1 Flags: 0x80=Device-to-Host (IN), 0x00=Host-to-Device (OUT)
0x0D 1 LUN: 逻辑单元号
0x0E 1 CB Length: 命令块长度 (1-16)
0x0F 16 CB: SCSI 命令块
```
### 5.2 CSW (Command Status Wrapper, 13 字节)
```text
Offset Size Field
0x00 4 Signature: 0x53425355 ("USBS")
0x04 4 Tag: 匹配 CBW Tag
0x08 4 Data Residue: 未传输字节数
0x0C 1 Status: 0=OK, 1=Failed, 2=Phase Error
```
### 5.3 传输流程
```text
OUT (Bulk EP): CBW →
IN/OUT (Bulk EP): Data (如有) →
IN (Bulk EP): CSW
```
---
## 6. SCSI 命令集
### 6.1 INQUIRY (0x12)
```text
CB: [0x12, 0x00, 0x00, 0x00, allocation_length, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Response: 36 字节标准 INQUIRY 数据
- Byte 0: Peripheral Device Type (0x00 = Direct Access Block)
- Bytes 8-15: Vendor Identification
- Bytes 16-31: Product Identification
- Bytes 32-35: Product Revision Level
```
### 6.2 READ CAPACITY(10) (0x25)
```text
CB: [0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Response: 8 字节
- Bytes 0-3: Maximum LBA (big-endian)
- Bytes 4-7: Block Size (big-endian, typically 512)
```
### 6.3 READ(10) (0x28)
```text
CB: [0x28, 0x00, LBA[3], LBA[2], LBA[1], LBA[0], 0x00, TransferLength[1], TransferLength[0], 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Direction: Device-to-Host (IN)
Data: TransferLength × BlockSize 字节
```
### 6.4 WRITE(10) (0x2A)
```text
CB: [0x2A, 0x00, LBA[3], LBA[2], LBA[1], LBA[0], 0x00, TransferLength[1], TransferLength[0], 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
Direction: Host-to-Device (OUT)
Data: TransferLength × BlockSize 字节
```
---
## 7. Context 格式
### 7.1 Slot Context (32 或 64 字节)
```text
DW0: Route String[23:0], Speed[29:27], MTT[31:30]
DW1: Max Packet Size[15:0], Device Address[31:24]
DW2: TT Hub Slot ID[7:0], TT Port Number[15:8], TTT[19:18], Rsv[23:20], Context Entries[31:27]
DW3: Exit Latency[19:0], Slot State[31:27]
```
### 7.2 Endpoint Context (32 或 64 字节)
```text
DW1: EP Type[3:0], Max Packet Size[31:16]
DW2: Max ESIT Payload Lo
DW4: Average TRB Length[15:0], Max Burst[31:24]
DW6: Dequeue Pointer Low (Transfer Ring)
DW7: Dequeue Pointer High
```
### 7.3 Input Context
```text
Offset 0: Input Control Context (32/64 bytes) - 位图指示哪些 Context 有效
Offset 32/64: Slot Context
Offset 64/128: Endpoint 0 Context
Offset 96/192: Endpoint 1 Context (Bulk OUT)
Offset 128/256: Endpoint 2 Context (Bulk IN)
```
---
## 8. USB 描述符格式
### 8.1 设备描述符 (18 字节)
```text
Offset Size Field
0 1 bLength: 18
1 1 bDescriptorType: 1
2 2 bcdUSB: USB 版本 (0x0200 = USB 2.0)
4 1 bDeviceClass: 设备类 (0x08 = Mass Storage)
5 1 bDeviceSubClass
6 1 bDeviceProtocol
7 1 bMaxPacketSize0: EP0 最大包大小 (8/16/32/64)
8 2 idVendor
10 2 idProduct
12 2 bcdDevice
14 1 iManufacturer
15 1 iProduct
16 1 iSerialNumber
17 1 bNumConfigurations
```
### 8.2 配置描述符 (9 字节) + 接口描述符 (9 字节) + 端点描述符 (7 字节×N)
```text
配置描述符:
bLength=9, bDescriptorType=2, wTotalLength, bNumInterfaces,
bConfigurationValue, bmAttributes, bMaxPower
接口描述符:
bLength=9, bDescriptorType=4, bInterfaceNumber, bAlternateSetting,
bNumEndpoints, bInterfaceClass(0x08=MSC), bInterfaceSubClass(0x06=SCSI),
bInterfaceProtocol(0x50=BOT)
端点描述符:
bLength=7, bDescriptorType=5, bEndpointAddress(0x81=Bulk IN, 0x02=Bulk OUT),
bmAttributes(0x02=Bulk), wMaxPacketSize, bInterval
```
---
## 9. Control Transfer Setup Packet (8 字节)
```text
Offset Size Field
0 1 bmRequestType: 数据方向 + 类型 + 接收者
1 1 bRequest: 请求码
2 2 wValue: 参数
4 2 wIndex: 索引
6 2 wLength: 数据长度
GET_DESCRIPTOR(Device):
bmRequestType = 0x80 (Device-to-Host, Standard, Device)
bRequest = 0x06 (GET_DESCRIPTOR)
wValue = 0x0100 (Device Descriptor, Index 0)
wIndex = 0x0000
wLength = 0x0012 (18 bytes)
GET_DESCRIPTOR(Configuration):
bmRequestType = 0x80
bRequest = 0x06
wValue = 0x0200 (Configuration Descriptor, Index 0)
wIndex = 0x0000
wLength = 0x00FF (255 bytes, 足够包含完整配置)
```
---
## 10. USB Legacy Support (BIOS Handoff)
### 10.1 Extended Capability
```text
HCCPARAMS1[31:16] → ExtCapPtr (以 32-bit 字为单位的偏移)
ExtCap 结构:
DW0: ID[7:0], Next[15:8] (下一能力偏移,0=结束)
ID=1: USB Legacy Support Capability
DW0: ID=1, Next
DW1: USBLEGSUP (BIOS/OS Ownership Semaphore)
bit[0-15]: BIOS Semaphore (BIOS 写 1 占用)
bit[16-31]: OS Semaphore (OS 写 1 请求所有权)
DW2: USBLEGCTLSTS (Legacy Control)
```
### 10.2 Handoff 流程
```text
1. 读取 USBLEGSUP
2. 若 BIOS Semaphore (bit[0]) = 1:
a. 写 USBLEGSUP: OS Semaphore (bit[16]) = 1
b. 等待 BIOS Semaphore 清除 (超时 1s)
c. 写 USBLEGCTLSTS: 清除所有 SMI 触发位
```
---
## 11. 错误处理与安全不变式
### 11.1 安全不变式
1. **不阻塞启动**:任何 xHCI 初始化步骤失败,只记日志,驱动返回 0(成功),不注册块设备
2. **超时保护**:所有硬件等待使用 TSC deadline,无无限循环
3. **Cache 一致性**:写入 DMA 缓冲区后执行 `wbinvd`
4. **DMA 缓冲区**:所有 DMA 内存通过 `kernel_api.dma->alloc_pages` 分配(低 4G,4K 对齐)
5. **端口重置安全**:重置超时后跳过该端口,不阻塞其他端口枚举
### 11.2 Completion Code 映射
| Completion Code | 含义 | 处理 |
|----------------|------|------|
| 0 | Success | 正常完成 |
| 1 | USB Transaction Error | 重试或报错 |
| 2 | Babble Detected | 报错 |
| 3 | USB Ring Stall | 端点 Stall,需 Clear Feature |
| 4 | Stop | 停止 |
| 5 | Short Packet | 部分完成,检查 residue |
| 13 | Event Data | 含事件数据 |
| 22 | Stream Protocol Error | 报错 |
---
## 12. 性能考虑
1. **TRB Ring 大小**:Command Ring 256 TRB,Event Ring 256 TRB,Transfer Ring 256 TRB
2. **Bulk 传输最大包**:USB 2.0 High-Speed = 512 字节,USB 3.0 Super-Speed = 1024 字节
3. **SCSI 传输粒度**:每次 READ(10)/WRITE(10) 最多 16 扇区 (8KB),与 AHCI 一致
4. **轮询 vs 中断**:首版使用 Event Ring 同步轮询 + TSC deadline,后续升级 MSI-X 中断
5. **多 TRB 链式传输**:大数据传输拆分为多个 Normal TRB,最后一次设置 IOC
---
## 13. QEMU 测试配置
```text
# xHCI 控制器 + USB 大容量存储
-device qemu-xhci,id=xhci0
-device usb-storage,bus=xhci0.0,drive=usb0
-drive id=usb0,if=none,file=usb_test.img,format=raw
# USB 测试镜像创建 (FAT32, 64MB)
# Windows:
# fsutil file createnew usb_test.img 67108864
# 格式化为 FAT32 (需管理员权限)
```
---
## 14. 与现有驱动的对比
| 特性 | AHCI | NVMe | xHCI |
|------|------|------|------|
| PCI Class | 01/06/01 | 01/08/02 | 0C/03/30 |
| 命令模型 | Port CI 命令 | SQ/CQ Doorbell | TRB Ring + Doorbell |
| 中断 | IRQ (PIC) | 轮询 | 轮询 (首版) |
| 数据传输 | PRDT DMA | PRP DMA | USB Bulk Transfer |
| 协议层 | ATA FIS | NVMe 命令 | USB + BOT + SCSI |
| 复杂度 | ~700 行 | ~800 行 | ~1500 行 |
| 块设备名 | ahci0 | nvme0 | usb0 |
---
## 参考资料
1. Intel xHCI Specification Rev 1.2 (2019): https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
2. USB Mass Storage Class Bulk-Only Transport Rev 1.0
3. SCSI Primary Commands (SPC-4) / SCSI Block Commands (SBC-3)
4. USB 2.0 Specification
5. USB 3.1 Specification
+412
View File
@@ -0,0 +1,412 @@
# Deshab 内核插桩系统设计文档
> 文档版本: 1.0
> 创建日期: 2026-07-29
> 路径: docs/RE/插桩系统设计.md
---
## 1. 概述
### 1.1 设计目标
为 Deshab 内核及各子系统建立统一的插桩基础设施,解决以下问题:
| 问题 | 现状 | 插桩方案 |
|------|------|----------|
| 无运行时日志过滤 | 所有 `log_info/warn/error` 无条件输出到串口 | `INSTR_LOG` 带级别+模块过滤 |
| 无结构化事件记录 | 仅线性串口输出,无法事后回溯 | `INSTR_PROBE` 环形探测缓冲 |
| 无轻量级统计 | 热路径缺乏量化数据 | `INSTR_STAT` 计数器+周期摘要 |
| 无跨模块统一接口 | UTSM/DKM/DSK 各用不同日志接口 | 统一 `INSTR_*` 宏体系 |
| 无配置控制 | 插桩代码无法运行时开关 | FUCK `[debug]` 配置段 |
### 1.2 设计原则
1. **零开销禁用**: `g_instr_enabled=0` 时所有宏展开为空操作,不引入任何分支或内存访问
2. **热路径安全**: `STAT_INC` 仅 `if(enabled) count++`,一条条件分支;`PROBE` 仅条件写入
3. **统一接口**: 所有 UTSM 模块使用 `INSTR_*` 宏;DKM 驱动使用 `DKM_*` 宏
4. **配置驱动**: 通过 FUCK `[debug]` 段控制开关与粒度,无需重编译
5. **标签编码**: 4 字符 ASCII 编码为 u32,避免字符串比较开销
---
## 2. FUCK `[debug]` 配置说明
### 2.1 配置段
```ini
[debug]
# 全局插桩开关 (0=禁用,零开销; 1=启用所有 INSTR_* 宏)
enabled=0
# 日志级别阈值
# 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=SILENT
# 仅 >= log_level 的 INSTR_LOG 消息才输出
log_level=2
# 探测缓冲开关 (0=禁用; 1=启用 INSTR_PROBE 写入环形缓冲)
probe_enable=0
# 探测缓冲容量 (条目数,每条 40 字节)
# 默认 4096 条 ≈ 160KB
probe_capacity=4096
# 统计摘要间隔 (每 N 次增量输出一次摘要)
stat_summary_interval=256
# 模块级过滤: 1=启用该模块的 TRACE/DEBUG 级别日志
utsm_trace=0
vmm_trace=0
dkm_trace=0
dsk_trace=0
block_trace=0
net_trace=0
input_trace=0
```
### 2.2 启用方式
**调试启动性能**:
```ini
[debug]
enabled=1
log_level=1
# 启动时观察各阶段耗时
```
**调试 VMM VM-Exit**:
```ini
[debug]
enabled=1
log_level=1
probe_enable=1
vmm_trace=1
```
**调试 AHCI DMA**:
```ini
[debug]
enabled=1
log_level=1
dkm_trace=1
block_trace=1
```
---
## 3. 宏系统设计
### 3.1 INSTR_LOG — 带级别和模块过滤的日志
```c
INSTR_LOG(level, mod_flag, msg)
```
- `level`: `INSTR_LOG_TRACE(0)` / `DEBUG(1)` / `INFO(2)` / `WARN(3)` / `ERROR(4)` / `SILENT(5)`
- `mod_flag`: `INSTR_F_xxx` 模块标志位,或 `0` 表示不过滤
- `msg`: 常量字符串
**过滤逻辑**:
```
g_instr_enabled && level >= g_instr_log_level && (mod_flag==0 || mod_flag & g_instr_module_flags)
```
### 3.2 INSTR_STAT — 轻量级计数器
```c
INSTR_STAT_DECL(name) // 声明: static u64 name_count + name_last_ts
INSTR_STAT_INC(name) // 自增: if(enabled) name_count++
INSTR_STAT_SUMMARY(name) // 摘要: 达到间隔时输出并重置
```
**设计要点**:
- `STAT_INC` 仅 `if(enabled) count++`,一条条件分支,极轻量
- `STAT_SUMMARY` 在 `count >= stat_interval` 时输出,自动重置
- 适合热路径:VM-Exit 分发、IRQ handler、网络收发
### 3.3 INSTR_PROBE — 环形探测缓冲
```c
INSTR_PROBE(tag, a0, a1, a2, a3)
```
- `tag`: 标签常量 `INSTR_TAG_xxx`,4 字符 ASCII 编码为 u32
- `a0-a3`: 4 个 u64 参数,语义由标签定义
**探测条目结构** (40 字节/条):
```c
typedef struct probe_entry {
u32 tag; // 4字符ASCII标签
u32 tick; // 低32位TSC(~2s溢出@2GHz,足够诊断)
u64 args[4]; // 4个u64参数
} probe_entry;
```
**环形缓冲行为**:
- 容量由 `probe_capacity` 控制(默认 4096 条)
- 写入时 head 指针推进,满时覆盖最旧记录
- 通过 `probe_read(n, out)` 读取最近 N 条
- 通过 shell `$probe` 命令读取(Phase 4 实现)
### 3.4 INSTR_TS — 时间戳标注
```c
INSTR_TS_DECL(name) // 声明: static u64 name_ts
INSTR_TS_BEGIN(name) // 记录: name_ts = instr_rdtsc()
INSTR_TS_END(name, label) // 输出: "[TS] label elapsed_ms=0x..."
```
**设计要点**:
- 使用 `instr_tsc_per_ms`(独立于 serial.c 的 TSC 校准)
- 仅在 `g_instr_enabled` 时输出
- 适合启动阶段耗时测量
---
## 4. 标签编码约定
### 4.1 编码规则
4 字符 ASCII 大端序编码为 u32:
```
'B'(0x42) 'O'(0x4F) 'O'(0x4F) 'T'(0x54) → 0x424F4F54
```
### 4.2 标签注册表
| 标签常量 | 编码 | 用途 | 参数语义 |
|----------|------|------|----------|
| `INSTR_TAG_BOOT` | 0x424F4F54 | 通用启动事件 | a0=vendor[0:1] |
| `INSTR_TAG_IDT0` | 0x49445430 | IDT/中断初始化 | — |
| `INSTR_TAG_ARE0` | 0x41524530 | Arena 初始化 | — |
| `INSTR_TAG_DMA0` | 0x444D4130 | DMA 初始化 | — |
| `INSTR_TAG_NET0` | 0x4E455430 | 网络注册 | — |
| `INSTR_TAG_DRR0` | 0x44525230 | DRR 初始化 | — |
| `INSTR_TAG_UTS0` | 0x55545330 | UTSM 核心初始化 | — |
| `INSTR_TAG_DKM0` | 0x444B4D30 | DKM 加载 | — |
| `INSTR_TAG_DSM0` | 0x44534D30 | 驱动 manifest 加载 | — |
| `INSTR_TAG_DSK0` | 0x44534B30 | DSK 加载跳转 | — |
| `INSTR_TAG_SELF` | 0x53454C46 | 自检 | — |
| `INSTR_TAG_VMM0` | 0x564D4D30 | VMM 初始化 | — |
| `INSTR_TAG_LIN0` | 0x4C494E30 | Linux loader | — |
| `INSTR_TAG_VME0` | 0x564D4530 | VM-Exit 事件 | a0=reason |
| `INSTR_TAG_EPTV` | 0x45505456 | EPT violation | a0=GPA |
| `INSTR_TAG_EPTM` | 0x4550544D | EPT misconfig | a0=GPA |
| `INSTR_TAG_AHCI` | 0x41484349 | AHCI 事件 | a0=port |
| `INSTR_TAG_E1K0` | 0x45314B30 | e1000 事件 | a0=IRQ type |
| `INSTR_TAG_FAT0` | 0x46415430 | FAT32 事件 | a0=operation |
| `INSTR_TAG_NVM0` | 0x4E564D30 | NVMe 事件 | a0=BAR |
| `INSTR_TAG_IRQ0` | 0x49525130 | IRQ 注册/触发 | a0=vector |
| `INSTR_TAG_PIT0` | 0x50495430 | PIT 模拟 | a0=divisor |
| `INSTR_TAG_SRL0` | 0x53524C30 | 串口事件 | — |
| `INSTR_TAG_MMIO` | 0x4D4D494F | MMIO 访问 | a0=GPA |
| `INSTR_TAG_HCALL` | 0x4843414C | Hypercall | a0=nr, a1=arg1 |
| `INSTR_TAG_EXCP` | 0x45584350 | CPU 异常 | a0=vector, a1=err |
### 4.3 新标签添加规则
1. 选择 4 个大写 ASCII 字符,语义明确
2. 以 `INSTR_TAG_` 为前缀定义常量
3. 在上表注册,注明参数语义
4. 避免与已有标签冲突
---
## 5. 各模块插桩清单
### 5.1 UTSM 内核 (`CODE/UTSM/kernel/main.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| `kernel_main()` 入口 | INSTR_TS | 启动总时间戳 |
| CPU 检测完成 | INSTR_PROBE(BOOT) | vendor/phys_bits |
| `idt_init()` | INSTR_TS | IDT+PIC 初始化耗时 |
| FUCK 配置加载 | INSTR_PROBE(BOOT) | 加载成功/失败 |
| `arena_init()` | INSTR_TS | Arena 初始化耗时 |
| `instr_init()` | — | 插桩系统初始化(含探测缓冲) |
| `dma_init()` | INSTR_TS | DMA 初始化耗时 |
| `net_init()` | INSTR_TS | 网络注册耗时 |
| `drr_init()` | INSTR_TS | DRR 初始化耗时 |
| `utsm_init()` | INSTR_TS | UTSM 核心初始化耗时 |
| `dkm_init()` + fill_platform | INSTR_TS | DKM 加载耗时 |
| `dsm_load_by_manifest_ex()` | INSTR_TS | 驱动 manifest 加载耗时 |
| `utsm_selftest_run()` | INSTR_TS | 自检耗时 |
| `vmm_init()` + self_test | INSTR_TS | VMM 初始化+自检耗时 |
| `linux_loader_init()` + launch | INSTR_TS | Linux 加载耗时 |
| `dsk_load_and_jump()` | INSTR_PROBE(DSK0) | DSK 跳转 |
### 5.2 VMM VM-Exit (`CODE/UTSM/vmm/vmexit.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| `vmexit_dispatch()` 入口 | INSTR_STAT(vmexit_total) | VM-Exit 总计 |
| `EXIT_EPT_VIOLATION` | INSTR_STAT(ept_violation) | EPT violation 统计 |
| `EXIT_EPT_MISCONFIG` | INSTR_PROBE(EPTM) | EPT 配置错误记录 |
| `EXIT_EXCEPTION_NMI` | INSTR_PROBE(EXCP) | CPU 异常记录 |
| `EXIT_TRIPLE_FAULT` | INSTR_PROBE(EXCP) | 三重故障记录 |
| `EXIT_EXTERNAL_INTERRUPT` | INSTR_STAT(ext_irq) | 外部中断统计 |
| `EXIT_CPUID` | INSTR_STAT(cpuid_exit) | CPUID 统计 |
| `EXIT_IO_INSTRUCTION` | INSTR_STAT(io_exit) | I/O 端口统计 |
| `EXIT_INTERRUPT_WINDOW` | INSTR_STAT(intr_window) | 中断窗口统计 |
| `EXIT_VMX_PREEMPTION_TIMER` | INSTR_STAT(preempt_timer) | 抢占定时器统计 |
| `EXIT_RDMSR/WRMSR` | INSTR_STAT(msr_exit) | MSR 访问统计 |
| `EXIT_VMCALL` | INSTR_PROBE(HCALL) | Hypercall 记录 |
| `vmexit_before_resume()` | INSTR_STAT(pre_resume) | resume 前处理统计 |
### 5.3 IDT (`CODE/UTSM/arch/x86_64/idt.c`)
| 插桩点 | 类型 | 说明 |
|--------|------|------|
| CPU 异常 (vector<32) | INSTR_PROBE(EXCP) | vector, err_code, rip |
| PIC IRQ (32-47) | INSTR_STAT(pic_irq_total) | PIC IRQ 总计 |
| APIC IRQ (48-255) | INSTR_STAT(apic_irq_total) | APIC/IOAPIC/MSI IRQ |
| `irq_register()` | INSTR_LOG(DEBUG) | IRQ handler 注册 |
### 5.4 DKM 驱动
每个驱动在 `driver_init` 开头调用 `dkm_instr_init(api)` 初始化开关。
| 驱动 | 插桩 | 说明 |
|------|------|------|
| **AHCI** | DKM_STAT/DKM_TS | PCI 发现、ABAR 映射、DMA 操作 |
| **e1000** | DKM_STAT | IRQ、RX/TX 统计、链路变化 |
| **FAT32** | DKM_STAT | BPB 解析、FAT 遍历、文件读写 |
| **NVMe** | DKM_STAT/DKM_TS | PCI 发现、BAR 解析 |
| **ps2kbd** | DKM_STAT | IRQ1 统计、扫描码计数 |
### 5.5 DSK / FirstInit / mouseInit
DSK 系列模块是独立 ELF,使用自有最小串口/TSC 接口:
| 模块 | 插桩 | 说明 |
|------|------|------|
| **DSK** | DSK_TS/DSK_STAT | FAT32 读取耗时、ELF 加载、模块调度 |
| **FirstInit** | logl 增强 | 按键字段切换日志 |
| **mouseInit** | 计数器 | IRQ12 包统计(已有日志) |
---
## 6. 探测缓冲系统设计
### 6.1 数据结构
```
probe_entry (40 bytes):
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ tag │ tick │ args[0] │ args[1] │ args[2] │ args[3] │
│ u32 │ u32 │ u64 │ u64 │ u64 │ u64 │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
```
### 6.2 环形缓冲管理
```
g_probe_buf[0..cap-1] ← arena 分配的连续内存
g_probe_head ← 下一条写入位置(单调递增)
g_probe_count ← 已写入总条目数
写入: idx = head % cap; buf[idx] = entry; head++; count++;
读取: 从 (head - n) 到 head,按时间顺序
```
### 6.3 读取接口
```c
int probe_read(u32 n, probe_entry *out); // 读取最近 N 条
u32 probe_count(void); // 已写入总条目数
u32 probe_capacity(void); // 缓冲容量
```
### 6.4 Shell 命令
```
probe — 显示最近 16 条探测记录
probe 64 — 显示最近 64 条探测记录
probe stat — 显示探测缓冲统计信息
probe tags — 显示所有已知标签
```
**实现方式**: UTSM 在 `dsk_fill_boot_context()` 中通过 `reserved[6]` 传递 `probe_info` 结构体指针,
shell 从中读取探测缓冲地址并直接访问共享内存。`probe_info` 结构定义在 `dsk.h` 中:
```c
typedef struct probe_info {
u32 magic; /* PROBE_INFO_MAGIC = 0x50524F42 ("PRFB") */
u32 version; /* ABI 版本,当前 1 */
u64 probe_buf_addr; /* probe_entry 缓冲基地址 */
u32 probe_cap; /* 缓冲容量(条目数) */
u32 probe_head; /* 当前写入位置 */
u32 probe_count; /* 已写入总条目数 */
u32 probe_dropped; /* 覆盖条目数 */
u32 instr_enabled; /* 插桩开关快照 */
u32 instr_probe_enable; /* 探测开关快照 */
u64 reserved[4];
} probe_info;
```
---
## 7. 零开销设计说明
### 7.1 禁用时展开
当 `g_instr_enabled=0`(默认配置)时:
```c
INSTR_LOG(level, mod_flag, msg)
→ if (0 && ...) { ... } // 编译器优化为空操作
INSTR_STAT_INC(name)
→ if (0) { name_count++; } // 编译器优化为空操作
INSTR_PROBE(tag, a0, a1, a2, a3)
→ if (0 && 0) probe_write(...) // 编译器优化为空操作
INSTR_TS_BEGIN(name) / INSTR_TS_END(name, label)
→ name_ts = rdtsc(); // 写入 static 变量(可优化掉)
→ if (0) { ... } // 编译器优化为空操作
```
### 7.2 热路径分析
**vmexit_dispatch** (每秒可能数千次):
- `INSTR_STAT_INC(vmexit_total)`: `if(enabled) count++` → 一条条件自增
- 所有 STAT_INC 合计: 1 条条件分支 + 1 条自增
- 无 PROBE 写入(仅在 `probe_enable=1` 时触发)
- 无 LOG 输出(仅在 `log_level <= DEBUG` 时触发)
**idt_handler** (每次中断/异常):
- `INSTR_STAT_INC(pic_irq_total)`: 一条条件自增
- `INSTR_PROBE(EXCP)`: 仅 CPU 异常时触发(极低频)
---
## 8. 实施路线图
| Phase | 内容 | 文件 | 状态 |
|-------|------|------|------|
| 1 | 基础设施 | instr.h, instr.c, probe.c, dkm_instr.h, FUCK | ✅ 完成 |
| 2 | UTSM 核心 | main.c, vmexit.c, idt.c | ✅ 完成 |
| 3 | DKM 驱动 | ahci/e1000/fat32/nvme/ps2kbd | ✅ 完成 |
| 4 | DSK+Shell | dsk/main.c, shell/main.c, $probe | ✅ 完成 |
| 5 | 文档 | docs/RE/插桩系统设计.md | ✅ 本文档 |
---
## 9. 文件清单
| 文件 | 类型 | 说明 |
|------|------|------|
| `CODE/UTSM/include/utsm/instr.h` | 头文件 | 统一插桩宏定义、标签常量、probe_entry |
| `CODE/UTSM/include/utsm/dsk.h` | 头文件 | probe_info 结构定义(reserved[6] 传递) |
| `CODE/UTSM/kernel/instr.c` | 源文件 | 插桩系统初始化、全局配置、TSC 校准 |
| `CODE/UTSM/kernel/probe.c` | 源文件 | 环形探测缓冲管理 + 状态访问函数 |
| `CODE/UTSM/kernel/dsk_loader.c` | 源文件 | dsk_fill_boot_context 填充 probe_info |
| `CODE/DKM/dkm_instr.h` | 头文件 | DKM 驱动专用插桩宏(无 UTSM 依赖) |
| `SYSTEM/system/deshab64/FUCK` | 配置 | 新增 `[debug]` 配置段 |
| `CODE/UTSM/kernel/main.c` | 源文件 | 启动流程 TS+PROBE 插桩 |
| `CODE/UTSM/vmm/vmexit.c` | 源文件 | VM-Exit STAT+PROBE 插桩 |
| `CODE/UTSM/arch/x86_64/idt.c` | 源文件 | IDT STAT+PROBE 插桩 |
| `CODE/shell/main.c` | 源文件 | `probe` 命令实现(probe stat/tags/N) |
| `docs/RE/插桩系统设计.md` | 文档 | 本文档 |