Trusted Firmware-A中ARMv7页表创建以及MMU使能
关键词:mmap_region,TTBCR,MAIR,TCR,SCTLR等。
1 TF-A关于页表转换说明
《10. Translation (XLAT) Tables Library》中介绍了BL32中转换页表相关功能库,主要包括:
- 静态分配转换页表,根据内存区域生成转换页表。
- 根据ExceptionLevel生成不同translation regime转换页表。
- MMU打开时,支持区域动态映射和去映射。
- 支持非物理和虚拟同地址映射。
- 支持运行时动态改变内存属性。
10.1. About version 1 and version 2
TFA中转换页表支持v1和v2两种,但默认使用v2,但是有更新也会更新到v1。v1不支持动态映射且不够灵活。
10.2. Design concepts and interfaces
10.2.1. mmap regions
MMU映射的每一个区域用mmap_region表示:
typedef struct mmap_region { unsigned long long base_pa;--物理地址基地址。 uintptr_t base_va;--虚拟地址基地址。 size_t size;--区域大小。 unsigned int attr;--区域属性。 /* Desired granularity. See the MAP_REGION2() macro for more details. */ size_t granularity;--区域创建页表的粒度大小,可选项。 } mmap_region_t;
10.2.2. Translation Context
REGISTER_XLAT_CONTEXT初始化静态的页表转换上下文:
REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE);
REGISTER_XLAT_CONTEXT的参数包括:
- 名称,即静态的tf_xlat_ctx。
- MAX_MMAP_REGIONS定义了支持的最大mmap区域数量,包括静态和动态区域。。
-
MAX_XLAT_TABLES定义了level-2和level-3专业页表最大数量。
-
PLAT_VIRT_ADDR_SPACE_SIZE虚拟地址空间大小。
-
PLAT_PHY_ADDR_SPACE_SIZE物理地址空间大小。
10.3. Library APIs
API常规调用流程:
- MMU初始状态为Off。
- mmap_add_region()增加静态或mmap_add_dynamic_region()动态内存区域。
- 根据创建的内存映射区域表,使用init_xlat_tables()生成转换页表。
- 此后,不可增加静态区域,仅可增删动态区域。
- enable_mmu_svc_mon()使能MMU。
- 动态区域仍可增删mmap_add_dynamic_region()/mmap_remove_dynamic_region()。
10.5. Implementation details
10.5.4. TLB maintenance operations
2 BL32 MMU配置
bl32中对MMU的配置如下:一是在启动时创建页表,使能MMU;二是在
sp_min_entrypoint sp_min_plat_arch_setup setup_page_tables--创建页表。 mmap_add--将bl_regions(BL32_BASE到BL32_END区间)和plat_regions加入到tf_xlat_ctx中。 mmap_add_ctx--tf_xlat_ctx保存BL镜像使用的页表转换上下文信息。 mmap_add_region_ctx init_xlat_tables-- xlat_arch_current_el--获取当前EL等级,决定页表转换使用何种机制。 init_xlat_tables_ctx--根据tf_xlat_ctx创建页表。 enable_mmu_svc_mon setup_mmu_cfg--配置MAIR/TTBCR/TTBR0寄存器的值到mmu_cfg_params数组中。 enable_mmu_direct_svc_mon--enable_mmu.S中汇编函数,配置MAIR0/TTBCR/TTBR0/TTBR1/SCTLR寄存器。
sp_min_warm_entrypoint
bl32_plat_enable_mmu
enable_mmu_svc_mon
disable_mmu_secure--关闭MMU,D Cache。
disable_mmu_icache_secure--关闭MMU,I/D Cache。
2.1 配置MMU映射区域
首先需要配置bl32自身text/data/bss段,需要分成三两部分:
- text段:MT_CODE|MT_SECURE属性。
- rodata段:MT_RO_DATA|MT_SECURE属性。
- data/bss段:MT_RW_DATA|MT_SECURE属性。
这就需要对bl32的text/rodata/data/bss段linker script设置为页对齐。
另外还需要映射Device寄存器和uboot:
- 外设等寄存器:MT_DEVICE|MT_RW|MT_SECURE。
- uboot:由于bl32需要跳转到uboot,因此将uboot区域设置为MT_CODE|MT_SECURE。或者设置为MT_NS,但是要打开SCR.SIF。
- 共享内存:MT_MEMORY|MT_RW|MT_NS。
void sp_min_plat_arch_setup(void) { const mmap_region_t bl_regions[] = { MAP_BL_SP_MIN_TOTAL, ARM_MAP_BL_RO, #if USE_COHERENT_MEM ARM_MAP_BL_COHERENT_RAM, #endif {0} }; setup_page_tables(bl_regions, plat_arm_get_mmap()); enable_mmu_svc_mon(0); } const mmap_region_t *plat_arm_get_mmap(void) { return plat_arm_mmap; } const mmap_region_t plat_arm_mmap[] = { ARM_MAP_SHARED_RAM, V2M_MAP_IOFPGA, MAP_DEVICE0, {0} };
2.2 MMU使能配置
setup_mmu_cfg()生成关于MMU的配置到mmu_cfg_params。
void setup_mmu_cfg(uint64_t *params, unsigned int flags, const uint64_t *base_table, unsigned long long max_pa, uintptr_t max_va, __unused int xlat_regime) { uint64_t mair, ttbr0; uint32_t ttbcr; /* Set attributes in the right indices of the MAIR */ mair = MAIR0_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX); mair |= MAIR0_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX); mair |= MAIR0_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX);--设置三组属性指代。 /* * Configure the control register for stage 1 of the PL1&0 or EL2 * translation regimes. */ /* Use the Long-descriptor translation table format. */ ttbcr = TTBCR_EAE_BIT;--使用40位页表转换。 if (xlat_regime == EL1_EL0_REGIME) { assert(IS_IN_SVC() || IS_IN_MON()); /* * Disable translation table walk for addresses that are * translated using TTBR1. Therefore, only TTBR0 is used. */ ttbcr |= TTBCR_EPD1_BIT;--仅使用TTBR0的地址页表作为页表查询。不使用TTBR1。 } else { assert(xlat_regime == EL2_REGIME); assert(IS_IN_HYP()); /* * Set HTCR bits as well. Set HTTBR table properties * as Inner & outer WBWA & shareable. */ ttbcr |= HTCR_RES1 | HTCR_SH0_INNER_SHAREABLE | HTCR_RGN0_OUTER_WBA | HTCR_RGN0_INNER_WBA; } /* * Limit the input address ranges and memory region sizes translated * using TTBR0 to the given virtual address space size, if smaller than * 32 bits. */ if (max_va != UINT32_MAX) { uintptr_t virtual_addr_space_size = max_va + 1U; assert(virtual_addr_space_size >= xlat_get_min_virt_addr_space_size()); assert(virtual_addr_space_size <= MAX_VIRT_ADDR_SPACE_SIZE); assert(IS_POWER_OF_TWO(virtual_addr_space_size)); /* * __builtin_ctzll(0) is undefined but here we are guaranteed * that virtual_addr_space_size is in the range [1, UINT32_MAX]. */ int t0sz = 32 - __builtin_ctzll(virtual_addr_space_size); ttbcr |= (uint32_t) t0sz; } /* * Set the cacheability and shareability attributes for memory * associated with translation table walks using TTBR0. */ if ((flags & XLAT_TABLE_NC) != 0U) { /* Inner & outer non-cacheable non-shareable. */ ttbcr |= TTBCR_SH0_NON_SHAREABLE | TTBCR_RGN0_OUTER_NC | TTBCR_RGN0_INNER_NC; } else { /* Inner & outer WBWA & shareable. */ ttbcr |= TTBCR_SH0_INNER_SHAREABLE | TTBCR_RGN0_OUTER_WBA | TTBCR_RGN0_INNER_WBA; } /* Set TTBR0 bits as well */ ttbr0 = (uint64_t)(uintptr_t) base_table;--页表基地址,即tf_xlat_ctx.base_table。 if (is_armv8_2_ttcnp_present()) { /* Enable CnP bit so as to share page tables with all PEs. */ ttbr0 |= TTBR_CNP_BIT; } /* Now populate MMU configuration */ params[MMU_CFG_MAIR] = mair; params[MMU_CFG_TCR] = (uint64_t) ttbcr; params[MMU_CFG_TTBR0] = ttbr0; }
enable_mmu_direct_svc_mon()将mmu_cfg_params数据配置到寄存器中:
/* void enable_mmu_direct_svc_mon(unsigned int flags) */ func enable_mmu_direct_svc_mon /* Assert that MMU is turned off */ #if ENABLE_ASSERTIONS ldcopr r1, SCTLR tst r1, #SCTLR_M_BIT ASM_ASSERT(eq) #endif /* Invalidate TLB entries */ TLB_INVALIDATE(r0, TLBIALL)--根据flags决定是否置所有TLB无效。 mov r3, r0 ldr r0, =mmu_cfg_params--mmu_cfg_params存放MAIR/TTBCR/TTBR0寄存器的配置值,下面分别将r0指向的数据配置到寄存器中。 /* MAIR0. Only the lower 32 bits are used. */ ldr r1, [r0, #(MMU_CFG_MAIR << 3)] stcopr r1, MAIR0 /* TTBCR. Only the lower 32 bits are used. */ ldr r2, [r0, #(MMU_CFG_TCR << 3)] stcopr r2, TTBCR /* TTBR0 */ ldr r1, [r0, #(MMU_CFG_TTBR0 << 3)] ldr r2, [r0, #((MMU_CFG_TTBR0 << 3) + 4)] stcopr16 r1, r2, TTBR0_64 /* TTBR1 is unused right now; set it to 0. */ mov r1, #0 mov r2, #0 stcopr16 r1, r2, TTBR1_64--TTBR1清零。 /* * Ensure all translation table writes have drained into memory, the TLB * invalidation is complete, and translation register writes are * committed before enabling the MMU */ dsb ish isb /* Enable enable MMU by honoring flags */ ldcopr r1, SCTLR ldr r2, =(SCTLR_WXN_BIT | SCTLR_C_BIT | SCTLR_M_BIT)--分别表示:可写属性强制被置为ExecuteNever;使能D Cache和Unified Cache;使能MMU。 orr r1, r1, r2 /* Clear C bit if requested */ tst r3, #DISABLE_DCACHE bicne r1, r1, #SCTLR_C_BIT stcopr r1, SCTLR--配置MMU相关,此后MMU生效。 isb bx lr endfunc enable_mmu_direct_svc_mon