우리는 새 언어에 대한 글꼴을 추가해주는 방법을 학습했으며, 시스템의 일부 기존 언어에 대한 문자열을 동적으로 채우는 방법을 알게 되었다. 이번 장에서는 다른 언어를 동적으로 생성할 수 있는지 살펴보겠다.
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;
}
}
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에서 잠겨있기 때문에 발생하는 것이다.