程序能运行以后,硬件边界还要确认什么¶
用户态 SDK 可以在容器中启动、收发消息、处理超时。把相同代码搬到开发板上,却可能卡在驱动加载、设备初始化或第一笔传输。这里需要先分清启动链、资源描述和异步硬件访问各自承担的责任。
前置阅读:内核设备与退出、构建与 ELF。实验条件和命令位置见环境准备,代码入口见源码与版本。
沿启动链定位第一处失败¶
常见嵌入式 Linux 启动链可以写成:
每一段的输入和成功标志不同。bootloader 已经输出串口信息,只能说明早期启动与部分硬件可用;内核已经打印日志,也不说明根文件系统能挂载。根文件系统可用后,服务还可能因为库、权限、设备节点或配置失败。
本实验采用 QEMU ARM64 virt,直接加载预构建内核和 initramfs。/init 挂载 proc、sysfs、devtmpfs,加载教学模块并运行 UAPI 测试。这个流程覆盖内核到测试程序的路径;Boot ROM、板级 bootloader、电源时序和真实存储启动需要另做板上验证。
排查时保存第一处异常的完整上下文,例如 kernel command line、根文件系统类型、init 的路径与 ELF 解释器。后续报错可能只是这处失败的连锁结果。
设备树描述资源与关系¶
设备树向系统描述设备、地址范围、中断、时钟以及设备间关系。驱动仍要匹配节点、取得资源、初始化设备并承担失败回滚。一个节点出现于 DTS,不代表驱动已经正确工作。Linux 设备树使用模型
下面导出 QEMU 自己生成的 DTB。先在宿主项目根目录运行:
mkdir -p build/vm
qemu-system-aarch64 -machine virt,dumpdtb=build/vm/virt.dtb \
-cpu cortex-a72 -nographic -nic none
随后在挂载同一项目目录的专用 Linux 工具容器内运行:
阅读一个 reg 字段时,先找到父节点的 #address-cells、#size-cells 和可能存在的 ranges。设备地址可能需要总线翻译,不能看到一串数就直接把它当 CPU 可访问地址。本教学模块注册软件 platform device,没有虚构 MMIO 或 IRQ 资源。
把一次传输的责任写成时间线¶
考虑上层向总线适配层传入一块 buffer:
| 时点 | 上层看见什么 | 下层需要确认什么 |
|---|---|---|
| 调用传输接口 | 参数通过检查 | 是否复制;是否开始异步访问 |
| 接口返回 | 提交成功或失败 | 返回是否意味着硬件访问已结束 |
| 硬件完成 | 完成通知 | DMA 是否停止;cache 是否同步;buffer 能否复用 |
| 取消或 reset | 请求结束 | 如何确保晚到的完成不访问已回收对象 |
如果接口只把地址交给 DMA,调用返回后立即把 buffer 归池,就可能被下一个请求覆盖。解决办法取决于契约:提交前复制、持续借用到完成,或者显式转移所有权。三种做法有不同的内存和收尾成本,详见并发与所有权。
CPU 虚拟地址、物理地址和设备使用的 DMA 地址分别处理。驱动应使用适合设备的 DMA API;一致性分配也仍需处理描述符发布等顺序要求。Linux DMA 指南
平台适配时的核对表¶
| 边界 | 必须确认的内容 | 证据入口 |
|---|---|---|
| 启动 | 镜像、DTB、rootfs、加载地址与启动参数 | 启动日志与构建清单 |
| 资源 | MMIO 范围、IRQ、时钟、reset、电源依赖 | 原理图、DTS、芯片文档与驱动 |
| 内存 | DMA 地址能力、可访问区、cache 与对齐 | DMA 映射结果和板上测试 |
| 异步完成 | 借用时长、失败、取消和解绑后行为 | 接口契约与定向故障测试 |
| 总线恢复 | 断连、复位、超时及晚到完成 | 恢复时序、计数与日志 |
Linux 的 USB 功能驱动、usbcore 和主控 HCD 处于不同层次。移植时把责任落实到具体实现,避免上层和下层各自假设对方已经复制或完成。
这套 QEMU 实验能够验证软件对象的引用与退出。真实 DMA、IRQ、时钟、电源和 MCU 中断上下文需要相应硬件证据。平台选择和验证计划见从负载选择平台。