이전 장에서는 EFI_SHELL_PARAMETERS_PROTOCOL
을 사용하여 애플리케이션에 매개변수를 받는 방법을 알아보았다. 이것도 맞는 방법이지만 더욱 간단한 방법이 있다.
더 쉽게 하기 위해서는 다음 형식의 Entry point를 사용하면 된다.
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
);
이런 방식을 쓰면 일반적인 C프로그래밍에서 동작하는 것처럼 Argc/Argv가 애플리케이션 Entry point로 바로 전달된다.
우리가 만들었던 HelloWorld
앱을 기반으로 간단한 SimpleShellApp
을 만들어보자.
수정을 거친 inf
파일은 다음과 같다.
[Defines]
INF_VERSION = 1.25
BASE_NAME = SimpleShellApp
FILE_GUID = 2afd1202-545e-4f8d-b8fb-bc179e84ddc8
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = ShellCEntryLib
[Sources]
SimpleShellApp.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiLib
ShellCEntryLib
주요 변경 사항:
ENTRY_POINT = ShellCEntryLib
가 [Defines]
섹션에 추가 되었다.
UefiApplicationEntryPoint
대신에 ShellCEntryLib
가 [LibraryClasses]
섹션에 추가되었다.
Shell C 라이브러리는 표준 Entry point가 있는 기본 UEFI 애플리케이션이다.
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
우리가 했던 것처럼 EFI_SHELL_PARAMETERS_PROTOCOL
로 수신된 매개변수를 파싱한 다음 애플리케이션의 Entry point를 호출함과 동시에 파싱된 모든 매개변수를 전달한다.
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
);
다음 링크에서 ShellAppMain의 소스코드를 확인 가능하다.
소스코드를 확인하면 우리가 썼던 HandleProtocol
대신에,
Status = gBS->HandleProtocol(
ImageHandle,
&gEfiShellParametersProtocolGuid,
(VOID **) &ShellParameters
);
OpenProtocol이라는 API를 사용한다. (우리가 썼던 것과 비교 가능하게 코드를 약간 수정하였다.)
Status = gBS->OpenProtocol(
ImageHandle,
&gEfiShellParametersProtocolGuid,
(VOID **)&ShellParameters,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
UEFI 스펙에 따르면 HandleProtocol
API는 오래되었으며 대신 OpenProtocol
을 사용해야 한다고 적혀있다. OpenProtocol
API는 더 많은 경우의 수를 다룰 수 있는 보다 일반적인 호출이다.
UEFI 드라이버 개발을 시작할 때 이런 모든 것들이 중요하다. 더 많은 정보를 얻기 위해서는 UEFI 스펙을 읽어 보는 것이 좋다. 현재는 UEFI 애플리케이션에 대해서 이 두 호출 방법이 동일하다는 사실을 알고 넘어가면 된다.
다시 코드로 돌아가서 우리가 필요한 ShellCEntryLib
를 검색해서 찾아본다.
$ grep ShellCEntryLib -r ./ --include=*.inf | grep LIBRARY_CLASS
./ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf: LIBRARY_CLASS = ShellCEntryLib|UEFI_APPLICATION UEFI_DRIVER
찾은 라이브러리 클래스를 UefiLessonsPkg/UefiLessonsPkg.dsc
에 추가한다.
ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
새로 만든 애플리케이션을 [Components]
섹션에 추가하는 것도 잊지말자.
UefiLessonsPkg/SimpleShellApp/SimpleShellApp.inf
이제 .c
파일을 살펴보자.
이제 SystemTable
을 사용할 수 없으므로 원래의 출력 방법을 사용할 수 없지만 gST
포인터의 도움으로 간단히 SystemTable
을 사용할 수 있다.
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
{
Print(L"Argc=%d\n", Argc);
// SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
Print(L"Bye!\n");
return 0;
}
이제 매개변수를 처리하는 코드를 작성해보자.
for (UINTN i=Argc; i>0; i--) {
Print(L"Arg[%d]=%s\n", Argc-i, Argv[Argc-i]);
}
애플리케이션을 빌드 후에 OVMF로 실행해보면, 다음과 같은 결과를 볼 수 있다.
FS0:\> SimpleShellApp.efi kkk ggg
Hello again!
Bye!
Arg[0]=FS0:\SimpleShellApp.efi
Arg[1]=kkk
Arg[2]=ggg
FS0:\> SimpleShellApp.efi
Hello again!
Bye!
Arg[0]=FS0:\SimpleShellApp.efi