良许Linux教程网 干货合集 如何实现FreeRTOS应用到安全SAFERTOS的迁移

如何实现FreeRTOS应用到安全SAFERTOS的迁移

FreeRTOS是一种面向微控制器和小型微处理器的实时操作系统,它采用MIT许可证分发。这个操作系统的设计注重可靠性和易用性。在汽车、医疗和工业等市场中,安全规范要求设计者使用经过相关行业标准认证的实时操作系统(RTOS)。然而,认证RTOS对于长期安全项目来说是一项昂贵的投入。

SAFERTOS是一种经过IEC61508和ISO26262预认证的安全关键RTOS。它采用了与FreeRTOS相同的功能模型,并专注于安全性。在安全产品中,项目原型可以使用FreeRTOS内核进行实现,然后在正式开发阶段切换到SAFERTOS。

本文将通过一个简单的示例项目来说明如何将一个FreeRTOS应用程序迁移到SAFERTOS。在这个示例中,使用的硬件平台是NXP Freedom K64F开发板,开发环境是MCUXpresso IDE v11.1。示例项目可以从www.highintegritysystems.com/downloads免费下载。

image-20231130215953023
image-20231130215953023

项目包含3个工程:

1、FreeRTOS_Demo-基础的FreeRTOS工程

2、RTOS_Demo-特权模式的SAFERTOS工程

3、RTOS_UnprvDemo-具有非特权任务的SAFERTOS项目

FreeRTOS和SAFERTOS主要区别

与FreeRTOS相比,SAFERTOS:

· API函数更少

· 函数中执行了更多的错误检查

· 大多数API调用会返回状态码,其它函数通过引用返回数据

· 需要应用提供所有堆栈,任务控制块和队列缓冲区内存

· 使用静态分配机制,不提供heap函数

· 默认使用处理器的MPU单元

· 完全重新设计,满足安全关键软件需求

因此,当将FreeRTOS项目迁移到SAFERTOS时,需要做一些工作来完成内核启动和运行。

FreeRTOS内部隐藏了许多常规内存管理,在任务创建时动态分配堆栈,在内核启动时分配内核缓冲区等。在Free RTOS中也可以配置静态分配,由应用程序提供内存,但大多数人倾向于更简单的方法,让FreeRTOS实现。

FreeRTOS还提供了许多编译选项,并通过hook宏机制,允许应用程序设计者在内核中插入额外的功能代码,在任务切换时运行,例如,在任务删除或创建时,运行额外的hook函数。

API区别

RTOS定义的类型名称不同。使用FreeRTOS,应用程序文件需要包含(#include)API(任务,队列,信号量)相应的头文件;SAFERTOS中,应用程序文件只需要包含一个SafeRTOS_API.h头文件。

静态分配及MPU

SAFERTOS要求应用任务和内核对象所需的内存静态分配。安全严格系统推荐静态分配机制,容易证明运行时有足够的内存空间。

绝大多数SAFERTOS移植中假定使用了MPU。MPU的使用意味着应用程序设计人员需要监督内存结构地址的确切位置,包括内核任务和队列缓冲区。此外,MPU还需满足区域的对齐和大小限制,应用程序工程师需要仔细安排空间,以避免空间浪费。

因此,使用FreeRTOS时,在调用xTaskCreate之前,需确保heap中足够的空闲空间。使用SAFERTOS,需要预先分配并定位对齐的堆栈及任务TCB内存,然后将指向这些结构的指针传递到xTaskCreate的相应参数。

任务特权模式及内核函数封装层

每个SAFERTOS任务被分配一个操作权限,特权(Privileged)任务与内核代码具有相同的权限,许多CPU支持特权(privileged)和非特权(unprivileged)模式,限制非特权模式的指令访问,有限的软件trap、异常和中断等。

通常,应用程序以非特权模式运行,每个任务都提供了一组MPU参数,这些参数在任务切换时配置相应的MPU域。

SAFERTOS任务创建时,增加了一个MPU域,定义用户任务堆栈,确保任务只访问自己的堆栈。

内核API工作在特权模式,SAFERTOS的每个API有一个权限升级封装(privilege-escalating wrapper)层,通过触发异常(通常是系统调用)、同步中断或CPU的trap实现。API的封装层通过临时提升任务权限,允许非特权任务执行内核API,执行完成后降回任务原先的权限。因为实际的API函数与调用时的名称不同,调试不方便。

尽管FreeRTOS也通过权限升级封装类似的机制支持MPU功能,但仅提供了有限的MPU移植参考。在SAFERTOS中,我们假定应用任务运行非特权模式。在FreeRTOS MPU移植中,任务通常被假定为运行特权模式,但是任务可以选择创建为restricted,即非特权模式。

基于FreeRTOS的应用

示例项目中包含一个向导生成Amazon FreeRTOS项目:FreeRTOS_Demo。

自动生成的链接文件

工程构建后,将自动生成链接定位文件,由于SAFERTOS工程中,需修改链接文件,我们不希望自动生成的链接文件覆盖已修改内容,将生成的链接文件从Debug目录迁移到单独的目录,并在工程选项中关闭自动生成linker文件,并指向新的链接文件目录。

image-20231130215958097
image-20231130215958097

应用代码

应用包含3个LED任务和一个控制任务,控制任务更新每个LED的“目标亮度”值的全局数组,并使用互斥信号量监视全局数组的访问。全局数组不是任务之间通信的最佳方法,但我们的目标是提供简单的示例,说明从FreeRTOS如何转换为SAFERTOS。

从FreeRTOS迁移到SAFERTOS

一,将工程升级为SAFERTOS,所有代码运行在privileged模式

1、替换FreeRTOS内核代码为SAFERTOS

删除工程amazon-freertos目录中的代码,替换为SAFERTOS库及头文件,修改工程options中的include path-C/C++ Build/Settings->Tool Settings->MCU C compiler->includes,

image-20231130220005599
image-20231130220005599

向导自动生成的工程中,还需修改C/C++ General->Paths and Symbols>includes路径信息。

image-20231130220008988
image-20231130220008988

2、 编辑链接文件,导出SAFERTOS需要的符号

SAFERTOS需要使用链接文件中定义的段和变量符号,设置MPU区域保护内核代码和数据。需要的段和符号可查阅portmpu.h文件。

内核函数段名为kernel_func,内核数据段为kernel_data,GCC中,内核函数和数据通过段属性放到相应段中。

内核函数通常紧随向量表放置。内核数据被放置RAM中的某个位置,需符合MPU对齐需要。

链接文件还需导出段起始和结束符号,ROM及RAM的起始地址、结束地址及大小。

在portmpu.h中,需要下列符号

·lnkStartFlashAddress

· lnkEndFlashAddress

· lnkStartKernelFunc

· lnkEndKernelFunc

· lnkStartKernelData

· lnkEndKernelData

· lnkRAMEnd(RTOS_Demo_Debug_memory.ld)

· lnkRAMStart(RTOS_Demo_Debug_memory.ld)

3、安装SAFERTOS需要的中断和异常

K6xxF移植中需要SysTick,PendSV,SVC中断,这些函数使用CMSIS定义的实现处理,相应的处理入口位于默认的向量表位置,RTOS可以命名自己的异常处理函数。

在SAFERTOSConfig.h中,通过#defining 实现SAFERTOS异常处理替代CMSIS定义,但SAFERTOS库文件无法修改,可以重新定义startup文件向量表中的CMSIS名称。

工程代码中,向量表定义位于startup_mk64f12.c文件,在该文件中插入:

/* SAFERTOS system tick, SVC and PendSV handlers */

#define SysTick_Handler vTaskProcessSystemTickFromISR

#define SVC_Handler vSafeRTOSSVCHandler

#define PendSV_Handler vSafeRTOSPendSVHandler

与FreeRTOS不同,SAFERTOS在启动时检查向量表条目,如果它们不存在或不正确,则拒绝运行。

4、内核hook函数

SAFERTOS提供了有限的hook函数,其中最主要的是Error Hook函数。在系统检测到不可恢复错误时,进入安全的错误状态。例如检测到被破坏的TCB或堆栈溢出,将调用error hook。项目中,error hook是一个简单的无限循环(位于HookFunctions.c文件)。

5、内核任务、内核配置及启动

除hook函数的地址外,还需将堆栈及TCB的地址和大小传给空闲任务和timer任务(可选),内核任务还需MPU参数,timer 命令队列等信息。SAFERTOS通过一个专用结构将参数传给专用的API来配置内核。应用从FreeRTOS迁移到SAFERTOS最复杂的部分是配置并启动调度器。

内核配置(包含内核任务堆栈和TCB)信息,放在单独的SafeRTOSConfig.c文件中。

在内核启动的每个阶段,当配置结构被传递到内核配置函数后,内核启动,API将返回相应的错误代码。函数返回的错误代码,可以在内核include目录的projdefs.h文件中查找含义。该文件列出了所有错误代码信息。

对于示例应用,还需使用一个“备用”MPU区域,建立一个全局MPU区域,以访问板载LED的GPIO,这样我们就不必提升相应的任务权限以使用GPIO资源。在SafeRTOSConfig.c中,通过xMPUConfigureGlobal Region()调用设置了“全局”MPU区域,允许读写GPIO寄存器 (实际上,该区域包含所有的外设地址空间)。

6、应用任务TCB和堆栈

为每个应用任务设置堆栈和TCB,填充其它任务参数。

在FreeRTOS中,创建任务所需的6个参数直接传递给xTaskCreate()函数,函数将返回新创建任务的句柄或pdFALL错误信息。在SAFERTOS中,任务参数更多,通过一个指向移植特定的xTaskParameters结构参数,传递给xTaskCreate(),第二个参数接收新创建任务的句柄,函数将返回pdPASS或错误码。

在示例中可以看到,FreeRTOS与SAFERTOS的任务名称和参数各不相同。SAFERTOS任务参数中还有每个任务的特权级别和MPU区域设置。第一步我们使所有任务都运行在特权级别,所以目前不需要额外的MPU参数,这些参数设置为0/null。

7、更新调用的API

针对每个API调用,检查并修改SAFERTOS对应的函数名称及返回值。

8、类型更新

除API函数名称不同外,移植层定义的类型名也有区别。可以借助IDE的search&replace功能替换。

二、修改应用任务为非特权模式

我们已经通过一个“全局MPU域”允许所有任务访问使用的GPIO寄存器。任务被转换为非特权模式后,还需要有哪些访问权限?

将某个任务标记为unprivileged,然后加载并运行应用程序,如果应用最终进入MPU fault处理程序中。可以检查调试器的寄存器以识别故障地址,可以尝试使用调试器强制从处理程序返回,快速识别导致MPU故障的指令。

示例中,每个LED任务需要访问共享的“brightness request”数组,处理访问该数据的互斥量,互斥量buffer仅由内核代码访问,任务如何实现访问共享RAM region?

在链接文件中创建一个命名段,导出开始地址和大小符号,添加放置属性来指定函数或变量的定位,在GCC中如下:

attribute ( ( section ( “section_name”)))

最后,使用链接文件导出的段符号来定义MPU域,添加到任务MPU参数中,以获得访问权限。注意,导出的linker符号为地址,在声明其为extern变量后,我们可以获取符号的地址,或者将其声明为数组类型,数组名将是其地址。

结论

本文通过一个简单的示例工程及迁移过程,探讨了FreeRTOS和SAFERTOS的差异。

通过示例项目,在FreeRTOS转换为SAFERTOS的每个阶段,分析预先配置的项目源代码,运行每个版本,可以分析原来的FreeRTOS平台上的应用程序与最终的非特权版本之间的差异。

显然,即使非常简单的代码,也有许多不同的SAFERTOS转换实现方式,早期的设计决策也会影响转换的简单性。建议尽量减少使用FreeRTOS特有的API,使用内核管理的任务间通信机制,使用FreeRTOS和SAFERTOS共享的类型名称,可以让事情变得更容易。

从应用一开始就考虑到任务为非特权执行模式,当升级到安全应用时,会更简单。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部