앞서 생성한 패키지 내부에 HelloWorld
애플리케이션을 포함하도록 한다.
Copy $ mkdir UefiLessonsPkg/HelloWorld
$ vi UefiLessonsPkg/HelloWorld/HelloWorld.inf
-----
[Defines]
INF_VERSION = 1.25
BASE_NAME = HelloWorld
FILE_GUID = 2e55fa38-f148-42d3-af90-1be247323e30
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
[Sources]
HelloWorld.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiApplicationEntryPoint
$ vi UefiLessonsPkg/UefiLessonsPkg.dsc
-----
[Components]
UefiLessonsPkg/SimplestApp/SimplestApp.inf
+ UefiLessonsPkg/HelloWorld/HelloWorld.inf
HelloWorld.c
의 소스 코드는 아래와 같이 작성한다.
Copy $ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
return EFI_SUCCESS;
}
위와 같이 작성한 이유는 다음과 같다.
문자열을 출력하기 위해서는 애플리케이션의 Entry Point에 전달되는EFI_SYSTEM_TABLE
서비스를 이용해야 한다.
EFI_SYSTEM_TABLE
은 다음과 같이 구성되어 있으며 공식 UEFI 스펙에서 자세히 기술하고 있다.(https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf)
Copy typedef struct {
EFI_TABLE_HEADER Hdr;
CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
그 중에서 *ConOut
은 'Console Output'의 약자로 ConsoleOutHandle
과 연결된 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
인터페이스에 대한 포인터이다.
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
인터페이스에 대한 정보는 다음과 같다.
Copy typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
EFI_TEXT_RESET Reset;
EFI_TEXT_STRING OutputString;
EFI_TEXT_TEST_STRING TestString;
EFI_TEXT_QUERY_MODE QueryMode;
EFI_TEXT_SET_MODE SetMode;
EFI_TEXT_SET_ATTRIBUTE SetAttribute;
EFI_TEXT_CLEAR_SCREEN ClearScreen;
EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition;
EFI_TEXT_ENABLE_CURSOR EnableCursor;
SIMPLE_TEXT_OUTPUT_MODE *Mode;
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
출력 기능을 담당하는 OutputString
의 구현은 다음과 같이 정의되어 있다.
(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/SimpleTextOut.h)
Copy EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString()
Summary
Writes a string to the output device.
Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_TEXT_STRING) (
IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
IN CHAR16 *String
);
Parameters
This A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL instance.
String The Null-terminated string to be displayed on the output device(s).
또한 L""
은 CHAR16
을 의미하며 다른 프로세서 아키텍처에서 다른 유형 구현을 위한 프록시이다.
이 때문에 아키텍처별 다른 ProcessorBind.h
헤더 정보를 지정하여 빌드한다.
예로 X64경우에는 다음과 같든 헤더 정보를 이용한다(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/X64/ProcessorBind.h)
이제 빌드를 통해 정상적인 출력이 진행되는지 확인해보자
Copy $ 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
아래와 같은 결과를 얻을 수 있다.
Copy 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:\> HelloWorld.efi
Hello World!
FS0:\>