비휘발성 UEFI 변수(재부팅 후에도 지속되는 변수)와 함께 작동하는 HII 폼을 만들기 전에 UEFI 변수 서비스를 한 번 더 확인해야 한다.
지금까지는 gRT->GetVariable
및 gRT->GetNextVariableName
서비스를 통해 이미 존재하는 변수만 확인했다.
이번 장에서는 사용자 지정 UEFI 변수를 생성, 변경 및 삭제할 수 있는 애플리케이션을 만들어 본다.
이번 장에서는 gRT->SetVariable
함수를 사용한다.
Copy 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.
VariableName must contain 1 or more characters. If VariableName is an empty string, then
EFI_INVALID_PARAMETER is returned
VendorGuid A unique identifier for the vendor
Attributes Attributes bitmask to set for the variable
DataSize The size in bytes of the Data buffer ... A size of zero causes the variable to be deleted.
Data The contents for the variable
이제 애플리케이션을 생성한다.
Copy $ createNewApp.sh SetVariableExample
UefiLessonsPkg/UefiLessonsPkg.dsc
Copy [Components]
UefiLessonsPkg/SetVariableExample/SetVariableExample.inf
사용자에게 사용자 정의 UEFI 변수를 만들거나 삭제할 수 있는 기능을 제공한다. 단순화를 위해 변수 값은 사용자가 제공하는 문자열이다. 아래는애플리케이션에 대한 도움말이다.
UefiLessonsPkg/SetVariableExample/SetVariableExample.c
Copy VOID Usage()
{
Print(L"Delete variable\n");
Print(L" SetVariableExample <variable name>\n");
Print(L"\n");
Print(L"Set variable\n");
Print(L" SetVariableExample <variable name> <attributes> <value>\n");
Print(L"\n");
Print(L"<attributes> can be <n|b|r>\n");
Print(L"n - NON_VOLATILE\n");
Print(L"b - BOOTSERVICE_ACCESS\n");
Print(L"r - RUNTIME_ACCESS\n");
}
변수를 생성할 때 변수 속성을 제공해야 한다는 것을 알 수 있다. 아래는 플래그의 조합일 수 있다.
EFI_VARIABLE_NOW_VOLATILE
- 재부팅 후에도 변수가 남아있다.
EFI_VARIABLE_BOOTSERVICE_ACCESS
- 변수를UEFI 단계에서 사용할 수 있다.
EFI_VARIABLE_RUNTIME_ACCESS
- 변수를 OS단계에서 사용할 수 있다. (EFI_BOOT_SERVICES.ExitBootServices()
호출이 성공한 후)
이러한 플래그는 아래 링크에 정의되어 있으며, 지금은 다루지 않을 다른 플래그도 있다.
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiMultiPhase.h
위에서 살펴본 3개의 플래그가 있는 경우 아래 조합으로만 사용할 수 있다.
NON_VOLATILE
BOOTSERVICE_ACCESS
RUNTIME_ACCESS
때로는 코드에서 아래와 같은 변수 속성 조합을 볼 수 있다.
https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h
Copy ///
/// Variable Attribute combinations.
///
#define VARIABLE_ATTRIBUTE_NV_BS (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS)
#define VARIABLE_ATTRIBUTE_BS_RT (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)
#define VARIABLE_ATTRIBUTE_NV_BS_RT (VARIABLE_ATTRIBUTE_BS_RT | EFI_VARIABLE_NON_VOLATILE)
...
이제 다시 애플리케이션을 이어간다. 애플리케이션에서 명령 인자를 처리할 수 있도록 코드를 작성한다.
UefiLessonsPkg/SetVariableExample/SetVariableExample.c
Copy #include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>
VOID Usage()
{
...
}
INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
{
EFI_STATUS Status;
if (Argc==2) {
CHAR16* VariableName = Argv[1];
Status = gRT->SetVariable (
VariableName,
&gEfiCallerIdGuid,
0,
0,
NULL
);
if (EFI_ERROR(Status)) {
Print(L"%r\n", Status);
} else {
Print(L"Variable %s was successfully deleted\n", VariableName);
}
return Status;
} else if (Argc==4) {
CHAR16* VariableName = Argv[1];
CHAR16* AttributesStr = Argv[2];
CHAR16* Value = Argv[3];
UINT32 Attributes = 0;
for (UINTN i=0; i<StrLen(AttributesStr); i++) {
switch(AttributesStr[i]) {
case L'n':
Attributes |= EFI_VARIABLE_NON_VOLATILE;
break;
case L'b':
Attributes |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
break;
case L'r':
Attributes |= EFI_VARIABLE_RUNTIME_ACCESS;
break;
default:
Print(L"Error! Unknown attribute!");
return EFI_INVALID_PARAMETER;
}
}
Status = gRT->SetVariable (
VariableName,
&gEfiCallerIdGuid,
Attributes,
StrSize(Value),
Value
);
if (EFI_ERROR(Status)) {
Print(L"%r\n", Status);
} else {
Print(L"Variable %s was successfully changed\n", VariableName);
}
return Status;
} else {
Usage();
}
return EFI_SUCCESS;
}
UefiLessonsPkg/SetVariableExample/SetVariableExample.inf
Copy [Defines]
INF_VERSION = 1.25
BASE_NAME = SetVariableExample
FILE_GUID = bb2a829f-7943-4691-a03a-f1f48519d7e6
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = ShellCEntryLib
[Sources]
SetVariableExample.c
[Packages]
MdePkg/MdePkg.dec
[LibraryClasses]
UefiLib
ShellCEntryLib
Argc==4
일 때, 들어오는 인자를 변수 생성을 위한 입력으로 처리한다. 인자를 파싱하고 gRT->SetVariable
서비스를 호출한다.
Argc==2
일 때는 사용자가 변수 이름만 제공했음을 의미하여 도움말 메시지에 따라 해당 변수를 삭제해야 한다. 이를 위해 DataSize
가 0인 gRT->SetVariable
을 호출한다.
모든 변수는 gEfiCallerIdGuid
GUID에서 생성되며, 여기서는 bb2a829f-7943-4691-a03a-f1f48519d7e6
를 의미한다.
애플리케이션을 빌드 후 UEFI 공유 폴더에 복사한다.
이번에는 UEFI 변수를 다루기 때문에 아래 명령을 통해 QEMU를 실행한다.
Copy qemu-system-x86_64 \
-drive if=pflash,format=raw,readonly,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd \
-drive format=raw,file=fat:rw:~/UEFI_disk \
-net none \
존재하는 UEFI 변수를 보려면 UEFI Shell에서 dmpstore
명령을 사용할 수 있다. dmpstore
명령어에는 특정 GUID와 관련된 변수만 출력하는 옵션이 있다. UEFI Shell에서 바로 실행하면 GUID와 관련된 내용이 없어 확인할 수 없다.
Copy FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
dmpstore: No matching variables found. Guid BB2A829F-7943-4691-A03A-F1F48519D7E6
SetVariableExample.efi
애플리케이션을 통해 Hello
라는 내용을 가진 MyVar
변수를 생성한다.
Copy FS0:\> SetVariableExample.efi MyVar b "Hello"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x0C
00000000: 48 00 65 00 6C 00 6C 00-6F 00 00 00 *H.e.l.l.o...*
dmpstore
명령을 통해 변수가 생성된 것을 확인할 수 있다.
MyVar
변수를 수정한다.
Copy FS0:\> SetVariableExample.efi MyVar b "Hello World"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x18
00000000: 48 00 65 00 6C 00 6C 00-6F 00 20 00 57 00 6F 00 *H.e.l.l.o. .W.o.*
00000010: 72 00 6C 00 64 00 00 00- *r.l.d...*
MyVar
변수를 삭제한다.
Copy FS0:\> SetVariableExample.efi MyVar
Variable MyVar was successfully deleted
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
dmpstore: No matching variables found. Guid BB2A829F-7943-4691-A03A-F1F48519D7E6
또한 변수를 생성할 때 위에서 확인했던 조합이 아닌 경우에는 변수를 생성할 수 없다.
Copy FS0:\> SetVariableExample.efi MyVar nr "Hello"
Invalid Parameter
NV+BS
와 BS
속성을 가진 두 개의 변수를 생성한다.
Copy FS0:\> SetVariableExample.efi MyPersistentVar nb "Persistent variable"
Variable MyPersistentVar was successfully changed
FS0:\> SetVariableExample.efi MyVar b "Memory variable"
Variable MyVar was successfully changed
FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable NV+BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar' DataSize = 0x28
00000000: 50 00 65 00 72 00 73 00-69 00 73 00 74 00 65 00 *P.e.r.s.i.s.t.e.*
00000010: 6E 00 74 00 20 00 76 00-61 00 72 00 69 00 61 00 *n.t. .v.a.r.i.a.*
00000020: 62 00 6C 00 65 00 00 00- *b.l.e...*
Variable BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyVar' DataSize = 0x20
00000000: 4D 00 65 00 6D 00 6F 00-72 00 79 00 20 00 76 00 *M.e.m.o.r.y. .v.*
00000010: 61 00 72 00 69 00 61 00-62 00 6C 00 65 00 00 00 *a.r.i.a.b.l.e...*
QEMU를 재실행 하고 dmpsote
명령을 확인하면 MyPersistentVar
변수가 남아있는 것을 확인할 수 있다.
Copy FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
Variable NV+BS 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar' DataSize = 0x28
00000000: 50 00 65 00 72 00 73 00-69 00 73 00 74 00 65 00 *P.e.r.s.i.s.t.e.*
00000010: 6E 00 74 00 20 00 76 00-61 00 72 00 69 00 61 00 *n.t. .v.a.r.i.a.*
00000020: 62 00 6C 00 65 00 00 00- *b.l.e...*
또한 이미 생성된 변수의 속성을 변경할 수 없는지 확인할 수 있다.
Copy FS0:\> SetVariableExample.efi MyPersistentVar b "Persistent variable"
Invalid Parameter
dmpstore
명령을 사용해 변수를 삭제할 수도 있다.
Copy FS0:\> dmpstore -d -guid bb2a829f-7943-4691-a03a-f1f48519d7e6 MyPersistentVar
Delete variable 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar': Success
dmpsotre -d <GUID>
명령을 사용해 GUID 뒤에 있는 모든 변수를 삭제할 수 있다.
여기서는 예시로 애플리케이션에서 변수 내용을 단순 문자열로 하여 진행했지만 변수에는 복잡한 구조가 저장될 수 있다.