40. Key #### NVRAM 변수
GetNextVariableName()
UEFI 서비스와 시스템의 모든 NVRAM 변수를 조사해보면, EFI_GLOBAL_VARIABLE GUID
(gEfiGlobalVariableGuid
) 아래에 다음 변수가 존재한다.
8BE4DF61-93CA-11D2-AA0D-00E098032B8C: Key0000
8BE4DF61-93CA-11D2-AA0D-00E098032B8C: Key0001
UEFI 스펙 또는 EDKII 파일(MdePkg/Include/Guid/GlobalVariable.h
)를 읽고 이러한 옵션에 대한 도움말을 찾을 수 있다.
// L"Key####" - Describes hot key relationship with a Boot#### load option
OVMF는 BdsPlatform.c
파일에서 위 옵션을 설정한다.
$ cat MdePkg/Include/Guid/GlobalVariable.h
---
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
코드는 부트 관리자 메뉴 항목에 대한 단축키로 F2
및 ESC
키 입력을 설정한다. 그리고 필요한 모든 정보와 함께 NVRAM 변수 KeyXXXX
를 추가한다,
$ cat 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;
}
EFI_BOOT_MANAGER_KEY_OPTION
이 어떻게 코딩되어있는지 확인하려면, MdeModulePkg/Include/Library/UefiBootManager.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()
위의 gRT->SetVariable
호출에 사용된 BmSizeOfKeyOption
함수(BmHotKey.c
)를 살펴보면 다음과 같다.
$ cat MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
---
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);
}
EFI_BOOT_MANAGER_KEY_OPTION
의 OptionNumber
필드가 NVRAM에 저장되지 않고, 키 배열의 크기가 가변적임을 알 수 있다.
다른 하위 유형의 경우,
EFI_BOOT_KEY_DATA
는UefiSpec.h
에 정의되어 있다.
$ cat 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
는SimpleTextIn.h
에 정의되어 있다.
$ cat MdePkg/Include/Protocol/SimpleText.h
---
typedef struct {
UINT16 ScanCode;
CHAR16 UnicodeChar;
} EFI_INPUT_KEY;
이 모든 정보들을 염두해 두고, shell에서 dmpstore
명령을 호출하고 KeyXXXX
옵션을 파싱할 수 있다.

보다시피 Key0000
은 Boot0000
Option에 대한 0x000c
스캔 코드로 단축키를 정의한다. 그리고 Key0001
은 동일한 Boot0000
Option에 대해 0x0017
스캔 코드단축키를 정의한다. 또한 Boot0000
은 부트메뉴인 UiApp
을 나타낸다.
그리고 SimpleTextIn.h
에 따르면,
$ cat MdePkg/Include/Protocol/SimpleTextIn.h
---
#define SCAN_F2 0x000C
...
#define SCAN_ESC 0x0017
따라서 이제 F2
및 ESC
키를 사용하여 부트 프로세스를 중지하고, 부트 메뉴로 이동할 수 있다.
이 HotKey 함수 구현에 대해 더 알고 싶다면, callback 함수 BmHotkeyCallback
과 이것이 설정하는 mBmHotkeyBootOption
변수를 살펴보는 것을 추천한다.(BmHotkey.c
)
한가지 더 언급하자면, -nographic
option을 사용하여 QEMU를 시작하면, 모든 비표준 키는 SCAN_ESC
기호로 시작하는 escape sequence를 통해 전송된다. 따라서 이 경우 모든 비표준 키가 단축키로 작동하게 된다.
Last updated