当前位置: 首页 > 科技观察

鸿蒙LightKernelA核心源码解析系列之虚拟现实映射(二)虚拟现实映射初始化

时间:2023-03-20 02:00:19 科技观察

更多内容请访问:鸿蒙技术社区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;iflags|=VM_MAP_REGION_FLAG_UNCACHED;}status=LOS_ArchMmuMap(&kSpace->archMmu,kernelMap->virt,kernelMap->phys,kernelMap->size>>MMU_DESCRIPTOR_L2_SMALL_SHIFT,kernelMap->flags);if(status!=(kernelMap->size>>MMU_DESCRIPTOR_L2_SMALL_SHIFT)){VM_ERR("mmapfailed,status:%d",status);return;}LOS_VmSpaceReserve(kSpace,kernelMap->size,kernelMap->virt);}⑹kmallocLength=virtAddr+SYS_MEM_SIZE_DEFAULT-bssEndBoundary;flags=VM_MAP_REGION_FLAG_PERM_READ|VM_MAP_REGION_FLAG_PERM_READ|VM_MAP_REGION_FLAG_PERM_WRITE);{ifg_WRITE)|=VM_MAP_REGION_FLAG_UNCACHED;}status=LOS_ArchMmuMap(&kSpace->archMmu,bssEndBoundary,SYS_MEM_BASE+bssEndBoundary-virtAddr,kmallocLength>>MMU_DESCRIPTOR_L2_SMALL_SHIFT,flags);if(状态!=(kmallocLength>>MMU_DESCRIPTOR_L2_SMALL_SHIFT)){VM_ERR("mmapfailed",servdren,status;%);(kSpace,kmallocLength,bssEndBoundary);}2.3FunctionOsKSectionNewAttrEnableFunctionOsKSectionNewAttrEnable设置虚实地址转换表基址TTB并清空TLB缓存(1)获取内核虚拟进程空间,(2)设置进程空间MMUTTB的虚拟地址转换表的基地址,然后查询物理内存地址,设置物理内存地址转换表的基地址。(3)从CP15C2寄存器读取TTB地址,取高20位。(4)将内核物理内存页表的基地址写入CP15c2TTB寄存器。(5)在⑸处清空TLB缓冲区,然后释放内存。涉及到MMU寄存器,后续系列会详细讲解。STATICVOIDOsKSectionNewAttrEnable(VOID){⑴LosVmSpace*kSpace=LOS_GetKVmSpace();paddr_toldTtPhyBase;⑵kSpace->archMmu.virtTtb=(PTE_T*)g_firstPageTable;kSpace->archMmu.physTtb=LOS_PaddrQuery/kmuetbwenetbvirtM);⑶oldTtPhyBase=OsArmReadTtbr0();oldTtPhyBase=oldTtPhyBase&MMU_DESCRIPTOR_L2_SMALL_FRAME;⑷OsArmWriteTtbr0(kSpace->archMmu.physTtb|MMU_TTBRx_FLAGS);ISB;/*wechangedpagetableentry,soweneedtocleanTLBhere*/⑸OsCleanTLB();(VOID)LOS_MemFree(m_aucSysMem0,(VOID*)(UINTPTR)(oldTtPhyBase-SYS_MEM_BASE+KERNEL_VMM_BASE));}更多内容请访问:Harmonyos技术社区https://harmonyos.51cto.com与华为官方共建