From 8a514aa757dd29af481fdd355b887c844d758375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20H=C3=A4user?= <8659494+mhaeuser@users.noreply.github.com> Date: Sat, 27 Feb 2021 22:02:38 +0100 Subject: [PATCH] OcBootManagementLib: Support default custom entries --- .../Acidanthera/Library/OcBootManagementLib.h | 4 + .../OcBootManagementLib/BootEntryManagement.c | 174 +++++++++--- .../BootManagementInternal.h | 45 ++++ .../OcBootManagementLib/DefaultEntryChoice.c | 248 +++++++++++++----- 4 files changed, 368 insertions(+), 103 deletions(-) diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index 9540d37c..c81cbbb5 100755 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -194,6 +194,10 @@ typedef struct OC_BOOT_ENTRY_ { // BOOLEAN IsGeneric; // + // Set when this entry refers to a custom boot entry. + // + BOOLEAN IsCustom; + // // Should make this option default boot option. // BOOLEAN SetDefault; diff --git a/Library/OcBootManagementLib/BootEntryManagement.c b/Library/OcBootManagementLib/BootEntryManagement.c index 622d1222..15bcfebd 100644 --- a/Library/OcBootManagementLib/BootEntryManagement.c +++ b/Library/OcBootManagementLib/BootEntryManagement.c @@ -613,6 +613,8 @@ AddBootEntryFromCustomEntry ( } } + BootEntry->IsCustom = TRUE; + RegisterBootOption ( BootContext, FileSystem, @@ -996,7 +998,9 @@ EFI_STATUS AddBootEntryFromBootOption ( IN OUT OC_BOOT_CONTEXT *BootContext, IN UINT16 BootOption, - IN BOOLEAN LazyScan + IN BOOLEAN LazyScan, + IN OUT OC_BOOT_FILESYSTEM *CustomFileSystem, + IN OUT UINT32 *CustomIndex ) { EFI_STATUS Status; @@ -1012,6 +1016,10 @@ AddBootEntryFromBootOption ( EFI_LOAD_OPTION *LoadOption; UINTN LoadOptionSize; + CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath; + UINT32 Index; + INTN CmpResult; + DEBUG ((DEBUG_INFO, "OCB: Building entry from Boot%04x\n", BootOption)); // @@ -1179,6 +1187,31 @@ AddBootEntryFromBootOption ( NULL ); } while (NumPatchedNodes > 0); + // + // If requested, pre-construct a custom entry found in BOOT#### so it can be + // set as default. + // + if (ExpandedDevicePath == NULL && CustomFileSystem != NULL) { + ASSERT (CustomIndex != NULL); + + CustomDevPath = InternetGetOcCustomDevPath (DevicePath); + + for (Index = 0; Index < BootContext->PickerContext->AllCustomEntryCount; ++Index) { + CmpResult = MixedStrCmp ( + CustomDevPath->EntryName.PathName, + BootContext->PickerContext->CustomEntries[Index].Name + ); + if (CmpResult == 0) { + *CustomIndex = Index; + AddBootEntryFromCustomEntry ( + BootContext, + CustomFileSystem, + &BootContext->PickerContext->CustomEntries[Index] + ); + break; + } + } + } FreePool (DevicePath); DevicePath = ExpandedDevicePath; @@ -1396,14 +1429,43 @@ AddFileSystemEntry ( return EFI_SUCCESS; } +STATIC +OC_BOOT_FILESYSTEM * +CreateFileSystemForCustom ( + IN OUT CONST OC_BOOT_CONTEXT *BootContext + ) +{ + OC_BOOT_FILESYSTEM *FileSystem; + + FileSystem = AllocateZeroPool (sizeof (*FileSystem)); + if (FileSystem == NULL) { + return NULL; + } + + FileSystem->Handle = OC_CUSTOM_FS_HANDLE; + InitializeListHead (&FileSystem->BootEntries); + + DEBUG (( + DEBUG_INFO, + "OCB: Adding fs %p for %u custom entries%a%a\n", + OC_CUSTOM_FS_HANDLE, + BootContext->PickerContext->AllCustomEntryCount, + BootContext->PickerContext->ShowNvramReset ? " and nvram reset" : "", + BootContext->PickerContext->HideAuxiliary ? " (aux hidden)" : " (aux shown)" + )); + + return FileSystem; +} + STATIC EFI_STATUS AddFileSystemEntryForCustom ( - IN OUT OC_BOOT_CONTEXT *BootContext + IN OUT OC_BOOT_CONTEXT *BootContext, + IN OUT OC_BOOT_FILESYSTEM *FileSystem, + IN UINT32 PrecreatedCustomIndex ) { EFI_STATUS Status; - OC_BOOT_FILESYSTEM *FileSystem; UINTN Index; // @@ -1416,28 +1478,16 @@ AddFileSystemEntryForCustom ( return EFI_NOT_FOUND; } - DEBUG (( - DEBUG_INFO, - "OCB: Adding fs %p for %u custom entries%a%a\n", - OC_CUSTOM_FS_HANDLE, - BootContext->PickerContext->AllCustomEntryCount, - BootContext->PickerContext->ShowNvramReset ? " and nvram reset" : "", - BootContext->PickerContext->HideAuxiliary ? " (aux hidden)" : " (aux shown)" - )); - - FileSystem = AllocateZeroPool (sizeof (*FileSystem)); - if (FileSystem == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - FileSystem->Handle = OC_CUSTOM_FS_HANDLE; - InitializeListHead (&FileSystem->BootEntries); - InsertTailList (&BootContext->FileSystems, &FileSystem->Link); - ++BootContext->FileSystemCount; - Status = EFI_NOT_FOUND; for (Index = 0; Index < BootContext->PickerContext->AllCustomEntryCount; ++Index) { - Status = AddBootEntryFromCustomEntry ( + // + // Skip the custom boot entry that has already been created. + // + if (Index == PrecreatedCustomIndex) { + continue; + } + + AddBootEntryFromCustomEntry ( BootContext, FileSystem, &BootContext->PickerContext->CustomEntries[Index] @@ -1681,6 +1731,9 @@ OcScanForBootEntries ( UINTN Index; LIST_ENTRY *Link; OC_BOOT_FILESYSTEM *FileSystem; + OC_BOOT_FILESYSTEM *CustomFileSystem; + OC_BOOT_FILESYSTEM *CustomFileSystemDefault; + UINT32 DefaultCustomIndex; // // Obtain the list of filesystems filtered by scan policy. @@ -1706,9 +1759,33 @@ OcScanForBootEntries ( ); } + CustomFileSystem = CreateFileSystemForCustom (BootContext); + + // + // Delay CustomFileSystem insertion to have custom entries and the end. + // + + DefaultCustomIndex = MAX_UINT32; + if (Context->BootOrder != NULL) { + CustomFileSystemDefault = CustomFileSystem; + for (Index = 0; Index < Context->BootOrderCount; ++Index) { - AddBootEntryFromBootOption (BootContext, Context->BootOrder[Index], FALSE); + AddBootEntryFromBootOption ( + BootContext, + Context->BootOrder[Index], + FALSE, + CustomFileSystemDefault, + &DefaultCustomIndex + ); + + // + // Pre-create at most one custom entry. Under normal circumstances, no + // more than one entry should exist anyway. + // + if (DefaultCustomIndex != MAX_UINT32) { + CustomFileSystemDefault = NULL; + } } } @@ -1744,10 +1821,18 @@ OcScanForBootEntries ( AddBootEntryFromSelfRecovery (BootContext, FileSystem); } - // - // Build custom and system options. - // - AddFileSystemEntryForCustom (BootContext); + if (CustomFileSystem != NULL) { + // + // Insert the custom file system last for entry order. + // + InsertTailList (&BootContext->FileSystems, &CustomFileSystem->Link); + ++BootContext->FileSystemCount; + + // + // Build custom and system options. + // + AddFileSystemEntryForCustom (BootContext, CustomFileSystem, DefaultCustomIndex); + } if (BootContext->BootEntryCount == 0) { OcFreeBootContext (BootContext); @@ -1775,6 +1860,8 @@ OcScanForDefaultBootEntry ( EFI_STATUS Status; UINTN NoHandles; EFI_HANDLE *Handles; + UINT32 DefaultCustomIndex; + OC_BOOT_FILESYSTEM *CustomFileSystem; // // Obtain empty list of filesystems. @@ -1797,9 +1884,27 @@ OcScanForDefaultBootEntry ( ); } + CustomFileSystem = CreateFileSystemForCustom (BootContext); + if (CustomFileSystem != NULL) { + // + // The entry order does not matter, UI will not be shown. + // + InsertTailList (&BootContext->FileSystems, &CustomFileSystem->Link); + ++BootContext->FileSystemCount; + } + if (Context->BootOrder != NULL) { for (Index = 0; Index < Context->BootOrderCount; ++Index) { - AddBootEntryFromBootOption (BootContext, Context->BootOrder[Index], TRUE); + // + // DefaultCustomIndex is not used as the entry list will never be shown. + // + AddBootEntryFromBootOption ( + BootContext, + Context->BootOrder[Index], + TRUE, + CustomFileSystem, + &DefaultCustomIndex + ); // // Return as long as we are good. @@ -1865,10 +1970,13 @@ OcScanForDefaultBootEntry ( FreePool (Handles); } - // - // Build custom and system options. - // - AddFileSystemEntryForCustom (BootContext); + if (CustomFileSystem != NULL) { + // + // Build custom and system options. Do not try to deduplicate custom options + // as the list is never shown. + // + AddFileSystemEntryForCustom (BootContext, CustomFileSystem, MAX_UINT32); + } if (BootContext->DefaultEntry == NULL) { OcFreeBootContext (BootContext); diff --git a/Library/OcBootManagementLib/BootManagementInternal.h b/Library/OcBootManagementLib/BootManagementInternal.h index a872fdfd..441d78a3 100644 --- a/Library/OcBootManagementLib/BootManagementInternal.h +++ b/Library/OcBootManagementLib/BootManagementInternal.h @@ -26,6 +26,41 @@ #define OC_CUSTOM_FS_HANDLE ((EFI_HANDLE)(UINTN) 0x2007C5F5U) +/// +/// Identifies the DevicePath structure for OpenCore custom entries. +/// +#define OC_CUSTOM_BOOT_DEVICE_PATH_GUID \ + { 0xd6f263f9, 0x0b19, 0x4670, \ + { 0xb0, 0xa4, 0x9d, 0x95, 0x9f, 0x58, 0xdf, 0x65 } } + +#pragma pack(1) + +/// +/// DevicePath to describe OpenCore custom entries. +/// +typedef PACKED struct { + VENDOR_DEVICE_PATH Hdr; + FILEPATH_DEVICE_PATH EntryName; +} OC_CUSTOM_BOOT_DEVICE_PATH; + +// +// Ideally, a variant of FILEPATH_DEVICE_PATH will be used with PathName as a +// flexible array. Such cannot be used for declarations, so provide an +// alternative. +// +typedef PACKED struct { + VENDOR_DEVICE_PATH Header; + EFI_DEVICE_PATH_PROTOCOL EntryName; +} OC_CUSTOM_BOOT_DEVICE_PATH_DECL; + +#pragma pack() + +/// +/// The size of a OC_CUSTOM_BOOT_DEVICE_PATH structure excluding the name. +/// +#define SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH \ + (sizeof (VENDOR_DEVICE_PATH) + SIZE_OF_FILEPATH_DEVICE_PATH) + typedef struct { EFI_DEVICE_PATH_PROTOCOL *DevicePath; OC_APPLE_DISK_IMAGE_CONTEXT *DmgContext; @@ -169,4 +204,14 @@ InternalSystemActionResetNvram ( VOID ); +/** + Determines whether DevicePath is an OpenCore custom boot entry. + + @returns The OpenCore custom boot entry, or NULL. +**/ +CONST OC_CUSTOM_BOOT_DEVICE_PATH * +InternetGetOcCustomDevPath ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + #endif // BOOT_MANAGEMENET_INTERNAL_H diff --git a/Library/OcBootManagementLib/DefaultEntryChoice.c b/Library/OcBootManagementLib/DefaultEntryChoice.c index 42429ccf..c0f2394d 100644 --- a/Library/OcBootManagementLib/DefaultEntryChoice.c +++ b/Library/OcBootManagementLib/DefaultEntryChoice.c @@ -39,6 +39,57 @@ #include #include +/// +/// Template for an OpenCore custom boot entry DevicePath node. +/// +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 } + } +}; + +CONST OC_CUSTOM_BOOT_DEVICE_PATH * +InternetGetOcCustomDevPath ( + 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; +} + EFI_LOAD_OPTION * InternalGetBootOptionData ( OUT UINTN *OptionSize, @@ -226,10 +277,9 @@ InternalDebugBootEnvironment ( } STATIC -OC_BOOT_ENTRY * -InternalGetBootEntryByDevicePath ( - IN OUT OC_BOOT_ENTRY *BootEntries, - IN UINTN NumBootEntries, +BOOLEAN +InternalMatchBootEntryByDevicePath ( + IN OUT OC_BOOT_ENTRY *BootEntry, IN EFI_DEVICE_PATH_PROTOCOL *UefiDevicePath, IN EFI_DEVICE_PATH_PROTOCOL *UefiRemainingDevicePath, IN UINTN UefiDevicePathSize, @@ -243,68 +293,81 @@ InternalGetBootEntryByDevicePath ( EFI_DEVICE_PATH_PROTOCOL *OcDevicePath; EFI_DEVICE_PATH_PROTOCOL *OcRemainingDevicePath; - OC_BOOT_ENTRY *BootEntry; - UINTN Index; - RootDevicePathSize = ((UINT8 *)UefiRemainingDevicePath - (UINT8 *)UefiDevicePath); - for (Index = 0; Index < NumBootEntries; ++Index) { - BootEntry = &BootEntries[Index]; - if (BootEntry->DevicePath == NULL || BootEntry->Type == OC_BOOT_SYSTEM) { - continue; - } - - OcDevicePath = BootEntry->DevicePath; - - if ((GetDevicePathSize (OcDevicePath) - END_DEVICE_PATH_LENGTH) < RootDevicePathSize) { - continue; - } - - CmpResult = CompareMem (OcDevicePath, UefiDevicePath, RootDevicePathSize); - if (CmpResult != 0) { - continue; - } - // - // 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) - ) { - continue; - } - } 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 BootEntry; + if (BootEntry->DevicePath == NULL || BootEntry->Type == OC_BOOT_SYSTEM) { + return FALSE; } - return NULL; + 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) { + return FALSE; + } + + CmpResult = StrCmp (BootEntry->Name, DevicePath->EntryName.PathName); + if (CmpResult != 0) { + return FALSE; + } + + return TRUE; } STATIC @@ -671,7 +734,7 @@ OcSetDefaultBootEntry ( EFI_DEVICE_PATH *BootOptionDevicePath; EFI_DEVICE_PATH *BootOptionRemainingDevicePath; EFI_HANDLE DeviceHandle; - OC_BOOT_ENTRY *MatchedEntry; + BOOLEAN MatchedEntry; EFI_GUID *BootVariableGuid; CHAR16 *BootOrderName; CHAR16 *BootVariableName; @@ -686,6 +749,10 @@ OcSetDefaultBootEntry ( UINTN LoadOptionNameSize; EFI_LOAD_OPTION *LoadOption; + CONST OC_CUSTOM_BOOT_DEVICE_PATH *CustomDevPath; + OC_CUSTOM_BOOT_DEVICE_PATH *DestCustomDevPath; + EFI_DEVICE_PATH_PROTOCOL *DestCustomEndNode; + // // Do not allow when prohibited. // @@ -718,14 +785,14 @@ OcSetDefaultBootEntry ( NULL ); - MatchedEntry = NULL; + MatchedEntry = FALSE; BootChosenIndex = BootOrderCount; for (Index = 0; Index < BootOrderCount; ++Index) { if (BootOrder[Index] == 0x80) { BootChosenIndex = Index; } - if (MatchedEntry != NULL) { + if (MatchedEntry) { if (BootChosenIndex != BootOrderCount) { break; } @@ -758,25 +825,38 @@ OcSetDefaultBootEntry ( ); if (!EFI_ERROR (Status)) { - MatchedEntry = InternalGetBootEntryByDevicePath ( + MatchedEntry = InternalMatchBootEntryByDevicePath ( Entry, - 1, BootOptionDevicePath, BootOptionRemainingDevicePath, LoadOption->FilePathListLength, FALSE ); + } else { + CustomDevPath = InternetGetOcCustomDevPath (BootOptionDevicePath); + if (CustomDevPath != NULL) { + MatchedEntry = InternalMatchCustomBootEntryByDevicePath ( + Entry, + CustomDevPath + ); + } } FreePool (LoadOption); } - if (MatchedEntry == NULL) { + if (!MatchedEntry) { // // Write to Boot0080 // LoadOptionNameSize = StrSize (Entry->Name); - DevicePathSize = GetDevicePathSize (Entry->DevicePath); + + if (!Entry->IsCustom) { + DevicePathSize = GetDevicePathSize (Entry->DevicePath); + } else { + DevicePathSize = SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH + LoadOptionNameSize + sizeof (EFI_DEVICE_PATH_PROTOCOL); + } + LoadOptionSize = sizeof (EFI_LOAD_OPTION) + LoadOptionNameSize + DevicePathSize; LoadOption = AllocatePool (LoadOptionSize); @@ -791,7 +871,35 @@ OcSetDefaultBootEntry ( LoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT; LoadOption->FilePathListLength = (UINT16) DevicePathSize; CopyMem (LoadOption + 1, Entry->Name, LoadOptionNameSize); - CopyMem ((UINT8 *) (LoadOption + 1) + LoadOptionNameSize, Entry->DevicePath, DevicePathSize); + + if (!Entry->IsCustom) { + CopyMem ((UINT8 *) (LoadOption + 1) + LoadOptionNameSize, Entry->DevicePath, DevicePathSize); + } else { + DestCustomDevPath = (OC_CUSTOM_BOOT_DEVICE_PATH *) ( + (UINT8 *) (LoadOption + 1) + LoadOptionNameSize + ); + CopyMem ( + DestCustomDevPath, + &mOcCustomBootDevPathTemplate, + sizeof (mOcCustomBootDevPathTemplate) + ); + CopyMem ( + DestCustomDevPath->EntryName.PathName, + Entry->Name, + LoadOptionNameSize + ); + // + // FIXME: This may theoretically overflow. + // + DestCustomDevPath->EntryName.Header.Length[0] += (UINT8) LoadOptionNameSize; + + DestCustomEndNode = (EFI_DEVICE_PATH_PROTOCOL *) ( + (UINT8 *) DestCustomDevPath + SIZE_OF_OC_CUSTOM_BOOT_DEVICE_PATH + LoadOptionNameSize + ); + SetDevicePathEndNode (DestCustomEndNode); + + ASSERT (GetDevicePathSize (&DestCustomDevPath->Hdr.Header) == DevicePathSize); + } Status = gRT->SetVariable ( BootVariableName,