Mike Beaton 2439d58412
OcConsoleLib: Prevent verbose boot text overwriting pre-existing graphics (#426)
- Clear console screen on change to console mode
 - Ensure entire screen is cleared at least once in console mode
   o Was not needed/was done by default, prior to a189bd53bc2ac1490dac7125a9ce11fd03571755
   o Remove GOP clear screen work-around no longer required with this change.
 - These changes improve EnableGop driver in the same way, so update version number
   o Add EnableGop version in UI section, to enable tool builders to track it
2023-02-22 02:01:39 +03:00

830 lines
26 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @file
Copyright (C) 2019, vit9696. All rights reserved.<BR>
Copyright (C) 2021, Mike Beaton. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include "BootManagementInternal.h"
#include <Guid/AppleVariable.h>
#include <IndustryStandard/AppleCsrConfig.h>
#include <Protocol/AppleKeyMapAggregator.h>
#include <Protocol/OcBootEntry.h>
#include <Library/BaseLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcDebugLogLib.h>
#include <Library/OcTimerLib.h>
#include <Library/OcAppleKeyMapLib.h>
#include <Library/OcBootManagementLib.h>
#include <Library/OcConfigurationLib.h>
#include <Library/OcMiscLib.h>
#include <Library/OcTemplateLib.h>
#include <Library/OcTypingLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
typedef struct {
UINTN NumKeys;
APPLE_MODIFIER_MAP Modifiers;
APPLE_KEY_CODE *Keys;
EFI_STATUS Result;
} BEP_APPLY_HOTKEYS_CONTEXT;
STATIC
BOOLEAN
EFIAPI
CheckHotKeysForProtocolInstance (
IN OUT OC_PICKER_CONTEXT *PickerContext,
IN EFI_HANDLE BootEntryProtocolHandle,
IN OC_BOOT_ENTRY_PROTOCOL *BootEntryProtocol,
IN VOID *Context
)
{
BEP_APPLY_HOTKEYS_CONTEXT *ApplyHotkeysContext;
CHAR8 *BootId;
ASSERT (Context != NULL);
ApplyHotkeysContext = Context;
if (BootEntryProtocol->CheckHotKeys) {
BootId = BootEntryProtocol->CheckHotKeys (
PickerContext,
ApplyHotkeysContext->NumKeys,
ApplyHotkeysContext->Modifiers,
ApplyHotkeysContext->Keys
);
if (BootId != NULL) {
PickerContext->PickerCommand = OcPickerProtocolHotKey;
PickerContext->HotKeyProtocolHandle = BootEntryProtocolHandle;
PickerContext->HotKeyEntryId = BootId;
ApplyHotkeysContext->Result = EFI_SUCCESS;
return FALSE;
}
}
return TRUE;
}
STATIC
EFI_STATUS
ApplyBootEntryProtocolHotKeys (
IN OUT OC_PICKER_CONTEXT *PickerContext,
IN UINTN NumKeys,
IN APPLE_MODIFIER_MAP Modifiers,
IN APPLE_KEY_CODE *Keys
)
{
EFI_HANDLE *EntryProtocolHandles;
UINTN EntryProtocolHandleCount;
BEP_APPLY_HOTKEYS_CONTEXT ApplyHotkeysContext;
ApplyHotkeysContext.NumKeys = NumKeys;
ApplyHotkeysContext.Modifiers = Modifiers;
ApplyHotkeysContext.Keys = Keys;
ApplyHotkeysContext.Result = EFI_NOT_FOUND;
OcLocateBootEntryProtocolHandles (&EntryProtocolHandles, &EntryProtocolHandleCount);
OcConsumeBootEntryProtocol (
PickerContext,
EntryProtocolHandles,
EntryProtocolHandleCount,
CheckHotKeysForProtocolInstance,
&ApplyHotkeysContext
);
OcFreeBootEntryProtocolHandles (&EntryProtocolHandles);
return ApplyHotkeysContext.Result;
}
//
// Get hotkeys pressed at load
//
VOID
OcLoadPickerHotKeys (
IN OUT OC_PICKER_CONTEXT *Context
)
{
EFI_STATUS Status;
APPLE_KEY_MAP_AGGREGATOR_PROTOCOL *KeyMap;
UINTN NumKeys;
APPLE_MODIFIER_MAP Modifiers;
APPLE_KEY_CODE Keys[OC_KEY_MAP_DEFAULT_SIZE];
BOOLEAN HasCommand;
BOOLEAN HasEscape;
BOOLEAN HasZero;
BOOLEAN HasOption;
BOOLEAN HasKeyR;
BOOLEAN HasKeyX;
//
// Waiting for boot chime to finish around here, even as a separate option, is
// problematic because the chime does not play if muted or below minimum audible gain.
//
if (Context->TakeoffDelay > 0) {
gBS->Stall (Context->TakeoffDelay);
}
KeyMap = OcGetProtocol (&gAppleKeyMapAggregatorProtocolGuid, DEBUG_ERROR, "OcLoadPickerHotKeys", "AppleKeyMapAggregator");
NumKeys = ARRAY_SIZE (Keys);
Status = KeyMap->GetKeyStrokes (
KeyMap,
&Modifiers,
&NumKeys,
Keys
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "OCHK: GetKeyStrokes - %r\n", Status));
return;
}
//
// I do not like this code a little, as it is prone to race conditions during key presses.
// For the good false positives are not too critical here, and in reality users are not that fast.
//
// Reference key list:
// https://support.apple.com/HT201255
// https://support.apple.com/HT204904
//
// We are slightly more permissive than AppleBds, as we permit combining keys.
//
Status = ApplyBootEntryProtocolHotKeys (Context, NumKeys, Modifiers, Keys);
if (!EFI_ERROR (Status)) {
//
// If boot entry protocol hotkeys detected return without detecting builtin hotkeys, otherwise
// for example CMD+OPT+P+R for reset NVRAM gets overridden by CMD+R for recovery.
//
return;
}
HasCommand = (Modifiers & (APPLE_MODIFIER_LEFT_COMMAND | APPLE_MODIFIER_RIGHT_COMMAND)) != 0;
HasOption = (Modifiers & (APPLE_MODIFIER_LEFT_OPTION | APPLE_MODIFIER_RIGHT_OPTION)) != 0;
HasEscape = OcKeyMapHasKey (Keys, NumKeys, AppleHidUsbKbUsageKeyEscape);
HasZero = OcKeyMapHasKey (Keys, NumKeys, AppleHidUsbKbUsageKeyZero);
HasKeyR = OcKeyMapHasKey (Keys, NumKeys, AppleHidUsbKbUsageKeyR);
HasKeyX = OcKeyMapHasKey (Keys, NumKeys, AppleHidUsbKbUsageKeyX);
if (HasCommand && HasKeyR) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+R causes recovery to boot\n"));
Context->PickerCommand = OcPickerBootAppleRecovery;
} else if (HasKeyX) {
DEBUG ((DEBUG_INFO, "OCHK: X causes macOS to boot\n"));
Context->PickerCommand = OcPickerBootApple;
} else if (HasOption) {
DEBUG ((DEBUG_INFO, "OCHK: OPT causes picker to show\n"));
Context->PickerCommand = OcPickerShowPicker;
} else if (HasEscape || HasZero) {
DEBUG ((DEBUG_INFO, "OCHK: ESC/0 causes picker to show as OC extension\n"));
Context->PickerCommand = OcPickerShowPicker;
} else {
//
// In addition to these overrides we always have ShowPicker = YES in config.
// The following keys are not implemented:
// C - CD/DVD boot, legacy that is gone now.
// D - Diagnostics, could implement dumping stuff here in some future,
// but we will need to store the data before handling the key.
// Should also be DEBUG only for security reasons.
// N - Network boot, simply not supported (and bad for security).
// T - Target disk mode, simply not supported (and bad for security).
//
}
}
STATIC
VOID
EFIAPI
PickerFlushTypingBuffer (
IN OUT OC_PICKER_CONTEXT *Context
)
{
OcFlushTypingBuffer (Context->HotKeyContext->TypingContext);
}
STATIC
VOID
EFIAPI
GetPickerKeyInfo (
IN OUT OC_PICKER_CONTEXT *Context,
IN OC_PICKER_KEY_MAP KeyFilter,
OUT OC_PICKER_KEY_INFO *PickerKeyInfo
)
{
EFI_STATUS Status;
UINTN NumKeys;
APPLE_KEY_CODE *Keys;
APPLE_KEY_CODE Key;
APPLE_MODIFIER_MAP Modifiers;
CHAR16 UnicodeChar;
UINTN NumKeysUp;
UINTN NumKeysDoNotRepeat;
APPLE_KEY_CODE KeysDoNotRepeat[OC_KEY_MAP_DEFAULT_SIZE];
APPLE_MODIFIER_MAP ModifiersDoNotRepeat;
UINTN AkmaNumKeys;
APPLE_MODIFIER_MAP AkmaModifiers;
APPLE_KEY_CODE AkmaKeys[OC_KEY_MAP_DEFAULT_SIZE];
APPLE_MODIFIER_MAP ValidBootModifiers;
BOOLEAN TriggerBoot;
BOOLEAN HasCommand;
BOOLEAN HasKeyC;
BOOLEAN HasKeyK;
BOOLEAN HasKeyS;
BOOLEAN HasKeyV;
BOOLEAN HasKeyMinus;
BOOLEAN WantsZeroSlide;
UINT32 CsrActiveConfig;
UINTN CsrActiveConfigSize;
ASSERT (Context->HotKeyContext->KeyMap != NULL);
ASSERT (Context->HotKeyContext->TypingContext != NULL);
ASSERT (Context->HotKeyContext->DoNotRepeatContext != NULL);
ASSERT (PickerKeyInfo != NULL);
PickerKeyInfo->OcKeyCode = OC_INPUT_NO_ACTION;
PickerKeyInfo->OcModifiers = OC_MODIFIERS_NONE;
PickerKeyInfo->UnicodeChar = CHAR_NULL;
//
// AKMA hotkeys
//
AkmaNumKeys = ARRAY_SIZE (AkmaKeys);
Status = Context->HotKeyContext->KeyMap->GetKeyStrokes (
Context->HotKeyContext->KeyMap,
&AkmaModifiers,
&AkmaNumKeys,
AkmaKeys
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCHK: AKMA GetKeyStrokes - %r\n", Status));
return;
}
//
// Apple Event typing
//
Keys = &Key;
OcGetNextKeystroke (Context->HotKeyContext->TypingContext, &Modifiers, Keys, &UnicodeChar);
if (*Keys == 0) {
NumKeys = 0;
} else {
NumKeys = 1;
}
//
// Non-repeating keys
//
NumKeysUp = 0;
NumKeysDoNotRepeat = ARRAY_SIZE (KeysDoNotRepeat);
Status = OcGetUpDownKeys (
Context->HotKeyContext->DoNotRepeatContext,
&ModifiersDoNotRepeat,
&NumKeysUp,
NULL,
&NumKeysDoNotRepeat,
KeysDoNotRepeat,
0ULL // time not needed for non-repeat keys
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "OCHK: GetUpDownKeys for DoNotRepeatContext - %r\n", Status));
return;
}
DEBUG_CODE_BEGIN ();
if (Context->KbDebug != NULL) {
Context->KbDebug->Show (NumKeys, AkmaNumKeys, Modifiers);
}
DEBUG_CODE_END ();
//
// Set OcModifiers early, so they are correct even if - say - a hotkey or non-repeating key returns first.
//
ValidBootModifiers = APPLE_MODIFIERS_NONE;
if (Context->AllowSetDefault) {
ValidBootModifiers |= APPLE_MODIFIERS_CONTROL;
}
//
// Note: As historically SHIFT handling here is considered a 'hotkey':
// it's original reason for being here is to fix difficulties in
// detecting this and other hotkey modifiers during no-picker boot.
//
if (((KeyFilter & OC_PICKER_KEYS_HOTKEYS) != 0) && Context->PollAppleHotKeys) {
ValidBootModifiers |= APPLE_MODIFIERS_SHIFT;
}
//
// Default update is desired for Ctrl+Index and Ctrl+Enter.
// Strictly apply only on CTRL or CTRL+SHIFT (when SHIFT allowed),
// but no other modifiers.
// Needs to be set/unset even if filtered for typing (otherwise can
// get locked on if user tabs to typing context).
//
if ( ((Modifiers & ~ValidBootModifiers) == 0)
&& ((Modifiers & APPLE_MODIFIERS_CONTROL) != 0))
{
PickerKeyInfo->OcModifiers |= OC_MODIFIERS_SET_DEFAULT;
}
//
// Alternative 'set default' key, if modifiers not working;
// useful both for 'set default' and for tuning KeyForgetThreshold.
//
if ( Context->AllowSetDefault
&& OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyEquals))
{
PickerKeyInfo->OcModifiers |= OC_MODIFIERS_SET_DEFAULT;
}
//
// Loosely apply regardless of other modifiers.
//
if ( ((KeyFilter & OC_PICKER_KEYS_TAB_CONTROL) != 0)
&& ((Modifiers & APPLE_MODIFIERS_SHIFT) != 0))
{
PickerKeyInfo->OcModifiers |= OC_MODIFIERS_REVERSE_SWITCH_FOCUS;
}
//
// Handle key combinations.
//
if (((KeyFilter & OC_PICKER_KEYS_HOTKEYS) != 0) && Context->PollAppleHotKeys) {
HasCommand = (AkmaModifiers & APPLE_MODIFIERS_COMMAND) != 0;
HasKeyC = OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyC);
HasKeyK = OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyK);
HasKeyS = OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyS);
HasKeyV = OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyV);
//
// Checking for PAD minus is our extension to support more keyboards.
//
HasKeyMinus = OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyMinus)
|| OcKeyMapHasKey (AkmaKeys, AkmaNumKeys, AppleHidUsbKbUsageKeyPadMinus);
//
// CMD+V is always valid and enables Verbose Mode.
//
// Note: Verbose boot may be entered in three different ways:
// - Loaded image options passed from bootloader (as will happen due to below
// code, when CMD+V is pressed during OpenCore picker menu).
// - `-v` option in NVRAM `boot-args` variable.
// - boot.efi itself detecting that CMD+V is held down when it starts.
//
if (HasCommand && HasKeyV) {
if (OcGetArgumentFromCmd (Context->AppleBootArgs, "-v", L_STR_LEN ("-v"), NULL) == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+V means -v\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "-v", L_STR_LEN ("-v"));
}
PickerKeyInfo->OcKeyCode = OC_INPUT_INTERNAL;
}
//
// CMD+C+MINUS is always valid and disables compatibility check.
//
if (HasCommand && HasKeyC && HasKeyMinus) {
if (OcGetArgumentFromCmd (Context->AppleBootArgs, "-no_compat_check", L_STR_LEN ("-no_compat_check"), NULL) == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+C+MINUS means -no_compat_check\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "-no_compat_check", L_STR_LEN ("-no_compat_check"));
}
PickerKeyInfo->OcKeyCode = OC_INPUT_INTERNAL;
}
//
// CMD+K is always valid for new macOS and means force boot to release kernel.
//
if (HasCommand && HasKeyK) {
if (AsciiStrStr (Context->AppleBootArgs, "kcsuffix=release") == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+K means kcsuffix=release\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "kcsuffix=release", L_STR_LEN ("kcsuffix=release"));
}
PickerKeyInfo->OcKeyCode = OC_INPUT_INTERNAL;
}
//
// boot.efi also checks for CMD+X, but I have no idea what it is for.
//
//
// boot.efi requires unrestricted NVRAM just for CMD+S+MINUS, and CMD+S
// does not work at all on T2 macs. For CMD+S we simulate T2 behaviour with
// DisableSingleUser Booter quirk if necessary.
// Ref: https://support.apple.com/HT201573
//
if (HasCommand && HasKeyS) {
WantsZeroSlide = HasKeyMinus;
if (WantsZeroSlide) {
CsrActiveConfig = 0;
CsrActiveConfigSize = sizeof (CsrActiveConfig);
Status = gRT->GetVariable (
L"csr-active-config",
&gAppleBootVariableGuid,
NULL,
&CsrActiveConfigSize,
&CsrActiveConfig
);
//
// FIXME: CMD+S+Minus behaves as CMD+S when "slide=0" is not supported
// by the SIP configuration. This might be an oversight, but is
// consistent with the boot.efi implementation.
//
WantsZeroSlide = !EFI_ERROR (Status) && (CsrActiveConfig & CSR_ALLOW_UNRESTRICTED_NVRAM) != 0;
}
if (WantsZeroSlide) {
if (AsciiStrStr (Context->AppleBootArgs, "slide=0") == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+S+MINUS means slide=0\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "slide=0", L_STR_LEN ("slide=0"));
}
} else if (OcGetArgumentFromCmd (Context->AppleBootArgs, "-s", L_STR_LEN ("-s"), NULL) == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: CMD+S means -s\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "-s", L_STR_LEN ("-s"));
}
PickerKeyInfo->OcKeyCode = OC_INPUT_INTERNAL;
}
}
//
// Handle typing chars.
//
if ((KeyFilter & OC_PICKER_KEYS_TYPING) != 0) {
PickerKeyInfo->UnicodeChar = UnicodeChar;
}
//
// Handle VoiceOver - non-repeating.
//
if ( ((KeyFilter & OC_PICKER_KEYS_VOICE_OVER) != 0)
&& ((Modifiers & APPLE_MODIFIERS_COMMAND) != 0)
&& OcKeyMapHasKey (KeysDoNotRepeat, NumKeysDoNotRepeat, AppleHidUsbKbUsageKeyF5))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_VOICE_OVER;
return;
}
if ((KeyFilter & OC_PICKER_KEYS_TYPING) == 0) {
//
// Handle reload menu - non-repeating.
//
if ( OcKeyMapHasKey (KeysDoNotRepeat, NumKeysDoNotRepeat, AppleHidUsbKbUsageKeyEscape)
|| OcKeyMapHasKey (KeysDoNotRepeat, NumKeysDoNotRepeat, AppleHidUsbKbUsageKeyZero)
)
{
PickerKeyInfo->OcKeyCode = OC_INPUT_ABORTED;
return;
}
//
// Handle show or toggle auxiliary - non-repeating.
//
if (OcKeyMapHasKey (KeysDoNotRepeat, NumKeysDoNotRepeat, AppleHidUsbKbUsageKeySpaceBar)) {
PickerKeyInfo->OcKeyCode = OC_INPUT_MORE;
return;
}
}
if (NumKeys == 1) {
if ( ((KeyFilter & OC_PICKER_KEYS_TAB_CONTROL) != 0)
&& (Keys[0] == AppleHidUsbKbUsageKeyTab))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_SWITCH_FOCUS;
return;
}
if ((KeyFilter & OC_PICKER_KEYS_TYPING) != 0) {
//
// Typing index key strokes.
//
if (Keys[0] == AppleHidUsbKbUsageKeyLeftArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_TYPING_LEFT;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyRightArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_TYPING_RIGHT;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyEscape) {
PickerKeyInfo->OcKeyCode = OC_INPUT_TYPING_CLEAR_ALL;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyBackSpace) {
PickerKeyInfo->OcKeyCode = OC_INPUT_TYPING_BACKSPACE;
return;
}
if ( (Keys[0] == AppleHidUsbKbUsageKeyEnter)
|| (Keys[0] == AppleHidUsbKbUsageKeyReturn)
|| (Keys[0] == AppleHidUsbKbUsageKeyPadEnter))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_TYPING_CONFIRM;
return;
}
} else {
//
// Non-typing index key strokes.
//
//
// Only select OS if valid modifiers are in place.
//
if ((Modifiers & ~ValidBootModifiers) == 0) {
TriggerBoot = FALSE;
if ( (Keys[0] == AppleHidUsbKbUsageKeyEnter)
|| (Keys[0] == AppleHidUsbKbUsageKeyReturn)
|| (Keys[0] == AppleHidUsbKbUsageKeyPadEnter))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_CONTINUE;
TriggerBoot = TRUE;
}
STATIC_ASSERT (AppleHidUsbKbUsageKeyOne + 8 == AppleHidUsbKbUsageKeyNine, "Unexpected encoding");
if ((Keys[0] >= AppleHidUsbKbUsageKeyOne) && (Keys[0] <= AppleHidUsbKbUsageKeyNine)) {
PickerKeyInfo->OcKeyCode = (INTN)(Keys[0] - AppleHidUsbKbUsageKeyOne);
TriggerBoot = TRUE;
}
STATIC_ASSERT (AppleHidUsbKbUsageKeyA + 25 == AppleHidUsbKbUsageKeyZ, "Unexpected encoding");
if ((Keys[0] > AppleHidUsbKbUsageKeyA) && (Keys[0] <= AppleHidUsbKbUsageKeyZ)) {
PickerKeyInfo->OcKeyCode = (INTN)(Keys[0] - AppleHidUsbKbUsageKeyA + 9);
TriggerBoot = TRUE;
}
if (TriggerBoot) {
//
// In order to allow shift+tab, shift only applies at time of hitting
// enter or index, but if held then it enables Safe Mode.
//
if ((Modifiers & APPLE_MODIFIERS_SHIFT) != 0) {
if (OcGetArgumentFromCmd (Context->AppleBootArgs, "-x", L_STR_LEN ("-x"), NULL) == NULL) {
DEBUG ((DEBUG_INFO, "OCHK: Shift means -x\n"));
OcAppendArgumentToCmd (Context, Context->AppleBootArgs, "-x", L_STR_LEN ("-x"));
}
}
return;
}
}
//
// Apply navigation keys regardless of modifiers.
//
if (Keys[0] == AppleHidUsbKbUsageKeyUpArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_UP;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyDownArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_DOWN;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyLeftArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_LEFT;
return;
}
if (Keys[0] == AppleHidUsbKbUsageKeyRightArrow) {
PickerKeyInfo->OcKeyCode = OC_INPUT_RIGHT;
return;
}
if ( (Keys[0] == AppleHidUsbKbUsageKeyPgUp)
|| (Keys[0] == AppleHidUsbKbUsageKeyHome))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_TOP;
return;
}
if ( (Keys[0] == AppleHidUsbKbUsageKeyPgDn)
|| (Keys[0] == AppleHidUsbKbUsageKeyEnd))
{
PickerKeyInfo->OcKeyCode = OC_INPUT_BOTTOM;
return;
}
//
// Fn. keys should only apply if no modifiers held.
//
if (Modifiers == APPLE_MODIFIERS_NONE) {
STATIC_ASSERT (AppleHidUsbKbUsageKeyF1 + 11 == AppleHidUsbKbUsageKeyF12, "Unexpected encoding");
if ((Keys[0] >= AppleHidUsbKbUsageKeyF1) && (Keys[0] <= AppleHidUsbKbUsageKeyF12)) {
PickerKeyInfo->OcKeyCode = OC_INPUT_FUNCTIONAL (Keys[0] - AppleHidUsbKbUsageKeyF1 + 1);
return;
}
STATIC_ASSERT (AppleHidUsbKbUsageKeyF13 + 11 == AppleHidUsbKbUsageKeyF24, "Unexpected encoding");
if ((Keys[0] >= AppleHidUsbKbUsageKeyF13) && (Keys[0] <= AppleHidUsbKbUsageKeyF24)) {
PickerKeyInfo->OcKeyCode = OC_INPUT_FUNCTIONAL (Keys[0] - AppleHidUsbKbUsageKeyF13 + 13);
return;
}
}
}
}
//
// Return NO_ACTION here, since all non-null actions now feedback
// immediately to either picker, to allow UI response.
//
PickerKeyInfo->OcKeyCode = OC_INPUT_NO_ACTION;
}
STATIC
UINT64
EFIAPI
WaitForPickerKeyInfoGetEndTime (
IN UINT64 Timeout
)
{
if (Timeout == 0) {
return 0ULL;
}
return GetTimeInNanoSecond (GetPerformanceCounter ()) + Timeout * 1000000ULL;
}
STATIC
BOOLEAN
EFIAPI
WaitForPickerKeyInfo (
IN OUT OC_PICKER_CONTEXT *Context,
IN UINT64 EndTime,
IN OC_PICKER_KEY_MAP KeyFilter,
IN OUT OC_PICKER_KEY_INFO *PickerKeyInfo
)
{
OC_MODIFIER_MAP OldOcModifiers;
UINT64 CurrTime;
UINT64 LoopDelayStart;
LoopDelayStart = 0;
OldOcModifiers = PickerKeyInfo->OcModifiers;
//
// These hotkeys are normally parsed by boot.efi, and they work just fine
// when ShowPicker is disabled. On some BSPs, however, they may fail badly
// when ShowPicker is enabled, and for this reason we support these hotkeys
// within picker itself.
//
while (TRUE) {
GetPickerKeyInfo (Context, KeyFilter, PickerKeyInfo);
//
// All non-null actions (even internal) are now returned to picker for possible UI response.
//
if ((PickerKeyInfo->OcKeyCode != OC_INPUT_NO_ACTION) ||
(PickerKeyInfo->OcModifiers != OldOcModifiers) ||
(PickerKeyInfo->UnicodeChar != CHAR_NULL))
{
break;
}
if (EndTime != 0) {
CurrTime = GetTimeInNanoSecond (GetPerformanceCounter ());
if ((CurrTime != 0) && (CurrTime >= EndTime)) {
PickerKeyInfo->OcKeyCode = OC_INPUT_TIMEOUT;
break;
}
}
DEBUG_CODE_BEGIN ();
LoopDelayStart = AsmReadTsc ();
DEBUG_CODE_END ();
MicroSecondDelay (OC_MINIMAL_CPU_DELAY);
DEBUG_CODE_BEGIN ();
if (Context->KbDebug != NULL) {
Context->KbDebug->InstrumentLoopDelay (LoopDelayStart, AsmReadTsc ());
}
DEBUG_CODE_END ();
}
return PickerKeyInfo->OcModifiers != OldOcModifiers;
}
//
// Initialise picker keyboard handling.
//
EFI_STATUS
OcInitHotKeys (
IN OUT OC_PICKER_CONTEXT *Context
)
{
EFI_STATUS Status;
DEBUG ((DEBUG_INFO, "OCHK: InitHotKeys\n"));
ASSERT_EQUALS (Context->HotKeyContext, NULL);
//
// No kb debug unless initialiased on settings flag by a given picker itself.
//
Context->KbDebug = NULL;
Context->HotKeyContext = AllocatePool (sizeof (*(Context->HotKeyContext)));
if (Context->HotKeyContext == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Fn. ptrs.
//
Context->HotKeyContext->GetKeyInfo = GetPickerKeyInfo;
Context->HotKeyContext->GetKeyWaitEndTime = WaitForPickerKeyInfoGetEndTime;
Context->HotKeyContext->WaitForKeyInfo = WaitForPickerKeyInfo;
Context->HotKeyContext->FlushTypingBuffer = PickerFlushTypingBuffer;
Context->HotKeyContext->KeyMap = OcGetProtocol (&gAppleKeyMapAggregatorProtocolGuid, DEBUG_ERROR, "OcInitHotKeys", "AppleKeyMapAggregator");
if (Context->HotKeyContext->KeyMap == NULL) {
FreePool (Context->HotKeyContext);
Context->HotKeyContext = NULL;
return EFI_NOT_FOUND;
}
//
// Non-repeating keys, e.g. ESC and SPACE.
//
Status = OcInitKeyRepeatContext (
&Context->HotKeyContext->DoNotRepeatContext,
Context->HotKeyContext->KeyMap,
OC_HELD_KEYS_DEFAULT_SIZE,
0,
0,
TRUE
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "OCHK: Init non-repeating context - %r\n", Status));
FreePool (Context->HotKeyContext);
Context->HotKeyContext = NULL;
return Status;
}
//
// Typing handler, for most keys.
//
Status = OcRegisterTypingHandler (&Context->HotKeyContext->TypingContext);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "OCHK: Register typing handler - %r\n", Status));
OcFreeKeyRepeatContext (&Context->HotKeyContext->DoNotRepeatContext);
FreePool (Context->HotKeyContext);
Context->HotKeyContext = NULL;
return Status;
}
//
// Note: Raw AKMA is also still used for HotKeys, since we really do need
// three different types of keys response for fluent UI behaviour.
//
return EFI_SUCCESS;
}
//
// Free picker keyboard handling resources.
//
VOID
OcFreeHotKeys (
IN OC_PICKER_CONTEXT *Context
)
{
if (Context == NULL) {
return;
}
DEBUG ((DEBUG_INFO, "OCHK: FreeHotKeys\n"));
ASSERT (Context->HotKeyContext != NULL);
OcUnregisterTypingHandler (&Context->HotKeyContext->TypingContext);
OcFreeKeyRepeatContext (&Context->HotKeyContext->DoNotRepeatContext);
FreePool (Context->HotKeyContext);
Context->HotKeyContext = NULL;
}