鸿蒙轻内核A核源码分析系列之虚实映射(2)虚实映射初始化

开发 前端
虚实映射其实就是一个建立页表的过程。MMU支持多级页表,LiteOS-A内核采用二级页表描述进程空间。首先介绍下一级页表和二级页表。

[[438168]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

2、 虚拟映射初始化

在文件kernel/base/vm/los_vm_boot.c中的系统内存初始化函数OsSysMemInit()会调用虚实映射初始化函数OsInitMappingStartUp()。该函数代码定义在文件arch/arm/arm/src/los_arch_mmu.c,代码如下。

⑴处函数使TLB失效,清理虚实映射缓存数据,涉及些cp15寄存器和汇编,后续再分析。

⑵处函数切换到临时TTB。

⑶处设置内核地址空间的映射。下面分别详细这些函数代码。

  1. VOID OsInitMappingStartUp(VOID) 
  2. ⑴   OsArmInvalidateTlbBarrier(); 
  3.  
  4. ⑵   OsSwitchTmpTTB(); 
  5.  
  6. ⑶  OsSetKSectionAttr(KERNEL_VMM_BASE, FALSE); 
  7.     OsSetKSectionAttr(UNCACHED_VMM_BASE, TRUE); 
  8.     OsKSectionNewAttrEnable(); 

2.1 函数OsSwitchTmpTTB

函数OsSwitchTmpTTB申请16KiB的内存存放L1页表项数据,把页表项数据从g_firstPageTable复制到申请的内存区域。⑴处获取内核地址空间。L1页表由4096个页表项组成,每个4KiB,共需要16KiB大小。所以⑵处代码按16KiB对齐申请16KiB大小的内存区域存放L1页表项。⑶处设置内核虚拟内存地址空间的转换表基地址TTB。⑷处把g_firstPageTable页表数据复制到内核地址空间的转换表区域。如果复制失败,则直接使用g_firstPageTable。⑸处设置内核虚拟地址空间的TTB转换地址对应的物理内存地址,然后调用函数OsArmWriteTtbr0写入MMU寄存器。

  1. STATIC VOID OsSwitchTmpTTB(VOID) 
  2.     PTE_T *tmpTtbase = NULL
  3.     errno_t err; 
  4. ⑴   LosVmSpace *kSpace = LOS_GetKVmSpace(); 
  5.  
  6.     /* ttbr address should be 16KByte align */ 
  7. ⑵   tmpTtbase = LOS_MemAllocAlign(m_aucSysMem0, MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS, 
  8.                                   MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS); 
  9.     if (tmpTtbase == NULL) { 
  10.         VM_ERR("memory alloc failed"); 
  11.         return
  12.     } 
  13.  
  14. ⑶  kSpace->archMmu.virtTtb = tmpTtbase; 
  15. ⑷  err = memcpy_s(kSpace->archMmu.virtTtb, MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS, 
  16.                    g_firstPageTable, MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS); 
  17.     if (err != EOK) { 
  18.         (VOID)LOS_MemFree(m_aucSysMem0, tmpTtbase); 
  19.         kSpace->archMmu.virtTtb = (VADDR_T *)g_firstPageTable; 
  20.         VM_ERR("memcpy failed, errno: %d", err); 
  21.         return
  22.     } 
  23. ⑸  kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb); 
  24.     OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS); 
  25.     ISB; 

2.2 函数OsSetKSectionAttr

内部函数OsSetKSectionAttr用于设置内核虚拟地址空间的区间属性,分别针对内核虚拟地址空间的内核区间[KERNEL_ASPACE_BASE,KERNEL_ASPACE_BASE+KERNEL_ASPACE_SIZE]和未缓存区间[UNCACHED_VMM_BASE,UNCACHED_VMM_BASE+UNCACHED_VMM_SIZE]进行设置。内核虚拟地址空间是固定映射到物理内存的,内核地址空间的映射包含代码段、数据段、堆栈区间映射,如下示意图所示:

鸿蒙轻内核A核源码分析系列五 虚实映射(2)虚实映射初始化-鸿蒙HarmonyOS技术社区

⑴处计算相对内核虚拟地址空间基地址KERNEL_VMM_BASE的偏移大小。⑵处先计算相对偏移值的text、rodata、data_bss段的虚拟内存地址,然后创建这些段的虚实映射关系数组mmuKernelMappings。⑶处设置内核虚拟地址区间的虚拟转换基地址TTB和物理转换基地址TTB。然后解除虚拟地址virtAddr的虚实映射,解除映射的长度就是代码段、只读数据段、数据BSS段这些内存段的长度。⑷处按指定的标签flags对text代码段之前的内存区间进行虚实映射。⑸处映射text代码段、rodata只读数据段、data_bss数据段的内存区间,并调用函数LOS_VmSpaceReserve在进程空间中预定地址区间。⑹是BSS段后面的heap区、stack区的映射,映射虚拟地址空间的内存堆栈区间到对应的物理内存区间。

  1. STATIC VOID OsSetKSectionAttr(UINTPTR virtAddr, BOOL uncached) 
  2. ⑴  UINT32 offset = virtAddr - KERNEL_VMM_BASE; 
  3.     /* every section should be page aligned */ 
  4. ⑵  UINTPTR textStart = (UINTPTR)&__text_start + offset; 
  5.     UINTPTR textEnd = (UINTPTR)&__text_end + offset; 
  6.     UINTPTR rodataStart = (UINTPTR)&__rodata_start + offset; 
  7.     UINTPTR rodataEnd = (UINTPTR)&__rodata_end + offset; 
  8.     UINTPTR ramDataStart = (UINTPTR)&__ram_data_start + offset; 
  9.     UINTPTR bssEnd = (UINTPTR)&__bss_end + offset; 
  10.     UINT32 bssEndBoundary = ROUNDUP(bssEnd, MB); 
  11.     LosArchMmuInitMapping mmuKernelMappings[] = { 
  12.         { 
  13.             .phys = SYS_MEM_BASE + textStart - virtAddr, 
  14.             .virt = textStart, 
  15.             .size = ROUNDUP(textEnd - textStart, MMU_DESCRIPTOR_L2_SMALL_SIZE), 
  16.             .flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE, 
  17.             .name = "kernel_text" 
  18.         }, 
  19.         { 
  20.             .phys = SYS_MEM_BASE + rodataStart - virtAddr, 
  21.             .virt = rodataStart, 
  22.             .size = ROUNDUP(rodataEnd - rodataStart, MMU_DESCRIPTOR_L2_SMALL_SIZE), 
  23.             .flags = VM_MAP_REGION_FLAG_PERM_READ, 
  24.             .name = "kernel_rodata" 
  25.         }, 
  26.         { 
  27.             .phys = SYS_MEM_BASE + ramDataStart - virtAddr, 
  28.             .virt = ramDataStart, 
  29.             .size = ROUNDUP(bssEndBoundary - ramDataStart, MMU_DESCRIPTOR_L2_SMALL_SIZE), 
  30.             .flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE, 
  31.             .name = "kernel_data_bss" 
  32.         } 
  33.     }; 
  34.     LosVmSpace *kSpace = LOS_GetKVmSpace(); 
  35.     status_t status; 
  36.     UINT32 length; 
  37.     int i; 
  38.     LosArchMmuInitMapping *kernelMap = NULL
  39.     UINT32 kmallocLength; 
  40.     UINT32 flags; 
  41.  
  42.     /* use second-level mapping of default READ and WRITE */ 
  43. ⑶  kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable; 
  44.     kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb); 
  45.     status = LOS_ArchMmuUnmap(&kSpace->archMmu, virtAddr, 
  46.                               (bssEndBoundary - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT); 
  47.     if (status != ((bssEndBoundary - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) { 
  48.         VM_ERR("unmap failed, status: %d", status); 
  49.         return
  50.     } 
  51.  
  52.     flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE | VM_MAP_REGION_FLAG_PERM_EXECUTE; 
  53.     if (uncached) { 
  54.         flags |= VM_MAP_REGION_FLAG_UNCACHED; 
  55.     } 
  56. ⑷  status = LOS_ArchMmuMap(&kSpace->archMmu, virtAddr, SYS_MEM_BASE, 
  57.                             (textStart - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, 
  58.                             flags); 
  59.     if (status != ((textStart - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) { 
  60.         VM_ERR("mmap failed, status: %d", status); 
  61.         return
  62.     } 
  63.  
  64. ⑸  length = sizeof(mmuKernelMappings) / sizeof(LosArchMmuInitMapping); 
  65.     for (i = 0; i < length; i++) { 
  66.         kernelMap = &mmuKernelMappings[i]; 
  67.         if (uncached) { 
  68.             kernelMap->flags |= VM_MAP_REGION_FLAG_UNCACHED; 
  69.         } 
  70.         status = LOS_ArchMmuMap(&kSpace->archMmu, kernelMap->virt, kernelMap->phys, 
  71.                                  kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, kernelMap->flags); 
  72.         if (status != (kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) { 
  73.             VM_ERR("mmap failed, status: %d", status); 
  74.             return
  75.         } 
  76.         LOS_VmSpaceReserve(kSpace, kernelMap->size, kernelMap->virt); 
  77.     } 
  78.  
  79. ⑹   kmallocLength = virtAddr + SYS_MEM_SIZE_DEFAULT - bssEndBoundary; 
  80.     flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE; 
  81.     if (uncached) { 
  82.         flags |= VM_MAP_REGION_FLAG_UNCACHED; 
  83.     } 
  84.     status = LOS_ArchMmuMap(&kSpace->archMmu, bssEndBoundary, 
  85.                             SYS_MEM_BASE + bssEndBoundary - virtAddr, 
  86.                             kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, 
  87.                             flags); 
  88.     if (status != (kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) { 
  89.         VM_ERR("mmap failed, status: %d", status); 
  90.         return
  91.     } 
  92.     LOS_VmSpaceReserve(kSpace, kmallocLength, bssEndBoundary); 

2.3 函数OsKSectionNewAttrEnable

函数OsKSectionNewAttrEnable设置虚实地址的转换表基地址TTB并清楚TLB缓存。⑴处获取内核虚拟进程空间,⑵处设置进程空间MMU的虚拟地址转换表基地址TTB,然后查询到物理内存地址并设置物理内存地址转换表基地址。⑶处从CP15 C2寄存器读取TTB地址,取高20位。⑷处将内核物理内存页表基地址写入CP15 c2 TTB寄存器。⑸处清空TLB缓冲区,然后释放内存。涉及到了MMU寄存器,后续系列会专门详细讲解。

  1. STATIC VOID OsKSectionNewAttrEnable(VOID) 
  2. ⑴  LosVmSpace *kSpace = LOS_GetKVmSpace(); 
  3.     paddr_t oldTtPhyBase; 
  4.  
  5. ⑵  kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable; 
  6.     kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb); 
  7.  
  8.     /* we need free tmp ttbase */ 
  9. ⑶  oldTtPhyBase = OsArmReadTtbr0(); 
  10.     oldTtPhyBase = oldTtPhyBase & MMU_DESCRIPTOR_L2_SMALL_FRAME; 
  11. ⑷  OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS); 
  12.     ISB; 
  13.  
  14.     /* we changed page table entry, so we need to clean TLB here */ 
  15. ⑸  OsCleanTLB(); 
  16.  
  17.     (VOID)LOS_MemFree(m_aucSysMem0, (VOID *)(UINTPTR)(oldTtPhyBase - SYS_MEM_BASE + KERNEL_VMM_BASE)); 

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-12-03 16:22:05

鸿蒙HarmonyOS应用

2021-12-03 16:20:26

鸿蒙HarmonyOS应用

2021-12-01 15:59:22

鸿蒙HarmonyOS应用

2022-03-11 20:23:14

鸿蒙源码分析进程管理

2022-01-12 10:50:23

鸿蒙HarmonyOS应用

2022-01-10 15:31:44

鸿蒙HarmonyOS应用

2022-04-13 11:02:12

鸿蒙事件模块事件Event

2022-03-03 18:28:28

Harmony进程任务管理模块

2021-06-04 09:57:49

鸿蒙HarmonyOS应用

2022-04-13 11:12:43

鸿蒙轻内核信号量模块操作系统

2021-10-20 16:08:57

鸿蒙HarmonyOS应用

2021-05-25 09:28:34

鸿蒙HarmonyOS应用

2021-11-05 15:00:33

鸿蒙HarmonyOS应用

2021-11-08 15:06:15

鸿蒙HarmonyOS应用

2021-06-04 14:15:10

鸿蒙HarmonyOS应用

2021-05-08 15:14:50

鸿蒙HarmonyOS应用

2021-05-17 09:28:59

鸿蒙HarmonyOS应用

2022-01-14 08:39:47

鸿蒙HarmonyOS应用

2021-05-31 20:30:55

鸿蒙HarmonyOS应用

2021-06-17 09:36:07

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号