x64 分页管理(一)基础知识

😄

1 分页管理

分段模式,用来将逻辑地址(Seg:Offset)转换为线性地址,分页模式用来将线性地址转译为物理地址。

Intel 支持 4 种分页模式,只有启用了保护模式 CR0.PE = 1,才能启用分页(CR0.PG = 1)。CR0.PG = 1 后就启用了分页模式,具体使用哪一种分页模式,取决于 CR4 寄存器:

  1. CR4.PAE = 0,32-bit paging(10-10-12)。
  2. CR4.PAE = 1、IA32_EFER.LME = 0,PAE paging(2-9-9-12)。
  3. CR4.PAE = 1、IA32_EFER.LME = 1、CR4.LA57 = 0,4-level paging(9-9-9-9-12)。
  4. CR4.PAE = 1、IA32_EFER.LME = 1、CR4.LA57 = 1,5-level paging(9-9-9-9-9-12)。

注意:

  • 32-bit paging、PAE-paging 为 32 位虚拟地址使用分页,虚拟地址寻址空间始终为 4GB,物理地址空间最大扩展至 64GB。
  • 4-level paging、5-level paging 为 IA-32e 模式下使用的分页模式,虚拟地址寻址空间为 256TB(48)、4PB(57),物理地址空间最大扩展至 4PB(57)。
  1. 在分页模式中,有 36、40、52 位宽的最大物理地址,使用 MAXPHYADDR 表示。所以在物理地址分页结构中,物理地址宽度始终以 MAXPHYADDR 为界。
  2. 32-paging 分页下,使用 PSE-36 模式时,才有 4MB 页面大小,物理地址才允许扩展到 40 位(对新机器看来说PSE-36模式没多大意义)。
  3. XD 为在 32-paging 分页模式不适用,且仅在 IA32_EFER.NXE= 1 时启用。
  4. 兼容模式中,仅使用 32 位线性地址(63:32 视为 0),使用 PAE-paging 分页模式。
  5. 64-bit 模式下:
    • 4 级分页:高 16 位保留,仅使用低 48 位。规范地址为 0xFFFF/0x0000。
    • 5 级分页:高 7 位保留,仅使用低 57 位。规范地址为 0xFE/0x01。
  6. 支持 4 级分页或 5 级分页的处理器不一定支持1-GByte 的分页。
  7. 仅当 CR4.PCIDE = 1 时才使用 PCID。
  8. 使用 4 级和 5 级分页,每个线性地址都与一个保护密钥相关联。软件可以使用保护密钥权限寄存器来禁用每个保护密钥对与该保护密钥关联的线性地址的某些访问方式,即读写权限(包括 0/3 环的地址)。

分页模式中,每种表结构都是 4KB 大小,表结构包括:PLM4/PLM5、PDPT、PDT、PTT。但是有两种宽度大小的表条目:

  • 32-bit paging:每个 PDE、PTE 条目都是 4 bytes 大小。
  • PAE、4-level、5-level:每个 PLM4/PLM5、PDPE、PDE、PTE 条目都是 8 bytes。

4 种分页区别如下:

59.png

1.1 物理页面

注意虚拟地址不是物理地址,虚拟地址的宽度也不是物理地址的宽度。

现在的处理器支持 32、48、57 位的线性地址(虚拟地址),通过分页模式,可以将其转译为 36、40、52 位宽的物理地址。

一、处理器最高物理地址(MAXPHYADDR)

CPUID.80000008H:EAX[bits 7:0-Physical,bits 15:8-Linear] 得到的物理地址信息即为支持最大物理地址位数(MAXPHYADDR)。一般有 3 种尺寸的 MAXPHYADDR:

  • 36 位:在 Intel 平台的桌面处理器上普遍实现了 36 位的最高物理地址值,可寻址 64 GB 空间。
  • 40 位:在 Intel 的服务器产品和 AMD 平台上普遍实现 40 位的最高物理地值,可寻址 1 TB 空间。
  • 52 位:这是 x64 体系描述中的最高实现值。

二、物理页面的尺寸大小:有 4 种

58.png

1.2 启用分页模式

开启 IA-32e 模式的过程如下:

注意:必须在 CR0.PG = 0 时才能开启 PAE(CR4.PAE = 1)。

60.png

关闭 IA-32e 模式的过程如下:

注意:在关闭 PAE 分页模式前,必须先关闭分页机制(CR0.PG = 0)。也就是说,不能在 long-mode active 的状态下对 CR4.PAE 进行修改。

61.png

启用分页时需要注意:

  1. 要关闭 IA-32e 模式(将 IA_EFER.LME = 0)前,必须先切换到兼容模式,才能设置 CR0.PG = 0。
  2. 在 CR4.PAE = 1 和 CR4.LA57 = 1 时,不能通过 MOV 到 CR4 来直接置为 0。
  3. 如果逻辑处理器处于 64 位模式或 CRA.PCIDE = 1,则尝试清除 CR0.PG 会导致一般保护异常(#GP)。在尝试禁用分页之前,软件应转换到兼容模式并清除 CR4.PCIDE。
  4. 处在 4/5 级分页时,不能直接在各种分页之间直接切换(使用MOV,WRMSR指令分别修改CRx、MSR寄存器),应该先禁用分页,最后再切换。
  5. 在 VMX 模式下,允许直接在各种分页模式直接切换。这是因为 VMX 转换可以在一次操作中加载 CR0、CR4 和 IA32_EFER。

分页的切换应该遵循下图:

62.png

2 4/5-level 分页

2.1 4/5-level 分页概述

在分页管理中,由于有 36、40、52 三种物理地址宽度,所以统一使用 MAXPHYADDR 来表示物理地址的最大界限,在分页中 51:MAXPHYADDR 使用 0 填充。

分页结构中,除了 PAE 下的 PDPT 表为 32 字节大小,其他所有的表每个都是 4KB 大小。

  • 32-paging:每个结构4KB,即每个结构包含1024个条目,每个条目4字节。
  • PAE、4/5-level:除了 PAE 下的 PDPT 表为 32 字节大小(包含4个条目),其他所有的表每个都是 4KB 大小(包含512个条目),每个条目 8 字节。

表 4-2 给出了每级分页结构的名称,以及每级分页结构物理地址的来源。

63.png

上一个表结构提供下一个表结构的基址,4KB 页面大小的表结构中,将条目数据低 12 位清零即为下一个表结构的基址。

4/5-level 分页中,支持最大的物理寻址都为52位(4PB),但是虚拟地址限制不同:

  • 4-level 分页中在使用 48 位(256TB)虚拟地址。
  • 5-level 分页中,将 57 位的虚拟地址转译为最大 52 位的物理地址,可以访问整个物理地址线性空间。
  • 4-level、5-level 分页中都用转译到 52 位物理地址为例,MAXPHYADDR<52 时,52:MAXPHYADDR 使用 0 填充。

有两种形式的 4 级分页和 5 级分页,它们主要区别在于:线性地址转换时如何识别第一个分页结构(PLM4/5T表结构)。 另一种分页形式可以与 VMX 功能一起使用,称为虚拟机监控程序管理的线性地址转换 (HLAT)。此形式称为 HLAT 分页,仅在 VMX 非根操作中使用,并且仅当“启用 HLAT”虚拟机执行控件为 1 时才使用。HLAT 分页使用 VMCS 中称为 HLAT 指针 (HLATP) 的虚拟机执行控制字段查找第一个分页结构。第 4.5.3 节提供了详细信息

  1. 使用 CR3 寄存器来定位 PLM4/5T 表结构,称为普通分页(Ordinary paging )。
  2. hypervisor-managed 在 VMX 下 non-root 的分页,称为 HLAT 分页,仅用于4/5-level paging。

本文不涉及 VT 相关知识,主要分析 Ordinary paging。

2.2 4/5-level 地址转换图

65.png

66.png

2.3 PLM4T、PLM5T

在 Ordinary paging 下,使用 CR3 来定位 PLM4T、PLM5T,但是 CR3 的结构受到 CR4.PCIDE 位的影响(处理器上下文标志符,与处理器的缓存相关):

64.png

2.4 各级页表目录

Ignored、Reserved 必须为 0。

68.png

71.png

2.5 虚拟地址转换

  1. 查看当前处理器虚拟地址空间、物理地址空间大小。

    使用 CPUID.80000008H:EAX[bits 7:0-Physical,bits 15:8-Linear]。

    1
    2
    3
    4
    5
    6
    7
    8
    mov rax, 80000008h;
    cpuid;

    // 输出如下
    RAX 0000000000003024 //Physical:0x24=36(64GB), Linear:0x30=48(256TB)
    RBX 00007FFA00000000 //Reserved
    RCX 00007FFA00000000 //Reserved
    RDX 0000000000000000 //Reserved

    可以看到当前使用 48 位虚拟地址转译 36 位物理地址。

  2. 查看 IA32_EFER.LMA(MSR 0xC0000080 Bit 10) IA-32e 是否激活,如果激活再查看 CR4.LA57 是否启用 5-level 分页。否则是 4-level 分页。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    kd> rdmsr 0xC0000080
    msr[c0000080] = 00000000`00000d01
    kd> .formats 00000000`00000d01
    Evaluate expression:
    Hex: 00000000`00000d01
    Decimal: 3329
    Octal: 0000000000000000006401
    Binary: 00000000 00000000 00000000 00000000 00000000 00000000 00001101 00000001
    Chars: ........
    Time: Thu Jan 1 08:55:29 1970
    Float: low 4.66492e-042 high 0
    Double: 1.64474e-320
    • IA32_EFER.LME(8) = 1
    • IA32_EFER.LMA(10) = 1
    • IA32_EFER.ED(11) = 1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    kd> .formats cr4
    Evaluate expression:
    Hex: 00000000`00370678
    Decimal: 3606136
    Octal: 0000000000000015603170
    Binary: 00000000 00000000 00000000 00000000 00000000 00110111 00000110 01111000
    Chars: .....7.x
    Time: Thu Feb 12 01:42:16 1970
    Float: low 5.05327e-039 high 0
    Double: 1.78167e-317
    • CR4.LA57 = 0,说明当前是 4-level 分页。
  3. 使用 Windbg 查看物理地址。

    69.png

    可以看到地址为 0x0000020D'EAC52A40。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    kd> .formats 20DEAC52A40
    Evaluate expression:
    Hex: 0000020d`eac52a40
    Decimal: 2258796620352
    Octal: 0000000040675261225100
    Binary: 00000000 00000000 00000010 00001101 11101010 110 00101 00101010 01000000
    Chars: ......*@
    Time: Wed Jan 3 22:44:39.662 1601 (UTC + 8:00)
    Float: low -1.19179e+026 high 7.35682e-043
    Double: 1.11599e-311
  4. 解析各级页表偏移。

    1
    2
    3
    4
    5
    PML4E,9 bits: 0 0000 0100 -- 0x4;
    PDPTE,9 bits: 0 0011 0111 -- 0x37;
    PDE ,9 bits: 1 0101 0110 -- 0x156;
    PTE ,9 bits: 0 0101 0010 -- 0x52;
    Offset,12 bits: 1010 0100 0000 -- 0xa40;
  5. 获取 CR3。为 0x3cb63002。

    1
    2
    3
    4
    5
    kd> !process 0 0 notepad.exe
    PROCESS ffff9386ffaa4080
    SessionId: 1 Cid: 0858 Peb: 555dab6000 ParentCid: 0c5c
    DirBase: 3cb63002 ObjectTable: ffffe68e661f4d80 HandleCount: 237.
    Image: notepad.exe
  6. 解析物理地址。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    kd> !dq 3cb63000+0x4*8c
    #3cb63020 8a000000`35a05867 00000000`00000000
    #3cb63030 00000000`00000000 00000000`00000000
    #3cb63040 00000000`00000000 00000000`00000000

    kd> !dq 35a05000+0x37*8
    #35a051b8 0a000000`25786867 00000000`00000000
    #35a051c8 00000000`00000000 00000000`00000000
    #35a051d8 00000000`00000000 00000000`00000000

    kd> !dq 25786000+0x156*8
    #25786ab0 0a000000`70e29867 0a000000`20a28867
    #25786ac0 0a000000`13925867 00000000`00000000
    #25786ad0 00000000`00000000 00000000`00000000

    kd> !dq 70e29000+0x52*8
    #70e29290 81000000`3d28a867 00002000`17298880
    #70e292a0 00002000`5fd99880 00002000`59b29880
    #70e292b0 00002000`1452b880 00002000`0d180880

    kd> !dq 3d28a000+0xa40
    #3d28aa40 00320032`00300032 00300031`00310031
    #3d28aa50 00000000`00000000 00000000`00000000
    #3d28aa60 00000000`00000000 00000000`00000000

    kd> !db 3d28a000+0xa40
    #3d28aa40 32 00 30 00 32 00 32 00-31 00 31 00 31 00 30 00 2.0.2.2.1.1.1.0.
    #3d28aa50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
    #3d28aa60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

    kd> !du 3d28a000+0xa40
    #3d28aa40 "20221110"

3 访问权限

3.1 地址的读写与执行权限

当启用控制流强制技术(CET)的影子堆栈时,对线性地址的访问被叫做影子堆栈访问(shadow-stack accesses ),关于影子堆栈看卷1-18.2。和普通数据访问一样,对影子堆栈的访问也分为supervisor-mode和user-mode(根据CPL来决定)。但是WRUSS指令除外,尽管他仅在CPL = 0时才能执行,但处理器将其对影子堆栈的访问视为用户模式。

对数据的访问分为:

  • implicit access:隐式访问,比如 supervisor-mode 访问 GDT 加载段描述符、中断或异常传递时访问 IDT等。
  • explicit access:显示访问,除了上述的范围跟外,直接指定访问地址的属于显示访问。

70.png

处理器可以将分页结构条目中的数据缓存至 TLB 和 paging-structure caches,关于虚拟地址的访问权限,处理器有可能使用这些缓存结构中的访问权限控制位,而不是用分页结构条目中的访问权限控制位。具体见 4-10。如果通过修改分页结构的属性位来修改访问权限,由于缓存的存在,处理器可能不会将这种修改用于对后续线性地址地址的访问,不会影响访问权限。

上表对地址进行读写时,默认秘钥保护(Protection Key)允许,秘钥保护仅在 4-level、5-level 分页下使用,专门用来增强对线性地址读写权限,不涉及代码执行的保护(代码执行保护由IA32_EFER.NXE、XD位、SMEP决定)。

3.2 保护秘钥(Protection Key)

在 4-level、5-level 分页下,PTE 中 62:59 共 4 位为保护秘钥(Protection Keys ),控制着对该线性地址的读写权限。保护秘钥可用于用户模式地址和内核模式地址的保护,用 CR4 寄存器下面两个位进行控制是否开启:

  • CR4.PKE = 1,使用 PKRU 寄存器可以指定每个保护密钥关联的用户模式线性地址是否可以被读取或写入。
  • CR4.PKS = 1,使用 IA32_PKRS MSR(MSR index 6E1H)寄存器来指定每个保护密钥关联的内核模式线性地址是否可以被读取或写入。(IA32_PKRS MSR 高 32 位保留为 0。)

PKRU(protection-key rights for user pages),该寄存器是 32 位的,Protection key 共 4 位,其值为 0 <= i <16,在 PKRU 寄存器中计算方法是:

  • 偶数位:2i,禁止访问位,以 ADi 表示(access-disable)。
  • 奇数位:2i+1,禁止写入位,以 WDi 表示(write-disable)。

软件可以使用 RDPKRU、WRPKRU 指令来读写 PKRU 寄存器(ECX = 0条件下)。此外,PKRU 是 XSAVE-managed 管理的,因此可以使用 XSAVE 功能集的指令进行读写。处理器可以使用 RDMSR、WRMSR 来读写 IA32_PKRS MSR 寄存器,该寄存器不是 XSAVE 管理的。

保护秘钥仅仅对线性地址进行读写的访问保护,不会对地址进行取指令的执行保护。

保护秘钥对线性地址的访问控制:

  • CR4.PKE = 0,表示不开启用户模式秘钥保护,对线性地址的访问由章节 3.1 表决定。

  • CR4.PKE = 1,对用户模式线性地址的访问由 PKRU 寄存器控制:

    • ADi = 1,禁止访问数据。
    • WDi = 1,数据写入权限如下可能:
      • 用户模式权限禁止写入该地址。
      • 如果 CR0.WP = 1,禁止特权模式向用户地址写入。如果 CR0.WP = 0,保护秘钥 i 不影响特权模式向用户模式写入数据。
  • CR4.PKS = 0,表示不开启特权模式秘钥保护,对线性地址的访问由章节 3.1 表决定。

  • CR4.PKS = 1,对特权模式线性地址的访问由 IA32_PKRS MSR 寄存器控制:

    • ADi = 1,禁止访问数据。
    • WDi = 1,如果 CR0.WP = 1,禁止写入。如果 CR0.WP = 0,保护秘钥 IA32_PKRS.WDi 不影响写入数据。

保护秘钥同样适用于影子堆栈(Shadow-stack),像普通数据这样进行使用即可。

4 TLB 和 Cache

4.1 前置知识

每级页表转换时,都需要访问一次内存去查找该级页表目录转换的结果,4-level、5-level 分页地址转换时就需要进行多次内存访问,很耗时,所以处理器就在自身结构内设置了缓存(Cache),保存上一次虚拟地址转换的结果(物理地址),以加快地址转译过程。

  • 段的 Cache:实际上就是段寄存器隐藏部分,共 128-16=112 位。
  • 页的 Cache:分为两类缓存,TLB 和 Paging-Structure Cache。

TLB(Translation Lookaside Buffers)就是 Cache 的一类。通过 TLB,处理器可以绕过内存里的 table 和 table entry,直接在 Cache 里查找页的转换后的结果(即 page frame 信 息),这个结果包括了最终的物理页面基址和页的属性。

在 Intel64 里实现了两类缓存:

  1. TLB(Translation Lookaside Buffers):保存虚拟地址转换的物理页面地址结果。
  2. Paging-Structure Cache:保存各级页表条目的信息。

关于 Page Number、Page Offset、page Frame 如下图。

72.png

TLB、Paging-Structure Cache 是与 PCID相关联的。PCID(Process-context identifiers, PCIDs)是 12 Bits 的标志 ID,PCID 是否启用由 CR4.PCIDE 决定。

  • CR4.PCIDE = 1 时,CR3 低 12 位表示表示 PCID 值,此时 PCID > 0。
  • CR4.PCIDE = 0 时,不启用 PCID 机制,此时 PCID 值为 000H。

64.png

4.2 PCID 机制

PCID 是为 TLB 和 paging-structure cache 而产生的,CR3 低12 位的 PCID 值可以为不同的线性地址定义一个编号(同一个进程下不同的线性地址)。处理器会在 TLB 和 paging-structure cache 里为不同 PCID 编号的线性地址维护一份 cache,有着对应关系。

也就是说,当拿到一个线性地址时,先查看 CR4.PCIDE 是否启用,如果启用了此时 CR3 的低 12 位就为 PCID 值,有了 PCID 值才能找到与该值对应的 TLB 和 paging-structure cache。每一个 PCID 号对应一份 TLB 和 paging-structure cache,也就是说处理器中的 TLB 和 paging-structure cache 特定的 PCID 值是对应的。如下图。

73.png

可以看到:一个 PCID 下面虽然只对应一组 TLB、paging-structure cache,但是一个 TLB 就是一个 Buffer,里面包含了很多地址的缓存。所以一个 PCID 值其实是对应非常多的线性地址转译到物理地址的缓存的。

  • 一个 PCID 对应一组 TLB、paging-structure cache。
  • 注意:一个 TLB 包含许多条目,每个条目是一个线性地址(Page number)转译的物理地址相关信息。

关于 TLB 和 paging-structure cache 的刷新:

使用 MOV CR3, Reg 更新 CR3 的值时,根据新的 CR3 的低 12 位 PCID 值,对该 PCID 编号对应的 TLB 和 paging-structure cache 进行刷新,不会对其他 PCID 编号和新 PCID 号对应的全局(G)页面进行刷新。

4.3 TLB

TLB 分为两类:Data TLB、Instruction TLB(指令TLB)。

  • Instruction TLB:在 Instruction TLB 里只有 fetch 指令的地址。当 fetch 指令时处理器从 Instruction TLB entry 里找 page frame,否则作为读/写访问内 存时,则从 Data TLB entry 里找到 page frame。处理器对首次执行的代码 page 会在 Instruction TLB 里建立相应的 TLB entry(或者说 加载 TLB,相对应于 Segment 的加载)。Intel 明确说明了在 Instruction TLB entry 里无须 包含R/W 和D标志位。
  • Data TLB:当处理器对首次成功访问的 page frame(必须注意是成功访问,失败的访问不会建立 TLB entry),会在当前 PCID 的 TLB里建立相应的 TLB entry 来保存 page fame 或者建 立 global TLB entry 来保存 global page frame(当 page 的 G = 1 时)。

TLB 每个条目都包含 page number 和 物理页面的属性:

page frame 的访问权限是采用”从严“策略:

  • P 位:能在 TLB entry 中保存的 page frame, 必须是有效的 page frame,并且保留位为 0。一个无效的 page frame 处理器将不会在 TLB 中建立相应的 entry。
  • R/W、U/S :最终的 page frame 对应的属性是各级页表条目的逻辑与(AND) 关系。
  • XD:最终的 page frame 对应的属性是各级页表条目的逻辑或(OR) 关系。

一个处理器可能包含多种类型的 TLB(数据、指令等),如专门用于取指令(执行)的 TLB,这种 TLB 不需要包含 R/W、D 等标志。

处理器并非一定要有 TLB 的存在,没有 TLB 也可以正常转译地址。

4.4 Paging-Structure Cache

在 Intel64 中可以实现另一种 Cache 技术,处理器可以选择支持或不支持这种 Cache。 paging-structure cache 是与 TLB 互补的:TLB 是 cache 线性地址对应的 page frame, 而 paging-structure cache 则是 cache 页转换表中除 page frame 外的其他 table entry。

在 4-level 分页下,在 Paging-Structure Cache 里可以建立 3 种 table entry 的缓存:

  1. PML4E cache entry:只使用于 IA-32e paging 模式下。
  2. PDPTE cache entry: 只使用于 IA-32e paging 模式下,PAE paging 模式的 PDPTE 是在 PDPTE 寄存器里 cache。
  3. PDE cache entry:可以使用在 32 位 paging、 PAE paging 和 IA-32e paging 模式下。

75.png

4.5 TLB 和 Paging-Structure Cache 区别

TLB 是用来缓存虚拟地址与其对应的物理页面(转译结果)对应关系的。Paging-Structure Cache 则是用来缓存虚拟地址转移过程中的每级分页结构的。

  • TLB Entries:包含物理页面的 Page Frame,物理页面属性。
  • Paging-Structure Cache:包含每级页表结构,但是不包含最后一级页表(PTE或PS = 1的页表)5-level 分页下为 PLM5E、PLM4E、PDPTE、PDE。

Global page

当 PTE.G = 1 时,物理页面为全局页面,不会因为 MOV CR3, Reg 而对 TLB 中的信息进行刷新。这是因为处理器会为 PTE.G = 1 的页面建立独立的 TLB(依赖于PCID号):

74.png

立即刷新 TLB 和 Paging-Structure Cache

以下的刷新方法属于主动刷新(立即刷新)。

一、刷新指定线性地址对应的 TLB 条目 和 Paging-Structure Cache

使用 INVLPG 指令(该指令可以强制刷新全局页面。即 G = 1 页面)。

1
invlpg qword ptr[0x00000078`4cd260000];

这种方式与 PCID 无关。

二、刷新特定 PCID 号下所有 TLB 条目 和 Paging-Structure Cache

使用刷新 CR3 的方式。

有两种情况,分别是 CR3[63] = 0、CR3[63] = 1。

  1. CR3[63] = 0。

    1
    mov cr3,0x200001;

    该指令会刷新 PCID = 0001h 下 TLB 中的所有条目及 Paging-Structure Cache,但不会刷新全局页面及其他 PCID 对应的页面。

  2. CR3[63] = 1。

    1
    2
    mov rax,0x80000000`000200001;
    mov cr3, rax;

    不会刷新 TLB。

三、刷新全局页面和全局页面

使用更新 CR3 的方法不会刷新全局页面,但是可以通过 CR4.PGE 或 CR4.PCIDE 来进行刷新。

将 CR4.PGE 或 CR4.PCIDE 从 1 改为 0,然后再改为 1。

效果是:刷新所有全局页面的 TLB,刷新所有的 PCID 下的所有 TLB entry 和 Paging-Structure Cache。

四、根据 PCID 值来刷新 TLB

Intel64 提供了一个新指令 INVPCID 来根据提供的 invalidation type 和 descriptor 这两 个操作数做相应的刷新操作。INVPCID 指令是 PCID 功能配套指令,需要处理器支持, 从 CPUID.07H:EBX[10].INVPCID 位里查询是否获得支持。

INVPCID 指令可以做到上面所说的刷新 TLB entry 和 paging-structure cache entry 的功能,它可以提供 4 个 invalidation type:

  • 0 号 type 可以刷新单个 entry
  • 1号 type 可以刷新 PCID 下的所有 entry(除了 global TLB entry)
  • 2号 type 可以刷新所有 PCID 的所有 entry(包括 global TLB entry)
  • 3号 type 可以刷新所有 PCID 的所有 entry(除了 global TLB entry)

延迟刷新

在 Intel 手册里列举了一些不必立即进行主动刷新 TLB 的情形,主要是基于 table entry 的属性修改,我们可以认为这些修改操作属于 OS 内存管理模块的管理。

当遭遇 page frame 的:

R/W、U/S 标志由 0 修改为1,XD 标志由 1 修改为 0 时,如果尝试对不可写的 page 进行写,不可执行的 page 进行执行,以及使用 User 权限进行访问将产生 #PF 异常。于是,系统在 对 #PF handfer 处理中,可以对情况做出判断,然后在 #PF handler 里再做出选择是否刷新 TLB entry 和 Page-Structure Cache entry 的操作。在后续的执行中,TLB entry 已经被刷新为正确的。

相反,R/W、U/S 标志由 1 修改为0,XD 标志由 0 修改为 1 时,OS 必须立即刷新 TLB entry 和 Page-Structure Cache entry,否则起不了相应的控制作用。

4.6 处理器使用缓存的顺序

处理器在访问内存时寻找目标 page frame 有严格的先后查找次序:

  1. 首先,在 TLB 里查找 page frame 信息,找到就直接访问内存,Physical address = page frame + Offset。
  2. 其次,当在 TLB 找不到时,如果 PCID>0 情况下,就在 Paging-Structure Cache 里逐级从 PDE-entry、 PDPTE-entry 及 PMLAE-entry 里查找。
  3. 最后在 Paging-Structure Cache 里也找不到时,就只好老老实实在内存里 walk 下去(说明该地址没有缓存)。

所有支持 4 级分页或 5 级分页的处理器都支持 PAT。因此,不支持 APT 的处理器仅适用于 32 位分页和 PAE 分页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
.text:00000001402EDF10 // =============== S U B R O U T I N E =======================================
.text:00000001402EDF10
.text:00000001402EDF10
.text:00000001402EDF10 MmIsAddressValidEx proc near // CODE XREF: MiCommitExistingVad+69E↑p
.text:00000001402EDF10 // MiCommitExistingVad+DE2↑p ...
.text:00000001402EDF10
.text:00000001402EDF10 var_30 = qword ptr -30h
.text:00000001402EDF10 var_28_PTE = qword ptr -28h
.text:00000001402EDF10 var_20_PDE = qword ptr -20h
.text:00000001402EDF10 var_18_PDPTE = qword ptr -18h
.text:00000001402EDF10 var_10_PLM4E = qword ptr -10h
.text:00000001402EDF10 arg_0 = qword ptr 8
.text:00000001402EDF10
.text:00000001402EDF10 // FUNCTION CHUNK AT .text:000000014045F898 SIZE 00000056 BYTES
.text:00000001402EDF10
.text:00000001402EDF10 mov [rsp+arg_0], rbx
.text:00000001402EDF15 push rdi
.text:00000001402EDF16 sub rsp, 20h
.text:00000001402EDF1A mov rax, rcx // 保存 VirtualAddress
.text:00000001402EDF1D mov r10, rcx
.text:00000001402EDF20 sar rax, 2Fh // 算术右移 47 位(高位补符号位)
.text:00000001402EDF20 // 判断线性地址是用户模式地址还是内核地址:
.text:00000001402EDF20 // - 第48位 = 1:内核模式地址
.text:00000001402EDF20 // - 第48位 = 0:用户模式地址
.text:00000001402EDF24 inc rax
.text:00000001402EDF27 cmp rax, 1
.text:00000001402EDF2B ja loc_1402EE034
.text:00000001402EDF31 mov rdx, rcx
.text:00000001402EDF34 mov rdi, 0FFFFF68000000000h // PTE_BASE
.text:00000001402EDF3E shr rdx, 9
.text:00000001402EDF42 mov rcx, 7FFFFFFFF8h
.text:00000001402EDF4C and rdx, rcx
.text:00000001402EDF4F mov rax, rdi // (VA >> 9) & 0x7FFFFFFFF8
.text:00000001402EDF52 add rdx, rax // rdx = PTE = (VA >> 9) & 0x7FFFFFFFF8 + PTE_BASE
.text:00000001402EDF55 mov [rsp+28h+var_28_PTE], rdx
.text:00000001402EDF59 shr rdx, 9
.text:00000001402EDF5D and rdx, rcx
.text:00000001402EDF60 mov rax, rdi
.text:00000001402EDF63 add rdx, rax // rdx = PDE = (PTE >> 9) & 0x7FFFFFFFF8 + PTE_BASE
.text:00000001402EDF66 mov [rsp+28h+var_20_PDE], rdx
.text:00000001402EDF6B shr rdx, 9
.text:00000001402EDF6F and rdx, rcx
.text:00000001402EDF72 mov rax, rdi
.text:00000001402EDF75 add rdx, rax // rdx = PDPTE = (PDE >> 9) & 0x7FFFFFFFF8 + PTE_BASE
.text:00000001402EDF78 mov [rsp+28h+var_18_PDPTE], rdx
.text:00000001402EDF7D shr rdx, 9
.text:00000001402EDF81 and rdx, rcx
.text:00000001402EDF84 mov rax, rdi
.text:00000001402EDF87 add rdx, rax // rdx = PLM4E = (PDPTE >> 9) & 0x7FFFFFFFF8 + PTE_BASE
.text:00000001402EDF8A mov r11, 0FFFFF6FB7DBED000h
.text:00000001402EDF94 mov [rsp+28h+var_10_PLM4E], rdx
.text:00000001402EDF99 mov edx, 4
.text:00000001402EDF9E mov rbx, 0FFFFF6FB7DBED7F8h
.text:00000001402EDFA8
.text:00000001402EDFA8 loc_1402EDFA8: // CODE XREF: MmIsAddressValidEx+BF↓j
.text:00000001402EDFA8 mov r9, [rsp+rdx*8+28h+var_30]
.text:00000001402EDFAD dec rdx
.text:00000001402EDFB0 mov rcx, [r9]
.text:00000001402EDFB3 mov rax, r11
.text:00000001402EDFB6 cmp r9, rax
.text:00000001402EDFB9 jb short loc_1402EDFC3
.text:00000001402EDFBB mov rax, rbx
.text:00000001402EDFBE cmp r9, rax
.text:00000001402EDFC1 jbe short loc_1402EDFDF
.text:00000001402EDFC3
.text:00000001402EDFC3 loc_1402EDFC3: // CODE XREF: MmIsAddressValidEx+A9↑j
.text:00000001402EDFC3 // MmIsAddressValidEx+D9↓j ...
.text:00000001402EDFC3 test cl, 1
.text:00000001402EDFC6 jz short loc_1402EE034
.text:00000001402EDFC8 test cl, cl
.text:00000001402EDFCA js short loc_1402EE01D
.text:00000001402EDFCC test rdx, rdx
.text:00000001402EDFCF jnz short loc_1402EDFA8
.text:00000001402EDFD1
.text:00000001402EDFD1 loc_1402EDFD1: // CODE XREF: MmIsAddressValidEx+113↓j
.text:00000001402EDFD1 // MmIsAddressValidEx+122↓j
.text:00000001402EDFD1 mov al, 1
.text:00000001402EDFD3
.text:00000001402EDFD3 loc_1402EDFD3: // CODE XREF: MmIsAddressValidEx+126↓j
.text:00000001402EDFD3 mov rbx, [rsp+28h+arg_0]
.text:00000001402EDFD8 add rsp, 20h
.text:00000001402EDFDC pop rdi
.text:00000001402EDFDD retn
.text:00000001402EDFDD // ---------------------------------------------------------------------------
.text:00000001402EDFDE db 0CCh
.text:00000001402EDFDF // ---------------------------------------------------------------------------
.text:00000001402EDFDF
.text:00000001402EDFDF loc_1402EDFDF: // CODE XREF: MmIsAddressValidEx+B1↑j
.text:00000001402EDFDF test cs:MiFlags, 0C00000h
.text:00000001402EDFE9 jz short loc_1402EDFC3
.text:00000001402EDFEB mov rax, gs:188h
.text:00000001402EDFF4 mov r8, [rax+_KTHREAD.___u25.ApcState.Process]
.text:00000001402EDFFB cmp byte ptr [r8+390h], 1
.text:00000001402EE003 jz short loc_1402EDFC3
.text:00000001402EE005 test cl, 1
.text:00000001402EE008 jz short loc_1402EE034
.text:00000001402EE00A test cl, 20h
.text:00000001402EE00D jz loc_14045F898
.text:00000001402EE013 test cl, 42h
.text:00000001402EE016 jnz short loc_1402EDFC3
.text:00000001402EE018 jmp loc_14045F898
.text:00000001402EE01D // ---------------------------------------------------------------------------
.text:00000001402EE01D
.text:00000001402EE01D loc_1402EE01D: // CODE XREF: MmIsAddressValidEx+BA↑j
.text:00000001402EE01D mov rax, rdi
.text:00000001402EE020 cmp r10, rax
.text:00000001402EE023 jb short loc_1402EDFD1
.text:00000001402EE025 mov rax, 0FFFFF6FFFFFFFFFFh
.text:00000001402EE02F cmp r10, rax
.text:00000001402EE032 ja short loc_1402EDFD1
.text:00000001402EE034
.text:00000001402EE034 loc_1402EE034: // CODE XREF: MmIsAddressValidEx+1B↑j
.text:00000001402EE034 // MmIsAddressValidEx+B6↑j ...
.text:00000001402EE034 xor al, al
.text:00000001402EE036 jmp short loc_1402EDFD3
.text:00000001402EE036 MmIsAddressValidEx endp

https://tttang.com/archive/1589/

https://blog.betacat.io/post/introduction-to-paging/

https://blog.xpnsec.com/total-meltdown-cve-2018-1038/

https://bbs.pediy.com/thread-224348.htm

https://lzeroyuee.cn/old-blog/x64分页%20-%20获取PTE基址%20-%20lZeroyuee%27s%20Blog.html

https://connormcgarr.github.io/paging/

https://jhinxs.com/x64的一些特性/

http://www.kaikuoyun.com/thread-1048-1-1.html

https://bbs.pediy.com/thread-269402.htm