aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons
diff options
context:
space:
mode:
Diffstat (limited to 'Lessons')
-rw-r--r--Lessons/Lesson_40/README.md209
1 files changed, 209 insertions, 0 deletions
diff --git a/Lessons/Lesson_40/README.md b/Lessons/Lesson_40/README.md
new file mode 100644
index 0000000..0f24216
--- /dev/null
+++ b/Lessons/Lesson_40/README.md
@@ -0,0 +1,209 @@
+When we were investigating `GetNextVariableName()` UEFI service and all the NVRAM variables in the system, we've found out that under `EFI_GLOBAL_VARIABLE GUID` (`gEfiGlobalVariableGuid`)
+ these variables are present:
+```
+8BE4DF61-93CA-11D2-AA0D-00E098032B8C: Key0000
+8BE4DF61-93CA-11D2-AA0D-00E098032B8C: Key0001
+```
+
+You can read UEFI specification or edk2 file https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Guid/GlobalVariable.h to find help for these options:
+```
+// L"Key####" - Describes hot key relationship with a Boot#### load option
+```
+
+OVMF sets these options in a file https://github.com/tianocore/edk2/blob/master/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c:
+```
+VOID
+PlatformRegisterOptionsAndKeys (
+ VOID
+ )
+{
+ ...
+
+ //
+ // Map F2 to Boot Manager Menu
+ //
+ F2.ScanCode = SCAN_F2;
+ F2.UnicodeChar = CHAR_NULL;
+ Esc.ScanCode = SCAN_ESC;
+ Esc.UnicodeChar = CHAR_NULL;
+ Status = EfiBootManagerGetBootManagerMenu (&BootOption);
+ ASSERT_EFI_ERROR (Status);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
+ );
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
+ Status = EfiBootManagerAddKeyOptionVariable (
+ NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
+ );
+ ...
+}
+```
+`EfiBootManagerAddKeyOptionVariable` code sets `F2` and `ESC` keystrokes as hotkeys for the boot manager menu entry. And adds NVRAM variables `KeyXXXX` with all the necessary info.
+
+https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c:
+```
+/**
+ Add the key option.
+ It adds the key option variable and the key option takes affect immediately.
+ @param AddedOption Return the added key option.
+ @param BootOptionNumber The boot option number for the key option.
+ @param Modifier Key shift state.
+ @param ... Parameter list of pointer of EFI_INPUT_KEY.
+ @retval EFI_SUCCESS The key option is added.
+ @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddKeyOptionVariable (
+ OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption, OPTIONAL
+ IN UINT16 BootOptionNumber,
+ IN UINT32 Modifier,
+ ...
+ )
+{
+ EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
+ ...
+ UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
+
+ Status = gRT->SetVariable ( // <------ this call sets 'KeyXXXX' variable
+ KeyOptionName,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ BmSizeOfKeyOption (&KeyOption),
+ &KeyOption
+ );
+ if (!EFI_ERROR (Status)) {
+ ...
+ if (mBmHotkeyServiceStarted) {
+ BmProcessKeyOption (&KeyOption); // <---- this function calls 'RegisterKeyNotify'
+ }
+ }
+
+ return Status;
+}
+```
+
+To get an understanding about how `EFI_BOOT_MANAGER_KEY_OPTION` is coded, take a look at its definition in https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Library/UefiBootManagerLib.h
+```
+#pragma pack(1)
+///
+/// EFI Key Option.
+///
+typedef struct {
+ ///
+ /// Specifies options about how the key will be processed.
+ ///
+ EFI_BOOT_KEY_DATA KeyData;
+ ///
+ /// The CRC-32 which should match the CRC-32 of the entire EFI_LOAD_OPTION to
+ /// which BootOption refers. If the CRC-32s do not match this value, then this key
+ /// option is ignored.
+ ///
+ UINT32 BootOptionCrc;
+ ///
+ /// The Boot#### option which will be invoked if this key is pressed and the boot option
+ /// is active (LOAD_OPTION_ACTIVE is set).
+ ///
+ UINT16 BootOption;
+ ///
+ /// The key codes to compare against those returned by the
+ /// EFI_SIMPLE_TEXT_INPUT and EFI_SIMPLE_TEXT_INPUT_EX protocols.
+ /// The number of key codes (0-3) is specified by the EFI_KEY_CODE_COUNT field in KeyOptions.
+ ///
+ EFI_INPUT_KEY Keys[3];
+ UINT16 OptionNumber;
+} EFI_BOOT_MANAGER_KEY_OPTION;
+#pragma pack()
+```
+
+
+If you'll look at the `BmSizeOfKeyOption` function (https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c) that was used in a `gRT->SetVariable` call above:
+```
+UINTN
+BmSizeOfKeyOption (
+ IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
+ )
+{
+ return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)
+ + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);
+}
+```
+you will see that `OptionNumber` field of `EFI_BOOT_MANAGER_KEY_OPTION` is not stored in NVRAM and `Keys` array size is variable.
+
+
+As for other subtypes:
+- `EFI_BOOT_KEY_DATA` is defined in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Uefi/UefiSpec.h
+```
+///
+/// EFI Boot Key Data
+///
+typedef union {
+ struct {
+ ///
+ /// Indicates the revision of the EFI_KEY_OPTION structure. This revision level should be 0.
+ ///
+ UINT32 Revision : 8;
+ ///
+ /// Either the left or right Shift keys must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 ShiftPressed : 1;
+ ///
+ /// Either the left or right Control keys must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 ControlPressed : 1;
+ ///
+ /// Either the left or right Alt keys must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 AltPressed : 1;
+ ///
+ /// Either the left or right Logo keys must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 LogoPressed : 1;
+ ///
+ /// The Menu key must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 MenuPressed : 1;
+ ///
+ /// The SysReq key must be pressed (1) or must not be pressed (0).
+ ///
+ UINT32 SysReqPressed : 1;
+ UINT32 Reserved : 16;
+ ///
+ /// Specifies the actual number of entries in EFI_KEY_OPTION.Keys, from 0-3. If
+ /// zero, then only the shift state is considered. If more than one, then the boot option will
+ /// only be launched if all of the specified keys are pressed with the same shift state.
+ ///
+ UINT32 InputKeyCount : 2;
+ } Options;
+ UINT32 PackedValue;
+} EFI_BOOT_KEY_DATA;
+```
+- `EFI_INPUT_KEY` is defined in https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/SimpleTextIn.h
+```
+typedef struct {
+ UINT16 ScanCode;
+ CHAR16 UnicodeChar;
+} EFI_INPUT_KEY;
+```
+
+With all this information in mind we can call `dmpstore` command in our shell and parse `KeyXXXX` options.
+
+![dmpstore](dmpstore.png?raw=true "dmpstore")
+
+As you can see `Key0000` defines hot key with a `0x000c` scan code for the `Boot0000` option. And `Key0001` defines hot key with a `0x0017` scan code for the same `Boot0000` option.
+You can also see that `Boot0000` stands for the `UiApp` which happens to be a boot menu.
+
+
+And according to the https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Protocol/SimpleTextIn.h
+```
+#define SCAN_F2 0x000C
+...
+#define SCAN_ESC 0x0017
+```
+
+So you can use `F2` and `ESC` keys to stop the boot process and go to the boot menu.
+
+If you want to know more about this HotKey functionality implementation take a look at a callback function `BmHotkeyCallback` and a `mBmHotkeyBootOption` variable that it sets (https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c)
+
+One more notice. As you might remember if we launch QEMU with `-nographic` option, every non-standard key is transmitted through the escape sequence, which starts with the `SCAN_ESC` symbol. So don't be surprised, when every non-standard key would act as a HotKey in this case.
+