在 LattePanda BIOS 插入自定义模块

UEFI 的一个优点是模块化,意思是你可以编写一个模块,编译后能够插入到已有的BIOS中并且执行。在此之前,每个 IVB 实现方式差异巨大,除了按照 PCI ROM 这种特别形式插入几乎没有办法实现通用。这次演示的就是在 EDK2环境下编写一个DXE 模块,然后插入到LattePanda中。

首先进行代码的设计,仿照\OvmfPkg\8254TimerDxe这个模块就可以写好。

代码非常简单,入口是InitializezMessage () 函数,进入之后注册OnMyReadyToBoot函数,当ReadyToBootEvent事件时触发之。代码如下:

EFI_STATUS
EFIAPI
InitializezMessage (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status=EFI_SUCCESS;
  EFI_EVENT   ReadyToBootEvent;

  DEBUG ((DEBUG_INFO, "UPassword DXE Loaded\n"));

  //
  // Register the event to reclaim variable for OS usage.
  //
  EfiCreateEventReadyToBootEx (
    TPL_NOTIFY,              
    OnMyReadyToBoot, 
    NULL,
    &ReadyToBootEvent  
    );  

  return Status;
}

这样,当BIOS 准备启动的时候就是跳入OnMyReadyToBoot() 函数,执行,在函数中只是循环显示一段字符串。

/**
  On Ready To Boot Services Event notification handler.

  Notify SMM variable driver about the event.

  @param[in]  Event     Event whose notification function is being invoked
  @param[in]  Context   Pointer to the notification function's context

**/
VOID
EFIAPI
OnMyReadyToBoot (
  IN      EFI_EVENT                         Event,
  IN      VOID                              *Context
  )
{
  DEBUG ((EFI_D_INFO, "Invoke OnMyReadyToBoot\n"));
  
  for (UINTN i=0;i<5;i++) {
	gST->ConOut->OutputString(gST->ConOut,L"\r\n delay from www.lab-z.com\r\n");
	gBS->Stall(1000000UL);
  }
	
  gBS->CloseEvent (Event);
}

然后将其加入编译,在\OvmfPkg\OvmfPkgX64.dsc 修改如下:

!ifdef $(CSM_ENABLE)
  OvmfPkg/8259InterruptControllerDxe/8259.inf
  OvmfPkg/8254TimerDxe/8254Timer.inf
!else
  OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf
!endif
  #LABZ_Debug_Start
  OvmfPkg/ zMessage / zMessage.inf
  #LABZ_Debug_End 
  OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf
  OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf

在 \OvmfPkg\OvmfPkgX64.fdf 修改如下:

!ifdef $(CSM_ENABLE)
  INF  OvmfPkg/8259InterruptControllerDxe/8259.inf
  INF  OvmfPkg/8254TimerDxe/8254Timer.inf
!else
  INF  OvmfPkg/LocalApicTimerDxe/LocalApicTimerDxe.inf
!endif
#LABZ_Debug_Start
INF  OvmfPkg/ zMessage / zMessage.inf
#LABZ_Debug_End
INF  OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf
INF  OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
INF  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf

使用如下命令在 QEMU 上测试:

qemu-system-x86_64 -bios ovmf.fd  -net none 

可以看到在启动过程中屏幕上输出了定义的字符串。

生成的FFS文件在\Build\OvmfX64\DEBUG_VS2019\FV\Ffs\00002237-2024-0513-A7F3-1449F9E0E4BDzMessage目录中(为了方便查找,我们在 INF中指定模块的 GUID 是 0000开头的)。文件名是 00002237-2024-0513-A7F3-1449F9E0E4BD.ffs。这就是我们编译后的模块。

接下来,使用 MMTOOL打开 LattePanda官方提供的 IFWI 文件:

我们选择在Volume 03:02-01 Index 137 的地方插入(理论上 UEFI模块在BIOS中的位置不影响加载顺序,这里只是为了方便查找随便选择的位置),下面就是完成后的结果:

最后,使用“Save Image as…”重新保存命名为 zMessage.fd,然后烧录到 LattePanda上即可。

本文提到的 UEFI 源代码:

UEFI 源代码生成的 FFS 文件:

最终加入模块的 LattePanda 的IFWI: