Skip to content

Commit

Permalink
OcBootManagementLib: Handle legacy default boot entry
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldfish64 committed Aug 5, 2023
1 parent 3756798 commit 323a420
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 130 deletions.
6 changes: 6 additions & 0 deletions Include/Acidanthera/Library/OcFileLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,12 @@ OcGetDiskMbrTable (
IN BOOLEAN UseBlockIo2
);

EFI_DEVICE_PATH_PROTOCOL *
OcDiskFindActiveMbrPartitionPath (
IN EFI_DEVICE_PATH_PROTOCOL *DiskDevicePath,
OUT UINTN *PartitionDevicePathSize
);

/**
Creates a device path for a firmware file.
Expand Down
293 changes: 168 additions & 125 deletions Library/OcBootManagementLib/BootEntryManagement.c
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,97 @@ AddBootEntryFromSelfRecovery (
return Status;
}

/**
Create bootable entries from legacy boot partitons.
@param[in,out] BootContext Context of filesystems.
@param[in,out] FileSystem Filesystem to scan for recovery.
@retval EFI_SUCCESS on success.
**/
STATIC
EFI_STATUS
AddBootEntryFromLegacyPartition (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
OC_BOOT_ENTRY *BootEntry;

//
// Only support legacy filesystems.
//
if (FileSystem->LegacyOsType == OcLegacyOsTypeNone) {
return EFI_UNSUPPORTED;
}

Status = gBS->HandleProtocol (
FileSystem->Handle,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}

DebugPrintDevicePath (DEBUG_INFO, "OCB: Adding legacy entry for disk", DevicePath);

//
// Allocate, initialise, and describe boot entry.
//
BootEntry = AllocateZeroPool (sizeof (*BootEntry));
if (BootEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}

BootEntry->DevicePath = DuplicateDevicePath (DevicePath);
if (BootEntry->DevicePath == NULL) {
FreePool (BootEntry);
return EFI_OUT_OF_RESOURCES;
}

BootEntry->IsExternal = FileSystem->External;
BootEntry->LegacyOsType = FileSystem->LegacyOsType;
BootEntry->LaunchInText = TRUE;

//
// Load options are set based on type of disk.
// Internal disk = "HD"
// External disk = "USB"
// Optical disk = "CD"
//
// TODO: Support optical media.
//
if (BootEntry->IsExternal) {
BootEntry->LoadOptionsSize = L_STR_SIZE ("USB");
BootEntry->LoadOptions = AllocateCopyPool (L_STR_SIZE ("USB"), "USB");
} else {
BootEntry->LoadOptionsSize = L_STR_SIZE ("HD");
BootEntry->LoadOptions = AllocateCopyPool (L_STR_SIZE ("HD"), "HD");
}

if (BootEntry->LoadOptions == NULL) {
FreePool (BootEntry);
return EFI_OUT_OF_RESOURCES;
}

Status = InternalDescribeLegacyBootEntry (BootContext, FileSystem->LegacyOsType, BootEntry);
if (EFI_ERROR (Status)) {
FreePool (BootEntry);
return Status;
}

RegisterBootOption (
BootContext,
FileSystem,
BootEntry
);

return EFI_SUCCESS;
}

/**
Create bootable entries from boot options.
Expand Down Expand Up @@ -1209,11 +1300,13 @@ AddBootEntryFromBootOption (
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ExpandedDevicePath;
EFI_DEVICE_PATH_PROTOCOL *ActivePartitionDevicePath;
EFI_HANDLE FileSystemHandle;
OC_BOOT_FILESYSTEM *FileSystem;
UINTN DevicePathSize;
INTN NumPatchedNodes;
BOOLEAN IsAppleLegacy;
BOOLEAN IsLegacyEntry;
BOOLEAN IsRoot;
EFI_LOAD_OPTION *LoadOption;
UINTN LoadOptionSize;
Expand Down Expand Up @@ -1262,6 +1355,7 @@ AddBootEntryFromBootOption (
// BootCamp device path will point to disk instead of partition.
//
IsAppleLegacy = InternalIsAppleLegacyLoadApp (DevicePath);
IsLegacyEntry = FALSE;
if (IsAppleLegacy) {
FreePool (DevicePath);
Status = GetVariable2 (
Expand Down Expand Up @@ -1303,7 +1397,15 @@ AddBootEntryFromBootOption (
}

//
// Expand BootCamp device path to EFI partition device path.
// Handle Apple BootCamp device path.
//
// If we have a legacy installation of Windows in place, Device Path
// will point to disk containing the active partition to boot. This will
// need to be expanded to the full Device Path of the active partition.
//
// If not, locate the EFI partition Device Path to boot Windows in UEFI mode.
// Installing Windows on Macs that normally use legacy mode for Boot Camp
// will exhibit this behavior if Windows is selected as a startup disk from within macOS.
//
if (IsAppleLegacy) {
//
Expand All @@ -1316,12 +1418,36 @@ AddBootEntryFromBootOption (
return EFI_NOT_FOUND;
}

RemainingDevicePath = DevicePath;
DevicePath = OcDiskFindSystemPartitionPath (
DevicePath,
&DevicePathSize,
&FileSystemHandle
);
//
// Attempt to locate active MBR partition Device Path.
//
RemainingDevicePath = DevicePath;
ActivePartitionDevicePath = OcDiskFindActiveMbrPartitionPath (
DevicePath,
&DevicePathSize
);
if (ActivePartitionDevicePath != NULL) {
DevicePath = ActivePartitionDevicePath;
Status = gBS->LocateDevicePath (
&gEfiBlockIoProtocolGuid,
&ActivePartitionDevicePath,
&FileSystemHandle
);

if (!EFI_ERROR (Status)) {
IsLegacyEntry = TRUE;
}

//
// Expand BootCamp device path to EFI partition device path.
//
} else {
DevicePath = OcDiskFindSystemPartitionPath (
DevicePath,
&DevicePathSize,
&FileSystemHandle
);
}

FreePool (RemainingDevicePath);

Expand All @@ -1341,8 +1467,9 @@ AddBootEntryFromBootOption (
}

//
// The Device Path returned by OcDiskFindSystemPartitionPath() is a pointer
// to an installed protocol. Duplicate it so we own the memory.
// The Device Path returned by OcDiskFindActiveMbrPartitionPath() and
// OcDiskFindSystemPartitionPath() is a pointer to an installed protocol.
// Duplicate it so we own the memory.
//
if (DevicePath != NULL) {
DevicePath = AllocateCopyPool (DevicePathSize, DevicePath);
Expand Down Expand Up @@ -1565,125 +1692,41 @@ AddBootEntryFromBootOption (
FreePool (DevicePath);
}

//
// We may have a Boot#### entry pointing to macOS with full DP (up to boot.efi),
// so IsRoot will be true. However, if this is APFS, we may still have:
// - Recovery for this macOS.
// - Another macOS installation.
// We can only detect them with bless, so we invoke bless in deduplication mode.
// We also detect only the Core Apple Boot Policy predefined booter paths to
// avoid detection of e.g. generic booters (such as BOOTx64) to avoid
// duplicates.
//
// The amount of paths depends on the kind of the entry.
// - If this is a root entry (i.e. it points to the partition)
// we invoke full bless, as it may be Windows entry created by legacy NVRAM script.
// - If this is a full entry (i.e. it points to the bootloader)
// we invoke partial bless, which ignores BOOTx64.efi.
// Ignoring BOOTx64.efi is important as we may already have bootmgfw.efi as our entry,
// and we do not want to see Windows added twice.
//
Status = AddBootEntryFromBless (
BootContext,
FileSystem,
gAppleBootPolicyPredefinedPaths,
IsRoot ? gAppleBootPolicyNumPredefinedPaths : gAppleBootPolicyCoreNumPredefinedPaths,
LazyScan,
TRUE
);

return Status;
}

/**
Create bootable entries from legacy boot partitons.
@param[in,out] BootContext Context of filesystems.
@param[in,out] FileSystem Filesystem to scan for recovery.
@retval EFI_SUCCESS on success.
**/
STATIC
EFI_STATUS
AddBootEntryFromLegacyPartition (
IN OUT OC_BOOT_CONTEXT *BootContext,
IN OUT OC_BOOT_FILESYSTEM *FileSystem
)
{
EFI_STATUS Status;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
OC_BOOT_ENTRY *BootEntry;

//
// Only support legacy filesystems.
//
if (FileSystem->LegacyOsType == OcLegacyOsTypeNone) {
return EFI_UNSUPPORTED;
}

Status = gBS->HandleProtocol (
FileSystem->Handle,
&gEfiDevicePathProtocolGuid,
(VOID **)&DevicePath
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}

DebugPrintDevicePath (DEBUG_INFO, "OCB: Adding legacy entry for disk", DevicePath);

//
// Allocate, initialise, and describe boot entry.
//
BootEntry = AllocateZeroPool (sizeof (*BootEntry));
if (BootEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}

BootEntry->DevicePath = DuplicateDevicePath (DevicePath);
if (BootEntry->DevicePath == NULL) {
FreePool (BootEntry);
return EFI_OUT_OF_RESOURCES;
}

BootEntry->IsExternal = FileSystem->External;
BootEntry->LegacyOsType = FileSystem->LegacyOsType;
BootEntry->LaunchInText = TRUE;

//
// Load options are set based on type of disk.
// Internal disk = "HD"
// External disk = "USB"
// Optical disk = "CD"
//
// TODO: Support optical media.
//
if (BootEntry->IsExternal) {
BootEntry->LoadOptionsSize = L_STR_SIZE ("USB");
BootEntry->LoadOptions = AllocateCopyPool (L_STR_SIZE ("USB"), "USB");
if (IsLegacyEntry) {
Status = AddBootEntryFromLegacyPartition (
BootContext,
FileSystem
);
} else {
BootEntry->LoadOptionsSize = L_STR_SIZE ("HD");
BootEntry->LoadOptions = AllocateCopyPool (L_STR_SIZE ("HD"), "HD");
}

if (BootEntry->LoadOptions == NULL) {
FreePool (BootEntry);
return EFI_OUT_OF_RESOURCES;
}

Status = InternalDescribeLegacyBootEntry (BootContext, FileSystem->LegacyOsType, BootEntry);
if (EFI_ERROR (Status)) {
FreePool (BootEntry);
return Status;
//
// We may have a Boot#### entry pointing to macOS with full DP (up to boot.efi),
// so IsRoot will be true. However, if this is APFS, we may still have:
// - Recovery for this macOS.
// - Another macOS installation.
// We can only detect them with bless, so we invoke bless in deduplication mode.
// We also detect only the Core Apple Boot Policy predefined booter paths to
// avoid detection of e.g. generic booters (such as BOOTx64) to avoid
// duplicates.
//
// The amount of paths depends on the kind of the entry.
// - If this is a root entry (i.e. it points to the partition)
// we invoke full bless, as it may be Windows entry created by legacy NVRAM script.
// - If this is a full entry (i.e. it points to the bootloader)
// we invoke partial bless, which ignores BOOTx64.efi.
// Ignoring BOOTx64.efi is important as we may already have bootmgfw.efi as our entry,
// and we do not want to see Windows added twice.
//
Status = AddBootEntryFromBless (
BootContext,
FileSystem,
gAppleBootPolicyPredefinedPaths,
IsRoot ? gAppleBootPolicyNumPredefinedPaths : gAppleBootPolicyCoreNumPredefinedPaths,
LazyScan,
TRUE
);
}

RegisterBootOption (
BootContext,
FileSystem,
BootEntry
);

return EFI_SUCCESS;
return Status;
}

/**
Expand Down
Loading

0 comments on commit 323a420

Please sign in to comment.