13.ShellAppMain Entry point

이전 장에서는 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

Last updated