MMU(内存管理单元)负责将软件使用的虚拟地址转换为内存系统中使用的物理地址。MMU包括两个模块:TLB(Translation Lookaside Buffer)和TWU(Table Walk Unit)。TLB缓存最近使用的转换(类似cache,将转换映射放入缓存,提高映射效率),而TWU从内存中读取转换表(查表,完成虚拟地址到物理地址的转换)。
转换表位于内存中,用于存储虚拟地址和物理地址之间的映射,另外还包括物理内存位置的属性。这些转换表是由内存管理单元MMU访问。
软件分配的所有内存地址都是虚拟的。这些内存地址被传递到 MMU,MMU 检查 TLB 中是否有最近使用的缓存转换。如果 MMU 没有找到最近缓存的转换,TWU会从内存中读取相应的表条目,如下所示:
转换表库有两个版本V1和V2,我们这里只关注V2的转换表库。
├── aarch64
│ ├── enable_mmu.S
│ └── xlat_tables_arch.c
├── ro_xlat_tables.mk
├── xlat_tables.mk
├── xlat_tables_context.c
├── xlat_tables_core.c
├── xlat_tables_private.h
└── xlat_tables_utils.c
xlat_tables_core.c
文件,提供转换上下文初始化和内存映射等功能xlat_tables_context.c
文件,提供添加映射区域等接口xlat_tables_utils.c
文件,提供转换表状态打印,内存属性查看等功能xlat_tables_arch.c
文件,提供TLB无效,创建MMU及计算物理地址空间大小等功能mmap regions
是对要内存映射区域的抽象。
/*
* Structure for specifying a single region of memory.
*/
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;
为了不直接使用结构体struct mmap_region
,使用宏MAP_REGION
和MAP_REGION_PLAT
定义映射区域。
/*
* Default granularity size for an mmap_region_t.
* Useful when no specific granularity is required.
*
* By default, choose the biggest possible block size allowed by the
* architectural state and granule size in order to minimize the number of page
* tables required for the mapping.
*/
#define REGION_DEFAULT_GRANULARITY XLAT_BLOCK_SIZE(MIN_LVL_BLOCK_DESC)
/* Helper macro to define an mmap_region_t. */
#define MAP_REGION(_pa, _va, _sz, _attr) \
MAP_REGION_FULL_SPEC(_pa, _va, _sz, _attr, REGION_DEFAULT_GRANULARITY)
/* Helper macro to define an mmap_region_t with an identity mapping. */
#define MAP_REGION_FLAT(_adr, _sz, _attr) \
MAP_REGION(_adr, _adr, _sz, _attr)
可以看出,最终展开到宏MAP_REGION_FULL_SPEC
,定义如下。
/*
* Helper macro to define an mmap_region_t. This macro allows to specify all
* the fields of the structure but its parameter list is not guaranteed to
* remain stable as we add members to mmap_region_t.
*/
#define MAP_REGION_FULL_SPEC(_pa, _va, _sz, _attr, _gr) \
{ \
.base_pa = (_pa), \
.base_va = (_va), \
.size = (_sz), \
.attr = (_attr), \
.granularity = (_gr), \
}
内存属性包括内存类型、访问权限、执行权限以及缓存性与共享性等。
/*
* Memory mapping attributes
*/
/*
* Memory types supported.
* These are organised so that, going down the list, the memory types are
* getting weaker; conversely going up the list the memory types are getting
* stronger.
*/
#define MT_DEVICE U(0)
#define MT_NON_CACHEABLE U(1)
#define MT_MEMORY U(2)
/* Values up to 7 are reserved to add new memory types in the future */
#define MT_RO (U(0) << MT_PERM_SHIFT)
#define MT_RW (U(1) << MT_PERM_SHIFT)
#define MT_SECURE (U(0) << MT_PAS_SHIFT)
#define MT_NS (U(1) << MT_PAS_SHIFT)
#define MT_ROOT (U(2) << MT_PAS_SHIFT)
#define MT_REALM (U(3) << MT_PAS_SHIFT)
/*
* Access permissions for instruction execution are only relevant for normal
* read-only memory, i.e. MT_MEMORY | MT_RO. They are ignored (and potentially
* overridden) otherwise:
* - Device memory is always marked as execute-never.
* - Read-write normal memory is always marked as execute-never.
*/
#define MT_EXECUTE (U(0) << MT_EXECUTE_SHIFT)
#define MT_EXECUTE_NEVER (U(1) << MT_EXECUTE_SHIFT)
/*
* When mapping a region at EL0 or EL1, this attribute will be used to determine
* if a User mapping (EL0) will be created or a Privileged mapping (EL1).
*/
#define MT_USER (U(1) << MT_USER_SHIFT)
#define MT_PRIVILEGED (U(0) << MT_USER_SHIFT)
/*
* Shareability defines the visibility of any cache changes to
* all masters belonging to a shareable domain.
*
* MT_SHAREABILITY_ISH: For inner shareable domain
* MT_SHAREABILITY_OSH: For outer shareable domain
* MT_SHAREABILITY_NSH: For non shareable domain
*/
#define MT_SHAREABILITY_ISH (U(1) << MT_SHAREABILITY_SHIFT)
#define MT_SHAREABILITY_OSH (U(2) << MT_SHAREABILITY_SHIFT)
#define MT_SHAREABILITY_NSH (U(3) << MT_SHAREABILITY_SHIFT)
/* Compound attributes for most common usages */
#define MT_CODE (MT_MEMORY | MT_RO | MT_EXECUTE)
#define MT_RO_DATA (MT_MEMORY | MT_RO | MT_EXECUTE_NEVER)
#define MT_RW_DATA (MT_MEMORY | MT_RW | MT_EXECUTE_NEVER)
MT_MEMORY | MT_RO
。设备内存和读写内存始终标记为不可执行,即忽略执行权限。另外还定义了EL3下的内存类型,如果使能了RME,EL3的PAS(物理地址空间)属性是ROOT,否则是SECURE。
/* Memory type for EL3 regions. With RME, EL3 is in ROOT PAS */
#if ENABLE_RME
#define EL3_PAS MT_ROOT
#else
#define EL3_PAS MT_SECURE
#endif /* ENABLE_RME */
转换上下文存放了转换表的所有信息,结构体定义如下:
/* Struct that holds all information about the translation tables. */
struct xlat_ctx {
/*
* Max allowed Virtual and Physical Addresses.
*/
unsigned long long pa_max_address;
uintptr_t va_max_address;
/*
* Array of all memory regions stored in order of ascending end address
* and ascending size to simplify the code that allows overlapping
* regions. The list is terminated by the first entry with size == 0.
* The max size of the list is stored in `mmap_num`. `mmap` points to an
* array of mmap_num + 1 elements, so that there is space for the final
* null entry.
*/
struct mmap_region *mmap;
int mmap_num;
/*
* Array of finer-grain translation tables.
* For example, if the initial lookup level is 1 then this array would
* contain both level-2 and level-3 entries.
*/
uint64_t (*tables)[XLAT_TABLE_ENTRIES];
int tables_num;
#if PLAT_RO_XLAT_TABLES
bool readonly_tables;
#endif
/*
* Keep track of how many regions are mapped in each table. The base
* table can't be unmapped so it isn't needed to keep track of it.
*/
#if PLAT_XLAT_TABLES_DYNAMIC
int *tables_mapped_regions;
#endif /* PLAT_XLAT_TABLES_DYNAMIC */
int next_table;
/*
* Base translation table. It doesn't need to have the same amount of
* entries as the ones used for other levels.
*/
uint64_t *base_table;
unsigned int base_table_entries;
/*
* Max Physical and Virtual addresses currently in use by the
* translation tables. These might get updated as we map/unmap memory
* regions but they will never go beyond pa/va_max_address.
*/
unsigned long long max_pa;
uintptr_t max_va;
/* Level of the base translation table. */
unsigned int base_level;
/* Set to true when the translation tables are initialized. */
bool initialized;
/*
* Translation regime managed by this xlat_ctx_t. It should be one of
* the EL*_REGIME defines.
*/
int xlat_regime;
};
对于启动阶段,创建分配了一个默认转换上下文,使用宏REGISTER_XLAT_CONTEXT
定义。
REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE);
#define REGISTER_XLAT_CONTEXT(_ctx_name, _mmap_count, _xlat_tables_count, \
_virt_addr_space_size, _phy_addr_space_size) \
REGISTER_XLAT_CONTEXT_FULL_SPEC(_ctx_name, (_mmap_count), \
(_xlat_tables_count), \
(_virt_addr_space_size), \
(_phy_addr_space_size), \
EL_REGIME_INVALID, \
"xlat_table", "base_xlat_table")
该宏需要提供如下参数:
每个API都有两种接口类型:用于BL启动镜像的当前转换上下文;用于给定的上下文,以_ctx
结尾。
根据当前映射区域初始化转换表。在调用该API之后,只允许添加动态内存区域。
/*
* Initialize translation tables from the current list of mmap regions. Calling
* this function marks the transition point after which static regions can no
* longer be added.
*/
void init_xlat_tables(void);
void init_xlat_tables_ctx(xlat_ctx_t *ctx);
init_xlat_tables
最终也是调用init_xlat_tables_ctx
函数,只是先获取当前的运行异常等级,配置使用哪个转换机制。
void __init init_xlat_tables(void)
{
assert(tf_xlat_ctx.xlat_regime == EL_REGIME_INVALID);
unsigned int current_el = xlat_arch_current_el();
if (current_el == 1U) {
tf_xlat_ctx.xlat_regime = EL1_EL0_REGIME;
} else if (current_el == 2U) {
tf_xlat_ctx.xlat_regime = EL2_REGIME;
} else {
assert(current_el == 3U);
tf_xlat_ctx.xlat_regime = EL3_REGIME;
}
init_xlat_tables_ctx(&tf_xlat_ctx);
}
init_xlat_tables_ctx
主要功能是将映射区域添加到转换表中,定义如下,关键函数为xlat_tables_map_region
。
void __init init_xlat_tables_ctx(xlat_ctx_t *ctx)
{
assert(ctx != NULL);
assert(!ctx->initialized);
assert((ctx->xlat_regime == EL3_REGIME) ||
(ctx->xlat_regime == EL2_REGIME) ||
(ctx->xlat_regime == EL1_EL0_REGIME));
assert(!is_mmu_enabled_ctx(ctx));
mmap_region_t *mm = ctx->mmap;
assert(ctx->va_max_address >=
(xlat_get_min_virt_addr_space_size() - 1U));
assert(ctx->va_max_address <= (MAX_VIRT_ADDR_SPACE_SIZE - 1U));
assert(IS_POWER_OF_TWO(ctx->va_max_address + 1U));
xlat_mmap_print(mm);
/* All tables must be zeroed before mapping any region. */
for (unsigned int i = 0U; i < ctx->base_table_entries; i++)
ctx->base_table[i] = INVALID_DESC;
for (int j = 0; j < ctx->tables_num; j++) {
#if PLAT_XLAT_TABLES_DYNAMIC
ctx->tables_mapped_regions[j] = 0;
#endif
for (unsigned int i = 0U; i < XLAT_TABLE_ENTRIES; i++)
ctx->tables[j][i] = INVALID_DESC;
}
while (mm->size != 0U) {
uintptr_t end_va = xlat_tables_map_region(ctx, mm, 0U,
ctx->base_table, ctx->base_table_entries,
ctx->base_level);
#if !(HW_ASSISTED_COHERENCY || WARMBOOT_ENABLE_DCACHE_EARLY)
xlat_clean_dcache_range((uintptr_t)ctx->base_table,
ctx->base_table_entries * sizeof(uint64_t));
#endif
if (end_va != (mm->base_va + mm->size - 1U)) {
ERROR("Not enough memory to map region:\n"
" VA:0x%lx PA:0x%llx size:0x%zx attr:0x%x\n",
mm->base_va, mm->base_pa, mm->size, mm->attr);
panic();
}
mm++;
}
assert(ctx->pa_max_address <= xlat_arch_get_max_supported_pa());
assert(ctx->max_va <= ctx->va_max_address);
assert(ctx->max_pa <= ctx->pa_max_address);
ctx->initialized = true;
xlat_tables_print(ctx);
}
上面两个API是根据PA和VA添加映射区域,下面两个是以数组形式进行添加映射区域。
/*
* Add a static region with defined base PA and base VA. This function can only
* be used before initializing the translation tables. The region cannot be
* removed afterwards.
*/
void mmap_add_region(unsigned long long base_pa, uintptr_t base_va,
size_t size, unsigned int attr);
void mmap_add_region_ctx(xlat_ctx_t *ctx, const mmap_region_t *mm);
/*
* Add an array of static regions with defined base PA and base VA. This
* function can only be used before initializing the translation tables. The
* regions cannot be removed afterwards.
*/
void mmap_add(const mmap_region_t *mm);
void mmap_add_ctx(xlat_ctx_t *ctx, const mmap_region_t *mm);
在启动阶段定义了一个默认的转换上下文:
/*
* Allocate and initialise the default translation context for the BL image
* currently executing.
*/
REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE);
mmap_add_region
和mmap_add
都是添加到这个默认的转换上下文中。
void mmap_add_region(unsigned long long base_pa, uintptr_t base_va, size_t size,
unsigned int attr)
{
mmap_region_t mm = MAP_REGION(base_pa, base_va, size, attr);
mmap_add_region_ctx(&tf_xlat_ctx, &mm);
}
void mmap_add(const mmap_region_t *mm)
{
mmap_add_ctx(&tf_xlat_ctx, mm);
}
上面最终都会调用mmap_add_region_ctx
函数,将定义的映射区域添加到转换上下文定义的数组中,其是就是整合到一个映射区域数组中,虚拟地址小和区域大小小的排在前面。
void mmap_add_region_ctx(xlat_ctx_t *ctx, const mmap_region_t *mm)
{
mmap_region_t *mm_cursor = ctx->mmap, *mm_destination;
const mmap_region_t *mm_end = ctx->mmap + ctx->mmap_num;
const mmap_region_t *mm_last;
unsigned long long end_pa = mm->base_pa + mm->size - 1U;
uintptr_t end_va = mm->base_va + mm->size - 1U;
int ret;
/* Ignore empty regions */
if (mm->size == 0U)
return;
/* Static regions must be added before initializing the xlat tables. */
assert(!ctx->initialized);
ret = mmap_add_region_check(ctx, mm);
if (ret != 0) {
ERROR("mmap_add_region_check() failed. error %d\n", ret);
assert(false);
return;
}
/*
* Find correct place in mmap to insert new region.
*
* 1 - Lower region VA end first.
* 2 - Smaller region size first.
*
* VA 0 0xFF
*
* 1st |------|
* 2nd |------------|
* 3rd |------|
* 4th |---|
* 5th |---|
* 6th |----------|
* 7th |-------------------------------------|
*
* This is required for overlapping regions only. It simplifies adding
* regions with the loop in xlat_tables_init_internal because the outer
* ones won't overwrite block or page descriptors of regions added
* previously.
*
* Overlapping is only allowed for static regions.
*/
while (((mm_cursor->base_va + mm_cursor->size - 1U) < end_va)
&& (mm_cursor->size != 0U)) {
++mm_cursor;
}
while (((mm_cursor->base_va + mm_cursor->size - 1U) == end_va) &&
(mm_cursor->size != 0U) && (mm_cursor->size < mm->size)) {
++mm_cursor;
}
/*
* Find the last entry marker in the mmap
*/
mm_last = ctx->mmap;
while ((mm_last->size != 0U) && (mm_last < mm_end)) {
++mm_last;
}
/*
* Check if we have enough space in the memory mapping table.
* This shouldn't happen as we have checked in mmap_add_region_check
* that there is free space.
*/
assert(mm_last->size == 0U);
/* Make room for new region by moving other regions up by one place */
mm_destination = mm_cursor + 1;
(void)memmove(mm_destination, mm_cursor,
(uintptr_t)mm_last - (uintptr_t)mm_cursor);
/*
* Check we haven't lost the empty sentinel from the end of the array.
* This shouldn't happen as we have checked in mmap_add_region_check
* that there is free space.
*/
assert(mm_end->size == 0U);
*mm_cursor = *mm;
if (end_pa > ctx->max_pa)
ctx->max_pa = end_pa;
if (end_va > ctx->max_va)
ctx->max_va = end_va;
}
启用MMU有两个函数:setup_mmu_cfg
和enable_mmu_direct_el*
。定义了一个与MMU配置寄存器相对应的数组,每个元素都是64位:
/*
* MMU configuration register values for the active translation context. Used
* from the MMU assembly helpers.
*/
uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
setup_mmu_cfg
是将转换上下文中信息存储到上面的配置寄存器数组中。
void setup_mmu_cfg(uint64_t *params, unsigned int flags,
const uint64_t *base_table, unsigned long long max_pa,
uintptr_t max_va, int xlat_regime)
{
uint64_t mair, ttbr0, tcr;
uintptr_t virtual_addr_space_size;
/* Set attributes in the right indices of the MAIR. */
mair = MAIR_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX);
mair |= MAIR_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, ATTR_IWBWA_OWBWA_NTR_INDEX);
mair |= MAIR_ATTR_SET(ATTR_NON_CACHEABLE, ATTR_NON_CACHEABLE_INDEX);
/*
* Limit the input address ranges and memory region sizes translated
* using TTBR0 to the given virtual address space size.
*/
assert(max_va < ((uint64_t)UINTPTR_MAX));
virtual_addr_space_size = (uintptr_t)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,UINTPTR_MAX].
*/
int t0sz = 64 - __builtin_ctzll(virtual_addr_space_size);
tcr = (uint64_t)t0sz << TCR_T0SZ_SHIFT;
/*
* Set the cacheability and shareability attributes for memory
* associated with translation table walks.
*/
if ((flags & XLAT_TABLE_NC) != 0U) {
/* Inner & outer non-cacheable non-shareable. */
tcr |= TCR_SH_NON_SHAREABLE |
TCR_RGN_OUTER_NC | TCR_RGN_INNER_NC;
} else {
/* Inner & outer WBWA & shareable. */
tcr |= TCR_SH_INNER_SHAREABLE |
TCR_RGN_OUTER_WBA | TCR_RGN_INNER_WBA;
}
/*
* It is safer to restrict the max physical address accessible by the
* hardware as much as possible.
*/
unsigned long long tcr_ps_bits = tcr_physical_addr_size_bits(max_pa);
if (xlat_regime == EL1_EL0_REGIME) {
/*
* TCR_EL1.EPD1: Disable translation table walk for addresses
* that are translated using TTBR1_EL1.
*/
tcr |= TCR_EPD1_BIT | (tcr_ps_bits << TCR_EL1_IPS_SHIFT);
} else if (xlat_regime == EL2_REGIME) {
tcr |= TCR_EL2_RES1 | (tcr_ps_bits << TCR_EL2_PS_SHIFT);
} else {
assert(xlat_regime == EL3_REGIME);
tcr |= TCR_EL3_RES1 | (tcr_ps_bits << TCR_EL3_PS_SHIFT);
}
/* Set TTBR bits as well */
ttbr0 = (uint64_t) base_table;
if (is_armv8_2_ttcnp_present()) {
/* Enable CnP bit so as to share page tables with all PEs. */
ttbr0 |= TTBR_CNP_BIT;
}
params[MMU_CFG_MAIR] = mair;
params[MMU_CFG_TCR] = tcr;
params[MMU_CFG_TTBR0] = ttbr0;
}
首先是设置内存属性到MAIR寄存器,这里配置设备内存属性ATTR_DEVICE
为Device_nGnRE,即
聚合、不重排和提前写应答;普通内存属性为两种:ATTR_NON_CACHEABLE
即普通内存、高速缓存的回写策略为直写策略、内部无高速缓存,ATTR_IWBWA_OWBWA_NTR
即普通内存、内外均直写策略。
接着设置TCR_ELx寄存器,TCR_ELx 寄存器中的 TnSZ 字段控制虚拟地址空间的大小,其值为64 - 虚地址空间大小;RGN字段控制共享性和缓存性;PS/IPS控制PA或者IPA空间大小。
最后配置TTBR寄存器,存放了页表的基地址,这里使用了低位虚拟地址空间TTBR0。
enable_mmu_direct_elx
是一段汇编实现,将上面的数组配置到相应的MMU寄存器中,然后使能MMU,以enable_mmu_direct_el3
为例,实现如下。
func enable_mmu_direct_\()el\el
#if ENABLE_ASSERTIONS
_mrs x1, sctlr, \el
tst x1, #SCTLR_M_BIT
ASM_ASSERT(eq)
#endif
/* Invalidate all TLB entries */
tlbi_invalidate_all \el
mov x7, x0
adrp x0, mmu_cfg_params
add x0, x0, :lo12:mmu_cfg_params
/* MAIR */
ldr x1, [x0, #(MMU_CFG_MAIR << 3)]
_msr mair, \el, x1
/* TCR */
ldr x2, [x0, #(MMU_CFG_TCR << 3)]
_msr tcr, \el, x2
/* TTBR */
ldr x3, [x0, #(MMU_CFG_TTBR0 << 3)]
_msr ttbr0, \el, x3
/*
* 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
/* Set and clear required fields of SCTLR */
_mrs x4, sctlr, \el
mov_imm x5, SCTLR_WXN_BIT | SCTLR_C_BIT | SCTLR_M_BIT
orr x4, x4, x5
/* Additionally, amend SCTLR fields based on flags */
bic x5, x4, #SCTLR_C_BIT
tst x7, #DISABLE_DCACHE
csel x4, x5, x4, ne
_msr sctlr, \el, x4
isb
ret
endfunc enable_mmu_direct_\()el\el
以ARM FVP平台BL1阶段为例,并假设不支持RME。在arm_bl1_plat_arch_setup
函数中会创建MMU页表,并启用MMU。
void arm_bl1_plat_arch_setup(void)
{
...
const mmap_region_t bl_regions[] = {
MAP_BL1_TOTAL,
MAP_BL1_RO,
{0}
};
setup_page_tables(bl_regions, plat_arm_get_mmap());
enable_mmu_el3(0);
...
}
页表创建包括两种类型:通用内存区域和特定平台内存区域。
MAP_BL1_TOTAL
定义了BL1可见的所有可信RAM空间,属性为:普通内存,可读性,安全。其宏展开如下:
#define MAP_BL1_TOTAL MAP_REGION_FLAT( \
bl1_tzram_layout.total_base, \
bl1_tzram_layout.total_size, \
MT_MEMORY | MT_RW | EL3_PAS)
而MAP_BL1_RO
定义了BL1的代码段内存,属性为:代码(普通内存,只读,可执行),安全。
#define MAP_BL1_RO MAP_REGION_FLAT( \
BL_CODE_BASE, \
BL1_CODE_END - BL_CODE_BASE, \
MT_CODE | EL3_PAS)
对于不同的平台,也需要创建平台相关的页表(主要是外设),并映射内存区域。FVP平台BL1阶段的plat_arm_get_mmap()
定义为:
/*
* Table of memory regions for various BL stages to map using the MMU.
* This doesn't include Trusted SRAM as setup_page_tables() already takes care
* of mapping it.
*/
#ifdef IMAGE_BL1
const mmap_region_t plat_arm_mmap[] = {
ARM_MAP_SHARED_RAM,
V2M_MAP_FLASH0_RO,
V2M_MAP_IOFPGA,
MAP_DEVICE0,
#if FVP_INTERCONNECT_DRIVER == FVP_CCN
MAP_DEVICE1,
#endif
#if TRUSTED_BOARD_BOOT
/* To access the Root of Trust Public Key registers. */
MAP_DEVICE2,
/* Map DRAM to authenticate NS_BL2U image. */
ARM_MAP_NS_DRAM1,
#endif
{0}
};
#endif
对于ARM_MAP_SHARED_RAM
定义了共享内存区域,属性为:设备内存,读写,安全。其展开如下:
#define ARM_MAP_SHARED_RAM MAP_REGION_FLAT( \
ARM_SHARED_RAM_BASE, \
ARM_SHARED_RAM_SIZE, \
MT_DEVICE | MT_RW | EL3_PAS)
如果使能安全启动,MAP_DEVICE2
定义了访问设备根公钥寄存器内存区域,属性为:设备内存,读写,安全。其展开如下:
#define MAP_DEVICE2 MAP_REGION_FLAT(DEVICE2_BASE, \
DEVICE2_SIZE, \
MT_DEVICE | MT_RW | MT_SECURE)
setup_page_tables
函数用于创建页表,定义如下:
/*
* Set up the page tables for the generic and platform-specific memory regions.
* The size of the Trusted SRAM seen by the BL image must be specified as well
* as an array specifying the generic memory regions which can be;
* - Code section;
* - Read-only data section;
* - Init code section, if applicable
* - Coherent memory region, if applicable.
*/
void __init setup_page_tables(const mmap_region_t *bl_regions,
const mmap_region_t *plat_regions)
{
/*
* Map the Trusted SRAM with appropriate memory attributes.
* Subsequent mappings will adjust the attributes for specific regions.
*/
mmap_add(bl_regions);
/* Now (re-)map the platform-specific memory regions */
mmap_add(plat_regions);
/* Create the page tables to reflect the above mappings */
init_xlat_tables();
}
分别将BL区域和平台自定义区域添加到转换表中,然后创建页表,添加映射关系。
由于BL1处于EL3等级,调用enable_mmu_el3
函数,使能MMU。
void enable_mmu_el3(unsigned int flags)
{
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, flags,
tf_xlat_ctx.base_table, MAX_PHYS_ADDR,
tf_xlat_ctx.va_max_address, EL3_REGIME);
enable_mmu_direct_el3(flags);
}