鸿蒙轻内核A核源码分析系列五 虚实映射(3)虚拟物理内存映射

3、虚实映射函数LOS_ArchMmuMap

从上文可知,用户程序加载启动时,会将代码段、数据段映射进虚拟内存空间,此时并没有物理页做实际的映射;程序执行时,如下图(图片来自OpenHarmony docs开源站点)粗箭头所示,CPU访问虚拟地址,通过MMU查找是否有对应的物理内存,若该虚拟地址无对应的物理地址则触发缺页异常,内核申请物理内存并将虚实映射关系及对应的属性配置信息写进页表,并把页表条目缓存至TLB,接着CPU可直接通过转换关系访问实际的物理内存;若CPU访问已缓存至TLB的页表条目,无需再访问保存在内存中的页表,可加快查找速度。本小节我们就详细分析下虚实映射函数的实现代码。

3.1 函数LOS_ArchMmuMap

函数LOS_ArchMmuMap用于映射进程空间虚拟地址区间与物理地址区间,其中输入参数archMmu为MMU结构体,vaddrpaddr分别是虚拟内存和物理内存的开始地址;count为虚拟地址和物理地址映射的内存页数量;flags为映射标签。
⑴处进行函数参数校验,不支持NON-SECURE的标记,虚拟地址和物理地址需要内存页4KiB对齐,参数检查函数代码简单,自行查看即可。
⑵处当虚拟地址、物理地址基于1MiB对齐,并且数量count大于256时,使用Section页表项格式。
⑶处生成L1 section类型页表项并保存,下文详细分析该函数OsMapSection()。如果不满足⑵处条件,需要使用L2映射。首先执行⑷处获取虚拟地址vaddr对应的L1页表项,接着执行⑸处判断是否映射,如果没有对应的映射,则执行⑹处的函数OsMapL1PTE生成L1 page table类型页表项并保存,然后执行函数OsMapL2PageContinous生成L2 页表项并保存。如果已经映射为L1 page table页表项类型,则执行函数OsMapL2PageContinous生成L2 页表项并保存。如果不是支持的页表项类型,则执行LOS_Panic()触发异常。⑺处统计生成映射的调试,最终会返回映射成功的数量。

可以看出,在给定虚实内存地址和映射的内存页数后,使用L1页表映射还是L2页表映射的判断条件是:虚实内存地址是否1MiB内存对齐,并且映射数量是否大于256。当使用L1映射时,映射为Section页表项类型。当使用L2映射时,根据L1页表项类型,分布处理无效页表项和Page Table页表项类型这2种情况。具体的映射方式见下文。

status_t LOS_ArchMmuMap(LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr, size_t count, UINT32 flags)
{
    PTE_T l1Entry;
    UINT32 saveCounts = 0;
    INT32 mapped = 0;
    INT32 checkRst;

⑴  checkRst = OsMapParamCheck(flags, vaddr, paddr);
    if (checkRst < 0) {
        return checkRst;
    }

    /* see what kind of mapping we can use */
    while (count > 0) {
⑵      if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) &&
            MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(paddr) &&
            count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) {
            /* compute the arch flags for L1 sections cache, r ,w ,x, domain and type */
⑶          saveCounts = OsMapSection(archMmu, flags, &vaddr, &paddr, &count);
        } else {
            /* have to use a L2 mapping, we only allocate 4KB for L1, support 0 ~ 1GB */
⑷          l1Entry = OsGetPte1(archMmu->virtTtb, vaddr);
⑸          if (OsIsPte1Invalid(l1Entry)) {
⑹              OsMapL1PTE(archMmu, &l1Entry, vaddr, flags);
                saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);
            } else if (OsIsPte1PageTable(l1Entry)) {
                saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);
            } else {
                LOS_Panic("%s %d, unimplemented tt_entry %x/n", __FUNCTION__, __LINE__, l1Entry);
            }
        }
⑺      mapped += saveCounts;
    }

    return mapped;
}

3.2 OsMapSectionL1 Section类型页表项映射函数

函数OsMapSection生成L1 Section类型页表项并保存。⑴处把内存区间标签(这些标签定义在文件kernel\base\include\los_vm_map.h中,标签名称一般为VM_MAP_REGION_FLAG_XXXX)转换为MMU标签(定义在arch\arm\arm\include\los_mmu_descriptor_v6.h中,标签名称一般为MMU_DESCRIPTOR_L1_TYPE_XXXX)。 ⑵处的函数OsGetPte1Ptr(archMmu->virtTtb, *vaddr)用于获取虚拟地址对应的页表项索引地址,等于页表项基地址加上虚拟地址的高20位;OsTruncPte1(*paddr) | mmuFlags | MMU_DESCRIPTOR_L1_TYPE_SECTION)为物理内存地址的高12位+MMU标签+页表项Section类型值。该行语句的作用是把虚拟地址和物理地理进行映射,映射关系维护在页表项。这行代码比较关键,我们绘制下图形来表示,见下图。⑶处把虚拟地址和物理地址增加1MiB的大小,映射数量减去256(1MiB有256个4KiB大小的内存页)。

STATIC UINT32 OsMapSection(const LosArchMmu *archMmu, UINT32 flags, VADDR_T *vaddr,
                           PADDR_T *paddr, UINT32 *count)
{
    UINT32 mmuFlags = 0;

⑴  mmuFlags |= OsCvtSecFlagsToAttrs(flags);
⑵  OsSavePte1(OsGetPte1Ptr(archMmu->virtTtb, *vaddr),
        OsTruncPte1(*paddr) | mmuFlags | MMU_DESCRIPTOR_L1_TYPE_SECTION);
⑶  *count -= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
    *vaddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;
    *paddr += MMU_DESCRIPTOR_L1_SMALL_SIZE;

    return MMU_DESCRIPTOR_L2_NUMBERS_PER_L1;
}

3.3 函数OsGetL2Table生成L2页表项基地址

函数OsGetL2Table用于生成L2页表,函数参数中archMmu是MMU结构体,l1Index是L1页表项索引(页号),ppa属于输出参数,保存L2页表项基地址。⑴处计算L2页表项偏移值(为啥这么计算? 看不懂 TODO),其中(MMU_DESCRIPTOR_L2_SMALL_SIZE / MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE)的大小等于1024;l1Index & (MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE - 1)为虚拟地址的第20-21位。⑵处通过循环遍历查询是否存在L2页表(为啥查询4次?TODO),⑶处获取页表项基地址,然后判断是否页表类型,如果是,则返回L2页表项基地址。

如果没有存在的页表,则为L2页表申请内存,如果支持虚拟地址LOSCFG_KERNEL_VM,执行⑷使用LOS_PhysPageAlloc申请内存页,把申请的内存页挂载页表链表上,并根据内存页计算虚拟内存地址kvaddr;如果不支持虚拟地址,执行⑸使用LOS_MemAlloc申请内存。⑹处转换为物理地址,然后加上页表偏移值l2Offset返回L2页表项基地址。

STATIC STATUS_T OsGetL2Table(LosArchMmu *archMmu, UINT32 l1Index, paddr_t *ppa)
{
    UINT32 index;
    PTE_T ttEntry;
    VADDR_T *kvaddr = NULL;
⑴  UINT32 l2Offset = (MMU_DESCRIPTOR_L2_SMALL_SIZE / MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) *
        (l1Index & (MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE - 1));
    /* lookup an existing l2 page table */
⑵   for (index = 0; index < MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE; index++) {
⑶      ttEntry = archMmu->virtTtb[ROUNDDOWN(l1Index, MMU_DESCRIPTOR_L1_SMALL_L2_TABLES_PER_PAGE) + index];
        if ((ttEntry & MMU_DESCRIPTOR_L1_TYPE_MASK) == MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE) {
            *ppa = (PADDR_T)ROUNDDOWN(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry), MMU_DESCRIPTOR_L2_SMALL_SIZE) +
                l2Offset;
            return LOS_OK;
        }
    }

#ifdef LOSCFG_KERNEL_VM
    /* not found: allocate one (paddr) */
⑷  LosVmPage *vmPage = LOS_PhysPageAlloc();
    if (vmPage == NULL) {
        VM_ERR("have no memory to save l2 page");
        return LOS_ERRNO_VM_NO_MEMORY;
    }
    LOS_ListAdd(&archMmu->ptList, &vmPage->node);
    kvaddr = OsVmPageToVaddr(vmPage);
#else
⑸  kvaddr = LOS_MemAlloc(OS_SYS_MEM_ADDR, MMU_DESCRIPTOR_L2_SMALL_SIZE);
    if (kvaddr == NULL) {
        VM_ERR("have no memory to save l2 page");
        return LOS_ERRNO_VM_NO_MEMORY;
    }
#endif
    (VOID)memset_s(kvaddr, MMU_DESCRIPTOR_L2_SMALL_SIZE, 0, MMU_DESCRIPTOR_L2_SMALL_SIZE);

    /* get physical address */
⑹  *ppa = LOS_PaddrQuery(kvaddr) + l2Offset;
    return LOS_OK;
}

3.4 OsMapL1PTEL1 Page Table类型页表项映射函数

和函数OsMapSection对应,函数OsMapL1PTE用于生成L1 Page Table类型页表项并保存,其中函数参数pte1Ptr是L1页表项地址。⑴处调用函数OsGetL2Table()获取L2页表项基地址TTB,⑵处把L2页表项基地址加上描述符类型作为L1页表项数据。⑶处开始的3行代码为页表项设置标签,⑷处为虚拟内存地址vaddr保存页表项数据。

STATIC VOID OsMapL1PTE(LosArchMmu *archMmu, PTE_T *pte1Ptr, vaddr_t vaddr, UINT32 flags)
{
    paddr_t pte2Base = 0;

⑴  if (OsGetL2Table(archMmu, OsGetPte1Index(vaddr), &pte2Base) != LOS_OK) {
        LOS_Panic("%s %d, failed to allocate pagetable\n", __FUNCTION__, __LINE__);
    }

⑵  *pte1Ptr = pte2Base | MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE;
⑶  if (flags & VM_MAP_REGION_FLAG_NS) {
        *pte1Ptr |= MMU_DESCRIPTOR_L1_PAGETABLE_NON_SECURE;
    }
    *pte1Ptr &= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_MASK;
    *pte1Ptr |= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_CLIENT; // use client AP
⑷   OsSavePte1(OsGetPte1Ptr(archMmu->virtTtb, vaddr), *pte1Ptr);
}

3.5 OsMapL2PageContinuous映射L2页表函数

函数OsMapL2PageContinuous用于映射L2页表项,其中函数参数pte1为L1页表项数据,flags为虚实映射标签,vaddr为虚拟内存,paddr为物理内存,count为需要映射的内存页数量。

⑴处根据L1页表项数据获取L2页表项虚拟内存基地址。页表项的高22位为L2页表项的物理内存基地址,然后转换为虚拟内存基地址即可。⑵处把地址区间标签转换为L2页表标签,⑶处连续设置L2页表项数据,saveCounts表示映射了多少个L2页表项。⑷处映射L2页表项数据后,更新虚拟、物理内存地址,更新映射后的内存页数量count。由于一个L2页表项占用4KiB大小,saveCounts个页表项,需要把saveCounts左移12位来增长内存地址。

STATIC UINT32 OsMapL2PageContinuous(PTE_T pte1, UINT32 flags, VADDR_T *vaddr, PADDR_T *paddr, UINT32 *count)
{
    PTE_T *pte2BasePtr = NULL;
    UINT32 archFlags;
    UINT32 saveCounts;

⑴  pte2BasePtr = OsGetPte2BasePtr(pte1);
    if (pte2BasePtr == NULL) {
        LOS_Panic("%s %d, pte1 %#x error\n", __FUNCTION__, __LINE__, pte1);
    }

    /* compute the arch flags for L2 4K pages */
⑵  archFlags = OsCvtPte2FlagsToAttrs(flags);
⑶  saveCounts = OsSavePte2Continuous(pte2BasePtr, OsGetPte2Index(*vaddr), *paddr | archFlags, *count);
⑷  *paddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT);
    *vaddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT);
    *count -= saveCounts;
    return saveCounts;
}

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://qr21.cn/FV7h05

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/703237.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java到AI大模型,我为什么选择的后者

我为什么从Java转到AI大模型 在编程的海洋里&#xff0c;Java一直是我信赖的“小船”&#xff0c;载着我航行在代码的世界中。然而&#xff0c;随着行业的不断发展和变化&#xff0c;我开始感受到了一丝的迷茫和不安。我开始担心&#xff0c;随着技术的不断更新&#xff0c;Ja…

支付平台界面感知评估

目标&#xff1a; 了解本地用户在本地语言下对产品用户界面 (UI) 的感受和体验&#xff1a; 界面的目的是否对本地用户清晰&#xff0c;并且是否符合本地文化和国家标准&#xff1b;界面中的文本是否正确显示&#xff0c;是否存在语法、拼写或其他错误&#xff0c;包括品牌一致…

Vue21-列表排序

一、需求 二、解决方式 <body><div id"root"><h2>人员列表</h2><input type"text" placeholder"请输入" v-model"keyword"><button click"sortType 1">年龄升序</button><b…

[AI资讯·0612] AI测试高考物理题,最高准确率100%,OpenAI与苹果合作,将ChatGPT融入系统中,大模型在物理领域应用潜力显现

AI资讯 国产AI大战高考物理&#xff0c;第1题全对&#xff0c;第2题开始放飞终于放大招了&#xff0c;2024WWDC&#xff0c;苹果开启AI反击战苹果一夜重塑iPhone&#xff01;GPT-4o加持Siri&#xff0c;AI深入所有APPOpenAI确认苹果集成ChatGPT 还任命了两位新高管GPT-4搞不定…

中文藏文翻译怎么在线翻译?通过这些方法

中文藏文翻译怎么在线翻译&#xff1f;随着全球化的加速和跨文化交流的日益频繁&#xff0c;中文与藏文之间的翻译需求逐渐增加。为了满足这一需求&#xff0c;各种在线翻译工具和方法应运而生。下面&#xff0c;本文将详细介绍三种中文藏文在线翻译的方法&#xff0c;帮助用户…

yolov3 详解

文章目录 1、yolov3原理2、损失函数3、yolov3改进4、使用opencv实现yolov35、卷积神经网络工作原理 1、yolov3原理 参考视频 darknet53&#xff1a;52个卷积层和1个全联接层 输入图像为416416 1313 -》 下采样32倍 2626 -》 下采样16倍 5252 -》 下采样8倍 由标注框中心点落在…

图形学初识--定义摄像机类(实战)

文章目录 前言正文定义摄像机的操作方式键盘操作鼠标操作 定义摄像机类核心数据视图矩阵回顾&#xff1a;模拟摄像机的移动模拟摄像机的旋转 结尾&#xff1a;喜欢的小伙伴点点关注赞哦! 前言 前面一些章节讲解了图形学的比较原理性的内容&#xff0c;这一章节咱就实战一下&am…

Vue2后台管理:项目开发全流程(一)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:Vue2后台管理&#xff1a;项目开发全流程(一) 目录 Vue项目开发 项目架构搭建 1、创建项目 2、…

SolidWorks 2016 SP5安装教程

软件介绍 Solidworks软件功能强大&#xff0c;组件繁多。 Solidworks有功能强大、易学易用和技术创新三大特点&#xff0c;这使得SolidWorks 成为领先的、主流的三维CAD解决方案。 SolidWorks 能够提供不同的设计方案、减少设计过程中的错误以及提高产品质量。SolidWorks 不仅…

开发移动端常见的问题:VW适配问题,基于 postcss 插件 实现项目vw适配

当你开发移动端的时候有一个问题是避免不了的&#xff0c;那就是当屏幕大小无论怎么变化时&#xff0c;内部尺寸也要随之发生改变&#xff0c;也就是适配问题。这里我们讲的是最新的VW适配&#xff0c;也就是用vw作为单位&#xff0c;100vw是整个页面的大小。而在开发的设计图中…

Mysql学习(九)——存储引擎

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 七、存储引擎7.1 MySQL体系结构7.2 存储引擎简介7.3 存储引擎特点7.4 存储引擎选择7.5 总结 七、存储引擎 7.1 MySQL体系结构 连接层&#xff1a;最上层是一些客户…

SAP SO定价上面2个ZPR1 其中一个不活跃

查看价格表 取定价的时候排除不活动的 即可

永磁同步电机滞环电流控制(PI双闭环)matlab仿真模型

微♥“电击小子程高兴的MATLAB小屋”获取模型 1.滞环电流控制的原理 将给定的电流信号与反馈的电流信号进行比较&#xff0c;然后控制它俩之间的差值稳定在一个滞环范围内&#xff0c;若超出范围&#xff0c;则进行相应的调节操作。 操作如下叙述&#xff1a;假设以三相中的A相…

DockerCompose+Jenkins+Pipeline流水线打包SpringBoot项目(解压安装配置JDK、Maven等)入门

场景 DockerCompose中部署Jenkins&#xff08;Docker Desktop在windows上数据卷映射&#xff09;&#xff1a; DockerCompose中部署Jenkins&#xff08;Docker Desktop在windows上数据卷映射&#xff09;-CSDN博客 DockerJenkinsGiteeMaven项目配置jdk、maven、gitee等拉取代…

论文图片颜色提取

论文绘图的时候有些颜色不知道怎么选取&#xff0c;参考其他论文&#xff0c;将其他论文中的颜色提取下来&#xff0c;用取色器识别出来&#xff0c;记录如下&#xff1a; 颜色代码&#xff1a;#BEAED4 190,174,212 颜色代码&#xff1a;#C4CBCB 196,203,203 颜色代码&am…

51单片机实验05 -点阵

目录 一&#xff0c;熟悉矩阵led小灯 1&#xff0c;点亮矩阵的一只led 2&#xff0c;点亮矩阵的一排led 3&#xff0c;点亮矩阵的全部led static 关键字 unsigned 关键字 4&#xff0c;点阵的静态显示 2&#xff09;心形矩阵显示代码 3&#xff09;效果 二&#xff0c;课…

6.nginx负载均衡

说明 增加服务器的数量,将请求分发到各个服务器上。 将原来请求集中到单个服务器上的情况改为将请求分发到多个服务器上。 案例 浏览器请求地址http://ip/edu/a.html, 负载均衡的效果,平分到8080和8081两台服务上中。 准备工作 tomcat8080配置 tomcat8081配置 直接通过…

----几种接口的使用---

Compareable接口 对于给数组中的变量成员排序&#xff0c;我们能想到用sort&#xff0c;根据成员之间的大小进行排序&#xff0c;那么如果数组中的成员是对象的话&#xff0c;单单只是用sort去排序肯定是步成功的&#xff0c;因为并不知道要根据什么去排序&#xff0c; 这时要…

【C++】编译

三、C编译 前面给大家演示了如何从写C代码到编译代码再到执行代码的全过程。这个过程中非常重要的编译环节&#xff0c;被我们一个按钮或者一个ctrlF7快捷键就给带过了。其实这个环节非常重要&#xff0c;如果你非常了解这个环节&#xff0c;你开发源代码就会更加自信和清醒&a…

CorelDRAW 2024开启设计新纪元,终身永久版与中文破解版的全面解析及安装攻略

当我们谈论图形设计软件时&#xff0c;CorelDRAW无疑是一个响亮的名字。作为一款强大的矢量图形编辑工具&#xff0c;它以其丰富的功能和用户友好的界面赢得了全球设计师的喜爱。随着CorelDRAW 2024的发布&#xff0c;这个备受瞩目的版本带来了前所未有的创新特性&#xff0c;进…