Trusted Firmware-A中ARMv7页表创建以及MMU使能

发布时间 2023-08-17 23:40:08作者: ArnoldLu

 关键词: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常规调用流程:

  1. MMU初始状态为Off。
  2. mmap_add_region()增加静态或mmap_add_dynamic_region()动态内存区域。
  3. 根据创建的内存映射区域表,使用init_xlat_tables()生成转换页表。
  4. 此后,不可增加静态区域,仅可增删动态区域。
  5. enable_mmu_svc_mon()使能MMU。
  6. 动态区域仍可增删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
在进入睡眠时,低功耗函数中关闭MMU/Cache:
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