Change function being executed before processor entered MemManage_Handler, app uses MPU for isolating threads.

Hi everyone,

I am currently trying to implement process isolation using the Cortex-M MPU and FreeRTOS(This may or may not be relevant), I am also trying to improve the FreeRTOS-MPU feature since it is rather limiting. So far the MPU is doing its job and I have not had issues preventing tasks from interfering with each other, however I am also trying to develop a way for tasks to handle system faults, and be able to do this in a way that the task can recover, or at least clean up resources before suspending/deleting itself.

My current idea is to simply adjust the program counter of the application from the fault handler, so that when the processor leaves the interrupt routine it begins execution in a function called vServiceThrowException() that restarts the task and potentially allows it to cleanup what ever it was doing before the fault.

Currently the code below kind of works, as the processor jumps to vServiceThrowException() exception which then performs a longjmp() to the start of the task and reenters user mode so that the task may cleanup resources itself or notify some parent process of the fault.

The code is able to call longjmp(), exit from the original setjmp() location, reset its internal state and call the FreeRTOS macro portSWITCH_TO_USER_MODE() but as soon as it returns from prvWaitForServiceStart() it triggers an illegal execution fault, calls MemManage_Handler a second time and eventually ends up inside HardFault_Handler.

Part of MemManage_Handler code.

// MemManage_Handler fault ISR code.
if( xFixed == pdFALSE )
        {
            /* When we exit the fault handler start executing inside
             * vServiceThrowException(). With the argument indicating the
             * exception reason. */
            pulFaultStackAddress[ portOFFSET_TO_PC ] = ( uint32_t ) vServiceThrowException;
            pulFaultStackAddress[ 0 ] = xException;

            /* Raise privilege before exiting the handler since vServiceThrowException 
            is a priviledged function. */
            __asm volatile
            (
                "	mrs r1, control		\n"    /* Obtain current control value. */
                "	bic r1, #1			\n"    /* Clear unprivileged bit. */
                "	msr control, r1		\n"    /* Write back new control value. */
                ::: "r1", "memory"
            );
        }

Other functions, my "processes" are called "services" and prvServiceTask is my task entry function for all services.

void vServiceThrowException( BaseType_t xException )
{
    Service_t * pxService = pvCapsuleGetCurrentServiceID();

    pxService->xLastException = xException;

    #if ( capconfigEXCEPTION_SELF_HANDLING == 1 )
        {
            longjmp( pxService->xJumpBuffer, xException );
        }
    #else
        {
            vTaskSuspend( NULL );
        }
    #endif
}

/*-----------------------------------------------------------*/

static void prvServiceTask( void * pvParameters )
{
    prvWaitForServiceStart()( pvParameters );
}
/*-----------------------------------------------------------*/

static TaskFunction_t prvWaitForServiceStart( void )
{
    Service_t * pxCurrentService;
    TaskFunction_t xReturn;

    ( void ) xTaskNotifyWait( 0UL, 0UL, NULL, portMAX_DELAY );

    pxCurrentService = pvCapsuleGetCurrentServiceID();

    if( setjmp( pxCurrentService->xJumpBuffer ) )
    {
        /* An exception has occured and so pxCurrentService may have been
         * overwritten on the stack. */
        pxCurrentService = pvCapsuleGetCurrentServiceID();
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }

    xReturn = pxCurrentService->xServiceFunction;

    portSWITCH_TO_USER_MODE();

    return xReturn;
}
/*-----------------------------------------------------------*/

The second memory fault is triggered when the code returns from prvWaitForServiceStart() or fairly soon after, maybe when it calls the TaskFunction_t returned by prvWaitForServiceStart() though I am not too sure.

My current theory is that the program counter is getting set to zero, and that is triggering the instruction access violation, which then is not handled properly so it finally enters my hard fault handler and hangs.

Feel free to ask questions if you do not understand what I am asking, I have never worked with Cortex-M at this low of a level before so my terminology may be a little off.

NOTE: that the service does execute perfectly fine and the original memory management fault is an intentional R/W violation for testing purposes.

More questions in this forum