2018-10-15 04:33:47 +02:00

524 lines
15 KiB
C

#include <Uefi.h>
#include <Guid/AppleApfsInfo.h>
#include <Guid/AppleBless.h>
#include <Guid/FileInfo.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/CupertinoBlessLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/MiscFileLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
///
/// An array of file paths to search for in case no file is blessed.
///
STATIC CONST CHAR16 *mBootPathNames[] = {
APPLE_BOOTER_DEFAULT_FILE_NAME,
APPLE_REMOVABLE_MEDIA_FILE_NAME,
APPLE_BOOTER_ROOT_FILE_NAME
};
STATIC
CONST CHAR16 *
InternalGetBlessedSystemFolderPathName (
IN EFI_FILE_PROTOCOL *Root
)
{
CONST CHAR16 *PathName;
VOID *DevicePath;
CONST VOID *DevicePathWalker;
CONST FILEPATH_DEVICE_PATH *FilePath;
PathName = NULL;
DevicePath = CupertinoGetBlessedSystemFolder (Root);
if (DevicePath != NULL) {
DevicePathWalker = DevicePath;
while (!IsDevicePathEnd (DevicePathWalker)) {
if ((DevicePathType (DevicePathWalker) == MEDIA_DEVICE_PATH)
&& (DevicePathSubType (DevicePathWalker) == MEDIA_FILEPATH_DP)) {
FilePath = (CONST FILEPATH_DEVICE_PATH *)DevicePathWalker;
PathName = AllocateCopyPool (
StrSize (&FilePath->PathName[0]),
&FilePath->PathName[0]
);
break;
}
DevicePathWalker = NextDevicePathNode (DevicePathWalker);
}
FreePool (DevicePath);
}
return PathName;
}
STATIC
CONST CHAR16 *
InternalGetProbedBootPathName (
IN EFI_FILE_PROTOCOL *Root
)
{
CONST CHAR16 *BootPathName;
UINTN Index;
CONST CHAR16 *PathName;
BootPathName = NULL;
for (Index = 0; Index < ARRAY_SIZE (mBootPathNames); ++Index) {
PathName = mBootPathNames[Index];
if (FileExists (Root, PathName)) {
BootPathName = PathName;
break;
}
}
return BootPathName;
}
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;
CONST APPLE_APFS_CONTAINER_INFO *ApfsContainerInfo;
CONST APPLE_APFS_VOLUME_INFO *ApfsVolumeInfo;
Root = NULL;
Status = gBS->HandleProtocol (
Device,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&FileSystem
);
if (!EFI_ERROR (Status)) {
Status = FileSystem->OpenVolume (FileSystem, &Root);
if (!EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
ApfsContainerInfo = MiscGetFileInformation (
Root,
&gAppleApfsContainerInfoGuid
);
if (ApfsContainerInfo != NULL) {
CopyGuid (
ContainerGuid,
&ApfsContainerInfo->Uuid
);
FreePool ((VOID *)ApfsContainerInfo);
Status = EFI_SUCCESS;
}
ApfsVolumeInfo = MiscGetFileInformation (Root, &gAppleApfsVolumeInfoGuid);
if (ApfsVolumeInfo != NULL) {
CopyGuid (
VolumeGuid,
&ApfsVolumeInfo->Uuid
);
*VolumeRole = ApfsVolumeInfo->Role;
FreePool ((VOID *)ApfsVolumeInfo);
}
Root->Close (Root);
}
}
return Status;
}
STATIC
EFI_STATUS
InternalGetApfsBootFile (
IN EFI_HANDLE Device,
IN EFI_FILE_PROTOCOL *Root,
IN CONST GUID *ContainerUuid,
IN CONST CHAR16 *VolumeUuid,
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
OUT EFI_HANDLE *VolumeHandle
)
{
EFI_STATUS Status;
UINTN NumberOfHandles;
EFI_HANDLE *HandleBuffer;
UINTN Index;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
EFI_FILE_PROTOCOL *HandleRoot;
CONST APPLE_APFS_CONTAINER_INFO *ContainerInfo;
BOOLEAN Equal;
CONST APPLE_APFS_VOLUME_INFO *VolumeInfo;
CHAR16 DirPathNameBuffer[38];
EFI_FILE_PROTOCOL *NewHandle;
CONST EFI_FILE_INFO *VolumeDirectoryInfo;
CONST EFI_DEVICE_PATH_PROTOCOL *FilePath;
CONST CHAR16 *FilePathName;
UINTN GuidPathNameSize;
BOOLEAN DirectoryExists;
UINTN BootFileNameSize;
CHAR16 *FullPathName;
UINTN DevPathSize;
EFI_DEVICE_PATH_PROTOCOL *DevPath;
EFI_DEVICE_PATH_PROTOCOL *DevPathWalker;
INTN Result;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumberOfHandles,
&HandleBuffer
);
if (!EFI_ERROR (Status)) {
Status = EFI_NOT_FOUND;
for (Index = 0; Index < NumberOfHandles; ++Index) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&FileSystem
);
if (!EFI_ERROR (Status)) {
Status = FileSystem->OpenVolume (FileSystem, &HandleRoot);
if (!EFI_ERROR (Status)) {
ContainerInfo = MiscGetFileInformation (
HandleRoot,
&gAppleApfsContainerInfoGuid
);
if (ContainerInfo != NULL) {
Equal = CompareGuid (&ContainerInfo->Uuid, ContainerUuid);
if (Equal) {
VolumeInfo = MiscGetFileInformation (
HandleRoot,
&gAppleApfsVolumeInfoGuid
);
if (VolumeInfo != NULL) {
UnicodeSPrint (
&DirPathNameBuffer[0],
sizeof (DirPathNameBuffer),
L"%g",
&VolumeInfo->Uuid
);
if ((VolumeUuid != NULL)
&& StrStr (VolumeUuid, &DirPathNameBuffer[0]) != NULL) {
*VolumeHandle = HandleBuffer[Index];
}
// BUG: NewHandle is not closed.
Status = Root->Open (
Root,
&NewHandle,
&DirPathNameBuffer[0],
EFI_FILE_MODE_READ,
0
);
if (!EFI_ERROR (Status)) {
VolumeDirectoryInfo = MiscGetFileInformation (
NewHandle,
&gEfiFileInfoGuid
);
if (VolumeDirectoryInfo != NULL) {
if ((VolumeDirectoryInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
Status = EFI_NOT_FOUND;
for (Index = 0; Index < ARRAY_SIZE (mBootPathNames); ++Index) {
FilePathName = mBootPathNames[Index];
DirectoryExists = FileExists (
NewHandle,
(FilePathName[0] == L'\\')
? &FilePathName[1]
: &FilePathName[0]
);
if (DirectoryExists) {
GuidPathNameSize = (StrSize (&DirPathNameBuffer[0]) + 1);
BootFileNameSize = StrSize (FilePathName);
FullPathName = AllocateZeroPool (
GuidPathNameSize + BootFileNameSize
);
if (FullPathName != NULL) {
if (DirPathNameBuffer[0] != L'\\') {
StrCpy (FullPathName, L"\\");
}
StrCat (FullPathName, &DirPathNameBuffer[0]);
StrCat (FullPathName, FilePathName);
FilePath = FileDevicePath (Device, FullPathName);
FreePool ((VOID *)FullPathName);
if (FilePath != NULL) {
Status = EFI_SUCCESS;
DevPathSize = GetDevicePathSize (FilePath);
DevPath = *DevicePath;
do {
DevPathWalker = GetNextDevicePathInstance (
&DevPath,
&DevPathSize
);
if (DevPathWalker == NULL) {
*DevicePath = AppendDevicePath (
*DevicePath,
FilePath
);
break;
}
if ((DevPathSize == 0)
|| (DevPathWalker == FilePath)) {
FreePool ((VOID *)DevPathWalker);
break;
}
Result = CompareMem (
(VOID *)DevPathWalker,
(VOID *)FilePath,
DevPathSize
);
FreePool ((VOID *)DevPathWalker);
} while (Result != 0);
}
}
break;
}
}
}
FreePool ((VOID *)VolumeDirectoryInfo);
}
}
}
if (VolumeInfo != NULL) {
FreePool ((VOID *)VolumeInfo);
}
}
FreePool ((VOID *)ContainerInfo);
}
HandleRoot->Close (HandleRoot);
}
}
}
FreePool (HandleBuffer);
}
return Status;
}
STATIC
EFI_STATUS
EFIAPI
InternalGetBootFileWorker (
IN EFI_HANDLE Device,
IN EFI_FILE_PROTOCOL *Root,
IN OUT CONST FILEPATH_DEVICE_PATH **FilePath
)
{
EFI_STATUS Status;
CONST FILEPATH_DEVICE_PATH *DevicePath;
CONST CHAR16 *PathName;
BOOLEAN FreePathName;
Status = EFI_NOT_FOUND;
DevicePath = CupertinoGetBlessedSystemFile (Root);
if (DevicePath == NULL) {
PathName = InternalGetBlessedSystemFolderPathName (Root);
FreePathName = FALSE;
if (PathName != NULL) {
FreePathName = TRUE;
} else {
PathName = InternalGetProbedBootPathName (Root);
}
if (PathName != NULL) {
DevicePath = (CONST FILEPATH_DEVICE_PATH *)(
FileDevicePath (Device, PathName)
);
if (FreePathName) {
FreePool ((VOID *)PathName);
}
}
}
*FilePath = DevicePath;
return Status;
}
/**
Locates the bootable file of the given volume. Prefered are the values
blessed, though if unavailable, hard-coded names are being verified and used
if existing.
The blessed paths are to be determined by the HFS Driver via
EFI_FILE_PROTOCOL.GetInfo(). The related identifier definitions are to be
found in AppleBless.h.
@param[in] Device The Device's Handle to perform the search on.
@param[out] FilePath A pointer to the device path pointer to set to the file
path of the boot file.
@return The status of the operation is returned.
@retval EFI_NOT_FOUND A bootable file could not be found on the given
volume.
@retval EFI_OUT_OF_RESOURCES The memory necessary to complete the operation
could not be allocated.
@retval EFI_SUCCESS The operation completed successfully and the
PathName Buffer has been filled.
@retval other The status of an operation used to complete
this operation is returned.
**/
EFI_STATUS
EFIAPI
BootPolicyGetBootFile (
IN EFI_HANDLE Device,
IN OUT CONST EFI_DEVICE_PATH_PROTOCOL **FilePath
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
EFI_FILE_PROTOCOL *Root;
Status = gBS->HandleProtocol (
Device,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&FileSystem
);
if (!EFI_ERROR (Status)) {
Status = FileSystem->OpenVolume (FileSystem, &Root);
if (!EFI_ERROR (Status)) {
Status = InternalGetBootFileWorker (Device, Root, FilePath);
Root->Close (Root);
}
}
return Status;
}
EFI_STATUS
EFIAPI
BootPolicyGetBootFileEx (
IN EFI_HANDLE Device,
IN UINT32 Unused, OPTIONAL
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
EFI_FILE_PROTOCOL *Root;
APPLE_APFS_CONTAINER_INFO *ContainerInfo;
APPLE_APFS_VOLUME_INFO *VolumeInfo;
EFI_STATUS Status2;
*FilePath = NULL;
Status = gBS->HandleProtocol (
Device,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&FileSystem
);
if (!EFI_ERROR (Status)) {
Status = FileSystem->OpenVolume (FileSystem, &Root);
if (!EFI_ERROR (Status)) {
VolumeInfo = MiscGetFileInformation (Root, &gAppleApfsVolumeInfoGuid);
if (VolumeInfo != NULL) {
Status = EFI_NOT_FOUND;
if ((VolumeInfo->Role & APPLE_APFS_VOLUME_ROLE_PREBOOT) != 0) {
Status = InternalGetBootFileWorker (Device, Root, FilePath);
if (EFI_ERROR (Status)) {
ContainerInfo = MiscGetFileInformation (Root, &gAppleApfsContainerInfoGuid);
if (ContainerInfo != NULL) {
Status = InternalGetApfsBootFile (
Device,
Root,
&ContainerInfo->Uuid,
NULL,
FilePath,
NULL
);
FreePool ((VOID *)ContainerInfo);
}
}
}
FreePool ((VOID *)VolumeInfo);
} else {
Status = InternalGetBootFileWorker (Device, Root, FilePath);
}
Root->Close (Root);
}
}
return Status;
}