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

2024-06-13 12:20

本文主要是介绍鸿蒙轻内核A核源码分析系列五 虚实映射(2)虚实映射初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2、 虚拟映射初始化

在文件kernel/base/vm/los_vm_boot.c中的系统内存初始化函数OsSysMemInit()会调用虚实映射初始化函数OsInitMappingStartUp()。该函数代码定义在文件arch/arm/arm/src/los_arch_mmu.c,代码如下。⑴处函数使TLB失效,清理虚实映射缓存数据,涉及些cp15寄存器和汇编,后续再分析。⑵处函数切换到临时TTB。⑶处设置内核地址空间的映射。下面分别详细这些函数代码。

VOID OsInitMappingStartUp(VOID)
{
⑴   OsArmInvalidateTlbBarrier();⑵   OsSwitchTmpTTB();⑶  OsSetKSectionAttr(KERNEL_VMM_BASE, FALSE);OsSetKSectionAttr(UNCACHED_VMM_BASE, TRUE);OsKSectionNewAttrEnable();
}

2.1 函数OsSwitchTmpTTB

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

STATIC VOID OsSwitchTmpTTB(VOID)
{PTE_T *tmpTtbase = NULL;errno_t err;
⑴   LosVmSpace *kSpace = LOS_GetKVmSpace();/* ttbr address should be 16KByte align */
⑵   tmpTtbase = LOS_MemAllocAlign(m_aucSysMem0, MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS,MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS);if (tmpTtbase == NULL) {VM_ERR("memory alloc failed");return;}⑶  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)LOS_MemFree(m_aucSysMem0, tmpTtbase);kSpace->archMmu.virtTtb = (VADDR_T *)g_firstPageTable;VM_ERR("memcpy failed, errno: %d", err);return;}
⑸  kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb);OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS);ISB;
}

2.2 函数OsSetKSectionAttr

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

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

STATIC VOID OsSetKSectionAttr(UINTPTR virtAddr, BOOL uncached)
{
⑴  UINT32 offset = virtAddr - KERNEL_VMM_BASE;/* every section should be page aligned */
⑵  UINTPTR textStart = (UINTPTR)&__text_start + offset;UINTPTR textEnd = (UINTPTR)&__text_end + offset;UINTPTR rodataStart = (UINTPTR)&__rodata_start + offset;UINTPTR rodataEnd = (UINTPTR)&__rodata_end + offset;UINTPTR ramDataStart = (UINTPTR)&__ram_data_start + offset;UINTPTR bssEnd = (UINTPTR)&__bss_end + offset;UINT32 bssEndBoundary = ROUNDUP(bssEnd, MB);LosArchMmuInitMapping mmuKernelMappings[] = {{.phys = SYS_MEM_BASE + textStart - virtAddr,.virt = 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(rodataEnd - rodataStart, 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_t status;UINT32 length;int i;LosArchMmuInitMapping *kernelMap = NULL;UINT32 kmallocLength;UINT32 flags;/* use second-level mapping of default READ and WRITE */
⑶  kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable;kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb);status = LOS_ArchMmuUnmap(&kSpace->archMmu, virtAddr,(bssEndBoundary - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT);if (status != ((bssEndBoundary - virtAddr) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {VM_ERR("unmap failed, 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("mmap failed, status: %d", status);return;}⑸  length = sizeof(mmuKernelMappings) / sizeof(LosArchMmuInitMapping);for (i = 0; i < length; i++) {kernelMap = &mmuKernelMappings[i];if (uncached) {kernelMap->flags |= 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("mmap failed, 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_WRITE;if (uncached) {flags |= VM_MAP_REGION_FLAG_UNCACHED;}status = LOS_ArchMmuMap(&kSpace->archMmu, bssEndBoundary,SYS_MEM_BASE + bssEndBoundary - virtAddr,kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT,flags);if (status != (kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {VM_ERR("mmap failed, status: %d", status);return;}LOS_VmSpaceReserve(kSpace, kmallocLength, bssEndBoundary);
}

2.3 函数OsKSectionNewAttrEnable

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

STATIC VOID OsKSectionNewAttrEnable(VOID)
{
⑴  LosVmSpace *kSpace = LOS_GetKVmSpace();paddr_t oldTtPhyBase;⑵  kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable;kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb);/* we need free tmp ttbase */
⑶  oldTtPhyBase = OsArmReadTtbr0();oldTtPhyBase = oldTtPhyBase & MMU_DESCRIPTOR_L2_SMALL_FRAME;
⑷  OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS);ISB;/* we changed page table entry, so we need to clean TLB here */
⑸  OsCleanTLB();(VOID)LOS_MemFree(m_aucSysMem0, (VOID *)(UINTPTR)(oldTtPhyBase - SYS_MEM_BASE + KERNEL_VMM_BASE));
}

这篇关于鸿蒙轻内核A核源码分析系列五 虚实映射(2)虚实映射初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057290

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL