FreeRTOS是一种面向微控制器和小型微处理器的实时操作系统,它采用MIT许可证分发。这个操作系统的设计注重可靠性和易用性。在汽车、医疗和工业等市场中,安全规范要求设计者使用经过相关行业标准认证的实时操作系统(RTOS)。然而,认证RTOS对于长期安全项目来说是一项昂贵的投入。
SAFERTOS是一种经过IEC61508和ISO26262预认证的安全关键RTOS。它采用了与FreeRTOS相同的功能模型,并专注于安全性。在安全产品中,项目原型可以使用FreeRTOS内核进行实现,然后在正式开发阶段切换到SAFERTOS。
本文将通过一个简单的示例项目来说明如何将一个FreeRTOS应用程序迁移到SAFERTOS。在这个示例中,使用的硬件平台是NXP Freedom K64F开发板,开发环境是MCUXpresso IDE v11.1。示例项目可以从www.highintegritysystems.com/downloads免费下载。
项目包含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文件,并指向新的链接文件目录。
应用代码
应用包含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,
向导自动生成的工程中,还需修改C/C++ General->Paths and Symbols>includes路径信息。
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”,或扫描下方二维码进行关注,更多干货等着你 !