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..5266af4dfc3 100644 --- a/Library/OcDevicePathLib/OcDevicePathLib.c +++ b/Library/OcDevicePathLib/OcDevicePathLib.c @@ -408,12 +408,169 @@ InternalExpandNewPath ( return -1; } +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 ( + *DevicePathNode, + (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 +781,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 +836,8 @@ OcFixAppleBootDevicePath ( Result = OcFixAppleBootDevicePathNode ( DevicePath, RemainingDevicePath, - RestoreContextPtr + RestoreContextPtr, + Device ); // // Save a restore context only for the first processing of the first node.