optee内存管理和页表建立

文章目录

        • 1、armv8的页表定义
        • 2、rodata section指向内存的结构体
        • 3、内存的分类和注册
        • 3、内存的属性(type)
        • 4、页表的构建

1、armv8的页表定义

透过事务看本质,页表里都有什么? 如下图描述了页表中的一个entry信息,而我们软件做的工作,其实就是针对每一个页面的管理,去填充一个entry, 一个entry对应一个页面, entry中包含页面地址和内存的一些属性。把所有的entry放到一起,就构成了页表www
optee内存管理和页表建立_第1张图片

2、rodata section指向内存的结构体

rodata section的地址段

		*(.rodata .rodata.*)

		/*
		 * 8 to avoid unwanted padding between __start_ta_head_section
		 * and the first structure in ta_head_section, in 64-bit
		 * builds
		 */
		. = ALIGN(8);
		__start_ta_head_section = . ;
		KEEP(*(ta_head_section))
		__stop_ta_head_section = . ;
		. = ALIGN(8);
		__start_phys_mem_map_section = . ;
		KEEP(*(phys_mem_map_section))
		__end_phys_mem_map_section = . ;
		. = ALIGN(8);
		__start_phys_sdp_mem_section = . ;
		KEEP(*(phys_sdp_mem_section))
		__end_phys_sdp_mem_section = . ;
		. = ALIGN(8);
		__start_phys_nsec_ddr_section = . ;
		KEEP(*(phys_nsec_ddr_section))
		__end_phys_nsec_ddr_section = . ;
		. = ALIGN(8);
		__start_phys_ddr_overall_section = . ;
		KEEP(*(phys_ddr_overall_section))
		__end_phys_ddr_overall_section = . ;

可以看出在rodata中定义了如下section:

  • ta_head_section
  • phys_mem_map_section
  • phys_sdp_mem_section
  • phys_nsec_ddr_section
  • phys_ddr_overall_section

指向内存的结构体:

struct core_mmu_phys_mem {
     
	const char *name;
	enum teecore_memtypes type;
	__extension__ union {
     
		paddr_t addr;
	};
	__extension__ union {
     
		paddr_size_t size;
	};
};

注:在section段落中的,每一个core_mmu_phys_mem数据类型,都代表这一块region.

3、内存的分类和注册

在optee中注册内存的宏有:

  • register_phys_mem 将注册的内存地址添加到phys_mem_map_section段
  • register_sdp_mem 将注册的内存地址添加到phys_sdp_mem_section段, 注意:sdp 应该是 secure device peripheral的意思
  • register_dynamic_shm 将注册的内存地址添加到phys_nsec_ddr_section段
  • register_ddr 将注册的内存地址添加到phys_ddr_overall_section段

以为register_phys_mem为例,该宏就是在.rodata的__start_phys_mem_map_section的section段定义一个struct core_mmu_phys_mem结构体类型的数据中,指向注册的这块mem的物理地址.

#define __register_memory2(_name, _type, _addr, _size, _section, _id) \
	static const struct core_mmu_phys_mem __phys_mem_ ## _id \
		__used __section(_section) = \
		{
        .name = _name, .type = _type, .addr = _addr, .size = _size }

#define __register_memory2_ul(_name, _type, _addr, _size, _section, _id) \
		__register_memory2(_name, _type, _addr, _size, _section, _id)
#endif

#define __register_memory1(name, type, addr, size, section, id) \
		__register_memory2(name, type, addr, size, #section, id)

#define __register_memory1_ul(name, type, addr, size, section, id) \
		__register_memory2_ul(name, type, addr, size, #section, id)

#define register_phys_mem(type, addr, size) \
		__register_memory1(#addr, (type), (addr), (size), \
				   phys_mem_map_section, __COUNTER__)

在core/arch/arm/plat_xxx/main.c中,注册这些内存:
可以注册多块物理内存:

register_phys_mem(xxx0_TYPE, xxx0_PA_BASE, xxx_SIZE);
register_phys_mem(xxx1_TYPE, xxx1_PA_BASE, xxx1_SIZE);

可以注册多个IO设备:

register_sdp_mem(DRM_VPU1_BASE, DRM_VPU1_BASE);
register_sdp_mem(DRM_VPU2_BASE, DRM_VPU2_BASE);

3、内存的属性(type)

在注册内存的时候,其实就是将包含name、type、addr、size四个元素的结构体,填写到了section段落,而type对应的就是内存的属性,对应着teecore_memtypes类型:

在optee中,有如下十几种type

enum teecore_memtypes {
     
	MEM_AREA_END = 0,
	MEM_AREA_TEE_RAM,
	MEM_AREA_TEE_RAM_RX,
	MEM_AREA_TEE_RAM_RO,
	MEM_AREA_TEE_RAM_RW,
	MEM_AREA_TEE_COHERENT,
	MEM_AREA_TEE_ASAN,
	MEM_AREA_TA_RAM,
	MEM_AREA_NSEC_SHM,
	MEM_AREA_RAM_NSEC,
	MEM_AREA_RAM_SEC,
	MEM_AREA_IO_NSEC,
	MEM_AREA_IO_SEC,
	MEM_AREA_RES_VASPACE,
	MEM_AREA_SHM_VASPACE,
	MEM_AREA_TA_VASPACE,
	MEM_AREA_PAGER_VASPACE,
	MEM_AREA_SDP_MEM,
	MEM_AREA_DDR_OVERALL,
	MEM_AREA_MAXTYPE
};

4、页表的构建

在optee中,定义一个region的静态数组,CFG_MMAP_REGIONS=127, 可以存放127块region

static struct tee_mmap_region
	static_memory_map[CFG_MMAP_REGIONS + 1];

struct tee_mmap_region {
     
	unsigned int type; /* enum teecore_memtypes */
	unsigned int region_size;
	paddr_t pa;
	vaddr_t va;
	size_t size;
	uint32_t attr; /* TEE_MATTR_* above */
};

在构建页表之前,先调用init_mem_map函数,将内存中已注册了的region读取到static_memory_map这个静态数组中

init_mem_map(static_memory_map, ARRAY_SIZE(static_memory_map));

init_mem_map()函数原型

在init_mem_map读取section region到static_memory_map的过程中,还会调用core_mmu_type_to_attr函数,将注册时写入的内存属性(type)转换位memory attribute
这里返回的memory attribute是一个32bit的值,而armv8要求的是64bit的。

uint32_t core_mmu_type_to_attr(enum teecore_memtypes t)
{
     
	const uint32_t attr = TEE_MATTR_VALID_BLOCK;
	const uint32_t cached = TEE_MATTR_CACHE_CACHED << TEE_MATTR_CACHE_SHIFT;
	const uint32_t noncache = TEE_MATTR_CACHE_NONCACHE <<
				  TEE_MATTR_CACHE_SHIFT;

	switch (t) {
     
	case MEM_AREA_TEE_RAM:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRWX | cached;
	case MEM_AREA_TEE_RAM_RX:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRX | cached;
	case MEM_AREA_TEE_RAM_RO:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PR | cached;
	case MEM_AREA_TEE_RAM_RW:
	case MEM_AREA_TEE_ASAN:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRW | cached;
	case MEM_AREA_TEE_COHERENT:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRWX | noncache;
	case MEM_AREA_TA_RAM:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRW | cached;
	case MEM_AREA_NSEC_SHM:
		return attr | TEE_MATTR_PRW | cached;
	case MEM_AREA_IO_NSEC:
		return attr | TEE_MATTR_PRW | noncache;
	case MEM_AREA_IO_SEC:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRW | noncache;
	case MEM_AREA_RAM_NSEC:
		return attr | TEE_MATTR_PRW | cached;
	case MEM_AREA_RAM_SEC:
		return attr | TEE_MATTR_SECURE | TEE_MATTR_PRW | cached;
	case MEM_AREA_RES_VASPACE:
	case MEM_AREA_SHM_VASPACE:
		return 0;
	case MEM_AREA_PAGER_VASPACE:
		return TEE_MATTR_SECURE;
	default:
		panic("invalid type");
	}
}

在创建页表的时候会将32位的内存属性,转换成64的内存属性之后,再填充到页表的entry中
core_init_mmu_tables()—>core_mmu_map_region()—>core_mmu_set_entry()—>core_mmu_set_entry_primitive()

void core_mmu_set_entry_primitive(void *table, size_t level, size_t idx,
				  paddr_t pa, uint32_t attr)
{
     
	uint64_t *tbl = table;
	uint64_t desc = mattr_to_desc(level, attr);

	tbl[idx] = desc | pa;
}

static uint64_t mattr_to_desc(unsigned level, uint32_t attr)
{
     
	uint64_t desc;
	uint32_t a = attr;

	if (a & TEE_MATTR_HIDDEN_BLOCK)
		return INVALID_DESC | HIDDEN_DESC;

	if (a & TEE_MATTR_HIDDEN_DIRTY_BLOCK)
		return INVALID_DESC | HIDDEN_DIRTY_DESC;

	if (a & TEE_MATTR_TABLE)
		return TABLE_DESC;

	if (!(a & TEE_MATTR_VALID_BLOCK))
		return 0;

	if (a & (TEE_MATTR_PX | TEE_MATTR_PW))
		a |= TEE_MATTR_PR;
	if (a & (TEE_MATTR_UX | TEE_MATTR_UW))
		a |= TEE_MATTR_UR;
	if (a & TEE_MATTR_UR)
		a |= TEE_MATTR_PR;
	if (a & TEE_MATTR_UW)
		a |= TEE_MATTR_PW;

	if (level == 3)
		desc = L3_BLOCK_DESC;
	else
		desc = BLOCK_DESC;

	if (!(a & (TEE_MATTR_PX | TEE_MATTR_UX)))
		desc |= UPPER_ATTRS(XN);
	if (!(a & TEE_MATTR_PX))
		desc |= UPPER_ATTRS(PXN);

	if (a & TEE_MATTR_UR)
		desc |= LOWER_ATTRS(AP_UNPRIV);

	if (!(a & TEE_MATTR_PW))
		desc |= LOWER_ATTRS(AP_RO);

	/* Keep in sync with core_mmu.c:core_mmu_mattr_is_ok */
	switch ((a >> TEE_MATTR_CACHE_SHIFT) & TEE_MATTR_CACHE_MASK) {
     
	case TEE_MATTR_CACHE_NONCACHE:
		desc |= LOWER_ATTRS(ATTR_DEVICE_INDEX | OSH);
		break;
	case TEE_MATTR_CACHE_CACHED:
		desc |= LOWER_ATTRS(ATTR_IWBWA_OWBWA_NTR_INDEX | ISH);
		break;
	default:
		/*
		 * "Can't happen" the attribute is supposed to be checked
		 * with core_mmu_mattr_is_ok() before.
		 */
		panic();
	}

	if (a & (TEE_MATTR_UR | TEE_MATTR_PR))
		desc |= LOWER_ATTRS(ACCESS_FLAG);

	if (!(a & TEE_MATTR_GLOBAL))
		desc |= LOWER_ATTRS(NON_GLOBAL);

	desc |= a & TEE_MATTR_SECURE ? 0 : LOWER_ATTRS(NS);

	return desc;
}

下图是一个页表创建的过程:
optee内存管理和页表建立_第2张图片

你可能感兴趣的:(optee)