文章目录
前言
操作系统,在入行嵌入式j接触它之前,感觉那是多么高深、神圣的技术,感觉它是高不可攀的。曾经也幻想过它有多么的复杂,在裸机编程时无数次想去应用这个技术,但无奈,无人指引,以致于每每都会望而却步。如今,也在多种OS的基础上做过各行各业的软件开发,所以想在闲暇之时,将使用过的OS内核软件逐行阅读,以提升自我编程能力、去了解更多软件编程思想。写此专栏文章的目的也仅仅是为了让这个过程留痕。
本文所有代码源于RTOS-Keenel
一、任务MPU配置
上源码
void vPortStoreTaskMPUSettings( xMPU_SETTINGS * xMPUSettings,
const struct xMEMORY_REGION * const xRegions,
StackType_t * pxBottomOfStack,
configSTACK_DEPTH_TYPE uxStackDepth )
{
uint32_t ulRegionStartAddress, ulRegionEndAddress, ulRegionNumber;
int32_t lIndex = 0;
#if defined( __ARMCC_VERSION )
/* Declaration when these variable are defined in code instead of being
* exported from linker scripts. */
extern uint32_t * __privileged_sram_start__;
extern uint32_t * __privileged_sram_end__;
#else
/* Declaration when these variable are exported from linker scripts. */
extern uint32_t __privileged_sram_start__[];
extern uint32_t __privileged_sram_end__[];
#endif /* defined( __ARMCC_VERSION ) */
/* Setup MAIR0. */
xMPUSettings->ulMAIR0 = ( ( portMPU_NORMAL_MEMORY_BUFFERABLE_CACHEABLE << portMPU_MAIR_ATTR0_POS ) & portMPU_MAIR_ATTR0_MASK );
xMPUSettings->ulMAIR0 |= ( ( portMPU_DEVICE_MEMORY_nGnRE << portMPU_MAIR_ATTR1_POS ) & portMPU_MAIR_ATTR1_MASK );
/* This function is called automatically when the task is created - in
* which case the stack region parameters will be valid. At all other
* times the stack parameters will not be valid and it is assumed that
* the stack region has already been configured. */
if( uxStackDepth > 0 )
{
ulRegionStartAddress = ( uint32_t ) pxBottomOfStack;
ulRegionEndAddress = ( uint32_t ) pxBottomOfStack + ( uxStackDepth * ( configSTACK_DEPTH_TYPE ) sizeof( StackType_t ) ) - 1;
/* If the stack is within the privileged SRAM, do not protect it
* using a separate MPU region. This is needed because privileged
* SRAM is already protected using an MPU region and ARMv8-M does
* not allow overlapping MPU regions. */
if( ( ulRegionStartAddress >= ( uint32_t ) __privileged_sram_start__ ) &&
( ulRegionEndAddress <= ( uint32_t ) __privileged_sram_end__ ) )
{
xMPUSettings->xRegionsSettings[ 0 ].ulRBAR = 0;
xMPUSettings->xRegionsSettings[ 0 ].ulRLAR = 0;
}
else
{
/* Define the region that allows access to the stack. */
ulRegionStartAddress &= portMPU_RBAR_ADDRESS_MASK;
ulRegionEndAddress &= portMPU_RLAR_ADDRESS_MASK;
xMPUSettings->xRegionsSettings[ 0 ].ulRBAR = ( ulRegionStartAddress ) |
( portMPU_REGION_NON_SHAREABLE ) |
( portMPU_REGION_READ_WRITE ) |
( portMPU_REGION_EXECUTE_NEVER );
xMPUSettings->xRegionsSettings[ 0 ].ulRLAR = ( ulRegionEndAddress ) |
( portMPU_RLAR_ATTR_INDEX0 ) |
( portMPU_RLAR_REGION_ENABLE );
}
}
/* User supplied configurable regions. */
for( ulRegionNumber = 1; ulRegionNumber <= portNUM_CONFIGURABLE_REGIONS; ulRegionNumber++ )
{
/* If xRegions is NULL i.e. the task has not specified any MPU
* region, the else part ensures that all the configurable MPU
* regions are invalidated. */
if( ( xRegions != NULL ) && ( xRegions[ lIndex ].ulLengthInBytes > 0UL ) )
{
/* Translate the generic region definition contained in xRegions
* into the ARMv8 specific MPU settings that are then stored in
* xMPUSettings. */
ulRegionStartAddress = ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress ) & portMPU_RBAR_ADDRESS_MASK;
ulRegionEndAddress = ( uint32_t ) xRegions[ lIndex ].pvBaseAddress + xRegions[ lIndex ].ulLengthInBytes - 1;
ulRegionEndAddress &= portMPU_RLAR_ADDRESS_MASK;
/* Start address. */
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRBAR = ( ulRegionStartAddress ) |
( portMPU_REGION_NON_SHAREABLE );
/* RO/RW. */
if( ( xRegions[ lIndex ].ulParameters & tskMPU_REGION_READ_ONLY ) != 0 )
{
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRBAR |= ( portMPU_REGION_READ_ONLY );
}
else
{
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRBAR |= ( portMPU_REGION_READ_WRITE );
}
/* XN. */
if( ( xRegions[ lIndex ].ulParameters & tskMPU_REGION_EXECUTE_NEVER ) != 0 )
{
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRBAR |= ( portMPU_REGION_EXECUTE_NEVER );
}
/* End Address. */
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRLAR = ( ulRegionEndAddress ) |
( portMPU_RLAR_REGION_ENABLE );
/* PXN. */
#if ( portARMV8M_MINOR_VERSION >= 1 )
{
if( ( xRegions[ lIndex ].ulParameters & tskMPU_REGION_PRIVILEGED_EXECUTE_NEVER ) != 0 )
{
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRLAR |= ( portMPU_RLAR_PRIVILEGED_EXECUTE_NEVER );
}
}
#endif /* portARMV8M_MINOR_VERSION >= 1 */
/* Normal memory/ Device memory. */
if( ( xRegions[ lIndex ].ulParameters & tskMPU_REGION_DEVICE_MEMORY ) != 0 )
{
/* Attr1 in MAIR0 is configured as device memory. */
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRLAR |= portMPU_RLAR_ATTR_INDEX1;
}
else
{
/* Attr0 in MAIR0 is configured as normal memory. */
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRLAR |= portMPU_RLAR_ATTR_INDEX0;
}
}
else
{
/* Invalidate the region. */
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRBAR = 0UL;
xMPUSettings->xRegionsSettings[ ulRegionNumber ].ulRLAR = 0UL;
}
lIndex++;
}
}
二、详细逻辑分析
用于存储任务内存保护单元(MPU)设置。MPU 是一种硬件功能,用于限制程序访问的内存区域,从而提高系统的安全性和稳定性。
xMPUSettings
:用于存储任务 MPU 设置的结构体。xRegions
:用户提供的可配置内存区域。pxBottomOfStack
:任务堆栈的底部地址。uxStackDepth
:任务堆栈的深度。- 设置
MAIR0
寄存器,该寄存器用于定义内存属性。在这个例子中,MAIR0
被设置为允许缓存和缓冲的正常内存,以及不允许缓存和缓冲的设备内存。 - 如果
uxStackDepth
大于 0,那么函数会设置任务的堆栈区域。首先,函数会计算堆栈区域的起始地址和结束地址。然后,如果堆栈区域在特权 SRAM 内,那么函数不会为堆栈区域设置单独的 MPU 区域,因为特权 SRAM 已经被保护。否则,函数会定义一个允许访问堆栈的 MPU 区域。 - 函数会处理用户提供的可配置内存区域。对于每个可配置内存区域,函数会将其转换为 ARMv8-M 特定的 MPU 设置,并存储在
xMPUSettings
中。如果xRegions
为 NULL,或者某个区域的长度为 0,那么函数会无效化该区域。
三、源码复杂点解读
3.1、privileged_sram_start 和 privileged_sram_end
表示特权SRAM的起始地址和结束地址。
3.2、xMPUSettings->ulMAIR0
设置MAIR0寄存器。该寄存器用于定义内存属性。在这个位置MAIR0被设置为允许缓存和缓冲的正常内存,以及不允许缓存和缓冲的设备内存。
3.3、uxStackDepth > 0
该条件语句中,主要是计算堆栈区域的起始地址和结束地址。然后再检查该堆栈区域是否在特权SRAM中。如果是,那么函数不会为堆栈区域设置单独的MPU区域,因为特权SRAM已经被保护。否则,函数会定义一个运行访问堆栈的MPU区域。