diff options
Diffstat (limited to 'Lessons')
-rw-r--r-- | Lessons/Lesson_40/README.md | 209 |
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. + |