mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-27 12:15:19 +01:00
cd23181296
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1727 lines
44 KiB
C
1727 lines
44 KiB
C
/** @file
|
|
Copyright (C) 2019, vit9696. All rights reserved.
|
|
|
|
All rights reserved.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
**/
|
|
|
|
#include <AppleMacEfi.h>
|
|
#include <MicrosoftWindows.h>
|
|
|
|
#include <Guid/AppleApfsInfo.h>
|
|
#include <Guid/AppleBless.h>
|
|
#include <Guid/FileInfo.h>
|
|
|
|
#include <Protocol/SimpleFileSystem.h>
|
|
#include <Protocol/AppleBootPolicy.h>
|
|
|
|
#include <Library/OcAppleBootPolicyLib.h>
|
|
#include <Library/OcDevicePathLib.h>
|
|
#include <Library/OcFileLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/OcDebugLogLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
#ifndef DEBUG_BULK_INFO
|
|
#define DEBUG_BULK_INFO (DEBUG_VERBOSE|DEBUG_INFO)
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
EFI_HANDLE Handle;
|
|
GUID ContainerGuid;
|
|
GUID VolumeGuid;
|
|
APPLE_APFS_VOLUME_ROLE VolumeRole;
|
|
} APFS_VOLUME_INFO;
|
|
|
|
typedef struct {
|
|
EFI_HANDLE Handle;
|
|
CHAR16 *VolumeDirName;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
} APFS_VOLUME_ROOT;
|
|
|
|
///
|
|
/// An array of file paths to search for in case no file is blessed.
|
|
/// On Apple Macs this list includes:
|
|
/// 1. APPLE_BOOTER_DEFAULT_FILE_NAME -- \System\Library\CoreServices\boot.efi
|
|
/// 2. APPLE_REMOVABLE_MEDIA_FILE_NAME -- \EFI\APPLE\X64\BOOT.EFI
|
|
/// 3. EFI_REMOVABLE_MEDIA_FILE_NAME -- \EFI\BOOT\BOOTX64.EFI
|
|
/// 4. APPLE_BOOTER_ROOT_FILE_NAME -- \boot.efi
|
|
///
|
|
/// Since in real world only 1st and 3rd entries are used, we do not include
|
|
/// 2nd and 4th until further notice. However, we do include a custom entry
|
|
/// for Windows, for the reason OpenCore, unlike Apple EFI, may override
|
|
/// BOOTx64.efi, and this is not the case for Apple EFI. So we end up with:
|
|
/// 1. APPLE_BOOTER_DEFAULT_FILE_NAME -- \System\Library\CoreServices\boot.efi
|
|
/// 2. MS_BOOTER_DEFAULT_FILE_NAME -- \EFI\Microsoft\Boot\bootmgfw.efi
|
|
/// 3. EFI_REMOVABLE_MEDIA_FILE_NAME -- \EFI\BOOT\BOOTX64.EFI
|
|
///
|
|
/// This resolves a problem when Windows installer does not replace our BOOTx64.efi
|
|
/// file with Windows file and then NVRAM reset or Boot Camp software reboot to macOS
|
|
/// results in the removal of the Windows boot entry from NVRAM making Windows
|
|
/// disappear from the list of OpenCore entries without BlessOverride.
|
|
///
|
|
/// Linux and related entries are not present here, because they have fine working
|
|
/// software for boot management and do not use BOOTx64.efi in the first place.
|
|
///
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR16 *gAppleBootPolicyPredefinedPaths[] = {
|
|
APPLE_BOOTER_DEFAULT_FILE_NAME,
|
|
MS_BOOTER_DEFAULT_FILE_NAME,
|
|
EFI_REMOVABLE_MEDIA_FILE_NAME
|
|
};
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST UINTN gAppleBootPolicyNumPredefinedPaths =
|
|
ARRAY_SIZE (gAppleBootPolicyPredefinedPaths);
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST UINTN gAppleBootPolicyCoreNumPredefinedPaths = 2;
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFile (
|
|
IN EFI_HANDLE Device,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFileEx (
|
|
IN EFI_HANDLE Device,
|
|
IN BOOT_POLICY_ACTION Action,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyDevicePathToDirPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetApfsRecoveryFilePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 *PathName,
|
|
OUT CHAR16 **FullPathName,
|
|
OUT VOID **Reserved,
|
|
OUT EFI_FILE_PROTOCOL **Root,
|
|
OUT EFI_HANDLE *DeviceHandle
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetAllApfsRecoveryFilePath (
|
|
IN EFI_HANDLE Handle OPTIONAL,
|
|
OUT VOID **Volumes,
|
|
OUT UINTN *NumberOfEntries
|
|
);
|
|
|
|
///
|
|
/// The APPLE_BOOT_POLICY_PROTOCOL instance to get installed.
|
|
///
|
|
STATIC APPLE_BOOT_POLICY_PROTOCOL mAppleBootPolicyProtocol = {
|
|
APPLE_BOOT_POLICY_PROTOCOL_REVISION,
|
|
BootPolicyGetBootFile,
|
|
BootPolicyGetBootFileEx,
|
|
BootPolicyDevicePathToDirPath,
|
|
BootPolicyGetApfsRecoveryFilePath,
|
|
BootPolicyGetAllApfsRecoveryFilePath
|
|
};
|
|
|
|
/**
|
|
Checks whether the given file exists or not.
|
|
|
|
@param[in] Root The volume's opened root.
|
|
@param[in] FileName The path of the file to check.
|
|
|
|
@return Returned is whether the specified file exists or not.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalFileExists (
|
|
IN EFI_FILE_HANDLE Root,
|
|
IN CONST CHAR16 *FileName
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
|
|
Status = SafeFileOpen (
|
|
Root,
|
|
&FileHandle,
|
|
FileName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
FileHandle->Close (FileHandle);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetApfsSpecialFileInfo (
|
|
IN EFI_FILE_PROTOCOL *Root,
|
|
IN OUT APPLE_APFS_VOLUME_INFO **VolumeInfo OPTIONAL,
|
|
IN OUT APPLE_APFS_CONTAINER_INFO **ContainerInfo OPTIONAL
|
|
)
|
|
{
|
|
if (ContainerInfo == NULL && VolumeInfo == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (VolumeInfo != NULL) {
|
|
*VolumeInfo = GetFileInfo (
|
|
Root,
|
|
&gAppleApfsVolumeInfoGuid,
|
|
sizeof (**VolumeInfo),
|
|
NULL
|
|
);
|
|
|
|
if (*VolumeInfo == NULL) {
|
|
DEBUG ((DEBUG_VERBOSE, "OCBP: APFS Volume Info is missing\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: APFS Volume Info - %p (%u, %g, %u)\n",
|
|
*VolumeInfo,
|
|
(*VolumeInfo)->Always1,
|
|
&(*VolumeInfo)->Uuid,
|
|
(*VolumeInfo)->Role
|
|
));
|
|
}
|
|
|
|
if (ContainerInfo != NULL) {
|
|
*ContainerInfo = GetFileInfo (
|
|
Root,
|
|
&gAppleApfsContainerInfoGuid,
|
|
sizeof (**ContainerInfo),
|
|
NULL
|
|
);
|
|
|
|
if (*ContainerInfo == NULL) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: APFS Container Info is missing\n"));
|
|
if (VolumeInfo != NULL) {
|
|
FreePool (*VolumeInfo);
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: APFS Container Info - %p (%u, %g)\n",
|
|
*ContainerInfo,
|
|
(*ContainerInfo)->Always1,
|
|
&(*ContainerInfo)->Uuid
|
|
));
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBooterFromBlessedSystemFilePath (
|
|
IN EFI_FILE_PROTOCOL *Root,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
UINTN FilePathSize;
|
|
|
|
*FilePath = (EFI_DEVICE_PATH_PROTOCOL *) GetFileInfo (
|
|
Root,
|
|
&gAppleBlessedSystemFileInfoGuid,
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
&FilePathSize
|
|
);
|
|
|
|
if (*FilePath == NULL) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed file is missing\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DebugPrintHexDump (DEBUG_BULK_INFO, "OCBP: BlessedFileHEX", (UINT8 *) *FilePath, FilePathSize);
|
|
|
|
if (!IsDevicePathValid (*FilePath, FilePathSize)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed file is invalid\n"));
|
|
FreePool (*FilePath);
|
|
*FilePath = NULL;
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DebugPrintDevicePath (DEBUG_BULK_INFO, "OCBP: BlessedFileDP", *FilePath);
|
|
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed file is valid\n"));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBooterFromBlessedSystemFolderPath (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *Root,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePathWalker;
|
|
FILEPATH_DEVICE_PATH *FolderDevicePath;
|
|
UINTN DevicePathSize;
|
|
UINTN BooterDirPathSize;
|
|
UINTN BooterPathSize;
|
|
CHAR16 *BooterPath;
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) GetFileInfo (
|
|
Root,
|
|
&gAppleBlessedSystemFolderInfoGuid,
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
&DevicePathSize
|
|
);
|
|
|
|
if (DevicePath == NULL) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed folder is missing\n"));
|
|
return Status;
|
|
}
|
|
|
|
DebugPrintHexDump (DEBUG_BULK_INFO, "OCBP: BlessedFolderHEX", (UINT8 *) DevicePath, DevicePathSize);
|
|
|
|
if (!IsDevicePathValid (DevicePath, DevicePathSize)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed folder is invalid\n"));
|
|
FreePool (DevicePath);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DebugPrintDevicePath (DEBUG_BULK_INFO, "OCBP: BlessedFolderDP", DevicePath);
|
|
|
|
DevicePathWalker = DevicePath;
|
|
|
|
while (!IsDevicePathEnd (DevicePathWalker)) {
|
|
if ((DevicePathType (DevicePathWalker) == MEDIA_DEVICE_PATH)
|
|
&& (DevicePathSubType (DevicePathWalker) == MEDIA_FILEPATH_DP)) {
|
|
|
|
FolderDevicePath = (FILEPATH_DEVICE_PATH *) DevicePathWalker;
|
|
BooterDirPathSize = OcFileDevicePathNameSize (FolderDevicePath);
|
|
BooterPathSize = BooterDirPathSize + L_STR_SIZE (APPLE_BOOTER_ROOT_FILE_NAME) - sizeof (CHAR16);
|
|
BooterPath = AllocateZeroPool (BooterPathSize);
|
|
|
|
if (BooterPath != NULL) {
|
|
//
|
|
// FolderDevicePath->PathName may be unaligned, thus byte copying is needed.
|
|
//
|
|
CopyMem (BooterPath, FolderDevicePath->PathName, BooterDirPathSize);
|
|
StrCatS (BooterPath, BooterPathSize, APPLE_BOOTER_ROOT_FILE_NAME);
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
DevicePathWalker = NextDevicePathNode (DevicePathWalker);
|
|
}
|
|
|
|
FreePool (DevicePath);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed folder append failed - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalFileExists (Root, BooterPath);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Blessed folder %s is missing - %r\n", BooterPath, Status));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*FilePath = FileDevicePath (Device, BooterPath);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcGetBooterFromPredefinedPathList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *Root,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL,
|
|
IN CHAR16 *Prefix OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN FullPathSize;
|
|
CHAR16 *FullPath;
|
|
CONST CHAR16 *PathName;
|
|
EFI_STATUS Status;
|
|
|
|
for (Index = 0; Index < NumPredefinedPaths; ++Index) {
|
|
PathName = PredefinedPaths[Index];
|
|
|
|
//
|
|
// For relative paths (i.e. when Prefix is a volume GUID) we must
|
|
// not use leading slash. This is what AppleBootPolicy does.
|
|
//
|
|
if (Prefix != NULL) {
|
|
ASSERT (PathName[0] == L'\\');
|
|
}
|
|
Status = InternalFileExists (
|
|
Root,
|
|
Prefix != NULL ? &PathName[1] : &PathName[0]
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Predefined %s %s was found\n",
|
|
Prefix != NULL ? Prefix : L"<nil>",
|
|
PathName
|
|
));
|
|
if (DevicePath != NULL) {
|
|
//
|
|
// Append volume directory prefix if any.
|
|
//
|
|
if (Prefix != NULL) {
|
|
ASSERT (Prefix[0] != L'\\');
|
|
FullPathSize = StrSize (Prefix) + StrSize (PathName);
|
|
FullPath = AllocatePool (FullPathSize);
|
|
if (FullPath != NULL) {
|
|
UnicodeSPrint (FullPath, FullPathSize, L"\\%s%s", Prefix, PathName);
|
|
*DevicePath = FileDevicePath (Device, FullPath);
|
|
FreePool (FullPath);
|
|
} else {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
} else {
|
|
*DevicePath = FileDevicePath (Device, PathName);
|
|
}
|
|
}
|
|
ASSERT (DevicePath == NULL || *DevicePath != NULL);
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Predefined %s %s is missing - %r\n",
|
|
Prefix != NULL ? Prefix : L"<nil>",
|
|
PathName,
|
|
Status
|
|
));
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetApfsVolumeInfo (
|
|
IN EFI_HANDLE Device,
|
|
OUT EFI_GUID *ContainerGuid,
|
|
OUT EFI_GUID *VolumeGuid,
|
|
OUT APPLE_APFS_VOLUME_ROLE *VolumeRole
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_FILE_PROTOCOL *Root;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
APPLE_APFS_CONTAINER_INFO *ApfsContainerInfo;
|
|
APPLE_APFS_VOLUME_INFO *ApfsVolumeInfo;
|
|
|
|
Root = NULL;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Device,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetApfsSpecialFileInfo (Root, &ApfsVolumeInfo, &ApfsContainerInfo);
|
|
|
|
Root->Close (Root);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
CopyGuid (
|
|
VolumeGuid,
|
|
&ApfsVolumeInfo->Uuid
|
|
);
|
|
|
|
*VolumeRole = ApfsVolumeInfo->Role;
|
|
|
|
CopyGuid (
|
|
ContainerGuid,
|
|
&ApfsContainerInfo->Uuid
|
|
);
|
|
|
|
FreePool (ApfsVolumeInfo);
|
|
FreePool (ApfsContainerInfo);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBooterFromApfsVolumePredefinedPathList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *PrebootRoot,
|
|
IN CHAR16 *VolumeDirectoryName,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_PROTOCOL *VolumeDirectoryHandle;
|
|
EFI_FILE_INFO *VolumeDirectoryInfo;
|
|
|
|
Status = SafeFileOpen (
|
|
PrebootRoot,
|
|
&VolumeDirectoryHandle,
|
|
VolumeDirectoryName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Missing partition %s on preboot - %r\n", VolumeDirectoryName, Status));
|
|
return Status;
|
|
} else {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Found partition %s on preboot\n", VolumeDirectoryName));
|
|
}
|
|
|
|
VolumeDirectoryInfo = GetFileInfo (
|
|
VolumeDirectoryHandle,
|
|
&gEfiFileInfoGuid,
|
|
sizeof (*VolumeDirectoryInfo),
|
|
NULL
|
|
);
|
|
|
|
if (VolumeDirectoryInfo == NULL) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Missing volume file info %s - %r\n", VolumeDirectoryName, Status));
|
|
VolumeDirectoryHandle->Close (VolumeDirectoryHandle);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Want predefined list for APFS %u at %s\n",
|
|
VolumeDirectoryInfo->Attribute,
|
|
VolumeDirectoryName
|
|
));
|
|
|
|
if ((VolumeDirectoryInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
|
|
Status = OcGetBooterFromPredefinedPathList (
|
|
Device,
|
|
VolumeDirectoryHandle,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
DevicePath,
|
|
VolumeDirectoryName
|
|
);
|
|
}
|
|
|
|
FreePool (VolumeDirectoryInfo);
|
|
VolumeDirectoryHandle->Close (VolumeDirectoryHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
APFS boot happens from Preboot volume, which normally contains blessed file
|
|
or folder. In case blessed path location fails, we iterate every volume
|
|
within the container, and try to locate a predefined booter path on Preboot
|
|
volume (e.g. Preboot://{VolumeUuid}/System/Library/CoreServices/boot.efi).
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBooterFromApfsPredefinedPathList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *PrebootRoot,
|
|
IN CONST GUID *ContainerUuid,
|
|
IN CONST CHAR16 *VolumeUuid OPTIONAL,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL,
|
|
OUT EFI_HANDLE *VolumeHandle OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS TmpStatus;
|
|
|
|
UINTN NumberOfHandles;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN Index;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *HandleRoot;
|
|
APPLE_APFS_CONTAINER_INFO *ContainerInfo;
|
|
APPLE_APFS_VOLUME_INFO *VolumeInfo;
|
|
CHAR16 VolumeDirectoryName[GUID_STRING_LENGTH+1];
|
|
EFI_DEVICE_PATH_PROTOCOL *VolumeDevPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevPath;
|
|
BOOLEAN ContainerMatch;
|
|
|
|
ASSERT (VolumeUuid == NULL || (VolumeUuid != NULL && VolumeHandle != NULL && *VolumeHandle == NULL));
|
|
|
|
NumberOfHandles = 0;
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: %u filesystems for APFS - %r\n", (UINT32) NumberOfHandles, Status));
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
for (Index = 0; Index < NumberOfHandles; ++Index) {
|
|
TmpStatus = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR (TmpStatus)) {
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Borked APFS filesystem %u of %u - %r\n",
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
TmpStatus
|
|
));
|
|
continue;
|
|
}
|
|
|
|
TmpStatus = FileSystem->OpenVolume (FileSystem, &HandleRoot);
|
|
if (EFI_ERROR (TmpStatus)) {
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Borked APFS root volume %u of %u - %r\n",
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
TmpStatus
|
|
));
|
|
continue;
|
|
}
|
|
|
|
TmpStatus = InternalGetApfsSpecialFileInfo (HandleRoot, &VolumeInfo, &ContainerInfo);
|
|
|
|
HandleRoot->Close (HandleRoot);
|
|
|
|
if (EFI_ERROR (TmpStatus)) {
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
"OCBP: No APFS info %u of %u - %r\n",
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
TmpStatus
|
|
));
|
|
continue;
|
|
}
|
|
|
|
ContainerMatch = CompareGuid (&ContainerInfo->Uuid, ContainerUuid);
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: APFS match container %g vs %g for %u of %u - %d\n",
|
|
&ContainerInfo->Uuid,
|
|
ContainerUuid,
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
ContainerMatch
|
|
));
|
|
|
|
if (!ContainerMatch) {
|
|
FreePool (ContainerInfo);
|
|
FreePool (VolumeInfo);
|
|
continue;
|
|
}
|
|
|
|
UnicodeSPrint (
|
|
VolumeDirectoryName,
|
|
sizeof (VolumeDirectoryName),
|
|
L"%g",
|
|
&VolumeInfo->Uuid
|
|
);
|
|
|
|
FreePool (ContainerInfo);
|
|
FreePool (VolumeInfo);
|
|
|
|
if (VolumeUuid != NULL && StrStr (VolumeUuid, VolumeDirectoryName) != NULL) {
|
|
*VolumeHandle = HandleBuffer[Index];
|
|
}
|
|
|
|
TmpStatus = InternalGetBooterFromApfsVolumePredefinedPathList (
|
|
Device,
|
|
PrebootRoot,
|
|
VolumeDirectoryName,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
DevicePath != NULL ? &VolumeDevPath : NULL
|
|
);
|
|
|
|
if (EFI_ERROR (TmpStatus)) {
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: No APFS booter %u of %u for %s - %r\n",
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
VolumeDirectoryName,
|
|
TmpStatus
|
|
));
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: Found APFS booter %u of %u for %s (%p)\n",
|
|
(UINT32) (Index + 1),
|
|
(UINT32) NumberOfHandles,
|
|
VolumeDirectoryName,
|
|
DevicePath
|
|
));
|
|
|
|
if (DevicePath != NULL) {
|
|
TempDevPath = *DevicePath;
|
|
*DevicePath = OcAppendDevicePathInstanceDedupe (
|
|
TempDevPath,
|
|
VolumeDevPath
|
|
);
|
|
if (TempDevPath != NULL) {
|
|
FreePool (TempDevPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: APFS bless for %g:%s is %r\n",
|
|
ContainerUuid,
|
|
VolumeUuid,
|
|
Status
|
|
));
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
HasValidGuidStringPrefix (
|
|
IN CONST CHAR16 *String
|
|
)
|
|
{
|
|
UINTN Length;
|
|
UINTN Index;
|
|
UINTN GuidLength = GUID_STRING_LENGTH;
|
|
|
|
Length = StrLen (String);
|
|
if (Length < GuidLength) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (Index = 0; Index < GuidLength; ++Index) {
|
|
if (Index == 8 || Index == 13 || Index == 18 || Index == 23) {
|
|
if (String[Index] != '-') {
|
|
return FALSE;
|
|
}
|
|
} else if (!(String[Index] >= L'0' && String[Index] <= L'9')
|
|
&& !(String[Index] >= L'A' && String[Index] <= L'F')
|
|
&& !(String[Index] >= L'a' && String[Index] <= L'f')) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBootPathName (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName
|
|
)
|
|
{
|
|
UINTN Size;
|
|
CHAR16 *PathName;
|
|
UINTN PathNameSize;
|
|
FILEPATH_DEVICE_PATH *FilePath;
|
|
CHAR16 *Slash;
|
|
UINTN Len;
|
|
CHAR16 *FilePathName;
|
|
|
|
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH)
|
|
&& (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP)) {
|
|
FilePath = (FILEPATH_DEVICE_PATH *) DevicePath;
|
|
|
|
Size = OcFileDevicePathNameSize (FilePath);
|
|
|
|
PathNameSize = Size + sizeof (CHAR16);
|
|
PathName = AllocateZeroPool (PathNameSize);
|
|
|
|
if (PathName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (PathName, FilePath->PathName, Size);
|
|
|
|
Slash = StrStr (PathName, L"\\");
|
|
|
|
if (Slash != NULL) {
|
|
Len = StrLen (PathName);
|
|
|
|
FilePathName = &PathName[Len - 1];
|
|
|
|
while (*FilePathName != L'\\') {
|
|
*FilePathName = L'\0';
|
|
--FilePathName;
|
|
}
|
|
} else {
|
|
StrCpyS (PathName, PathNameSize, L"\\");
|
|
}
|
|
} else {
|
|
PathName = AllocateZeroPool (sizeof (L"\\"));
|
|
|
|
if (PathName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
StrCpyS (PathName, sizeof (L"\\"), L"\\");
|
|
}
|
|
|
|
*BootPathName = PathName;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetApfsVolumeHandle (
|
|
IN EFI_HANDLE DeviceHandle,
|
|
IN CHAR16 *PathName,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
APPLE_APFS_CONTAINER_INFO *ContainerInfo;
|
|
CHAR16 *FilePathName;
|
|
|
|
FilePathName = &PathName[0];
|
|
|
|
if (PathName[0] == L'\\') {
|
|
FilePathName = &PathName[1];
|
|
}
|
|
|
|
if (!HasValidGuidStringPrefix (FilePathName)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
DeviceHandle,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetApfsSpecialFileInfo (Root, NULL, &ContainerInfo);
|
|
if (!EFI_ERROR(Status)) {
|
|
//
|
|
// FIXME: ApfsVolumeHandle is only returned when a predefined path exists
|
|
//
|
|
Status = InternalGetBooterFromApfsPredefinedPathList (
|
|
DeviceHandle,
|
|
Root,
|
|
&ContainerInfo->Uuid,
|
|
FilePathName,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
NULL,
|
|
ApfsVolumeHandle
|
|
);
|
|
|
|
FreePool (ContainerInfo);
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcBootPolicyGetBootFile (
|
|
IN EFI_HANDLE Device,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
|
|
ASSERT (Device != NULL);
|
|
ASSERT (FilePath != NULL);
|
|
|
|
*FilePath = NULL;
|
|
Root = NULL;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Device,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetBooterFromBlessedSystemFilePath (Root, FilePath);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = InternalGetBooterFromBlessedSystemFolderPath (Device, Root, FilePath);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = OcGetBooterFromPredefinedPathList (
|
|
Device,
|
|
Root,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
FilePath,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFile (
|
|
IN EFI_HANDLE Device,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
if (Device == NULL || FilePath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return OcBootPolicyGetBootFile (
|
|
Device,
|
|
gAppleBootPolicyPredefinedPaths,
|
|
ARRAY_SIZE (gAppleBootPolicyPredefinedPaths),
|
|
FilePath
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcBootPolicyGetBootFileEx (
|
|
IN EFI_HANDLE Device,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS TmpStatus;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
APPLE_APFS_CONTAINER_INFO *ContainerInfo;
|
|
APPLE_APFS_VOLUME_INFO *VolumeInfo;
|
|
|
|
ASSERT (Device != NULL);
|
|
ASSERT (FilePath != NULL);
|
|
|
|
*FilePath = NULL;
|
|
Root = NULL;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Device,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Missing filesystem - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: Invalid root volume - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetApfsSpecialFileInfo (Root, &VolumeInfo, &ContainerInfo);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = EFI_NOT_FOUND;
|
|
if ((VolumeInfo->Role & APPLE_APFS_VOLUME_ROLE_PREBOOT) != 0) {
|
|
TmpStatus = InternalGetBooterFromBlessedSystemFilePath (Root, FilePath);
|
|
if (EFI_ERROR (TmpStatus)) {
|
|
TmpStatus = InternalGetBooterFromBlessedSystemFolderPath (Device, Root, FilePath);
|
|
}
|
|
|
|
//
|
|
// Blessed entry is always first, and subsequent entries are added with deduplication.
|
|
//
|
|
Status = InternalGetBooterFromApfsPredefinedPathList (
|
|
Device,
|
|
Root,
|
|
&ContainerInfo->Uuid,
|
|
NULL,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
FilePath,
|
|
NULL
|
|
);
|
|
if (!EFI_ERROR (TmpStatus)) {
|
|
Status = TmpStatus;
|
|
}
|
|
}
|
|
|
|
FreePool (VolumeInfo);
|
|
FreePool (ContainerInfo);
|
|
} else {
|
|
Status = InternalGetBooterFromBlessedSystemFilePath (Root, FilePath);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = InternalGetBooterFromBlessedSystemFolderPath (Device, Root, FilePath);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = OcGetBooterFromPredefinedPathList (
|
|
Device,
|
|
Root,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
FilePath,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFileEx (
|
|
IN EFI_HANDLE Device,
|
|
IN BOOT_POLICY_ACTION Action,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
if (Device == NULL || FilePath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return OcBootPolicyGetBootFileEx (
|
|
Device,
|
|
gAppleBootPolicyPredefinedPaths,
|
|
ARRAY_SIZE (gAppleBootPolicyPredefinedPaths),
|
|
FilePath
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcBootPolicyDevicePathToDirPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
ASSERT (BootPathName != NULL);
|
|
ASSERT (Device != NULL);
|
|
|
|
*BootPathName = NULL;
|
|
*Device = NULL;
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
&DevicePath,
|
|
Device
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetBootPathName (DevicePath, BootPathName);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
OcBootPolicyDevicePathToDirPathAndApfsHandle (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
ASSERT (BootPathName != NULL);
|
|
ASSERT (Device != NULL);
|
|
ASSERT (ApfsVolumeHandle != NULL);
|
|
|
|
Status = OcBootPolicyDevicePathToDirPath (
|
|
DevicePath,
|
|
BootPathName,
|
|
Device
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// InternalGetApfsVolumeHandle status code is ignored, as ApfsVolumeHandle
|
|
// may not exist.
|
|
//
|
|
*ApfsVolumeHandle = NULL;
|
|
(VOID) InternalGetApfsVolumeHandle (
|
|
*Device,
|
|
*BootPathName,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
ApfsVolumeHandle
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyDevicePathToDirPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
if (DevicePath == NULL
|
|
|| BootPathName == NULL
|
|
|| Device == NULL
|
|
|| ApfsVolumeHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = OcBootPolicyDevicePathToDirPathAndApfsHandle (
|
|
DevicePath,
|
|
gAppleBootPolicyPredefinedPaths,
|
|
ARRAY_SIZE (gAppleBootPolicyPredefinedPaths),
|
|
BootPathName,
|
|
Device,
|
|
ApfsVolumeHandle
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
*BootPathName = NULL;
|
|
*Device = NULL;
|
|
*ApfsVolumeHandle = NULL;
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcBootPolicyGetApfsRecoveryFilePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 *PathName,
|
|
IN CONST CHAR16 **PredefinedPaths,
|
|
IN UINTN NumPredefinedPaths,
|
|
OUT CHAR16 **FullPathName,
|
|
OUT EFI_FILE_PROTOCOL **Root,
|
|
OUT EFI_HANDLE *DeviceHandle
|
|
)
|
|
{
|
|
EFI_STATUS Result;
|
|
|
|
EFI_STATUS Status;
|
|
|
|
CHAR16 *BootPathName;
|
|
EFI_HANDLE Device;
|
|
EFI_HANDLE VolumeHandle;
|
|
|
|
GUID ContainerGuid;
|
|
GUID VolumeGuid;
|
|
APPLE_APFS_VOLUME_ROLE VolumeRole;
|
|
|
|
GUID ContainerGuid2;
|
|
GUID VolumeGuid2;
|
|
APPLE_APFS_VOLUME_ROLE VolumeRole2;
|
|
|
|
UINTN NoHandles;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN Index;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
|
|
CHAR16 *FullPathBuffer;
|
|
UINTN FullPathNameSize;
|
|
|
|
EFI_FILE_PROTOCOL *NewHandle;
|
|
|
|
EFI_FILE_INFO *FileInfo;
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
ASSERT (PathName != NULL);
|
|
ASSERT (FullPathName != NULL);
|
|
ASSERT (Root != NULL);
|
|
ASSERT (DeviceHandle != NULL);
|
|
|
|
NewHandle = NULL;
|
|
*Root = NULL;
|
|
*FullPathName = NULL;
|
|
|
|
Status = OcBootPolicyDevicePathToDirPathAndApfsHandle (
|
|
DevicePath,
|
|
PredefinedPaths,
|
|
NumPredefinedPaths,
|
|
&BootPathName,
|
|
&Device,
|
|
&VolumeHandle
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: APFS recovery boot info failed - %r\n", Status));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (VolumeHandle == NULL) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: APFS recovery volume handle missing - %s\n", BootPathName));
|
|
FreePool (BootPathName);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
FreePool (BootPathName);
|
|
|
|
Status = InternalGetApfsVolumeInfo (
|
|
VolumeHandle,
|
|
&ContainerGuid,
|
|
&VolumeGuid,
|
|
&VolumeRole
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: APFS recovery volume info missing\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NoHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
DEBUG ((DEBUG_BULK_INFO, "OCBP: APFS recovery simple fs missing - %r\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
Result = EFI_NOT_FOUND;
|
|
|
|
for (Index = 0; Index < NoHandles; ++Index) {
|
|
ZeroMem (&ContainerGuid2, sizeof (ContainerGuid2));
|
|
ZeroMem (&VolumeGuid2, sizeof (VolumeGuid2));
|
|
VolumeRole2 = 0;
|
|
|
|
Status = InternalGetApfsVolumeInfo (
|
|
HandleBuffer[Index],
|
|
&ContainerGuid2,
|
|
&VolumeGuid2,
|
|
&VolumeRole2
|
|
);
|
|
|
|
DEBUG ((
|
|
DEBUG_BULK_INFO,
|
|
"OCBP: APFS recovery info %u/%u due to %g/%g/%X - %r\n",
|
|
(UINT32) Index,
|
|
(UINT32) NoHandles,
|
|
&ContainerGuid2,
|
|
&ContainerGuid,
|
|
(UINT32) VolumeRole2,
|
|
Status
|
|
));
|
|
|
|
if (EFI_ERROR(Status)
|
|
|| VolumeRole2 != APPLE_APFS_VOLUME_ROLE_RECOVERY
|
|
|| !CompareGuid (&ContainerGuid2, &ContainerGuid)) {
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, Root);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
FullPathNameSize = sizeof (CHAR16) + StrSize (PathName);
|
|
FullPathNameSize += GUID_STRING_LENGTH * sizeof (CHAR16);
|
|
FullPathBuffer = AllocateZeroPool (FullPathNameSize);
|
|
|
|
if (FullPathBuffer == NULL) {
|
|
(*Root)->Close (*Root);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Note, this \\ prefixing does not exist in Apple code, and should not be required,
|
|
// but we add it for return path consistency.
|
|
//
|
|
UnicodeSPrint (
|
|
FullPathBuffer,
|
|
FullPathNameSize,
|
|
L"\\%g%s",
|
|
&VolumeGuid,
|
|
PathName
|
|
);
|
|
|
|
Status = SafeFileOpen (
|
|
*Root,
|
|
&NewHandle,
|
|
FullPathBuffer,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
(*Root)->Close (*Root);
|
|
FreePool (FullPathBuffer);
|
|
continue;
|
|
}
|
|
|
|
FileInfo = GetFileInfo (
|
|
NewHandle,
|
|
&gEfiFileInfoGuid,
|
|
sizeof (*FileInfo),
|
|
NULL
|
|
);
|
|
|
|
if (FileInfo != NULL) {
|
|
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
|
|
*FullPathName = FullPathBuffer;
|
|
*DeviceHandle = HandleBuffer[Index];
|
|
Result = EFI_SUCCESS;
|
|
}
|
|
|
|
FreePool (FileInfo);
|
|
}
|
|
|
|
NewHandle->Close (NewHandle);
|
|
|
|
if (!EFI_ERROR (Result)) {
|
|
break;
|
|
}
|
|
|
|
(*Root)->Close (*Root);
|
|
FreePool (FullPathBuffer);
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
|
|
return Result;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetApfsRecoveryFilePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 *PathName,
|
|
OUT CHAR16 **FullPathName,
|
|
OUT VOID **Reserved,
|
|
OUT EFI_FILE_PROTOCOL **Root,
|
|
OUT EFI_HANDLE *DeviceHandle
|
|
)
|
|
{
|
|
if (DevicePath == NULL
|
|
|| PathName == NULL
|
|
|| FullPathName == NULL
|
|
|| Reserved == NULL
|
|
|| Root == NULL
|
|
|| DeviceHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Reserved = NULL;
|
|
|
|
return OcBootPolicyGetApfsRecoveryFilePath (
|
|
DevicePath,
|
|
PathName,
|
|
gAppleBootPolicyPredefinedPaths,
|
|
ARRAY_SIZE (gAppleBootPolicyPredefinedPaths),
|
|
FullPathName,
|
|
Root,
|
|
DeviceHandle
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcBootPolicyGetAllApfsRecoveryFilePath (
|
|
IN EFI_HANDLE Handle OPTIONAL,
|
|
OUT VOID **Volumes,
|
|
OUT UINTN *NumberOfEntries
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
UINTN NumberOfHandles;
|
|
EFI_HANDLE *HandleBuffer;
|
|
APFS_VOLUME_INFO *VolumeInfo;
|
|
GUID *ContainerGuids;
|
|
UINTN NumberOfContainers;
|
|
UINTN NumberOfVolumeInfos;
|
|
UINTN Index;
|
|
UINTN Index2;
|
|
UINTN Index3;
|
|
APFS_VOLUME_ROOT **ApfsVolumes;
|
|
BOOLEAN GuidPresent;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
EFI_FILE_PROTOCOL *NewHandle;
|
|
CHAR16 VolumePathName[GUID_STRING_LENGTH + 1];
|
|
EFI_FILE_INFO *FileInfo;
|
|
APFS_VOLUME_ROOT *ApfsRoot;
|
|
|
|
ASSERT (Volumes != NULL);
|
|
ASSERT (NumberOfEntries != NULL);
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
VolumeInfo = AllocateZeroPool (NumberOfHandles * sizeof (*VolumeInfo));
|
|
ContainerGuids = AllocateZeroPool (NumberOfHandles * sizeof (*ContainerGuids));
|
|
|
|
if (VolumeInfo == NULL || ContainerGuids == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (HandleBuffer);
|
|
|
|
if (VolumeInfo != NULL) {
|
|
FreePool (VolumeInfo);
|
|
}
|
|
|
|
if (ContainerGuids != NULL) {
|
|
FreePool (ContainerGuids);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NumberOfVolumeInfos = 0;
|
|
NumberOfContainers = 0;
|
|
|
|
for (Index = 0; Index < NumberOfHandles; ++Index) {
|
|
Status = InternalGetApfsVolumeInfo (
|
|
HandleBuffer[Index],
|
|
&VolumeInfo[NumberOfVolumeInfos].ContainerGuid,
|
|
&VolumeInfo[NumberOfVolumeInfos].VolumeGuid,
|
|
&VolumeInfo[NumberOfVolumeInfos].VolumeRole
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
VolumeInfo[NumberOfVolumeInfos].Handle = HandleBuffer[Index];
|
|
|
|
GuidPresent = FALSE;
|
|
for (Index2 = 0; Index2 < NumberOfContainers; ++Index2) {
|
|
if (CompareGuid (&ContainerGuids[Index2], &VolumeInfo[NumberOfVolumeInfos].ContainerGuid)) {
|
|
GuidPresent = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!GuidPresent) {
|
|
CopyGuid (
|
|
&ContainerGuids[NumberOfContainers],
|
|
&VolumeInfo[NumberOfVolumeInfos].ContainerGuid
|
|
);
|
|
|
|
if (Index2 != 0 && HandleBuffer[Index] == Handle) {
|
|
CopyMem (
|
|
&ContainerGuids[1],
|
|
&ContainerGuids[0],
|
|
NumberOfContainers * sizeof (ContainerGuids[0])
|
|
);
|
|
CopyGuid (
|
|
&ContainerGuids[0],
|
|
&VolumeInfo[NumberOfVolumeInfos].ContainerGuid
|
|
);
|
|
}
|
|
|
|
++NumberOfContainers;
|
|
}
|
|
|
|
++NumberOfVolumeInfos;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (NumberOfVolumeInfos > 0) {
|
|
ApfsVolumes = AllocateZeroPool (
|
|
NumberOfVolumeInfos * sizeof (*ApfsVolumes)
|
|
);
|
|
if (ApfsVolumes == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
} else {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
FreePool (HandleBuffer);
|
|
FreePool (VolumeInfo);
|
|
FreePool (ContainerGuids);
|
|
return Status;
|
|
}
|
|
|
|
*Volumes = ApfsVolumes;
|
|
*NumberOfEntries = 0;
|
|
|
|
for (Index = 0; Index < NumberOfContainers; ++Index) {
|
|
for (Index2 = 0; Index2 < NumberOfVolumeInfos; ++Index2) {
|
|
if ((VolumeInfo[Index2].VolumeRole & APPLE_APFS_VOLUME_ROLE_RECOVERY) == 0
|
|
|| !CompareGuid (&ContainerGuids[Index], &VolumeInfo[Index2].ContainerGuid)) {
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
VolumeInfo[Index2].Handle,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Locate recovery for every volume present.
|
|
//
|
|
for (Index3 = 0; Index3 < NumberOfVolumeInfos; ++Index3) {
|
|
if ((VolumeInfo[Index2].VolumeRole &
|
|
(APPLE_APFS_VOLUME_ROLE_RECOVERY|APPLE_APFS_VOLUME_ROLE_PREBOOT)) != 0
|
|
|| !CompareGuid (&ContainerGuids[Index], &VolumeInfo[Index3].ContainerGuid)) {
|
|
continue;
|
|
}
|
|
|
|
UnicodeSPrint (
|
|
VolumePathName,
|
|
sizeof (VolumePathName),
|
|
L"%g",
|
|
&VolumeInfo[Index3].VolumeGuid
|
|
);
|
|
|
|
Status = SafeFileOpen (
|
|
Root,
|
|
&NewHandle,
|
|
VolumePathName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
|
|
FileInfo = GetFileInfo (
|
|
NewHandle,
|
|
&gEfiFileInfoGuid,
|
|
sizeof (*FileInfo),
|
|
NULL
|
|
);
|
|
|
|
NewHandle->Close (NewHandle);
|
|
|
|
if (FileInfo != NULL) {
|
|
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
|
|
ApfsRoot = ApfsVolumes[*NumberOfEntries];
|
|
ApfsRoot->Handle = VolumeInfo[Index2].Handle;
|
|
ApfsRoot->VolumeDirName = AllocateCopyPool (
|
|
StrSize (VolumePathName),
|
|
VolumePathName
|
|
);
|
|
ApfsRoot->Root = Root;
|
|
|
|
++(*NumberOfEntries);
|
|
}
|
|
FreePool (FileInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (VolumeInfo);
|
|
FreePool (ContainerGuids);
|
|
FreePool (HandleBuffer);
|
|
|
|
if (!EFI_ERROR(Status) && *NumberOfEntries == 0) {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetAllApfsRecoveryFilePath (
|
|
IN EFI_HANDLE Handle OPTIONAL,
|
|
OUT VOID **Volumes,
|
|
OUT UINTN *NumberOfEntries
|
|
)
|
|
{
|
|
if (Volumes == NULL || NumberOfEntries == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
return OcBootPolicyGetAllApfsRecoveryFilePath (
|
|
Handle,
|
|
Volumes,
|
|
NumberOfEntries
|
|
);
|
|
}
|
|
|
|
APPLE_BOOT_POLICY_PROTOCOL *
|
|
OcAppleBootPolicyInstallProtocol (
|
|
IN BOOLEAN Reinstall
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
APPLE_BOOT_POLICY_PROTOCOL *Protocol;
|
|
EFI_HANDLE Handle;
|
|
|
|
if (Reinstall) {
|
|
Status = OcUninstallAllProtocolInstances (&gAppleBootPolicyProtocolGuid);
|
|
if (EFI_ERROR(Status)) {
|
|
// DEBUG ((DEBUG_ERROR, "OCBP: Uninstall failed: %r\n", Status));
|
|
return NULL;
|
|
}
|
|
} else {
|
|
Status = gBS->LocateProtocol (
|
|
&gAppleBootPolicyProtocolGuid,
|
|
NULL,
|
|
(VOID *) &Protocol
|
|
);
|
|
|
|
if (!EFI_ERROR(Status)) {
|
|
return Protocol;
|
|
}
|
|
}
|
|
|
|
Handle = NULL;
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&Handle,
|
|
&gAppleBootPolicyProtocolGuid,
|
|
(VOID **) &mAppleBootPolicyProtocol,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
return &mAppleBootPolicyProtocol;
|
|
}
|