电子说
正文
1. OS错误处理介绍
1.1 错误类型
OS的Error类型分为三类,Application Errors, Protection Errors, Kernel Errors, 每种Errors产生的原因及产生Error后OS执行的动作都不相同,详见下表:
Error Types | Feature |
Application Errors |
1. 如果操作系统无法正确执行应用程序请求的操作系统提供的API服务,则引发Application Erros。典型情况就是操作系统API使用错误(例如对象ID无效)。 2. Application Error不会损坏操作系统内部数据。 3. 如果配置了Error Hook,则OS会调用ErrorHook(),ErrorHook()是一个Callout函数,需要用户自定义错误处理机制。 4. 不会造成OS调用Shutdown/terminate。应用程序可以通过简单地从ErrorHooks返回来继续执行。 |
Protection Errors |
1. 如果应用程序违反其配置的边界则会触发Protection Errors, 典型的就是配置了内存保护或者时间保护后发生内存非法访问或超时。 2. Protection Errors不会损坏操作系统内部数据。 3. 在发生未处理的异常和中断时会触发Protection Error。 4. 将导致ProtectionHook()的调用,在该调用中可以选择引发Shutdown或Terminate handing(ProtectionHook返回值将觉得OS接下来的执行流,无论是否重新启动)。 5. 如果配置了Shutdownhook,则会调用ShutdownHook(). 6. 如果配置了ProtectionHook,则会调用ProtectionHook(). |
Kernel Errors |
1. 如果操作系统无法再确保其内部数据的正确性,则引发Kernel Errors(例如,ProtectionHook()期间内存访问违规)。 2. 发生Kernel Errors后OS会关闭所有中断且调用Os_PannicHook()通知应用程序。 3. 最后操作系统进入无限循环。 |
1.2 错误码
发生Application Errors后OS会调用ErrorHook(), ErrorHook()函数是callout函数,函数原型:
(void) ErrorHook(StatusType Error);
参数Error标识具体的错误码
发生Protection Errors后OS会调用ProtectionHook(), ProtectionHook函数是callout函数,函数原型:
ProtectionReturnType ProtectionHook(StatusType Fatalerror);
参数Fatalerror标识具体的错误码
返回值类型ProtectionReturnType是一个枚举类型:
typedef enum ProtectionReturnType_e { PRO_IGNORE, PRO_TERMINATETASKISR, PRO_TERMINATEAPPL, PRO_TERMINATEAPPL_RESTART, PRO_SHUTDOWN, PRO_NOTCONFIGURED } ProtectionReturnType;
也就是,我们可以通过自定义ProtectionHook()的返回值来控制发送ProtectionHook后Os的执行流。
每个厂商(Vector, Etas…)Os实现的Os_Types.h文件中都具体定义了每一种Error Code,这里以Vector的代码实现为例说明每种Error Type包含的常见的Error Code:
Error Types | 包含的Error Codes |
Application Errors |
E_OS_ACCESS: Illegal access E_OS_CALLEVEL: Invalid calling context. E_OS_ID: Invalid OS object ID. E_OS_LIMIT: Maximum task activations reached. E_OS_NOFUNC: OS object is currently not in use. E_OS_RESOURCE: Scheduling requested with occupied resource. E_OS_STATE: OS object is not in correct state to perform the requested operation. E_OS_VALUE: Given value is out of the configured range. E_OS_SERVICEID: Service cannot be called. |
Protection Errors |
E_OS_PROTECTION_MEMORY: A memory access violation occurred. E_OS_PROTECTION_EXCEPTION: A trap occurred. E_OS_SYS_PROTECTION_SYSCALL: An unhandled syscall occurred. E_OS_STACKFAULT: A stack fault detected via stack monitoring by the OS. E_OS_SYS_API_ERROR: Wrong API usage. |
1.3 Davinci中配置OsHooks
三个Error相关的Hook函数可以在Davinci中配置,如果配置后就需要用户自定义实现。
2. 自定义错误处理
通过第一节,我们知道了Error的类型及其包含的具体的Error Code,同时,如果我们配置Error发生后Hook函数,那么在Error发生时我们就能被通知到。那么现在,我们在Error发生后应该考虑如何存储错误相关的信息,同时能在事后通过存储的Error相关信息定位和分析Error。
2.1 错误信息存储
背景知识1:RAM Retention。RAMretention是一种技术,用于在断电后保持随机存取存储器(RAM)中的数据。在计算机系统中,RAM是一种易失性存储器,这意味着在断电情况下,其中的数据会被清除。这对于一些应用程序来说是不可接受的,因为它们需要在断电后仍然能够保持数据。这就是RAMretention技术的用武之地。
背景知识2:断电系统和深度休眠系统。ECU在设计时根据具体需求可以在硬件上添加SBC或无SBC。如果ECU有SBC,ECU就是一个断电系统。那么ECU在系统下电(Shutdown)流程的最后一步会调用SBC的服务接口断掉MCU的电,整个MCU在休眠中是处于断电状态的。在外部信号(Can Transceiver/Lin Transceiver的INH引脚,Dio唤醒引脚 )唤醒MCU时,SBC重新给MCU供电,MCU重新冷启动。
如果ECU无SBC,ECU就是一个深度休眠系统。那么ECU在系统下电(Shutdown)流程的最后一步会调用MCU的服务进入到Deep Sleep深度休眠状态(MCU陷入深度休眠状态,程序不在运行,但是MCU还有电存在)。在外部信号(Can Transceiver/Lin Transceiver的INH引脚,Dio唤醒引脚 )通过中断唤醒MCU,MCU被唤醒后,程序可以选择软件复位,整个软件重新运行,也可以选择从上次停止的地方接着运行。
Aurix芯片进入深度休眠后后SCR会接管芯片控制,在进入SCR前可以配置PMS模块的PMSWCR0.STBYRAMSEL位域,选择给哪快RAM进行供电。只有休眠后改被供电的RAM才有RAM Retentions的功能。
问题1:为什么要考虑错误信息的存储了?
答:因为Error发生时如果时Protection Error的话,一般就会在OS调用ProtectionHook()后执行Shutdown,在ShutdownHook()中一般执行ECU复位了,如果我们不存储Error发生时的上下文信息的话,一旦系统复位的话我们就无法再分析Error发生的原因了。
问题2:错误信息存储在那里了,是不是可以存储在NvM?
答:错误信息可以存储在NvM中,但是因为ProtectionHook()后一般马上就要进行MCU复位了,来不及调用异步的NvM接口来存储错误信息了,所以只能把错误信息存储到Retention RAM中。复位起来后,错误信息处理SWC读取Retention RAM中的异常信息,此时可选择是否再次写入到NvM当中。
Note:
1.如果系统是断电系统,那么一定要注意OS ShutdownHook()中调用Mcu_PerformReset()进行软件复位而不是调用SBC的接口给MCU断电,因为MCU断电后是冷启动,Retention RAM中的数据也没了。
2.如果系统是深度休眠系统且使用Aurix芯片的SCR功能,那么Retention RAM一定要配置在PMSWCR0.STBYRAMSEL配置供电的RAM块中。
3.无论是深度休眠系统还是断点系统,MCU复位后在Main函数之前的Startup阶段都不能把Retention RAM给清零了(需要修改启动代码和连接器脚本)。
2.2 关键上下文信息获取
问题1:通过2.1小结我们知道错误信息应该存放在Retention RAM当中,那么我们应该存储哪些异常时的上下文信息了?
答:我们通过一个表格来举例给出答案。
Error Types | Error Contex |
Application Errors |
如果在使用Spinlock是发生Application Error,可以获取以下信息: 1. 通过Os_GetDetailedError()获取服务ID及Error Code等信息。 2. 通过OSError_GetSpinlock_SpinlockId()返回错误的GetSpinlock调用的参数SpinlockId. 使用其他OS服务,比如Alarm, Resource等发生错误时同样可以调用OsError_xxx_xxx()获取相关错误现场信息。 |
Protection Errors |
1. ProtectionHook()的参数Fataerror. 2. 通过GetTaskID获取Error发生时的正在处理的Task. 3. 通过GetISRID()获取当前执行ISR的标识符。 4. 通过Os_GetExceptionAddress()获取引发最新异常的指令的地址。 5. 读取DEADD寄存器获取发生trap时的地址信息。 6. 通过Os_GetExceptionContext()获取异常上下文信息,异常结构体为:struct Os_ExceptionContextType_Tag; 通过结构体成员Ra和ExceptionSource对应的TIN和Class信息,可以轻松定位MPU保护产生的Error. |
Kernel Errors | 通过GetTaskID获取Error发生时的正在处理的Task. |
/*! Set of hardware registers to be able to resume from an exception. */ struct Os_ExceptionContextType_Tag { /* Stored Address registers of the thread (a2-a7, a12-a15)*/ uint32 AddressRegisters[16]; /* Stored Data registers of the thread (d0-d15)*/ uint32 DataRegisters[16]; /*! Stored return address of the thread */ uint32 Ra; /*! Stored Psw of the thread */ uint32 Psw; /*! Stored Exception source(Exception class and tin number) of the thread */ uint32 ExceptionSource; /*! Stored Pcpn (Previous CPU Priority number) from the Pcxi of the thread */ uint32 Pcpn; /*! Stored Pie (Previous Interrupt Enable) from the Pcxi of the thread */ uint32 Pie; /*! The lower address of the MPU region for stack. */ uint32 MpuRegionForStackLow; /*! The upper address of the MPU region for stack. */ uint32 MpuRegionForStackUpper; /*! The raw PCXI value from the upper context; may be used to look up in CSAs prior to the exception */ uint32 RawPCXI; };
2.3 错误定位
对于Application Error一般都是错误使用OS API导致的,只要我们记录好错误发生时的ServiceID等就能轻松定位。
对于Kernel Error由于OS内部数据可能被异常打乱了,数据不在可信,可获取的上下文信息不多,这类错误就只能根据具体硬件平台和OS代码积累经验了(开发阶段可以通过故障注入提前获知Kernel Error产生后的表现)。
实际项目中最可能出问题的就是Protection Error了,而这里面也以MPU保护Error为最常见。出现内存保护Error后,通过Ra(A11程序返回寄存器) 查找Map文件可以大概知道那块代码(指令所在的地址)发生异常;通过DEADD寄存器可以得知大概是访问了哪块Data数据(访问的数据的地址)发生了异常,比如异常改写了调用栈内容。
3.总结
最后通过回答开头的三个问题来结束本文。
问题1:有哪里常见的OS错误 ?
答:大类有Application Errors, Protection Errors, Kernel Errors三种,每种大类包含的具体Error Code可以参考1.1章节。
问题2:如何进行OS错误处理?
答:通过Retention RAM来存储OS错误信息,通过OS给出的一系列API获取Error发生时的上下文信息。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !