💻
UEFI 프로젝트
  • 🧑‍🏫프로젝트 개요
  • 📖UEFI 개념
    • 1. BIOS의 과거
    • 2. UEFI 개념
    • 3. BIOS vs UEFI
    • 4. UEFI 부팅 단계
  • 🖥️UEFI 개발
    • UEFI 개발 시작하기
      • 0. EDK II 빌드 환경 구성
      • 1. 간단한 EFI application 만들기
      • 2. 간단한 Pkg 만들기
      • 3. Hello World 출력하기
      • 4. 라이브러리와 Hello World
      • 5. Conf를 통한 Build 단순화
    • 핸들 및 프로토콜
      • 6. 핸들/프로토콜 데이터 베이스 구조 - Part 1
      • 7. 핸들/프로토콜 데이터 베이스 구조 - Part 2
      • 8. HandleProtocol API 함수 & ImageHandle 프로토콜을 통한 정보
      • 9. ProtocolsPerHandle API를 통한 ImageHandle 프로토콜 가져오기
      • 10. EFI_STATUS 타입 과 EFI_ERROR 매크로
    • 메모리 맵
      • 11. EFI 메모리 맵 정보 얻기
      • 12. EFI 메모리 맵을 리눅스 커널 스타일로 바꾸기
    • 명령줄 인수를 받는 간단한 앱 만들기
      • 13.ShellAppMain Entry point
      • 14.gRT->GetNextVariableName API를 사용하여 모든 변수 이름 및 GUID 가져오기
    • 부팅 옵션
      • 15. gRT->GetVariable API를 사용하여 부팅 변수 가져오기 및 구문 분석
      • 16. OVMF 이미지 내에 부팅 옵션 추가
      • 17. 부팅 옵션에 WaitForEvent 함수 추가
      • 18. ReadKeyStroke 함수로 사용자 입력 처리
      • 19. bcfg 명령어를 사용한 부팅 옵션 수정
    • PCD
      • 20. PCD 소개
      • 21. PCD 변수에 대한 Overriding
      • 22. Feature Flag PCD와 BOOLEAN FixedAtBuild PCD의 비교
      • 23. PatchableInModule PCD 및 GenPatchPcdTable/PatchPcdValue 유틸리티를 통해 PCD를 변경하는 방법
      • 24. Dynamic/DynamiEx PCDs
      • 25. PCD 더 알아보기
    • 테이블
      • 26. EFI_CONFIGURATION_TABLE에서 참조되는 테이블
      • 27. dmem/EFI_SMBIOS_PROTOCOL/smbiosview를 통해서 SMBIOS 정보 가져오기
      • 28. EFI_SHELL_PROTOCOL을 통하여 ACPI 테이블을 파일에 저장하기
      • 29. EFI_ACPI_SDT_PROTOCOL 및 ShellLib를 사용하여 ACPI BGRT 테이블에서 BMP 이미지 저장하기
    • PCI
      • 30. PCI 루트 브리지 찾은 후 시스템의 모든 PCI 기능 가져오기
      • 31. ShellLib/PrintLib 함수를 사용해 PCI Vendor/Device 정보 가져오기
      • 32. EFI_PCI_IO_PROTOCOL을 사용해 PCI Option ROM 이미지 표시
      • 33. EfiRom 유틸리티를 사용한 PCI Option ROM 이미지 파싱 및 생성
    • 드라이버 및 라이브러리
      • 34. 간단한 UEFI 드라이버 생성
      • 35. 애플리케이션에서 사용할 간단한 라이브러리 생성
      • 36. Library의 constructor와 destructor, NULL Library
      • 37. Shell에 acpiview 명령을 추가하는 방법 조사
      • 38. 사용자 지정 프로토콜을 만들고 사용하기
      • 39. RegisterKeyNotify / UnrigisterKeyNotify 함수를 사용해 단축키 기능을 추가하는 드라이버 만들기
      • 40. Key #### NVRAM 변수
    • 디버그
      • 41. DEBUG 출력문 내부 구조와 DEBUG 문 제어를 위한 PCD 분석, 그리고 OVMF 부트 로그 가져오기
      • 42. GDB를 이용한 Driver/Application 및 OVMF Debug
    • HII
      • 43. HII 데이터베이스 개념 및 출력
      • 44. HII 데이터베이스 내부
      • 45. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 문자열 목록 게시
      • 46. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 47. EFI_HII_DATABASE_PROTOCOL의 NewPackageList를 사용하여 문자열 패키지가 포함된 HII 패키지 목록 게시
      • 48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기
      • 49.MODULE_UNI_FILE/PACKAGE_UNI_FILE/[UserExtensions.TianoCore."ExtraFiles"]의 도움으로 UNI 파일 선언하기
      • 50.UEFI_HII_RESOURCE_SECTION을 사용하여 문자열 패키지와 함께 HII 패키지 목록 게시하기
      • 51. UEFI APP에 메뉴얼 추가하기(shell의 -?와 help 옵션)
      • 52. Russian 글꼴 추가 - Part 1.
      • 53. Russian 글꼴 추가 - Part 2.
      • 54. EFI_HII_STRING_PROTOCOL의 NewString 및 SetString 함수를 사용하여 다른 언어에 대한 문자열 패키지를 동적으로 추가
      • 55. PlatformLangCodes EFI 변수 수정 및 다른 언어를 동적 추가하기
      • 56. 코드에서 FILE_GUID 및 BASE_NAME을 가져오기
    • VFR
      • 57. VFR을 사용해 간단한 폼 생성 및 EFI_FORM_BROWSER2_PROTOCOL.SendForm()를 통해 화면에 폼 표시하기
      • 58. VFR 요소 : subtitle 및 text
      • 59. 간단한 폼 애플리케이션을 UEFI 드라이버 Form으로 변환하기
      • 60. gRT->SetVariable() 함수를 사용한 UEFI 변수 생성, 변경 및 삭제
      • 61.dmpstore 명령을 사용하여 변수를 파일에 저장/로드하기
      • 62. UEFI Device path의 구조
      • 63. checkbox를 가진 HII 폼 만들기
      • 64. checkbox를 가진 HII폼 만들기
      • 65. VFR 추가 입력 요소 Part 1: number
      • 66. VFR 추가 입력 요소 Part 2: string
      • 67. VFR 추가 입력 요소 Part 3: date & time
      • 68. VFR 추가 입력 요소 Part 3: oneof & orderedlist
      • 69. VFR의 조건부 키워드
      • 70. VFR의 상수 및 연산자가 내장된 기본 조건문
      • 71. 기본 VFR 내장 문자열용 함수
      • 72. label 키워드를 이용하여 HII 양식에 동적 요소 추가하기
      • 73. VFR question 기본값 설정
  • 🔐UEFI 보안
    • 1. 개요
    • 2. 공격 벡터
    • 3. mitigation
    • 4. 정적 분석 방법
    • 5. 동적 분석 방법
Powered by GitBook
On this page
  • EDKII_VARIABLE_POLICY_PROTOCOL
  • Try to execute DisableVariablePolicy()
  1. UEFI 개발
  2. HII

55. PlatformLangCodes EFI 변수 수정 및 다른 언어를 동적 추가하기

Previous54. EFI_HII_STRING_PROTOCOL의 NewString 및 SetString 함수를 사용하여 다른 언어에 대한 문자열 패키지를 동적으로 추가Next56. 코드에서 FILE_GUID 및 BASE_NAME을 가져오기

Last updated 2 years ago

우리는 새 언어에 대한 글꼴을 추가해주는 방법을 학습했으며, 시스템의 일부 기존 언어에 대한 문자열을 동적으로 채우는 방법을 알게 되었다. 이번 장에서는 다른 언어를 동적으로 생성할 수 있는지 살펴보겠다.

Select Language 메뉴는 PlatformLangCodes EFI 변수 값에서 가능한 모든 언어 옵션을 가져온다. 그리고 현재 언어 옵션은 PlatformLang EFI 옵션에 반영된다.

이러한 옵션의 값은 서 PCD의 도움으로 설정된다.

  ## Default platform supported RFC 4646 languages: (American) English & French.
  # @Prompt Default Value of PlatformLangCodes Variable.
  gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLangCodes|"en;fr;en-US;fr-FR"|VOID*|0x0000001e

  ## Default current RFC 4646 language: (American) English.
  # @Prompt Default Value of PlatformLang Variable.
  gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang|"en-US"|VOID*|0x0000001f

에서 UiCreateLanguageMenu 함수의 실제 코드를 볼 수 있다.

PlatformLangCodes를 수정하고, 다른 언어를 추가할 수 있는지 살펴보자.

먼저 PlatformLangCodes 옵션의 값을 출력하는 애플리케이션을 만든다.

#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;

  CHAR8* LanguageString;
  Status = GetEfiGlobalVariable2(L"PlatformLangCodes", (VOID**)&LanguageString, NULL);
  if (EFI_ERROR(Status)) {
    Print(L"Error! Can't perform GetEfiGlobalVariable2, status=%r\n", Status);
    return Status;
  }
  Print(L"Current value of the 'PlatformLangCodes' variable is '%a'\n", LanguageString);

  return EFI_SUCCESS;
}

**
  Returns a pointer to an allocated buffer that contains the contents of a
  variable retrieved through the UEFI Runtime Service GetVariable().  This
  function always uses the EFI_GLOBAL_VARIABLE GUID to retrieve variables.
  The returned buffer is allocated using AllocatePool().  The caller is
  responsible for freeing this buffer with FreePool().
  If Name is NULL, then ASSERT().
  If Value is NULL, then ASSERT().
  @param[in]  Name  The pointer to a Null-terminated Unicode string.
  @param[out] Value The buffer point saved the variable info.
  @param[out] Size  The buffer size of the variable.
  @return EFI_OUT_OF_RESOURCES      Allocate buffer failed.
  @return EFI_SUCCESS               Find the specified variable.
  @return Others Errors             Return errors from call to gRT->GetVariable.
**/
EFI_STATUS
EFIAPI
GetEfiGlobalVariable2 (
  IN CONST CHAR16    *Name,
  OUT VOID           **Value,
  OUT UINTN          *Size OPTIONAL
  )

애플리케이션을 빌드하고 실행하면, PCD에서 값을 얻을 수 있다.

FS0:\> AddNewLanguage.efi
Current value of the 'PlatformLangCodes' variable is 'en;fr;en-US;fr-FR'

이제 변수 끝에 ;ru-RU를 추가하고 다시 작성해보자.

먼저 새 문자열을 구성하고 필요한 데이터로 채운다.

CHAR8* NewLanguageString = AllocatePool(AsciiStrLen(LanguageString) + AsciiStrSize(";ru-RU"));
if (NewLanguageString == NULL) {
  Print(L"Error! Can't allocate size for new PlatformLangCodes variable\n");
  FreePool(LanguageString);
  return EFI_OUT_OF_RESOURCES;
}

CopyMem(NewLanguageString, LanguageString, AsciiStrLen(LanguageString));
CopyMem(&NewLanguageString[AsciiStrLen(LanguageString)], ";ru-RU", AsciiStrSize(";ru-RU"));

Print(L"Set 'PlatformLangCodes' variable to '%a'\n", NewLanguageString);

이제 SetVariable 호출을 사용하여 변수를 업데이트한다.

Status = gRT->SetVariable (
              L"PlatformLangCodes",
              &gEfiGlobalVariableGuid,
              EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
              AsciiStrSize(NewLanguageString),
              NewLanguageString
              );
if (EFI_ERROR(Status)) {
  Print(L"Error! Can't set 'PlatformLangCodes' variable, status=%r\n", Status);
}

SetVariable은 Runtime 서비스이며, UEFI 스펙에서 해당 정의를 찾을 수 있다.

SetVariable()

Summary:
Sets the value of a variable.

Prototype:
typedef
EFI_STATUS
SetVariable (
 IN CHAR16 *VariableName,
 IN EFI_GUID *VendorGuid,
 IN UINT32 Attributes,
 IN UINTN DataSize,
 IN VOID *Data
 );

Parameters:
VariableName 	A Null-terminated string that is the name of the vendor’s variable. Each VariableName is unique for each VendorGuid.
VendorGuid 	A unique identifier for the vendor.
Attributes 	Attributes bitmask to set for the variable.
DataSize 	The size in bytes of the Data buffer.
Data 		The contents for the variable.

gRT는 UefiRuntimeServicesTableLib 라이브러리의 SystemTable->RuntimeServices에 대한 바로가기이다. 따라서 라이브러리 헤더 <Library/UefiRuntimeServicesTableLib.h>를 포함하는 것을 잊지 않도록 하자.

애플리케이션을 빌드하고 실행하면, 다음과 같은 결과를 얻을 수 있다.

FS0:\> AddNewLanguage.efi
Current value of the 'PlatformLangCodes' variable is 'en;fr;en-US;fr-FR'
Set 'PlatformLangCodes' variable to 'en;fr;en-US;fr-FR;ru-RU'
Error! Can't set 'PlatformLangCodes' variable, status=Write Protected

안타깝게도, 'PlatformLangCodes' EFI 변수가 쓰기 금지되어 있으므로 Runtime 시, 새 언어를 추가할 수 없다. 따라서 Runtime에 다른 로컬화 언어를 추가할 수 없다.

UEFI 스펙을 보면 다음과 같이 표시된다.

The PlatformLangCodes variable contains a null- terminated ASCII string representing the language
codes that the firmware can support. At initialization time the firmware computes the supported
languages and creates this data variable. Since the firmware creates this value on each initialization, its
contents are not stored in nonvolatile memory. This value is considered read-only. 

EDKII_VARIABLE_POLICY_PROTOCOL

PlatformLangCodes는 gEdkiiVariablePolicyProtocolGuid 프로토콜의 도움으로 수정할 수 있도록 잠겨 있다. 이는 변수에 대해 다른 정책을 설정하기 위한 사용자 정의 EDKII 프로토콜이다.

///
/// The read-only variables defined in UEFI Spec.
///
CHAR16  *mReadOnlyVariables[] = {
  EFI_PLATFORM_LANG_CODES_VARIABLE_NAME,             // L"PlatformLangCodes"  The language codes that the firmware supports
  EFI_LANG_CODES_VARIABLE_NAME,
  EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME,
  EFI_HW_ERR_REC_SUPPORT_VARIABLE_NAME,
  EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME
  };

...

  // Mark the read-only variables if the Variable Lock protocol exists
  //
  Status = gBS->LocateProtocol(&gEdkiiVariablePolicyProtocolGuid,
NULL, (VOID**)&VariablePolicy);
  DEBUG((DEBUG_INFO, "[BdsDxe] Locate Variable Policy protocol -
%r\n", Status));
  if (!EFI_ERROR (Status)) {
    for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {
      Status = RegisterBasicVariablePolicy(
                 VariablePolicy,
                 &gEfiGlobalVariableGuid,
                 mReadOnlyVariables[Index],
                 VARIABLE_POLICY_NO_MIN_SIZE,
                 VARIABLE_POLICY_NO_MAX_SIZE,
                 VARIABLE_POLICY_NO_MUST_ATTR,
                 VARIABLE_POLICY_NO_CANT_ATTR,
                 VARIABLE_POLICY_TYPE_LOCK_NOW
                 );
      ASSERT_EFI_ERROR(Status);
    }
  }

Try to execute DisableVariablePolicy()

DisableVariablePolicy()를 수행하여, VariablePolicyProtocol을 비활성화할 수 있다.

$ vi UefiLessonsPkg/AddNewLanguage/AddNewLanguage.c
---
...
#include <Protocol/VariablePolicy.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  ...

  EDKII_VARIABLE_POLICY_PROTOCOL* VariablePolicyProtocol;
  Status = gBS->LocateProtocol(&gEdkiiVariablePolicyProtocolGuid,
                               NULL, 
                               (VOID**)&VariablePolicyProtocol);
  if (EFI_ERROR(Status)) {
    Print(L"Error! Could not find Variable Policy protocol: %r\n", Status);
    return Status;
  }  
  Status = VariablePolicyProtocol->DisableVariablePolicy();
  if (EFI_ERROR(Status)) {
    Print(L"Error! Can't disable VariablePolicy: %r\n", Status);
    return Status;
  }
}
$ vi UefiLessonsPkg/AddNewLanguage/AddNewLanguage.inf
---
....
[Packages]
  ...
  MdeModulePkg/MdeModulePkg.dec
...

[Protocols]
  gEdkiiVariablePolicyProtocolGuid

하지만, 이 호출에 에러가 발생한다.

FS0:\> AddNewLanguage.efi
Current value of the 'PlatformLangCodes' variable is 'en;fr;en-US;fr-FR'
Set 'PlatformLangCodes' variable to 'en;fr;en-US;fr-FR;ru-RU'
Error! Can't set PlatformLangCodes variable, status=Write Protected
Error! Can't disable VariablePolicy: Write Protected

이는 DXE UEFI 단계의 변수 정책이 VariableDxe.c에서 잠겨있기 때문에 발생하는 것이다.

VOID
EFIAPI
OnEndOfDxe (
  EFI_EVENT Event,
  VOID *Context
)
{
  EFI_STATUS Status;
  DEBUG ((EFI_D_INFO, "[Variable]END_OF_DXE is signaled\n"));
   ...
  Status = LockVariablePolicy ();
   ...
}

Locking(잠금)은 더 이상 변수에 대한 정책을 변경하거나, 비활성화할 수 없음을 의미한다. 따라서 Runtime에 PlatformLangCodes를 변경할 방법이 없다.

이전에 이미 gRT->GetVariable 프로토콜 함수를 직접 사용한 적이 있으므로, 여기서는 GetEfiGlobalVariable2 라이브러리 함수를 사용하여 코드를 단순화한다. 이 함수는 정의되어 있다.

ASCII 문자열 함수가 정의된 경우에 대비. 또한 AllocatePool 함수에 대한 Library/MemoryAllocationLib.h 헤더와 CopyMem 함수에 대한 Library/BaseMemoryLib.h 헤더를 포함하는 것을 잊지 않도록 하자.

보면, UEFI 변수 정책 프로토콜에 대한 자세한 내용을 알 수 있다.

아래에 헤더파일이 존재한다.

PlatformLangCodes EFI 변수에 대한 정책은 몇가지 다른 변수와 함께 설정된다.

🖥️
https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Application/UiApp/FrontPageCustomizedUiSupport.c
https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiLib/UefiLib.c
https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/String.c
https://github.com/tianocore/edk2/tree/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Protocol/VariablePolicy.h
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Universal/BdsDxe/BdsEntry.c