如何在现代 x86/amd64 芯片上关闭 L1、L2、L3 CPU 缓存?

Posted

技术标签:

【中文标题】如何在现代 x86/amd64 芯片上关闭 L1、L2、L3 CPU 缓存?【英文标题】:How can the L1, L2, L3 CPU caches be turned off on modern x86/amd64 chips? 【发布时间】:2018-06-29 19:47:02 【问题描述】:

x86/x86_64 架构的每个现代高性能 CPU 都有一些数据缓存层次结构:L1、L2,有时还有 L3(在极少数情况下还有 L4),从主 RAM 加载/向主 RAM 加载的数据在某些缓存中缓存其中。

有时程序员可能希望某些数据不缓存在某些或所有缓存级别(例如,当想要 memset 16 GB 的 RAM 并将某些数据仍在缓存中时):有一些非临时(NT ) 类似 MOVNTDQA (https://***.com/a/37092http://lwn.net/Articles/255364/) 的说明

但是是否有一种编程方式(对于某些 AMD 或 Intel CPU 系列,如 P3、P4、Core、Core i* 等)来完全(但暂时)关闭部分或所有级别的缓存,以更改每个内存访问指令(全局或对于 RAM 的某些应用程序/区域)如何使用内存层次结构?例如:关闭L1,关闭L1和L2?或者将每个内存访问类型更改为“未缓存”UC(CR0 的 CD+NW 位???SDM vol3a 页 423424、425 和“三级缓存禁用标志,第 6 位IA32_MISC_ENABLE MSR(仅在基于 Intel NetBurst 微架构的处理器中可用)- 允许禁用和启用 L3 缓存,独立于 L1 和 L2 缓存。”)。

我认为此类操作将有助于保护数据免受缓存侧通道攻击/泄漏,例如窃取 AES 密钥、隐蔽缓存通道、Meltdown/Spectre。虽然这种禁用会产生巨大的性能成本。

PS:我记得很多年前在一些技术新闻网站上发布过这样的程序,但现在找不到了。它只是一个 Windows exe,将一些神奇的值写入 MSR 并让每个 Windows 程序在它之后运行非常慢。缓存一直关闭,直到重新启动或使用“撤消”选项启动程序。

【问题讨论】:

您好!检查英特尔 SDM 卷的“禁用和启用 L3 缓存”(及周边)和“MTRR”部分。 3a software.intel.com/en-us/articles/intel-sdm - xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/… xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/… "第三级缓存禁用标志(IA32_MISC_ENABLE MSR 的第 6 位)允许禁用和启用 L3 缓存,独立于 L1 和 L2 缓存”和 IA32_MISC_ENABLE,第 424 页“表 11-5。缓存操作模式”CR0 reg 的 CD 标志 enable/disable cache on intel 64bit machine: CD bit always set? 和 system becomes extremely slow after disable cache 的可能重复项。另外:禁用缓存用于攻击 SGX 飞地:Georgia/MS 2016 1611.06952v1 "Inferring Fine-grained Control Flow Inside SGX Enclaves with Branch Shadowing""Disabling cache. If we want to attack .. short loop" 还有:linuxquestions.org/questions/linux-kernel-70/… 和 memtest 的 cache_on/cache_off 函数:github.com/vathpela/memtest86-/blob/master/test.h#L206; software.intel.com/en-us/forums/…“缓存可能不被使用,但它们没有被禁用。”和software.intel.com/en-us/forums/…“CR0.CD 的范围是“核心”。”。也用于部分禁用:PCD "page-level cache disable (bit 4 of cr3)" 【参考方案1】:

The Intel's manual 3A,第 11.5.3 节,提供了一种全局禁用缓存的算法:

11.5.3 防止缓存

要在启用并收到缓存填充后禁用 L1、L2 和 L3 缓存,请执行以下步骤:

    进入无填充缓存模式。 (将控制寄存器 CR0 中的 CD 标志设置为 1,将 NW 标志设置为 0。 使用 WBINVD 指令刷新所有缓存。 禁用 MTRR 并将默认内存类型设置为未缓存或将所有 MTRR 设置为未缓存内存 类型(参见第 11.11.2.1 节中对 TYPE 字段和 E 标志的讨论, “IA32_MTRR_DEF_TYPE MSR”)。

在设置 CD 标志后必须刷新缓存(步骤 2)以确保系统内存一致性。如果缓存是 未刷新,读取时仍会发生缓存命中,并且将从有效的缓存行中读取数据。

上面列出的三个独立步骤的目的是满足三个不同的要求:(i) 停止使用新数据 替换缓存中的现有数据 (ii) 确保缓存中已经存在的数据被驱逐到内存中,(iii) 确保后续内存引用遵守 UC 内存类型语义。缓存的不同处理器实现 控制硬件可能允许这三个要求的软件实现的一些变化。请参阅下面的注释。

注意事项 设置控制寄存器 CR0 中的 CD 标志会修改处理器的缓存行为,如下所示 在表 11-5 中,但单独设置 CD 标志可能不足以在所有处理器系列中 强制所有物理内存的有效内存类型为 UC,也不强制严格内存 排序,由于不同处理器系列的硬件实现不同。强迫 UC 内存类型和所有物理内存上的严格内存排序,这足以 将所有物理内存的 MTRR 编程为 UC 内存类型或禁用所有 MTRR。

对于 Pentium 4 和 Intel Xeon 处理器,在完成上述步骤顺序之后 执行时,包含 WBINVD 指令末尾和 在 MTRRS 实际被禁用之前,可能会保留在缓存层次结构中。在这里,要从缓存中完全删除代码,必须在执行完之后执行第二条 WBINVD 指令 MTRR 已被禁用。

这是一个很长的引用,但归结为这段代码

;Step 1 - Enter no-fill mode
mov eax, cr0
or eax, 1<<30        ; Set bit CD
and eax, ~(1<<29)    ; Clear bit NW
mov cr0, eax

;Step 2 - Invalidate all the caches
wbinvd

;All memory accesses happen from/to memory now, but UC memory ordering may not be enforced still.  

;For Atom processors, we are done, UC semantic is automatically enforced.

xor eax, eax
xor edx, edx
mov ecx, IA32_MTRR_DEF_TYPE    ;MSR number is 2FFH
wrmsr

;P4 only, remove this code from the L1I
wbinvd

其中大部分不能从用户模式执行。


AMD's manual 2 在第 7.6.2 节中提供了类似的算法

7.6.2 缓存控制机制 AMD64 架构提供了多种机制来控制内存的可缓存性。这些将在以下部分中进行描述。

缓存禁用。 CR0 寄存器的第 30 位是高速缓存禁用位 CR0.CD。缓存已启用 当 CR0.CD 被清除为 0,当 CR0.CD 被设置为 1 时,缓存被禁用。当缓存被 禁用,读写访问主存。

软件可以在缓存仍然保存有效数据(或指令)时禁用缓存。如果读或写 当 CR0.CD=1 时命中 L1 数据缓存或 L2 缓存,处理器执行以下操作:

    如果缓存行处于修改或拥有状态,则将其写回。 使缓存行无效。 执行不可缓存的主内存访问以读取或写入数据。

如果在 CR0.CD=1 时指令提取命中 L1 指令缓存,某些处理器型号可能会读取 缓存的指令,而不是访问主存。当 CR0.CD=1 时,L2 的确切行为 L3 缓存取决于模型,并且可能因不同类型的内存访问而有所不同。

当 CR0.CD=1 时,处理器也会响应缓存探测。命中缓存的探测器会导致 处理器执行第 1 步。第 2 步(高速缓存行失效)仅在探测为 代表内存写入或独占读取执行。

直写禁用。 CR0 寄存器的第 29 位是非直写禁用位 CR0.NW。在 早期的 x86 处理器,CR0.NW 用于控制缓存的透写行为,并结合了 CR0.NW 和 CR0.CD 确定缓存操作模式。

[...]

在 AMD64 架构的实现中,CR0.NW 不用于限定缓存操作 CR0.CD建立的模式。

这转化为这段代码(与英特尔的代码非常相似):

;Step 1 - Disable the caches
mov eax, cr0
or eax, 1<<30
mov cr0, eax

;For some models we need to invalidated the L1I
wbinvd

;Step 2 - Disable speculative accesses
xor eax, eax
xor edx, edx
mov ecx, MTRRdefType  ;MSR number is 2FFH
wrmsr

缓存也可以在以下位置选择性地禁用:

页面级别,具有属性位 PCD(页面缓存禁用)[仅适用于 Pentium Pro 和 Pentium II]。 当两者都明确时,使用相关的 MTTR,如果设置了 PCD,则疼痛 页面级别,具有 PAT(页面属性表)机制。 通过使用缓存类型填充IA32_PAT 并使用位 PAT、PCD、PWT 作为 3 位索引,可以选择六种缓存类型(UC-、UC、WC、WT、WP、WB)中的一种。 使用 MTTR(固定或可变)。 通过将特定物理区域的缓存类型设置为 UC 或 UC-。

在这些选项中,只有页面属性可以暴露给用户模式程序(参见例如this)。

【讨论】:

那么,PCD 位和 MTTR 会禁用所有级别的缓存吗?选择性禁用呢? IA32_MISC_ENABLE MSR 的第 6 位是否会仅禁用 L3,使 L1 和 L2 保持在线,是否记录在案?是否有可用的 linux 内核模块源来测试 CR0.CD 禁用? @osgx IA32_MISC_ENABLE 的第 6 位应该出现在 Netburst 架构中(当时的 Pentium 4 和 Xeons)。但是,最近的 Xeon 具有 CAT(缓存分配技术),如第 17.18 章所述,将 LLC 的块分配给内核(包括根本没有块)。 MTTR 禁用所有缓存(处理器甚至不响应窥探)。 PCD 位的作用相同,但由于页面别名,我不确定命中是否一直到内存(我相信如此,任何行命中都应在任何访问时无效,并最终在从启用缓存的别名页面访问时重新填充) @osgx 我不知道任何内核模块,但有一个示例模块here。如果你不介意编译的话,我看看能不能把它变成一个缓存禁用模块。 @MargaretBloom 嗨。我试图运行您的示例并收到此错误“错误:符号'IA32_MTRR_DEF_TYPE'未定义”。我在 Intel x86_64 上测试过。 @Tigran84 你需要定义IA32_MTRR_DEF_TYPE,我没有因为代码是一个sn-p。注释还告诉了 MSR 编号,因此您可以在代码顶部添加 %define IA32_MTRR_DEF_TYPE 0x2ff(如果使用 NASM,其他汇编程序有不同的语法)或将 mov ecx, IA32_MTRR_DEF_TYPE 替换为 mov ecx, 0x2ff。 AMD 代码也是如此。

以上是关于如何在现代 x86/amd64 芯片上关闭 L1、L2、L3 CPU 缓存?的主要内容,如果未能解决你的问题,请参考以下文章

X86和X86_64和X64有什么区别?

在 Mac OS X 上使用现代 OpenGL [关闭]

如何不在其他机器(x86_64 Linux)上运行 Pi 特定代码? [关闭]

像Windows或Linux这样的现代操作系统如何知道特定于芯片组的内存映射?

现代芯片上的浮点数与整数算术性能

centos6.6-6.8的cobbler的kickstarts文件