当前位置: 首页 > news >正文

xv6 源码精读(二)开启MMU、一致性映射页表

目录

一、armv8页表

1)页表类型

2)页表描述符(页表项)

3)页表翻译过程

二、页表基地址寄存器(TTBR——Translation Table Base Register)

三、一致性页表、使能MMU

3.1、一致性页表

3.2、TCR_EL1(Translation Control Register) 

3.3、MAIR_EL1(Memory Attribute Indirection Register)

3.4、SCTLR_EL1(System Control Register)

四、xv6 启动阶段代码

五、总结


引言:

        本文分2部分,前半部分主要介绍armv8中MMU相关的一些背景知识,后半部分 分析xv6 aarch64中主核启动过程中enable MMU的实现细节。

一、armv8页表

1)页表类型

        armv8中,支持2级页表、3级页表、4级页表(level0 ~ level3),以4级页表为例,分别有以下几个类型的页表构成:

  • PGD:page global directory (VA's bit[47] ~ bit[39]),页全局目录;
  • PUD:page upper directory(VA's bit[38] ~ bit[30]),页上级目录;
  • PMD:page middle directory(VA's bit[29] ~ bit[21]),页中间目录;
  • PTE:page table entry(VA's bit[29] ~ bit[21]),页中间目录;

        以上4种类型的页表,大小都为4KB,每个页表中包含512个页表项,记录下一级页表地址、物理页地址。

        可能有些小伙伴会好奇:为什么每个页表由512个页表项组成,而不是1024个或其他个数的页表项组成?

        其实这个问题也很好理解,一个页的大小为4096字节,每个页表项都是一个个指针,指针的大小为8字节,所以页表能够容纳的页表项个数是512个(4096 ÷ 8 = 512)。

        若不考虑block mapping的话,PGD、PUD、PMD 这3类页表中记录的内容,都是下一级页表的物理基地址、以及一些页表属性,PTE页表中的页表项记录的是真正物理内存的4K对齐的物理地址、以及一些页表属性;

2)页表描述符(页表项)

        页表描述符(Translation table descriptor)分为两大类型:

        (1)Table descriptor format:此类描述符指向下一级页表的物理地址,且此类页表描述符只有Upper attributes;

        (2)Block/Page descriptor format:此类描述符指向VA对应PA所在物理页地址,此类描述符有Upper attribute、Lower attribute;

Table descriptorUpper attribute;只存在于 level 0、1、2 级页表
Block descriptorUpper/Lower attribute;只存在于 level 1、2 级页表
Page descriptorUpper/Lower attribute;只存在于 level 3 级页表

        在ARMv8的页表描述符中,不仅包含目标物理地址,还包含一大块用于描述该内存区域属性的字段,这些属性控制着内存区域的访问行为、缓存策略等。

        为了结构清晰,这些属性被分成两组:

        - Upper attribute:位于描述符的[63:50]位;

        - Lower attribute:位于描述符的[16:2]位;

2.1)Translation descriptor format(bit[0]、bit[1])

        页表中每个描述符的类型、是否有效,由页表描述符的 bit[0]、bit[1] 决定:

2.2)UXN or XN(bit[54])

        系统仅有一级特权时,代表XN(Execute-never);系统支持EL0/EL1两级特权时,代表UXN(Unprivileged execute-never);置1代表用户态不可执行;内核加载ELF时,为用户态程序的Data段内存区域设置该字段时,可以控制用户态数据段不可执行;

2.3)nG(bit[11])

        若nG为0,代表这块内存区域是全局的,任何进程都可以访问;

        若nG为1,代表内存区域是本地的,只能由当前进程访问(ASID);

        页表项属性中的nG字段(non-global)用来设置对应TLB的类型。TLB的表项分成全局的、进程特有的。当设置nG1时,表示这个页面对应的TLB表项是进程特有的;当为0时,表示这个TLB表项是全局的

2.4)AF(bit[10])

        Access Flag,当第一次访问页面时,硬件会自动设置这个访问位;Descriptors with AF set to zero can never be cached in a TLB;

        当DBM为1,stage1的AP2为1时,可以等同于writeable。

2.5)SH(bit[9:8])

        Shareability attribute规定了内存的Shareability,即:是否是Shareable的,以及是Inner Shareable 还是 Outer Shareable。该字段的配置,会影响硬件 MESI 协议起作用的范围。

        对于同一Cluster内部,是Inner Shareable;对于两个Cluster之间,是Outer Shareable的;若这个字段配置错误,会导致缓存一致性出问题,比如对于Outer Shareable的物理内存相应页表想没有配置为Outer Shareable,那么对该物理内存的修改,外设的cache中的内存可能不会得到更新,造成缓存不一致的问题;

2.6)AP(bit[7:6])

        Access permission,该字段控制CPU对页面的访问权限,具体如下所示:

2.7)NS(bit[5])

        Non-secure,该字段用于标识物理内存是属于Non-Secure(REE侧访问)的还是Secure的(TEE侧访问);

2.8)AttrIndex[2:0](bit[4:2])

        该字段是作为MAIR中的索引,用于查看具体的内存属性,MAIR_ELn寄存器分为8段,每一段用于描述不同的内存属性。AttrIndex字段可以控制对应内存区域是否使能cache等行为!

        Memory type(normal or device)信息不是直接填写在页表项中的,是记录在页表项中AttrIndex指向的MAIR_ELn寄存器中的。
        注:memory type信息不是直接编码记录在table entry中,但是TLB entry中会记录memory type信息。因此MAIR_ELn寄存器内容的修改,需要执行 ISB instruction barrier 和 TLB invalidate operation这两个操作后,才能确保可见!

3)页表翻译过程

        当CPU要访问一个虚拟地址VA上的内容、且该内容没有缓存在cache时,会进行地址翻译获取VA对应的PA,最终真正去访问物理地址上的内容。获取物理地址PA的过程就是由MMU硬件根据OS设置好的页表去自行翻译查找的。

        整个页表翻译的过程如下(以4级页表为例):

        step1:根据VA的最高位,选取对应的页表寄存器,获取pgd页表所在地址;

        step2 ~ step8:根据VA中对应位,获取在页表中的偏移位置,接着根据页表中记录的页表项内容获取下一级页表的基地址;

        step9 ~ step10:获取真是物理页基地址(4K对齐),并根据VA中的低12位与物理地址拼接获得最终VA对应的PA地址

二、页表基地址寄存器(TTBR——Translation Table Base Register)

        armv8中,MMU负责 “VA ---> PA” 的转换,在进行地址翻译的时候,首先需要获取PGD页表的地址,然后开始地址翻译。

        pgd页表的地址,记录在页表基地址寄存器中,由内核设置好每一级页表的内容后,将pgd的物理地址设置到页表基地址寄存器中。

        EL1中有2个页表基地址寄存器:ttbr0_el1、ttbr1_el1,分别指向 “用户态”、“内核态”的PGD页表。

        更准确的说法是:地址翻译时,虚拟地址的最高位用于标识地址的属性,其中最高位用于区分选取 ttbr0_el1、还是选取 ttbr1_el1作为页表基地址(仅适用于EL0、EL1):

  • 虚拟地址最高位为0:选取ttbr0_el1中记录的pgd进行翻译,表示该地址属于用户空间;
  • 虚拟地址最高位为1:选取ttbr1_el1中记录的pgd进行翻译,表示该地址属于内核空间;

地址空间布局:

三、一致性页表、使能MMU

3.1、一致性页表

        当SoC上电后,前期BL1、BL2、BL31等初始化完毕跳转到uboot,并由uboot跳转到内核的那一刻,MMU通常情况下是关闭的。内核入口的汇编代码,一般是直接在物理内存上运行的,不需要MMU进行地址转换。

        为了运行效率(cache的使能前提,需要MMU enable)等考虑,在一定阶段,需要开启MMU,因此在开启MMU之前,需要先准备好页表。

        在armv8架构上,一般内核在开启MMU之前,会准备好两套页表:

        1) 一致性页表:PA == VA(VA 最高位为0)

        2)正常映射内核虚拟地址的页表:VA == 属于内核区域的高地址虚拟地址区域(VA 最高位为1)

        引入一致性页表的原因:

        在开启MMU的瞬间,由于CPU流水线的原因,可能先前预加载的指令还未执行,这些指令内存地址仍是PA,在使能MMU的那一瞬间,这些地址会需要经过MMU翻译,但若页表是以内核链接地址(高16位全1)建立的,那么这些PA在MMU翻译时就会报错!

        为了解决上述问题,在开启MMU之前,会额外建立一份“VA==PA”的页表,并且将页表基地址存放到 TTBR0_EL1中,这样在开启MMU的瞬间,就算流水线中执行/访问了一些物理地址上的指令或内存,也同样能够正常进行MMU翻译动作!(注:由于PA的高16位为0,因此将VA设置为PA后,在MMU翻译时,会以TTBR0_EL1寄存器中的页表进行翻译!

        在使能MMU之前,除了配置页表寄存器(TTBR)之外,还需要配置其他几个寄存器:TCR、MAIR;用于设置页面大小、寻址大小、页表级别、内存属性等等。

3.2、TCR_EL1(Translation Control Register)

        - 页面粒度(TG0)

        TG0字段控制页面大小,常见设置为4KB;

        - 地址空间大小(T0SZ、T1SZ)

                决定 用户态、内核态的虚拟地址范围(VA),例如T0SZ=25表示用户态39位VA;

        - 内存属性(ORGN0、IRGN0)

        - 页表级别

        TCR_EL1支持xv6的3级页表,起始级别由T0SZ和TG0决定(例如,T0SZ=25,TG0=4KB,3级页表)。

        - ASID相关配置

        AS字段决定了TTBR寄存器中,高8位、还是高16位存放ASID的信息;A1字段决定ASID字段保存在TTBR0_EL1还是TTBR1_EL1中;

3.3、MAIR_EL1(Memory Attribute Indirection Register)

        mair_el1 寄存器,用于定义虚拟地址空间中不同区域的内存属性(memory typeCacheability)。例如某个区域可以被标记为可读写、可执行、缓存等属性。ARM提供了页表项AttrIdx字段、MAIR_ELx来支持内存类型、内存Cacheability属性的配置。

        只要“MAIR_ELx配置的内存属性所处下标” 与 “页表项AttrIdx字段所代表的下标” 是一致的就行!

3.4、SCTLR_EL1(System Control Register)

        SCTLR_EL1是一个系统寄存器,控制系统的种种行为,MMU、D-Cache、I-Cache的使能就是通过该寄存器中的指定字段来配置的;

        - MMU使能

        - I-Cache使能

        - D-Cache使能

四、xv6 启动阶段代码

1)内核入口 _entry

        内核入口定义在 kernel.ld 链接脚本中,通过readelf查看编译出来的 kernel elf 文件可见:内核的编译地址(即:虚拟地址)是0xffffff8040000000,这个也是通过链接脚本中指定的。

2)内核启动

        当CPU跳转到内核入口后,会根据 mpidr_el1 寄存器判断当前CPU是主核还是从核,跳转到不同代码label处执行,主核会跳转到entry label处,并将BSS段清0;

_entry:mrs x1, mpidr_el1and x1, x1, #0x3cbz x1, entry         // primaryb entryothers         // secondaryentry:// clear .bssadrp x1, bss_startldr w2, =bss_size
1:cbz w2, 2fstr xzr, [x1], #8sub w2, w2, #1b 1b
2:

3)设置一致性页表、内核页表

        xv6 开启MMU之前,会通过汇编设置两套页表:

        - 一致性页表(l1entrypgt)

                VA映射范围:[ 0x40000000,  PA(end) ]

                PA映射范围:[ 0x40000000,  PA(end) ]

                映射方式:block块映射(2M)、二级页表

        - 内核页表(l1kpgt)

                VA映射范围:[ 0xffffff8040000000,  PA(end) ]

                PA映射范围:[ 0x40000000,  PA(end) ]

                映射方式:block块映射(2M)、二级页表

        // set up entry pagetable//// Phase 1.// map the kernel code identically.// map [0x40000000,PA(end)) to [0x40000000,PA(end))// memory type is normal//// Phase 2.// map the kernel code.// map [0xffffff8040000000,VA(end)) to [0x40000000,PA(end))// memory type is normal.// Phase 1// map [0x40000000,PA(end)) to [0x40000000,PA(end))adrp x0, l2entrypgtmov x1, #0x40000000ldr x2, =V2P_WO(end)-1lsr x3, x1, #PXSHIFT(2)and x3, x3, #PXMASK   // PX(2, x1)lsr x4, x2, #PXSHIFT(2)and x4, x4, #PXMASK   // PX(2, x2)mov x5, #(PTE_AF | PTE_INDX(AI_NORMAL_NC_IDX) | PTE_VALID) // entry attrorr x6, x1, x5      // block entry
l2epgt_loop:str x6, [x0, x3, lsl #3]  // l2entrypgt[l2idx] = block entryadd x3, x3, #1          // next indexadd x6, x6, #0x200000   // next block, block size is 2MBcmp x3, x4b.ls l2epgt_loop    // if start va idx <= end va idxadrp x0, l1entrypgtlsr x3, x1, #PXSHIFT(1)and x3, x3, #PXMASK     // start va level1 indexmov x4, #(PTE_TABLE | PTE_VALID)  // entry attradrp x5, l2entrypgtorr x6, x4, x5    // table entrystr x6, [x0, x3, lsl #3]    // l1entrypgt[l1idx] = table entry// Phase 2// map [0xffffff8040000000,VA(end)) to [0x40000000,PA(end))adrp x0, l2kpgtmov x1, #0x40000000   // start paldr x2, =V2P_WO(end)-1   // end pamov x3, #KERNBASEadd x4, x1, x3    // start vaadd x5, x2, x3    // end valsr x6, x4, #PXSHIFT(2)and x6, x6, #PXMASK   // x6 = PX(2,x4)lsr x7, x5, #PXSHIFT(2)and x7, x7, #PXMASK   // x7 = PX(2,x5)mov x8, #(PTE_AF | PTE_INDX(AI_NORMAL_NC_IDX) | PTE_VALID) // entry attrorr x9, x1, x8      // block entry
l2kpgt_loop:str x9, [x0, x6, lsl #3]  // l2entrypgt[l2idx] = block entryadd x6, x6, #1          // next indexadd x9, x9, #0x200000   // next block, block size is 2MBcmp x6, x7b.ls l2kpgt_loop    // if start va idx <= end va idxadrp x0, l1kpgtlsr x5, x4, #PXSHIFT(1)and x5, x5, #PXMASK  // x5 = PX(1,x4)mov x6, #(PTE_TABLE | PTE_VALID)  // entry attradrp x7, l2kpgtorr x8, x6, x7    // table entrystr x8, [x0, x5, lsl #3]    // l1kpgt[l1idx] = table entry

        上述两套页表所在的内存,是内核data段的一块全局数组:

__attribute__((aligned(PGSIZE))) pte_t l1entrypgt[512];
__attribute__((aligned(PGSIZE))) pte_t l2entrypgt[512];
__attribute__((aligned(PGSIZE))) pte_t l1kpgt[512];
__attribute__((aligned(PGSIZE))) pte_t l2kpgt[512];

4)开启MMU,并跳转到内核编译地址(虚拟地址)

        在准备好2套页表后,分别将它们设置到 TTBR0_EL1、TTBR1_EL1中,并且配置好TCR_EL1、MAIR_EL1寄存器内容,采用39位虚拟地址大小、4K页面粒度;

        随后,通过设置sctlr_el1的bit[0],将其置1,来使能MMU。

        值得注意的是:ldr x1, =_start 这条汇编语句,其作用是获取_start的链接地址(即:内核的虚拟地址),用于开启MMU后,通过 br x1 跳转到内核虚拟地址继续运行!这也是armv8中,内核开启MMU后,地址跳转的常用方式。

entryothers:  // secondary CPU starts here// load pagetableadrp x0, l1entrypgtadrp x1, l1kpgtmsr ttbr0_el1, x0       <<<<< 设置页表寄存器msr ttbr1_el1, x1// setup tcrldr x0, =(TCR_T0SZ(25)|TCR_T1SZ(25)|TCR_TG0(0)|TCR_TG1(2)|TCR_IPS(0))msr tcr_el1, x0// setup mairldr x1, =((MT_DEVICE_nGnRnE<<(8*AI_DEVICE_nGnRnE_IDX)) | (MT_NORMAL_NC<<(8*AI_NORMAL_NC_IDX)))msr mair_el1, x1isbldr x1, =_start   // x1 = VA(_start)// enable pagingmrs x0, sctlr_el1orr x0, x0, #1msr sctlr_el1, x0       <<<<< 开启MMU <<<<<br x1       // jump to higher address (0xffffff8000000000~)

五、总结

        本文借助xv6 aarch64的代码,介绍了 armv8 中内核启动阶段的MMU使能方式,以及页表、页表寄存器、一致性页表等相关基础概念。更多细节内容,感兴趣的小伙伴可参考官方文档、及linux内核启动阶段相关代码。

http://www.dtcms.com/a/512008.html

相关文章:

  • 珠海网站建设尚古道策略长沙口碑好网站建设公司
  • =word插入公式后行距变大怎么办?-笔记
  • Android 接入 Google 和 Facebook 第三方登录指南(初始版)
  • Aspose.words关于builder.CellFormat.Width、row.Cells[0].CellFormat.Width的设置单元格宽度区别
  • 罗湖网站建设的公司哪家好阳泉做网站公司
  • 口碑好的共晶贴片机公司
  • 挑战概率直觉:蒙提霍尔问题的解密与应用
  • 网站域名哪些后缀更好给自己公司做个网站
  • 算法笔记 07
  • Steps + Input.TextArea + InfiniteScroll 联调优化
  • /dev/mem 原理及使用
  • 机关网站建设 方案泰安新闻完整版
  • Endpoint
  • 阿里巴巴双11微服务智能监控体系:从全链路追踪到AI自愈的技术实践
  • 在ros2 humble版本上安装D455相机并获取图像和深度信息
  • C++DirectX9坐标系与基本图元之渲染状态(RenderState)_0304
  • 网站建设app长春seo技术
  • 【C++】力扣hot100错误总结
  • C++中的vector讲解
  • 笔记【字符串,转义字符,注释】
  • visual studio安装本地帮助手册
  • 北京市基础建设质量监督局网站wordpress 插件怎么看
  • 大模型技术分析与演进逻辑
  • 苏州模板网站建站长沙网站建设推广
  • 从零起步学习MySQL || 第六章:MySQL数据库中的一条数据是如何存储的?(结合源码深度解析)
  • 微信小程序页面配置,基本语法,页面切换,tabbar全局配置
  • 数据结构 07
  • 18.基本的ACL
  • 网站后台编程语言创业中文网站模板
  • 从“刘易斯拐点”到“骑手拐点”,即时零售3.0时代还有多远?