4. 라이브러리와 Hello World

UEFI/EDKII 에서는SystemTable 포인터와 SystemTable->BootServices 그리고 주요 함수 매개변수인 ImageHandle을 사용하게 된다.

따라서 이러한 변수의 접근성을 위해 EDKII 라이브러리에는 gST, gBS, gImageHandle이 전역 선언되어 있다. (https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c) (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiBootServicesTableLib.h)

EFI_HANDLE         gImageHandle = NULL;
EFI_SYSTEM_TABLE   *gST         = NULL;
EFI_BOOT_SERVICES  *gBS         = NULL;

EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Cache the Image Handle
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  //
  // Cache pointer to the EFI System Table
  //
  gST = SystemTable;
  ASSERT (gST != NULL);

  //
  // Cache pointer to the EFI Boot Services Table
  //
  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  return EFI_SUCCESS;
}

1. gST 전역변수를 이용한 문자열 출력하기

$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
...
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
+ gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
...

위와 같이 작성 후 다음과 같은 명령어를 이용해 빌드한다.

$ build --platform=UefiLessonsPkg/UefiLessonsPkg.dsc \
        --module=UefiLessonsPkg/HelloWorld/HelloWorld.inf \
        --arch=X64 \
        --buildtarget=RELEASE --tagname=GCC5

그럼 아래와 같은 빌드 에러가 발생한다.

error: ‘gST’ undeclared (first use in this function)

따라서 #include를 이용해 헤더에 라이브러리 정보를 포함 시켜야 한다.

$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
+ #include <Library/UefiBootServicesTableLib.h>
...

*.dsc 파일에는 위 라이브러리 정보가 이미 LibraryClasses에 포함되어 있으므로 정상적으로 빌드 할 수 있으며 완성된 코드는 아래와 같다.

#include <Library/UefiBootServicesTableLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
  gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
  return EFI_SUCCESS;
}

2. edk2 라이브러리 함수를 이용한 문자열 출력

$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
+ #include <Library/UefiLib.h>
  #include <Library/UefiBootServicesTableLib.h>
...
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
  gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
+ Print(L"Bye!\n");
...

Print 함수는 UefiLib 라이브러리에 포함되어 있으며 이에 대한 정보를 소스 코드와 *.dsc 파일에 포함해야 한다. (https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiLib.h)

만약 *.dsc 파일에 해당 정보를 기입하지 않을 경우 다음과 같은 에러러가 발생한다.

.c:821: undefined reference to `Print'
error 4000: Instance of library class [MemoryAllocationLib] is not found

따라서 다음과 같은 과정을 통해 LibraryClasses를 추가하도록 한다.

$ grep UefiLib -r ./ --include=*.inf | grep LIBRARY_CLASS
./MdePkg/Library/UefiLib/UefiLib.inf:  LIBRARY_CLASS                  = UefiLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE
$ grep MemoryAllocationLib -r ./ --include=*.inf | grep LIBRARY_CLASS | grep MdePkg
./MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|PEIM PEI_CORE SEC
./MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|DXE_SMM_DRIVER
./MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
$ vi UefiLessonsPkg/UefiLessonsPkg.dsc
-----
[LibraryClasses]
  ...
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf

또한 Hellworld.inf 에도 LibraryClasses 정보를 추가해준다.

$ vi UefiLessonsPkg/HelloWorld/HelloWorld.inf
-----
[LibraryClasses]
  UefiApplicationEntryPoint
+ UefiLib

이후 빌드를 진행하면 성공적인 결과를 얻을 수 있다.

$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/HelloWorld.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
                     
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 1 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> HelloWorld.efi
Hello World!
Hello again!
Bye!
FS0:\>

Last updated