- 操作系统
- 嵌入式
- 嵌入式OS
【免费下载链接】tock
A secure embedded operating system for microcontrollers
本文围绕 Tock 仓库中的nrf52840dk-test-invs测试板内核展开:它是一套用于验证“隔离非易失存储”(Isolated Nonvolatile Storage)的最小内核,将存储区域放在 nRF52840-DK 板载外部 SPI Flash 上,并为每个应用划分互不干扰的私有存储地址空间。读完本文,你将掌握该测试板的硬件接线、驱动装配方式、存储权限策略以及0x50004系统调用接口的完整使用细节,并了解如何在真实硬件上构建与烧录这套测试内核。
一、测试板定位:一个为“存储隔离”而生的最小内核
boards/configurations/nrf52840dk/nrf52840dk-test-invs/README.md 对该板给出了极为简洁的定义:
This is a minimal kernel for testing isolated storage. The board uses the external flash for the storage area.
即:这是一套用于测试隔离存储的最小内核,存储区域使用外部 Flash。它隶属于 Tock 仓库boards/configurations/nrf52840dk/下的 nRF52840-DK 测试板族,目录内还包含同为测试用途的nrf52840dk-test-base、nrf52840dk-test-appid-*、nrf52840dk-test-dynamic-app-load、nrf52840dk-test-kernel等配置,便于在固定硬件平台上快速验证内核子系统的行为。
与普通产品级板级支持包(BSP)不同,这类测试板刻意保持“最小化”:它不追求外设全量覆盖,而是聚焦单一验证目标。本板的完整源码位于 src/main.rs,从代码结构看,其核心链路为:
SPIM0 外设 → SpiMux 复用 → MX25R6435F 外部 SPI Flash → NonvolatileToPages(页对齐转换层) → IsolatedNonvolatileStorage 驱动(grant 为每个 App 分配私有存储区) → 0x50004 系统调用接口(受 StoragePermissions 保护)整个内核复用 nrf52840dk-test-base 提供的基础平台(调度器、UART、定时器等),在其上叠加存储相关组件,最终通过SyscallDriverLookup把0x50004驱动号路由到隔离存储驱动,其余驱动号全部委托给基础平台。
二、硬件接线与外部 Flash 初始化
2.1 SPI 引脚定义
板载外部 Flash 通过 nRF52840 的SPIM0外设驱动,引脚定义集中在 src/main.rs:
const SPI_MOSI: Pin = Pin::P0_20; const SPI_MISO: Pin = Pin::P0_21; const SPI_CLK: Pin = Pin::P0_19; const SPI_MX25R6435F_CHIP_SELECT: Pin = Pin::P0_17; const SPI_MX25R6435F_WRITE_PROTECT_PIN: Pin = Pin::P0_22; const SPI_MX25R6435F_HOLD_PIN: Pin = Pin::P0_23;| 信号 | nRF52840 引脚 | 用途 |
|---|---|---|
| SPI_CLK | P0_19 | SPI 时钟 |
| SPI_MOSI | P0_20 | 主出从入 |
| SPI_MISO | P0_21 | 主入从出 |
| SPI_MX25R6435F_CHIP_SELECT | P0_17 | Flash 片选 |
| SPI_MX25R6435F_WRITE_PROTECT_PIN | P0_22 | 写保护(可选配置) |
| SPI_MX25R6435F_HOLD_PIN | P0_23 | HOLD 引脚(可选配置) |
2.2 SPIM0 与 SpiMux 装配
在main()中,代码先取出基础平台返回的base_peripherals,把spim0接入SpiMuxComponent做总线复用(因为外部 Flash 并非 SPI 总线的唯一潜在使用者),并完成引脚复用配置:
let mux_spi = components::spi::SpiMuxComponent::new(&base_peripherals.spim0) .finalize(components::spi_mux_component_static!(nrf52840::spi::SPIM)); base_peripherals.spim0.configure( nrf52840::pinmux::Pinmux::new(SPI_MOSI), nrf52840::pinmux::Pinmux::new(SPI_MISO), nrf52840::pinmux::Pinmux::new(SPI_CLK), );随后通过Mx25r6435fComponent实例化 MX25R6435F 驱动,把片选、写保护、HOLD 三个 GPIO 引脚、复用后的 SPI 总线以及mux_alarm(用于 Flash 操作超时/延时)注入组件:
let mx25r6435f = components::mx25r6435f::Mx25r6435fComponent::new( Some(&nrf52840_peripherals.gpio_port[SPI_MX25R6435F_WRITE_PROTECT_PIN]), Some(&nrf52840_peripherals.gpio_port[SPI_MX25R6435F_HOLD_PIN]), &nrf52840_peripherals.gpio_port[SPI_MX25R6435F_CHIP_SELECT], mux_alarm, mux_spi, ) .finalize(components::mx25r6435f_component_static!( nrf52840::spi::SPIM, nrf52840::gpio::GPIOPin, nrf52840::rtc::Rtc ));该组件以SPIM、GPIOPin、Rtc为泛型参数,最终得到一个实现了hil::flash::Flash特征的 Flash 客户端,供上层存储驱动使用。
三、隔离非易失存储驱动装配
3.1 核心概念:隔离存储 vs 普通存储
隔离非易失存储组件定义在 boards/components/src/isolated_nonvolatile_storage.rs,其文档注释明确区分了它与普通非易失存储驱动的差异:
This differs from NonvolatileStorageComponent in that it provides isolation between apps. Each app has its own storage address space (that starts at 0) which doesn't interfere with other apps.
即:每个应用拥有从地址 0 开始的私有存储地址空间,互不干扰。上层应用感知到的偏移始终是“自己区域内的偏移”,而内核负责把该偏移映射到物理 Flash 的真实地址,这是“隔离”语义的关键。
3.2 组件实例化参数
src/main.rs 中实例化隔离存储组件:
let invs = components::isolated_nonvolatile_storage::IsolatedNonvolatileStorageComponent::new( board_kernel, capsules_extra::isolated_nonvolatile_storage_driver::DRIVER_NUM, mx25r6435f, 0x40000, // start address 0x100000, // length create_capability!(capabilities::MemoryAllocationCapability), ) .finalize(components::isolated_nonvolatile_storage_component_static!( Mx25r6435f, APP_STORAGE_REGION_SIZE ));各参数含义如下:
| 参数 | 本板取值 | 说明 |
|---|---|---|
board_kernel | 内核实例 | 用于创建 grant(board_kernel.create_grant(driver_num, &mem_cap)) |
driver_num | DRIVER_NUM(即0x50004) | 系统调用驱动号,对应 doc/syscalls/50004_isolated_nonvolatile_storage.md |
flash | mx25r6435f | 底层 Flash 驱动(外部 SPI Flash) |
userspace_start | 0x40000(256 KiB) | 用户空间可访问存储区域的起始物理地址 |
userspace_length | 0x100000(1 MiB) | 用户空间可访问存储区域长度 |
APP_STORAGE_REGION_SIZE | 4096(4 KiB) | 每个应用私有存储区大小 |
其中APP_STORAGE_REGION_SIZE在 src/main.rs 定义为:
const APP_STORAGE_REGION_SIZE: usize = 4096;它作为常量泛型传给驱动,决定每个应用每次新分配存储区时的固定大小(当前实现中区域不可增长,所以该值就是应用最终获得的存储量)。组件源码中还提供了默认值ISOLATED_NONVOLATILE_STORAGE_APP_REGION_SIZE_DEFAULT = 2048(见 isolated_nonvolatile_storage.rs),本板显式覆盖为 4096,即每个应用可获得 4 KiB 私有存储。
3.3 底层装配细节:NonvolatileToPages 与 grant
finalize()阶段(isolated_nonvolatile_storage.rs)完成三层对象的静态初始化:
- 页缓冲区:
<F as hil::flash::Flash>::Page::default()分配一页 Flash 读写缓冲区; NonvolatileToPages转换层:capsules_extra::nonvolatile_to_pages::NonvolatileToPages把上层“任意偏移 + 任意长度”的读写请求翻译成底层 Flash 的“页对齐擦写”操作,并通过hil::flash::HasClient::set_client挂接为 Flash 驱动客户端;IsolatedNonvolatileStorage驱动本体:通过board_kernel.create_grant(driver_num, &mem_cap)创建 grant,把每个应用可访问的存储区状态(分配标记、基址等)保存在内核 grant 区中,实现进程级隔离。
随后hil::nonvolatile_storage::NonvolatileStorage::set_client(nv_to_page, nonvolatile_storage)把转换层与驱动双向连接,构成完整调用链:应用系统调用 → 驱动 → NonvolatileToPages → Flash 驱动 → 外部 SPI Flash。
静态内存由isolated_nonvolatile_storage_component_static!宏(isolated_nonvolatile_storage.rs)一次性分配Page、NonvolatileToPages、IsolatedNonvolatileStorage以及BUF_LEN字节的内部缓冲,符合 Tock 无堆分配(static_init)的内存模型。
四、存储权限策略:invs_permissions.rs
隔离存储接口受StoragePermissions保护(doc/syscalls/50004_isolated_nonvolatile_storage.md 开篇即注明),因此测试板必须实现一套权限分配策略。本板在 src/invs_permissions.rs 中实现了自定义策略InvsStoragePermissions。
其核心逻辑在get_permissions()(invs_permissions.rs):
match process.short_app_id() { ShortId::Fixed(id) => { if process.get_tbf_storage_permissions().is_some() { self.tbf_permissions.get_permissions(process) } else { StoragePermissions::new_self_only(id, &self.cap) } } ShortId::LocallyUnique => StoragePermissions::new_null(), }策略可概括为三条规则:
| 进程 ShortId 类型 | TBF 头是否含存储权限 | 分配的权限 |
|---|---|---|
ShortId::Fixed | 是 | 采用 TBF 头中声明的存储权限(TbfHeaderStoragePermissions) |
ShortId::Fixed | 否 | 默认“仅访问自身状态”权限(new_self_only) |
ShortId::LocallyUnique | — | 空权限(new_null),无法访问任何存储 |
其中TbfHeaderStoragePermissions来自capsules_system::storage_permissions::tbf_header,即进程二进制(TBF)格式头中携带的存储权限声明;new_self_only允许应用读写自己的私有区域;new_null完全禁止存储访问。这种“TBF 优先、默认自访问、局部唯一 ID 一律禁止”的分层设计,正好覆盖了测试隔离存储所需的典型权限场景,相关设计背景可进一步参考 doc/reference/trd-storage-permissions.md。
在 src/main.rs 中,该策略通过自定义 capabilityAppStoreCapability(实现ApplicationStorageCapability)实例化,并注入进程加载器:
let storage_permissions_policy = static_init!( invs_permissions::InvsStoragePermissions< nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>, kernel::process::ProcessStandardDebugFull, AppStoreCapability, >, invs_permissions::InvsStoragePermissions::new(AppStoreCapability) );五、进程加载与凭证检查链路
测试板还组装了完整的进程加载管线(src/main.rs),保证存储隔离测试在真实的 Tock 进程模型下进行:
- 软件 SHA-256 引擎:
ShaSoftware256Component提供纯软件哈希,避免依赖硬件加密单元; - 凭证检查策略:
AppCheckerSha256Component基于 SHA-256 校验进程凭证(AppID); - AppID 分配器:
AppIdAssignerNamesComponent按进程名称分配 AppID; - 进程检查机:
ProcessCheckerMachineComponent包装检查策略,形成异步检查状态机; - 顺序进程加载器:
ProcessLoaderSequentialComponent接收检查器、分配器、存储权限策略以及app_flash/app_memory区域(由链接脚本符号_sapps/_eapps/_sappmem/_eappmem界定),在启动时逐个加载并验证应用镜像。
由此可见,存储权限策略被集成在进程加载阶段——应用只有在通过凭证检查并获得 AppID 后,其存储访问权限才会按上一节的三条规则被计算出来。
六、系统调用接口:0x50004
应用通过驱动号0x50004(capsules_extra::isolated_nonvolatile_storage_driver::DRIVER_NUM)访问隔离存储。完整协议定义见 doc/syscalls/50004_isolated_nonvolatile_storage.md,支持 32 位与 64 位地址空间。
6.1 Command
| Command | 名称 | 参数 1 / 参数 2 | 说明 |
|---|---|---|---|
0 | 驱动存在性检查 | 未使用 | 成功返回SUCCESS,否则NODEVICE |
1 | Get Size | 未使用 | 查询应用存储区大小(字节);若尚未分配则触发分配,结果经 upcall 异步返回 |
2 | Read | 读取偏移低 32 位 / 高 32 位 | 异步读取,数据拷贝到 RW Allow 0 缓冲区;读取长度等于缓冲区长度 |
3 | Write | 写入偏移低 32 位 / 高 32 位 | 异步写入,数据来自 RO Allow 0 缓冲区;写入长度等于缓冲区长度 |
Command 1/2/3 的同步失败返回码:
NOSUPPORT:应用无存储访问权限;BUSY:前一个请求仍在进行中。
6.2 Subscribe(upcall)
| Subscribe | 名称 | Upcall 签名 | 说明 |
|---|---|---|---|
0 | Get Size 完成 | fn upcall(s: Statuscode, length: usize) | 参数 0 为状态码,参数 1 为区域大小(仅SUCCESS时有效) |
1 | Read 完成 | fn upcall(s: Statuscode) | 读取命令完成或出错时触发 |
2 | Write 完成 | fn upcall(s: Statuscode) | 写入命令完成或出错时触发 |
各 upcall 的Statuscode取值:
| 状态码 | Get Size | Read | Write |
|---|---|---|---|
SUCCESS | 返回区域大小 | 已读取length字节到缓冲区 | 已写入length字节 |
RESERVE | — | RW Allow 0 未提供或长度为 0 | RO Allow 0 未提供或长度为 0 |
NOMEM | 无剩余空间可分配 | 应用没有存储区域 | 应用没有存储区域 |
NOSUPPORT | 无存储权限 | 无存储权限 | 无存储权限 |
INVAL | — | 读取超出应用存储区 | 写入超出应用存储区 |
FAIL | — | 底层存储访问出错 | 底层存储访问出错 |
6.3 Allow
- Read-Only Allow 0:写入非易失存储时使用的数据缓冲区;
- Read-Write Allow 0:从非易失存储读取数据时使用的缓冲区。
由于读写均为异步操作,应用需要先通过allow共享缓冲区、再下发 Command 2/3,最后在对应 upcall 中确认结果。
七、构建与烧录
7.1 目录与构建配置
该测试板是一个独立的 Cargo 包,Cargo.toml 依赖本仓库内的nrf52840dk-test-base、components、kernel、nrf52840、segger、nrf52_components以及capsules-core/capsules-extra/capsules-system,全部走 workspace 路径引用,不依赖外部 crate 即可编译。
链接脚本 layout.ld 依次包含两块:
INCLUDE ../../../nordic/nrf52840_chip_layout.ld INCLUDE tock_kernel_layout.ld即先引入 nRF52840 芯片内存布局(Flash/RAM 分区),再引入 Tock 标准内核布局(内核与应用的 ROM/RAM 区域划分)。其中_sapps、_eapps、_sappmem、_eappmem等符号正是由这些链接脚本定义,进程加载器据此确定应用镜像与内存区域。
7.2 编译
与 Tock 其他板卡一致,在该板目录下执行:
makeMakefile仅两行,依次引入 Makefile.common(workspace 构建规则)与 nrf52840dk.mk(nRF 测试板共享规则),产物为target/<target-triple>/release/nrf52840dk-test-invs.bin。
7.3 烧录
nrf52840dk.mk 定义了基于tockloader的烧录流程:
make flash:通过J-Link烧录内核到KERNEL_ADDRESS=0x00000,目标板参数--board nrf52dk;make flash-openocd:通过OpenOCD烧录,其余参数相同;make install:等价于flash。
示例(指定串口端口时):
make flash PORT=/dev/ttyACM0烧录内核后,即可通过tockloader加载测试应用;应用需预先在 TBF 头中声明(或按默认策略获得)合适的存储权限,才能通过0x50004驱动号访问其 4 KiB 私有存储区。
八、小结:这套测试内核验证了什么
nrf52840dk-test-invs以最小化内核的形式,完整覆盖了 Tock 隔离非易失存储子系统的三个关键环节:
- 隔离语义:通过
IsolatedNonvolatileStorageComponent+ grant 机制,为每个应用分配独立的APP_STORAGE_REGION_SIZE(本板 4096 字节)私有存储区,应用偏移从 0 开始、互不可见; - 权限模型:
InvsStoragePermissions演示了“TBF 头权限优先、默认自访问、局部唯一 ID 拒绝”的分层策略,与 doc/reference/trd-storage-permissions.md 描述的存储权限体系对应; - 真实硬件路径:整条链路跑在 nRF52840-DK 的 SPIM0 → MX25R6435F 外部 SPI Flash 上,经
NonvolatileToPages完成任意偏移读写到页对齐擦写的转换,并通过0x50004驱动号的 Command/Subscribe/Allow 协议与用户态应用交互。
对于希望在自有平台上接入隔离存储的开发者,该测试板是一份可以直接对照的参考实现:从引脚配置、组件装配、权限策略到进程加载集成的完整代码路径,均可在 boards/configurations/nrf52840dk/nrf52840dk-test-invs/ 目录中逐一查阅。
- 操作系统
- 嵌入式
- 嵌入式OS
【免费下载链接】tock
A secure embedded operating system for microcontrollers
相关推荐
Longhorn 存储网络隔离设计与实现:基于 Instance Manager gRPC Proxy 的 Storage Network 方案
Longhorn 存储网络隔离设计与实现:基于 Instance Manager gRPC Proxy 的 Storage Network 方案 导读 本文深入
云原生存储高可用容器编排nRF52840-DK SHA256 AppID 测试板:Tock 内核应用凭证检查的最小实现指南
nRF52840 DK SHA256 AppID 测试板:Tock 内核应用凭证检查的最小实现指南 nRF52840 DK SHA256 AppID 测试板(
操作系统嵌入式嵌入式OS5分钟快速上手ImportExcel:无需Excel创建专业报表
5分钟快速上手ImportExcel:无需Excel创建专业报表 ImportExcel是一款强大的PowerShell模块,让你无需安装Microsoft E
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考