很多年前的BIOS Setup中通常存在 Disable Cache的选项。时至今日这个选项已经消失的无影无踪,而当你提出这个问题时,人们第一个反应是“为什么要 Disable 它”?不过最近我遇到客户提出这样的需求,只得进行一番研究。
在 “Intel® 64 and IA-32 Architectures Software Developer’s Manual”【参考1】对此有明确表述:

上文提到的操作就是设置 CR0的2个 Bits ,再用 WBINVD 清除缓冲队列,最后Disable MTRR即可,具体代码如下:
AsmDisableCache(); //这个函数自带 WBINVD指令,所以这里无需再写一次
//
// Disable MTRRs
//
AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, 0);
运行之后会死在Application中。无奈中只得架设起来 DCI 对代码进行调试,起初我以为是在 Disable Cache之后发生了某些异常,但是追踪发现出现问题的时候并没有停在某个死循环上,反而不停有中断进入CommonExceptionHandlerWorker () 要求处理。之后继续试验,在前面提到的函数中加入了Debug Message 然后编译成 Debug 版本的BIOS 发现在 HPET Enable之后不断有中断进入处理,直到Disable Cache之后依然如此,这样的现象着实令人费解。猜测是时间中断导致的问题,但是并没有导致异常,一切看起来仍然按部就班在运行…… 终于,晚上灵光乍现,Cache对于系统来说是透明的,当Disable Cache 之后,系统唯一的变化是处理时间变长,因此有一种可能是:中断处理来不及处理导致中断事件堵塞了CPU,这样系统实际上是活着的没有异常,只是对于用户来说是Hang了。
经过研究,UEFI 的时间中是断EFI_TIMER_ARCH_PROTOCOL 提供的,具体可以由 8254 或者 HPET 来实现。现在的平台大多数都是后者。在 \MdePkg\Include\Protocol\Timer.h 有定义一些函数可供调用:
///
/// This protocol provides the services to initialize a periodic timer
/// interrupt, and to register a handler that is called each time the timer
/// interrupt fires. It may also provide a service to adjust the rate of the
/// periodic timer interrupt. When a timer interrupt occurs, the handler is
/// passed the amount of time that has passed since the previous timer
/// interrupt.
///
struct _EFI_TIMER_ARCH_PROTOCOL {
EFI_TIMER_REGISTER_HANDLER RegisterHandler;
EFI_TIMER_SET_TIMER_PERIOD SetTimerPeriod;
EFI_TIMER_GET_TIMER_PERIOD GetTimerPeriod;
EFI_TIMER_GENERATE_SOFT_INTERRUPT GenerateSoftInterrupt;
};
第一个试验是直接在 Application 中使用SetTimerPeriod 关闭时间中断,运行之后 Application 可以正常退出到 fs0: 这样的盘符下,但是 Shell 已经无法工作,因此就是说没有了这个时钟源, UEFI 好比失去了心跳;接下来就是先用GetTimerPeriod 取得当前设置的时间中断发生的间隔,直接扩大十倍再用SetTimerPeriod 回写。之后 Shell 仍然可以正常工作。
最后试验进入 OS,感觉一直死机,后来挂上 DCI 不断查看 EIP 是否有变,终于确认没有发生死机,只是慢而已。耐心等待,花费80分钟进入了 OS看到了桌面。随后又测试了几次都是差不多90分钟左右才进入桌面。这样证明能够正常工作。
完整代码如下:
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/IoLib.h>
#include <Library/CpuLib.h>
#include <Protocol/Timer.h>
#define MSR_IA32_MTRR_DEF_TYPE 0x000002FF
extern EFI_BOOT_SERVICES *gBS;
EFI_GUID gEfiTimerArchProtocolGuid = { 0x26BACCB3, 0x6F42, 0x11D4,
{ 0xBC, 0xE7, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};
/***
Print a welcoming message.
Establishes the main structure of the application.
@retval 0 The application exited normally.
@retval Other An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)
{
EFI_TIMER_ARCH_PROTOCOL *gTimer = NULL;
UINT64 t;
// Get Timer Arch Protocol
gBS->LocateProtocol(
&gEfiTimerArchProtocolGuid,
NULL,
(VOID **)&gTimer);
// Disable Timer
gTimer->GetTimerPeriod (gTimer, &t);
gTimer->SetTimerPeriod (gTimer, 0);
AsmDisableCache();
//
// Disable MTRRs
//
AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, 0);
Print(L"Cache disabled\n");
gTimer->SetTimerPeriod (gTimer, t*10);
Print(L"[CR0]=%lX\n",AsmReadCr0());
gTimer->GetTimerPeriod (gTimer, &t);
Print(L"Set a new timer for Shell\n");
return(0);
}
下载:
参考:
1. Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4