Skip to content

Commit

Permalink
OcBootManagementLib: Add Apple DP expansion for VirtIO BLK (#561)
Browse files Browse the repository at this point in the history
Apparently macOS strips down HD nodes from VirtIO BLK devices
leaving them "infix" shortened, i.e. PCI/APFS instead of
PCI/HD/APFS.
  • Loading branch information
vit9696 authored Oct 7, 2024
1 parent 402b3b1 commit 4b3b0cb
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 3 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion Include/Acidanthera/Library/OcDevicePathLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
);

/**
Expand Down
1 change: 1 addition & 0 deletions Library/OcBootManagementLib/BootEntryManagement.c
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,7 @@ AddBootEntryFromBootOption (
NumPatchedNodes = OcFixAppleBootDevicePathNode (
&DevicePath,
&RemainingDevicePath,
NULL,
NULL
);
} while (NumPatchedNodes > 0);
Expand Down
194 changes: 192 additions & 2 deletions Library/OcDevicePathLib/OcDevicePathLib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 4b3b0cb

Please sign in to comment.