#applications #devices #setup #developing #signal #t-key #tillitis

bin+lib rustkey

rusTkey — 用于 tillitis TKey 应用程序开发的 rust 库

5 个版本 (3 个重大更新)

0.4.0 2024 年 5 月 2 日
0.3.1 2024 年 4 月 12 日
0.3.0 2024 年 4 月 8 日
0.2.0 2024 年 4 月 2 日
0.1.0 2024 年 3 月 12 日

#204 in 嵌入式开发

GPL-2.0-only

63KB
847

rusTkey ("rusty key")

一个用于在 rust 中开发 tillitis TKey 的裸机应用程序的库。

警告 该库仍在开发中。未来可能会有破坏 API 的更改。

FIXME 考虑使用 GPLv3 许可证,因为与 GPLv2 以及 Apache 2.0 等知名许可证存在兼容性问题。

该crate提供

  • 为新的基于 rust 的 TKey 应用程序的基本设置
  • 与 TKey 设备交互的函数(封装 unsafe 操作)

该库和所提议的设置旨在保持简单。该设置本身不需要外部依赖,链接器脚本和初始汇编代码几乎与 tillitis 提供的完全一致。

注意 几点注意事项

  • rustTkey 提供,以支持 Rust 中的开发,而不是针对 C 的声明。虽然 Rust 因其更安全、更安全的程序而受到赞誉,但我认为这种差异并不总是像宣称的那么大。用 C 编写的应用程序,例如采用仅栈开发方法的应用程序,已经避免了某些问题。在选择之前考虑更大的生态系统可能是有益的。
  • rusTkey 在不正确使用的情况下会 panic:没有必要掩盖或规避不正确的使用。这样做风险更大。相反,断言panic 将表明不正确的使用(或 rusTkey 中的错误)。

更改

变更日志

0.4.0

  • 模块 io
    • 添加了以下受控读取函数:read_availableread_into_checkedread_into_timedread_into_limitedread_checkedread_timed
    • available() 的返回类型更改为 usize,这是它所表示的更合适的数据类型。
    • 添加了配置,用于配置 UART I/O 操作:configuration()configure(bitrate, data_bits, stop_bits)。有关参数详细信息,请参阅文档。《code>bitrate 以设备使用的表示形式表示。
  • rustkey::Error:添加了由受控读取函数使用的变体 UnderrunTimeout
  • timer::PRESCALE_MILLISECONDS,也用于定时读取函数。
  • rustkey::TK1_CPU_FREQUENCY 作为 TKey1 的 CPU 频率。

0.3.1

  • rustkey::random:在不使用 &mut 的情况下(static_mut_refs)对缓冲区进行适当的不安全访问。

0.3.0

  • rustkey::random:如果 TRNG 尚未准备好,则重新混合现有的 TRNG 混沌熵。鉴于 TRNG 的速率约为每秒 66.6 次,这提高了性能。
  • benchbench_trng 重命名为,因为它测试了 TKey 的更多方面。
  • 将更改日志从 CHANGELOG.md 移动到 README.md,以便在可以读取 README 的任何地方都可以访问。

0.2.0

  • 模块 cpumonitor:允许 firstlast 使用相同的地址。
  • 模块 timer
    • 提供 PRESCALE_SECONDS 预分频常量。
    • 提供基于定时器的阻塞睡眠的 sleep
  • 模块 touch:调整并改进 request
  • 模块 trng
    • 关于熵的生产速率的注释
    • 添加 read_next,将 read_next_bytes 重命名为 read_bytes_next
  • 模块 frame 提供了根据协议的最大帧长度的 LENGTH_MAX
  • Error 继承了 Debug 特性。
  • README.md:注释记录了一些配置选项,关于最小化 rust 二进制文件的参考,清理。
  • bench_trng:用于重复采样 TRNG 混沌熵以进行性能测试的应用程序。似乎每秒稳定在约 66.6 个样本。

0.1.0

初始发布。

开放问题

这些都是已知的开放问题。

  • rustkey::random 表示一个应(某种程度上)具有密码学安全的随机字节生成器。这尚未得到验证。
    它使用固件中的 blake2s 来生成随机数,使用持久性缓冲区来维护使用之间的某些状态,并使用来自 TRNG 的字节将新的熵引入混合。使用 seed 参数可以手动注入额外的熵。
  • cargo build 警告不稳定功能。也许我们应该为此创建一个问题。
    cargo build --release --bins
    warning: unknown and unstable feature specified for `-Ctarget-feature`: `zmmul`
      |
      = note: it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
      = help: consider filing a feature request
    

除了这里列出的项目外,我还为需要考虑的事情留下了 TODO 注释,即使它们最终被证明是不相关的。

决策

  • 不使用堆/动态内存分配(目前)。
  • 不线程安全(直到使用std或自定义线程时才需要)。
  • 在启动执行方面不使用依赖项(例如rust-embedded/riscv
    1. 增加了一层不透明性,非直接的逻辑。
    2. 更全面的初始化,解决了不支持的功能,执行了不必要的初始化。
    3. 相反,在全局汇编中初始化时,从入口点(链接脚本)到初始化再到main的执行是明显的。
  • 不正确使用函数可能会导致panic。
  • 实现(在罕见情况下)可能会更改。
    例如,如果random在合理约束下可以改进。

入门

以下描述了构建加载到TKey的应用程序二进制文件所需的应用程序设置。

以下步骤是必须的

  • riscv32i-unknown-none-elf配置构建,并添加额外的CPU功能 'c'(压缩指令)和'Zmmul'(仅乘法指令,即不除法)。其他构建选项有助于生成更小的二进制文件,以便它可以装入128 KiB的内存。
  • 应用合适的链接脚本。
  • 创建一个入口点,用于执行应用程序的TKey设备的基本初始化。
  • 使用llvm-objcopytillitis TKey创建原始裸机应用程序二进制文件。
  • 应用程序:src/main.rs是一个使用此设置并演示工作TKey-app的示例。

配置构建

构建目标riscv32i-unknown-none-elf必须在rust生态系统中可用。

rustup target add riscv32i-unknown-none-elf

以下构建配置启用了必要的功能和选项。

.cargo/config.toml:

[build]
target = "riscv32i-unknown-none-elf"

[profile.dev]
panic = "abort"

[profile.release]
codegen-units = 1 # No parallelism, may increase ability to optimize.
debug = false # Do not produce a debug-build.
debug-assertions = false # Drop debug-assertions.
opt-level = 3 # Perform a high level of optimization, also 'z' to specifically target size.
overflow-checks = false
strip = true # Currently translates to 'strip = "symbols"', i.e. strip all excess information.
lto = true # Perform link-time optimization, to further optimize and reduce binary size.
panic = "abort"

[target.riscv32i-unknown-none-elf]
rustflags = [
	"-Ctarget-feature=+c,+zmmul",
	"-Ccode-model=medium",
	"-Cpanic=abort",
	"-Crelocation-model=pic",
	"-Clink-arg=-Tapp.lds",
]

此配置适用于--release构建。

注意:panic设置应该消除在链接脚本中需要.eh_frame部分的需求,但当前并非如此。

链接脚本

以下链接脚本定义了TKey的内存区域。

对原始链接脚本的变化

  • 定义_stack_start符号,以便可以在汇编代码之外修改此地址。
  • _stack_start被分配了不同的值:'ORIGIN(RAM)+LENGTH(RAM)'从原始值'0x4001fff0。原始值略低,我怀疑这是不必要的,因为在使用之前已经操纵了堆栈指针。(问题
  • .text区域添加了*(.eh_frame)
    这不应该有必要,因为 panic=abort 应该会消除对 .eh_frame 节的需要,根据许多参考资料和我的理解,因为它禁用了堆栈展开。不应该需要保留额外数据。然而,这种改变却非常突然,没有明确的解释。添加 .eh_frame 节会产生一个适应这种情况的内存布局,尽管如果我们能完全去掉它会更好。
/*
 * Copyright (C) 2022, 2023 - Tillitis AB
 * SPDX-License-Identifier: GPL-2.0-only
 */

OUTPUT_ARCH( "riscv" )
ENTRY(_start)

MEMORY
{
	RAM (rwx)   : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
}

SECTIONS
{
	.text.init :
	{
		*(.text.init)
	} >RAM

	.text :
	{
		. = ALIGN(4);
		*(.text)           /* .text sections (code) */
		*(.text*)          /* .text* sections (code) */
		*(.rodata)         /* .rodata sections (constants, strings, etc.) */
		*(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
		*(.srodata)        /* .rodata sections (constants, strings, etc.) */
		*(.srodata*)       /* .rodata* sections (constants, strings, etc.) */
		*(.eh_frame)
		. = ALIGN(4);
		_etext = .;
		_sidata = _etext;
	} >RAM

	.data : AT (_etext)
	{
		. = ALIGN(4);
		_sdata = .;
		. = ALIGN(4);
		*(.data)           /* .data sections */
		*(.data*)          /* .data* sections */
		*(.sdata)          /* .sdata sections */
		*(.sdata*)         /* .sdata* sections */
		. = ALIGN(4);
		_edata = .;
	} >RAM

	/* Uninitialized data section */
	.bss :
	{
		. = ALIGN(4);
		_sbss = .;
		*(.bss)
		*(.bss*)
		*(.sbss)
		*(.sbss*)
		*(COMMON)

		. = ALIGN(4);
		_ebss = .;
	} >RAM
}

_stack_start = ORIGIN(RAM) + LENGTH(RAM);

创建入口点

以下(全局)汇编代码创建入口点 _start,在此之后设备被初始化用于加载的应用程序,之后调用函数 main

设备从 'main' 返回时的响应似乎取决于应用程序二进制文件的优化级别。最好将 main 定义为 '#[no_mangle] extern "C" fn main() -> !',即非返回,以避免遇到这种情况。

对原始汇编代码的修改

  • 以期望符号 _stack_start 在连接器脚本中定义,用于初始化堆栈指针。
// Note: this assembly provides the initialization procedure that clears memory and finally calls
// `main` to start execution of the app-binary.
global_asm!(
    ".section \".text.init\"",
    ".global _start",
    "_start:",
    "li x1, 0",
    "li x2, 0",
    "li x3, 0",
    "li x4, 0",
    "li x5, 0",
    "li x6, 0",
    "li x7, 0",
    "li x8, 0",
    "li x9, 0",
    "li x10,0",
    "li x11,0",
    "li x12,0",
    "li x13,0",
    "li x14,0",
    "li x15,0",
    "li x16,0",
    "li x17,0",
    "li x18,0",
    "li x19,0",
    "li x20,0",
    "li x21,0",
    "li x22,0",
    "li x23,0",
    "li x24,0",
    "li x25,0",
    "li x26,0",
    "li x27,0",
    "li x28,0",
    "li x29,0",
    "li x30,0",
    "li x31,0",
    /* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
    "la sp, _stack_start",
    /* zero-init bss section */
    "la a0, _sbss",
    "la a1, _ebss",
    "bge a0, a1, end_init_bss",
    "loop_init_bss:",
    "sw zero, 0(a0)",
    "addi a0, a0, 4",
    "blt a0, a1, loop_init_bss",
    "end_init_bss:",
    "call main",
    options(raw)
);

创建原始应用程序二进制文件

使用 'llvm-objcopy --input-target=elf32-littleriscv --output-target=binary target/riscv32i-unknown-none-elf/release/example example.bin' 生成加载到 TKey 上的原始二进制数据。

应用程序

此时,设置已经完成,直到进入函数 main 的应用程序。

有一些推荐

  • '#![no_std]' 以防止包含标准库。这也意味着 std::* 不可用,尽管大多数基本函数也可以在 core::* 中找到。
  • '#![no_main]' 以指示我们提供了进入应用程序的自己的入口点
  • 定义 '#[no_mangle]extern"C"fnmain()->!' 作为我们 main 的定义,以便
    • main 可以在初始化后找到,
    • #[no_mangle]extern"C" 确保在预期的未更改的函数名下可用,
    • 不返回,以避免遇到未定义的行为和/或使设备不可用。
  • 因为运行时间最小化,所以需要定义一个 #[panic_handler]
  • heapless 提供了无需动态内存分配的静态友好数据结构。

在此代码库中可以找到基本示例 src/main.rs

替代方案

另一种方法,是使用 #![feature(start)] 宏与 #[start] 注释的 fnmain()。这目前仅在激活 Rust nightly 功能时才可行。这,加上适当的构建配置,以确保 main 作为函数以未混淆的形式可用,足以让应用程序运行。不幸的是,这并不能解决上面描述的 .eh_frame 问题。

目前,此 crate 将在应用程序中使用 global_asm! 宏。这从链接脚本 (app.lds) 到全局汇编 (_start) 到调用 main() 创建了一条直接路径。这也为调用需要早期初始化的 堆栈保护 和其他安全功能留下了可能性,所有这些都集中在应用程序中。

许可

rusTkey, a rust crate/library that provides a development API for the
tillitis TKey.
Copyright (C) 2024  Danny van Heumen

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

有关完整许可信息,请参阅 LICENSE

参考

无运行时依赖