mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
1722 lines
50 KiB
C
1722 lines
50 KiB
C
/** @file
|
|
Copyright (C) 2019-2021, vit9696, mikebeaton. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
#include <Library/OcMainLib.h>
|
|
#include "BootManagementInternal.h"
|
|
|
|
#include <Guid/AppleFile.h>
|
|
#include <Guid/AppleVariable.h>
|
|
#include <Guid/GlobalVariable.h>
|
|
#include <Guid/Gpt.h>
|
|
#include <Guid/OcVariable.h>
|
|
|
|
#include <Protocol/DevicePath.h>
|
|
#include <Protocol/LoadedImage.h>
|
|
#include <Protocol/OcFirmwareRuntime.h>
|
|
#include <Protocol/SimpleFileSystem.h>
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/OcDebugLogLib.h>
|
|
#include <Library/OcDevicePathLib.h>
|
|
#include <Library/OcFileLib.h>
|
|
#include <Library/OcStringLib.h>
|
|
#include <Library/OcVariableLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
///
|
|
/// Template for OpenCore custom boot entry DevicePath.
|
|
///
|
|
STATIC CONST OC_CUSTOM_BOOT_DEVICE_PATH_DECL mOcCustomBootDevPathTemplate = {
|
|
{
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_VENDOR_DP,
|
|
{ sizeof (VENDOR_DEVICE_PATH), 0 }
|
|
},
|
|
OC_CUSTOM_BOOT_DEVICE_PATH_GUID
|
|
},
|
|
{
|
|
MEDIA_DEVICE_PATH,
|
|
MEDIA_FILEPATH_DP,
|
|
{ SIZE_OF_FILEPATH_DEVICE_PATH, 0 }
|
|
}
|
|
};
|
|
|
|
///
|
|
/// Template for Boot Entry Protocol custom boot entry DevicePath.
|
|
///
|
|
STATIC CONST OC_ENTRY_PROTOCOL_DEVICE_PATH_DECL mOcEntryProtocolDevPathTemplate = {
|
|
{
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_VENDOR_DP,
|
|
{ sizeof (VENDOR_DEVICE_PATH) + sizeof (EFI_GUID), 0 }
|
|
},
|
|
OC_ENTRY_PROTOCOL_DEVICE_PATH_GUID
|
|
},
|
|
EFI_PART_TYPE_UNUSED_GUID,
|
|
{
|
|
MEDIA_DEVICE_PATH,
|
|
MEDIA_FILEPATH_DP,
|
|
{ SIZE_OF_FILEPATH_DEVICE_PATH, 0 }
|
|
}
|
|
};
|
|
|
|
CONST OC_CUSTOM_BOOT_DEVICE_PATH *
|
|
InternalGetOcCustomDevPath (
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
UINTN DevicePathSize;
|
|
INTN CmpResult;
|
|
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
|
|
|
|
DevicePathSize = GetDevicePathSize (DevicePath);
|
|
if (DevicePathSize < SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH) {
|
|
return NULL;
|
|
}
|
|
|
|
CmpResult = CompareMem (
|
|
DevicePath,
|
|
&mOcCustomBootDevPathTemplate.Header,
|
|
sizeof (mOcCustomBootDevPathTemplate.Header)
|
|
);
|
|
if (CmpResult != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
CustomDevPath = (CONST OC_CUSTOM_BOOT_DEVICE_PATH *)DevicePath;
|
|
if ( (CustomDevPath->EntryName.Header.Type != MEDIA_DEVICE_PATH)
|
|
|| (CustomDevPath->EntryName.Header.SubType != MEDIA_FILEPATH_DP))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return CustomDevPath;
|
|
}
|
|
|
|
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *
|
|
InternalGetOcEntryProtocolDevPath (
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
UINTN DevicePathSize;
|
|
INTN CmpResult;
|
|
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *EntryProtocolDevPath;
|
|
|
|
DevicePathSize = GetDevicePathSize (DevicePath);
|
|
if (DevicePathSize < SIZE_OF_OC_ENTRY_PROTOCOL_DEVICE_PATH) {
|
|
return NULL;
|
|
}
|
|
|
|
CmpResult = CompareMem (
|
|
DevicePath,
|
|
&mOcEntryProtocolDevPathTemplate.Header,
|
|
sizeof (mOcEntryProtocolDevPathTemplate.Header)
|
|
);
|
|
if (CmpResult != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
EntryProtocolDevPath = (CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *)DevicePath;
|
|
if ( (EntryProtocolDevPath->EntryName.Header.Type != MEDIA_DEVICE_PATH)
|
|
|| (EntryProtocolDevPath->EntryName.Header.SubType != MEDIA_FILEPATH_DP))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return EntryProtocolDevPath;
|
|
}
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *
|
|
InternalGetBootOptionPath (
|
|
IN EFI_LOAD_OPTION *LoadOption,
|
|
IN UINTN LoadOptionSize
|
|
)
|
|
{
|
|
UINT8 *LoadOptionPtr;
|
|
|
|
CHAR16 *Description;
|
|
UINTN DescriptionSize;
|
|
UINT16 FilePathListSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *FilePathList;
|
|
|
|
FilePathListSize = LoadOption->FilePathListLength;
|
|
|
|
LoadOptionPtr = (UINT8 *)(LoadOption + 1);
|
|
LoadOptionSize -= sizeof (*LoadOption);
|
|
|
|
if (FilePathListSize > LoadOptionSize) {
|
|
return NULL;
|
|
}
|
|
|
|
LoadOptionSize -= FilePathListSize;
|
|
|
|
STATIC_ASSERT (
|
|
sizeof (*LoadOption) % BASE_ALIGNOF (CHAR16) == 0,
|
|
"The following accesses may be unaligned."
|
|
);
|
|
|
|
Description = (CHAR16 *)(VOID *)LoadOptionPtr;
|
|
DescriptionSize = StrnSizeS (Description, (LoadOptionSize / sizeof (CHAR16)));
|
|
if (DescriptionSize > LoadOptionSize) {
|
|
return NULL;
|
|
}
|
|
|
|
LoadOptionPtr += DescriptionSize;
|
|
|
|
FilePathList = (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr;
|
|
if (!IsDevicePathValid (FilePathList, FilePathListSize)) {
|
|
return NULL;
|
|
}
|
|
|
|
return FilePathList;
|
|
}
|
|
|
|
VOID
|
|
InternalDebugBootEnvironment (
|
|
IN CONST UINT16 *BootOrder,
|
|
IN EFI_GUID *BootGuid,
|
|
IN UINTN BootOrderCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *UefiDevicePath;
|
|
UINTN UefiDevicePathSize;
|
|
CHAR16 *DevicePathText;
|
|
UINTN Index;
|
|
INT32 Predefined;
|
|
EFI_LOAD_OPTION *LoadOption;
|
|
UINTN LoadOptionSize;
|
|
|
|
STATIC CONST CHAR16 *AppleDebugVariables[] = {
|
|
L"efi-boot-device-data",
|
|
L"efi-boot-next-data",
|
|
L"efi-backup-boot-device-data",
|
|
L"efi-apple-recovery-data"
|
|
};
|
|
|
|
STATIC CONST UINT16 ApplePredefinedVariables[] = {
|
|
0x80, 0x81, 0x82
|
|
};
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (AppleDebugVariables); ++Index) {
|
|
Status = GetVariable2 (
|
|
AppleDebugVariables[Index],
|
|
&gAppleBootVariableGuid,
|
|
(VOID **)&UefiDevicePath,
|
|
&UefiDevicePathSize
|
|
);
|
|
if (!EFI_ERROR (Status) && IsDevicePathValid (UefiDevicePath, UefiDevicePathSize)) {
|
|
DevicePathText = ConvertDevicePathToText (UefiDevicePath, FALSE, FALSE);
|
|
if (DevicePathText != NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: %s = %s\n", AppleDebugVariables[Index], DevicePathText));
|
|
FreePool (DevicePathText);
|
|
FreePool (UefiDevicePath);
|
|
continue;
|
|
}
|
|
|
|
FreePool (UefiDevicePath);
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OCB: %s - %r\n", AppleDebugVariables[Index], Status));
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OCB: Dumping BootOrder\n"));
|
|
|
|
for (Predefined = 0; Predefined < 2; ++Predefined) {
|
|
for (Index = 0; Index < BootOrderCount; ++Index) {
|
|
LoadOption = OcGetBootOptionData (
|
|
&LoadOptionSize,
|
|
BootOrder[Index],
|
|
BootGuid
|
|
);
|
|
if (LoadOption == NULL) {
|
|
continue;
|
|
}
|
|
|
|
UefiDevicePath = InternalGetBootOptionPath (LoadOption, LoadOptionSize);
|
|
if (UefiDevicePath == NULL) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: %u -> Boot%04x - failed to read\n",
|
|
(UINT32)Index,
|
|
BootOrder[Index]
|
|
));
|
|
FreePool (LoadOption);
|
|
continue;
|
|
}
|
|
|
|
DevicePathText = ConvertDevicePathToText (UefiDevicePath, FALSE, FALSE);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: %u -> Boot%04x = %s\n",
|
|
(UINT32)Index,
|
|
BootOrder[Index],
|
|
DevicePathText
|
|
));
|
|
if (DevicePathText != NULL) {
|
|
FreePool (DevicePathText);
|
|
}
|
|
|
|
FreePool (LoadOption);
|
|
}
|
|
|
|
//
|
|
// Redo with predefined.
|
|
//
|
|
if (Predefined == 0) {
|
|
BootOrder = &ApplePredefinedVariables[0];
|
|
BootOrderCount = ARRAY_SIZE (ApplePredefinedVariables);
|
|
DEBUG ((DEBUG_INFO, "OCB: Parsing predefined list...\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
InternalMatchBootEntryByDevicePath (
|
|
IN OUT OC_BOOT_ENTRY *BootEntry,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *UefiDevicePath,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *UefiRemainingDevicePath,
|
|
IN UINTN UefiDevicePathSize,
|
|
IN BOOLEAN IsBootNext
|
|
)
|
|
{
|
|
INTN CmpResult;
|
|
|
|
UINTN RootDevicePathSize;
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *OcDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *OcRemainingDevicePath;
|
|
|
|
RootDevicePathSize = ((UINT8 *)UefiRemainingDevicePath - (UINT8 *)UefiDevicePath);
|
|
|
|
if ((BootEntry->DevicePath == NULL) || ((BootEntry->Type & OC_BOOT_SYSTEM) != 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
OcDevicePath = BootEntry->DevicePath;
|
|
|
|
if ((GetDevicePathSize (OcDevicePath) - END_DEVICE_PATH_LENGTH) < RootDevicePathSize) {
|
|
return FALSE;
|
|
}
|
|
|
|
CmpResult = CompareMem (OcDevicePath, UefiDevicePath, RootDevicePathSize);
|
|
if (CmpResult != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// FIXME: Ensure that all the entries get properly filtered against any
|
|
// malicious sources. The drive itself should already be safe, but it is
|
|
// unclear whether a potentially safe device path can be transformed into
|
|
// an unsafe one.
|
|
//
|
|
OcRemainingDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)(
|
|
(UINT8 *)OcDevicePath + RootDevicePathSize
|
|
);
|
|
if (!IsBootNext) {
|
|
//
|
|
// For non-BootNext boot, the File Paths must match for the entries to be
|
|
// matched. Startup Disk however only stores the drive's Device Path
|
|
// excluding the booter path, which we treat as a match as well.
|
|
//
|
|
if ( !IsDevicePathEnd (UefiRemainingDevicePath)
|
|
&& !IsDevicePathEqual (UefiRemainingDevicePath, OcRemainingDevicePath)
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
//
|
|
// Only use the BootNext path when it has a file path.
|
|
//
|
|
if (!IsDevicePathEnd (UefiRemainingDevicePath)) {
|
|
//
|
|
// TODO: Investigate whether macOS adds BootNext entries that are not
|
|
// possibly located by bless.
|
|
//
|
|
FreePool (BootEntry->DevicePath);
|
|
BootEntry->DevicePath = AllocateCopyPool (
|
|
UefiDevicePathSize,
|
|
UefiDevicePath
|
|
);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
InternalMatchCustomBootEntryByDevicePath (
|
|
IN OUT OC_BOOT_ENTRY *BootEntry,
|
|
IN CONST OC_CUSTOM_BOOT_DEVICE_PATH *DevicePath
|
|
)
|
|
{
|
|
INTN CmpResult;
|
|
|
|
if (!BootEntry->IsCustom || BootEntry->IsBootEntryProtocol) {
|
|
return FALSE;
|
|
}
|
|
|
|
CmpResult = StrCmp (BootEntry->Name, DevicePath->EntryName.PathName);
|
|
if (CmpResult != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
InternalMatchEntryProtocolEntryByDevicePath (
|
|
IN OUT OC_BOOT_ENTRY *BootEntry,
|
|
IN CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *DevicePath
|
|
)
|
|
{
|
|
INTN CmpResult;
|
|
|
|
if (!BootEntry->IsCustom || !BootEntry->IsBootEntryProtocol || (BootEntry->Id == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
CmpResult = CompareMem (&BootEntry->UniquePartitionGUID, &DevicePath->Partuuid, sizeof (EFI_GUID));
|
|
if (CmpResult != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
CmpResult = StrCmp (BootEntry->Id, DevicePath->EntryName.PathName);
|
|
if (CmpResult != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
InternalClearNextVariables (
|
|
IN EFI_GUID *BootVariableGuid,
|
|
IN BOOLEAN ClearApplePayload
|
|
)
|
|
{
|
|
CHAR16 VariableName[32];
|
|
CHAR16 *BootNextName;
|
|
UINTN Index;
|
|
|
|
if (CompareGuid (BootVariableGuid, &gOcVendorVariableGuid)) {
|
|
BootNextName = OC_VENDOR_BOOT_NEXT_VARIABLE_NAME;
|
|
} else {
|
|
BootNextName = EFI_BOOT_NEXT_VARIABLE_NAME;
|
|
}
|
|
|
|
//
|
|
// Next variable data specified by UEFI spec.
|
|
// For now we do not bother dropping the variable it points to.
|
|
//
|
|
gRT->SetVariable (
|
|
BootNextName,
|
|
BootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Next variable string (in xml format) specified by Apple macOS.
|
|
//
|
|
gRT->SetVariable (
|
|
L"efi-boot-next",
|
|
&gAppleBootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Next variable blob (in DevicePath format) specified by Apple macOS.
|
|
//
|
|
gRT->SetVariable (
|
|
L"efi-boot-next-data",
|
|
&gAppleBootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (ClearApplePayload) {
|
|
for (Index = 0; Index <= 3; ++Index) {
|
|
UnicodeSPrint (
|
|
VariableName,
|
|
sizeof (VariableName),
|
|
L"efi-apple-payload%u%a",
|
|
(UINT32)Index,
|
|
"-data"
|
|
);
|
|
|
|
gRT->SetVariable (
|
|
VariableName,
|
|
&gAppleBootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
UnicodeSPrint (
|
|
VariableName,
|
|
sizeof (VariableName),
|
|
L"efi-apple-payload%u%a",
|
|
(UINT32)Index,
|
|
""
|
|
);
|
|
|
|
gRT->SetVariable (
|
|
VariableName,
|
|
&gAppleBootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
InternalHasFirmwareUpdateAsNext (
|
|
IN EFI_GUID *BootVariableGuid
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 VariableAttributes;
|
|
UINT16 BootNext;
|
|
CHAR16 *BootNextName;
|
|
UINTN VariableSize;
|
|
OC_BOOT_ENTRY_TYPE EntryType;
|
|
EFI_DEVICE_PATH_PROTOCOL *UefiDevicePath;
|
|
EFI_LOAD_OPTION *LoadOption;
|
|
UINTN LoadOptionSize;
|
|
|
|
if (CompareGuid (BootVariableGuid, &gOcVendorVariableGuid)) {
|
|
BootNextName = OC_VENDOR_BOOT_NEXT_VARIABLE_NAME;
|
|
} else {
|
|
BootNextName = EFI_BOOT_NEXT_VARIABLE_NAME;
|
|
}
|
|
|
|
VariableSize = sizeof (BootNext);
|
|
Status = gRT->GetVariable (
|
|
BootNextName,
|
|
BootVariableGuid,
|
|
&VariableAttributes,
|
|
&VariableSize,
|
|
&BootNext
|
|
);
|
|
if (EFI_ERROR (Status) || (VariableSize != sizeof (BootNext))) {
|
|
return FALSE;
|
|
}
|
|
|
|
LoadOption = OcGetBootOptionData (
|
|
&LoadOptionSize,
|
|
BootNext,
|
|
BootVariableGuid
|
|
);
|
|
if (LoadOption == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
UefiDevicePath = InternalGetBootOptionPath (LoadOption, LoadOptionSize);
|
|
if (UefiDevicePath == NULL) {
|
|
FreePool (LoadOption);
|
|
return FALSE;
|
|
}
|
|
|
|
EntryType = OcGetBootDevicePathType (UefiDevicePath, NULL, NULL);
|
|
DEBUG ((DEBUG_INFO, "OCB: Found BootNext %04x of type %u\n", BootNext, EntryType));
|
|
FreePool (LoadOption);
|
|
|
|
return EntryType == OC_BOOT_APPLE_FW_UPDATE;
|
|
}
|
|
|
|
BOOLEAN
|
|
InternalIsAppleLegacyLoadApp (
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_DEV_PATH_PTR FwVolDevPath;
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
|
|
if ( (DevicePath->Type == HARDWARE_DEVICE_PATH)
|
|
&& (DevicePath->SubType == HW_MEMMAP_DP))
|
|
{
|
|
FwVolDevPath.DevPath = NextDevicePathNode (DevicePath);
|
|
if ( (FwVolDevPath.DevPath->Type == MEDIA_DEVICE_PATH)
|
|
&& (FwVolDevPath.DevPath->SubType == MEDIA_PIWG_FW_FILE_DP))
|
|
{
|
|
return CompareGuid (
|
|
&FwVolDevPath.FirmwareFile->FvFileName,
|
|
&gAppleLegacyLoadAppFileGuid
|
|
);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
UINT16 *
|
|
OcGetBootOrder (
|
|
IN EFI_GUID *BootVariableGuid,
|
|
IN BOOLEAN WithBootNext,
|
|
OUT UINTN *BootOrderCount,
|
|
OUT BOOLEAN *Deduplicated OPTIONAL,
|
|
OUT BOOLEAN *HasBootNext OPTIONAL,
|
|
IN BOOLEAN UseBootNextOnly
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 VariableAttributes;
|
|
UINT16 BootNext;
|
|
CHAR16 *BootOrderName;
|
|
CHAR16 *BootNextName;
|
|
UINT16 *BootOrder;
|
|
UINTN VariableSize;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
BOOLEAN BootOrderChanged;
|
|
|
|
*BootOrderCount = 0;
|
|
|
|
if (Deduplicated != NULL) {
|
|
*Deduplicated = FALSE;
|
|
}
|
|
|
|
if (HasBootNext != NULL) {
|
|
*HasBootNext = FALSE;
|
|
}
|
|
|
|
if (CompareGuid (BootVariableGuid, &gOcVendorVariableGuid)) {
|
|
BootOrderName = OC_VENDOR_BOOT_ORDER_VARIABLE_NAME;
|
|
BootNextName = OC_VENDOR_BOOT_NEXT_VARIABLE_NAME;
|
|
} else {
|
|
BootOrderName = EFI_BOOT_ORDER_VARIABLE_NAME;
|
|
BootNextName = EFI_BOOT_NEXT_VARIABLE_NAME;
|
|
}
|
|
|
|
//
|
|
// Precede variable with boot next.
|
|
//
|
|
if (WithBootNext) {
|
|
VariableSize = sizeof (BootNext);
|
|
Status = gRT->GetVariable (
|
|
BootNextName,
|
|
BootVariableGuid,
|
|
&VariableAttributes,
|
|
&VariableSize,
|
|
&BootNext
|
|
);
|
|
if (!EFI_ERROR (Status) && (VariableSize == sizeof (BootNext))) {
|
|
if (HasBootNext != NULL) {
|
|
*HasBootNext = TRUE;
|
|
}
|
|
} else {
|
|
WithBootNext = FALSE;
|
|
}
|
|
}
|
|
|
|
if (UseBootNextOnly) {
|
|
Status = EFI_ABORTED;
|
|
} else {
|
|
VariableSize = 0;
|
|
Status = gRT->GetVariable (
|
|
BootOrderName,
|
|
BootVariableGuid,
|
|
&VariableAttributes,
|
|
&VariableSize,
|
|
NULL
|
|
);
|
|
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
BootOrder = AllocatePool ((UINTN)WithBootNext * sizeof (BootNext) + VariableSize);
|
|
if (BootOrder == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = gRT->GetVariable (
|
|
BootOrderName,
|
|
BootVariableGuid,
|
|
&VariableAttributes,
|
|
&VariableSize,
|
|
BootOrder + (UINTN)WithBootNext
|
|
);
|
|
if ( EFI_ERROR (Status)
|
|
|| (VariableSize < sizeof (*BootOrder))
|
|
|| (VariableSize % sizeof (*BootOrder) != 0))
|
|
{
|
|
FreePool (BootOrder);
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
} else if (!EFI_ERROR (Status)) {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (WithBootNext) {
|
|
BootOrder = AllocateCopyPool (sizeof (BootNext), &BootNext);
|
|
if (BootOrder != NULL) {
|
|
*BootOrderCount = 1;
|
|
return BootOrder;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (WithBootNext) {
|
|
BootOrder[0] = BootNext;
|
|
VariableSize += sizeof (*BootOrder);
|
|
}
|
|
|
|
BootOrderChanged = FALSE;
|
|
|
|
for (Index = 1; Index < VariableSize / sizeof (BootOrder[0]); ++Index) {
|
|
for (Index2 = 0; Index2 < Index; ++Index2) {
|
|
if (BootOrder[Index] == BootOrder[Index2]) {
|
|
//
|
|
// Found duplicate.
|
|
//
|
|
BootOrderChanged = TRUE;
|
|
CopyMem (
|
|
&BootOrder[Index],
|
|
&BootOrder[Index + 1],
|
|
VariableSize - sizeof (BootOrder[0]) * (Index + 1)
|
|
);
|
|
VariableSize -= sizeof (BootOrder[0]);
|
|
--Index;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*BootOrderCount = VariableSize / sizeof (*BootOrder);
|
|
if (Deduplicated != NULL) {
|
|
*Deduplicated = BootOrderChanged;
|
|
}
|
|
|
|
return BootOrder;
|
|
}
|
|
|
|
UINT16 *
|
|
InternalGetBootOrderForBooting (
|
|
IN EFI_GUID *BootVariableGuid,
|
|
IN BOOLEAN BlacklistAppleUpdate,
|
|
OUT UINTN *BootOrderCount,
|
|
IN BOOLEAN UseBootNextOnly
|
|
)
|
|
{
|
|
UINT16 *BootOrder;
|
|
BOOLEAN HasFwBootNext;
|
|
BOOLEAN HasBootNext;
|
|
|
|
//
|
|
// Precede variable with boot next unless we were forced to ignore it.
|
|
//
|
|
if (BlacklistAppleUpdate) {
|
|
HasFwBootNext = InternalHasFirmwareUpdateAsNext (BootVariableGuid);
|
|
} else {
|
|
HasFwBootNext = FALSE;
|
|
}
|
|
|
|
BootOrder = OcGetBootOrder (
|
|
BootVariableGuid,
|
|
HasFwBootNext == FALSE,
|
|
BootOrderCount,
|
|
NULL,
|
|
&HasBootNext,
|
|
UseBootNextOnly
|
|
);
|
|
if (BootOrder == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: BootOrder/BootNext are not present or unsupported %u %u\n", HasFwBootNext, UseBootNextOnly));
|
|
return NULL;
|
|
}
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Found %u BootOrder entries with BootNext %a\n",
|
|
(UINT32)*BootOrderCount,
|
|
UseBootNextOnly ? "only" : (HasBootNext ? "included" : "excluded")
|
|
));
|
|
InternalDebugBootEnvironment (BootOrder, BootVariableGuid, *BootOrderCount);
|
|
DEBUG_CODE_END ();
|
|
|
|
if (HasFwBootNext || HasBootNext) {
|
|
InternalClearNextVariables (BootVariableGuid, HasFwBootNext);
|
|
}
|
|
|
|
return BootOrder;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcSetDefaultBootEntry (
|
|
IN OC_PICKER_CONTEXT *Context,
|
|
IN OC_BOOT_ENTRY *Entry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH *BootOptionDevicePath;
|
|
EFI_DEVICE_PATH *BootOptionRemainingDevicePath;
|
|
EFI_HANDLE DeviceHandle;
|
|
BOOLEAN MatchedEntry;
|
|
BOOLEAN IsOverflow;
|
|
BOOLEAN IsAsciiOptionName;
|
|
EFI_GUID *BootVariableGuid;
|
|
CHAR16 *BootOrderName;
|
|
CHAR16 *BootVariableName;
|
|
CHAR16 *LoadOptionId;
|
|
VOID *LoadOptionName;
|
|
CHAR8 *FirstFlavourEnd;
|
|
UINT16 *BootOrder;
|
|
UINT16 *NewBootOrder;
|
|
UINT16 BootTmp;
|
|
UINT16 EntryIdLength;
|
|
UINTN BootOrderCount;
|
|
UINTN BootChosenIndex;
|
|
UINTN Index;
|
|
UINTN DevicePathSize;
|
|
UINTN UnmanagedBootDevPathSize;
|
|
UINTN LoadOptionSize;
|
|
UINTN LoadOptionIdSize;
|
|
UINTN LoadOptionNameSize;
|
|
UINTN LoadOptionNameLen;
|
|
UINTN CopiedLength;
|
|
EFI_LOAD_OPTION *LoadOption;
|
|
|
|
CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath;
|
|
CONST OC_ENTRY_PROTOCOL_DEVICE_PATH *EntryProtocolDevPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *UnmanagedBootDevPath;
|
|
VENDOR_DEVICE_PATH *DestCustomDevPath;
|
|
FILEPATH_DEVICE_PATH *DestCustomEntryName;
|
|
EFI_DEVICE_PATH_PROTOCOL *DestCustomEndNode;
|
|
|
|
//
|
|
// Do not allow when prohibited.
|
|
//
|
|
if (!Context->AllowSetDefault) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
//
|
|
// Ignore entries without device paths.
|
|
//
|
|
if (Entry->DevicePath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Get final device path for unmanaged boot entries.
|
|
//
|
|
UnmanagedBootDevPath = NULL;
|
|
if ((Entry->Type == OC_BOOT_UNMANAGED) && (Entry->UnmanagedBootGetFinalDevicePath != NULL)) {
|
|
UnmanagedBootDevPath = Entry->DevicePath;
|
|
Status = Entry->UnmanagedBootGetFinalDevicePath (Context, &UnmanagedBootDevPath);
|
|
if (EFI_ERROR (Status)) {
|
|
UnmanagedBootDevPath = NULL;
|
|
} else {
|
|
UnmanagedBootDevPathSize = GetDevicePathSize (UnmanagedBootDevPath);
|
|
}
|
|
}
|
|
|
|
if (Context->CustomBootGuid) {
|
|
BootVariableGuid = &gOcVendorVariableGuid;
|
|
BootOrderName = OC_VENDOR_BOOT_ORDER_VARIABLE_NAME;
|
|
BootVariableName = OC_VENDOR_BOOT_VARIABLE_PREFIX L"0080";
|
|
} else {
|
|
BootVariableGuid = &gEfiGlobalVariableGuid;
|
|
BootOrderName = EFI_BOOT_ORDER_VARIABLE_NAME;
|
|
BootVariableName = L"Boot0080";
|
|
}
|
|
|
|
BootOrder = OcGetBootOrder (
|
|
BootVariableGuid,
|
|
FALSE,
|
|
&BootOrderCount,
|
|
NULL,
|
|
NULL,
|
|
FALSE
|
|
);
|
|
|
|
MatchedEntry = FALSE;
|
|
BootChosenIndex = BootOrderCount;
|
|
for (Index = 0; Index < BootOrderCount; ++Index) {
|
|
if (BootOrder[Index] == 0x80) {
|
|
BootChosenIndex = Index;
|
|
}
|
|
|
|
if (MatchedEntry) {
|
|
if (BootChosenIndex != BootOrderCount) {
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
LoadOption = OcGetBootOptionData (
|
|
&LoadOptionSize,
|
|
BootOrder[Index],
|
|
BootVariableGuid
|
|
);
|
|
if (LoadOption == NULL) {
|
|
continue;
|
|
}
|
|
|
|
BootOptionDevicePath = InternalGetBootOptionPath (
|
|
LoadOption,
|
|
LoadOptionSize
|
|
);
|
|
if (BootOptionDevicePath == NULL) {
|
|
FreePool (LoadOption);
|
|
continue;
|
|
}
|
|
|
|
if (UnmanagedBootDevPath != NULL) {
|
|
DevicePathSize = GetDevicePathSize (BootOptionDevicePath);
|
|
if (DevicePathSize >= UnmanagedBootDevPathSize) {
|
|
MatchedEntry = CompareMem (BootOptionDevicePath, UnmanagedBootDevPath, UnmanagedBootDevPathSize) == 0;
|
|
}
|
|
} else {
|
|
BootOptionRemainingDevicePath = BootOptionDevicePath;
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
&BootOptionRemainingDevicePath,
|
|
&DeviceHandle
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
MatchedEntry = InternalMatchBootEntryByDevicePath (
|
|
Entry,
|
|
BootOptionDevicePath,
|
|
BootOptionRemainingDevicePath,
|
|
LoadOption->FilePathListLength,
|
|
FALSE
|
|
);
|
|
} else {
|
|
CustomDevPath = InternalGetOcCustomDevPath (BootOptionDevicePath);
|
|
if (CustomDevPath != NULL) {
|
|
MatchedEntry = InternalMatchCustomBootEntryByDevicePath (
|
|
Entry,
|
|
CustomDevPath
|
|
);
|
|
} else {
|
|
EntryProtocolDevPath = InternalGetOcEntryProtocolDevPath (BootOptionDevicePath);
|
|
if (EntryProtocolDevPath != NULL) {
|
|
MatchedEntry = InternalMatchEntryProtocolEntryByDevicePath (
|
|
Entry,
|
|
EntryProtocolDevPath
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (LoadOption);
|
|
}
|
|
|
|
if (!MatchedEntry) {
|
|
//
|
|
// Write to Boot0080
|
|
//
|
|
ASSERT (Entry->Name != NULL);
|
|
IsAsciiOptionName = FALSE;
|
|
if (Entry->Id == NULL) {
|
|
//
|
|
// Re-use user defined entry name as stored id.
|
|
//
|
|
LoadOptionName = Entry->Name;
|
|
LoadOptionNameSize = StrSize (Entry->Name);
|
|
|
|
LoadOptionId = LoadOptionName;
|
|
LoadOptionIdSize = LoadOptionNameSize;
|
|
} else {
|
|
//
|
|
// Re-use first part of flavour as option name if available, it is more human
|
|
// readable than entry id, but is not version specific, unlike entry name.
|
|
//
|
|
LoadOptionId = Entry->Id;
|
|
LoadOptionIdSize = StrSize (Entry->Id);
|
|
|
|
if ((Entry->Flavour != NULL) && (Entry->Flavour[0] != '\0') && (Entry->Flavour[0] != ':')) {
|
|
FirstFlavourEnd = OcAsciiStrChr (Entry->Flavour, ':');
|
|
if (FirstFlavourEnd != NULL) {
|
|
LoadOptionNameLen = FirstFlavourEnd - Entry->Flavour;
|
|
} else {
|
|
LoadOptionNameLen = AsciiStrLen (Entry->Flavour);
|
|
}
|
|
|
|
IsAsciiOptionName = TRUE;
|
|
LoadOptionNameSize = (LoadOptionNameLen + 1) * sizeof (CHAR16) / sizeof (CHAR8);
|
|
LoadOptionName = Entry->Flavour;
|
|
} else {
|
|
LoadOptionName = LoadOptionId;
|
|
LoadOptionNameSize = LoadOptionIdSize;
|
|
}
|
|
}
|
|
|
|
if (UnmanagedBootDevPath != NULL) {
|
|
DevicePathSize = UnmanagedBootDevPathSize;
|
|
} else if (!Entry->IsCustom) {
|
|
DevicePathSize = GetDevicePathSize (Entry->DevicePath);
|
|
} else {
|
|
DevicePathSize = SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH
|
|
+ (Entry->IsBootEntryProtocol ? sizeof (EFI_GUID) : 0)
|
|
+ LoadOptionIdSize
|
|
+ sizeof (EFI_DEVICE_PATH_PROTOCOL);
|
|
|
|
if (LoadOptionIdSize > MAX_UINT16) {
|
|
IsOverflow = TRUE;
|
|
} else {
|
|
IsOverflow = BaseOverflowAddU16 (SIZE_OF_FILEPATH_DEVICE_PATH, (UINT16)LoadOptionIdSize, &EntryIdLength);
|
|
}
|
|
|
|
if (IsOverflow) {
|
|
DEBUG ((DEBUG_ERROR, "OCB: Overflowing option id size (%u)\n", LoadOptionIdSize));
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
LoadOptionSize = sizeof (EFI_LOAD_OPTION) + LoadOptionNameSize + DevicePathSize;
|
|
|
|
LoadOption = AllocatePool (LoadOptionSize);
|
|
if (LoadOption == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to allocate default option (%u)\n", (UINT32)LoadOptionSize));
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
LoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT;
|
|
LoadOption->FilePathListLength = (UINT16)DevicePathSize;
|
|
if (IsAsciiOptionName) {
|
|
Status = AsciiStrnToUnicodeStrS (LoadOptionName, LoadOptionNameLen, (CHAR16 *)(LoadOption + 1), LoadOptionNameSize / sizeof (CHAR16), &CopiedLength);
|
|
ASSERT_EFI_ERROR (Status);
|
|
ASSERT (CopiedLength == LoadOptionNameLen);
|
|
} else {
|
|
CopyMem (LoadOption + 1, LoadOptionName, LoadOptionNameSize);
|
|
}
|
|
|
|
if (UnmanagedBootDevPath != NULL) {
|
|
CopyMem ((UINT8 *)(LoadOption + 1) + LoadOptionNameSize, UnmanagedBootDevPath, DevicePathSize);
|
|
} else if (!Entry->IsCustom) {
|
|
CopyMem ((UINT8 *)(LoadOption + 1) + LoadOptionNameSize, Entry->DevicePath, DevicePathSize);
|
|
} else {
|
|
DestCustomDevPath = (VENDOR_DEVICE_PATH *)(
|
|
(UINT8 *)(LoadOption + 1) + LoadOptionNameSize
|
|
);
|
|
if (Entry->IsBootEntryProtocol) {
|
|
CopyMem (
|
|
DestCustomDevPath,
|
|
&mOcEntryProtocolDevPathTemplate,
|
|
sizeof (mOcEntryProtocolDevPathTemplate)
|
|
);
|
|
CopyMem (
|
|
DestCustomDevPath + 1,
|
|
&Entry->UniquePartitionGUID,
|
|
sizeof (EFI_GUID)
|
|
);
|
|
DestCustomEntryName = (FILEPATH_DEVICE_PATH *)(
|
|
(UINT8 *)(DestCustomDevPath + 1) +
|
|
sizeof (EFI_GUID)
|
|
);
|
|
} else {
|
|
CopyMem (
|
|
DestCustomDevPath,
|
|
&mOcCustomBootDevPathTemplate,
|
|
sizeof (mOcCustomBootDevPathTemplate)
|
|
);
|
|
DestCustomEntryName = (FILEPATH_DEVICE_PATH *)(
|
|
(UINT8 *)(DestCustomDevPath + 1)
|
|
);
|
|
}
|
|
|
|
CopyMem (
|
|
DestCustomEntryName->PathName,
|
|
LoadOptionId,
|
|
LoadOptionIdSize
|
|
);
|
|
|
|
DestCustomEntryName->Header.Length[0] = (UINT8)EntryIdLength;
|
|
DestCustomEntryName->Header.Length[1] = (UINT8)(EntryIdLength >> 8);
|
|
|
|
DestCustomEndNode = (EFI_DEVICE_PATH_PROTOCOL *)(
|
|
(UINT8 *)DestCustomEntryName + EntryIdLength
|
|
);
|
|
SetDevicePathEndNode (DestCustomEndNode);
|
|
|
|
ASSERT (GetDevicePathSize ((EFI_DEVICE_PATH_PROTOCOL *)DestCustomDevPath) == DevicePathSize);
|
|
}
|
|
|
|
Status = gRT->SetVariable (
|
|
BootVariableName,
|
|
BootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
| EFI_VARIABLE_RUNTIME_ACCESS
|
|
| EFI_VARIABLE_NON_VOLATILE,
|
|
LoadOptionSize,
|
|
LoadOption
|
|
);
|
|
|
|
FreePool (LoadOption);
|
|
|
|
if (UnmanagedBootDevPath != NULL) {
|
|
FreePool (UnmanagedBootDevPath);
|
|
UnmanagedBootDevPath = NULL;
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Failed to set default entry Boot0080 - %r\n",
|
|
Status
|
|
));
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "OCB: Matched default entry in BootOrder\n"));
|
|
BootChosenIndex = Index;
|
|
}
|
|
|
|
//
|
|
// Update BootOrder to contain new option.
|
|
//
|
|
if (BootChosenIndex != BootOrderCount) {
|
|
BootTmp = BootOrder[0];
|
|
BootOrder[0] = BootOrder[BootChosenIndex];
|
|
BootOrder[BootChosenIndex] = BootTmp;
|
|
NewBootOrder = BootOrder;
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Found default entry in BootOrder, reordering %X <-> %X\n",
|
|
NewBootOrder[0],
|
|
NewBootOrder[BootChosenIndex]
|
|
));
|
|
} else {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Adding default entry Boot0080 to BootOrder\n"
|
|
));
|
|
|
|
NewBootOrder = AllocatePool ((BootOrderCount + 1) * sizeof (*BootOrder));
|
|
if (NewBootOrder == NULL) {
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
NewBootOrder[0] = 0x80;
|
|
CopyMem (&NewBootOrder[1], &BootOrder[0], BootOrderCount * sizeof (*BootOrder));
|
|
|
|
if (BootOrder != NULL) {
|
|
FreePool (BootOrder);
|
|
}
|
|
|
|
++BootOrderCount;
|
|
}
|
|
|
|
Status = gRT->SetVariable (
|
|
BootOrderName,
|
|
BootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
| EFI_VARIABLE_RUNTIME_ACCESS
|
|
| EFI_VARIABLE_NON_VOLATILE,
|
|
BootOrderCount * sizeof (*BootOrder),
|
|
NewBootOrder
|
|
);
|
|
|
|
FreePool (NewBootOrder);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Failed to set default BootOrder - %r\n",
|
|
Status
|
|
));
|
|
}
|
|
|
|
OcSaveLegacyNvram ();
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
Retrieves the Bootstrap Load Option data, matching it from BootOrder by
|
|
finding a path ending with MatchSuffix.
|
|
|
|
@param[out] LoadOptionSize The size, in bytes, of the Load Option data.
|
|
@param[out] BootOption The index of the Boot Option.
|
|
@param[out] LoadPath Pointer into the Load Option data to the
|
|
Device Path.
|
|
@param[in] BootOptions The list of Boot Option indices to match.
|
|
@param[in] NumBootOptions The number of elements in BootOptions.
|
|
@param[in] MatchSuffix The file Device Path suffix of a matching option.
|
|
@param[in] MatchSuffixLen The length, in characters, of MatchSuffix.
|
|
*/
|
|
EFI_LOAD_OPTION *
|
|
InternalGetBoostrapOptionData (
|
|
OUT UINTN *LoadOptionSize,
|
|
OUT UINT16 *BootOption,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **LoadPath,
|
|
IN UINT16 *BootOptions,
|
|
IN UINTN NumBootOptions,
|
|
IN CONST CHAR16 *MatchSuffix,
|
|
IN UINTN MatchSuffixLen
|
|
)
|
|
{
|
|
UINTN BootOptionIndex;
|
|
EFI_LOAD_OPTION *CurrLoadOption;
|
|
EFI_DEVICE_PATH_PROTOCOL *CurrDevicePath;
|
|
BOOLEAN IsBooptstrap;
|
|
|
|
//
|
|
// Check all boot options for trailing "\Bootstrap\Bootstrap.efi".
|
|
//
|
|
for (BootOptionIndex = 0; BootOptionIndex < NumBootOptions; ++BootOptionIndex) {
|
|
CurrLoadOption = OcGetBootOptionData (
|
|
LoadOptionSize,
|
|
BootOptions[BootOptionIndex],
|
|
&gEfiGlobalVariableGuid
|
|
);
|
|
if (CurrLoadOption == NULL) {
|
|
continue;
|
|
}
|
|
|
|
CurrDevicePath = InternalGetBootOptionPath (
|
|
CurrLoadOption,
|
|
*LoadOptionSize
|
|
);
|
|
if (CurrDevicePath == NULL) {
|
|
FreePool (CurrDevicePath);
|
|
continue;
|
|
}
|
|
|
|
IsBooptstrap = OcDevicePathHasFilePathSuffix (
|
|
CurrDevicePath,
|
|
MatchSuffix,
|
|
MatchSuffixLen
|
|
);
|
|
if (IsBooptstrap) {
|
|
break;
|
|
}
|
|
|
|
FreePool (CurrLoadOption);
|
|
}
|
|
|
|
if (BootOptionIndex == NumBootOptions) {
|
|
return NULL;
|
|
}
|
|
|
|
*LoadPath = CurrDevicePath;
|
|
*BootOption = BootOptions[BootOptionIndex];
|
|
return CurrLoadOption;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalRegisterBootstrapBootOption (
|
|
IN CONST CHAR16 *OptionName,
|
|
IN EFI_HANDLE DeviceHandle,
|
|
IN CONST CHAR16 *FilePath,
|
|
IN BOOLEAN ShortForm,
|
|
IN CONST CHAR16 *MatchSuffix,
|
|
IN UINTN MatchSuffixLen
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LOAD_OPTION *Option;
|
|
UINTN OptionNameSize;
|
|
UINTN ReferencePathSize;
|
|
UINTN OptionSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *CurrDevicePath;
|
|
UINTN Index;
|
|
UINT16 *BootOrder;
|
|
UINTN BootOrderSize;
|
|
UINT32 BootOrderAttributes;
|
|
BOOLEAN CurrOptionExists;
|
|
BOOLEAN CurrOptionValid;
|
|
EFI_DEVICE_PATH_PROTOCOL *ShortFormPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *ReferencePath;
|
|
CHAR16 BootOptionVariable[L_STR_LEN (L"Boot####") + 1];
|
|
UINT16 BootOptionIndex;
|
|
UINTN OrderIndex;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
DeviceHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **)&DevicePath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to obtain device path for boot option - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
DevicePath = AppendFileNameDevicePath (DevicePath, (CHAR16 *)FilePath);
|
|
if (DevicePath == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to append %s loader path for boot option - %r\n", FilePath));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
ReferencePath = DevicePath;
|
|
|
|
if (ShortForm) {
|
|
ShortFormPath = FindDevicePathNodeWithType (
|
|
DevicePath,
|
|
MEDIA_DEVICE_PATH,
|
|
MEDIA_HARDDRIVE_DP
|
|
);
|
|
if (ShortFormPath != NULL) {
|
|
ReferencePath = ShortFormPath;
|
|
}
|
|
}
|
|
|
|
CurrOptionValid = FALSE;
|
|
CurrOptionExists = FALSE;
|
|
|
|
BootOrderSize = 0;
|
|
Status = gRT->GetVariable (
|
|
EFI_BOOT_ORDER_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderAttributes,
|
|
&BootOrderSize,
|
|
NULL
|
|
);
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Have existing order of size %u - %r\n",
|
|
(UINT32)BootOrderSize,
|
|
Status
|
|
));
|
|
|
|
BootOrder = NULL;
|
|
|
|
if ((Status == EFI_BUFFER_TOO_SMALL) && (BootOrderSize > 0) && (BootOrderSize % sizeof (UINT16) == 0)) {
|
|
BootOrder = AllocateZeroPool (BootOrderSize + sizeof (UINT16));
|
|
if (BootOrder == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to allocate boot order\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = gRT->GetVariable (
|
|
EFI_BOOT_ORDER_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
&BootOrderAttributes,
|
|
&BootOrderSize,
|
|
(VOID *)(BootOrder + 1)
|
|
);
|
|
|
|
if (EFI_ERROR (Status) || (BootOrderSize == 0) || (BootOrderSize % sizeof (UINT16) != 0)) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to obtain boot order %u - %r\n", (UINT32)BootOrderSize, Status));
|
|
FreePool (BootOrder);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Option = InternalGetBoostrapOptionData (
|
|
&OptionSize,
|
|
&BootOptionIndex,
|
|
&CurrDevicePath,
|
|
&BootOrder[1],
|
|
BootOrderSize / sizeof (*BootOrder),
|
|
MatchSuffix,
|
|
MatchSuffixLen
|
|
);
|
|
CurrOptionExists = Option != NULL;
|
|
if (CurrOptionExists) {
|
|
CurrOptionValid = IsDevicePathEqual (ReferencePath, CurrDevicePath);
|
|
FreePool (Option);
|
|
}
|
|
} else {
|
|
BootOrderSize = 0;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: %a existing option at Boot%04x, %a\n",
|
|
CurrOptionExists ? "Have" : "No",
|
|
BootOrder != NULL ? BootOrder[1] : 0,
|
|
CurrOptionValid ? "valid" : "invalid"
|
|
));
|
|
|
|
if (!CurrOptionValid) {
|
|
//
|
|
// Locate a free boot option index when no Bootstrap entry could be found.
|
|
//
|
|
if (!CurrOptionExists) {
|
|
//
|
|
// High magic numbers cause entry purging on e.g. HP 15-ab237ne, InsydeH2O.
|
|
//
|
|
// Find the lowest unused Boot#### index. In the absolutely unrealistic case
|
|
// that all entries are occupied, always overwrite BootFFFF.
|
|
//
|
|
// Boot0000 is reserved on ASUS boards and is treated like a deleted entry.
|
|
// Setting Boot0000 will essentially cause entries to duplicate and eventual
|
|
// BIOS brick as ASUS boards simply zero removed boot entries instead of
|
|
// shrinking BootOrder size. Reproduced on ASUS ROG STRIX Z370-F GAMING.
|
|
//
|
|
for (BootOptionIndex = 1; BootOptionIndex < 0xFFFF; ++BootOptionIndex) {
|
|
for (OrderIndex = 0; OrderIndex < BootOrderSize / sizeof (*BootOrder); ++OrderIndex) {
|
|
if (BootOrder[OrderIndex + 1] == BootOptionIndex) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (OrderIndex == BootOrderSize / sizeof (*BootOrder)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
UnicodeSPrint (
|
|
BootOptionVariable,
|
|
sizeof (BootOptionVariable),
|
|
L"Boot%04x",
|
|
BootOptionIndex
|
|
);
|
|
|
|
OptionNameSize = StrSize (OptionName);
|
|
ReferencePathSize = GetDevicePathSize (ReferencePath);
|
|
OptionSize = sizeof (EFI_LOAD_OPTION) + OptionNameSize + ReferencePathSize;
|
|
|
|
DEBUG ((DEBUG_INFO, "OCB: Creating boot option %s of %u bytes\n", OptionName, (UINT32)OptionSize));
|
|
|
|
Option = AllocatePool (OptionSize);
|
|
if (Option == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to allocate boot option (%u)\n", (UINT32)OptionSize));
|
|
FreePool (DevicePath);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Option->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT;
|
|
Option->FilePathListLength = (UINT16)ReferencePathSize;
|
|
CopyMem (Option + 1, OptionName, OptionNameSize);
|
|
CopyMem ((UINT8 *)(Option + 1) + OptionNameSize, ReferencePath, ReferencePathSize);
|
|
|
|
Status = gRT->SetVariable (
|
|
BootOptionVariable,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
| EFI_VARIABLE_RUNTIME_ACCESS
|
|
| EFI_VARIABLE_NON_VOLATILE,
|
|
OptionSize,
|
|
Option
|
|
);
|
|
|
|
FreePool (Option);
|
|
FreePool (DevicePath);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Failed to store boot option - %r\n", Status));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (BootOrderSize != 0) {
|
|
if (BootOrder[1] == BootOptionIndex) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Boot order has first option as the default option\n"));
|
|
FreePool (BootOrder);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BootOrder[0] = BootOptionIndex;
|
|
|
|
Index = 1;
|
|
while (Index <= BootOrderSize / sizeof (UINT16)) {
|
|
if (BootOrder[Index] == BootOptionIndex) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Moving boot option to the front from %u position\n", (UINT32)Index));
|
|
CopyMem (
|
|
&BootOrder[Index],
|
|
&BootOrder[Index + 1],
|
|
BootOrderSize - Index * sizeof (UINT16)
|
|
);
|
|
BootOrderSize -= sizeof (UINT16);
|
|
} else {
|
|
++Index;
|
|
}
|
|
}
|
|
|
|
Status = gRT->SetVariable (
|
|
EFI_BOOT_ORDER_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
| EFI_VARIABLE_RUNTIME_ACCESS
|
|
| EFI_VARIABLE_NON_VOLATILE,
|
|
BootOrderSize + sizeof (UINT16),
|
|
BootOrder
|
|
);
|
|
|
|
FreePool (BootOrder);
|
|
} else {
|
|
Status = gRT->SetVariable (
|
|
EFI_BOOT_ORDER_VARIABLE_NAME,
|
|
&gEfiGlobalVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS
|
|
| EFI_VARIABLE_RUNTIME_ACCESS
|
|
| EFI_VARIABLE_NON_VOLATILE,
|
|
sizeof (UINT16),
|
|
&BootOptionIndex
|
|
);
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OCB: Wrote new boot order with boot option - %r\n", Status));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcRegisterBootstrapBootOption (
|
|
IN CONST CHAR16 *OptionName,
|
|
IN EFI_HANDLE DeviceHandle,
|
|
IN CONST CHAR16 *FilePath,
|
|
IN BOOLEAN ShortForm,
|
|
IN CONST CHAR16 *MatchSuffix,
|
|
IN UINTN MatchSuffixLen
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
OC_FIRMWARE_RUNTIME_PROTOCOL *FwRuntime;
|
|
|
|
FwRuntime = OcDisableNvramProtection ();
|
|
|
|
Status = InternalRegisterBootstrapBootOption (
|
|
OptionName,
|
|
DeviceHandle,
|
|
FilePath,
|
|
ShortForm,
|
|
MatchSuffix,
|
|
MatchSuffixLen
|
|
);
|
|
|
|
OcRestoreNvramProtection (FwRuntime);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalLoadBootEntry (
|
|
IN OC_PICKER_CONTEXT *Context,
|
|
IN OC_BOOT_ENTRY *BootEntry,
|
|
IN EFI_HANDLE ParentHandle,
|
|
OUT EFI_HANDLE *EntryHandle,
|
|
OUT INTERNAL_DMG_LOAD_CONTEXT *DmgLoadContext,
|
|
OUT VOID **CustomFreeContext
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS OptionalStatus;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_HANDLE StorageHandle;
|
|
EFI_DEVICE_PATH_PROTOCOL *StoragePath;
|
|
CHAR16 *UnicodeDevicePath;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
VOID *EntryData;
|
|
UINT32 EntryDataSize;
|
|
CONST CHAR8 *Args;
|
|
OC_APPLE_DISK_IMAGE_PRELOAD_CONTEXT DmgPreloadContext;
|
|
|
|
ASSERT (BootEntry != NULL);
|
|
//
|
|
// System entries are not loaded but called directly.
|
|
//
|
|
ASSERT ((BootEntry->Type & OC_BOOT_UNMANAGED) == 0);
|
|
ASSERT ((BootEntry->Type & OC_BOOT_SYSTEM) == 0);
|
|
ASSERT (Context != NULL);
|
|
ASSERT (DmgLoadContext != NULL);
|
|
|
|
//
|
|
// TODO: support Apple loaded image, policy, and dmg boot.
|
|
//
|
|
|
|
ZeroMem (DmgLoadContext, sizeof (*DmgLoadContext));
|
|
|
|
EntryData = NULL;
|
|
EntryDataSize = 0;
|
|
StorageHandle = NULL;
|
|
StoragePath = NULL;
|
|
*CustomFreeContext = NULL;
|
|
ZeroMem (&DmgPreloadContext, sizeof (DmgPreloadContext));
|
|
|
|
//
|
|
// CustomRead must be set for external tools, but may also be set for boot
|
|
// entry protocol entries.
|
|
//
|
|
ASSERT (BootEntry->Type != OC_BOOT_EXTERNAL_TOOL || BootEntry->CustomRead != NULL);
|
|
|
|
if (BootEntry->CustomRead != NULL) {
|
|
Status = BootEntry->CustomRead (
|
|
Context->StorageContext,
|
|
BootEntry,
|
|
&EntryData,
|
|
&EntryDataSize,
|
|
&DevicePath,
|
|
&StorageHandle,
|
|
&StoragePath,
|
|
Context->DmgLoading,
|
|
&DmgPreloadContext,
|
|
CustomFreeContext
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "OCB: Custom read failed - %r\n", Status));
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if ( (DmgPreloadContext.DmgFile != NULL)
|
|
|| (DmgPreloadContext.DmgContext != NULL)
|
|
|| BootEntry->IsFolder)
|
|
{
|
|
if (Context->DmgLoading == OcDmgLoadingDisabled) {
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
|
|
DmgLoadContext->DevicePath = BootEntry->DevicePath;
|
|
DevicePath = InternalLoadDmg (
|
|
DmgLoadContext,
|
|
Context->DmgLoading,
|
|
&DmgPreloadContext
|
|
);
|
|
if (DevicePath == NULL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
} else if (BootEntry->CustomRead == NULL) {
|
|
DevicePath = BootEntry->DevicePath;
|
|
}
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
ASSERT (DevicePath != NULL);
|
|
UnicodeDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Perform boot %s to dp %s (%p/%u)\n",
|
|
BootEntry->Name,
|
|
UnicodeDevicePath != NULL ? UnicodeDevicePath : L"<null>",
|
|
EntryData,
|
|
EntryDataSize
|
|
));
|
|
if (UnicodeDevicePath != NULL) {
|
|
FreePool (UnicodeDevicePath);
|
|
}
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
Status = gBS->LoadImage (
|
|
FALSE,
|
|
ParentHandle,
|
|
DevicePath,
|
|
EntryData,
|
|
EntryDataSize,
|
|
EntryHandle
|
|
);
|
|
|
|
if (EntryData != NULL) {
|
|
FreePool (EntryData);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
OptionalStatus = gBS->HandleProtocol (
|
|
*EntryHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&LoadedImage
|
|
);
|
|
if (!EFI_ERROR (OptionalStatus)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Matching <%a>/%p[%u] args on type %u\n",
|
|
Context->AppleBootArgs,
|
|
BootEntry->LoadOptions,
|
|
BootEntry->LoadOptionsSize,
|
|
BootEntry->Type
|
|
));
|
|
|
|
LoadedImage->LoadOptionsSize = 0;
|
|
LoadedImage->LoadOptions = NULL;
|
|
|
|
if ((BootEntry->LoadOptions == NULL) && ((BootEntry->Type & OC_BOOT_APPLE_ANY) != 0)) {
|
|
Args = Context->AppleBootArgs;
|
|
} else {
|
|
Args = BootEntry->LoadOptions;
|
|
}
|
|
|
|
//
|
|
// This only correctly passes on args which are a NUL terminated ASCII string.
|
|
//
|
|
if ((Args != NULL) && (Args[0] != '\0')) {
|
|
OcAppendArgumentsToLoadedImage (
|
|
LoadedImage,
|
|
&Args,
|
|
1,
|
|
TRUE
|
|
);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Args <%s>\n",
|
|
LoadedImage->LoadOptions
|
|
));
|
|
}
|
|
|
|
if ((BootEntry->Type == OC_BOOT_EXTERNAL_OS) || (BootEntry->Type == OC_BOOT_EXTERNAL_TOOL)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Custom (%u) DeviceHandle %p FilePath %p\n",
|
|
BootEntry->Type,
|
|
LoadedImage->DeviceHandle,
|
|
LoadedImage->FilePath
|
|
));
|
|
|
|
//
|
|
// Some fragile firmware fail to properly set LoadedImage file source
|
|
// fields to our custom device path, so we fix it up here.
|
|
// REF: https://github.com/acidanthera/bugtracker/issues/712
|
|
//
|
|
if ((LoadedImage->DeviceHandle == NULL) && (StorageHandle != NULL)) {
|
|
if (LoadedImage->FilePath != NULL) {
|
|
FreePool (LoadedImage->FilePath);
|
|
}
|
|
|
|
LoadedImage->DeviceHandle = StorageHandle;
|
|
LoadedImage->FilePath = StoragePath;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
InternalUnloadDmg (DmgLoadContext);
|
|
if (BootEntry->CustomFree != NULL) {
|
|
BootEntry->CustomFree (*CustomFreeContext);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|