首页 > 其他分享 >ARM64页表映射

ARM64页表映射

时间:2023-04-13 09:23:17浏览次数:52  
标签:pgd 表项 映射 pte pmd ARM64 SIZE 页表

1.ARMv8-A架构

基于ARMv8-A架构的处理器最大可以支持到48根地址线,也就是寻址2的48次方的虚拟地址空间,即虚拟地址空间范围为0x0000_0000_0000_0000~0x0000_FFFF_FFFF_FFFF,共256TB。

一个是从0x0000_0000_0000_0000到0x0000_FFFF_FFFF_FFFF,另外一个是从0xFFFF_0000_0000_0000到0xFFFF_FFFF_FFFF_FFFF。

基于ARMv8-A架构的处理器支持的页面大小可以是4KB、16KB或者64KB。映射的层级可以是3级或者4级(地址范围是不一样的)。

下面是页面大小为4KB,地址宽度为48位,4级映射的内存分布图:
image

下面是页面大小为4KB,地址宽度为48位,3级映射的内存分布图:

image

2.示例4KB大小页面,48位地址宽度,4级映射

image

如果输入的虚拟地址最高位bit[63]为1,那么这个地址是用于内核空间的,页表的基地址寄存器用TTBR1_EL1(Translation Table Base Register 1)。如果bit[63]等于0,那么这个虚拟地址属于用户空间,页表基地址寄存器用TTBR0。

TTBRx寄存器保存了第0级页表的基地址(L0 Table base address, Linux内核中称为PGD), L0页表中有512个表项(Table Descriptor),以虚拟地址的bit[47:39]作为索引值在L0页表中查找相应的表项。每个表项的内容含有下一级页表的基地址,即L1页表(Linux内核中称为PUD)的基地址。

PUD页表中有512个表项,以虚拟地址的bit[38:30]为索引值在PUD表中查找相应的表项,每个表项的内容含有下一级页表的基地址,即L2页表(Linux内核中称为PMD)的基地址。

PMD页表中有512个表项,以虚拟地址的bit[29:21]为索引值在PMD表中查找相应的表项,每个表项的内容含有下一级页表的基地址,即L3页表(Linux内核中称为PTE)的基地址。

在PTE页表中,以虚拟地址的bit[20:12]为索引值在PTE表中查找相应的表项,每个PTE表项中含有最终的物理地址的bit[47:12],和虚拟地址中bit[11:0]合并成最终的物理地址,完成地址翻译过程。

3.示例39位有效位,4KB大小页面,3级页表

image

  • 当CONFIG_PGTABLE_LEVELS=4时:pgd-->pud-->pmd-->pte;
  • 当CONFIG_PGTABLE_LEVELS=3时,没有PUD页表:pgd(pud)-->pmd-->pte;

4.pgd_offset_k函数

通过pgd_offset_k()宏来得到具体的PGD页面目录项的表项。首先通过init_mm数据结构的pgd成员来获取PGD页表的基地址,然后通过pgd_index()来计算PGD页表中的偏移量offset。

/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(addr)     pgd_offset(&init_mm, addr)
 
#define pgd_offset(mm, addr)      ((mm)->pgd+pgd_index(addr))
 
/* to find an entry in a page-table-directory */
#define pgd index(addr)     (((addr) >> PGDIR SHIFT) & (PTRS PER PGD - 1))

通过计算可以得到PGDIR_SHIFT等于39, PUD_SHIFT等于30, PMD_SHIFT等于21。
每级页表的页表项数目分别用PTRS_PER_PGD、PTRS_PER_PUD、PTRS_PER_PMD和PTRS_PER_PTE来表示,都等于512。
PGDIR_SIZE宏表示一个PGD页表项能覆盖的内存范围大小为512GB。PUD_SIZE等于1GB, PMD_SIZE等于2MB, PAGE_SIZE等于4KB。

5.create_mapping函数

通过init_mm数据结构的pgd成员来获取, swapper_pg_dir全局变量指向PGD页表基地址。

[arch/arm64/kernel/vmlinux.lds.S]
 
idmap_pg_dir = .;
. += IDMAP_DIR_SIZE;
swapper_pg_dir = .;
. += SWAPPER_DIR_SIZE;

[arch/arm64/include/asm/page.h]

#define SWAPPER_PGTABLE_LEVELS  (CONFIG_ARM64_PGTABLE_LEVELS - 1)
#define SWAPPER DIR SIZE     (SWAPPER PGTABLE LEVELS * PAGE SIZE)

假设CONFIG_ARM64_PGTABLE_LEVELS定义为4,那么SWAPPER_DIR_SIZE大小就等于3个PAGE_SIZE的大小。从vmlinux.lds.S链接文件可以看到,PGD页表的大小定义为3个PAGE_SIZE。swapper_pg_dir的起始地址由vmlinux.lds.S链接文件计算得来。

6. alloc_init_pte()配置PTE页表项

static void alloc_init_pte(pmd_t *pmd, unsigned long addr,
                              unsigned long end, unsigned long pfn,
                              pgprot_t prot,
                              void *(*alloc)(unsigned long size))
{
    pte_t *pte;

    if (pmd_none(*pmd) || pmd_sect(*pmd)) {
        pte = alloc(PTRS_PER_PTE * sizeof(pte_t));
        if (pmd_sect(*pmd))
              split_pmd(pmd, pte);
        __pmd_populate(pmd, __pa(pte), PMD_TYPE_TABLE);
        flush_tlb_all();
    }
    BUG_ON(pmd_bad(*pmd));

    pte = pte_offset_kernel(pmd, addr);
    do {
        set_pte(pte, pfn_pte(pfn, prot));
        pfn++;
    } while (pte++, addr += PAGE_SIZE, addr ! = end);
}

首先判断PMD表项的内容是否为空?如果为空,说明下一级页表不存在,需要动态分配512个页表项,然后通过__pmd_populate()函数来设置PMD页表项。
通过pte_offset_kernel()宏来索引到相应的PTE页表项。索引值可以通过pte_index()来计算,最终会使用虚拟地址bit[20:12]来做索引值。
接下来以PAGE_SIZE即4KB大小为步长,通过while循环来设置PTE页表项。将pteval写入pteptr所指的页表项中:

define set_pte(pteptr, pteval) (*(pteptr) = pteval)

PTE_DIRTY:CPU在写操作时会设置该标志位,表示对应页面被写过,为脏页。
PTE_YOUNG:CPU访问该页时会设置该标志位。在页面换出时,如果该标志位置位了,说明该页刚被访问过,页面是young的,不适合把该页换出,同时清除该标志位。
PTE_PRESENT:表示页在内存中。

标签:pgd,表项,映射,pte,pmd,ARM64,SIZE,页表
From: https://www.cnblogs.com/linhaostudy/p/17312168.html

相关文章

  • Docker介绍下载安装、制作镜像及容器、做目录映射、做端口映射
    在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。这些资源的新虚拟部份是不受现有资源的架设方式,地域或物......
  • Mybatis-关联关系映射
    1.一对多 1.1.导入数据表--一对多--客户表(主表)createtablet_customer(customer_idintprimarykeynotnullauto_increment,customer_namevarchar(50)notnull);--多对一--订单表(从表)createtablet_order(order_idintprimarykeynotnull......
  • 在C#中使用Attributes(特性)来控制枚举成员是否应该被序列化或映射
    如果标记了[NonSerialized]特性,会防止将该字段序列化。但是,该字段仍然可以用于foreach迭代,因为它仍然是枚举的有效成员。如果要防止特定枚举成员被foreach迭代,用[NonSerialized]特性是不起作用的。相反,可以创建一个自定义的Attribute继承自System.Attribute,并将其应用到需要隐藏的......
  • Charles:解决Charles映射本地时,option请求的跨域问题
    解决方案原因关于为什么会出现该问题,请参考:https://juejin.cn/post/7021077647417409550解决1、查看页面option接口的请求头中带『Access-Control』开头的header2、通过Charles的rewrite功能修改映射时的header3、具体配置如下:4、再次访问后,即可正常映射参考链接ht......
  • 禁用 DevTools 源映射功能, 隐藏 "DevTools failed to load SourceMap" 报错
    警告DevToolsfailedtoloadsourcemap:Couldnotloadcontentforchrome-extension://cofdbpoegempjloogbagkncekinflcnj/build/content.js.map:系统错误:net::ERR_BLOCKED_BY_CLIENT这个问题可能是因为浏览器的版本不同,所以设置的位置也会略有不同。以下是几个常见浏......
  • 第5章 使用路由将URL映射到Razor Pages(ASP.NET Core in Action, 2nd Edition)
    本章包括(请点击这里阅读其他章节)将URL映射到Razor页面使用约束和默认值匹配URL从路由参数生成URL在第4章中,您了解了MVC设计模式,以及ASP.NETCore如何使用它为使用RazorPages的应用程序生成UI。RazorPages包含类似小型控制器的页面处理程序。页面处理程序......
  • vca的映射问题
    为什么使用svd?为了降维和降噪去除不必要的维度就减少了运输量。  映射的时候为什么还要选择不同的维度? ......
  • unidac 在linux loongxarch64或arm64使用SQLite
    unidacSQLite默认使用静态连接方式,但UNIDAC官方SQLite未提供arm64或loongxarch64静态库,造成linuxfor龙芯或arm64无法使用SQLite,经跟踪unidac源码发现将souce/uniProviders/SQLite/LiteDac.inc添加{$DEFINENOSTATIC}然后安装liteprovider10.lpk就可以。//////////////////......
  • #yyds干货盘点#学习笔记(1)Linux和Windows上实现端口映射
    一、Windows下实现端口映射1.查询端口映射情况netshinterfaceportproxyshowv4tov42.查询某一个IP的所有端口映射情况netshinterfaceportproxyshowv4tov4|find"[IP]"例:netshinterfaceportproxyshowv4tov4|find"192.168.1.1"3.增加一个端口映射netshinterfa......
  • 【Spring原理分析-Scope+Spring Boot自定义静态资源映射】
    Spring原理分析-Scope本文纲要:一、Scope1、Scope类型2、基础准备与演示二、Scope失效1、基础准备与演示2、失效原因及处理3、总结下面开始进入正文:一、Scope1、Scope类型singleton,prototype,request,session,application2、基础准备与演示①编写各种Scope的Beanrequest......