mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
1324 lines
33 KiB
C
1324 lines
33 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 <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/OcFileLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
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.
|
|
///
|
|
STATIC CONST CHAR16 *mBootPathNames[] = {
|
|
APPLE_BOOTER_DEFAULT_FILE_NAME,
|
|
APPLE_REMOVABLE_MEDIA_FILE_NAME,
|
|
EFI_REMOVABLE_MEDIA_FILE_NAME,
|
|
APPLE_BOOTER_ROOT_FILE_NAME
|
|
};
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFile (
|
|
IN EFI_HANDLE Device,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFileEx (
|
|
IN EFI_HANDLE Device,
|
|
IN UINT32 Unused OPTIONAL,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootInfo (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
);
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetPathNameOnApfsRecovery (
|
|
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
|
|
BootPolicyGetApfsRecoveryVolumes (
|
|
IN EFI_HANDLE Handle,
|
|
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,
|
|
BootPolicyGetBootInfo,
|
|
BootPolicyGetPathNameOnApfsRecovery,
|
|
BootPolicyGetApfsRecoveryVolumes
|
|
};
|
|
|
|
/**
|
|
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
|
|
BOOLEAN
|
|
InternalFileExists (
|
|
IN EFI_FILE_HANDLE Root,
|
|
IN CONST CHAR16 *FileName
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
|
|
Status = Root->Open (
|
|
Root,
|
|
&FileHandle,
|
|
(CHAR16 *) FileName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
FileHandle->Close (FileHandle);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
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) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (ContainerInfo != NULL) {
|
|
*ContainerInfo = GetFileInfo (
|
|
Root,
|
|
&gAppleApfsContainerInfoGuid,
|
|
sizeof (**ContainerInfo),
|
|
NULL
|
|
);
|
|
|
|
if (*ContainerInfo == NULL) {
|
|
if (VolumeInfo != NULL) {
|
|
FreePool (*VolumeInfo);
|
|
}
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (!IsDevicePathValid(*FilePath, FilePathSize)) {
|
|
FreePool (*FilePath);
|
|
*FilePath = NULL;
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
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 BooterPathSize;
|
|
CHAR16 *BooterPath;
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) GetFileInfo (
|
|
Root,
|
|
&gAppleBlessedSystemFolderInfoGuid,
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
&DevicePathSize
|
|
);
|
|
|
|
if (DevicePath == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
if (!IsDevicePathValid (DevicePath, DevicePathSize)) {
|
|
FreePool (DevicePath);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
DevicePathWalker = DevicePath;
|
|
|
|
while (!IsDevicePathEnd (DevicePathWalker)) {
|
|
if ((DevicePathType (DevicePathWalker) == MEDIA_DEVICE_PATH)
|
|
&& (DevicePathSubType (DevicePathWalker) == MEDIA_FILEPATH_DP)) {
|
|
|
|
FolderDevicePath = (FILEPATH_DEVICE_PATH *) DevicePathWalker;
|
|
BooterPathSize = StrSize (&FolderDevicePath->PathName[0])
|
|
+ StrSize (APPLE_BOOTER_ROOT_FILE_NAME) - sizeof (CHAR16);
|
|
BooterPath = AllocateZeroPool (BooterPathSize);
|
|
|
|
if (BooterPath != NULL) {
|
|
StrCpyS (BooterPath, BooterPathSize, &FolderDevicePath->PathName[0]);
|
|
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)) {
|
|
return Status;
|
|
}
|
|
|
|
if (!InternalFileExists (Root, BooterPath)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
*FilePath = FileDevicePath (Device, BooterPath);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetBooterFromPredefinedNameList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *Root,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL
|
|
)
|
|
{
|
|
UINTN Index;
|
|
CONST CHAR16 *PathName;
|
|
|
|
for (Index = 0; Index < ARRAY_SIZE (mBootPathNames); ++Index) {
|
|
PathName = mBootPathNames[Index];
|
|
|
|
if (InternalFileExists (Root, PathName)) {
|
|
if (DevicePath != NULL) {
|
|
*DevicePath = FileDevicePath (Device, PathName);
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
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
|
|
InternalGetBooterFromApfsVolumePredefinedNameList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *PrebootRoot,
|
|
IN CHAR16 *VolumeDirectoryName,
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_PROTOCOL *VolumeDirectoryHandle;
|
|
EFI_FILE_INFO *VolumeDirectoryInfo;
|
|
EFI_DEVICE_PATH_PROTOCOL *BooterPath;
|
|
|
|
Status = PrebootRoot->Open (
|
|
PrebootRoot,
|
|
&VolumeDirectoryHandle,
|
|
VolumeDirectoryName,
|
|
EFI_FILE_MODE_READ,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
VolumeDirectoryInfo = GetFileInfo (
|
|
VolumeDirectoryHandle,
|
|
&gEfiFileInfoGuid,
|
|
sizeof (*VolumeDirectoryInfo),
|
|
NULL
|
|
);
|
|
|
|
if (VolumeDirectoryInfo == NULL) {
|
|
VolumeDirectoryHandle->Close (VolumeDirectoryHandle);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
if ((VolumeDirectoryInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
|
|
Status = InternalGetBooterFromPredefinedNameList (
|
|
Device,
|
|
VolumeDirectoryHandle,
|
|
DevicePath != NULL ? &BooterPath : NULL
|
|
);
|
|
}
|
|
|
|
FreePool (VolumeDirectoryInfo);
|
|
VolumeDirectoryHandle->Close (VolumeDirectoryHandle);
|
|
|
|
if (EFI_ERROR (Status) || DevicePath == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
*DevicePath = AppendDevicePathInstance (*DevicePath, BooterPath);
|
|
FreePool (BooterPath);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
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
|
|
InternalGetBooterFromApfsPredefinedNameList (
|
|
IN EFI_HANDLE Device,
|
|
IN EFI_FILE_PROTOCOL *PrebootRoot,
|
|
IN CONST GUID *ContainerUuid,
|
|
IN CONST CHAR16 *VolumeUuid,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath OPTIONAL,
|
|
OUT EFI_HANDLE *VolumeHandle OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
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];
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
for (Index = 0; Index < NumberOfHandles; ++Index) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **) &FileSystem
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &HandleRoot);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = InternalGetApfsSpecialFileInfo (HandleRoot, &VolumeInfo, &ContainerInfo);
|
|
|
|
HandleRoot->Close (HandleRoot);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
if (!CompareGuid (&ContainerInfo->Uuid, ContainerUuid)) {
|
|
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];
|
|
}
|
|
|
|
Status = InternalGetBooterFromApfsVolumePredefinedNameList (
|
|
Device,
|
|
PrebootRoot,
|
|
VolumeDirectoryName,
|
|
DevicePath
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
FreePool (HandleBuffer);
|
|
|
|
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 = DevicePathNodeLength (DevicePath);
|
|
|
|
PathNameSize = Size - SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (CHAR16);
|
|
PathName = AllocateZeroPool (PathNameSize);
|
|
|
|
if (PathName == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (
|
|
PathName,
|
|
FilePath->PathName,
|
|
Size - SIZE_OF_FILEPATH_DEVICE_PATH
|
|
);
|
|
|
|
Slash = StrStr (FilePath->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,
|
|
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)) {
|
|
Status = InternalGetBooterFromApfsPredefinedNameList (
|
|
DeviceHandle,
|
|
Root,
|
|
&ContainerInfo->Uuid,
|
|
FilePathName,
|
|
NULL,
|
|
ApfsVolumeHandle
|
|
);
|
|
|
|
FreePool (ContainerInfo);
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
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 EFI_DEVICE_PATH_PROTOCOL **FilePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
|
|
*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 = InternalGetBooterFromPredefinedNameList (Device, Root, FilePath);
|
|
}
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootFileEx (
|
|
IN EFI_HANDLE Device,
|
|
IN UINT32 Mode,
|
|
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;
|
|
|
|
*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 = InternalGetApfsSpecialFileInfo (Root, &VolumeInfo, &ContainerInfo);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = EFI_NOT_FOUND;
|
|
if ((VolumeInfo->Role & APPLE_APFS_VOLUME_ROLE_PREBOOT) != 0) {
|
|
Status = InternalGetBooterFromBlessedSystemFilePath (Root, FilePath);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = InternalGetBooterFromBlessedSystemFolderPath (Device, Root, FilePath);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = InternalGetBooterFromApfsPredefinedNameList (
|
|
Device,
|
|
Root,
|
|
&ContainerInfo->Uuid,
|
|
NULL,
|
|
FilePath,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (VolumeInfo);
|
|
FreePool (ContainerInfo);
|
|
} else {
|
|
Status = InternalGetBooterFromBlessedSystemFilePath (Root, FilePath);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = InternalGetBooterFromBlessedSystemFolderPath (Device, Root, FilePath);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = InternalGetBooterFromPredefinedNameList (Device, Root, FilePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetBootInfo (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
OUT CHAR16 **BootPathName,
|
|
OUT EFI_HANDLE *Device,
|
|
OUT EFI_HANDLE *ApfsVolumeHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
EFI_HANDLE DeviceHandle;
|
|
CHAR16 *PathName;
|
|
|
|
*BootPathName = NULL;
|
|
*Device = NULL;
|
|
*ApfsVolumeHandle = NULL;
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
&DevicePath,
|
|
&DeviceHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = InternalGetBootPathName (DevicePath, &PathName);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
*Device = DeviceHandle;
|
|
*BootPathName = PathName;
|
|
|
|
InternalGetApfsVolumeHandle (DeviceHandle, PathName, ApfsVolumeHandle);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BootPolicyGetPathNameOnApfsRecovery (
|
|
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 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;
|
|
|
|
Result = EFI_INVALID_PARAMETER;
|
|
|
|
NewHandle = NULL;
|
|
*Root = NULL;
|
|
*FullPathName = NULL;
|
|
*Reserved = NULL;
|
|
|
|
if (PathName == NULL || DevicePath == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = BootPolicyGetBootInfo (
|
|
DevicePath,
|
|
&BootPathName,
|
|
&Device,
|
|
&VolumeHandle
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
FreePool (BootPathName);
|
|
|
|
if (VolumeHandle == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = InternalGetApfsVolumeInfo (
|
|
VolumeHandle,
|
|
&ContainerGuid,
|
|
&VolumeGuid,
|
|
&VolumeRole
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NoHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Result = EFI_NOT_FOUND;
|
|
|
|
for (Index = 0; Index < NoHandles; ++Index) {
|
|
Status = InternalGetApfsVolumeInfo (
|
|
HandleBuffer[Index],
|
|
&ContainerGuid2,
|
|
&VolumeGuid2,
|
|
&VolumeRole2
|
|
);
|
|
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 = (*Root)->Open (
|
|
*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
|
|
BootPolicyGetApfsRecoveryVolumes (
|
|
IN EFI_HANDLE Handle,
|
|
OUT VOID **Volumes,
|
|
OUT UINTN *NumberOfEntries
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
UINTN NumberOfHandles;
|
|
EFI_HANDLE *HandleBuffer;
|
|
APFS_VOLUME_INFO *VolumeInfo;
|
|
GUID *ContainerGuids;
|
|
EFI_GUID ContainerGuid;
|
|
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;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&NumberOfHandles,
|
|
&HandleBuffer
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (NumberOfEntries > 0) {
|
|
VolumeInfo = AllocateZeroPool (NumberOfHandles * sizeof (*VolumeInfo));
|
|
ContainerGuids = AllocateZeroPool (NumberOfHandles * sizeof (*ContainerGuids));
|
|
|
|
if (VolumeInfo == NULL || ContainerGuids == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
} else {
|
|
Status = EFI_NOT_FOUND;
|
|
VolumeInfo = NULL;
|
|
ContainerGuids = NULL;
|
|
}
|
|
|
|
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], &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 = Root->Open (
|
|
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;
|
|
}
|
|
|
|
/**
|
|
The Entry Point installing the APPLE_BOOT_POLICY_PROTOCOL.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the EFI image.
|
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The entry point is executed successfully.
|
|
@retval EFI_ALREADY_STARTED The protocol has already been installed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
OcAppleBootPolicyInstallProtocol (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
VOID *Interface;
|
|
EFI_HANDLE Handle;
|
|
|
|
Status = gBS->LocateProtocol (
|
|
&gAppleBootPolicyProtocolGuid,
|
|
NULL,
|
|
&Interface
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Handle = NULL;
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Handle,
|
|
&gAppleBootPolicyProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
(VOID **) &mAppleBootPolicyProtocol
|
|
);
|
|
} else {
|
|
Status = EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|