How to switch back to main stack on ARM M4 with FreeRTOS

dcrocker wrote on Wednesday, March 28, 2018:

I’ve just ported a large application to FreeRTOS. The application has the facility to do firmware updates, which it does by loading a bootloader into the top of flash memory, disabling all interrupts via the NVIC, resetting the vector table to point into the bootloader, and jumping to the start of the bootloader. Since I started using FreeRTOS in the main application, this doesn’t work. I think this is because the application is now using the process stack instead of the main stack. The bootloader assumes that only one stack is used, located at the top of RAM. So how can I switch back to the main stack from a FreeRTOS task, just before jumping into the bootloader? From the ARM docs I think it can only be done in an exception return, so some sort of SVC looks a possibility; but FreeRTOS doesn’t appear to have an facility for adding user-defined SVCs. Thanks - David

rtel wrote on Wednesday, March 28, 2018:

This is not a facility FreeRTOS gives you - so it is more a hardware
question that a software question. You can reset the main stack pointer
quite easily - see the function prvPortStartFirstTask() in the port.c
file as it does just that - but switching to use the main stack pointer
is a different thing and would require some reading of the Cortex-M
technical reference manual.

As you are going to disable interrupts anyway - could you jump to your
boot code from inside an interrupt? That way you would be using the
required stack.

Could you just update the bootloader code so it doesn’t care which stack
it is using? Are you sure it cares at the moment and the problem is not
just running out of stack space? You could set the process stack
pointer to the start of the main stack so you have lots of room -
providing you didn’t need either stack again [as you are going to
effectively restart everything with a new image].

dcrocker wrote on Wednesday, March 28, 2018:

Hi Richard, thanks for the quick reply.

Resetting the MSP isn’t an issue - the code already does that. I suspect the problem is that the PSP is in an area of RAM that is being used for other purposes by the bootloader. Maybe it will work if I change the PSP to be a few hundred bytes below the MSP.

I thought about jumping to the boot code from within an interrupt, but wouldn’t that leave the processor executing in handler mode? Perhaps that would work if the handler was a very low priority handler and the bootloader never needs to use a handler with the same or lower priority. [EDIT: does the “msr basepri, r0” with r0==0 that I see in vPortSVCHandler() persuade the processor that there are no exceptions/faults active?]

One of the complications is that I need users to be able to switch between RTOS and non-RTOS versions of the application, at least initially. So the bootloader must work coming from either.

I am tempted to change the implementation of vPortSVCHandler so that SVC 0 behaves as now, but SVC 1 switches the stack to MSP and returns to the original call address, or possibly returns to the address passed in R1 instead. Unfortunately, even though I have a lot of previous experiences with an RTOS, I have almost no experience of ARM assembler.

For the future, I think it would be good if FreeRTOS provided some way for users to hook in SVC handlers. I see from other posts that this has been requested before. I would have thought that in ports using the ARM MPU it would be even more impotant to have.

btw I am really pleased that FreeRTOS is MISRA-compliant, that’s one of the reasons why I chose it.

rtel wrote on Wednesday, March 28, 2018:

The in the non-MPU port the SVC handler is only used to start the
scheduler, so it doesn’t really have any logic to determine which SVC
was called. You could however look at the SVC handler in the MPU port
(\FreeRTOS\Source\portable\GCC\ARM_CM4_MPU) as an example as that
handles three different SVC numbers.

dcrocker wrote on Thursday, March 29, 2018:

Thanks, I’ll take a look at the MPU port to see how to implement an SVC dispatcher. Meanwhile the problem appears to be resolved if I set SP (i.e. the PSP) to 1K below the MSP just before jumping to the bootloader.