15

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

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

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

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

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

4

1 回答 1

17

英特尔手册 3A的第 11.5.3 节提供了一种全局禁用缓存的算法:

11.5.3 防止缓存

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

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

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

上面列出的三个单独步骤的意图解决了三个不同的要求:(i) 停止使用新数据替换缓存中的现有数据 (ii) 确保将缓存中的数据逐出到内存中,(iii) 确保后续内存引用观察 UC 内存类型语义。缓存控制硬件的不同处理器实现可能允许这三个要求的软件实现有一些变化。请参阅下面的注释。

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

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

这是一个很长的报价,但归结为这段代码

;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 的手册 2在第 7.6.2 节中提供了类似的算法

7.6.2 高速缓存控制机制
AMD64 架构提供了许多控制内存高速缓存能力的机制。这些将在以下部分中进行描述。

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

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

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

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

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

于 2018-01-20T22:02:40.000 回答