一句话摘要

axhal 是 ArceOS 的硬件抽象层,通过 features 实现模块化,封装 axplat 并提供统一的硬件访问接口,支持多平台编译和按需功能裁剪。

核心要点

  • 平台抽象:支持默认 qemu 平台和自定义平台两种导入方式
  • 模块化设计:通过 features 控制功能编译,axfeat 统一启用各模块 feature
  • 无堆初始化:启动初期无需堆内存,使用 LazyInit 保证线程安全
  • 多架构支持:x86_64、AArch64、RISC-V 的 TLS 布局各有适配

平台抽象机制

默认平台(qemu): 上层代码通过 extern crate 直接导入,无需关心具体平台,统一使用 axplat:: 访问。

自定义平台: 需在上层 APP 代码中自行导入,通过 APP_FEATURE 传入 feature,随后在 build.mk 中通过 cargo --feature 启用;若传入不存在的平台包,则会报错退出。

axhal 实质上是对 axplat 的封装与进一步扩展。


模块详解

mem.rs

物理内存区域结构:

PhysMemRegion {
    paddr: PhysAddr,           // 物理起始地址
    size: usize,               // 区域大小(字节)
    flags: MemRegionFlags,     // 内存区域标志
    name: &'static str,        // 区域名称(用于调试)
}

unsafe clear_bss() 说明:

unsafe 的原因:直接操作内存,将函数指针转换为裸指针。

初始化步骤:

  1. 创建 Vec 作为容器,并声明添加区域的辅助闭包函数
  2. 注册代码段、只读数据段、数据段、内核初始化栈、BSS 段
  3. 注册 MMIO 和保留区域(由具体的 axplat 实现)
  4. 排序后寻找已保留内存与总内存的差集,计算可分配内存并插入 all_regions
  5. 再次排序,检查是否存在重叠区域
  6. 使用 all_regions 初始化全局变量 ALL_MEM_REGIONS

设计要点:

  • 无堆设计:确保在系统启动初期可用
  • LazyInit:原子类型保证线程安全

paging.rs

axhal 中 paging 仅包含两部分:条件编译选择页表类型、使用 ZST 实现 PagingHandler Trait。大部分逻辑拆分到外部 crate(如 page_table_multiarch)。

调用关系:

alloc_frame  ->  alloc_table()
dealloc_frame  ->  dealloc_tree() / drop()
                       |
                  axmm 中使用(copy_from, try_new)

time.rs

axhal 仅 re-export axplat 中 time 的实现(与平台相关)。


irq.rs

  • Re-export axplat 的 irq 相关函数
  • 为 irq 的 trap_handler 提供实现
  • 可通过 ipi feature 控制跨处理器中断功能是否编译

percpu.rs

使用 #[percpu::def_percpu] 宏,为每个 CPU 生成独立的存储空间和访问方法。

提供的访问方法:

  • this_cpu_id()
  • this_cpu_is_bsp()
  • current_task_ptr() / set_current_task_ptr()
  • init_primary() / init_secondary()
  • cache_current_task_ptr()(aarch64 专属)

Bootstrap Processor(BSP)的职责:

  1. 执行引导加载程序
  2. 初始化内核数据结构
  3. 启动其他 CPU
  4. 分配全局资源
  5. 初始化设备驱动

tls.rs

Layout 存储内存区域的大小和对齐要求,用于后续释放操作。

pub struct TlsArea {
    base: NonNull<u8>,  // TLS 区域的基地址
    layout: Layout,     // 内存布局信息(大小和对齐)
}

同时为该结构实现了 Drop Trait。

核心方法:

  • tls_ptr():获取线程指针
  • alloc():计算大小、分配并清零、将 tdata 和 tbss 段数据赋值到分配区域中(x86 架构最后还会初始化一个指向自己的指针)

TLS 内存布局

x86_64:

aligned --> +-------------------------+- static_tls_offset
allocation  |                         | \
            | .tdata                  |  |
| address   |                         |  |
| grow up   + - - - - - - - - - - - - +   > Static TLS block
v           |                         |  |  (length: static_tls_size)
            | .tbss                   |  |
            |                         |  |
            +-------------------------+  |
            | / PADDING / / / / / / / | /
            +-------------------------+
   tls_ptr -+-> self pointer (void *) | \
(tp_offset) |                         |  |
            | Custom TCB format       |   > Thread Control Block (TCB)
            | (might be used          |  |  (length: TCB_SIZE)
            |  by a libC)             |  |
            |                         | /
            +-------------------------+- (total length: tls_area_size)

AArch64 / RISC-V:

            +-------------------------+
            |                         | \
            | Custom TCB format       |  |
            | (might be used          |   > Thread Control Block (TCB)
            |  by a libC)             |  |  (length: TCB_SIZE)
            |                         | /
   tls_ptr -+-------------------------+
(tp_offset) | GAP_ABOVE_TP            |
            +-------------------------+- static_tls_offset
            |                         | \
            | .tdata                  |  |
            |                         |  |
            + - - - - - - - - - - - - +   > Static TLS block
            |                         |  |  (length: static_tls_size)
            | .tbss                   |  |
            |                         | /
            +-------------------------+- (total length: tls_area_size)

延伸思考

  • axhal 通过 features 实现按需裁剪,既减小了编译产物大小,又保持了接口统一性;axfeat 作为统一入口进一步简化了上层模块的 feature 管理
  • 不同架构的 TLS 布局差异(x86_64 地址向下增长 vs AArch64/RISC-V 向上增长)直接反映了各自的 ABI 规范,理解这一差异有助于在移植时正确处理线程局部存储