# 6. 핸들/프로토콜 데이터 베이스 구조 - Part 1

`ImageHandle` 예제 코드를 만들어 보겠다.\
우선은 `ImageHandle`에 사용될 GUID 값을 생성한다.

```
$ uuidgen
b68d3472-70c7-4928-841b-6566032e0a23
```

이후 이전의 HelloWorld 어플리케이션을 기반으로한 c파일과 inf파일을 생성한다.

```
$ cat UefiLessonsPkg/ImageHandle/ImageHandle.inf
[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = ImageHandle
  FILE_GUID                      = b68d3472-70c7-4928-841b-6566032e0a23
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  ImageHandle.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
```

```c
$ cat UefiLessonsPkg/ImageHandle/ImageHandle.c
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}
```

애플리케이션을 새로 만들기 때문에 UefiLessonsPkg.dsc파일에 추가해야 한다.

```
[Components]
  ...
+ UefiLessonsPkg/ImageHandle/ImageHandle.inf
```

이렇게 하면 환경 설정이 끝나게 된다.

c 파일의 메인 함수를 보면 2개의 인자로 `EFI_HANDLE ImageHandle`과 `EFI_SYSTEM_TABLE *SystemTable`을 받는 것을 볼 수 있다.

```c
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
```

`EFI_SYSTEM_TABLE`은 이전 장에서 언급을 했으니 이번에는 `ImageHandle`에 대해서 알아보겠다.

`ImageHandle` 매개변수는 프로그램 자체를 위한 이미지의 핸들이다. `ImageHandle`의 type인 `EFI_HANDLE`에 대한 정의는 아래의 링크에서 볼 수있다.\
(<https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiBaseType.h>)

```c
/// A collection of related interfaces.
///
typedef VOID                      *EFI_HANDLE;
```

단순히 `EFI_HANDLE` type은 void에 대한 포인터인 것을 알 수 있으며 그로 인해서 모든것에 대한 포인터가 될 수 있다.

실제로 위와 같은 type 구조의 포인터는 아래에 명시되어 있다.\
(<https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Core/Dxe/Hand/Handle.h>)

```c
#define EFI_HANDLE_SIGNATURE            SIGNATURE_32('h','n','d','l')

///
/// IHANDLE - contains a list of protocol handles
///
typedef struct {
  UINTN               Signature;
  /// All handles list of IHANDLE
  LIST_ENTRY          AllHandles;
  /// List of PROTOCOL_INTERFACE's for this handle
  LIST_ENTRY          Protocols;
  UINTN               LocateRequest;
  /// The Handle Database Key value when this handle was last created or modified
  UINT64              Key;
} IHANDLE;
```

해당 구조를 보면 `LIST_ENTRY가` 존재하는데 이는 이중연결리스트(double-linked list)로 구성이 된다. 그 덕분에 시스템 상의 모든 핸들로 이동이 가능하며 이러한 특성으로 인하여 이는 핸들 데이터베이스라고 불린다.\
(<https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Base.h>)

```c
///
/// LIST_ENTRY structure definition.
///
typedef struct _LIST_ENTRY LIST_ENTRY;

///
/// _LIST_ENTRY structure definition.
///
struct _LIST_ENTRY {
  LIST_ENTRY  *ForwardLink;
  LIST_ENTRY  *BackLink;
};
```

이 핸들에는 연결된 프로토콜 목록이 존재한다.\
(프로토콜은 인터페이스와 같이 기능및 데이터 필드의 집합이다.)\
핸들에 연결된 프로토콜 목록을 추적하기 위해서 해당 아래의 필드가 사용된다.

```c
/// List of PROTOCOL_INTERFACE's for this handle
LIST_ENTRY          Protocols;
```

이는 위에서 언급한 것과 같이 다음 구조를 가리키는 이중연결리스트(double-linked list)이다.

```c
#define PROTOCOL_INTERFACE_SIGNATURE  SIGNATURE_32('p','i','f','c')

///
/// PROTOCOL_INTERFACE - each protocol installed on a handle is tracked
/// with a protocol interface structure
///
typedef struct {
  UINTN                       Signature;
  /// Link on IHANDLE.Protocols
  LIST_ENTRY                  Link;
  /// Back pointer
  IHANDLE                     *Handle;
  /// Link on PROTOCOL_ENTRY.Protocols
  LIST_ENTRY                  ByProtocol;
  /// The protocol ID
  PROTOCOL_ENTRY              *Protocol;
  /// The interface value
  VOID                        *Interface;
  /// OPEN_PROTOCOL_DATA list
  LIST_ENTRY                  OpenList;
  UINTN                       OpenListCount;

} PROTOCOL_INTERFACE;
```

`PROTOCOL_INTERFACE`는 핸들의 이중연결리스트(double-linked list)되어 있다. 그리고 다음 구조인`PROTOCOL_ENTRY`를 가리키고 있다.

```c
#define PROTOCOL_ENTRY_SIGNATURE        SIGNATURE_32('p','r','t','e')

///
/// PROTOCOL_ENTRY - each different protocol has 1 entry in the protocol
/// database.  Each handler that supports this protocol is listed, along
/// with a list of registered notifies.
///
typedef struct {
  UINTN               Signature;
  /// Link Entry inserted to mProtocolDatabase
  LIST_ENTRY          AllEntries;
  /// ID of the protocol
  EFI_GUID            ProtocolID;
  /// All protocol interfaces
  LIST_ENTRY          Protocols;
  /// Registerd notification handlers
  LIST_ENTRY          Notify;
} PROTOCOL_ENTRY;
```

`PROTOCOL_INTERFACE` 구조는 별도의 프로토콜 데이터베이스에 존재하는 실제 `PROTOCOL_ENTRY` 구조를 가리키는 프록시일 뿐입니다.

위에서 시사한 점들은 아래의 그림으로 통해서 한눈에 볼 수 있다.

<figure><img src="/files/XNMsWdCDjJrBSaqjKBQs" alt=""><figcaption></figcaption></figure>

* 모든 핸들은 각 `IHANDLE` 구조의 `AllHandles` 필드의 도움으로 인하여 상호 연결된다.
* 모든 프로토콜은 `PROTOCOL_ENTRY` 구조의 `AllEntries` 필드의 도움으로 상호 연결된다.
* 각 Handle에는 Link 필드의 녹색 선을 통해 내부적으로 서로 연결된 모든 `PROTOCOL_INTERFACES`의 이중 연결구조로 되어있다.
* 각 프로토콜 항목은 사용되는 모든곳(`PROTOCOL_INTERFACES`)을 효과적으로 알고 있다.

`EFI_HANDLE ImageHandle이 IHANDLE` 구조인지 확인해보기 위해서 `ImageHandle`을 `IHANDLE`로 캐스팅하고 서명을 출력해주겠다.

```c
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>


typedef struct {
  UINTN               Signature;
  /// All handles list of IHANDLE
  LIST_ENTRY          AllHandles;
  /// List of PROTOCOL_INTERFACE's for this handle
  LIST_ENTRY          Protocols;
  UINTN               LocateRequest;
  /// The Handle Database Key value when this handle was last created or modified
  UINT64              Key;
} IHANDLE;

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  IHANDLE* MyHandle = ImageHandle;
  Print(L"Signature: %c %c %c %c\n", (MyHandle->Signature >>  0) & 0xff,
                                     (MyHandle->Signature >>  8) & 0xff,
                                     (MyHandle->Signature >> 16) & 0xff,
                                     (MyHandle->Signature >> 24) & 0xff);

  return EFI_SUCCESS;
}
```

해당구조(`IHANDLE/PROTOCOL_ENTRY/PROTOCOL_INTERFACE`)들은 비공개 상태이며 `MdeModulePkg` 외부에서 사용을 못한다. 그렇기 때문에.재정의를 해줘야한다. 이외에도 사용을 위해서 재정의를 해준 예시가 아래의 링크에 존재한다.(<https://github.com/tianocore/edk2/blob/master/StandaloneMmPkg/Core/StandaloneMmCore.h>)

```bash
$ . edksetup.sh
$ build
$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/ImageHandle.efi ~/UEFI_disk/
$ qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
                     -drive format=raw,file=fat:rw:~/UEFI_disk \
                     -nographic \
                     -net none
```

```bash
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
      FS0: Alias(s):HD0a1:;BLK1:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
     BLK0: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
     BLK2: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 4 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> ImageHandle.efi
Signature: h n d l
```

<figure><img src="/files/VtlCjbUZbQsvodPsgfPI" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bob-mcd-team.gitbook.io/uefi/uefi-development/handle-protocol/6.handle-protocol-part-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
