- 操作系统
- 嵌入式
- 嵌入式OS
【免费下载链接】tock
A secure embedded operating system for microcontrollers
nRF52840-DK SHA256 Test Board(nrf52840dk-test-sha256)是 Tock 仓库中一个用于在 Nordic nRF52840 开发板上验证 SHA-256 功能的最小内核配置。本文围绕该配置,完整讲解其组成文件、编译与烧录流程、内核启动与平台装配逻辑,并结合源码深入剖析软件 SHA-256 引擎与用户空间驱动的实现原理,帮助读者理解 Tock 如何以极小的内核配置承载一个完整的加密哈希功能验证环境。
配置概览:一个"最小内核"由哪些文件组成
该测试板位于仓库 boards/configurations/nrf52840dk/nrf52840dk-test-sha256/ 目录,仅由 5 个文件构成,其职责划分非常清晰:
| 文件 | 作用 |
|---|---|
| src/main.rs | 平台定义与内核入口:装配 SHA256 软件引擎、注册 syscall 驱动并启动内核主循环 |
| Cargo.toml | 声明对基板库、组件库、芯片与胶囊(capsule)等 crate 的依赖 |
| Makefile | 复用公共构建规则与 nRF52 测试板烧录规则,提供flash/flash-openocd目标 |
| layout.ld | 链接脚本,包含芯片布局与 Tock 内核布局 |
| README.md | 一句话说明该板用途:用于测试 SHA256 的最小内核 |
从 Cargo.toml 的依赖声明可以看出它的"最小化"策略:它并不直接重复实现串口、LED、告警时钟等基础外设,而是通过nrf52840dk-test-base(对应源码库 boards/configurations/nrf52840dk/nrf52840dk-test-base/src/lib.rs)复用基板级启动代码,自身只额外引入 SHA256 相关组件。这一点在main.rs中表现得尤为明显。
平台装配:在基板之上叠加 SHA256 驱动
main.rs以标准的#![no_std]+#![no_main]编写,并开启#![deny(missing_docs)],这是 Tock 各 board 源码的通用风格。
类型层面的装配
main.rs开头定义了两个关键类型别名:
type Sha = components::sha::ShaSoftware256ComponentType; const SHA_DIGEST_LEN: usize = 32; type ShaDriver = components::sha::ShaDriverComponentType<Sha, SHA_DIGEST_LEN>;Sha对应capsules_extra::sha256::Sha256Software,即纯软件实现的 SHA-256 引擎(摘要长度固定为 32 字节);SHA_DIGEST_LEN = 32是 SHA-256 输出长度,既是类型泛型参数也是驱动内部缓冲区的尺寸依据。
Platform结构体把基板平台与 SHA 驱动组合起来:
pub struct Platform { base: nrf52840dk_test_base_lib::Platform, sha_driver: &'static ShaDriver, }在SyscallDriverLookup实现中,with_driver首先匹配capsules_extra::sha256_driver::DRIVER_NUM返回 SHA 驱动,其余驱动号全部转发给基板平台:
match driver_num { capsules_extra::sha256_driver::DRIVER_NUM => f(Some(self.sha_driver)), _ => self.base.with_driver(driver_num, f), }而KernelResources<ChipHw>的实现则几乎全部委托给self.base——调度器、调度器定时器、看门狗、上下文切换回调、syscall 过滤与进程故障策略均复用基板平台,其类型关联也通过<nrf52840dk_test_base_lib::Platform as KernelResources<ChipHw>>::Xxx继承。这种"基板 + 增量扩展"的模式是 Tock 测试板配置的典型写法,可让多个测试配置共享同一份基础启动代码,只差分测试特性。
运行时装配与内核启动
入口函数main()先调用基板库的start()完成底层初始化:
let (board_kernel, base_platform, chip, _default_peripherals, _mux_uart, _mux_alarm) = nrf52840dk_test_base_lib::start();随后是 SHA256 的装配段落。第一步创建软件 SHA-256 引擎,注意该组件在finalize时会把自身注册为 DeferredCall 客户端(见 boards/components/src/sha.rs 中ShaSoftware256Component的finalize实现):
let sha = components::sha::ShaSoftware256Component::new() .finalize(components::sha_software_256_component_static!());第二步把引擎包装成对用户空间暴露的 syscall 驱动,并申请 grant 空间(需要MemoryAllocationCapability):
let sha_driver = components::sha::ShaDriverComponent::new( board_kernel, capsules_extra::sha256_driver::DRIVER_NUM, sha, create_capability!(capabilities::MemoryAllocationCapability), ) .finalize(components::sha_driver_component_static!( Sha, SHA_DIGEST_LEN ));ShaDriverComponent(源码见 boards/components/src/sha.rs)的finalize会做三件事:初始化 64 字节的data_buffer(用于从进程缓冲区搬运数据)、初始化SHA_DIGEST_LEN字节的dest_buffer(用于存放摘要)、通过board_kernel.create_grant(driver_num, ...)创建该驱动专用的 grant,最后self.sha.set_client(sha)把驱动注册为哈希引擎的客户端。sha_driver_component_static!宏则为这三块内存提供静态分配空间。
组装好Platform后,main()通过链接脚本符号_sapps/_eapps/_sappmem/_eappmem划定应用镜像的 flash 与 RAM 区域,调用kernel::process::load_processes加载进程(失败时仅打印错误而不 panic),最后持有MainLoopCapability进入board_kernel.kernel_loop,内核自此开始调度。
基板库:测试板共用的基础平台
为了理解该测试板"最小"在何处,需要看基板库 nrf52840dk-test-base/src/lib.rs:
- 芯片类型:
ChipHw = nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>,基于 nRF52840 芯片 crate; - 外设初始化:
create_peripherals()为 IEEE 802.15.4 ACK 缓冲区与 AES-ECB 缓冲区申请静态内存后构建默认外设集合,再调用nrf52840_peripherals.init()完成环形依赖初始化; - 基础驱动:该基板平台仅注册了
console(串口控制台,波特率 115200)、led(4 个 LED,对应 P0_13/P0_14/P0_15/P0_16)与alarm(基于 RTC 的告警驱动),并采用 Round-Robin 调度器、SysTick调度器定时器; - 进程策略:
FAULT_RESPONSE使用PanicFaultPolicy,即进程出错时内核直接 panic,这是测试场景下的常见选择;NUM_PROCS = 8表示最多并发 8 个进程; - 串口通道:通过
UartChannel::Pins使用传统 UART GPIO(TX=P0_06、RX=P0_08,RTS/CTS 可选),而非 Segger RTT; - 启动流程:
start()依次完成 errata 修复与中断使能(ChipHw::init())、deferred call 状态初始化、外设创建、进程数组、Kernel结构、chip、nRF 启动配置(含复位按钮 P0_18 与默认稳压器输出)、LED、RTC/告警、UART/控制台、时钟(NrfClockComponent)、Round-Robin 调度器等全部装配,最后返回(board_kernel, platform, chip, ...)供各测试板main()继续定制。
构建、烧录与运行
构建目标
该配置使用 cortex-m4 架构(见 Cargo.toml 中的cortexm4依赖),目标平台由 boards/Makefile.common 中的规则自动推导:PLATFORM取自 crate 名(即nrf52840dk-test-sha256),TARGET取自 Cargo 配置的build.target。产物位于target/<target>/release/nrf52840dk-test-sha256.bin。
Makefile 烧录规则
Makefile 内容极简,仅两行:
include ../../../Makefile.common include ../nrf52840dk.mk公共规则由 boards/Makefile.common 提供(release/debug/lst/stack-analysis/memory等目标),nRF 测试板专用规则由 boards/configurations/nrf52840dk/nrf52840dk.mk 提供。该 mk 文件定义了内核烧录地址KERNEL_ADDRESS=0x00000,并提供两个目标:
flash:通过tockloader flash --address 0x00000 --board nrf52dk --jlink经 J-Link 烧录内核到 flash;flash-openocd:同样的命令改用--openocd走 OpenOCD 通道。
此外,install目标别名到flash;如果设置了PORT环境变量,会以--port $(PORT)附加到 tockloader 参数,用于指定串口。注意tockloader是外部工具,需要在使用前另行安装。
运行
烧录后,内核在 nRF52840-DK 上启动并进入kernel_loop。用户空间进程可调用 SHA256 驱动完成哈希运算,内核串口调试输出(经 UART P0_06/P0_08)可用于观察启动信息。
从内核到用户空间:SHA256 驱动的三层实现
该测试板验证的 SHA-256 能力由三层代码协作完成,每一层都有独立的源码可查:
- 软件算法层:
capsules_extra::sha256::Sha256Software(源码 capsules/extra/src/sha256.rs); - 系统调用驱动层:
capsules_extra::sha256_driver::ShaDriver(源码 capsules/extra/src/sha256_driver.rs); - HIL 抽象层:
kernel::hil::digest(kernel/src/hil 目录下的 digest 模块),定义Digest、DigestData、DigestHash、DigestVerify、DigestDataHash等 trait。
软件 SHA-256 引擎的设计要点
Sha256Software是一个纯 Rust 实现、不依赖硬件加速器的哈希引擎,其实现基于 32 位原生整型运算,输入按处理器字节序翻译、输出统一转为大端格式。几个值得注意的实现细节:
- 常量与状态:内部持有 64 个轮常量
ROUND_CONSTANTS与 8 个hash_values(初始化为 SHA-256 标准 IV0x6a09e667...),单块长度 64 字节、输出 32 字节; - 状态机:
State枚举区分Idle/Data/Hash/Verify及对应的CancelXxx状态,busy()依据状态判断引擎是否可接受新操作,从而保证同一时刻只有一个操作在途; - 分块缓冲:
compute_sha256()先把数据填充进 64 字节data_buffer,满一块就调用compute_block压缩,处理完input_data后把不足一块的尾部留存,buffered_length/total_length分别跟踪缓冲长度与总输入长度; - 收尾填充:
complete_sha256()实现标准 SHA-256 的填充:追加0x80字节,若超过 56 字节则补零并压缩一个额外块,最后写入 64 位位长(total_length * 8)的大端表示; - 消息调度与压缩:
perform_sha()展开 16..64 的消息调度扩展(s0/s1移位异或组合),再执行 64 轮压缩函数(Σ0/Σ1、Ch、Maj、wrapping_add取模加法),最后把工作变量累加回hash_values; - 延迟回调:引擎通过
DeferredCall把完成回调推迟到中断上下文之外执行(handle_deferred_call中分发Data/Hash/Verify/CancelXxx各状态),这是 Tock 中避免在中断栈上执行长逻辑的常用手法; - 验证模式:
verify()不返回摘要,而是在handle_deferred_call的State::Verify分支内逐字节比对output_data与内部哈希值,通过verification_done(Ok(pass), ...)上报结果。
系统调用驱动的用户空间接口
ShaDriver把哈希引擎暴露给用户空间。其 syscall 驱动号定义为capsules_extra::driver::NUM::Sha,即0x40005(见 capsules/core/src/driver.rs 中的Sha = 0x40005)。
用户空间通过三类 syscall 使用该驱动:
allow(只读缓冲区ro_allow::DATA,编号 0):提供待哈希数据;allow(读写缓冲区rw_allow::DEST,编号 0):提供接收 32 字节摘要的输出缓冲区;command:0:驱动存在性检查;1(set_algorithm):data1指定算法,当前仅支持0即 SHA-256,其余值返回NOSUPPORT(SHA-384/512 在源码中以注释形式预留);2(hash):入队并启动哈希操作,若驱动忙则返回NOMEM;
upcall(编号 0,HASH):哈希完成时以(statuscode, 0, 0)通知进程。
驱动内部的核心流程(run()→add_mut_data_done()→hash_done())体现了一个典型的"分片搬运 + 串行化"设计:
run()先调用底层引擎的set_mode_sha256()设置算法,再把用户ro_allow::DATA缓冲区中的数据按内核data_buffer(64 字节)容量分片复制,通过SubSliceMut以可变租赁缓冲交给引擎add_mut_data;add_mut_data_done回调返回后,驱动归还内核缓冲区,若仍有剩余数据则继续搬运,否则调用sha.run(buf)触发哈希;因为数据需要从进程内存复制进内核 RAM 缓冲区,驱动统一走"可变数据"路径,add_data_done回调保持空实现;hash_done回调中,驱动把 32 字节摘要复制到用户提供的rw_allow::DEST(长度必须恰为DIGEST_LEN,否则返回NOMEM),随后通过schedule_upcall唤醒进程,并调用check_queue_async()处理下一个排队的应用。
此外,驱动通过processid与 per-app grant 保证同一时刻只有一个进程独占哈希引擎:processid.is_some()时新请求只入队(app.operation记录AppOp::Hash),待当前操作结束后由check_queue取下一个就绪应用。这解释了为什么command 2在已有操作排队时返回NOMEM——per-app 队列仅容纳一个待处理操作。
测试验证:capsule 级 SHA256 测试如何检验实现
仓库中提供了配套的 SHA256 胶囊测试 capsules/extra/src/test/sha256.rs,用于直接验证上述软件实现的正确性,其设计思路可作为阅读引擎实现时的对照:
- 分片注入:测试以
CHUNK_SIZE = 12字节的固定块逐步调用add_mut_data,注释明确说明这样做是为了验证底层缓冲机制能正确处理"填满整块"与"清零未满块"两种路径——12 与 64 互质,可覆盖多种跨块边界; - 校验策略:
TestSha256::new(sha, data, hash, correct)接受期望哈希与correct标志;数据全部注入后调用verify(),verification_done回调中若比对结果与correct不符则 panic,相符则通过CapsuleTestClient::done上报测试完成; - 错误路径:模式设置、数据注入、校验过程中的任何
ErrorCode错误都会触发 panic,确保异常可见。
结合 sha256.rs 中initialize()的 IV 重置、complete_sha256()的长度编码与handle_deferred_call的状态分发,可以完整推演一个哈希请求从"分块注入 → 填充收尾 → 压缩输出 → 回调通知"的完整生命周期。该测试既可在 nRF52840-DK 这类真实硬件上运行,也印证了本测试板配置所验证功能的内部行为。
小结
nrf52840dk-test-sha256是一个"小而完整"的 Tock 内核配置范本:借助nrf52840dk-test-base复用平台基础,仅在main.rs中增量装配软件 SHA-256 引擎与 syscall 驱动,即可在 nRF52840-DK 上获得一个可运行、可通过tockloader烧录、可供用户空间进程调用0x40005驱动完成哈希运算的最小测试环境。配套的组件宏、驱动分片搬运逻辑、DeferredCall 状态机与 capsule 级测试,共同构成了理解 Tock 加密胶囊(crypto capsule)从 HIL 到 syscall 全链路的最佳样例之一。
- 操作系统
- 嵌入式
- 嵌入式OS
【免费下载链接】tock
A secure embedded operating system for microcontrollers
相关推荐
nRF52840-DK SHA256 AppID 测试板:Tock 内核应用凭证检查的最小实现指南
nRF52840 DK SHA256 AppID 测试板:Tock 内核应用凭证检查的最小实现指南 nRF52840 DK SHA256 AppID 测试板(
操作系统嵌入式嵌入式OSTock nRF52840-DK 隔离非易失存储测试板:基于外部 Flash 的存储隔离内核设计与验证
Tock nRF52840 DK 隔离非易失存储测试板:基于外部 Flash 的存储隔离内核设计与验证 本文围绕 Tock 仓库中的 nrf52840dk te
操作系统嵌入式嵌入式OSTock nRF52840-DK 平台使用指南:从内核烧录、应用部署到 UART/RTT 控制台与 JTAG 调试
Tock nRF52840 DK 平台使用指南:从内核烧录、应用部署到 UART/RTT 控制台与 JTAG 调试 本篇技术指南面向 Tock 内核开发者与嵌入
操作系统嵌入式嵌入式OS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考