> For the complete documentation index, see [llms.txt](https://bob-mcd-team.gitbook.io/uefi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bob-mcd-team.gitbook.io/uefi/uefi-development/boot-option/16.ovmf-image-boot-option.md).

# 16. OVMF 이미지 내에 부팅 옵션 추가

이전 장에서는 현재 부팅 옵션을 보여주는 애플리케이션을 개발했다.

```
FS0:\> ShowBootVariables.efi
Boot0000
UiApp
Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(462CAA21-7614-4503-836E-8AB6F4662331)

Boot0001
UEFI QEMU DVD-ROM QM00003
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)

Boot0002
UEFI QEMU HARDDISK QM00001
PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)

Boot0003*
EFI Internal Shell
Fv(7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1)/FvFile(7C04A583-9E3E-4F1C-AD65-E05268D0B4D1)
```

출력된 GUID에 대한 정보를 확인하려면 아래의 링크를 통해 확인할 수 있다.

* `FILE_GUID = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid`\
  <https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/Shell.inf>
* `FILE_GUID = 462CAA21-7614-4503-836E-8AB6F4662331` - UiApp module is driver for BDS phase\
  <https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Application/UiApp/UiApp.inf>

`7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1`는 OVMF 이미지의 펌웨어 볼륨에 대한 GUID이다.\
<https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.fdf>

\*.fdf 파일에는 SEC, PEI 또는 DXE 단계의 볼륨을 포함하여 모든 볼륨에 배치되는 드라이버와 애플리케이션의 리스트를 가지고 있다.

```
...

[FV.SECFV]
FvNameGuid         = 763BED0D-DE9F-48F5-81F1-3E90E1B1A015
BlockSize          = 0x1000
FvAlignment        = 16
...
INF <...>
...

[FV.PEIFV]
FvNameGuid         = 6938079B-B503-4E3D-9D24-B28337A25806
BlockSize          = 0x10000
FvAlignment        = 16
...
INF <...>
...

[FV.DXEFV]
FvForceRebase      = FALSE
FvNameGuid         = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1        ### <---- this is a GUID from our output
BlockSize          = 0x10000
FvAlignment        = 16
...
INF  ShellPkg/Application/Shell/Shell.inf          <--- this apps are placed in the FV.DXEFV firmware volume
...
INF  MdeModulePkg/Application/UiApp/UiApp.inf
```

전체 EDKII FDF 파일 사양 확인\
<https://edk2-docs.gitbook.io/edk-ii-fdf-specification/>

<https://github.com/tianocore/edk2/blob/master/ShellPkg/Application/Shell/Shell.inf>\
위 링크를 통해 EFI Shell은 간단한 `UEFI_APPLICATION`인 것을 알 수 있다. 지금까지 여러 애플리케이션을 만들어봤으니 이번에는 부팅 옵션에 애플리케이션을 추가한다.

부팅 옵션에 HelloWorld 애플리케이션을 부팅 옵션에 추가해볼 것이다. 따라서 EFI Shell 애플리케이션이 어떻게 OVMF에 추가되어 있는지 살펴본다.

먼저 OVMF 이미지에 애플리케이션을 추가해야 한다. `UefiLessonsPkg/HelloWorld/HelloWorld.inf` 경로를 `OvmfPkg/OvmfPkgX64.fdf` 파일에 있는 `[FV.DXEFV]`의 `ShellPkg/Application/Shell/Shell.inf` 아래에 추가한다.

```
[FV.DXEFV]
FvForceRebase      = FALSE
FvNameGuid         = 7CB8BDC9-F8EB-4F34-AAEA-3EE4AF6516A1
BlockSize          = 0x10000
FvAlignment        = 16
...

  INF  ShellPkg/Application/Shell/Shell.inf
+ INF  UefiLessonsPkg/HelloWorld/HelloWorld.inf
...
```

추가한 후 새로 OVMF를 빌드하려고 하면 아래와 같은 에러가 발생한다. 해당 오류는 `OvmfPkg/OvmfPkgX64.dsc`에 HelloWorld 애플리케이션에 대한 정보가 추가되지 않아 발생하는 에러이다.

```
$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
...
build.py...
 : error F001: Module /home/kostr/tiano/edk2/UefiLessonsPkg/HelloWorld/HelloWorld.inf NOT found in DSC file; Is it really a binary module?
...
```

`OvmfPkg/OvmfPkgX64.dsc`에 HelloWorld 애플리케이션에 대한 정보를 추가하기 위해 아래와 같이 `[Components`] 섹션에 있는 ShellPkg 아래에 처음에 만들었던 `HelloWorld.inf`의 경로를 추가한다. `Shell.inf의 {...}` 안에는 해당 애플리케이션에서 필요한 정보가 작성되어 있는 것이므로 `HelloWorld` 애플리케이션은 작성해야 할 정보가 없어 `HelloWorld.inf`의 경로만 작성해주면 된다.

```
[Components]
  ...
  ShellPkg/Application/Shell/Shell.inf {
   ...
  }
+ UefiLessonsPkg/HelloWorld/HelloWorld.inf
  ...
```

`HelloWorld.inf`의 경로를 추가하고 OVMF를 다시 빌드하면 에러없이 컴파일되는 것을 볼 수 있다.

```
$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
```

OVMF 이미지에 `HelloWorld` 애플리케이션이 추가되었지만 부팅 옵션에는 아직 보이지 않는다.

`ShowBootVariables.efi` 애플리케이션을 사용할 때 UEFI Shell 부팅 옵션에 `EFI Internel Shell`에 대한 설명이 있었다. 해당 부분을 EDKII 코드베이스를 통해 검색한다.

```
$ grep "EFI Internal Shell" -r ./ --exclude=Build
./ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c:    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
./OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c:    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
./OvmfPkg/Library/PlatformBootManagerLibBhyve/BdsPlatform.c:    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
./OvmfPkg/Library/PlatformBootManagerLibGrub/BdsPlatform.c:    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
```

결과로 나온 파일들 중에서 관심을 가져야 할 파일은 `./OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c` 이다.

```c
VOID
EFIAPI
PlatformBootManagerAfterConsole (
  VOID
  )
{
  ...
  //
  // Register UEFI Shell
  //
  PlatformRegisterFvBootOption (
    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
    );

  ...
}
```

`BdsPlatform.c` 파일에서 Guid를 통해 부팅 옵션에 등록하는 것을 볼 수 있다. `gUefiShellFileGuid`가 어디서 사용되는지 찾아본다.

```
$ grep "gUefiShellFileGuid" -r ./ --exclude=Build

./OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c:    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
./OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf:  gUefiShellFileGuid
./ShellPkg/Application/Shell/Shell.inf:  FILE_GUID                      = 7C04A583-9E3E-4f1c-AD65-E05268D0B4D1 # gUefiShellFileGuid
./ShellPkg/ShellPkg.dec:  gUefiShellFileGuid              = {0x7c04a583, 0x9e3e, 0x4f1c, {0xad, 0x65, 0xe0, 0x52, 0x68, 0xd0, 0xb4, 0xd1}}
```

출력된 결과를 보면 gUefiShellFileGuid가 작성된 `ShellPkg/ShellPkg.dec` 파일이 존재하는 것을 볼 수 있다. 이것과 같이 `UefiLessons`도 `UefiLessons/UefiLessons.dec` 파일을 생성해 주어야 한다. GUID는 `UefiLessons/HelloWorld/HelloWorld.inf`에 작성했던 값과 동일한 `gHelloWorldFileGuid`를 생성한다.

```
[Defines]
  DEC_SPECIFICATION              = 0x00010005
  PACKAGE_NAME                   = UefiLessonsPkg
  PACKAGE_GUID                   = 7e7edbba-ca2c-4177-a3f0-d3371358773a
  PACKAGE_VERSION                = 1.01

[Guids]
  # FILE_GUID as defined in UefiLessonsPkg/HelloWorld/HelloWorld.inf
  gHelloWorldFileGuid            = {0x2e55fa38, 0xf148, 0x42d3, {0xaf, 0x90, 0x1b, 0xe2, 0x47, 0x32, 0x3e, 0x30}}
  
```

EDKII 패키지 선언(.dec) 파일 형식 사양 : <https://edk2-docs.gitbook.io/edk-ii-dec-specification/>

`HelloWorld` 애플리케이션의 GUID를 `OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf` 파일에 추가한다.

```
[Guids]
 ...
  gUefiShellFileGuid
+ gHelloWorldFileGuid
```

이제 `OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c` 파일에서 `gHelloWorldFileGuid`를 사용할 수 있게 되었다. 따라서 해당 파일에 아래와 같이 추가한다.

```c
VOID
EFIAPI
PlatformBootManagerAfterConsole (
  VOID
  )
{
  ...
  //
  // Register UEFI Shell
  //
  PlatformRegisterFvBootOption (
    &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE
    );

  //
  // Register HelloWorld app
  //
  PlatformRegisterFvBootOption (
    &gHelloWorldFileGuid, L"Hello World", LOAD_OPTION_ACTIVE
    );
  ...
}
```

아직까지는 OVMF를 빌드하려고 하면 아래와 같은 에러가 발생한다.

```
$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
...

build.py...
/home/kostr/tiano/edk2/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf(87): error 4000: Value of Guid [gHelloWorldFileGuid] is not found under [Guids] section in
        /home/kostr/tiano/edk2/MdePkg/MdePkg.dec
        /home/kostr/tiano/edk2/MdeModulePkg/MdeModulePkg.dec
        /home/kostr/tiano/edk2/SourceLevelDebugPkg/SourceLevelDebugPkg.dec
        /home/kostr/tiano/edk2/OvmfPkg/OvmfPkg.dec
        /home/kostr/tiano/edk2/SecurityPkg/SecurityPkg.dec
        /home/kostr/tiano/edk2/ShellPkg/ShellPkg.dec
...
```

에러는`OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf`에 `UefiLessonsPkg.dec` 의 경로를 추가하지 않았기 때문이다. 따라서 `[Pakages]`에 추가한다.

```
[Packages]
  ...
  ShellPkg/ShellPkg.dec
  UefiLessonsPkg/UefiLessonsPkg.dec
```

OVMF를 다시 빌드하면 에러없이 되는 것을 확인할 수 있다.

```
$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
```

`ShowBootVariables.efi`를 통해 확인해보면 부팅 옵션에 `HelloWorld`가 추가된 것을 볼 수 있다.

<figure><img src="/files/Wy6OiFVuqn22pukN5jrS" alt=""><figcaption><p>ShowBootVariables.efi를 통한 부팅 옵션 확인</p></figcaption></figure>

추가적으로 추가한 `HelloWorld` 애플리케이션의 정보가 제대로 되어 있는지 확인하려면 `bcfg boot dump` 명령어를 통해 확인할 수 있다.

<figure><img src="/files/icJdtvAwjrQsaqtzdhEn" alt=""><figcaption><p>bcfg boot dump 결과</p></figcaption></figure>
