8. HandleProtocol API 함수 & ImageHandle 프로토콜을 통한 정보

HandleProtocol 함수에 대한 기본 사용 및 ImageHandle 프로토콜로부터의 정보 얻기

지난 장에서 발견한 ImageHandle과 관련된 프로토콜에 대해서 알아보자.

이전과 동일하게 grep명령어를 통해서 EDKII 소스 코드에서 해당 GUID가 어디서 사용되는지 확인해보겠다.

$ grep -i bc62157e -r ./ --exclude-dir=Build
./MdePkg/Include/Protocol/LoadedImage.h:    0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } \
./MdePkg/MdePkg.dec:  gEfiLoadedImageDevicePathProtocolGuid = { 0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf }}
$ grep -i 5B1B31A1 -r ./ --exclude-dir=Build
./MdePkg/Include/Protocol/LoadedImage.h:    0x5B1B31A1, 0x9562, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } \
./MdePkg/MdePkg.dec:  gEfiLoadedImageProtocolGuid    = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}

두가지 중 LoadedImageProtocol은 아래의 링크에서 찾아볼 수 있다. (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/LoadedImage.h)

///
/// Can be used on any image handle to obtain information about the loaded image.
///
typedef struct {
  UINT32            Revision;       ///< Defines the revision of the EFI_LOADED_IMAGE_PROTOCOL structure.
                                    ///< All future revisions will be backward compatible to the current revision.
  EFI_HANDLE        ParentHandle;   ///< Parent image's image handle. NULL if the image is loaded directly from
                                    ///< the firmware's boot manager.
  EFI_SYSTEM_TABLE  *SystemTable;   ///< the image's EFI system table pointer.

  //
  // Source location of image
  //
  EFI_HANDLE        DeviceHandle;   ///< The device handle that the EFI Image was loaded from.
  EFI_DEVICE_PATH_PROTOCOL  *FilePath;  ///< A pointer to the file path portion specific to DeviceHandle
                                        ///< that the EFI Image was loaded from.
  VOID              *Reserved;      ///< Reserved. DO NOT USE.

  //
  // Images load options
  //
  UINT32            LoadOptionsSize;///< The size in bytes of LoadOptions.
  VOID              *LoadOptions;   ///< A pointer to the image's binary load options.

  //
  // Location of where image was loaded
  //
  VOID              *ImageBase;     ///< The base address at which the image was loaded.
  UINT64            ImageSize;      ///< The size in bytes of the loaded image.
  EFI_MEMORY_TYPE   ImageCodeType;  ///< The memory type that the code sections were loaded as.
  EFI_MEMORY_TYPE   ImageDataType;  ///< The memory type that the data sections were loaded as.
  EFI_IMAGE_UNLOAD  Unload;
} EFI_LOADED_IMAGE_PROTOCOL;

UEFI 스펙(specification)에 따르면 아래와 같이 설명이 되어 있는 것을 볼 수 있다.

나머지 하나의 GUID는 EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL을 나타낸다. - 설치된 경우 로드된 Loaded Image Device Path Protocol은 PE/COFF 이미지가 EFI 부팅 서비스 LoadImage()를 통해 로드될 때 사용된 장치 경로를 지정한다. Loaded Image Device Path ProtocolDevice Path Protocol과 동일한 프로토콜 인터페이스를 사용한다. 두가지의 유일한 차이점은 GUID 값이 다르다는 것이다.

Loaded Image Device Path Protocol은 EFI 부팅 서비스 LoadImage()를 통해 로드된 PE/COFF 이미지의 이미지 핸들에 설치해야만하며 이미지 핸들이 설치되기 전에 EFI 부팅 서비스 LoadImage()에 대한 DevicePath의 매개변수로 지정된 장치 경로의 복사본이 만들어진다.

NULL DevicePath 파라미터를 사용하여 메모리의 버퍼에 대해 LoadImage()를 호출하는 것이 가능하며 이와 같은 경우에는 Loaded Image Device Path Protocol은 NULL 인터페이스 포인터와 함께 설치가 된다.

따라서 해당 프로토콜에 대한 정의를 찾기 위해서 EFI_DEVICE_PATH_PROTOCOL을 검색해야만 한다. (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/DevicePath.h)

해당 구조는 Length 바이트 뒤에 오는 실제 경로 데이터의헤더와 같다고 생각하면 된다.

이제 모든 프로토콜의 구조에 대해서 이해를 했으니 프로토콜 구조를 직접 사용하는 방법을 알아야 할 때다. 지난 장에서 사용한 방법은 기초적인 원리를 이해하기 위해서 사용한 것이다. 실제로 UEFI에는 프로토콜 구조를 가져오는 여러가지 기능이 존재하기 때문에 이를 이용하겠다.

EFI_BOOT_SERVICES.HandleProtocol() - 핸들이 프로토콜을 지원하는지 확인하기 위해 핸들로 질의한다. UEFI 문서:

해당 정보들을 가지고 새로운 ImageInfo 앱의 만들어보겠다.

몇가지 새로운 지식:

해당 코드의 빌드를 진행하면 아래와 같은 오류가 뜰 것이다.

에러를 해결하려면 .inf 파일에 프로토콜 섹션을 추가해야한다.

EDKII INF 파일의 [Protocols] 섹션은 모듈 개발자가 사용하는 전역 프로토콜 "C Names" 목록이다. "C Names"는 주로 EDKII 패키지 DEC 파일에 있는 프로토콜의 실제 GUID 값을 조회하여 파싱한 후 데이터 구조를 모듈의 AutoGen.c 파일로 내보낸다.(https://edk2-docs.gitbook.io/edk-ii-inf-specification/2_inf_overview/29_-protocols-_section)

## CONSUMES는 프로토콜의 사용을 나타내기 위해서 사용된다. 해당 값은 아무 정보도 가지고 있지 않으며 EDKII codebase를 확인하면 아래와 같은 예시를 확인할 수 있다.

프로토콜 섹션을 추가한 뒤 실행을 하면 정상적으로 빌드가 되는 것을 볼 수 있다. (GUID 변수는 빌드 시스템에서자동으로 생성되는 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/ImageInfo/ImageInfo/DEBUG/AutoGen.c 파일로 이동한다.)

실행 모습:

Last updated