INVD指令有什么用?
What use is the INVD instruction?
x86 INVD
使缓存层次结构无效 没有 将内容写回内存,显然。
很好奇,这样的指令有什么用?鉴于人们对不同缓存级别中的数据几乎没有控制,对可能已经被异步刷新的数据的控制甚至更少,这似乎只是一种确保您不知道什么数据的方法已保存在内存中。
问得好!
一个 use-case 对于这样的 blunt-acting 指令,如 invd
是在专门的或 very-early-bootstrap 代码中,例如当 RAM 的存在或不存在尚未确定时验证。由于我们可能不知道 RAM 是否存在、它的大小,或者即使它的特定部分是否正常运行,或者我们可能不想依赖它,所以 CPU 对它自己的缓存的一部分进行编程有时很有用作为 RAM 运行并按原样使用它。这叫做Cache-as-RAM(CAR)。在 CAR 的设置过程中,在使用 CAR 的同时,在 CAR 模式的拆卸过程中,内核必须确保没有任何内容从缓存中写入内存。
Cache-as-RAM
进入汽车
要设置 CAR,CPU 必须设置为 No-Fill 缓存模式 并且必须将要用于 CAR 的内存范围指定为 Write-Back。这可以通过以下步骤完成:
- 设置一个MTRR(内存类型范围寄存器)来指定一块内存作为WB(Write-Back) .
invd
整个缓存,防止任何缓存写入被写出造成混乱。
- 将缓存模式设置为普通缓存模式 (
cr0.CD=0
)。
- 在普通缓存模式下,"touch"内存跨度的所有缓存行通过读取用作CAR,从而用它填充缓存。只能在 普通缓存模式 .
下填充缓存行
- 将缓存模式设置为No-Fill缓存模式 (
cr0.CD=1
).
使用汽车
设置 CAR 的动机是,一旦设置,CAR 区域内的所有访问(read/write)都将命中缓存并且 不会 命中 RAM,但是缓存的内容将是可寻址的,就像 RAM 一样。因此,现在可以使用普通的 C 代码,而不是编写只使用寄存器的汇编代码,前提是它访问的堆栈和 local/global 变量被限制在 CAR 区域内。
退出汽车
当 CAR 退出时,这 "pseudo-RAM" 中产生的所有内存写入突然从缓存中喷出并丢弃 RAM 中相同地址的任何实际内容将是一件坏事。所以在退出CAR的时候,再次使用invd
彻底删除CAR区域的内容,然后设置Normal Cache Mode。
英特尔 80486 手册
英特尔暗示 Cache-as-RAM 在 i486 Microprocessor Programmer's Reference Manual 中的使用。 Intel 80486是第一个引入invd
指令的CPU。第 12.2 节阅读:
12.2 OPERATION OF THE INTERNAL CACHE
Software controls the operating mode of the cache. Caching can be enabled (its state following reset initialization), caching can be disabled while valid cache lines exist (a mode in which the cache acts like a fast, internal RAM), or caching can be fully disabled.
Precautions must be followed when disabling the cache. Whenever CD is set to 1, the i486 processor will not read external memory if a copy is still in the cache. Whenever NW is set to 1, the i486 processor will not write to external memory if the data is in the cache. This means stale data can develop in the i486 CPU cache. This stale data will not be written to external memory if NW is later set to 0 or that cache line is later overwritten as a result of a cache miss. In general, the cache should be flushed when disabled.
It is possible to freeze data in the cache by loading it using test registers while CD and NW are set. This is useful to provide guaranteed cache hits for time critical interrupt code and data.
Note that all segments should start on 16 byte boundaries to allow programs to align code/data in cache lines.
使用示例
coreboot有一个slide-deck presenting their implementation of CAR,它描述了上面的过程。 invd
指令用于幻灯片 21。
AMD 在 §2.3.3: Using L2 Cache as General Storage During Boot 中称其为 Cache-as-general-storage。
其他用途
在某些涉及 cache-incoherency 的情况下,由于 DMA(直接内存访问) 硬件,invd
也可能有用。
详细说明 IwillnotexistIdonotexists 对 CAR 的回答:
我认为它实际上是如何完成的
- 设置 WB MTRR(而不是 PAT,因为必须禁用分页;PAT 在 PMH 中运行,而 MTRR 在加载/存储缓冲区或 L1d 中运行)以覆盖所需的 CAR space。我认为 CAR 范围需要正确映射到 SAD 中,并且需要有一个后备存储——在这种情况下,使用映射到 SPI 闪存的地址范围是有意义的,代码实际上位于,然后您只需要将其读入缓存即可。您可以拥有这个范围 UC 并将其读入缓存,然后将其写入映射到其他地方的 WB 范围——可能不需要物理设备支持,但在 SAD 中确实需要映射,否则您可能在它到达 L3 时或仅在需要访问后备存储时获取 MCA。在切换到 CAR 模式之前,您实际上确实需要首先从该地址范围引入有效行(除非您可以对该范围执行无 RFO 写入,这样它就不需要引入任何行,并且
rep stos
应该使用像 ItoM 这样的无 RFO 协议,即它只发送无效而不是 RFO),所以我认为它需要映射到实际设备,如 SPI 闪存,因为从在 SAD 中有映射但在到达 IIO 或 IMC 时没有接收设备的设备,我想你会得到一个 MCA。如果您在执行此操作时实际上得到 0,或者使用 rep stos
,那么这将是一个可能的替代方案。
- 您不需要
INVD
缓存,除非您知道缓存中的某些内容将占用不必要的空间 space,在启动阶段通常不会出现这种情况在配置内存控制器和 RAM 内存映射之前。
- 将数据读入缓存
- CR0.CD = 1. 你可能只是禁用了 BSP 上的缓存,所以你这样做只会禁用 L1 和 L2(我不知道它是否禁用了 L2只是那个逻辑核心或两者兼而有之,我认为只是那个逻辑核心,因为我相信 CD=1 是存储缓冲区中 load/store 本身的 属性,并且它不会为此驱逐任何东西 load/store). L3 永远不会发生逐出,因为缓存未在 CAR 模式下填充,但您可能会遇到在启用 CAR 之前写入期间推送到 L3 的内容。 L3 不能被禁用,因为没有
ia32_misc_enable
s 并且会正常运行,但核心可能会通知 L3 切片 CBo 它正在以无填充缓存模式运行,并且不会填充 L3错过了。或者,它可以通过向 Cbo 发出 UC 请求来完全绕过 L3。我不知道是否为写入命中保持了高速缓存一致性,以及是否处理了来自其他内核的一致请求,但是 couple of sources 声称是这种情况,但是当您只知道 BSP 处于活动状态时,这无关紧要。如果它确实命中了较低的缓存,那么它不会将其提升到更高的位置。
- 在CAR中,读取命中将从缓存行中读取,读取未命中将从内存中读取但不会填充缓存行。写入命中将写入缓存行,写入未命中将写入内存而不是填充缓存。这样,就不会发生驱逐(来自 L1/L2)并造成严重破坏。在此状态期间使用
INVD
将有效地完全禁用缓存,因为缓存中没有任何内容,因此 INVD
不会在 CAR 期间使用,仅在最后使用。
- 完成后,
INVD
。如果 CAR 区域由 SPI flash 支持,它不会 100% 匹配 SPI flash 的内容。它仍然在写我。一些随机代码的堆栈,你不想不小心写回 SPI 闪存,所以你必须 INVD
而不是 WBINVD
,如果 SPI 闪存拒绝写入,你会得到一个MCA可能。事实上,当第一次提到 CAR 时,该指令是在 486 上引入的,这表明该指令是为此目的而引入的,我认为没有其他用例。
- CR0.CD = 0
在 Intel 上,CAR 由微代码设置为 运行 启动 ACM,因此它不需要特定的宏指令。然而,INVD
和 CAR 在内存控制器初始化之前由 ACM 本身和 SEC 内核使用,这当然需要 INVD
宏指令。我必须检查 SEC 核心是否启用了 CAR 或者它是否已经启用,但我知道包含 SEC + PEI 的 IBB 块在 L3 中。
需要提及的重要一点是,当您将代码加载到缓存中时,您需要确保它被推出 L1d 并进入 L2,否则指令缓存将无法访问它。这可以通过加载代码然后加载大于 L1d 大小的内容来实现(这是共享的,而不是在线程之间静态分区,因此它需要大于 L1d 的完整大小)。我认为这是因为 L1d 与 L1i 不一致,虽然有一种叫做 SMC 的东西,所以我不确定它的真实程度。
x86 INVD
使缓存层次结构无效 没有 将内容写回内存,显然。
很好奇,这样的指令有什么用?鉴于人们对不同缓存级别中的数据几乎没有控制,对可能已经被异步刷新的数据的控制甚至更少,这似乎只是一种确保您不知道什么数据的方法已保存在内存中。
问得好!
一个 use-case 对于这样的 blunt-acting 指令,如 invd
是在专门的或 very-early-bootstrap 代码中,例如当 RAM 的存在或不存在尚未确定时验证。由于我们可能不知道 RAM 是否存在、它的大小,或者即使它的特定部分是否正常运行,或者我们可能不想依赖它,所以 CPU 对它自己的缓存的一部分进行编程有时很有用作为 RAM 运行并按原样使用它。这叫做Cache-as-RAM(CAR)。在 CAR 的设置过程中,在使用 CAR 的同时,在 CAR 模式的拆卸过程中,内核必须确保没有任何内容从缓存中写入内存。
Cache-as-RAM
进入汽车
要设置 CAR,CPU 必须设置为 No-Fill 缓存模式 并且必须将要用于 CAR 的内存范围指定为 Write-Back。这可以通过以下步骤完成:
- 设置一个MTRR(内存类型范围寄存器)来指定一块内存作为WB(Write-Back) .
invd
整个缓存,防止任何缓存写入被写出造成混乱。- 将缓存模式设置为普通缓存模式 (
cr0.CD=0
)。 - 在普通缓存模式下,"touch"内存跨度的所有缓存行通过读取用作CAR,从而用它填充缓存。只能在 普通缓存模式 . 下填充缓存行
- 将缓存模式设置为No-Fill缓存模式 (
cr0.CD=1
).
使用汽车
设置 CAR 的动机是,一旦设置,CAR 区域内的所有访问(read/write)都将命中缓存并且 不会 命中 RAM,但是缓存的内容将是可寻址的,就像 RAM 一样。因此,现在可以使用普通的 C 代码,而不是编写只使用寄存器的汇编代码,前提是它访问的堆栈和 local/global 变量被限制在 CAR 区域内。
退出汽车
当 CAR 退出时,这 "pseudo-RAM" 中产生的所有内存写入突然从缓存中喷出并丢弃 RAM 中相同地址的任何实际内容将是一件坏事。所以在退出CAR的时候,再次使用invd
彻底删除CAR区域的内容,然后设置Normal Cache Mode。
英特尔 80486 手册
英特尔暗示 Cache-as-RAM 在 i486 Microprocessor Programmer's Reference Manual 中的使用。 Intel 80486是第一个引入invd
指令的CPU。第 12.2 节阅读:
12.2 OPERATION OF THE INTERNAL CACHE
Software controls the operating mode of the cache. Caching can be enabled (its state following reset initialization), caching can be disabled while valid cache lines exist (a mode in which the cache acts like a fast, internal RAM), or caching can be fully disabled.
Precautions must be followed when disabling the cache. Whenever CD is set to 1, the i486 processor will not read external memory if a copy is still in the cache. Whenever NW is set to 1, the i486 processor will not write to external memory if the data is in the cache. This means stale data can develop in the i486 CPU cache. This stale data will not be written to external memory if NW is later set to 0 or that cache line is later overwritten as a result of a cache miss. In general, the cache should be flushed when disabled.
It is possible to freeze data in the cache by loading it using test registers while CD and NW are set. This is useful to provide guaranteed cache hits for time critical interrupt code and data.
Note that all segments should start on 16 byte boundaries to allow programs to align code/data in cache lines.
使用示例
coreboot有一个slide-deck presenting their implementation of CAR,它描述了上面的过程。 invd
指令用于幻灯片 21。
AMD 在 §2.3.3: Using L2 Cache as General Storage During Boot 中称其为 Cache-as-general-storage。
其他用途
在某些涉及 cache-incoherency 的情况下,由于 DMA(直接内存访问) 硬件,invd
也可能有用。
详细说明 IwillnotexistIdonotexists 对 CAR 的回答:
我认为它实际上是如何完成的
- 设置 WB MTRR(而不是 PAT,因为必须禁用分页;PAT 在 PMH 中运行,而 MTRR 在加载/存储缓冲区或 L1d 中运行)以覆盖所需的 CAR space。我认为 CAR 范围需要正确映射到 SAD 中,并且需要有一个后备存储——在这种情况下,使用映射到 SPI 闪存的地址范围是有意义的,代码实际上位于,然后您只需要将其读入缓存即可。您可以拥有这个范围 UC 并将其读入缓存,然后将其写入映射到其他地方的 WB 范围——可能不需要物理设备支持,但在 SAD 中确实需要映射,否则您可能在它到达 L3 时或仅在需要访问后备存储时获取 MCA。在切换到 CAR 模式之前,您实际上确实需要首先从该地址范围引入有效行(除非您可以对该范围执行无 RFO 写入,这样它就不需要引入任何行,并且
rep stos
应该使用像 ItoM 这样的无 RFO 协议,即它只发送无效而不是 RFO),所以我认为它需要映射到实际设备,如 SPI 闪存,因为从在 SAD 中有映射但在到达 IIO 或 IMC 时没有接收设备的设备,我想你会得到一个 MCA。如果您在执行此操作时实际上得到 0,或者使用rep stos
,那么这将是一个可能的替代方案。 - 您不需要
INVD
缓存,除非您知道缓存中的某些内容将占用不必要的空间 space,在启动阶段通常不会出现这种情况在配置内存控制器和 RAM 内存映射之前。 - 将数据读入缓存
- CR0.CD = 1. 你可能只是禁用了 BSP 上的缓存,所以你这样做只会禁用 L1 和 L2(我不知道它是否禁用了 L2只是那个逻辑核心或两者兼而有之,我认为只是那个逻辑核心,因为我相信 CD=1 是存储缓冲区中 load/store 本身的 属性,并且它不会为此驱逐任何东西 load/store). L3 永远不会发生逐出,因为缓存未在 CAR 模式下填充,但您可能会遇到在启用 CAR 之前写入期间推送到 L3 的内容。 L3 不能被禁用,因为没有
ia32_misc_enable
s 并且会正常运行,但核心可能会通知 L3 切片 CBo 它正在以无填充缓存模式运行,并且不会填充 L3错过了。或者,它可以通过向 Cbo 发出 UC 请求来完全绕过 L3。我不知道是否为写入命中保持了高速缓存一致性,以及是否处理了来自其他内核的一致请求,但是 couple of sources 声称是这种情况,但是当您只知道 BSP 处于活动状态时,这无关紧要。如果它确实命中了较低的缓存,那么它不会将其提升到更高的位置。 - 在CAR中,读取命中将从缓存行中读取,读取未命中将从内存中读取但不会填充缓存行。写入命中将写入缓存行,写入未命中将写入内存而不是填充缓存。这样,就不会发生驱逐(来自 L1/L2)并造成严重破坏。在此状态期间使用
INVD
将有效地完全禁用缓存,因为缓存中没有任何内容,因此INVD
不会在 CAR 期间使用,仅在最后使用。 - 完成后,
INVD
。如果 CAR 区域由 SPI flash 支持,它不会 100% 匹配 SPI flash 的内容。它仍然在写我。一些随机代码的堆栈,你不想不小心写回 SPI 闪存,所以你必须INVD
而不是WBINVD
,如果 SPI 闪存拒绝写入,你会得到一个MCA可能。事实上,当第一次提到 CAR 时,该指令是在 486 上引入的,这表明该指令是为此目的而引入的,我认为没有其他用例。 - CR0.CD = 0
在 Intel 上,CAR 由微代码设置为 运行 启动 ACM,因此它不需要特定的宏指令。然而,INVD
和 CAR 在内存控制器初始化之前由 ACM 本身和 SEC 内核使用,这当然需要 INVD
宏指令。我必须检查 SEC 核心是否启用了 CAR 或者它是否已经启用,但我知道包含 SEC + PEI 的 IBB 块在 L3 中。
需要提及的重要一点是,当您将代码加载到缓存中时,您需要确保它被推出 L1d 并进入 L2,否则指令缓存将无法访问它。这可以通过加载代码然后加载大于 L1d 大小的内容来实现(这是共享的,而不是在线程之间静态分区,因此它需要大于 L1d 的完整大小)。我认为这是因为 L1d 与 L1i 不一致,虽然有一种叫做 SMC 的东西,所以我不确定它的真实程度。