From 4b3b0cb1d7da14a7e42621935679fef8114febfe Mon Sep 17 00:00:00 2001 From: Vitaly Cheptsov <4348897+vit9696@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:44:08 +0300 Subject: [PATCH] OcBootManagementLib: Add Apple DP expansion for VirtIO BLK (#561) Apparently macOS strips down HD nodes from VirtIO BLK devices leaving them "infix" shortened, i.e. PCI/APFS instead of PCI/HD/APFS. --- Changelog.md | 1 + Include/Acidanthera/Library/OcDevicePathLib.h | 5 +- .../OcBootManagementLib/BootEntryManagement.c | 1 + Library/OcDevicePathLib/OcDevicePathLib.c | 194 +++++++++++++++++- 4 files changed, 198 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8e532b699e1..a8e78a47412 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ OpenCore Changelog - Enabled `XcpmExtraMsrs MSR_MISC_PWR_MGMT` patch back on macOS 12+ - Fixed `XcpmExtraMsrs MSR_MISC_PWR_MGMT` patch on macOS 15 - Added `UEFI` `Unload` option to unload existing firmware drivers +- Fixed boot device selection with VirtIO disk drives used for macOS installations #### v1.0.1 - Updated code and added progress bar to macrecovery, thx @soyeonswife63 diff --git a/Include/Acidanthera/Library/OcDevicePathLib.h b/Include/Acidanthera/Library/OcDevicePathLib.h index cfc12ce9b04..a9915104c11 100644 --- a/Include/Acidanthera/Library/OcDevicePathLib.h +++ b/Include/Acidanthera/Library/OcDevicePathLib.h @@ -303,6 +303,8 @@ typedef struct { restore DevicePathNode's original content in the case of failure. On success, data may need to be freed. + @param[in] ValidDevice A device handle pointing to previous valid + device path if any. @retval -1 DevicePathNode could not be fixed. @retval 0 DevicePathNode was not modified and may be valid. @@ -313,7 +315,8 @@ INTN OcFixAppleBootDevicePathNode ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, - OUT APPLE_BOOT_DP_PATCH_CONTEXT *RestoreContext OPTIONAL + OUT APPLE_BOOT_DP_PATCH_CONTEXT *RestoreContext OPTIONAL, + IN EFI_HANDLE ValidDevice OPTIONAL ); /** diff --git a/Library/OcBootManagementLib/BootEntryManagement.c b/Library/OcBootManagementLib/BootEntryManagement.c index 1f1bd2359a8..4837ab74dc0 100644 --- a/Library/OcBootManagementLib/BootEntryManagement.c +++ b/Library/OcBootManagementLib/BootEntryManagement.c @@ -1459,6 +1459,7 @@ AddBootEntryFromBootOption ( NumPatchedNodes = OcFixAppleBootDevicePathNode ( &DevicePath, &RemainingDevicePath, + NULL, NULL ); } while (NumPatchedNodes > 0); diff --git a/Library/OcDevicePathLib/OcDevicePathLib.c b/Library/OcDevicePathLib/OcDevicePathLib.c index 3181b17f9dc..ddfa1bf46a0 100644 --- a/Library/OcDevicePathLib/OcDevicePathLib.c +++ b/Library/OcDevicePathLib/OcDevicePathLib.c @@ -408,12 +408,191 @@ InternalExpandNewPath ( return -1; } +/** + Fix Apple Boot Device Path VirtIO node to be compatible with conventional UEFI + implementations. Currently only APFS file system is supported as VirtIO support + landed in macOS in 10.14, which was APFS-only. + + @param[in,out] DevicePath A pointer to the device path to fix a node of. + It must be a pool memory buffer. + On success, may be updated with a reallocated + pool memory buffer. + @param[in,out] DevicePathNode A pointer to the device path node to fix. It + must be a node of *DevicePath. + On success, may be updated with the + corresponding node of *DevicePath. + @param[out] RestoreContext A pointer to a context that can be used to + restore DevicePathNode's original content in + the case of failure. + On success, data may need to be freed. + + @retval 0 DevicePathNode was not modified and may be valid. + @retval 1 DevicePathNode was fixed and may be valid. + +**/ +STATIC INTN -OcFixAppleBootDevicePathNode ( +InternalFixAppleBootDevicePathVirtioNode ( IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, OUT APPLE_BOOT_DP_PATCH_CONTEXT *RestoreContext OPTIONAL ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE Device; + EFI_HANDLE *HandleBuffer; + UINTN Index; + UINTN DevicePathSize; + UINTN ValidPrefixSize; + EFI_DEVICE_PATH_PROTOCOL *TestedDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TestedDeviceNode; + EFI_DEVICE_PATH_PROTOCOL *NewDeviceNode; + EFI_DEVICE_PATH_PROTOCOL *FixedDevicePath; + UINTN TestedSize; + UINT8 TestedNodeType; + UINT8 TestedNodeSubType; + + // + // Apple may skip HD node for VirtIO-BLK devices giving APFS Vendor DP + // right away in their device paths. Try to locate and fix them up. + // + + // + // 1. Get all Block Io protocol handles. + // + HandleCount = 0; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiBlockIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "Failed to locate any block i/o to fixup DP\n")); + return 0; + } + + // + // 2. Calculate prefix size. + // + DevicePathSize = GetDevicePathSize (*DevicePath); + ValidPrefixSize = DevicePathSize - GetDevicePathSize (*DevicePathNode); + + for (Index = 0; Index < HandleCount; ++Index) { + // + // 2. Get Device Path protocol from each handle. + // + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **)&TestedDevicePath + ); + + if (EFI_ERROR (Status)) { + continue; + } + + // + // 3. Calculate found device path size. + // + TestedSize = GetDevicePathSize (TestedDevicePath); + + // + // 4.1 Skip tested device paths that do not match HD paths. + // + if (TestedSize != ValidPrefixSize + sizeof (HARDDRIVE_DEVICE_PATH) + END_DEVICE_PATH_LENGTH) { + continue; + } + + // + // 4.2 Skip tested device paths that do not share a common prefix. + // + if (CompareMem (TestedDevicePath, *DevicePath, ValidPrefixSize) != 0) { + continue; + } + + // + // 4.3 Skip tested device paths that are not adding HD nodes. + // + TestedDeviceNode = (VOID *)((UINTN)TestedDevicePath + ValidPrefixSize); + TestedNodeType = DevicePathType (TestedDeviceNode); + TestedNodeSubType = DevicePathSubType (TestedDeviceNode); + if ( (TestedNodeType != MEDIA_DEVICE_PATH) + || (TestedNodeSubType != MEDIA_HARDDRIVE_DP)) + { + continue; + } + + // + // 5. Initial checks matched. Build fixed device path. + // + FixedDevicePath = AllocatePool (DevicePathSize + sizeof (HARDDRIVE_DEVICE_PATH)); + if (FixedDevicePath == NULL) { + FreePool (HandleBuffer); + return 0; + } + + CopyMem ( + FixedDevicePath, + TestedDevicePath, + ValidPrefixSize + sizeof (HARDDRIVE_DEVICE_PATH) + ); + + TestedDeviceNode = (VOID *)((UINTN)FixedDevicePath + ValidPrefixSize + sizeof (HARDDRIVE_DEVICE_PATH)); + + CopyMem ( + TestedDeviceNode, + (VOID *)((UINTN)*DevicePath + ValidPrefixSize), + DevicePathSize - ValidPrefixSize + ); + + // + // 6. Check this is the right partition. + // + NewDeviceNode = FixedDevicePath; + Status = gBS->LocateDevicePath ( + &gEfiDevicePathProtocolGuid, + &NewDeviceNode, + &Device + ); + if (EFI_ERROR (Status)) { + FreePool (FixedDevicePath); + continue; + } + + // + // If the discovered device path is past the HD path, then it is ours. + // Otherwise we picked up wrong partition and need to retry. + // + if ((UINTN)NewDeviceNode > (UINTN)TestedDeviceNode) { + RestoreContext->OldPath = *DevicePath; + *DevicePath = FixedDevicePath; + *DevicePathNode = TestedDeviceNode; + FreePool (HandleBuffer); + return 1; + } + + FreePool (FixedDevicePath); + } + + FreePool (HandleBuffer); + + // + // We found nothing. + // + return 0; +} + +INTN +OcFixAppleBootDevicePathNode ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNode, + OUT APPLE_BOOT_DP_PATCH_CONTEXT *RestoreContext OPTIONAL, + IN EFI_HANDLE ValidDevice OPTIONAL + ) { INTN Result; EFI_DEVICE_PATH_PROTOCOL *OldPath; @@ -624,6 +803,16 @@ OcFixAppleBootDevicePathNode ( default: break; } + } else if ((NodeType == MEDIA_DEVICE_PATH) && (NodeSubType == MEDIA_VENDOR_DP)) { + if (ValidDevice == NULL) { + return 0; + } + + return InternalFixAppleBootDevicePathVirtioNode ( + DevicePath, + DevicePathNode, + RestoreContext + ); } return 0; @@ -669,7 +858,8 @@ OcFixAppleBootDevicePath ( Result = OcFixAppleBootDevicePathNode ( DevicePath, RemainingDevicePath, - RestoreContextPtr + RestoreContextPtr, + Device ); // // Save a restore context only for the first processing of the first node.