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에 대한 정보를 확인하려면 아래의 링크를 통해 확인할 수 있다.

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 이다.

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 파일이 존재하는 것을 볼 수 있다. 이것과 같이 UefiLessonsUefiLessons/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를 사용할 수 있게 되었다. 따라서 해당 파일에 아래와 같이 추가한다.

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.infUefiLessonsPkg.dec 의 경로를 추가하지 않았기 때문이다. 따라서 [Pakages]에 추가한다.

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

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

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

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

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

Last updated