ax-hal
路径:
os/arceos/modules/axhal类型:库 crate 分层:ArceOS 层 / ArceOS 内核模块 版本:0.3.0-preview.3文档依据:Cargo.toml、src/lib.rs、src/dtb.rs、src/mem.rs、src/percpu.rs、src/irq.rs、src/paging.rs、src/tls.rs、build.rs、linker.lds.S
ax-hal 是 ArceOS 家族中最关键的“硬件抽象粘合层”。它并不试图独立实现所有架构/平台逻辑,而是把 ax-cpu 的 ISA 语义、axplat 的平台实现和上层运行时需要的统一接口收束成一套稳定的 HAL 边界,因此它既是 ax-runtime 的启动基座,也是 ax-mm、ax-task、StarryOS 与 Axvisor 共同复用的低层能力入口。
架构设计
设计定位
ax-hal 处在三层之间:
- 向下连接
ax-cpu与选中的axplat-*平台 crate,分别承接 ISA 级抽象和平台事实;默认平台实现是axplat-dyn。 - 向上为
ax-runtime、ax-mm、ax-task、ax-driver等模块提供统一 API。 - 通过
AX_PLATFORM_CRATE选择平台实现,并通过功能 feature 控制 IRQ、分页、TLS、SMP、用户态等能力。
这意味着 ax-hal 的核心价值不是“算法复杂”,而是“边界清晰”与“初始化顺序正确”。它本质上是 ArceOS 运行时的硬件语义总入口。
模块结构
src/lib.rs:顶层聚合与 feature 门控。决定是链接真实平台 crate,还是链接dummy平台以支持cargo test。src/dummy.rs:无真实平台时的占位实现。通过axplat接口提供 no-op 或unimplemented!()行为,主要用于宿主侧测试构建。src/dtb.rs:管理 boot argument、FDT 解析与chosen.bootargs读取,负责把启动参数从引导阶段传给后续模块。src/mem.rs:整合链接符号、平台物理内存范围和 MMIO 区域,生成统一的memory_regions()视图,并负责.bss清零。src/percpu.rs:每 CPU 局部状态入口,维护当前任务指针并复用ax_plat::percpu提供的 CPU 本地能力。src/time.rs:时间相关能力的再导出层,把时钟源、计时器和时间转换统一暴露给上层。src/irq.rs:IRQ 处理桥接层,负责 trap handler 注册、IRQ hook、与ax_plat::irq的派发对接。src/paging.rs:页表处理桥接层,向ax-page-table-multiarch提供PagingHandlerImpl,并在不同 ISA 下导出统一的页表类型。src/tls.rs:内核态 TLS 布局与TlsArea管理,仅在tlsfeature 启用时进入构建。build.rs+linker.lds.S:生成选中平台 crate 的导入代码、SMP build info,并配合链接路径完成内核段布局。
1.3 关键数据结构与全局对象
BOOTARG:保存引导阶段传入的参数,后续由 DTB/FDT 解析流程读取。ALL_MEM_REGIONS:统一后的物理内存区域视图,是ax-alloc、ax-runtime等模块做内存初始化的基础。CURRENT_TASK_PTR:每 CPU 当前任务指针,供调度与上下文切换路径读取。IRQ_HOOK:可注册的 IRQ 钩子,用于平台 IRQ 分发前后的附加处理。CPU_NUM:在smp场景下,由平台运行时发现结果决定最终可用 CPU 数。PagingHandlerImpl:把页表帧申请/释放与地址翻译能力接到上层页表实现中。TlsArea:内核态线程局部存储块管理对象,仅在 TLS 打开时参与主线。
1.4 启动与初始化主线
ax-hal 的关键不是单个函数,而是一条被 ax-runtime 调用的 固定主线:
对应到源码,最重要的调用链是:
ax_runtime::rust_main()先调用ax-hal::mem::clear_bss()。- 调用
ax-hal::percpu::init_primary(cpu_id)建立 BSP 的每核状态。 - 调用
ax-hal::init_early(cpu_id, arg),内部先做dtb::init(arg),再转入ax_plat::init::init_early()。 - 上层完成日志、分配器、页表等初始化后,调用
ax-hal::init_later(cpu_id, arg)进入平台后期初始化。 - 若为 SMP,还会走
init_early_secondary()与init_later_secondary()的从核路径。
1.5 架构与平台分层
ax-hal 的架构设计遵循“ISA 与板级分离”的原则:
ax-cpu负责 ISA 级能力,如asm、TaskContext、TrapFrame、trap 编号与可选uspace支持。axplat负责平台/机器级能力,如控制台、物理内存布局、时钟、中断控制器、电源管理与 CPU 启动。默认实现是axplat-dyn,自定义平台可通过AX_PLATFORM_CRATE和对应ax-halfeature 接入;完整契约见 平台层 / 平台契约。ax-hal把二者统一包装成上层可依赖的稳定接口,例如console、power、trap、context、mem、time、irq、paging。
因此,修改 ax-hal 时要始终区分:
- 这是 ISA 级语义问题,还是平台级实现问题。
- 这是
ax-hal的接口问题,还是axplat/ax-cpu的具体实现问题。
核心功能
功能概览
- 提供统一的启动初始化入口:
init_early()、init_later()及从核对应入口。 - 提供统一的内存视图:
memory_regions()、kernel_aspace()、.bss清零等。 - 提供统一的 trap/IRQ 桥接:
register_trap_handler、IRQ 派发与 IRQ hook。 - 提供统一的时间与计时器接口:
monotonic_time()、wall_time()、one-shot timer 等由time子模块统一导出。 - 提供统一的每 CPU 与上下文接口:
TaskContext、TrapFrame、当前 CPU ID、当前任务指针等。 - 提供页表与 TLS 支撑:在打开
paging或tls时,为ax-mm、ax-task、用户态支持等提供底层能力。
使用场景
init_early()/init_later():仅供运行时和平台入口链调用,是系统 bring-up 的核心接口。cpu_num():为调度器、SMP 初始化和 CPU 亲和逻辑提供最终生效的 CPU 数。dtb::get_fdt()/dtb::get_chosen_bootargs():供驱动、文件系统和配置路径读取设备树与内核 bootargs。mem::memory_regions():供分配器、页表和系统内存统计使用。irq::register_irq_hook()与ax_plat::irq::register():供上层模块接入中断回调。paging子模块导出的页表类型:供ax-mm和用户态地址空间管理复用。
使用方式
对于普通上层模块,ax-hal 更常见的接入方式不是“主动初始化”,而是“在内核已启动后查询硬件状态”:
use ax-hal::{mem, time};
let regions = mem::memory_regions();
let now = time::monotonic_time();
for region in regions {
let _ = (region.paddr, region.size, region.flags);
}
如果需要读取平台引导参数,则通常走:
let bootargs = ax-hal::dtb::get_chosen_bootargs();
依赖关系
常见 feature 组合:
irq:中断与 timer tick 支持。paging:页表与虚拟内存支持。tls:内核态 TLS。smp:多核支持。uspace:用户态上下文支持。
4.2 初始化约束
ax-hal 不是普通工具库,初始化顺序必须严格遵循运行时主线:
- 先由平台入口或
ax-runtime调用mem::clear_bss()与percpu::init_primary()。 - 再调用
init_early()完成 bootarg/DTB 与平台早期初始化。 - 完成分配器、页表等基础设施后,再调用
init_later()。 - IRQ、TLS、SMP 等能力必须与对应 feature 和后续运行时初始化步骤对齐。