更多内容请访问:鸿蒙技术社区https://harmonyos.51cto.com与华为官方共建2.虚拟映射初始化文件kernel/base/vm/los_vm_boot.c中的系统内存初始化函数OsSysMemInit()会调用虚实映射初始化函数OsInitMappingStartUp()。函数代码定义在文件arch/arm/arm/src/los_arch_mmu.c中,代码如下。(1)函数使TLB失效,清除虚实映射缓存数据,涉及到一些cp15寄存器和汇编,后面再分析。(2)函数切换到临时TTB。⑶设置内核地址空间的映射。下面详细介绍这些函数的代码。VOIDOsInitMappingStartUp(VOID){⑴OsArmInvalidateTlbBarrier();⑵OsSwitchTmpTTB();⑶OsSetKSectionAttr(KERNEL_VMM_BASE,FALSE);OsSetKSectionAttr(UNCACHED_VMM_BASE,TRUE);OsKSectionNewAttrEnable();}2.1函数OsSwitchTmpTTB函数OsSwitchTmpTTB申请16KiB的内存存放L1页表项数据,把页表项数据从g_firstPageTable复制到分配的内存区域。(1)获取内核地址空间。L1页表由4096个页表条目组成,每个4KiB,总共需要16KiB的大小。因此,(2)处的代码按照16KiB对齐申请了一块16KiB的内存区域来存放L1页表项。(3)设置内核虚拟内存地址空间的转换表基地址TTB。(4)将g_firstPageTable页表数据复制到内核地址空间的转换表区。如果复制失败,则直接使用g_firstPageTable。(5)设置内核虚拟地址空间TTB转换地址对应的物理内存地址,然后调用函数OsArmWriteTtbr0写入MMU寄存器。STATICVOIDOsSwitchTmpTTB(VOID){PTE_T*tmpTtbase=NULL;errno_terr;⑴LosVmSpace*kSpace=LOS_GetKVmSpace();/*ttbraddressshouldbe16KBytealign*/⑵tmpTtbase=LOS_MemAllocAlign(m_aucSysMem0,MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS,MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS);if(tmpTtbase==NULL){VM_ERR("memoryallocfailed");返回;}⑶kSpace->archMmu.virtTtb=tmpTtbase;⑷err=memcpy_s(kSpace->archMmu.virtTtb,MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS,g_firstPageTable,MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS);if(err!=EOK){(VOID)(VOID)kSpace->archMmu.virtTtb=(VADDR_T*)g_firstPageTable;VM_ERR("memcpyfailed,errno:%d",err);返回;}⑸kSpace->archMmu.physTtb=LOS_PaddrQuery(kSpace->archMmu.virtTtb);OsArmWriteTtbr0(kSpace->archMmu.physTtb|MMU_TTBRx_FLAGS);ISB;}2.2函数OsSetKSectionAttr内部函数OsSetKSectionAttr用于设置内核虚拟地址空间的section属性,分别为内核虚拟地址空间的kernelsection[KERNEL_ASPACE_BASE,KERNEL_ASPACE_BASE+KERNEL_ASPACE_SIZE]和未缓存部分[UNCACHED_VMM_BASE,UNCACHED_VMM_BASE+UNCACHED_VMM_SIZE]设置内核虚拟地址空间固定映射到物理内存。内核地址空间的映射包括代码段、数据段、栈区间映射,如下示意图:(1)计算相对内核虚拟地址空间基地址KERNEL_VMM_BASE的偏移量大小。(2)首先计算相对偏移值的text、rodata、data_bss段的虚拟内存地址,然后创建这些段的虚实映射数组mmuKernelMappings。(3)设置内核虚拟地址范围的虚拟翻译基地址TTB和物理翻译基地址TTB。然后释放虚拟地址virtAddr的虚实映射,释放映射的长度为代码段、只读数据段、数据BSS段的内存段长度。(4)根据指定的标签flags,对文本代码段之前的内存范围进行虚实映射。(5)映射text代码段、rodata只读数据段、data_bss数据段的内存区间,调用函数LOS_VmSpaceReserve保留进程空间中的地址范围。(6)是BSS段后面堆区和栈区的映射,将虚拟地址空间的内存栈区映射到对应的物理内存区。STATICVOIDOsSetKSectionAttr(UINTPTRvirtAddr,BOOLuncached){⑴UINT32offset=virtAddr-KERNEL_VMM_BASE;/*everysectionshouldbepagealigned*/⑵UINTPTRtextStart=(UINTPTR)&__text_start+offset;UINTPTRtextEnd=(UINTPTR)&__text_end+offset;UINTPTRrodataStart=(UINTPTR)&__rodata_start+offset;UINTPTRrodataEnd=(UINTPTR)&__rodata_end+offset;UINTPTRramDataStart=(UINTPTR)&__ram_data_start+offset;UINTPTRbssEnd=(UINTPTR)&__bss_end+offset;UINT32bssEndBoundary=ROUNDUP(bssEnd,MB);LosArchMmuInitMappingmmuKernelMappings[]={{.phys=SYS_MEM_virttext=textStart,.,.size=ROUNDUP(textEnd-textStart,MMU_DESCRIPTOR_L2_SMALL_SIZE),.flags=VM_MAP_REGION_FLAG_PERM_READ|VM_MAP_REGION_FLAG_PERM_EXECUTE,.name="kernel_text"},{.phys=SYS_MEM_BASE+rodataStart-virtAddr,.virt=rodataStart,.size=ROUNDUP(rodataStart,.size=ROUNDUP(rorodataStart,MMU_DESCRIPTOR_L2_SMALL_SIZE),.flags=VM_MAP_REGION_FLAG_PERM_READ,.name="kernel_rodata"},{.phys=SYS_MEM_BASE+ramDataStart-virtAddr,.virt=ramDataStart,.size=ROUNDUP(bssEndBoundary-ramDataStart,MMU_DESCRIPTOR_L2_SMALL_SIZE),.flags=VM_MAP_REGION_FLAG_PERM_READ|VM_MAP_REGION_FLAG_PERM_WRITE,.name="kernel_data_bss"}};LosVmSpace*kSpace=LOS_GetKVmSpace();status_tstatus;UINT32length;inti;LosArchMmuInitMapping*kernelMap=NULL;UINT32kmallocLength;UINT32flags;/*使用second-levelmappingofdefaultREA??DandWRITE*/⑶kSpace->archMmu.virtTtb=(PTE_T*)g_firstPageTable;kSpace->archMmu.physTtb=LOS_PaddrQuery(kSpace->archMmu.stat=T)LOS_ArchMmuUnmap(&kSpace->archMmu,virtAddr,(bssEndBoundary-virtAddr)>>MMU_DESCRIPTOR_L2_SMALL_SHIFT);if(status!=((bssEndBoundary-virtAddr)>>MMU_DESCRIPTOR_L2_SMALL_SHIFT)){VM_ERR("unmapfailed,status:%d",status);return;}flags=VM_MAP_REGION_FLAG_PERM_READ|VM_MAP_REGION_FLAG_PERM_WRITE|VM_MAP_REGION_FLAG_PERM_EXECUTE;if(uncached){flags|=VM_MAP_REGION_FLAG_UNCACHED;}⑷status=LOS_ArchMmuMap(&kSpace->archMmu,virtAddr,SYS_MEM_BASE,(textStart-virtAddr)>>MMU_DESCRIPTOR_L2_SMALL_SHIFT,flags);if(status!=((textStart-virtAddr)>>MMU_DESCRIPTOR_L2_SMALL_SHIFT)){VM_ERR("mmapfailed,status:%d",status);return;}⑸length=sizeof(mmuKernelMappings)/sizeof(LosArchMmuInitMapping);for(i=0;i
