Step to UEFI (268)EFI 资源文件再研究

在之前的文章中介绍过将 BMP文件直接存放在EFI 文件中的方法 【参考1】,本文继续研究如何将其他格式的文件放在EFI 中的方法。

首先,我搜索了一下,没有发现直接将其他格式按照类似的方法放在EFI文件中的方法(从Win32编程的角度来说,这个是以Resource/资源文件的方式存放)。其次,如果将文件直接修改后缀为BMP,在编译过程中,工具会对资源文件进行检查,如果不是BMP将会报错。对于这种问题,可以将二进制文件通过加文件头的方式伪装成一个BMP文件。但是这样做估计会比较麻烦。

在阅读代码的过程中,处理BMP文件的代码位于 \BaseTools\Source\Python\AutoGen\GenC.py 文件中,可以看到支持三种图形格式,分别是PNG、JPG和BMP。对于后两者,都有针对这种类型的文件分析动作。

                            if File.Ext.upper() == '.PNG':
                                    TempBuffer = pack('B', EFI_HII_IIBT_IMAGE_PNG)
                                    TempBuffer += pack('I', len(Buffer))
                                    TempBuffer += Buffer
                                elif File.Ext.upper() == '.JPG':
                                    ImageType, = struct.unpack('4s', Buffer[6:10])
                                    if ImageType != b'JFIF':
                                        EdkLogger.error("build", FILE_TYPE_MISMATCH, "The file %s is not a standard JPG file." % File.Path)
                                    TempBuffer = pack('B', EFI_HII_IIBT_IMAGE_JPEG)
                                    TempBuffer += pack('I', len(Buffer))
                                    TempBuffer += Buffer
                                elif File.Ext.upper() == '.BMP':
                                    TempBuffer, TempPalette = BmpImageDecoder(File, Buffer, PaletteIndex, FileObj.TransParent)
                                    if len(TempPalette) > 1:
                                        PaletteIndex += 1
                                        NewPalette = pack('H', len(TempPalette))
                                        NewPalette += TempPalette
                                        PaletteBuffer += NewPalette
                                        PaletteStr = WriteLine(PaletteStr, '// %s: %s: %s' % (DecToHexStr(PaletteIndex - 1, 4), ID, DecToHexStr(PaletteIndex - 1, 4)))
                                        TempPaletteList = AscToHexList(NewPalette)
                                        PaletteStr = WriteLine(PaletteStr, CreateArrayItem(TempPaletteList, 16) + '\n')
                                ImageBuffer += TempBuffer
                                BufferStr = WriteLine(BufferStr, '// %s: %s: %s' % (DecToHexStr(Index, 4), ID, DecToHexStr(Index, 4)))
                                TempBufferList = AscToHexList(TempBuffer)
                                BufferStr = WriteLine(BufferStr, CreateArrayItem(TempBufferList, 16) + '\n')

                                StringH.Append(Line)
                                Index += 1

接下来的实验中,首先将【参考1】中的BMP文件后缀修改为 PNG ,然后重新编译。我们知道BMP文件开头的数值是 42 4D 76 6B,使用工具直接在生成EFI文件中搜索,可以看到如下数据 :

十六进制工具查看生成的EFI 文件

结合之前的知识,结合前面 PNG 打包的如下代码:

TempBuffer = pack('B', EFI_HII_IIBT_IMAGE_PNG)
                 TempBuffer += pack('I', len(Buffer))
                 TempBuffer += Buffer

猜测存储格式为:

EFI_HII_PACKAGE_LIST_HEADER
EFI_HII_IMAGE_PACKAGE_HDR
Type(EFI_HII_IIBT_IMAGE_PNG==0x19)
Length(len(Buffer)) //这里也就是我们放入的 BMP 的大小

为了便于验证,编写一个测试程序,它会将自身的资源文件保存为一个文件,这样可以方便的比较原始文件和EFI携带的资源文件:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Protocol/HiiDatabase.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/HiiImageEx.h>
#include <Protocol/PlatformLogo.h>
#include <Protocol/HiiPackageList.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/PrintLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include  <Library/ShellLib.h>

#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }
  
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;

//DO NOT REMOVE IMAGE_TOKEN (IMG_LOGO)

/**
  Entrypoint of this module.

  This function is the entrypoint of this module. It installs the Edkii
  Platform Logo protocol.

  @param  ImageHandle       The firmware allocated handle for the EFI image.
  @param  SystemTable       A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The entry point is executed successfully.

**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
)
{
  EFI_STATUS                  Status;
  
  
  
  EFI_HII_PACKAGE_LIST_HEADER *PackageListHeader;
  EFI_HII_IMAGE_PACKAGE_HDR   *ImageHeader;
  UINT8  					  *ImageData;
  UINTN						  FileSize;
  EFI_FILE_HANDLE             FileHandle;
  
        //Step1. Get Package List Header Address
        
        //
        // Retrieve HII package list from ImageHandle
        //
        Status = gBS->OpenProtocol (
                        ImageHandle,
                        &gEfiHiiPackageListProtocolGuid,
                        (VOID **) &PackageListHeader,
                        ImageHandle,
                        NULL,
                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
                        );
        if (EFI_ERROR (Status)) {
          Print(L"HII Image Package with logo not found in PE/COFF resource section\n");
          return Status;
        }
                        

        Print(L"PackageList :\nGUID=[%g] Length=[%X]\n",
                      PackageListHeader->PackageListGuid,
                      PackageListHeader->PackageLength);

        //Step2. Parser HII Image
        ImageHeader=(EFI_HII_IMAGE_PACKAGE_HDR*)(PackageListHeader+1);
		ImageData=(UINT8 *)(ImageHeader+1);
		
		FileSize=ImageData[1]+(ImageData[2]<<8)+(ImageData[3]<<16)+(ImageData[4]<<24);
		Print(L"Type: 0x%x  Size:%d\n",ImageData[0],FileSize);

		//Create a new file
		Status = ShellOpenFileByName(L"dump.bin", 
										(SHELL_FILE_HANDLE *)&FileHandle,
										EFI_FILE_MODE_READ |
										EFI_FILE_MODE_WRITE|
										EFI_FILE_MODE_CREATE, 
									0);  
		if(Status != RETURN_SUCCESS) {
				Print(L"CreatFile failed [%r]!\n",Status);
				return EFI_SUCCESS;
			}	
		Status = ShellWriteFile(FileHandle,
						&FileSize,
						&ImageData[5]);
  
		//Close the source file
		ShellCloseFile(&FileHandle);		
		
		Print(L"Dump.bin is generated!\n");
        
  return Status;
}

在WinHost.exe 的模拟环境种运行结果如下:

WinHost 下运行结果

使用 Beyond Compare 可以看到dump.bin 和TestImage.png内容完全相同。

结论:将要存储的二进制文件后缀修改为 PNG 之后可以直接存储。

完整代码下载:

参考:

1. https://www.lab-z.com/stu169bmp/  BMP 放在 EFI 文件中(下)

发表回复

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