29. EFI_ACPI_SDT_PROTOCOL 및 ShellLib를 사용하여 ACPI BGRT 테이블에서 BMP 이미지 저장하기

지난 장에서 시스템에 BGRT ACPI 테이블이 있음을 확인했다. ACPI 스펙에 따르면:

BGRT(Boot Graphics Resource Table)는 다음을 나타내는 메커니즘을 제공하는 선택적 테이블입니다.
부팅하는 동안 화면에 이미지가 그려지고 이미지에 대한 일부 정보가 표시됩니다. 
이미지가 화면에 그려지면 테이블이 작성됩니다.
이 작업은 펌웨어 구성 요소가 화면에 기록될 것으로 예상되고 이미지만 화면에 표시된 후 수행해야 합니다.
부팅 경로가 중단된 경우(예시: 키 누름), 현재 이미지가 무효화되었음을 OS에 나타내려면
Status 필드를 0으로 변경해야 합니다.

이 테이블에는 실제로 이미지 데이터에 대한 포인터가 존재한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Acpi63.h

///
/// Boot Graphics Resource Table definition.
///
typedef struct {
  EFI_ACPI_DESCRIPTION_HEADER Header;
  ///
  /// 2-bytes (16 bit) version ID. This value must be 1.
  ///
  UINT16                      Version;
  ///
  /// 1-byte status field indicating current status about the table.
  ///     Bits[7:1] = Reserved (must be zero)
  ///     Bit [0] = Valid. A one indicates the boot image graphic is valid.
  ///
  UINT8                       Status;
  ///
  /// 1-byte enumerated type field indicating format of the image.
  ///     0 = Bitmap
  ///     1 - 255  Reserved (for future use)
  ///
  UINT8                       ImageType;
  ///
  /// 8-byte (64 bit) physical address pointing to the firmware's in-memory copy
  /// of the image bitmap.
  ///
  UINT64                      ImageAddress;
  ///
  /// A 4-byte (32-bit) unsigned long describing the display X-offset of the boot image.
  /// (X, Y) display offset of the top left corner of the boot image.
  /// The top left corner of the display is at offset (0, 0).
  ///
  UINT32                      ImageOffsetX;
  ///
  /// A 4-byte (32-bit) unsigned long describing the display Y-offset of the boot image.
  /// (X, Y) display offset of the top left corner of the boot image.
  /// The top left corner of the display is at offset (0, 0).
  ///
  UINT32                      ImageOffsetY;
} EFI_ACPI_6_3_BOOT_GRAPHICS_RESOURCE_TABLE;

BGRT에서 이미지를 저장하는 애플리케이션을 만들어보자. 이번에는 BGRT 테이블을 얻기 위해 EFI_ACPI_SDT_PROTOCOL 프로토콜을 활용한다. 이후 ACPI 테이블 데이터를 얻기 위해서 해당 프로토콜에서 GetAcpiTable() 함수를 사용한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/AcpiSystemDescriptionTable.h

모든 테이블을 가져오기 위해서는 0부터 시작하여 인덱스 값을 증가시키는 GetAcpiTable을 호출해야 하며 함수는 EFI_SUCCESS를 반환해야 한다. 모든 성공 호출을 하면 ACPI 테이블의 공통 헤더에 대한 포인터를 얻을 수 있다.

EFI_ACPI_SDT_PROTOCOL을 사용하기 위해서는 이전과 동일하게 c 파일의 헤더와 inf 파일에 프로토콜을 추가해야 한다.

이를 토대로 코드를 짜면 아래와 같이 BGRT ACPI 테이블을찾을 수 있다.

이제 BGRT 테이블에서 이미지를 저장해야 한다. 현재 ACPI 스펙은 BMP 이미지 유형만 지원한다. https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#image-type

따라서 type이 실제로 BMP인지 확인을 우선시 해야한다.

이제 실제로 BMP 이미지를 저장하는 코드를 짜야한다. BGRT에는 이미지의 크기가 포함되지 않고 데이터에 대한오프셋만 포함되어 있다:ImageAddress.

그렇기 때문에 이미지의 크기를 구하기 위해서는 BMP header를 사용해야 한다. https://github.com/tianocore/edk2/blob/master/MdePkg/Include/IndustryStandard/Bmp.h

잊지 않고 헤더도 추가해줍니다.

이미지가 BMP인 것이 확인되면 서명(BM)을 확인하고 크기를 파싱 후 실제로 데이터를 파일에 쓰기가 가능하다. 여기서는 EFI_STATUS WriteFile(CHAR16* FileName, VOID* Data, UINTN* Size) 함수를 통해서 파일에 데이터를 쓰며 이에 대한 정의는 이후에 언급하겠다.

이전에는 EFI_SHELL_PROTOCOL을 사용하여 파일을 만들고 데이터를 기록했지만, 이번에는 ShellLib를 사용해보겠다. https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Library/ShellLib.h https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLib/UefiShellLib.c

이전과 동일하게 파일 열기, 쓰기, 닫기 기능이 필요하다.

ShellLib 사용으로써 얻는 점은 EFI_SHELL_PROTOCOL을 찾아서 수동으로 작업할 필요가 없다는 것이다.

WriteFile 함수는 아래와 같다.

ShellLib를 사용하기 위해서 헤더 파일을 추가하고 [Packages], [LibraryClasses]에도 ShellLib를 추가해야 한다.

그리고 우리가 사용하는 패키지지인 dsc 파일에 ShellLib 라이브러리 클래스를 포함되어야 한다.

하지만 이후에 빌드를 하더라도 아래와 같이 에러가 뜰 것이다. 왜냐하면 ShellLib를 사용하려면 다른 라이브러리들도 필요하기 때문이다.

이전에 사용했던 방식을 사용하면 간단하게 해결이 된다.

여러가지 LibraryClasses들을 추가 후 성공적으로 빌드가 되었다.

애플리케이션을 통해서 제작된 BGRT.bmp 사진을 보면 TianoCore 로고가 나오는 것을 볼 수 있다. https://raw.githubusercontent.com/tianocore/edk2/master/MdeModulePkg/Logo/Logo.bmp

BGRT 드라이버는 플래시 이미지를 사용하지 않고 실제로 부팅 화면을 가져와 BMP 이미지로 변환하기 때문에 파일 자체는 동일하지 않는다. BGRT가 플래시 이미지를 사용하는 대신 화면을 잡는 것이 이상하다고 생각되면 ACPI 스펙에서 BGRT가 정의된 방식을 다시 생각하는 것이 좋다.

바이너리 부팅 로고 이미지의 파일 GUID는 아래의 링크에 정의되어 있다. https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Logo/Logo.inf

일반적으로 BIOS에서 로고이미지에서 사용되는 GUID는 아래의 주소에 하드코딩 되어 있다. https://github.com/tianocore/edk2/blob/master/BaseTools/Source/Python/Eot/Report.py

만약 Logo 및 BGRT가 EDKII에서 어떤 방식으로 작동하는지 알고 싶다면 다음의 드라이버들을 확인하는 것이 좋다.

Last updated