Step to UEFI (169)BMP 放在 EFI 文件中(下)

这篇文章的上篇是2015年8月写的【参考1】,三年后终于填坑。时隔久远先介绍一下目标:很多时候我们的代码需要显示图片,但是我们不希望图片作为单独的文件放在外面,因此需要想办法“打包”到 EFI中。第一种方法就是用工具将图片转换为 C 头定义,然后在编译中直接内存调用,这就是上篇使用的方法。可以看到比较麻烦也不算正规。最近偶然看到了EFI_HII_PACKAGE_IMAGES 的定义,琢磨了一下可以通过这样的方法将图片打包成为 EFI文件中的一个 Section,然后再进行解析调用。需要注意的是之前版本的 UDK 中并不支持这种方法,本文实验都是在 UDK2018 中进行的【参考2】。

先介绍一下原理(网上无法找到EFI_HII_PACKAGE_IMAGES 的使用方法,下面的都是实验得出的,如果有问题欢迎指出。【参考3】提供了一个关于 HII 的介绍,建议阅读本文之前先浏览此文):

1. 在代码中设定一个 *.idx 文件,其中用下面这样的形式给出要打包的 Image
#image IMG_LOGO TestImage.bmp

2. 其中的IMG_LOGO 是一个编号,代码中并没有定义,但是在 Build 之后的目录中能找到, 这应该是对应工具在编译过程中自动生成的

#define IMG_LOGO 0x0001

3. *.c 代码中一定要有 IMAGE_TOKEN (IMG_LOGO) 这样的定义,即使放在注释中也无所谓,但是一定要有,否则无法生成 2 提到的定义,应该是有工具会做扫描

4. 有了 HII 的定义,在编译时会在生成的 EFI 中加入一个 名称为 HII的Section,和通常的 Windows PE 处理 Resource 文件的方法一样

5. 在 Build 目录的 OUTPUT 下面,有一个生成的 hithii.rc,可以用 VS 直接打开之,看到的内容和上面的是相同的

6. 使用 Hii Package List Protocol 可以取得 PackageListHeader

7. 取得了 PackageListHeader 之后整体格式如下

下面是手工分析的结果,涉及到的结构体可以在 UefiInternalFormRepresentation.h 中看到。

最终代码如下:
HIIImageTest.c

/** @file
  Logo DXE Driver, install Edkii Platform Logo protocol.

Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/


#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>

#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;
  EFI_HII_IMAGE_BLOCK         *ImageBlocktType;
  EFI_HII_IIBT_IMAGE_24BIT_BASE  *ImageData;
  EFI_GRAPHICS_OUTPUT_PROTOCOL   *GraphicsOutput;
  UINT16        i,j;
  UINT8*        RGB32;
  
        //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 :\n   GUID=[%g] Length=[%X]\n",
                      PackageListHeader->PackageListGuid,
                      PackageListHeader->PackageLength);
     
        //Step2. Parser HII Image
        ImageHeader=(EFI_HII_IMAGE_PACKAGE_HDR*)(PackageListHeader+1);
        Print(L"Image Header:\n Length=[%d]  Type=[%d]\n",ImageHeader->Header.Length,ImageHeader->Header.Type);
        Print(L"ImageOffset %d  PalOffset %d\n",ImageHeader->ImageInfoOffset,ImageHeader->PaletteInfoOffset);
     
        ImageBlocktType=(EFI_HII_IMAGE_BLOCK *)(ImageHeader+1);
        Print(L"ImageBlockType %x\n",*ImageBlocktType);
     
        ImageData=(EFI_HII_IIBT_IMAGE_24BIT_BASE *)(ImageBlocktType+1);
        Print(L"ImageData:\n  Width=[%d]   Heigth=[%d]\n",ImageData->Width,ImageData->Height);

        Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
        if (EFI_ERROR(Status)) {
                Print(L"Loading Graphics_Output_Protocol error!\n");
                return EFI_SUCCESS;
        }

        //Step3. Get BMP Image
        RGB32 = (UINT8*)AllocatePool(ImageData->Width *  ImageData->Height *4);
        for (j=0;j<ImageData->Height;j++) {
                for (i=0;i<ImageData->Width;i++) {
                        RGB32[(j*ImageData->Width+i)*4]  =  ImageData->Bitmap[(j*ImageData->Width+i)].b;//Blue   
                        RGB32[(j*ImageData->Width+i)*4+1]=  ImageData->Bitmap[(j*ImageData->Width+i)].g; //Green 
                        RGB32[(j*ImageData->Width+i)*4+2]=  ImageData->Bitmap[(j*ImageData->Width+i)].r; //Red  
                        RGB32[(j*ImageData->Width+i)*4+3]=  0;
                }
        }   
        
        //Step4. Show the Image
        GraphicsOutput->Blt(
                        GraphicsOutput, 
                        (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
                        EfiBltBufferToVideo,
                        0, 0, 
                        200, 0, 
                        ImageData->Width, ImageData->Height, 0); 
        
        FreePool(RGB32);
        
  return Status;
}

HIIImageTest.idf
#image IMG_LOGO TestImage.bmp

#image IMG_LOGO TestImage.bmp

HIIImageTest.inf

## @file
#  The default logo bitmap picture shown on setup screen.
#
#  Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
#
#  This program and the accompanying materials
#  are licensed and made available under the terms and conditions of the BSD License
#  which accompanies this distribution. The full text of the license may be found at
#  http://opensource.org/licenses/bsd-license.php
#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
##

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = hit
  MODULE_UNI_FILE                = LogoDxe.uni
  FILE_GUID                      = F74D20EE-37E7-48FC-97F7-9B1047749C69
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain
  
#
#  This flag specifies whether HII resource section is generated into PE image.
#
  UEFI_HII_RESOURCE_SECTION      = TRUE

#
# The following information is for reference only and not required by the build tools.
#
#  VALID_ARCHITECTURES           = IA32 X64
#

[Sources]
  TestImage.bmp
  HIIImageTest.c
  HIIImageTest.idf

[Packages]
  MdeModulePkg/MdeModulePkg.dec
  MdePkg/MdePkg.dec
  StdLib/StdLib.dec   
  ShellPkg/ShellPkg.dec 
  
[LibraryClasses]
  DebugLib
  UefiApplicationEntryPoint
  PrintLib    
  UefiLib
  
[Protocols]
  gEfiHiiPackageListProtocolGuid     ## PRODUCES CONSUMES

运行结果(NT32 环境下运行结果,实体机上相同):

总结:从实验可以看到,需要处理的图片是作为PE 的 Resource 存在 EFI 中的,用这样的方法应该也会有机会将一些其他二进制文件打包到EFI 文件中。
完整的代码下载

参考:

  1. http://www.lab-z.com/bmpinefi/ Step to UEFI (59) ----- BMP 放在EFI文件中(上)
  2. https://github.com/tianocore/edk2/commit/333ba578fef4dff8921051410c5b56f63e7eeadb 提到这个事情。为了支持这个功能需要修改编译工具,应该从 UDK2017 就已经支持:

BaseTools: support generating image package from BMP/JPEG/PNG files

BaseTools add support to generating image package from BMP/JPEG/PNG

files.

1) New file type *.idf Image definition file to describe HII image

resource. It is the ASCII text file, and includes one or more "#image

IMAGE_ID [TRANSPARENT] ImageFileName".

2) New IMAGE_TOKEN macro is used to refer to IMAGE_ID.

3) New AutoGen header file $(MODULE_NAME)ImgDefs.h to include the

generated ImageId definition.

4) New $(MODULE_NAME)Idf.hpk or $(MODULE_NAME)Images are generated

as the output binary HII image package.

Cc: Liming Gao <liming.gao@intel.com>

Contributed-under: TianoCore Contribution Agreement 1.0

Signed-off-by: Yonghong Zhu <yonghong.zhu@intel.com>

Reviewed-by: Liming Gao <liming.gao@intel.com>

3. https://blog.csdn.net/jiangwei0512/article/details/80386945 BIOS/UEFI基础——UEFI用户交互界面使用说明之C代码实现(HII Package)

《Step to UEFI (169)BMP 放在 EFI 文件中(下)》有2个想法

  1. 请问一下,这里的格式转换是怎么知道的?有没有什么参考
    //Step2. Parser HII Image
    ImageHeader=(EFI_HII_IMAGE_PACKAGE_HDR*)(PackageListHeader+1);
    Print(L"Image Header:\n Length=[%d] Type=[%d]\n",ImageHeader->Header.Length,ImageHeader->Header.Type);
    Print(L"ImageOffset %d PalOffset %d\n",ImageHeader->ImageInfoOffset,ImageHeader->PaletteInfoOffset);

    ImageBlocktType=(EFI_HII_IMAGE_BLOCK *)(ImageHeader+1);
    Print(L"ImageBlockType %x\n",*ImageBlocktType);

    ImageData=(EFI_HII_IIBT_IMAGE_24BIT_BASE *)(ImageBlocktType+1);
    Print(L"ImageData:\n Width=[%d] Heigth=[%d]\n",ImageData->Width,ImageData->Height);

    1. 参考的主要都列在文章中了,其他的都是手工测试出来的,我感觉这部分经常遇到 Spec 写的很模糊的情况,

      主要靠猜想和实验了。

发表回复

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