下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

在 ARMv8-M 微控制器上使用 FreeRTOS

[另请参阅描述使用 FreeRTOS 时如何设置 ARM Cortex-M 中断优先级的页面。]

ARM 将 TrustZone 引入使用 ARMv8-M 架构的 Cortex-M 系列微控制器。TrustZone 为可选安全扩展,可在单个处理器内启用两个安全 域。包含 TrustZone 的 Cortex-M 核心(包括 Cortex-M33 和 Cortex-M23)用它将执行空间分割为 安全 ('s') 和非安全 ('ns') 分区(或“端”)。此举增强安全性的方法为 实现安全端执行的可信任软件和 非安全端执行的不可信任软件之间的完全隔离。安全端和非安全端 都有单独的内存保护单元 (MPU), 可在两个安全域内实现 额外的隔离。

在安全端运行的软件可将其想要提供的功能导出 至非安全端运行的软件。这些功能必须 放置在明确标记为非安全可调用 (NSC) 的内存区域中。非安全 软件只能通过 NSC 内存中这些导出的功能来访问安全软件 。

ARMv8-M 架构还引入了堆栈限制寄存器, 可使应用软件在堆栈溢出时立即将其捕获。

以下小节描述了 在 ARMv8-M 核心上运行 FreeRTOS 时可用的运行时间和配置选项:


ARMv8-M 应用程序剖析

使用 TrustZone 的应用程序由两个单独的项目组成:

  1. 在安全端运行的安全应用程序。
  2. 在非安全端运行的非安全应用程序。

ARMv8-M Core 始终启动到安全端。然后由安全软件负责 对安全属性单元 (SAU) 和 实现定义属性单元 (IDAU) 进行编程,在分支到非安全软件之前将储存空间划分为 安全区和非安全区。 安全端的软件可以访问安全内存和非安全内存, 而非安全端的软件只能访问非安全内存。

通常,在安全端运行的软件需要稳健可靠,因此会尽量保持该软件 小巧——通常仅限于提供系统的可信根 (安全启动、身份、加密功能等)。然后,会使用非安全端上的内存保护单元 (MPU), 以较低特权水平运行非安全任务, 并按线程对 线程上的外围设备(RTOS 任务)和内存提供细粒度访问控制。

FreeRTOS ARMv8-M 移植的特点

FreeRTOS ARMv8-M (ARM CORTEX-M33 和 ARM CORTEX-M23) 移植:
  • 可以在安全端或非安全端运行。
  • 允许非安全任务(或线程)调用安全端可信 函数(通过 NSC 内存中的指定进入点), 这些函数可以反过来调用非安全函数,所有这些都不会违反 所述内核的优先调度策略。
  • 可选支持 TrustZone(当 FreeRTOS 内核在非安全端运行时)。
  • 可选支持内存保护单元 (MPU)。
  • 可选支持浮点单元 (FPU)。
  • 仅允许源自 FreeRTOS 内核代码的特权升级。
通常情况下,FreeRTOS 调度程序在非安全端运行,并且 每个非安全 FreeRTOS 任务都可以调用由安全端软件导出的函数。 因此,应用程序开发人员能够将关键软件放在安全端,并 确保非安全软件中的漏洞不会影响关键的安全软件。

使用 MPU 使非安全端的任务具有较低的特权级别,可以彼此隔离,并且 与内核隔离开。

快速启动

此页面正文提供了关于将 FreeRTOS 构建到 ARMv8-M 微控制器的详细信息,但最简单的开始方法是使用 以下任一预配置的示例项目:

示例 ARMv8-M 项目位于 主 FreeRTOS zip 文件下载中的 FreeRTOS/Demo。这些项目 可以直接使用,也可单纯用作源文件、配置选项 和下方详细介绍的编译器设置的工作示例和参考。


FreeRTOS ARMv8-M 移植使用说明

FreeRTOS ARMv8-M 移植使用以下编译时间宏以启用或禁用 TrustZone、内存保护单元 (MPU) 和浮点单元 (FPU) 支持。本页的以下 章节介绍了相关的使用详情。


/* Set to 1 when running FreeRTOS on the non-secure side to enable the

* ability to call the (non-secure callable) functions exported from secure side. */

#define configENABLE_TRUSTZONE 1

/* Set to 1 when running FreeRTOS on the secure side. Note that in this case TrustZone is

* not supported as secure tasks cannot call non-secure code i.e. configENABLE_TRUSTZONE

* must be set to 0 when setting configRUN_FREERTOS_SECURE_ONLY to 1. */

#define configRUN_FREERTOS_SECURE_ONLY 1

/* Set to 1 to enable the Memory Protection Unit (MPU), or 0 to leave the Memory

* Protection Unit disabled. */

#define configENABLE_MPU 1

/* Set to 1 to enable the Floating Point Unit (FPU), or 0 to leave the Floating

* Point Unit disabled. */

#define configENABLE_FPU 1

/* Set to 1 to enable the M-Profile Vector Extension (MVE) support, or 0 to

* leave the MVE support disabled. This option is only applicable to Cortex-M55

* and Cortex-M85 ports as M-Profile Vector Extension (MVE) is available only on

* these architectures. configENABLE_MVE must be left undefined, or defined to 0

* for the Cortex-M23 Cortex-M33 and Cortex-M35P ports. */

#define configENABLE_MVE 1
在 FreeRTOS ARMv8-M 移植中启用对多种功能的支持


使用 FreeRTOS(有 TrustZone 支持)

说明

当 FreeRTOS 在非安全端运行时, FreeRTOS ARMv8-M 移植中的 TrustZone 支持使非安全 FreeRTOS 任务能够调用 已被标记为非安全可调用 (NSC) 的安全函数。安全函数 是在安全端执行的函数。


FreeRTOSConfig.h 设置

要启用 TrustZone 支持,请通过将 configENABLE_TRUSTZONE 设置为 1 构建 FreeRTOS 设置位置: FreeRTOSConfig.h

#define configENABLE_TRUSTZONE 1
在 FreeRTOS 中启用 TrustZone 支持

从ARMv8-M MCU 不安全的一侧调用安全函数的任务 有两种上下文,一种在非安全 端,另一种在安全端。在 FreeRTOS v10.4.5 之前, ARMv8-M 安全端移植 分配参考安全端运行时上下文的结构体。从 FreeRTOS V10.4.5 开始,便在编译时对结构进行静态分配。 secureconfigMAX_SECURE_context 设置了静态分配的安全 上下文数量。 secureconfigMAX_SECURE_context 则默认为 8(如未定义)。 仅使用 ARMv8-M 微控制器非安全端上的 FreeRTOS 代码的应用程序 (例如,在安全端上运行第三方代码的应用程序)无需此常量。

#define secureconfigMAX_SECURE_CONTEXTS 8
定义安全上下文的最大数量


构建移植

FreeRTOS 内核源代码组织页面 包含有关向项目添加 FreeRTOS 内核的信息。除 该页面上的信息外 ,使用 TrustZone 支持的 FreeRTOS ARMv8-M 移植 还需要在安全项目和 非安全项目中编译的其他移植文件。

  • 将在安全项目中编译的源文件FreeRTOS/Source/portable/[compiler]/[architecture]/secure

  • 将在非安全项目中编译的源文件FreeRTOS/Source/portable/[compiler]/[architecture]/non_secure
其中 [架构] 为 ARM_CM23 还是 ARM_CM33,具体取决于 所述目标硬件是 ARM Cortex-M23 还是 ARM Cortex-M33。

注 1 : GCC 移植也可与 ARM 编译器版本 6 及以上 (ARM Clang) 一起使用 。

注 2 : 上述两个目录也必须包含在 包括相应编译器项目的 include 路径。


将安全函数定义为非安全可调用 (NSC)

使用 secureportNON_SECURE_CALLABLE 宏可从非安全端 调用安全函数。

secureportNON_SECURE_CALLABLE void NSCFunction( void );
使用 secureportNON_SECURE_callable 宏使
安全端函数 NSCFunction() 可从非安全任务
调用的示例

然后,安全项目的链接器脚本需要确保 非安全可调用函数放置在明确标记为非安全可调用的内存区域中 。以下是 用于将非安全可调用函数放置在 NSC 内存中的 GCC 语法:


MEMORY
{
/* Define each memory region. */
PROGRAM_FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 0xfe00
veneer_table (rx) : ORIGIN = 0x1000fe00, LENGTH = 0x200
Ram0 (rwx) : ORIGIN = 0x30000000, LENGTH = 0x8000
}

/* Veneer Table Section (Non-Secure Callable). */
.text_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.text_veneer_table*)
*(.text.$veneer_table*)
*(.rodata.$veneer_table*)
} > veneer_table
用于将非安全可调用函数放置在 NSC 内存中的 GCC 语法

主 FreeRTOS 发行版本中的预配置示例项目包含 各种其他编译器的示例。


为非安全任务分配安全上下文

任何要调用安全端函数的非安全 FreeRTOS 任务必须首先 通过调用 portALOCATE_SECURE_CONTEXT 宏分配安全上下文:

/* This task calls secure side functions. So allocate a secure

* context for it. */

portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE );
为非安全任务分配安全上下文

建议非安全任务首先进行安全调用以分配 安全上下文。



使用 FreeRTOS(无 TrustZone 支持)

说明

应用程序编写人员可以选择在硬件中禁用 TrustZone。完成此操作后, 微控制器启动为非安全,并且整个内存空间都为非安全。


FreeRTOSConfig.h 在无 TrustZone 支持的情况下在非安全端运行 FreeRTOS 的设置

要禁用 TrustZone 支持,请通过将 configENABLE_TRUSTZONE 设为 0 构建 FreeRTOS, 设置位置为: FreeRTOSConfig.h 文件

#define configENABLE_TRUSTZONE 0
在 FreeRTOS 中禁用 TrustZone 支持


在无 TrustZone 支持的情况下在安全端运行 FreeRTOS 的 FreeRTOSConfig.h 设置

如果应用程序编写人员不希望使用 TrustZone,但硬件 不支持禁用 TrustZone ,然后整个应用程序(包括 FreeRTOS 调度器)都可以在安全端运行,而无需分支到 非安全端。为此,除了将 configENABLE_TRUSTZONE 设置为 0 之外, 还需将 configRUN_FREERTOS_SECURE_ONLY 设置为 1。
#define configENABLE_TRUSTZONE 0
#define configRUN_FREERTOS_SECURE_ONLY 1
仅在安全端运行 FreeRTOS


构建移植

FreeRTOS 内核源代码组织页面 包含有关向项目添加 FreeRTOS 内核的信息。不支持 TrustZone 的 FreeRTOS ARMv8-M 移植的移植文件位于 以下目录: FreeRTOS/Source/portable/[compiler]/[architecture]/non_secure

其中 [架构] 为ARM_CM23_NTZ或ARM_CM33_NTZ ,具体取决于 所述目标硬件是ARM Cortex-M23 还是 ARM Cortex-M33。

注意1 : GCC 移植也可与 ARM 编译器版本 6 及以上版本一起使用 (ARM CLANG) 。

注意2 : 上述目录也必须包含在 包括相应编译器项目的路径。



使用 FreeRTOS(有内存保护单元 (MPU) 支持)

说明

端口中的内存保护单元 (MPU) 支持使 FreeRTOS ARMv8-M 应用程序任务能以特权或非特权(用户)模式执行,并按任务提供 细粒度内存和外围访问控制。

非特权任务:

  1. 使用 xTaskCreateRestricted() API 创建 。
  2. 默认为除了自己的堆栈之外没有其他 RAM 访问权限。
  3. 无法执行 MPU 仅标记为特权访问的代码。
  4. 可选择分配到对最多三个额外内存区域的访问,所述内存区域可在运行时更改。

每当非特权任务尝试访问其未被授予访问权限的任何内存区域时,都会触发内存故障 。 通过故障处理程序, 应用程序编写器有机会采取适当行动,来 终止所述违规任务。

请勿将任务的特权级别与其执行的安全域混淆 。 ARMv8-M Core 的安全端和非安全端各有其自身的 MPU -因此,在 安全端和非安全端都可以有特权和非特权执行。

为了降低安全风险,并减少软件错误带来的影响,建议 应用程序代码在任何可能的情况下都为非特权。 从非特权执行变为 特权执行的情况被称为特权升级。 软件启动的特权 升级只能从 FreeRTOS 内核的 API 函数中进行,但特权升级 在每次硬件接受中断时也会发生。 如果您不想应用程序提供的 中断服务程序 (ISR) 作为特权运行,那么可以在调用应用程序提供的处理程序函数之前降低权限级别 ,或者 将应用程序提供的处理程序函数的执行推迟到任务 (此类功能必须 由应用程序撰写人提供)来为每个中断例程安装子程序。


MPU 硬件限制

ARMv8-M MPU 比 ARMv7-M MPU 更灵活,内存区域的定义仅有以下限制:

  • 可为 MPU 区域编程的最小大小为 32 字节。
  • 任何 MPU 区域的最大大小为 4GB。
  • MPU 区域的大小必须是 32 字节的倍数。
  • 所有 MPU 区域必须以 32 字节对齐的地址开始。


FreeRTOSConfig.h 设置

要启用 MPU 支持,请构建 FreeRTOS,并把 configENABLE_MPU 设置为一 (在 FreeRTOSConfig.h 文件中):

#define configENABLE_MPU 1
启用 FreeRTOS 中的内存保护单元 (MPU) 支持


构建移植

如果应用程序使用 TrustZone 支持,请构建 FreeRTOS 源文件, 如 使用 FreeRTOSTrustZone 支持一节所述 。如果应用程序不使用 TrustZone 支持,请构建 FreeRTOS 源文件, 如使用 FreeRTOS(无 TrustZone 支持) 一节所述。此外, 请在非安全 项目中创建以下文件: /Source/portable/Common/mpu_wrappers.c FreeRTOS


正在为非特权任务分配堆栈

MPU 在 ARMv8-M 移植的支持使用 MPU 区域授予非特权 任务访问其堆栈的权限。因此,应用程序编写者必须确保 供应到 xTaskCreateRestricted() API 的堆栈缓冲区能 满足 MPU 硬件限制。以下 是用于将堆栈缓冲区放置在 32 字节边界上的 GCC 语法:

StackType_t xTaskStackBuffer[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
用于在 32 字节边界上放置缓冲区的 GCC 语法


授予非特权任务对其他内存区域的访问权限

可授予非特权任务在创建任务时访问最多三个附加内存区域的权限 。使用三个 MPU 区域来授予这些 内存区域访问的权限,因此这些内存区域须满足 MPU 硬件限制。下方为 创建非特权任务的示例,该任务被授予对 ucSharedMemory 仅读取的访问权限:


static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( 32 ) ) );

void vStartMPUDemo( void )
{
static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
TaskParameters_t xROAccessTaskParameters =
{
.pvTaskCode = prvROAccessTask,
.pcName = "ROAccess",
.usStackDepth = configMINIMAL_STACK_SIZE,
.pvParameters = NULL,
.uxPriority = tskIDLE_PRIORITY,
.puxStackBuffer = xROAccessTaskStack,
.xRegions = {
{ ucSharedMemory, 32, tskMPU_REGION_READ_ONLY | tskMPU_REGION_EXECUTE_NEVER },
{ 0, 0, 0 },
{ 0, 0, 0 },
}
};
/* Create an unprivileged task with RO access to ucSharedMemory. */
xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL );
}
创建非特权任务

请注意,代码需确保 ucSharedMemory xROAccessTaskStack 均符合 MPU 硬件限制


内存布局

具有 MPU 支持的应用程序的内存布局如下所示:

具有 MPU 支持的应用程序的内存布局

内核代码 -内核代码部分包含 FreeRTOS 内核可执行代码,并且只能由特权软件访问。非特权 软件需要通过内核系统调用(见下文)来访问 由 FreeRTOS 内核提供的功能。所有 FreeRTOS 内核函数都放置在命名的链接器部分 privileged_functions 中,且链接器脚本需要将它们放置在 单独的闪存部分中。

使用MPU区域确保只有特权软件才能访问 FreeRTOS 内核代码,因此,链接器脚本必须确保 包含 FreeRTOS 内核代码的闪存部分满足 MPU 硬件限制。此外, 链接器脚本需要导出两个变量,即 __ privileged_functions_start __ __ privileged_functions_end __ ,表示 FreeRTOS 内核代码的开始和结束,而 FreeRTOS 内核用内核代码来对 MPU 进行编程。

以下是用于将 FreeRTOS 内核代码放置于单独部分中的 GCC 语法 :


/* Privileged functions - Section needs to be 32 byte aligned to satisfy

* MPU requirements. */

.privileged_functions : ALIGN(32)
{
. = ALIGN(32);
__privileged_functions_start__ = .;
*(privileged_functions)
. = ALIGN(32);
/* End address must be the last address in the region, therefore, -1. */
__privileged_functions_end__ = . - 1;
} > PROGRAM_FLASH
用于将 FreeRTOS 内核代码放在单独的部分中的 GCC 语法

主 FreeRTOS 发行版本中的预配置示例项目包含 各种其他编译器的示例。

系统调用 -系统调用部分包含所有 FreeRTOS 系统调用。系统调用是非特权任务访问 原本仅供特权软件使用的 FreeRTOS API 的一种途径。 当非特权任务调用 FreeRTOS API时,它将通过 暂时会提高调用任务权限的系统调用,然后执行 请求的 API,并在返回调用方之前重置权限。全部 FreeRTOS 系统调用均放置在命名的链接器部分 freertos_system_call ,链接器脚本需要将它们放置在 单独的闪存部分。

使用 MPU 区域来确特权软件和非特权软件 均可以访问系统调用。 因此,连接器脚本必须确保 包含系统调用的闪存部分满足 MPU 硬件限制。此外, 链接器脚本需要导出两个变量,即 __ syscalls_flash_start __ __ syscalls_flash_end __ ,表示系统调用 FreeRTOS 的开始和结束, 而 FreeRTOS 内核使用系统调用来对 MPU 进行编程。 这些变量也用于确保非特权软件不能 任意升级其特权,并且特权升级仅限于 FreeRTOS 内核内部。

以下是用于将 FreeRTOS 系统调用置于单独部分中的 GCC 语法 :


/* FreeRTOS System calls - Section needs to be 32 byte aligned to satisfy

* MPU requirements. */

.freertos_system_calls : ALIGN(32)
{
. = ALIGN(32);
__syscalls_flash_start__ = .;
*(freertos_system_calls)
. = ALIGN(32);
/* End address must be the last address in the region, therefore, -1. */
__syscalls_flash_end__ = . - 1;
} > PROGRAM_FLASH
用于将 FreeRTOS 系统调用置于单独部分中的 GCC 语法

主 FreeRTOS 发行版本中的预配置示例项目包含 各种其他编译器的示例。

内核数据 -内核数据 (RAM) 部分包含所有 FreeRTOS 内核数据,且仅可由特权软件访问。所有的 FreeRTOS 内核数据均放置在命名链接器部分 privileged_data, 链接器脚本需要将其放置在单独的 RAM 部分中。

使用 MPU 区域来确保只有特权软件才能访问 FreeRTOS 内核数据,因此链接器脚本必须确保 RAM 包含 FreeRTOS 内核数据的部分满足 MPU 硬件限制。此外, 链接器脚本需要导出两个变量,即 __ privileged_sram_start __ __ privileged_sram_end __ ,表示 FreeRTOS 内核数据的开始和结束, 而 FreeRTOS 内核使用内核数据来对 MPU 进行编程。

以下是用于将 FreeRTOS 内核数据置于单独部分中的 GCC 语法 :


/* Main Data section (Ram0). */
.data : ALIGN(4)
{
/* Privileged data - It needs to be 32 byte aligned to satisfy

* MPU requirements. */

. = ALIGN(32);
__privileged_sram_start__ = .;
*(privileged_data);
. = ALIGN(32);
/* End address must be the last address in the region, therefore, -1. */
__privileged_sram_end__ = . - 1;
} > Ram0 AT>PROGRAM_FLASH
用于将 FreeRTOS 内核数据置于单独的部分中的 GCC 语法

主FreeRTOS发行版本中的预配置示例项目包含 各种其他编译器的示例。



使用 FreeRTOS(有浮点单元 (FPU) 支持)

说明

如果目标微控制器包括浮点单元 (FPU) , 且您将选项传递给您的编译器,指示其生成浮点指令 (与使用仿真浮点操作不同) ,则必须启用 FPU 支持。


FreeRTOSConfig.h 设置

要启用 FPU 支持,请构建 FreeRTOS,并将 configENABLE_FPU 设置为一(在 FreeRTOSConfig.h 文件中):

#define configENABLE_FPU 1
在 FreeRTOS 中启用浮点单元 (FPU) 支持


构建端口

如果应用程序使用 TrustZone 支持,请构建 FreeRTOS 源文件, 如 使用 FreeRTOSTrustZone 支持一节所述 。如果应用程序不使用 TrustZone 支持,请构建 FreeRTOS 源文件, 如使用 FreeRTOS(无 TrustZone 支持)



贡献 FreeRTOS ARMv8-M 移植

FreeRTOS ARMv8-M 移植组织如下:

  • 主副本 -在 FreeRTOS /Source/portable/ARMv8M 进行维护。
  • 副本 -主副本在多个 [编译器]/[架构] 目录中进行复制 ,以确保用户可以轻松找到 其编译器和架构组合所需的端口文件。
如果您想为 FreeRTOS ARMv8-M 移植做出贡献,请 对主副本做出更改并使用 FreeRTOS/Source/portable/ARMv8M/copy_files.py 脚本来复制主副本。

作者简介

Gaurav Aggarwal 是 Amazon Web Services IoT 边缘设备团队的 FreeRTOS 内核和嵌入式连接专家。 他帮助将 ARM Cortex-M33 和 Cortex-M23 FreeRTOS 内核端口推向市场,现在也支持这些端口的使用,同时还在 FreeRTOS 库产品组合的开发和改进中一直发挥着积极作用。
查看此作者的文章
FreeRTOS 论坛 获得来自专家的行业领先支持,并与全球同行合作。 查看论坛
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

s/js/vendor/google-code-prettify/prettify.js"