Kill with ISR on Korex-m0
I am using Cortex-M0 processor with realistic implementation (no OS). We have a firmware application in which we want to allow a third party to write a C function that will be built separately from the rest of the firmware and loaded into memory (like a DLL) and called by the main firmware if found.
The problem is that I want to run this external function in a secure environment so that it doesn't break the main firmware if it throws a crash exception or takes too long to execute. So what I want to do, either due to a hard ISR error (for crash exceptions) or by the ISR timer (for runtime issues), manipulate the stack to kill the external function and return execution to mainstream. I understand this will be right in the RTOS, but the main firmware has already been developed and at this point it will take significant effort to switch it.
I've looked at using try-catch in C ++, but the compiler doesn't seem to support it. So another option I see is to write some assembly to save the stack pointer before calling the external function, and restore the SP and context from the ISR and go to the return point in the main firmware. Can anyone provide any guidance on how to do this, or is there an easier way to do this?
source to share
Here is the implementation I ended up using. I used the built-in CMSIS functions __get_MSP () and __set_MSP () to access the SP. I saved the current SP before calling the protected function. The return address is the first item the function pushes onto the stack, so I increment the saved SP by one place, so it will point to the return address to be used in a crash recovery situation.
The ISR timer keeps track of how long the protected function has been running, and if it expires or if a severe interrupt occurs, an error handler is executed. The first thing that is required is to get out of the interrupt context (otherwise you will execute the main code out of the interrupt context, preventing further interrupts), so I figured out which stack location relative to the SP contains the ISR return address, and I overwrite it with the address of the fault recovery function. Note that the ISR of the timer type has the lowest priority, so the return address will always have a non-interrupt code. If the ISR priority is higher, it might not be the case, but I haven't tested it.
When exiting the ISR, the restore function is performed. This requires two instructions:
__set_MSP( StackPtrSave );
__asm volatile ("pop {pc}");
This restores the SP and PC to their correct state, as if the protected function had just exited. However, it does not restore any other registers. If the return point is at the end of the function (and the compiler doesn't try to inline it), you should be fine. In my case, I needed to set StackPtrSave = 0, as ISR knows, when the protected function is running. I tried using volatile pointer to force the compiler to reload the address of the variable into register after restore, but I couldn't get it to work. In the end, I set an attribute to disable feature optimization so that it always loads the address before writing to StackPtrSave.
As noted in the comments, this does not provide a completely secure environment. A protected function can still use a pointer to damage static variables or the stack. I see no way to avoid this as the Cortex-m0 does not have a memory protection unit. In my case, the third party responsible for the protected function has an interest in the functionality of the product, so I made a recommendation that their code avoids using pointers or arrays. I think this provides the highest level of protection that this platform can offer.
source to share