1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
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.
|