mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
Co-authored-by: vit9696 <vit9696@users.noreply.github.com> Co-authored-by: Download-Fritz <download-fritz@outlook.com>
1163 lines
28 KiB
C
1163 lines
28 KiB
C
/** @file
|
|
Copyright (C) 2019, vit9696. All rights reserved.
|
|
|
|
All rights reserved.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
**/
|
|
|
|
#include "OpenRuntimePrivate.h"
|
|
|
|
#include <Guid/OcVariable.h>
|
|
#include <Guid/GlobalVariable.h>
|
|
#include <Guid/ImageAuthentication.h>
|
|
#include <Guid/MicrosoftVariable.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/OcBootManagementLib.h>
|
|
#include <Library/OcDevicePathLib.h>
|
|
#include <Library/OcGuardLib.h>
|
|
#include <Library/OcStringLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
/**
|
|
Runtime accessible variables (all read only).
|
|
**/
|
|
STATIC EFI_GET_TIME mStoredGetTime;
|
|
STATIC EFI_SET_TIME mStoredSetTime;
|
|
STATIC EFI_GET_WAKEUP_TIME mStoredGetWakeupTime;
|
|
STATIC EFI_SET_WAKEUP_TIME mStoredSetWakeupTime;
|
|
STATIC EFI_GET_VARIABLE mStoredGetVariable;
|
|
STATIC EFI_GET_NEXT_VARIABLE_NAME mStoredGetNextVariableName;
|
|
STATIC EFI_SET_VARIABLE mStoredSetVariable;
|
|
STATIC EFI_GET_NEXT_HIGH_MONO_COUNT mStoredGetNextHighMonotonicCount;
|
|
STATIC EFI_RESET_SYSTEM mStoredResetSystem;
|
|
|
|
/**
|
|
Configurations to use.
|
|
**/
|
|
OC_FWRT_CONFIG gMainConfig;
|
|
OC_FWRT_CONFIG gOverrideConfig;
|
|
OC_FWRT_CONFIG *gCurrentConfig;
|
|
|
|
/**
|
|
Boot phase accessible variables.
|
|
**/
|
|
STATIC EFI_EVENT mTranslateEvent;
|
|
STATIC EFI_GET_VARIABLE mCustomGetVariable;
|
|
STATIC BOOLEAN mKernelStarted;
|
|
|
|
/**
|
|
Current boot order before updating.
|
|
**/
|
|
STATIC UINT16 mTmpBootOrder[16];
|
|
STATIC UINTN mTmpBootOrderSize;
|
|
STATIC INT32 mOcBootOrderMap[16];
|
|
STATIC UINT8 mTmpBootOption[512];
|
|
STATIC UINTN mTmpBootOptionSize;
|
|
|
|
STATIC
|
|
VOID
|
|
WriteUnprotectorPrologue (
|
|
OUT BOOLEAN *Ints,
|
|
OUT BOOLEAN *Wp
|
|
)
|
|
{
|
|
IA32_CR0 Cr0;
|
|
|
|
if (gCurrentConfig->WriteUnprotector) {
|
|
*Ints = SaveAndDisableInterrupts ();
|
|
Cr0.UintN = AsmReadCr0 ();
|
|
if (Cr0.Bits.WP == 1) {
|
|
*Wp = TRUE;
|
|
Cr0.Bits.WP = 0;
|
|
AsmWriteCr0 (Cr0.UintN);
|
|
} else {
|
|
*Wp = FALSE;
|
|
}
|
|
} else {
|
|
*Ints = FALSE;
|
|
*Wp = FALSE;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
WriteUnprotectorEpilogue (
|
|
IN BOOLEAN Ints,
|
|
IN BOOLEAN Wp
|
|
)
|
|
{
|
|
IA32_CR0 Cr0;
|
|
|
|
if (gCurrentConfig->WriteUnprotector) {
|
|
if (Wp) {
|
|
Cr0.UintN = AsmReadCr0 ();
|
|
Cr0.Bits.WP = 1;
|
|
AsmWriteCr0 (Cr0.UintN);
|
|
}
|
|
|
|
if (Ints) {
|
|
EnableInterrupts ();
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
IsEfiBootVar (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
OUT BOOLEAN *IsBootOrder OPTIONAL,
|
|
OUT INT32 *BootNum OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
CHAR16 Digit;
|
|
INT32 TmpNum;
|
|
|
|
if (!CompareGuid (VendorGuid, &gEfiGlobalVariableGuid)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (StrnCmp (L"Boot", VariableName, L_STR_LEN (L"Boot")) != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (IsBootOrder != NULL && StrCmp (VariableName, L"BootOrder") == 0) {
|
|
*IsBootOrder = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
if (BootNum != NULL
|
|
&& StrLen (VariableName) == L_STR_LEN (L"Boot####")) {
|
|
TmpNum = 0;
|
|
|
|
for (Index = 0; Index < 4; ++Index) {
|
|
Digit = VariableName[L_STR_LEN (L"Boot") + Index];
|
|
if (Digit >= '0' && Digit <= '9') {
|
|
TmpNum = 16 * TmpNum + (INT32) (Digit - '0');
|
|
} else if (Digit >= 'A' && Digit <= 'F') {
|
|
TmpNum = 16 * TmpNum + (INT32) (Digit - 'A') + 10;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
*BootNum = TmpNum;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
IsOcBootVar (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid
|
|
)
|
|
{
|
|
if (!CompareGuid (VendorGuid, &gOcVendorVariableGuid)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (StrnCmp (L"Boot", VariableName, L_STR_LEN (L"Boot")) != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapGetTime (
|
|
OUT EFI_TIME *Time,
|
|
OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = mStoredGetTime (
|
|
Time,
|
|
Capabilities
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// On old AMI firmwares (like the one found in GA-Z87X-UD4H) there is a chance
|
|
// of getting 2047 (EFI_UNSPECIFIED_TIMEZONE) from GetTime. This is valid,
|
|
// yet is disliked by some software including but not limited to UEFI Shell.
|
|
// See the patch: https://lists.01.org/pipermail/edk2-devel/2018-May/024534.html
|
|
// As a workaround we make sure this does not happen at all.
|
|
//
|
|
if (Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) {
|
|
Time->TimeZone = 0;
|
|
}
|
|
}
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapSetTime (
|
|
IN EFI_TIME *Time
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = mStoredSetTime (
|
|
Time
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapGetWakeupTime (
|
|
OUT BOOLEAN *Enabled,
|
|
OUT BOOLEAN *Pending,
|
|
OUT EFI_TIME *Time
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = mStoredGetWakeupTime (
|
|
Enabled,
|
|
Pending,
|
|
Time
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapSetWakeupTime (
|
|
IN BOOLEAN Enable,
|
|
IN EFI_TIME *Time OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = mStoredSetWakeupTime (
|
|
Enable,
|
|
Time
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapGetVariable (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
OUT UINT32 *Attributes OPTIONAL,
|
|
IN OUT UINTN *DataSize,
|
|
OUT VOID *Data OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
//
|
|
// Perform early checks for speedup.
|
|
//
|
|
if (VariableName == NULL
|
|
|| VendorGuid == NULL
|
|
|| DataSize == NULL
|
|
|| (Data == NULL && *DataSize != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Abort access to write-only variables.
|
|
//
|
|
if (gCurrentConfig->RestrictedVariables
|
|
&& mKernelStarted
|
|
&& CompareGuid (VendorGuid, &gOcWriteOnlyVariableGuid)) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Redirect Boot-prefixed variables to our own GUID.
|
|
//
|
|
if (gCurrentConfig->BootVariableRedirect && IsEfiBootVar (VariableName, VendorGuid, NULL, NULL)) {
|
|
VendorGuid = &gOcVendorVariableGuid;
|
|
}
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = (mCustomGetVariable != NULL ? mCustomGetVariable : mStoredGetVariable) (
|
|
VariableName,
|
|
VendorGuid,
|
|
Attributes,
|
|
DataSize,
|
|
Data
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapGetNextVariableName (
|
|
IN OUT UINTN *VariableNameSize,
|
|
IN OUT CHAR16 *VariableName,
|
|
IN OUT EFI_GUID *VendorGuid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN Size;
|
|
CHAR16 TempName[256];
|
|
EFI_GUID TempGuid;
|
|
BOOLEAN StartBootVar;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
//
|
|
// Perform initial checks as per spec. Last check is part of:
|
|
// Null-terminator is not found in the first VariableNameSize
|
|
// bytes of the input VariableName buffer.
|
|
//
|
|
if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Checking that VariableName never exceeds *VariableNameSize and is always null-terminated.
|
|
//
|
|
Size = 0;
|
|
for (Index = 0; Index < *VariableNameSize; ++Index) {
|
|
if (VariableName[Index] == L'\0') {
|
|
Size = (Index + 1) * sizeof (CHAR16);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also assume that too large variables do not exist, as we cannot work with them anyway.
|
|
//
|
|
if (Size == 0 || Size > sizeof (TempName)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
//
|
|
// In case we do not redirect, simply do nothing.
|
|
//
|
|
if (!gCurrentConfig->BootVariableRedirect) {
|
|
Status = mStoredGetNextVariableName (VariableNameSize, VariableName, VendorGuid);
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Copy vendor and variable name to internal buffer.
|
|
//
|
|
CopyGuid (&TempGuid, VendorGuid);
|
|
StrnCpyS (TempName, ARRAY_SIZE (TempName), VariableName, StrLen (VariableName));
|
|
|
|
StartBootVar = FALSE;
|
|
|
|
//
|
|
// In case we are not yet iterating EfiBoot variables,
|
|
// then go through the whole variable list and return
|
|
// variables except EfiBoot.
|
|
//
|
|
if (!IsEfiBootVar (TempName, &TempGuid, NULL, NULL)) {
|
|
while (TRUE) {
|
|
//
|
|
// Request for variables.
|
|
//
|
|
Size = sizeof (TempName);
|
|
Status = mStoredGetNextVariableName (&Size, TempName, &TempGuid);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (!IsEfiBootVar (TempName, &TempGuid, NULL, NULL)) {
|
|
Size = StrSize (TempName); ///< Not guaranteed to be updated with EFI_SUCCESS.
|
|
|
|
if (*VariableNameSize >= Size) {
|
|
//
|
|
// Return this variable.
|
|
//
|
|
CopyGuid (VendorGuid, &TempGuid);
|
|
StrnCpyS (
|
|
VariableName,
|
|
*VariableNameSize / sizeof (CHAR16),
|
|
TempName,
|
|
StrLen (TempName)
|
|
);
|
|
*VariableNameSize = Size; ///< This is NOT explicitly required by the spec.
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
//
|
|
// Request more space.
|
|
//
|
|
*VariableNameSize = Size;
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return Status;
|
|
} else {
|
|
//
|
|
// EfiBoot variables are handled outside of this loop.
|
|
//
|
|
}
|
|
} else if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
//
|
|
// This means sizeof (TempName) is too small for this system.
|
|
// At this step we cannot do anything, but let's replace error
|
|
// with something sensible.
|
|
//
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return EFI_DEVICE_ERROR;
|
|
} else if (Status == EFI_NOT_FOUND) {
|
|
//
|
|
// End of normal variable list.
|
|
// This means it is the time for boot variables to be searched
|
|
// from the beginning.
|
|
//
|
|
StartBootVar = TRUE;
|
|
break;
|
|
} else {
|
|
//
|
|
// We got EFI_UNSUPPORTED, EFI_DEVICE_ERROR or EFI_INVALID_PARAMETER.
|
|
// Return as is.
|
|
//
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle EfiBoot variables now.
|
|
// When StartBootVar is TRUE we start from the beginning.
|
|
// Otherwise we have boot variable in TempGuid/TempName.
|
|
//
|
|
|
|
if (StartBootVar) {
|
|
//
|
|
// It is not required to zero guid, but let's do it just in case.
|
|
//
|
|
TempName[0] = L'\0';
|
|
ZeroMem (&TempGuid, sizeof (TempGuid));
|
|
} else {
|
|
//
|
|
// Switch to real GUID as stored in variable storage.
|
|
//
|
|
CopyGuid (&TempGuid, &gOcVendorVariableGuid);
|
|
}
|
|
|
|
//
|
|
// Find next EfiBoot variable.
|
|
//
|
|
while (TRUE) {
|
|
Size = sizeof (TempName);
|
|
Status = mStoredGetNextVariableName (&Size, TempName, &TempGuid);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (IsOcBootVar (TempName, &TempGuid)) {
|
|
Size = StrSize (TempName); ///< Not guaranteed to be updated with EFI_SUCCESS.
|
|
|
|
if (*VariableNameSize >= Size) {
|
|
//
|
|
// Return this variable.
|
|
//
|
|
CopyGuid (VendorGuid, &gEfiGlobalVariableGuid);
|
|
StrnCpyS (
|
|
VariableName,
|
|
*VariableNameSize / sizeof (CHAR16),
|
|
TempName,
|
|
StrLen (TempName)
|
|
);
|
|
*VariableNameSize = Size; ///< This is NOT explicitly required by the spec.
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
//
|
|
// Request more space.
|
|
//
|
|
*VariableNameSize = Size;
|
|
Status = EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return Status;
|
|
}
|
|
} else {
|
|
//
|
|
// This is some error, but let us not care which and just exit cleanly.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Report this is the end.
|
|
//
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
HandleBootOrderFallback (
|
|
UINT16 *NewBootOrder,
|
|
UINTN NewBootOrderCount,
|
|
UINT16 *OldBootOrder,
|
|
UINTN OldBootOrderCount
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
EFI_STATUS Status;
|
|
INT32 BootNum;
|
|
|
|
//
|
|
// Look for BootOrder entry addition, assume only one exists if at all.
|
|
// Ignore deletions, as we can handle them later.
|
|
//
|
|
BootNum = -1;
|
|
for (Index = 0; Index < NewBootOrderCount; ++Index) {
|
|
for (Index2 = 0; Index2 < OldBootOrderCount; ++Index2) {
|
|
if (NewBootOrder[Index] == OldBootOrder[Index2]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index2 == OldBootOrderCount) {
|
|
BootNum = NewBootOrder[Index];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (BootNum < 0) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// BootNum now contains the variable added to OcVendorVariable BootOrder.
|
|
// Check our pending mapping and submit it to EfiGlobalVariable BootOrder if present.
|
|
//
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (mOcBootOrderMap); ++Index) {
|
|
if (mOcBootOrderMap[Index] >= 0 && mOcBootOrderMap[Index] == BootNum) {
|
|
//
|
|
// Erase from pending order.
|
|
//
|
|
mOcBootOrderMap[Index] = -1;
|
|
BootNum = (INT32) (Index + OC_GL_BOOT_OPTION_START);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == ARRAY_SIZE (mOcBootOrderMap)) {
|
|
//
|
|
// We do not know how to handle this variable, just ignore.
|
|
// Most likely we have already wrote it.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// BootNum now contains the variable we want to add EfiGlobalVariable BootOrder.
|
|
// Add it, reusing mTmpBootOrder array.
|
|
//
|
|
mTmpBootOrderSize = sizeof (mTmpBootOrder) - sizeof (mTmpBootOrder[0]);
|
|
Status = mStoredGetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&mTmpBootOrderSize,
|
|
mTmpBootOrder
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_NOT_FOUND) {
|
|
mTmpBootOrderSize = 0;
|
|
} else {
|
|
//
|
|
// We did not fit BootOrder into our list, just ignore.
|
|
// This should not happen.
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
mTmpBootOrder[mTmpBootOrderSize / sizeof (mTmpBootOrder[0])] = (UINT16) BootNum;
|
|
mTmpBootOrderSize += sizeof (mTmpBootOrder[0]);
|
|
|
|
mStoredSetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
mTmpBootOrderSize,
|
|
mTmpBootOrder
|
|
);
|
|
}
|
|
|
|
STATIC
|
|
EFI_DEVICE_PATH *
|
|
GetBootEntryDevicePath (
|
|
EFI_LOAD_OPTION *LoadOption,
|
|
UINTN LoadOptionSize,
|
|
BOOLEAN ForFallback
|
|
)
|
|
{
|
|
UINT8 *LoadOptionPtr;
|
|
CONST CHAR16 *Description;
|
|
UINTN DescriptionSize;
|
|
UINT16 FilePathListSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *FilePathList;
|
|
EFI_DEVICE_PATH_PROTOCOL *CurrNode;
|
|
FILEPATH_DEVICE_PATH *LastNode;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
UINTN PathLen;
|
|
UINTN CurrLen;
|
|
|
|
if (LoadOptionSize < sizeof (*LoadOption)
|
|
|| (LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0
|
|
|| (LoadOption->Attributes & LOAD_OPTION_CATEGORY) != LOAD_OPTION_CATEGORY_BOOT) {
|
|
return NULL;
|
|
}
|
|
|
|
FilePathListSize = LoadOption->FilePathListLength;
|
|
|
|
LoadOptionPtr = (UINT8 *) (LoadOption + 1);
|
|
LoadOptionSize -= sizeof (*LoadOption);
|
|
|
|
if (LoadOption->FilePathListLength > LoadOptionSize) {
|
|
return NULL;
|
|
}
|
|
|
|
LoadOptionSize -= FilePathListSize;
|
|
|
|
Description = (CHAR16 *) LoadOptionPtr;
|
|
DescriptionSize = StrnSizeS (Description, (LoadOptionSize / sizeof (CHAR16)));
|
|
if (DescriptionSize > LoadOptionSize) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Just discard all macOS entries.
|
|
//
|
|
if (ForFallback && StrCmp (Description, L"Mac OS X") == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
LoadOptionPtr += DescriptionSize;
|
|
LoadOptionSize -= DescriptionSize;
|
|
|
|
FilePathList = (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr;
|
|
if (!IsDevicePathValid (FilePathList, FilePathListSize)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Ignore checks for already stored paths.
|
|
//
|
|
if (!ForFallback) {
|
|
return FilePathList;
|
|
}
|
|
|
|
LastNode = NULL;
|
|
|
|
for (CurrNode = FilePathList; !IsDevicePathEnd (CurrNode); CurrNode = NextDevicePathNode (CurrNode)) {
|
|
if (DevicePathType (CurrNode) == MEDIA_DEVICE_PATH && DevicePathSubType (CurrNode) == MEDIA_FILEPATH_DP) {
|
|
LastNode = (FILEPATH_DEVICE_PATH *) CurrNode;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also discard entries that do not point to files (or maybe folders).
|
|
//
|
|
if (LastNode == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// In addition discard several predefined names that are known to be macOS related.
|
|
// There obviously are more, but we are unlikely to see them.
|
|
//
|
|
STATIC CONST CHAR16 *mAppleBootloaders[] = {
|
|
L"boot.efi",
|
|
L"ThorUtil.efi",
|
|
L"MultiUpdater.efi"
|
|
};
|
|
|
|
PathLen = OcFileDevicePathNameLen (LastNode);
|
|
for (Index = 0; Index < ARRAY_SIZE (mAppleBootloaders); ++Index) {
|
|
CurrLen = StrLen (mAppleBootloaders[Index]);
|
|
if (PathLen >= CurrLen) {
|
|
Index2 = PathLen - CurrLen;
|
|
if ((Index2 == 0 || LastNode->PathName[Index2 - 1] == L'\\')
|
|
&& CompareMem (&LastNode->PathName[Index2], mAppleBootloaders[Index], CurrLen) == 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FilePathList;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
HandleBootEntryFallback (
|
|
UINT16 OcBootNum,
|
|
EFI_LOAD_OPTION *LoadOption,
|
|
UINTN LoadOptionSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
CHAR16 BootOption[L_STR_LEN (L"Boot####") + 1];
|
|
INT32 BootNum;
|
|
EFI_DEVICE_PATH *SelfPath;
|
|
EFI_DEVICE_PATH *OtherPath;
|
|
|
|
//
|
|
// Check if this device path is eligible for fallback entry.
|
|
//
|
|
SelfPath = GetBootEntryDevicePath (LoadOption, LoadOptionSize, TRUE);
|
|
if (SelfPath == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check if this boot entry is not already present.
|
|
//
|
|
mTmpBootOrderSize = sizeof (mTmpBootOrder);
|
|
Status = mStoredGetVariable (
|
|
L"BootOrder",
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&mTmpBootOrderSize,
|
|
mTmpBootOrder
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_NOT_FOUND) {
|
|
mTmpBootOrderSize = 0;
|
|
} else {
|
|
//
|
|
// We cannot handle this boot order. Should not happen.
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
BootOption[0] = 'B';
|
|
BootOption[1] = 'o';
|
|
BootOption[2] = 'o';
|
|
BootOption[3] = 't';
|
|
BootOption[8] = '\0';
|
|
|
|
for (Index = 0; Index < mTmpBootOrderSize / sizeof (mTmpBootOrder[0]); ++Index) {
|
|
BootOption[4] = "0123456789ABCDEF"[((UINT32) mTmpBootOrder[Index] >> 12U) & 0xFU];
|
|
BootOption[5] = "0123456789ABCDEF"[((UINT32) mTmpBootOrder[Index] >> 8U) & 0xFU];
|
|
BootOption[6] = "0123456789ABCDEF"[((UINT32) mTmpBootOrder[Index] >> 4U) & 0xFU];
|
|
BootOption[7] = "0123456789ABCDEF"[((UINT32) mTmpBootOrder[Index] ) & 0xFU];
|
|
|
|
mTmpBootOptionSize = sizeof (mTmpBootOption);
|
|
Status = mStoredGetVariable (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
NULL,
|
|
&mTmpBootOptionSize,
|
|
mTmpBootOption
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Assume orphan entry or whatever it is.
|
|
// It can technically not fit, but this is not expected.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
OtherPath = GetBootEntryDevicePath ((EFI_LOAD_OPTION *) &mTmpBootOption[0], mTmpBootOptionSize, FALSE);
|
|
if (OtherPath == NULL) {
|
|
//
|
|
// Assume invalid entry or whatever it is, just ignore.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check option device paths for equality.
|
|
// In case they match, just ignore this option.
|
|
//
|
|
if (IsDevicePathEqual (SelfPath, OtherPath)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now want to fallback this option, and need to decide where.
|
|
// Check if this boot entry is not written already.
|
|
//
|
|
BootNum = -1;
|
|
for (Index = 0; Index < ARRAY_SIZE (mOcBootOrderMap); ++Index) {
|
|
if (mOcBootOrderMap[Index] >= 0) {
|
|
if ((UINT16) mOcBootOrderMap[Index] == OcBootNum) {
|
|
BootNum = (INT32) (OC_GL_BOOT_OPTION_START + Index);
|
|
break;
|
|
}
|
|
} else if (BootNum < 0) {
|
|
for (Index2 = 0; Index2 < mTmpBootOrderSize / sizeof (mTmpBootOrder[0]); ++Index2) {
|
|
if (mTmpBootOrder[Index2] == OC_GL_BOOT_OPTION_START + Index) {
|
|
break;
|
|
}
|
|
}
|
|
if (Index2 == mTmpBootOrderSize / sizeof (mTmpBootOrder[0])) {
|
|
BootNum = (INT32) (OC_GL_BOOT_OPTION_START + Index);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Unless zero or more we cannot write this entry. Should not happen.
|
|
//
|
|
if (BootNum < 0) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Store the variable.
|
|
//
|
|
mOcBootOrderMap[BootNum - OC_GL_BOOT_OPTION_START] = OcBootNum;
|
|
BootOption[4] = "0123456789ABCDEF"[((UINT32) BootNum >> 12U) & 0xFU];
|
|
BootOption[5] = "0123456789ABCDEF"[((UINT32) BootNum >> 8U) & 0xFU];
|
|
BootOption[6] = "0123456789ABCDEF"[((UINT32) BootNum >> 4U) & 0xFU];
|
|
BootOption[7] = "0123456789ABCDEF"[((UINT32) BootNum ) & 0xFU];
|
|
mStoredSetVariable (
|
|
BootOption,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
LoadOptionSize,
|
|
LoadOption
|
|
);
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapSetVariable (
|
|
IN CHAR16 *VariableName,
|
|
IN EFI_GUID *VendorGuid,
|
|
IN UINT32 Attributes,
|
|
IN UINTN DataSize,
|
|
IN VOID *Data
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
INT32 BootNum;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
BOOLEAN IsBootOrder;
|
|
BOOLEAN DoFallback;
|
|
|
|
//
|
|
// Abort access when running with read-only NVRAM.
|
|
//
|
|
if (gCurrentConfig->WriteProtection && (Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Abort access to read-only variables.
|
|
//
|
|
if (gCurrentConfig->RestrictedVariables
|
|
&& mKernelStarted
|
|
&& CompareGuid (VendorGuid, &gOcReadOnlyVariableGuid)) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Abort access to SecureBoot variables.
|
|
//
|
|
if (gCurrentConfig->ProtectSecureBoot) {
|
|
if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid)) {
|
|
if (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0
|
|
|| StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
} else if (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid)) {
|
|
if (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0
|
|
|| StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0
|
|
|| StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0
|
|
|| StrCmp (VariableName, L"dbr" /* EFI_IMAGE_SECURITY_DATABASE3 */) == 0) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
} else if (CompareGuid (VendorGuid, &gMicrosoftVariableGuid)) {
|
|
//
|
|
// CurrentActivePolicy, CurrentPolicy, RevocationList, WindowsBootChainSvn, whatever.
|
|
//
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Redirect Boot-prefixed variables to our own GUID.
|
|
//
|
|
DoFallback = FALSE;
|
|
IsBootOrder = FALSE;
|
|
BootNum = -1;
|
|
if (gCurrentConfig->BootVariableRedirect
|
|
&& IsEfiBootVar (VariableName, VendorGuid, &IsBootOrder, &BootNum)) {
|
|
VendorGuid = &gOcVendorVariableGuid;
|
|
DoFallback = gCurrentConfig->BootVariableFallback;
|
|
}
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
//
|
|
// Preserve current BootOrder.
|
|
//
|
|
if (DoFallback && IsBootOrder) {
|
|
mTmpBootOrderSize = sizeof (mTmpBootOrder);
|
|
Status = mStoredGetVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
NULL,
|
|
&mTmpBootOrderSize,
|
|
mTmpBootOrder
|
|
);
|
|
|
|
//
|
|
// This should never happen, but if it does, just ignore this BootOrder update
|
|
// as we simply cannot track the difference between current and next.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
if (Status == EFI_NOT_FOUND) {
|
|
mTmpBootOrderSize = 0;
|
|
} else {
|
|
IsBootOrder = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = mStoredSetVariable (
|
|
VariableName,
|
|
VendorGuid,
|
|
Attributes,
|
|
DataSize,
|
|
Data
|
|
);
|
|
|
|
if (!EFI_ERROR (Status) && DoFallback && Data != NULL && DataSize > 0) {
|
|
//
|
|
// So, we need to give back our changes.
|
|
//
|
|
if (IsBootOrder && OC_TYPE_ALIGNED (UINT16, Data)) {
|
|
HandleBootOrderFallback (
|
|
Data,
|
|
DataSize / sizeof (UINT16),
|
|
mTmpBootOrder,
|
|
mTmpBootOrderSize / sizeof (UINT16)
|
|
);
|
|
} else if (BootNum >= 0) {
|
|
HandleBootEntryFallback (
|
|
(UINT16) BootNum,
|
|
Data,
|
|
DataSize
|
|
);
|
|
}
|
|
}
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
EFIAPI
|
|
WrapGetNextHighMonotonicCount (
|
|
OUT UINT32 *Count
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
Status = mStoredGetNextHighMonotonicCount (
|
|
Count
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
WrapResetSystem (
|
|
IN EFI_RESET_TYPE ResetType,
|
|
IN EFI_STATUS ResetStatus,
|
|
IN UINTN DataSize,
|
|
IN VOID *ResetData OPTIONAL
|
|
)
|
|
{
|
|
BOOLEAN Ints;
|
|
BOOLEAN Wp;
|
|
|
|
WriteUnprotectorPrologue (&Ints, &Wp);
|
|
|
|
mStoredResetSystem (
|
|
ResetType,
|
|
ResetStatus,
|
|
DataSize,
|
|
ResetData
|
|
);
|
|
|
|
WriteUnprotectorEpilogue (Ints, Wp);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FwOnGetVariable (
|
|
IN EFI_GET_VARIABLE GetVariable,
|
|
OUT EFI_GET_VARIABLE *OrgGetVariable OPTIONAL
|
|
)
|
|
{
|
|
if (mCustomGetVariable != NULL) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
mCustomGetVariable = GetVariable;
|
|
|
|
if (OrgGetVariable != NULL) {
|
|
*OrgGetVariable = mStoredGetVariable;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
EFIAPI
|
|
TranslateAddressesHandler (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredGetTime);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredSetTime);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredGetWakeupTime);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredSetWakeupTime);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredGetVariable);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredGetNextVariableName);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredSetVariable);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredGetNextHighMonotonicCount);
|
|
gRT->ConvertPointer (0, (VOID **) &mStoredResetSystem);
|
|
|
|
gRT->ConvertPointer (0, (VOID **) &gCurrentConfig);
|
|
mCustomGetVariable = NULL;
|
|
|
|
//
|
|
// Ideally we do that from ExitBootServices, but VirtualAddressChange is fine as well.
|
|
//
|
|
mKernelStarted = TRUE;
|
|
}
|
|
|
|
VOID
|
|
RedirectRuntimeServices (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (mOcBootOrderMap); ++Index) {
|
|
mOcBootOrderMap[Index] = -1;
|
|
}
|
|
|
|
mStoredGetTime = gRT->GetTime;
|
|
mStoredSetTime = gRT->SetTime;
|
|
mStoredGetWakeupTime = gRT->GetWakeupTime;
|
|
mStoredSetWakeupTime = gRT->SetWakeupTime;
|
|
mStoredGetVariable = gRT->GetVariable;
|
|
mStoredGetNextVariableName = gRT->GetNextVariableName;
|
|
mStoredSetVariable = gRT->SetVariable;
|
|
mStoredGetNextHighMonotonicCount = gRT->GetNextHighMonotonicCount;
|
|
mStoredResetSystem = gRT->ResetSystem;
|
|
|
|
gRT->GetTime = WrapGetTime;
|
|
gRT->SetTime = WrapSetTime;
|
|
gRT->GetWakeupTime = WrapGetWakeupTime;
|
|
gRT->SetWakeupTime = WrapSetWakeupTime;
|
|
gRT->GetVariable = WrapGetVariable;
|
|
gRT->GetNextVariableName = WrapGetNextVariableName;
|
|
gRT->SetVariable = WrapSetVariable;
|
|
gRT->GetNextHighMonotonicCount = WrapGetNextHighMonotonicCount;
|
|
gRT->ResetSystem = WrapResetSystem;
|
|
|
|
gRT->Hdr.CRC32 = 0;
|
|
gBS->CalculateCrc32 (gRT, gRT->Hdr.HeaderSize, &gRT->Hdr.CRC32);
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
|
|
TPL_CALLBACK,
|
|
TranslateAddressesHandler,
|
|
NULL,
|
|
&mTranslateEvent
|
|
);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|