axplat-dyn
路径:
platforms/axplat-dyn类型:库 crate 分层:平台层 / 动态平台桥接层 版本:0.3.0-preview.3文档依据:当前仓库源码、Cargo.toml、build.rs、link.ld及os/arceos/modules/axhal/ax-driver的接入路径
axplat-dyn 不是那类“用静态配置文件固化板级常量”的常规 axplat-* 平台包。它更像一层桥接适配器:把 somehal 已经建立好的启动入口、FDT 地址、内存图、时钟、IRQ、电源与 SMP 元数据转译成 axplat 的统一契约;同时再补上一条 ax-driver 动态设备模型所需的设备探测与 DMA glue。这里的 dyn 真正表示“平台事实来自运行时抽象层和探测结果”,而不是“把平台包当作运行时可装卸模块加载”。
架构设计
设计定位
axplat-dyn 在当前仓库里的位置可以概括为:
- 向下:依赖
somehal提供的入口宏、内存映射、控制台、时钟、中断、电源和 CPU 元数据。 - 向上:实现
InitIf、ConsoleIf、MemIf、TimeIf、PowerIf以及可选IrqIf,并通过ax_plat::call_main()/call_secondary_main()把控制权交给内核入口。 - 向旁:公开
drivers模块,为ax-driver的dyn设备模型提供基于rdrive和rdif-block的设备探测与块设备封装。 - 向链接层:通过
build.rs生成axplat.x,把linkme段、异常表和若干 ArceOS 约定符号补进最终镜像。
这决定了它与普通板级包的一个根本差异:
- 普通
axplat-*平台包主要把编译期静态配置变成板级常量,再围绕这些常量实现axplat接口。 axplat-dyn则把somehal暴露的运行时事实直接转成axplat接口,不以静态配置文件为主线。
模块结构
| 模块 | 作用 | 关键内容 |
|---|---|---|
lib.rs | crate 根与装配层 | 裸机目标限定、模块导入、无 irq 时的空中断入口 |
boot | 启动 glue | #[somehal::entry(Kernel)]、#[somehal::secondary_entry]、Kernel 的 MmioOp 实现 |
init | InitIf 实现 | trap 初始化、计时器打开、post_paging()、后期 IRQ 打开 |
console | ConsoleIf 实现 | 控制台读写、\n 到 \r\n 的串口兼容转换 |
mem | MemIf 实现 | 从 somehal::mem::memory_map() 生成 RAM/保留区/MMIO 视图,导出 _percpu_base_ptr |
generic_timer | TimeIf 实现 | tick/nanos 转换、定时器 IRQ 编号、one-shot 定时器 |
irq | IrqIf 实现 | HandlerTable<1024>、启停 IRQ、注册/撤销、公共分发入口 |
power | PowerIf 实现 | cpu_boot()、system_off()、cpu_num() |
drivers | 动态设备探测 glue | probe_all_devices()、动态块设备注册表、DMA 适配、PCIe/VirtIO block 探测 |
1.3 平台实现装配方式
axplat-dyn 的实现不是单点完成的,而是由四层 glue 组合出来:
| 装配层 | 依赖来源 | 本 crate 的落点 | 作用 |
|---|---|---|---|
| 启动 glue | somehal | boot.rs | 把主核/次核入口收敛到 ax_plat::call_main() / call_secondary_main() |
| 平台契约 glue | axplat | init.rs、console.rs、mem.rs、generic_timer.rs、irq.rs、power.rs | 用 #[impl_plat_interface] 把 somehal 能力接到 axplat trait |
| 设备 glue | rdrive、rdif-block、ax-driver | drivers/* | 把 FDT/PCIe/VirtIO 探测结果转成 rdrive 可查询的动态块设备 |
| 链接 glue | build.rs、link.ld | crate 根目录 | 生成 axplat.x,插入所需段和符号,适配 ArceOS 链接约定 |
这套装配方式说明它承担的是“桥接与接线”职责,而不是“重新定义平台抽象”。
1.4 启动与初始化主线
boot.rs 给出的启动链很短,但责任很明确:
#[somehal::entry(Kernel)]标记的main()作为主核入口。- 入口尝试从
somehal::fdt_addr_phys()取得 FDT 物理地址,作为arg传给内核。 - 主核统一跳到
ax_plat::call_main(0, arg)。 - 若启用
smp,#[somehal::secondary_entry]标记的次核入口会继续调用ax_plat::call_secondary_main(cpu_id)。
init.rs 则把 somehal 已经具备的基础能力重新编排成 axplat 生命周期:
init_early()/init_early_secondary():初始化 trap,并打开基础计时器。init_later():在分页建立更完整后调用somehal::post_paging(),随后使能定时器 IRQ。init_later_secondary():次核路径上只补做定时器 IRQ 使能。
这里有一个重要前提:axplat-dyn 默认假设“更早的架构级 bring-up 已经由 somehal 完成”,它不自己构造早期页表,也不自己处理最底层的 CPU 模式切换。
1.5 内存、时间、中断与电源 glue
内存视图
mem.rs 直接遍历 somehal::mem::memory_map(),按 MemoryType 过滤出三类区间:
Free->phys_ram_ranges()Reserved/KImage/PerCpuData->reserved_phys_ram_ranges()Mmio->mmio_ranges()
这些结果被缓存到 spin::Once<Vec<...>> 中,说明该 crate 采用“首次查询时冻结运行时内存图”的策略,而不是每次重新探测。
地址转换和地址空间范围也完全委托给 somehal:
phys_to_virt()/virt_to_phys()直接转发到somehal::memkernel_aspace()来自somehal::mem::kernel_space()
此外,_percpu_base_ptr() 通过 somehal::smp::percpu_data_ptr() 向 ax-percpu crate 提供每核数据基址。这也解释了为什么 ax-hal::mem 不再额外注入一套传统平台包的内核保留区逻辑:当前路径默认信任 somehal 给出的内存事实已经包含 KImage 和 PerCpuData。