显然,x86 INVD
使缓存层次结构无效将内容写回内存。
我很好奇,这样的指示有什么用?鉴于人们几乎无法控制各种缓存级别中的数据,甚至对异步刷新的内容控制得更少,这似乎只是一种确保你不知道的方法。什么数据再次存储在内存中。
答案 0 :(得分:21)
很棒的问题!
像invd
这样的钝性指令的一个用例是专门的或非常早期的自举代码,例如当RAM的存在或不存在尚未被验证时。由于我们可能不知道RAM是否存在,它的大小,或者即使它的特定部分正常运行,或者我们可能不想依赖它,它有时对CPU编程自己的部分缓存很有用作为RAM运行并使用它。这称为缓存为RAM(CAR)。在设置CAR期间,在使用CAR时,以及在拆卸CAR模式期间,内核必须确保没有任何内容从该缓存写入内存。
要设置CAR,必须将CPU设置为 No-Fill Cache Mode ,并且必须将用于CAR的内存范围指定为 Write-Back 。这可以通过以下步骤完成:
invd
整个缓存,防止任何缓存的写入被写出并导致混乱。cr0.CD=0
)。cr0.CD=1
)。设置CAR的动机是,一旦设置,CAR区域内的所有访问(读/写)都将达到缓存,不命中RAM,但缓存的内容将是可寻址的,就像RAM一样。因此,不是编写只使用寄存器的汇编代码,而是现在可以使用普通的C代码,只要它访问的堆栈和局部/全局变量被限制在CAR区域内。
当退出CAR时,对于这个"伪RAM"中的所有内存写入都是一件坏事。突然从缓存中射出并将任何实际内容丢弃在RAM中的同一地址。因此,当退出CAR时,再次invd
用于完全删除CAR区域的内容,然后设置正常缓存模式。
英特尔提到i486 Microprocessor Programmer's Reference Manual中使用Cache-as-RAM。 Intel 80486是首先引入invd
指令的CPU。第12.2节阅读:
12.2内部缓存的操作
软件控制缓存的操作模式。可以启用缓存(复位初始化后的状态),缓存可以在存在有效缓存行时禁用(缓存充当快速内部RAM的模式),或者可以完全禁用缓存。
禁用缓存时必须遵循注意事项。只要CD设置为1,如果副本仍在缓存中,i486处理器将无法读取外部存储器。每当NW设置为1时,如果数据在缓存中,i486处理器将不会写入外部存储器。这意味着过时的数据可以在i486 CPU缓存中开发。如果NW稍后设置为0,或者由于高速缓存未命中而稍后覆盖该高速缓存行,则该过时数据将不会写入外部存储器。通常,禁用时应刷新缓存。
通过在设置CD和NW时使用测试寄存器加载数据,可以冻结缓存中的数据。这对于为时间关键的中断代码和数据提供有保证的高速缓存命中非常有用。
请注意,所有段都应从16字节边界开始,以允许程序将缓存行中的代码/数据对齐。
Libreboot有slide-deck presenting their implementation of CAR,描述了上述过程。幻灯片21上使用了invd
指令。
AMD称其为§2.3.3: Using L2 Cache as General Storage During Boot中的Cache-as-general-storage。
在某些由于 DMA(直接内存访问)硬件而导致缓存不一致的情况下,invd
也可能有用。
答案 1 :(得分:2)
详细说明 IwillnotexistIdonotexists 关于 CAR 的回答:
我认为它实际上是如何完成的
rep stos
,那么这将是一个可能的选择。rep stos
缓存,而在内存控制器和 RAM 之前的启动阶段通常不会出现这种情况内存映射已配置。INVD
并且会正常运行,但核心可能会通知 L3 切片 CBo 它在无填充缓存模式下运行,并且不会填充 L3在错过。或者,它可以通过向 Cbo 发出 UC 请求来完全绕过 L3。我不知道是否为写入命中保持缓存一致性,以及是否处理来自其他内核的一致性请求,但是 couple of sources 声称是这种情况,但是当您只知道 BSP 处于活动状态时,这无关紧要。如果它确实在较低的缓存中命中,那么它不会提高它。ia32_misc_enable
将有效地完全禁用缓存,因为缓存中不会命中任何内容,因此在 CAR 期间不使用 INVD
,仅在最后使用。INVD
。如果 CAR 区域由 SPI 闪存支持,它将不会 100% 匹配 SPI 闪存的内容。它仍然在写,即一些随机代码上的堆栈,您不想意外地将其写回 SPI 闪存,因此您必须INVD
而不是 INVD
,如果 SPI 闪存拒绝写入,您将得到一个马华大概。当第一次提到 CAR 时,该指令是在 486 上引入的,这表明该指令是为此目的而引入的,我认为没有其他用例。在 Intel 上,CAR 是通过微码设置来运行 Startup ACM,因此它不需要为此使用特定的宏指令。然而,WBINVD
和 CAR 在内存控制器初始化之前由 ACM 本身和 SEC 内核使用,这当然需要 INVD
宏指令。我必须检查 SEC 内核是否启用了 CAR 或者它是否已经启用,但我知道包含 SEC + PEI 的 IBB 块在 L3 中。
需要提及的重要一点是,当您将代码加载到缓存中时,您需要确保将其从 L1d 中推送到 L2 中,否则指令缓存将无法访问它。这可以通过加载代码然后加载大于 L1d 大小的东西来实现(它是共享的,而不是在线程之间静态分区,因此它需要大于 L1d 的完整大小)。我认为这是因为 L1d 与 L1i 不一致,尽管有一种叫做 SMC 的东西,所以我不确定它在多大程度上是正确的。