鴻蒙輕內核M核源碼分析系列十六 MPU內存保護單元
鴻蒙輕內核M核源碼分析系列十六 MPU內存保護單元

MPU(Memory Protection Unit,內存保護單元)把內存映射為一系列內存區域,定義這些內存區域的維洲,大小,訪問權限和內存熟悉信息。MPU支持對每個內存區域進行獨立的屬性設置,允許內存區域重, 可以導出內存屬性。有關MPU的詳細信息可以參考官方資料站點,比如對應Cortex-M3的文檔位置為:https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit。
在鴻蒙輕內核中,MPU用于任務棧的溢出檢測。本文主要分析鴻蒙輕內核MPU模塊的的源碼。本文中所涉及的源碼,以OpenHarmony LiteOS-M內核為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。鴻蒙輕內核支持的ARM Cortex-M芯片架構都支持MPU的,代碼都是一樣的,以kernel\arch\arm\cortex-m4\gcc\los_mpu.c為例進行講解。
1、MPU枚舉、結構體定義和常用宏定義
1.1 MPU枚舉、結構體定義
在文件kernel\arch\include\los_mpu.h定義MPU相關的結構體。⑴處定義MPU內存區域的訪問權限,有關訪問權限可以訪問官網https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-access-permission-attributes,特別是上述頁面的表格Table 4.47. AP encoding了解更多。⑵處定義MPU的是否可執行屬性枚舉,⑶處定義MPU內存區域是否可以共享屬性枚舉,⑷定義內存區域的類型屬性枚舉,⑸處的結構體用于定義MPU內存區域。
⑴ typedef enum { MPU_RW_BY_PRIVILEGED_ONLY = 0, MPU_RW_ANY = 1, MPU_RO_BY_PRIVILEGED_ONLY = 2, MPU_RO_ANY = 3, } MpuAccessPermission; ⑵ typedef enum { MPU_EXECUTABLE = 0, MPU_NON_EXECUTABLE = 1, } MpuExecutable; ⑶ typedef enum { MPU_NO_SHARE = 0, MPU_SHARE = 1, } MpuShareability; ⑷ typedef enum { MPU_MEM_ON_CHIP_ROM = 0, MPU_MEM_ON_CHIP_RAM = 1, MPU_MEM_XIP_PSRAM = 2, MPU_MEM_XIP_NOR_FLASH = 3, MPU_MEM_SHARE_MEM = 4, } MpuMemType; ⑸ typedef struct { UINT32 baseAddr; UINT64 size; /* armv7 size == 2^x (5 <= x <= 32) 128B - 4GB */ MpuAccessPermission permission; MpuExecutable executable; MpuShareability shareability; MpuMemType memType; } MPU_CFG_PARA;
1.2 MPU宏
MPU外設的一些宏定義有HAL Drivers定義,比如對于Cortex-M4,位置為Drivers\CMSIS\Core\Include\core_cm4.h。MPU結構體定義如下,關于MPU寄存器的詳細信息可以訪問https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit,查看頁面上的Table 4.38. MPU registers summary。下文在講解代碼時會涉及MPU的各個寄存器。
#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ #endif
另外,MPU支持8個內存區域,kernel\arch\arm\cortex-m4\gcc\los_mpu.c文件中定義的宏如下:
#define MPU_MAX_REGION_NUM 8
2、MPU常用操作
MPU常用操作函數包含使能MPUHalMpuEnable、失能MPUHalMpuDisable,設置指定的內存區域屬性HalMpuSetRegion,失能指定的內存區域HalMpuDisableRegion和獲取未使用的內存區域編號HalMpuUnusedRegionGet。
2.1 使能MPUHalMpuEnable
該函數使能MPU功能,⑴處對MPU控制寄存器MPU Control Register進行操作,通過對寄存器相關的bit位進行賦值來使能MPU。有關該寄存器建議詳細閱讀https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-control-register。⑵處代碼使能MemoryFault異常。接著執行的數據同步屏障__DSB()和指令同步屏障__ISB(),詳細的可以查閱ARM的DMB,DSB,ISB等指令。
VOID HalMpuEnable(UINT32 defaultRegionEnable) { UINT32 intSave = HalIntLock(); ⑴ MPU->CTRL = (MPU_CTRL_ENABLE_Msk | ((defaultRegionEnable << MPU_CTRL_PRIVDEFENA_Pos) & MPU_CTRL_PRIVDEFENA_Msk)); ⑵ SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; __DSB(); __ISB(); HalIntRestore(intSave); }
2.2 失能MPUHalMpuDisable
代碼很簡單,直接把MPU控制寄存器賦值為0來失能MPU功能。
VOID HalMpuDisable(VOID) { UINT32 intSave = HalIntLock(); MPU->CTRL = 0; __DSB(); __ISB(); HalIntRestore(intSave); }
2.3 失能指定的內存區域HalMpuDisableRegion
HalMpuDisableRegion函數執行后不再對指定的內存區域進行MPU保護,⑴處校驗參數合法性。⑵處沒有使用的MPU內存區域無法失能。⑶處獲取MPU的類型寄存器,詳細可以訪問https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-type-register。
⑷處表示MPU的數據內存區域(MPU data regions)數量不為空時,執行⑸處代碼更新MPU內存區域編號寄存器(MPU Region Number Register
)為指定的內存區域編號,詳細的信息可以參考https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-number-register。然后執行⑹處代碼更新MPU內存區域屬性和大小寄存器(MPU Region Attribute and Size Register
),詳細可以參考https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-attribute-and-size-register。⑺處把全局變量數組中指定的區域編號設置為未使用0。
UINT32 HalMpuDisableRegion(UINT32 regionId) { volatile UINT32 type; UINT32 intSave; ⑴ if (regionId >= MPU_MAX_REGION_NUM) { return LOS_NOK; } intSave = HalIntLock(); ⑵ if (!g_regionNumBeUsed[regionId]) { HalIntRestore(intSave); return LOS_NOK; } ⑶ type = MPU->TYPE; ⑷ if ((MPU_TYPE_DREGION_Msk & type) != 0) { ⑸ MPU->RNR = regionId; ⑹ MPU->RASR = 0; __DSB(); __ISB(); } ⑺ g_regionNumBeUsed[regionId] = 0; /* clear mpu region used flag */ HalIntRestore(intSave); return LOS_OK; }
2.4 設置指定的內存區域屬性HalMpuSetRegion
HalMpuSetRegion函數設置指定的內存區域的屬性。⑴處對參數進行合法性校驗。⑵處如果MPU類型寄存器中表示的數據內存區域的數量為0,無法繼續設置內嵌區域,直接返回LOS_NOK。⑶處調用函數HalMpuEncodeSize根據內存區域的實際大小值獲取編碼大小,該值后續會被賦值給MPU屬性和大小寄存器的size位。⑷判斷內存區域需要相對內存區域大小進行內存對齊,否則返回LOS_NOK。
⑸處計算基地址寄存器的數據,有關基地址寄存器(MPU Region Base Address Register),可以訪問https://developer.arm.com/documentation/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-base-address-register了解更多。⑹處計算屬性和大小寄存器的數值。⑺處如果指定的內存區域被使用,直接返回LOS_NOK。⑻處設置MPU相關的寄存器,并標記該內存區域已使用。代碼如下:
UINT32 HalMpuSetRegion(UINT32 regionId, MPU_CFG_PARA *para) { UINT32 RASR; UINT32 RBAR; UINT32 RNR; UINT32 encodeSize; UINT32 intSave; UINT64 size; ⑴ if ((regionId >= MPU_MAX_REGION_NUM) || (para == NULL)) { return LOS_NOK; } ⑵ if ((MPU_TYPE_DREGION_Msk & MPU->TYPE) == 0) { return LOS_NOK; } RNR = regionId; ⑶ encodeSize = HalMpuEncodeSize(para->size); if (encodeSize == 0) { return LOS_NOK; } ⑷ size = para->size - 1; /* size aligned after encode check */ if ((para->baseAddr & size) != 0) { /* base addr should aligned to region size */ return LOS_NOK; } ⑸ RBAR = para->baseAddr & MPU_RBAR_ADDR_Msk; ⑹ RASR = HalMpuGetRASR(encodeSize, para); intSave = HalIntLock(); ⑺ if (g_regionNumBeUsed[regionId]) { HalIntRestore(intSave); return LOS_NOK; } ⑻ MPU->RNR = RNR; MPU->RBAR = RBAR; MPU->RASR = RASR; __DSB(); __ISB(); g_regionNumBeUsed[regionId] = 1; /* Set mpu region used flag */ HalIntRestore(intSave); return LOS_OK; }
HalMpuEncodeSize函數根據內存區域實際大小獲取size屬性值,對應的計算公式為:(Region size in bytes) = 2^(SIZE+1),詳細信息可以訪問MPU屬性和大小寄存器官網資料頁面的Table 4.44. Example SIZE field values。32bytes對應4,1KB對應5,…,4GB對應31。
⑴處表示內存區域大小不能大于4GB,然后判斷是否相對32字節進行內存對齊。⑵處先右移2位,然后while循環,執行⑶每向右循環一位,size屬性大小增加1。
STATIC UINT32 HalMpuEncodeSize(UINT64 size) { UINT32 encodeSize = 0; ⑴ if (size > SIZE_4G_BYTE) { return 0; } if ((size & 0x1F) != 0) { /* size should aligned to 32 byte at least. */ return 0; } ⑵ size = (size >> 2); while (size != 0) { if (((size & 1) != 0) && ((size & 0xFFFFFFFE) != 0)) { /* size != 2^x (5 <= x <= 32) 128B - 4GB */ return 0; } ⑶ size = (size >> 1); encodeSize++; } return encodeSize; }
HalMpuGetRASR根據size屬性值和配置參數計算屬性和大小寄存器的值。⑴處根據配置的訪問權限計算AP(ACCESS permission),然后計算屬性和大小寄存器的值,最后執行⑶給寄存器賦值。
STATIC UINT32 HalMpuEncodeAP(MpuAccessPermission permission) { UINT32 ap; switch (permission) { case MPU_RW_BY_PRIVILEGED_ONLY: ap = MPU_AP_RW_USER_FORBID; break; case MPU_RW_ANY: ap = MPU_AP_RW_USER_RW; break; case MPU_RO_BY_PRIVILEGED_ONLY: ap = MPU_AP_RO_USER_FORBID; break; case MPU_RO_ANY: ap = MPU_AP_RO_USER_RO; break; default: ap = MPU_AP_RW_USER_RW; break; } return ap; } STATIC VOID HalMpuRASRAddMemAttr(MPU_CFG_PARA *para, UINT32 *RASR) { BOOL cachable = 0; BOOL buffable = 0; switch (para->memType) { case MPU_MEM_ON_CHIP_ROM: case MPU_MEM_ON_CHIP_RAM: cachable = 1; buffable = 0; break; case MPU_MEM_XIP_PSRAM: cachable = 1; buffable = 1; break; case MPU_MEM_XIP_NOR_FLASH: cachable = 0; buffable = 1; break; default: break; } (*RASR) |= ((cachable << MPU_RASR_C_Pos) | (buffable << MPU_RASR_B_Pos)); } STATIC UINT32 HalMpuGetRASR(UINT32 encodeSize, MPU_CFG_PARA *para) { UINT32 RASR; UINT32 ap; ⑴ ap = HalMpuEncodeAP(para->permission); RASR = MPU_RASR_ENABLE_Msk; RASR |= ((encodeSize << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk); RASR |= ((ap << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | ((para->executable << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | ((para->shareability << MPU_RASR_S_Pos) & MPU_RASR_S_Msk); ⑶ HalMpuRASRAddMemAttr(para, &RASR); return RASR; }
小結
本文帶領大家一起剖析了鴻蒙輕內核的MPU模塊的源代碼。感謝閱讀,如有任何問題、建議,都可以博客下留言給我,謝謝。
IoT 輕量級操作系統 LiteOS
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。