Step to UEFI (199)如何关闭 Cache

很多年前的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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注