Memory Protection Unit (MPU) Enhancements (#705)

Memory Protection Unit (MPU) Enhancements

This commit introduces a new MPU wrapper that places additional
restrictions on unprivileged tasks. The following is the list of changes
introduced with the new MPU wrapper:

1. Opaque and indirectly verifiable integers for kernel object handles:
   All the kernel object handles (for example, queue handles) are now
   opaque integers. Previously object handles were raw pointers.

2. Saving the task context in Task Control Block (TCB): When a task is
   swapped out by the scheduler, the task's context is now saved in its
   TCB. Previously the task's context was saved on its stack.

3. Execute system calls on a separate privileged only stack: FreeRTOS
   system calls, which execute with elevated privilege, now use a
   separate privileged only stack. Previously system calls used the
   calling task's stack. The application writer can control the size of
   the system call stack using new configSYSTEM_CALL_STACK_SIZE config
   macro.

4. Memory bounds checks: FreeRTOS system calls which accept a pointer
   and de-reference it, now verify that the calling task has required
   permissions to access the memory location referenced by the pointer.

5. System call restrictions: The following system calls are no longer
   available to unprivileged tasks:
    - vQueueDelete
    - xQueueCreateMutex
    - xQueueCreateMutexStatic
    - xQueueCreateCountingSemaphore
    - xQueueCreateCountingSemaphoreStatic
    - xQueueGenericCreate
    - xQueueGenericCreateStatic
    - xQueueCreateSet
    - xQueueRemoveFromSet
    - xQueueGenericReset
    - xTaskCreate
    - xTaskCreateStatic
    - vTaskDelete
    - vTaskPrioritySet
    - vTaskSuspendAll
    - xTaskResumeAll
    - xTaskGetHandle
    - xTaskCallApplicationTaskHook
    - vTaskList
    - vTaskGetRunTimeStats
    - xTaskCatchUpTicks
    - xEventGroupCreate
    - xEventGroupCreateStatic
    - vEventGroupDelete
    - xStreamBufferGenericCreate
    - xStreamBufferGenericCreateStatic
    - vStreamBufferDelete
    - xStreamBufferReset
   Also, an unprivileged task can no longer use vTaskSuspend to suspend
   any task other than itself.

We thank the following people for their inputs in these enhancements:
- David Reiss of Meta Platforms, Inc.
- Lan Luo, Xinhui Shao, Yumeng Wei, Zixia Liu, Huaiyu Yan and Zhen Ling
  of School of Computer Science and Engineering, Southeast University,
  China.
- Xinwen Fu of Department of Computer Science, University of
  Massachusetts Lowell, USA.
- Yuequi Chen, Zicheng Wang, Minghao Lin of University of Colorado
  Boulder, USA.
This commit is contained in:
kar-rahul-aws 2023-07-13 16:51:04 +05:30 committed by GitHub
parent 18e2937239
commit 97050a17aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
123 changed files with 94802 additions and 6515 deletions

File diff suppressed because it is too large Load diff

View file

@ -90,6 +90,7 @@
/* Constants required to set up the initial stack. */
#define portINITIAL_XPSR ( 0x01000000 )
#define portINITIAL_EXC_RETURN ( 0xfffffffdUL )
#define portINITIAL_CONTROL_IF_UNPRIVILEGED ( 0x03 )
#define portINITIAL_CONTROL_IF_PRIVILEGED ( 0x02 )
@ -103,12 +104,31 @@
#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL )
#define portPRIGROUP_SHIFT ( 8UL )
/* Constants used during system call enter and exit. */
#define portPSR_STACK_PADDING_MASK ( 1UL << 9UL )
/* Offsets in the stack to the parameters when inside the SVC handler. */
#define portOFFSET_TO_LR ( 5 )
#define portOFFSET_TO_PC ( 6 )
#define portOFFSET_TO_PSR ( 7 )
/* For strict compliance with the Cortex-M spec the task start address should
* have bit-0 clear, as it is loaded into the PC on exit from an ISR. */
#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL )
/* Does addr lie within [start, end] address range? */
#define portIS_ADDRESS_WITHIN_RANGE( addr, start, end ) \
( ( ( addr ) >= ( start ) ) && ( ( addr ) <= ( end ) ) )
/* Is the access request satisfied by the available permissions? */
#define portIS_AUTHORIZED( accessRequest, permissions ) \
( ( ( permissions ) & ( accessRequest ) ) == accessRequest )
/* Max value that fits in a uint32_t type. */
#define portUINT32_MAX ( ~( ( uint32_t ) 0 ) )
/* Check if adding a and b will result in overflow. */
#define portADD_UINT32_WILL_OVERFLOW( a, b ) ( ( a ) > ( portUINT32_MAX - ( b ) ) )
/*-----------------------------------------------------------*/
/*
@ -146,7 +166,7 @@ static void prvRestoreContextOfFirstTask( void ) __attribute__( ( naked ) ) PRIV
* C portion of the SVC handler. The SVC handler is split between an asm entry
* and a C wrapper for simplicity of coding and maintenance.
*/
static void prvSVCHandler( uint32_t * pulRegisters ) __attribute__( ( noinline ) ) PRIVILEGED_FUNCTION;
void vSVCHandler_C( uint32_t * pulRegisters ) __attribute__( ( noinline ) ) PRIVILEGED_FUNCTION;
/**
* @brief Checks whether or not the processor is privileged.
@ -182,6 +202,53 @@ void vResetPrivilege( void ) __attribute__( ( naked ) );
#else
void vPortExitCritical( void ) PRIVILEGED_FUNCTION;
#endif
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
/**
* @brief Sets up the system call stack so that upon returning from
* SVC, the system call stack is used.
*
* It is used for the system calls with up to 4 parameters.
*
* @param pulTaskStack The current SP when the SVC was raised.
*/
void vSystemCallEnter( uint32_t * pulTaskStack ) PRIVILEGED_FUNCTION;
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
/**
* @brief Sets up the system call stack so that upon returning from
* SVC, the system call stack is used.
*
* It is used for the system calls with 5 parameters.
*
* @param pulTaskStack The current SP when the SVC was raised.
*/
void vSystemCallEnter_1( uint32_t * pulTaskStack ) PRIVILEGED_FUNCTION;
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
/**
* @brief Sets up the task stack so that upon returning from
* SVC, the task stack is used again.
*
* @param pulSystemCallStack The current SP when the SVC was raised.
*/
void vSystemCallExit( uint32_t * pulSystemCallStack ) PRIVILEGED_FUNCTION;
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
/**
* @brief Checks whether or not the calling task is privileged.
*
* @return pdTRUE if the calling task is privileged, pdFALSE otherwise.
*/
BaseType_t xPortIsTaskPrivileged( void ) PRIVILEGED_FUNCTION;
/*-----------------------------------------------------------*/
/* Each task maintains its own interrupt status in the critical nesting
@ -207,34 +274,91 @@ static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
TaskFunction_t pxCode,
void * pvParameters,
BaseType_t xRunPrivileged )
BaseType_t xRunPrivileged,
xMPU_SETTINGS * xMPUSettings )
{
/* Simulate the stack frame as it would be created by a context switch
* interrupt. */
pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */
*pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
pxTopOfStack--;
*pxTopOfStack = 0; /* LR */
pxTopOfStack -= 5; /* R12, R3, R2 and R1. */
*pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
pxTopOfStack -= 9; /* R11, R10, R9, R8, R7, R6, R5 and R4. */
if( xRunPrivileged == pdTRUE )
{
*pxTopOfStack = portINITIAL_CONTROL_IF_PRIVILEGED;
xMPUSettings->ulTaskFlags |= portTASK_IS_PRIVILEGED_FLAG;
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_PRIVILEGED;
}
else
{
*pxTopOfStack = portINITIAL_CONTROL_IF_UNPRIVILEGED;
xMPUSettings->ulTaskFlags &= ( ~portTASK_IS_PRIVILEGED_FLAG );
xMPUSettings->ulContext[ 0 ] = portINITIAL_CONTROL_IF_UNPRIVILEGED;
}
xMPUSettings->ulContext[ 1 ] = 0x04040404; /* r4. */
xMPUSettings->ulContext[ 2 ] = 0x05050505; /* r5. */
xMPUSettings->ulContext[ 3 ] = 0x06060606; /* r6. */
xMPUSettings->ulContext[ 4 ] = 0x07070707; /* r7. */
xMPUSettings->ulContext[ 5 ] = 0x08080808; /* r8. */
xMPUSettings->ulContext[ 6 ] = 0x09090909; /* r9. */
xMPUSettings->ulContext[ 7 ] = 0x10101010; /* r10. */
xMPUSettings->ulContext[ 8 ] = 0x11111111; /* r11. */
xMPUSettings->ulContext[ 9 ] = portINITIAL_EXC_RETURN; /* EXC_RETURN. */
return pxTopOfStack;
xMPUSettings->ulContext[ 10 ] = ( uint32_t ) ( pxTopOfStack - 8 ); /* PSP with the hardware saved stack. */
xMPUSettings->ulContext[ 11 ] = ( uint32_t ) pvParameters; /* r0. */
xMPUSettings->ulContext[ 12 ] = 0x01010101; /* r1. */
xMPUSettings->ulContext[ 13 ] = 0x02020202; /* r2. */
xMPUSettings->ulContext[ 14 ] = 0x03030303; /* r3. */
xMPUSettings->ulContext[ 15 ] = 0x12121212; /* r12. */
xMPUSettings->ulContext[ 16 ] = 0; /* LR. */
xMPUSettings->ulContext[ 17 ] = ( ( uint32_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC. */
xMPUSettings->ulContext[ 18 ] = portINITIAL_XPSR; /* xPSR. */
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
{
/* Ensure that the system call stack is double word aligned. */
xMPUSettings->xSystemCallStackInfo.pulSystemCallStack = &( xMPUSettings->xSystemCallStackInfo.ulSystemCallStackBuffer[ configSYSTEM_CALL_STACK_SIZE - 1 ] );
xMPUSettings->xSystemCallStackInfo.pulSystemCallStack = ( uint32_t * ) ( ( uint32_t ) ( xMPUSettings->xSystemCallStackInfo.pulSystemCallStack ) &
( uint32_t ) ( ~( portBYTE_ALIGNMENT_MASK ) ) );
/* This is not NULL only for the duration of a system call. */
xMPUSettings->xSystemCallStackInfo.pulTaskStack = NULL;
}
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
return &( xMPUSettings->ulContext[ 19 ] );
}
/*-----------------------------------------------------------*/
void vPortSVCHandler( void )
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
void vPortSVCHandler( void ) /* __attribute__( ( naked ) ) PRIVILEGED_FUNCTION */
{
__asm volatile
(
".syntax unified \n"
".extern vSVCHandler_C \n"
".extern vSystemCallEnter \n"
".extern vSystemCallEnter_1 \n"
".extern vSystemCallExit \n"
" \n"
"tst lr, #4 \n"
"ite eq \n"
"mrseq r0, msp \n"
"mrsne r0, psp \n"
" \n"
"ldr r1, [r0, #24] \n"
"ldrb r2, [r1, #-2] \n"
"cmp r2, %0 \n"
"beq vSystemCallEnter \n"
"cmp r2, %1 \n"
"beq vSystemCallEnter_1 \n"
"cmp r2, %2 \n"
"beq vSystemCallExit \n"
"b vSVCHandler_C \n"
" \n"
: /* No outputs. */
:"i" ( portSVC_SYSTEM_CALL_ENTER ), "i" ( portSVC_SYSTEM_CALL_ENTER_1 ), "i" ( portSVC_SYSTEM_CALL_EXIT )
: "r0", "r1", "r2", "memory"
);
}
#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
void vPortSVCHandler( void ) /* __attribute__( ( naked ) ) PRIVILEGED_FUNCTION */
{
/* Assumes psp was in use. */
__asm volatile
@ -248,12 +372,14 @@ void vPortSVCHandler( void )
" mrs r0, psp \n"
#endif
" b %0 \n"
::"i" ( prvSVCHandler ) : "r0", "memory"
::"i" ( vSVCHandler_C ) : "r0", "memory"
);
}
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
/*-----------------------------------------------------------*/
static void prvSVCHandler( uint32_t * pulParam )
void vSVCHandler_C( uint32_t * pulParam ) /* PRIVILEGED_FUNCTION */
{
uint8_t ucSVCNumber;
uint32_t ulPC;
@ -262,7 +388,7 @@ static void prvSVCHandler( uint32_t * pulParam )
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
* exported from linker scripts. */
extern uint32_t * __syscalls_flash_start__;
extern uint32_t * __syscalls_flash_end__;
#else
@ -296,7 +422,6 @@ static void prvSVCHandler( uint32_t * pulParam )
break;
#if ( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 )
case portSVC_RAISE_PRIVILEGE: /* Only raise the privilege, if the
* svc was raised from any of the
@ -325,7 +450,7 @@ static void prvSVCHandler( uint32_t * pulParam )
::: "r1", "memory"
);
break;
#endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */
#endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */
default: /* Unknown SVC call. */
break;
@ -333,45 +458,311 @@ static void prvSVCHandler( uint32_t * pulParam )
}
/*-----------------------------------------------------------*/
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
void vSystemCallEnter( uint32_t * pulTaskStack ) /* PRIVILEGED_FUNCTION */
{
extern TaskHandle_t pxCurrentTCB;
xMPU_SETTINGS * pxMpuSettings;
uint32_t * pulSystemCallStack;
uint32_t ulSystemCallLocation, i;
const uint32_t ulStackFrameSize = 8;
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
extern uint32_t * __syscalls_flash_start__;
extern uint32_t * __syscalls_flash_end__;
#else
/* Declaration when these variable are exported from linker scripts. */
extern uint32_t __syscalls_flash_start__[];
extern uint32_t __syscalls_flash_end__[];
#endif /* #if defined( __ARMCC_VERSION ) */
ulSystemCallLocation = pulTaskStack[ portOFFSET_TO_PC ];
/* If the request did not come from the system call section, do nothing. */
if( ( ulSystemCallLocation >= ( uint32_t ) __syscalls_flash_start__ ) &&
( ulSystemCallLocation <= ( uint32_t ) __syscalls_flash_end__ ) )
{
pxMpuSettings = xTaskGetMPUSettings( pxCurrentTCB );
pulSystemCallStack = pxMpuSettings->xSystemCallStackInfo.pulSystemCallStack;
/* This is not NULL only for the duration of the system call. */
configASSERT( pxMpuSettings->xSystemCallStackInfo.pulTaskStack == NULL );
/* Make space on the system call stack for the stack frame. */
pulSystemCallStack = pulSystemCallStack - ulStackFrameSize;
/* Copy the stack frame. */
for( i = 0; i < ulStackFrameSize; i++ )
{
pulSystemCallStack[ i ] = pulTaskStack[ i ];
}
/* Use the pulSystemCallStack in thread mode. */
__asm volatile ( "msr psp, %0" : : "r" ( pulSystemCallStack ) );
/* Raise the privilege for the duration of the system call. */
__asm volatile (
" mrs r1, control \n" /* Obtain current control value. */
" bic r1, #1 \n" /* Clear nPRIV bit. */
" msr control, r1 \n" /* Write back new control value. */
::: "r1", "memory"
);
/* Remember the location where we should copy the stack frame when we exit from
* the system call. */
pxMpuSettings->xSystemCallStackInfo.pulTaskStack = pulTaskStack + ulStackFrameSize;
/* Store the value of the Link Register before the SVC was raised. We need to
* restore it when we exit from the system call. */
pxMpuSettings->xSystemCallStackInfo.ulLinkRegisterAtSystemCallEntry = pulTaskStack[ portOFFSET_TO_LR ];
/* Record if the hardware used padding to force the stack pointer
* to be double word aligned. */
if( ( pulTaskStack[ portOFFSET_TO_PSR ] & portPSR_STACK_PADDING_MASK ) == portPSR_STACK_PADDING_MASK )
{
pxMpuSettings->ulTaskFlags |= portSTACK_FRAME_HAS_PADDING_FLAG;
}
else
{
pxMpuSettings->ulTaskFlags &= ( ~portSTACK_FRAME_HAS_PADDING_FLAG );
}
/* We ensure in pxPortInitialiseStack that the system call stack is
* double word aligned and therefore, there is no need of padding.
* Clear the bit[9] of stacked xPSR. */
pulSystemCallStack[ portOFFSET_TO_PSR ] &= ( ~portPSR_STACK_PADDING_MASK );
}
}
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
/*-----------------------------------------------------------*/
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
void vSystemCallEnter_1( uint32_t * pulTaskStack ) /* PRIVILEGED_FUNCTION */
{
extern TaskHandle_t pxCurrentTCB;
xMPU_SETTINGS * pxMpuSettings;
uint32_t * pulSystemCallStack;
uint32_t ulSystemCallLocation, i;
const uint32_t ulStackFrameSize = 8;
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
extern uint32_t * __syscalls_flash_start__;
extern uint32_t * __syscalls_flash_end__;
#else
/* Declaration when these variable are exported from linker scripts. */
extern uint32_t __syscalls_flash_start__[];
extern uint32_t __syscalls_flash_end__[];
#endif /* #if defined( __ARMCC_VERSION ) */
ulSystemCallLocation = pulTaskStack[ portOFFSET_TO_PC ];
/* If the request did not come from the system call section, do nothing. */
if( ( ulSystemCallLocation >= ( uint32_t ) __syscalls_flash_start__ ) &&
( ulSystemCallLocation <= ( uint32_t ) __syscalls_flash_end__ ) )
{
pxMpuSettings = xTaskGetMPUSettings( pxCurrentTCB );
pulSystemCallStack = pxMpuSettings->xSystemCallStackInfo.pulSystemCallStack;
/* This is not NULL only for the duration of the system call. */
configASSERT( pxMpuSettings->xSystemCallStackInfo.pulTaskStack == NULL );
/* Make space on the system call stack for the stack frame and
* the parameter passed on the stack. We only need to copy one
* parameter but we still reserve 2 spaces to keep the stack
* double word aligned. */
pulSystemCallStack = pulSystemCallStack - ulStackFrameSize - 2UL;
/* Copy the stack frame. */
for( i = 0; i < ulStackFrameSize; i++ )
{
pulSystemCallStack[ i ] = pulTaskStack[ i ];
}
/* Copy the parameter which is passed the stack. */
if( ( pulTaskStack[ portOFFSET_TO_PSR ] & portPSR_STACK_PADDING_MASK ) == portPSR_STACK_PADDING_MASK )
{
pulSystemCallStack[ ulStackFrameSize ] = pulTaskStack[ ulStackFrameSize + 1 ];
/* Record if the hardware used padding to force the stack pointer
* to be double word aligned. */
pxMpuSettings->ulTaskFlags |= portSTACK_FRAME_HAS_PADDING_FLAG;
}
else
{
pulSystemCallStack[ ulStackFrameSize ] = pulTaskStack[ ulStackFrameSize ];
/* Record if the hardware used padding to force the stack pointer
* to be double word aligned. */
pxMpuSettings->ulTaskFlags &= ( ~portSTACK_FRAME_HAS_PADDING_FLAG );
}
/* Use the pulSystemCallStack in thread mode. */
__asm volatile ( "msr psp, %0" : : "r" ( pulSystemCallStack ) );
/* Raise the privilege for the duration of the system call. */
__asm volatile (
" mrs r1, control \n" /* Obtain current control value. */
" bic r1, #1 \n" /* Clear nPRIV bit. */
" msr control, r1 \n" /* Write back new control value. */
::: "r1", "memory"
);
/* Remember the location where we should copy the stack frame when we exit from
* the system call. */
pxMpuSettings->xSystemCallStackInfo.pulTaskStack = pulTaskStack + ulStackFrameSize;
/* Store the value of the Link Register before the SVC was raised. We need to
* restore it when we exit from the system call. */
pxMpuSettings->xSystemCallStackInfo.ulLinkRegisterAtSystemCallEntry = pulTaskStack[ portOFFSET_TO_LR ];
/* We ensure in pxPortInitialiseStack that the system call stack is
* double word aligned and therefore, there is no need of padding.
* Clear the bit[9] of stacked xPSR. */
pulSystemCallStack[ portOFFSET_TO_PSR ] &= ( ~portPSR_STACK_PADDING_MASK );
}
}
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
/*-----------------------------------------------------------*/
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
void vSystemCallExit( uint32_t * pulSystemCallStack ) /* PRIVILEGED_FUNCTION */
{
extern TaskHandle_t pxCurrentTCB;
xMPU_SETTINGS * pxMpuSettings;
uint32_t * pulTaskStack;
uint32_t ulSystemCallLocation, i;
const uint32_t ulStackFrameSize = 8;
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
extern uint32_t * __syscalls_flash_start__;
extern uint32_t * __syscalls_flash_end__;
#else
/* Declaration when these variable are exported from linker scripts. */
extern uint32_t __syscalls_flash_start__[];
extern uint32_t __syscalls_flash_end__[];
#endif /* #if defined( __ARMCC_VERSION ) */
ulSystemCallLocation = pulSystemCallStack[ portOFFSET_TO_PC ];
/* If the request did not come from the system call section, do nothing. */
if( ( ulSystemCallLocation >= ( uint32_t ) __syscalls_flash_start__ ) &&
( ulSystemCallLocation <= ( uint32_t ) __syscalls_flash_end__ ) )
{
pxMpuSettings = xTaskGetMPUSettings( pxCurrentTCB );
pulTaskStack = pxMpuSettings->xSystemCallStackInfo.pulTaskStack;
/* Make space on the task stack for the stack frame. */
pulTaskStack = pulTaskStack - ulStackFrameSize;
/* Copy the stack frame. */
for( i = 0; i < ulStackFrameSize; i++ )
{
pulTaskStack[ i ] = pulSystemCallStack[ i ];
}
/* Use the pulTaskStack in thread mode. */
__asm volatile ( "msr psp, %0" : : "r" ( pulTaskStack ) );
/* Drop the privilege before returning to the thread mode. */
__asm volatile (
" mrs r1, control \n" /* Obtain current control value. */
" orr r1, #1 \n" /* Set nPRIV bit. */
" msr control, r1 \n" /* Write back new control value. */
::: "r1", "memory"
);
/* Restore the stacked link register to what it was at the time of
* system call entry. */
pulTaskStack[ portOFFSET_TO_LR ] = pxMpuSettings->xSystemCallStackInfo.ulLinkRegisterAtSystemCallEntry;
/* If the hardware used padding to force the stack pointer
* to be double word aligned, set the stacked xPSR bit[9],
* otherwise clear it. */
if( ( pxMpuSettings->ulTaskFlags & portSTACK_FRAME_HAS_PADDING_FLAG ) == portSTACK_FRAME_HAS_PADDING_FLAG )
{
pulTaskStack[ portOFFSET_TO_PSR ] |= portPSR_STACK_PADDING_MASK;
}
else
{
pulTaskStack[ portOFFSET_TO_PSR ] &= ( ~portPSR_STACK_PADDING_MASK );
}
/* This is not NULL only for the duration of the system call. */
pxMpuSettings->xSystemCallStackInfo.pulTaskStack = NULL;
}
}
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
/*-----------------------------------------------------------*/
BaseType_t xPortIsTaskPrivileged( void ) /* PRIVILEGED_FUNCTION */
{
BaseType_t xTaskIsPrivileged = pdFALSE;
const xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL ); /* Calling task's MPU settings. */
if( ( xTaskMpuSettings->ulTaskFlags & portTASK_IS_PRIVILEGED_FLAG ) == portTASK_IS_PRIVILEGED_FLAG )
{
xTaskIsPrivileged = pdTRUE;
}
return xTaskIsPrivileged;
}
/*-----------------------------------------------------------*/
static void prvRestoreContextOfFirstTask( 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. */
" ldr r3, pxCurrentTCBConst2 \n"/* Restore the context. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n"/* The first item in the TCB is the task top of stack. */
" add r1, r1, #4 \n"/* Move onto the second item in the TCB... */
" \n"
" dmb \n"/* Complete outstanding transfers before disabling MPU. */
" ldr r2, =0xe000ed94 \n"/* MPU_CTRL register. */
" ldr r3, [r2] \n"/* Read the value of MPU_CTRL. */
" bic r3, #1 \n"/* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
" str r3, [r2] \n"/* Disable MPU. */
" \n"
" ldr r2, =0xe000ed9c \n"/* Region Base Address register. */
" ldmia r1!, {r4-r11} \n"/* Read 4 sets of MPU registers. */
" stmia r2!, {r4-r11} \n"/* Write 4 sets of MPU registers. */
" \n"
" ldr r2, =0xe000ed94 \n"/* MPU_CTRL register. */
" ldr r3, [r2] \n"/* Read the value of MPU_CTRL. */
" orr r3, #1 \n"/* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
" str r3, [r2] \n"/* Enable MPU. */
" dsb \n"/* Force memory writes before continuing. */
" \n"
" ldmia r0!, {r3, r4-r11} \n"/* Pop the registers that are not automatically saved on exception entry. */
" msr control, r3 \n"
" msr psp, r0 \n"/* Restore the task stack pointer. */
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldr r14, =0xfffffffd \n"/* Load exec return code. */
" bx r14 \n"
" \n"
" .ltorg \n"/* Assemble current literal pool to avoid offset-out-of-bound errors with lto. */
" .align 4 \n"
" 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. */
" \n"
/*------------ Program MPU. ------------ */
" ldr r3, pxCurrentTCBConst2 \n" /* r3 = pxCurrentTCBConst2. */
" ldr r2, [r3] \n" /* r2 = pxCurrentTCB. */
" add r2, r2, #4 \n" /* r2 = Second item in the TCB which is xMPUSettings. */
" \n"
" dmb \n" /* Complete outstanding transfers before disabling MPU. */
" ldr r0, =0xe000ed94 \n" /* MPU_CTRL register. */
" ldr r3, [r0] \n" /* Read the value of MPU_CTRL. */
" bic r3, #1 \n" /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
" str r3, [r0] \n" /* Disable MPU. */
" \n"
" ldr r0, =0xe000ed9c \n" /* Region Base Address register. */
" ldmia r2!, {r4-r11} \n" /* Read 4 sets of MPU registers [MPU Region # 4 - 7]. */
" stmia r0, {r4-r11} \n" /* Write 4 sets of MPU registers [MPU Region # 4 - 7]. */
" \n"
" ldr r0, =0xe000ed94 \n" /* MPU_CTRL register. */
" ldr r3, [r0] \n" /* Read the value of MPU_CTRL. */
" orr r3, #1 \n" /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
" str r3, [r0] \n" /* Enable MPU. */
" dsb \n" /* Force memory writes before continuing. */
" \n"
/*---------- Restore Context. ---------- */
" ldr r3, pxCurrentTCBConst2 \n" /* r3 = pxCurrentTCBConst2. */
" ldr r2, [r3] \n" /* r2 = pxCurrentTCB. */
" ldr r1, [r2] \n" /* r1 = Location of saved context in TCB. */
" \n"
" ldmdb r1!, {r0, r4-r11} \n" /* r0 contains PSP after the hardware had saved context. r4-r11 contain hardware saved context. */
" msr psp, r0 \n"
" stmia r0, {r4-r11} \n" /* Copy the hardware saved context on the task stack. */
" ldmdb r1!, {r3-r11, lr} \n" /* r3 contains CONTROL register. r4-r11 and LR restored. */
" msr control, r3 \n"
" str r1, [r2] \n" /* Save the location where the context should be saved next as the first member of TCB. */
" \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" bx lr \n"
" \n"
" .ltorg \n" /* Assemble current literal pool to avoid offset-out-of-bound errors with lto. */
" .align 4 \n"
"pxCurrentTCBConst2: .word pxCurrentTCB \n"
);
}
@ -585,53 +976,66 @@ void xPortPendSVHandler( void )
__asm volatile
(
" mrs r0, psp \n"
" ldr r3, pxCurrentTCBConst \n" /* r3 = pxCurrentTCBConst. */
" ldr r2, [r3] \n" /* r2 = pxCurrentTCB. */
" ldr r1, [r2] \n" /* r1 = Location where the context should be saved. */
" \n"
" ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */
" ldr r2, [r3] \n"
/*------------ Save Context. ----------- */
" mrs r3, control \n"
" mrs r0, psp \n"
" isb \n"
" \n"
" mrs r1, control \n"
" stmdb r0!, {r1, r4-r11} \n"/* Save the remaining registers. */
" str r0, [r2] \n"/* Save the new top of stack into the first member of the TCB. */
" stmia r1!, {r3-r11, lr} \n" /* Store CONTROL register, r4-r11 and LR. */
" ldmia r0, {r4-r11} \n" /* Copy hardware saved context into r4-r11. */
" stmia r1!, {r0, r4-r11} \n" /* Store original PSP (after hardware has saved context) and the hardware saved context. */
" str r1, [r2] \n" /* Save the location from where the context should be restored as the first member of TCB. */
" \n"
" stmdb sp!, {r3, r14} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n"
" \n"/* Restore the context. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n"/* The first item in the TCB is the task top of stack. */
" add r1, r1, #4 \n"/* Move onto the second item in the TCB... */
/*---------- Select next task. --------- */
" mov r0, %0 \n"
" msr basepri, r0 \n"
" dsb \n"
" isb \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" \n"
" dmb \n"/* Complete outstanding transfers before disabling MPU. */
" ldr r2, =0xe000ed94 \n"/* MPU_CTRL register. */
" ldr r3, [r2] \n"/* Read the value of MPU_CTRL. */
" bic r3, #1 \n"/* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
" str r3, [r2] \n"/* Disable MPU. */
/*------------ Program MPU. ------------ */
" ldr r3, pxCurrentTCBConst \n" /* r3 = pxCurrentTCBConst. */
" ldr r2, [r3] \n" /* r2 = pxCurrentTCB. */
" add r2, r2, #4 \n" /* r2 = Second item in the TCB which is xMPUSettings. */
" \n"
" ldr r2, =0xe000ed9c \n"/* Region Base Address register. */
" ldmia r1!, {r4-r11} \n"/* Read 4 sets of MPU registers. */
" stmia r2!, {r4-r11} \n"/* Write 4 sets of MPU registers. */
" dmb \n" /* Complete outstanding transfers before disabling MPU. */
" ldr r0, =0xe000ed94 \n" /* MPU_CTRL register. */
" ldr r3, [r0] \n" /* Read the value of MPU_CTRL. */
" bic r3, #1 \n" /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
" str r3, [r0] \n" /* Disable MPU. */
" \n"
" ldr r2, =0xe000ed94 \n"/* MPU_CTRL register. */
" ldr r3, [r2] \n"/* Read the value of MPU_CTRL. */
" orr r3, #1 \n"/* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
" str r3, [r2] \n"/* Enable MPU. */
" dsb \n"/* Force memory writes before continuing. */
" ldr r0, =0xe000ed9c \n" /* Region Base Address register. */
" ldmia r2!, {r4-r11} \n" /* Read 4 sets of MPU registers [MPU Region # 4 - 7]. */
" stmia r0, {r4-r11} \n" /* Write 4 sets of MPU registers [MPU Region # 4 - 7]. */
" \n"
" ldmia r0!, {r3, r4-r11} \n"/* Pop the registers that are not automatically saved on exception entry. */
" msr control, r3 \n"
" ldr r0, =0xe000ed94 \n" /* MPU_CTRL register. */
" ldr r3, [r0] \n" /* Read the value of MPU_CTRL. */
" orr r3, #1 \n" /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
" str r3, [r0] \n" /* Enable MPU. */
" dsb \n" /* Force memory writes before continuing. */
" \n"
" msr psp, r0 \n"
" bx r14 \n"
/*---------- Restore Context. ---------- */
" ldr r3, pxCurrentTCBConst \n" /* r3 = pxCurrentTCBConst. */
" ldr r2, [r3] \n" /* r2 = pxCurrentTCB. */
" ldr r1, [r2] \n" /* r1 = Location of saved context in TCB. */
" \n"
" .ltorg \n"/* Assemble current literal pool to avoid offset-out-of-bound errors with lto. */
" .align 4 \n"
" ldmdb r1!, {r0, r4-r11} \n" /* r0 contains PSP after the hardware had saved context. r4-r11 contain hardware saved context. */
" msr psp, r0 \n"
" stmia r0, {r4-r11} \n" /* Copy the hardware saved context on the task stack. */
" ldmdb r1!, {r3-r11, lr} \n" /* r3 contains CONTROL register. r4-r11 and LR restored. */
" msr control, r3 \n"
" \n"
" str r1, [r2] \n" /* Save the location where the context should be saved next as the first member of TCB. */
" bx lr \n"
" \n"
" .ltorg \n" /* Assemble current literal pool to avoid offset-out-of-bound errors with lto. */
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY )
);
@ -816,11 +1220,19 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
( prvGetMPURegionSizeSetting( ( uint32_t ) __SRAM_segment_end__ - ( uint32_t ) __SRAM_segment_start__ ) ) |
( portMPU_REGION_ENABLE );
xMPUSettings->xRegionSettings[ 0 ].ulRegionStartAddress = ( uint32_t ) __SRAM_segment_start__;
xMPUSettings->xRegionSettings[ 0 ].ulRegionEndAddress = ( uint32_t ) __SRAM_segment_end__;
xMPUSettings->xRegionSettings[ 0 ].ulRegionPermissions = ( tskMPU_READ_PERMISSION |
tskMPU_WRITE_PERMISSION );
/* Invalidate user configurable regions. */
for( ul = 1UL; ul <= portNUM_CONFIGURABLE_REGIONS; ul++ )
{
xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( ( ul - 1UL ) | portMPU_REGION_VALID );
xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionStartAddress = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionEndAddress = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionPermissions = 0UL;
}
}
else
@ -843,6 +1255,11 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
( prvGetMPURegionSizeSetting( ulStackDepth * ( uint32_t ) sizeof( StackType_t ) ) ) |
( portMPU_REGION_CACHEABLE_BUFFERABLE ) |
( portMPU_REGION_ENABLE );
xMPUSettings->xRegionSettings[ 0 ].ulRegionStartAddress = ( uint32_t ) pxBottomOfStack;
xMPUSettings->xRegionSettings[ 0 ].ulRegionEndAddress = ( uint32_t ) ( ( uint32_t ) ( pxBottomOfStack ) +
( ulStackDepth * ( uint32_t ) sizeof( StackType_t ) ) - 1UL );
xMPUSettings->xRegionSettings[ 0 ].ulRegionPermissions = ( tskMPU_READ_PERMISSION |
tskMPU_WRITE_PERMISSION );
}
lIndex = 0;
@ -863,12 +1280,28 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
( prvGetMPURegionSizeSetting( xRegions[ lIndex ].ulLengthInBytes ) ) |
( xRegions[ lIndex ].ulParameters ) |
( portMPU_REGION_ENABLE );
xMPUSettings->xRegionSettings[ ul ].ulRegionStartAddress = ( uint32_t ) xRegions[ lIndex ].pvBaseAddress;
xMPUSettings->xRegionSettings[ ul ].ulRegionEndAddress = ( uint32_t ) ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress + xRegions[ lIndex ].ulLengthInBytes - 1UL );
xMPUSettings->xRegionSettings[ ul ].ulRegionPermissions = 0UL;
if( ( ( xRegions[ lIndex ].ulParameters & portMPU_REGION_READ_ONLY ) == portMPU_REGION_READ_ONLY ) ||
( ( xRegions[ lIndex ].ulParameters & portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY ) == portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY ) )
{
xMPUSettings->xRegionSettings[ ul ].ulRegionPermissions = tskMPU_READ_PERMISSION;
}
if( ( xRegions[ lIndex ].ulParameters & portMPU_REGION_READ_WRITE ) == portMPU_REGION_READ_WRITE )
{
xMPUSettings->xRegionSettings[ ul ].ulRegionPermissions = ( tskMPU_READ_PERMISSION | tskMPU_WRITE_PERMISSION );
}
}
else
{
/* Invalidate the region. */
xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( ( ul - 1UL ) | portMPU_REGION_VALID );
xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionStartAddress = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionEndAddress = 0UL;
xMPUSettings->xRegionSettings[ ul ].ulRegionPermissions = 0UL;
}
lIndex++;
@ -877,6 +1310,47 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
}
/*-----------------------------------------------------------*/
BaseType_t xPortIsAuthorizedToAccessBuffer( const void * pvBuffer,
uint32_t ulBufferLength,
uint32_t ulAccessRequested ) /* PRIVILEGED_FUNCTION */
{
uint32_t i, ulBufferStartAddress, ulBufferEndAddress;
BaseType_t xAccessGranted = pdFALSE;
const xMPU_SETTINGS * xTaskMpuSettings = xTaskGetMPUSettings( NULL ); /* Calling task's MPU settings. */
if( ( xTaskMpuSettings->ulTaskFlags & portTASK_IS_PRIVILEGED_FLAG ) == portTASK_IS_PRIVILEGED_FLAG )
{
xAccessGranted = pdTRUE;
}
else
{
if( portADD_UINT32_WILL_OVERFLOW( ( ( uint32_t ) pvBuffer ), ( ulBufferLength - 1UL ) ) == pdFALSE )
{
ulBufferStartAddress = ( uint32_t ) pvBuffer;
ulBufferEndAddress = ( ( ( uint32_t ) pvBuffer ) + ulBufferLength - 1UL );
for( i = 0; i < portTOTAL_NUM_REGIONS_IN_TCB; i++ )
{
if( portIS_ADDRESS_WITHIN_RANGE( ulBufferStartAddress,
xTaskMpuSettings->xRegionSettings[ i ].ulRegionStartAddress,
xTaskMpuSettings->xRegionSettings[ i ].ulRegionEndAddress ) &&
portIS_ADDRESS_WITHIN_RANGE( ulBufferEndAddress,
xTaskMpuSettings->xRegionSettings[ i ].ulRegionStartAddress,
xTaskMpuSettings->xRegionSettings[ i ].ulRegionEndAddress ) &&
portIS_AUTHORIZED( ulAccessRequested, xTaskMpuSettings->xRegionSettings[ i ].ulRegionPermissions ) )
{
xAccessGranted = pdTRUE;
break;
}
}
}
}
return xAccessGranted;
}
/*-----------------------------------------------------------*/
#if ( configASSERT_DEFINED == 1 )
void vPortValidateInterruptPriority( void )
@ -936,4 +1410,4 @@ void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
}
#endif /* configASSERT_DEFINED */
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/

View file

@ -104,10 +104,45 @@
uint32_t ulRegionAttribute;
} xMPU_REGION_REGISTERS;
/* Plus 1 to create space for the stack region. */
typedef struct MPU_REGION_SETTINGS
{
uint32_t ulRegionStartAddress;
uint32_t ulRegionEndAddress;
uint32_t ulRegionPermissions;
} xMPU_REGION_SETTINGS;
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
#ifndef configSYSTEM_CALL_STACK_SIZE
#error configSYSTEM_CALL_STACK_SIZE must be defined to the desired size of the system call stack in words for using MPU wrappers v2.
#endif
typedef struct SYSTEM_CALL_STACK_INFO
{
uint32_t ulSystemCallStackBuffer[ configSYSTEM_CALL_STACK_SIZE ];
uint32_t * pulSystemCallStack;
uint32_t * pulTaskStack;
uint32_t ulLinkRegisterAtSystemCallEntry;
} xSYSTEM_CALL_STACK_INFO;
#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
#define MAX_CONTEXT_SIZE 20
/* Flags used for xMPU_SETTINGS.ulTaskFlags member. */
#define portSTACK_FRAME_HAS_PADDING_FLAG ( 1UL << 0UL )
#define portTASK_IS_PRIVILEGED_FLAG ( 1UL << 1UL )
typedef struct MPU_SETTINGS
{
xMPU_REGION_REGISTERS xRegion[ portTOTAL_NUM_REGIONS_IN_TCB ];
xMPU_REGION_SETTINGS xRegionSettings[ portTOTAL_NUM_REGIONS_IN_TCB ];
uint32_t ulContext[ MAX_CONTEXT_SIZE ];
uint32_t ulTaskFlags;
#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
xSYSTEM_CALL_STACK_INFO xSystemCallStackInfo;
#endif
} xMPU_SETTINGS;
/* Architecture specifics. */
@ -118,9 +153,12 @@
/*-----------------------------------------------------------*/
/* SVC numbers for various services. */
#define portSVC_START_SCHEDULER 0
#define portSVC_YIELD 1
#define portSVC_RAISE_PRIVILEGE 2
#define portSVC_START_SCHEDULER 0
#define portSVC_YIELD 1
#define portSVC_RAISE_PRIVILEGE 2
#define portSVC_SYSTEM_CALL_ENTER 3 /* System calls with upto 4 parameters. */
#define portSVC_SYSTEM_CALL_ENTER_1 4 /* System calls with 5 parameters. */
#define portSVC_SYSTEM_CALL_EXIT 5
/* Scheduler utilities. */
@ -232,6 +270,16 @@
#define portRESET_PRIVILEGE() vResetPrivilege()
/*-----------------------------------------------------------*/
extern BaseType_t xPortIsTaskPrivileged( void );
/**
* @brief Checks whether or not the calling task is privileged.
*
* @return pdTRUE if the calling task is privileged, pdFALSE otherwise.
*/
#define portIS_TASK_PRIVILEGED() xPortIsTaskPrivileged()
/*-----------------------------------------------------------*/
portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void )
{
uint32_t ulCurrentInterrupt;