aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lessons/Lesson_60/README.md279
-rw-r--r--README.md1
2 files changed, 280 insertions, 0 deletions
diff --git a/Lessons/Lesson_60/README.md b/Lessons/Lesson_60/README.md
new file mode 100644
index 0000000..7df9381
--- /dev/null
+++ b/Lessons/Lesson_60/README.md
@@ -0,0 +1,279 @@
+Before we start creating HII forms that would work with non-volatile UEFI variables (i.e. persistent across reboots), we need to check UEFI variable services one more time.
+
+Up until now we've only checked already existing variables with the help of `gRT->GetVariable` and `gRT->GetNextVariableName` services.
+
+Let's try to create an application that would create, change and delete our own custom UEFI variables.
+
+For this task we will need `gRT->SetVariable` function:
+```
+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
+```
+
+Now let's write a code for our application. It would give a user a possibility to create or delete custom UEFI variables. For the simplicity the variable value would be just a string supplied by user. Here is a help message for our application:
+
+```
+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");
+}
+```
+
+You can see that when variable is created it is necessary to provide variable attributes. They can be a combination of the following flags:
+- `EFI_VARIABLE_NON_VOLATILE` - variable is persistent across reboots
+- `EFI_VARIABLE_BOOTSERVICE_ACCESS` - variable is available in the UEFI stage
+- `EFI_VARIABLE_RUNTIME_ACCESS` - variable is available in the OS stage (after the successfull `EFI_BOOT_SERVICES.ExitBootServices()` call)
+
+These flags are defined in the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiMultiPhase.h with the several other flags that we wouldn't cover right now.
+
+With these 3 flags only the following combinations are allowed:
+
+| NV | BS | RT |
+|---|---|---|
+| + | + | + |
+| + | + | |
+| | + | |
+| | + | + |
+
+
+Sometimes in the code you could encounter variable attribute combinations like these (https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h):
+```
+///
+/// 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)
+...
+```
+
+But let's get back to our application. We would need to handle command arguments so it is better to create a shell application.
+
+The code is pretty simple.
+
+`UefiLessonsPkg/SetVariableExample/SetVariableExample.c`
+```
+#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`:
+```
+[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]
+ UefiApplicationEntryPoint
+ UefiLib
+ ShellCEntryLib
+```
+
+When `Argc==4`, we treat incoming arguments as inputs for the variable creation. We parse arguments and call `gRT->SetVariable` service.
+
+When `Argc==2`, it means that the user have supplied only the variable name and according to our help message we have to delete this variable. To do it we call `gRT->SetVariable` with a `DataSize` equal to 0.
+
+All the variables would be created under the `gEfiCallerIdGuid` GUID, which in our case means `bb2a829f-7943-4691-a03a-f1f48519d7e6`.
+
+Build our application and copy it to the UEFI shared disk folder.
+
+Don't forget to add `-drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd` to the QEMU arguments:
+```
+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 \
+ -nographic
+```
+
+To look at the currently present UEFI variables we can use `dmpstore` command from the UEFI shell. This command has an option to print only variables associated with a particular guid. If we try to execute it right from the UEFI shell start we wouldn't get anything associated with our GUID:
+```
+FS0:\> dmpstore -guid bb2a829f-7943-4691-a03a-f1f48519d7e6
+dmpstore: No matching variables found. Guid BB2A829F-7943-4691-A03A-F1F48519D7E6
+```
+
+Let's try to create a `MyVar` variable with a `Hello` content:
+```
+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...*
+```
+You can indeed see that the variable was created.
+
+You can try to change it:
+```
+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...*
+```
+Or delete it:
+```
+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
+```
+
+You can see the proof that not all the combination of attributes are supported:
+```
+FS0:\> SetVariableExample.efi MyVar nr "Hello"
+Invalid Parameter
+```
+
+
+Now let's create two variables with the different attributes: "NV+BS" and "BS"
+```
+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...*
+```
+Restart QEMU and issue `dmpstore` command:
+```
+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...*
+```
+As you can see only the `MyPersistentVar` is present.
+
+Also your can check that is not possible to change attributes of an already created variable.
+```
+FS0:\> SetVariableExample.efi MyPersistentVar b "Persistent variable"
+Invalid Parameter
+```
+
+`dmpstore` command also can be used to delete variable
+```
+FS0:\> dmpstore -d -guid bb2a829f-7943-4691-a03a-f1f48519d7e6 MyPersistentVar
+Delete variable 'BB2A829F-7943-4691-A03A-F1F48519D7E6:MyPersistentVar': Success
+```
+
+You can delete all the variables behind the GUID with a `dmpstore -d <GUID>` command.
+
+In our application the variable content is a simple string. But keep in mind that this is just an example. Often variables have complicated structures stored in them.
+
+
diff --git a/README.md b/README.md
index c2ab161..4291b62 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ These series of lessons are intendend to get you started with UEFI programming i
- [Lesson 57](Lessons/Lesson_57): Use VFR to create a simple form and display it with a help of `EFI_FORM_BROWSER2_PROTOCOL.SendForm()`. IFR data investigation
- [Lesson 58](Lessons/Lesson_58): `subtitle` and `text` VFR elements
- [Lesson 59](Lessons/Lesson_59): Create an application to display HII Forms by package list GUIDs. Convert our simple form application to UEFI driver form
+- [Lesson 60](Lessons/Lesson_60): Use `gRT->SetVariable()` function to create and delete UEFI variables. Investigate variable attributes. Practical uses of the `dmpstore` command
_____