Main stack pointer reset when starting the scheduler

bschleusner wrote on Thursday, January 01, 2015:

Hello,

I’ve been digging through the source of the ARM_CM3 port of FreeRTOS (V8.1.2 and older), and noticed that in the prvPortStartFirstTask function resets the MSR register to the top of the stack. Is there a reason why the MSR needs to be reset?

I bring this up because I have been running into issues with variables located on the stack in main() getting corrupted. Looking into the corruption issue I noticed that the prvPortStartFirstTask was reseting MSR, which leads to privlidged code corrupting the main stack.

Excerpt from FreeRTOS/Source/portable/GCC/ARM_CM3/port.c

static void prvPortStartFirstTask( void )
{
	__asm volatile(
					" ldr r0, =0xE000ED08 	\n" /* Use the NVIC offset register to locate the stack. */
					" ldr r0, [r0] 			\n"
					" ldr r0, [r0] 			\n"
					" msr msp, r0			\n" /* Set the msp back to the start of the stack. */
					" cpsie i				\n" /* Globally enable interrupts. */
					" cpsie f				\n"
					" dsb					\n"
					" isb					\n"
					" svc 0					\n" /* System call to start first task. */
					" nop					\n"
				);
}

I’ve disabled the part that resets the MSR and it appears to function just fine. It seams like a bug to be overwriting the stack.

Thanks

richard_damon wrote on Thursday, January 01, 2015:

Some of the ports will reuse the main tasks stack to be used as the ISR stack. Since after starting up the scheduler, main will never be re-entered it was considered safe to reuse this space.

There really is no reason to be trying to store stuff in the stack of main that needs to be used later, you can just place those in global (or file static) space.

What are you keeping an the stack of main that you need?

bschleusner wrote on Thursday, January 01, 2015:

Some of the ports will reuse the main tasks stack to be used as the ISR stack.

Correct. Priviledged code (ISR’s) will use the main stack.

There really is no reason to be trying to store stuff in the stack of main that needs to be used later, you can just place those in global (or file static) space.

Storing things on the stack is useful for composition and ordered construction of classes / structures. Saying there is no reason to store things on the stack may be true for some designs, but that is a dangerous assumption to make.

I think that the MSP reset is premature optimization. If someone doesn’t want to store anything on the stack in main, then the main stack will only be containing a few stack frames at most, and reseting it will have minimal gain. If someone has a design pattern that makes good use of the stack for construction of threads, then this optimization will lead to many bugs.

rtel wrote on Friday, January 02, 2015:

It has always been done this way, and is especially important on devices which only have a few K of RAM. I think the original Cortex-M ports were created on devices with 2K or RAM, in which case it was extremely important to re-use all the RAM that was not intended to be used after the scheduler has started.

Once the scheduler has started main() no longer exists. There is no way back to it and no way of [directly] referencing memory on its stack [which is a compiler controlled construct], so I would turn your argument around and say it is dangerous to try and use resources that were allocated to it, and wasteful to leave resources allocated to something which is in effect orphaned. I don’t see this as being any different to attempting to access something that was on the stack of a task after the task has been deleted.

After the scheduler has started only the tasks and interrupts exist, and they are allocated stacks by the scheduler. If you want memory to be globally or file scope accessible please allocate it globally or file scope.

Regards.

znatok wrote on Friday, January 02, 2015:

In consideration of memory usage, variables in main() stack “accessed” from
a tasks (whis is actually wrong) have no difference compared to the same
variables being defined and allocated as global. FreeRTOS does it correctly
being freeing main stack before starting scheduler.

I wanted to ask about another optimization mechanism. What about stack
usage in IRQ handlers? As far as I understand in current implementation IRQ
handlers use stak of the task being interrupted. It means that each task
has to reserve piece of it’s stack for IRQ handler.
What if IRQ handler will switch to main or more correctly kernel stack at
the beginning !? In this case IRQ stack can be reserved only once and thus
each task stack could be reduced. Something like:

void IRQ_Handler( void )
{
IRQ_ENTER()


IRQ_EXIT()
}

I’m aiming Cortex architecture (not sure how it work in other cases).

rtel wrote on Friday, January 02, 2015:

As far as I understand in current implementation IRQ
handlers use stak of the task being interrupted.

That is not correct. IRQ handlers use the stack that was being used by main() before the scheduler was started. There is no need to make any allowance for the stack used by an interrupt when allocating a stack to a task.

Regards.

znatok wrote on Friday, January 02, 2015:

Is it because of the MSP/PSP usage?

rtel wrote on Friday, January 02, 2015:

If I understand your question, then “yes”.

richard_damon wrote on Friday, January 02, 2015:

Ella,
The key thing is that it depends on the processor and the port. The way ARM processors work, they naturally switch stacks on an interrupt, so there is a dedicated interrupt stack (which FreeRTOS shares space with the main task to save memory). Other processors more naturally use the tasks stack for the interrupt, but in some cases the port layer may automatically switch the stack to a dedicated one to save ram (or it might not). The Pic24 port, for example, doesn’t switch the stack, so every task needs to have the needed extra room to handle interrupt stacks.

rtel wrote on Friday, January 02, 2015:

A lot of the decisions on these things depends on whether the port to
the MCU supports interrupt nesting. So the PIC32, which has a full
interrupt nesting model, does switch stacks in interrupts - and in that
case it is done is software rather than by the hardware.

There is always a trade off between the run time overhead of checking
for nesting and switching stacks, and just getting in and out of the
interrupt as quickly as possible. Generally if there is no nesting then
the stack usage is bounded and determinable, so the port is less likely
to switch stacks.

Regards.

znatok wrote on Friday, January 02, 2015:

Discussion started with CortexM3, I’ve noticed in my message that I’m
talking about Cortex ARM.
And yes I understand that specific answer depends on architecture.

On Fri, Jan 2, 2015 at 3:18 PM, Real Time Engineers ltd. rtel@users.sf.net
wrote:

A lot of the decisions on these things depends on whether the port to
the MCU supports interrupt nesting. So the PIC32, which has a full
interrupt nesting model, does switch stacks in interrupts - and in that
case it is done is software rather than by the hardware.

There is always a trade off between the run time overhead of checking
for nesting and switching stacks, and just getting in and out of the
interrupt as quickly as possible. Generally if there is no nesting then
the stack usage is bounded and determinable, so the port is less likely
to switch stacks.

Regards.

Main stack pointer reset when starting the scheduler
http://sourceforge.net/p/freertos/discussion/382005/thread/e5a776c1/?limit=25#8a0c/aa87

Sent from sourceforge.net because you indicated interest in
https://sourceforge.net/p/freertos/discussion/382005/

To unsubscribe from further messages, please visit
https://sourceforge.net/auth/subscriptions/

bschleusner wrote on Friday, January 02, 2015:

It has always been done this way, and is especially important on devices which only have a few K of RAM. I think the original Cortex-M ports were created on devices with 2K or RAM, in which case it was extremely important to re-use all the RAM that was not intended to be used after the scheduler has started.

Memory allocated by the linker for the main stack is fixed. If no variables were stored on the main stack (as you intend them to be), then reseting the main stack will still have minimal gains.

There is no way back to it and no way of [directly] referencing memory on its stack [which is a compiler controlled construct], so I would turn your argument around and say it is dangerous to try and use resources that were allocated to it, and wasteful to leave resources allocated to something which is in effect orphaned.

Constructing memory on the main stack is not that uncommon of a thing, nor is it wasteful. If it has to exist in memory, then it will take up the same amount regardless of if it is on the stack or in BSS. Allocating on the stack allows for ordered and safe construction or objects.

(Simplified example)

int main()
{
    // Construct a task, adds itself to the scheduler, knows of the GPIO driver
    GPIO led(PORT_A, PIN_10, OUTPUT_OPEN_DRAIN);
    LedTask ledTask(led);
    // Construct a driver on the stack, knows of the GPIO driver
    GPIO a2dChipSelect(PORT_C, PIN_3, OUTPUT);
    LMP901xxxDriver a2dDriver(a2dChipSelect, SPI_PORT_1);
    // Construct a task, adds itself to the scheduler, knows of a2dDriver
    TemperatureConversionTask(a2dDriver);
    vTaskStartScheduler();
}

If a variable is on the stack, there is nothing in C or C++ stopping it from being passed to a task and used by that task. (pass by pointer or by reference)

If the programmer choses to store things on the stack, then it is up to the programmer to make sure that there is sufficient room reserved by the linker for it. I think making that decision for the programmer takes away many useful and safe design patterns.

My vote is that a setting should at least be exposed to allow main stack to be preserved.

davedoors wrote on Friday, January 02, 2015:

Sorry, I’m with (both) Richard(s) on this one. Stack variables are accessed via compiler generated code. You are right C++ cannot prevent you from attempting to access anything by reference, but it doesnt mean it is good practice to do so. C++ doesnt prevent me from attempting to dereference a null pointer, but I know the consequences of trying to do so. If I call a function, allocate something on the stack inside that function, return back to the calling function, then attempt to access whatever was allocated on the stack when inside the function I know I’m in for trouble because I am accessing something which no longer has a context, but C++ will of course let me try.

bschleusner wrote on Friday, January 02, 2015:

You are right C++ cannot prevent you from attempting to access anything by reference, but it doesnt mean it is good practice to do so.

It is infact very good practice to access by reference in modern languages. Particularily when it comes to things like RAII.

C++ doesnt prevent me from attempting to dereference a null pointer, but I know the consequences of trying to do so.

Why is that even relevent? Unless of course you mean dereference a void pointer, in which case we can debate type safety in another forum.

If I call a function, allocate something on the stack inside that function, return back to the calling function, then attempt to access whatever was allocated on the stack when inside the function I know I’m in for trouble because I am accessing something which no longer has a context, but C++ will of course let me try.

Correct. Doing that is always gauenteed to be unsafe. However, the code that I am complaining about breaks the gaurentee that stack variables will exist for the duration of a function. For example:

void bar(SomeClass& s)
{
   s.something(2);
}
int foo(void)
{
   SomeClass a;
   bar(a);
   // do something else with a
}

When foo() calls bar(), it is gaurenteed by the language that SomeClass a will exist until foo() returns. vTaskStartScheduler() breaks the gauentee that variables created in main will exist for the duration of main, regardless of the possibility to return to main.

rtel wrote on Friday, January 02, 2015:

I think the discussion is getting a little academic. The port works the
way it does so please work within its constraints. I’m not actually
aware of anybody raising this topic before, and the port has been around
for a very long time, so I’m afraid it is not going to change.

However, the code
that I am complaining about breaks the gaurentee that stack variables
will exist for the duration of a function. For example:

…and perhaps that is the route cause of the disagreement. As far as
we are concerned main() no longer exists when the scheduler has started.

vTaskStartScheduler() breaks the
gauentee that variables created in main will exist for the duration of
main

Exactly. In a single threaded application main() is always at the
bottom of the stack frame. In a multi-threaded application it is not,
it is in a completely different context before the scheduler is started,
and does not have a context after the scheduler has started. Point of
view only - no need to reply.

Regards.

bschleusner wrote on Friday, January 02, 2015:

the port has been around for a very long time, so I’m afraid it is not going to change.

I appreciate API’s that don’t change behavior, but modern microcontrollers and coding practices may merit at least the introduction of a setting to configure the destruction of MSP or not.

…and perhaps that is the route cause of the disagreement. As far as
we are concerned main() no longer exists when the scheduler has started.

Correct. If this is the case, then the documentation needs to reflect this. There is no mention of stack destruction in any of the header files. (task.h, portable.h)

richard_damon wrote on Saturday, January 03, 2015:

Brad,
I’ve used similar constructs as global (or file scoped) variables and never had a problem with initialization order (the order of initialization of globals in a given file is just as strictly controlled as auto variables in a function (like main), and have the added advantage for debugging that they have a known fixed address so the debugger can inspect their values.

I would need to go back through the documentation, but I do believe that the ARM ports I used did mention that the main stack was reused for the interrupt stack. The documentation would be in a “port specific” place, as this IS port specific (as I mentioned, the Pic24 port doesn’t do this, for example).

I see it very unlikely that this behavior will change, as it will only cause existing working applications to waste space, while maintaining the current method just requires you to follow the rules you have been informed of. It is hard to imagine a variable that can’t be moved to file scope, and the few oddball cases can be created when needed an put on the heap.

theszczur wrote on Tuesday, June 30, 2015:

“C/C++ guarantee that stack variables will exist for the duration of a function” - and you are braking that standard.

it would be the best to add configuration option like “configREUSE_MAIN_STACK”

And it should be very well documented, right now I can’t find any information on the official API documentation: http://www.freertos.org/a00132.html
And there is no information in the code…