开发 Diosix
优先考虑 安全 (Safety) > 安全性 (Security) > 正确性 (Correctness) > 可维护性 (Maintainability)。编写具有自文档性、内存安全性和严格边界约束的惯用 Zig 0.17.0 和 RISC-V 汇编代码,以便每次更改都能一次性通过 reviewing-diosix 审查,而无需在提交前返工。
核心强制要求
- 可运行、完整且可构建的代码(基本要求):每次更改都必须在根虚拟机/虚拟机监控器构建和主机单元测试套件(
./scripts/build.sh和./scripts/build.sh test)中干净地编译通过。切勿遗留损坏的代码、存根的TODO占位符、模拟的生产路径或部分实现的功能。 - 行与文件限制(分解为子文件夹,绝不截断):所有代码和注释换行宽度 <= 80 列。源文件 <= 500 行。当一个文件超过 500 行时,切勿截断其功能或测试;应将其分解到专用的子文件夹中(例如
panes/vm.zig、vm/run.zig、tests/gui.zig),而不是散布扁平化的prefix_subname.zig文件(例如panes_vm.zig、vm_run.zig、tests_gui.zig),并将每个子模块连接到父级的test块中(_ = @import("vm/run.zig");)。 - 子文件夹优先于前缀下划线文件名,且零拷贝粘贴:使用简洁的
snake_case文件名,不带冗余的项目、目录或同级前缀(使用api.zig、main.c、vm/info.zig和tests/nav.zig,绝不使用diosix_api.zig、diosix-alsa-init.c、vm_info.zig或tests_nav.zig)。切勿在模块间重复共享原语(如Spinlock、页面对齐数学、路径遍历验证器或 ABI 常量);应从hypervisor/interface/、hypervisor/core/atomic.zig或common.zig中导入它们。 - 惯用分发方式:切勿为命令、标志或配置键编写
if (std.mem.eql(...)) else if (...)链。定义一个enum,使用std.meta.stringToEnum(EnumType, slice)进行解析,并使用switch分发。 - 显式固定宽度 ABI 类型、小写十六进制和命名位掩码:在所有 ABI/硬件结构体(
extern struct、packed struct)和 64 位客户寄存器状态中使用显式固定宽度类型(u64、u32、u16、u8)—— 绝不使用usize或 C 语言的unsigned long。始终用编译时字面量(const w: usize = @min(n, 1280);)对从@min/@max初始化的局部变量进行类型注解,以便 Zig 不会推断出一个较窄的整数类型(如u11)从而导致后续溢出。所有十六进制字面量请使用小写(0xfff、0xffff_ffff,绝不使用0xFFF),并用命名常量和自文档化辅助函数(PAGE_OFFSET_MASK、crossesPageBoundary(vaddr, @sizeOf(u32)))替换魔数位掩码/偏移量(如(vaddr & 0xfff) > 0xffc)。 - 动态分配优于静态猜测:只有在堆初始化之前执行的早期 M 模式引导代码才可以使用固定的静态缓冲区。一旦分配器可用,应动态分配和增长缓冲区,而不是猜测固定的栈缓冲区大小(例如避免使用
var buf: [4096]u8)。 - 内存所有权与清理:每次分配(
alloc、create、dupe、print、mmap)后都需立即添加defer或errdefer。清零已释放的客户页面和密码学机密(std.crypto.secureZero)。 - 企业级虚拟机监控器隔离:切勿因客户输入或状态而在 M 模式下引发 panic;应返回 SBI 错误或终止有问题的客户子树。将共享的客户内存一次性复制(
@memcpy)到虚拟机监控器拥有的内存中,然后在验证之前防止双取(TOCTOU)漏洞。 - 代码中实现机制,构建/运行时配置中定义策略:切勿在 Zig 代码中硬编码策略级别的设置或可调优的默认值。将可调优项和设置移至构建时配置(
build.zig选项 /boot/hypervisor.conf)或运行时配置文件(/etc/diosix/*.toml,保持key = "value"在单行内且不超过 80 列)。 - 在构建时生成二进制文件和镜像,而不是在 Zig 中运行时生成:避免在运行时在 Zig 中逐字组装 ELF 二进制文件、机器码垫片或媒体镜像。将垫片保留在汇编或 Zig 源文件中,在构建时编译;使用专用脚本构建媒体镜像;切勿将生成的构建输出放置在
tools/overlay-common/内(应使用.zig-cache/overlay-dynamic/)。 - 共置单元测试与独立的集成测试:将单元
test块放在其所测试的源文件中(调用std.testing.refAllDecls(@This()))。将多组件集成测试放在专用的tests/目录中。 - 保持向后兼容性,但避免累积遗留回退:维护外部契约的兼容性(SBI 超级调用 ABI、
/dev/diosixioctl、文档化的dsxCLI 命令/JSON 模式以及用户编写的/etc/diosix/*.toml文件),但切勿为同一版本化的内部管道累积多路径回退数组(如DEFAULT_GUEST_CANDIDATES)。当生产者与消费者在此仓库中一起发布时,应原子性地更新树内所有生产者、消费者和测试,以使用单一规范路径或符号,并在引导/安装边界处一次性处理持久磁盘布局升级,而不是永远探测历史路径。 - 不确定时,停下来询问主维护者:切勿猜测架构决策、模糊需求或缺失的子系统能力。在继续之前停下来询问主维护者。
M 模式 vs. U 模式执行环境
| 方面 | M 模式虚拟机监控器 (hypervisor/) | U 模式工具 (tools/) |
|---|---|---|
| 目标平台 | riscv64-freestanding-none | riscv64-linux-musl(测试时也可为主机) |
| 预堆引导 | entry.s 中的静态每核 slab (0..31) | 不适用(OS 进程环境在 main 时已就绪) |
| 分配器 | 每核 pcore.this().allocator.allocator() | std.heap.SafeAllocator / page_allocator / ArenaAllocator |
| 崩溃策略 | 绝不对客户输入引发 panic;隔离并终止有问题的客户 VM | 返回描述性错误并给出干净的退出码 |
| 硬件访问 | 直接 CSR/MMIO(必须用 if (is_test) 守卫以支持主机单元测试) | /dev/diosix ioctl(api.zig)、DRM/evdev、Linux 系统调用 |
重点参考指南
在编写或修改代码之前,请阅读相关参考文件:
- Zig 0.17.0 惯用法与内建函数:最新的 Zig 0.17 语法(
@backingInt、@splat、allocator.print、SafeAllocator、stringToEnum、@ptrCast用于extern struct、固定宽度 ABI 类型、带尾逗号的 80 列格式化以及构建缓存纯度)。 - 内存管理与动态缓冲区:M 模式与 U 模式分配器模式、动态缓冲区增长、
defer/errdefer规则、机密清零和出参数初始化。 - 架构与 TigerStyle:完整功能实现、不丢失单元测试的模块化文件拆分、零拷贝粘贴重复、有界循环、断言配对以及覆盖层/配置卫生。
- RISC-V 与企业级虚拟机监控器:RV64 汇编约定、CSR 模拟、爆炸半径控制、双取预防模式、IRQ 安全的自旋锁顺序以及 G 阶段/PMP 隔离。