mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
898 lines
25 KiB
C
898 lines
25 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 "BootManagementInternal.h"
|
|
|
|
#include <Guid/AppleBless.h>
|
|
#include <IndustryStandard/AppleDiskLabel.h>
|
|
#include <IndustryStandard/AppleIcon.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/OcDebugLogLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcDevicePathLib.h>
|
|
#include <Library/OcFileLib.h>
|
|
#include <Library/OcStringLib.h>
|
|
#include <Library/OcXmlLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
EFI_STATUS
|
|
InternalReadBootEntryFile (
|
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
|
|
IN CONST CHAR16 *DirectoryName,
|
|
IN CONST CHAR16 *Filename,
|
|
IN UINT32 MaxFileSize,
|
|
IN UINT32 MinFileSize,
|
|
OUT VOID **FileData,
|
|
OUT UINT32 *DataSize OPTIONAL
|
|
)
|
|
{
|
|
CHAR16 *FilePath;
|
|
UINTN FilePathSize;
|
|
UINT32 FileReadSize;
|
|
BOOLEAN Result;
|
|
|
|
Result = BaseOverflowAddUN (
|
|
StrSize (DirectoryName),
|
|
StrSize (Filename) - sizeof (CHAR16),
|
|
&FilePathSize
|
|
);
|
|
if (Result) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
FilePath = AllocatePool (FilePathSize);
|
|
|
|
if (FilePath == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
UnicodeSPrint (FilePath, FilePathSize, L"%s%s", DirectoryName, Filename);
|
|
*FileData = OcReadFile (FileSystem, FilePath, &FileReadSize, MaxFileSize);
|
|
|
|
FreePool (FilePath);
|
|
|
|
if (*FileData == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if (FileReadSize < MinFileSize) {
|
|
FreePool (*FileData);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (DataSize != NULL) {
|
|
*DataSize = FileReadSize;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
CHAR16 *
|
|
InternalGetAppleDiskLabel (
|
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
|
|
IN CONST CHAR16 *BootDirectoryName,
|
|
IN CONST CHAR16 *LabelFilename
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR8 *AsciiDiskLabel;
|
|
CHAR16 *UnicodeDiskLabel;
|
|
UINT32 DiskLabelLength;
|
|
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
BootDirectoryName,
|
|
LabelFilename,
|
|
OC_MAX_VOLUME_LABEL_SIZE,
|
|
0,
|
|
(VOID **)&AsciiDiskLabel,
|
|
&DiskLabelLength
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
UnicodeDiskLabel = AsciiStrCopyToUnicode (AsciiDiskLabel, DiskLabelLength);
|
|
if (UnicodeDiskLabel != NULL) {
|
|
UnicodeFilterString (UnicodeDiskLabel, TRUE);
|
|
}
|
|
|
|
FreePool (AsciiDiskLabel);
|
|
|
|
return UnicodeDiskLabel;
|
|
}
|
|
|
|
CHAR8 *
|
|
InternalGetContentFlavour (
|
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
|
|
IN CONST CHAR16 *BootDirectoryName
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR8 *AsciiContentFlavour;
|
|
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
BootDirectoryName,
|
|
L".contentFlavour",
|
|
OC_MAX_CONTENT_FLAVOUR_SIZE,
|
|
0,
|
|
(VOID **)&AsciiContentFlavour,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
AsciiFilterString (AsciiContentFlavour, TRUE);
|
|
|
|
return AsciiContentFlavour;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
GetAppleVersionFromPlist (
|
|
IN CHAR8 *SystemVersionData,
|
|
IN UINT32 SystemVersionDataSize,
|
|
OUT CHAR8 *AppleVersion
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
XML_DOCUMENT *Document;
|
|
XML_NODE *RootDict;
|
|
UINT32 DictSize;
|
|
UINT32 Index;
|
|
CONST CHAR8 *CurrentKey;
|
|
XML_NODE *CurrentValue;
|
|
CONST CHAR8 *Version;
|
|
|
|
Document = XmlDocumentParse (SystemVersionData, SystemVersionDataSize, FALSE);
|
|
|
|
if (Document == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
RootDict = PlistNodeCast (PlistDocumentRoot (Document), PLIST_NODE_TYPE_DICT);
|
|
|
|
if (RootDict == NULL) {
|
|
XmlDocumentFree (Document);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
DictSize = PlistDictChildren (RootDict);
|
|
for (Index = 0; Index < DictSize; Index++) {
|
|
CurrentKey = PlistKeyValue (PlistDictChild (RootDict, Index, &CurrentValue));
|
|
|
|
if ((CurrentKey == NULL) || (AsciiStrCmp (CurrentKey, "ProductUserVisibleVersion") != 0)) {
|
|
continue;
|
|
}
|
|
|
|
if (PlistNodeCast (CurrentValue, PLIST_NODE_TYPE_STRING) != NULL) {
|
|
Version = XmlNodeContent (CurrentValue);
|
|
if (Version != NULL) {
|
|
if (AsciiStrCpyS (AppleVersion, OC_APPLE_VERSION_MAX_SIZE, Version) != RETURN_SUCCESS) {
|
|
Status = EFI_UNSUPPORTED;
|
|
} else {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
XmlDocumentFree (Document);
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
CHAR16 *
|
|
InternalGetAppleRecoveryName (
|
|
IN CHAR8 *Version
|
|
)
|
|
{
|
|
CHAR16 *RecoveryName;
|
|
UINTN RecoveryNameSize;
|
|
|
|
RecoveryName = NULL;
|
|
|
|
if (Version[0] != '\0') {
|
|
RecoveryNameSize = L_STR_SIZE (L"Recovery ") + AsciiStrLen (Version) * sizeof (CHAR16);
|
|
RecoveryName = AllocatePool (RecoveryNameSize);
|
|
if (RecoveryName != NULL) {
|
|
UnicodeSPrint (RecoveryName, RecoveryNameSize, L"Recovery %a", Version);
|
|
UnicodeFilterString (RecoveryName, TRUE);
|
|
}
|
|
}
|
|
|
|
return RecoveryName;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalGetAppleVersion (
|
|
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem,
|
|
IN CONST CHAR16 *DirectoryName,
|
|
OUT CHAR8 *AppleVersion
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR8 *SystemVersionData;
|
|
UINT32 SystemVersionDataSize;
|
|
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
DirectoryName,
|
|
L"SystemVersion.plist",
|
|
BASE_1MB,
|
|
0,
|
|
(VOID **)&SystemVersionData,
|
|
&SystemVersionDataSize
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Status = GetAppleVersionFromPlist (SystemVersionData, SystemVersionDataSize, AppleVersion);
|
|
FreePool (SystemVersionData);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalGetRecoveryOsBooter (
|
|
IN EFI_HANDLE Device,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
|
|
IN BOOLEAN Basic
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
EFI_FILE_PROTOCOL *Root;
|
|
EFI_FILE_PROTOCOL *Recovery;
|
|
UINTN FilePathSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *TmpPath;
|
|
UINTN TmpPathSize;
|
|
CHAR16 *DevicePathText;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Device,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID **)&FileSystem
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FileSystem->OpenVolume (FileSystem, &Root);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
FilePathSize = 0;
|
|
|
|
if (!Basic) {
|
|
*FilePath = (EFI_DEVICE_PATH_PROTOCOL *)OcGetFileInfo (
|
|
Root,
|
|
&gAppleBlessedOsxFolderInfoGuid,
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
&FilePathSize
|
|
);
|
|
} else {
|
|
//
|
|
// Requested basic recovery support, i.e. only com.apple.recovery.boot folder check.
|
|
// This is useful for locating empty USB sticks with just a dmg in them.
|
|
//
|
|
*FilePath = NULL;
|
|
}
|
|
|
|
if (*FilePath != NULL) {
|
|
if (IsDevicePathValid (*FilePath, FilePathSize)) {
|
|
//
|
|
// We skip alternate entry when current one is the same.
|
|
// This is to prevent recovery and volume duplicates on HFS+ systems.
|
|
//
|
|
|
|
TmpPath = (EFI_DEVICE_PATH_PROTOCOL *)OcGetFileInfo (
|
|
Root,
|
|
&gAppleBlessedSystemFolderInfoGuid,
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
&TmpPathSize
|
|
);
|
|
|
|
if (TmpPath != NULL) {
|
|
if ( IsDevicePathValid (TmpPath, TmpPathSize)
|
|
&& IsDevicePathEqual (TmpPath, *FilePath))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "Skipping equal alternate device path %p\n", Device));
|
|
Status = EFI_ALREADY_STARTED;
|
|
FreePool (*FilePath);
|
|
*FilePath = NULL;
|
|
}
|
|
|
|
FreePool (TmpPath);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// This entry should point to a folder with recovery.
|
|
// Apple never adds trailing slashes to blessed folder paths.
|
|
// However, we do rely on trailing slashes in folder paths and add them here.
|
|
//
|
|
TmpPath = TrailedBooterDevicePath (*FilePath);
|
|
if (TmpPath != NULL) {
|
|
FreePool (*FilePath);
|
|
*FilePath = TmpPath;
|
|
}
|
|
}
|
|
} else {
|
|
FreePool (*FilePath);
|
|
*FilePath = NULL;
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
} else {
|
|
//
|
|
// Ok, this one can still be FileVault 2 HFS+ recovery or just a hardcoded basic recovery.
|
|
// Apple does add its path to so called "Alternate OS blessed file/folder", but this
|
|
// path is not accessible from HFSPlus.efi driver. Just why???
|
|
// Their SlingShot.efi app just bruteforces com.apple.recovery.boot directory existence,
|
|
// and we have to copy.
|
|
//
|
|
Status = OcSafeFileOpen (Root, &Recovery, L"\\com.apple.recovery.boot", EFI_FILE_MODE_READ, 0);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Do not do any extra checks for simplicity, as they will be done later either way.
|
|
//
|
|
Root->Close (Recovery);
|
|
Status = EFI_NOT_FOUND;
|
|
TmpPath = DevicePathFromHandle (Device);
|
|
|
|
if (TmpPath != NULL) {
|
|
*FilePath = AppendFileNameDevicePath (TmpPath, L"\\com.apple.recovery.boot\\");
|
|
if (*FilePath != NULL) {
|
|
DEBUG_CODE_BEGIN ();
|
|
DevicePathText = ConvertDevicePathToText (*FilePath, FALSE, FALSE);
|
|
if (DevicePathText != NULL) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Got recovery dp %s\n", DevicePathText));
|
|
FreePool (DevicePathText);
|
|
}
|
|
|
|
DEBUG_CODE_END ();
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
} else {
|
|
Status = EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
Root->Close (Root);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcGetBootEntryLabelImage (
|
|
IN OC_PICKER_CONTEXT *Context,
|
|
IN OC_BOOT_ENTRY *BootEntry,
|
|
IN UINT8 Scale,
|
|
OUT VOID **ImageData,
|
|
OUT UINT32 *DataLength
|
|
)
|
|
{
|
|
ASSERT (DataLength != NULL);
|
|
return OcGetBootEntryFile (
|
|
BootEntry,
|
|
Scale == 2 ? L".disk_label_2x" : L".disk_label",
|
|
"label",
|
|
BASE_16MB,
|
|
sizeof (APPLE_DISK_LABEL),
|
|
ImageData,
|
|
DataLength,
|
|
TRUE,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcGetBootEntryIcon (
|
|
IN OC_PICKER_CONTEXT *Context,
|
|
IN OC_BOOT_ENTRY *BootEntry,
|
|
OUT VOID **ImageData,
|
|
OUT UINT32 *DataLength
|
|
)
|
|
{
|
|
ASSERT (DataLength != NULL);
|
|
return OcGetBootEntryFile (
|
|
BootEntry,
|
|
L".VolumeIcon.icns",
|
|
"volume icon",
|
|
BASE_16MB,
|
|
sizeof (APPLE_ICNS_RECORD) * 2,
|
|
ImageData,
|
|
DataLength,
|
|
FALSE,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InternalGetBootEntryFile (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 *FileName,
|
|
IN CONST CHAR16 *DebugBootEntryName,
|
|
IN CONST CHAR8 *DebugFileType,
|
|
IN UINT32 MaxFileSize,
|
|
IN UINT32 MinFileSize,
|
|
OUT VOID **FileData,
|
|
OUT UINT32 *DataLength OPTIONAL,
|
|
IN BOOLEAN SearchAtLeaf,
|
|
IN BOOLEAN SearchAtRoot
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *BootDirectoryName;
|
|
CHAR16 *GuidPrefix;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
|
|
ASSERT (SearchAtLeaf || SearchAtRoot);
|
|
ASSERT (DevicePath != NULL);
|
|
ASSERT (FileData != NULL);
|
|
|
|
*FileData = NULL;
|
|
if (DataLength != NULL) {
|
|
*DataLength = 0;
|
|
}
|
|
|
|
Status = OcBootPolicyDevicePathToDirPath (
|
|
DevicePath,
|
|
&BootDirectoryName,
|
|
&FileSystem
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
if (SearchAtLeaf) {
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
BootDirectoryName,
|
|
FileName,
|
|
MaxFileSize,
|
|
MinFileSize,
|
|
FileData,
|
|
DataLength
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status) || !SearchAtRoot) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Get %a for %s - %r\n",
|
|
DebugFileType,
|
|
DebugBootEntryName,
|
|
Status
|
|
));
|
|
|
|
FreePool (BootDirectoryName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
GuidPrefix = BootDirectoryName[0] == '\\' ? &BootDirectoryName[1] : &BootDirectoryName[0];
|
|
if (HasValidGuidStringPrefix (GuidPrefix) && (GuidPrefix[GUID_STRING_LENGTH] == '\\')) {
|
|
GuidPrefix[GUID_STRING_LENGTH+1] = '\0';
|
|
} else {
|
|
GuidPrefix = NULL;
|
|
}
|
|
|
|
//
|
|
// OC-specific location, per-GUID and hence per-OS, below Preboot volume root.
|
|
// Not recognised by Apple bootpicker.
|
|
//
|
|
if (GuidPrefix != NULL) {
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
GuidPrefix,
|
|
FileName,
|
|
MaxFileSize,
|
|
MinFileSize,
|
|
FileData,
|
|
DataLength
|
|
);
|
|
} else {
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Apple default location at Preboot volume root (typically mounted within OS
|
|
// at /System/Volumes/Preboot/), shared by all OSes on a volume.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
Status = InternalReadBootEntryFile (
|
|
FileSystem,
|
|
L"",
|
|
FileName,
|
|
MaxFileSize,
|
|
MinFileSize,
|
|
FileData,
|
|
DataLength
|
|
);
|
|
}
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCB: Get %a for %s %s - %r\n",
|
|
DebugFileType,
|
|
DebugBootEntryName,
|
|
GuidPrefix,
|
|
Status
|
|
));
|
|
|
|
FreePool (BootDirectoryName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcGetBootEntryFileFromDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN CONST CHAR16 *FileName,
|
|
IN CONST CHAR8 *DebugFileType,
|
|
IN UINT32 MaxFileSize,
|
|
IN UINT32 MinFileSize,
|
|
OUT VOID **FileData,
|
|
OUT UINT32 *DataLength OPTIONAL,
|
|
IN BOOLEAN SearchAtLeaf,
|
|
IN BOOLEAN SearchAtRoot
|
|
)
|
|
{
|
|
return InternalGetBootEntryFile (
|
|
DevicePath,
|
|
FileName,
|
|
L"boot entry",
|
|
DebugFileType,
|
|
MaxFileSize,
|
|
MinFileSize,
|
|
FileData,
|
|
DataLength,
|
|
SearchAtLeaf,
|
|
SearchAtRoot
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcGetBootEntryFile (
|
|
IN OC_BOOT_ENTRY *BootEntry,
|
|
IN CONST CHAR16 *FileName,
|
|
IN CONST CHAR8 *DebugFileType,
|
|
IN UINT32 MaxFileSize,
|
|
IN UINT32 MinFileSize,
|
|
OUT VOID **FileData,
|
|
OUT UINT32 *DataLength OPTIONAL,
|
|
IN BOOLEAN SearchAtLeaf,
|
|
IN BOOLEAN SearchAtRoot
|
|
)
|
|
{
|
|
if ((BootEntry->Type & (OC_BOOT_EXTERNAL_TOOL | OC_BOOT_SYSTEM)) != 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
return InternalGetBootEntryFile (
|
|
BootEntry->DevicePath,
|
|
FileName,
|
|
BootEntry->Name,
|
|
DebugFileType,
|
|
MaxFileSize,
|
|
MinFileSize,
|
|
FileData,
|
|
DataLength,
|
|
SearchAtLeaf,
|
|
SearchAtRoot
|
|
);
|
|
}
|
|
|
|
//
|
|
// Duplicate each flavour in list, w/ apple version added to first of each duplicate
|
|
//
|
|
STATIC
|
|
CHAR8 *
|
|
InternalAddAppleVersion (
|
|
IN CHAR8 *Flavour,
|
|
IN CHAR8 *Version
|
|
)
|
|
{
|
|
UINTN VersionLength;
|
|
UINTN FlavourLength;
|
|
UINTN SepCount;
|
|
UINTN Size;
|
|
UINTN Index;
|
|
CHAR8 *NewFlavour;
|
|
CHAR8 *Start;
|
|
CHAR8 *End;
|
|
CHAR8 *Pos;
|
|
|
|
ASSERT (Flavour != NULL);
|
|
ASSERT (Version != NULL);
|
|
|
|
VersionLength = AsciiStrLen (Version);
|
|
FlavourLength = AsciiStrLen (Flavour);
|
|
SepCount = 0;
|
|
for (Index = 0; Index < FlavourLength; Index++) {
|
|
if (Flavour[Index] == ':') {
|
|
++SepCount;
|
|
}
|
|
}
|
|
|
|
Size = 2 * (FlavourLength + 1) + VersionLength * (SepCount + 1);
|
|
|
|
if (Size > OC_MAX_CONTENT_FLAVOUR_SIZE) {
|
|
return Flavour;
|
|
}
|
|
|
|
NewFlavour = AllocatePool (Size);
|
|
if (NewFlavour == NULL) {
|
|
return Flavour;
|
|
}
|
|
|
|
Pos = NewFlavour;
|
|
End = Flavour - 1;
|
|
do {
|
|
for (Start = ++End; *End != '\0' && *End != ':'; ++End) {
|
|
}
|
|
|
|
AsciiStrnCpyS (Pos, Size - (Pos - NewFlavour), Start, End - Start);
|
|
Pos += End - Start;
|
|
AsciiStrnCpyS (Pos, Size - (Pos - NewFlavour), Version, VersionLength);
|
|
Pos += VersionLength;
|
|
*Pos++ = ':';
|
|
AsciiStrnCpyS (Pos, Size - (Pos - NewFlavour), Start, End - Start);
|
|
Pos += End - Start;
|
|
*Pos++ = ':';
|
|
} while (*End != '\0');
|
|
|
|
*(Pos - 1) = '\0';
|
|
|
|
ASSERT ((UINTN)(Pos - NewFlavour) == Size);
|
|
|
|
FreePool (Flavour);
|
|
|
|
return NewFlavour;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalDescribeBootEntry (
|
|
IN OC_BOOT_CONTEXT *BootContext,
|
|
IN OUT OC_BOOT_ENTRY *BootEntry
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *BootDirectoryName;
|
|
CHAR16 *TmpBootName;
|
|
UINT32 BcdSize;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem;
|
|
CHAR8 *ContentFlavour;
|
|
CHAR8 AppleVersion[OC_APPLE_VERSION_MAX_SIZE];
|
|
CHAR8 *Dot;
|
|
|
|
AppleVersion[0] = '\0';
|
|
|
|
//
|
|
// Custom entries need no special description.
|
|
//
|
|
if ((BootEntry->Type == OC_BOOT_EXTERNAL_OS) || (BootEntry->Type == OC_BOOT_EXTERNAL_TOOL)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Status = OcBootPolicyDevicePathToDirPath (
|
|
BootEntry->DevicePath,
|
|
&BootDirectoryName,
|
|
&FileSystem
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Try to use APFS-style label or legacy HFS one.
|
|
//
|
|
BootEntry->Name = InternalGetAppleDiskLabel (FileSystem, BootDirectoryName, L".contentDetails");
|
|
if (BootEntry->Name == NULL) {
|
|
BootEntry->Name = InternalGetAppleDiskLabel (FileSystem, BootDirectoryName, L".disk_label.contentDetails");
|
|
}
|
|
|
|
//
|
|
// With FV2 encryption on HFS+ the actual boot happens from "Recovery HD/S/L/CoreServices".
|
|
// For some reason "Recovery HD/S/L/CoreServices/.disk_label" may not get updated immediately,
|
|
// and will contain "Recovery HD" despite actually pointing to "Macintosh HD".
|
|
// This also spontaneously happens with renamed APFS volumes. The workaround is to manually
|
|
// edit the file or sometimes choose the boot volume once more in preferences.
|
|
//
|
|
// TODO: Bugreport this to Apple, as this is clearly their bug, which should be reproducible
|
|
// on original hardware.
|
|
//
|
|
// There exists .root_uuid, which contains real partition UUID in ASCII, however, Apple
|
|
// BootPicker only uses it for entry deduplication, and we cannot figure out the name
|
|
// on an encrypted volume anyway.
|
|
//
|
|
|
|
//
|
|
// Windows boot entry may have a custom name, so ensure OC_BOOT_WINDOWS is set correctly.
|
|
//
|
|
if ((BootEntry->Type == OC_BOOT_UNKNOWN) && BootEntry->IsGeneric) {
|
|
DEBUG ((DEBUG_INFO, "OCB: Trying to detect Microsoft BCD\n"));
|
|
Status = OcReadFileSize (FileSystem, L"\\EFI\\Microsoft\\Boot\\BCD", &BcdSize);
|
|
if (!EFI_ERROR (Status)) {
|
|
BootEntry->Type = OC_BOOT_WINDOWS;
|
|
}
|
|
}
|
|
|
|
if ((BootEntry->Type == OC_BOOT_WINDOWS) && (BootEntry->Name == NULL)) {
|
|
BootEntry->Name = AllocateCopyPool (sizeof (L"Windows"), L"Windows");
|
|
}
|
|
|
|
// TODO: Should macOS installer have own OC_BOOT_ENTRY_TYPE (plus own voiceover file?)?
|
|
BootEntry->IsAppleInstaller = (StrStr (BootDirectoryName, L"com.apple.installer") != NULL);
|
|
if (BootEntry->Name == NULL) {
|
|
//
|
|
// Special case - installer should be clearly identified to end users but does not normally
|
|
// contain text label, only pre-rendered graphical label which is not usable in builtin
|
|
// picker, or in Canopy with disk labels disabled.
|
|
//
|
|
if (BootEntry->IsAppleInstaller) {
|
|
BootEntry->Name = AllocateCopyPool (L_STR_SIZE (L"macOS Installer"), L"macOS Installer");
|
|
} else {
|
|
BootEntry->Name = OcGetVolumeLabel (FileSystem);
|
|
if (BootEntry->Name != NULL) {
|
|
if ( (StrCmp (BootEntry->Name, L"Recovery HD") == 0)
|
|
|| (StrCmp (BootEntry->Name, L"Recovery") == 0))
|
|
{
|
|
if ((BootEntry->Type == OC_BOOT_UNKNOWN) || (BootEntry->Type == OC_BOOT_APPLE_OS)) {
|
|
BootEntry->Type = OC_BOOT_APPLE_RECOVERY;
|
|
}
|
|
|
|
Status = InternalGetAppleVersion (FileSystem, BootDirectoryName, AppleVersion);
|
|
if (EFI_ERROR (Status)) {
|
|
TmpBootName = NULL;
|
|
} else {
|
|
TmpBootName = InternalGetAppleRecoveryName (AppleVersion);
|
|
}
|
|
} else if (StrCmp (BootEntry->Name, L"Preboot") == 0) {
|
|
//
|
|
// Common Big Sur beta bug failing to create .contentDetails files.
|
|
// Workaround it by using the standard installed macOS system volume name.
|
|
// Applies to anything on the system volume without text labels (and not already
|
|
// handled above, such as installer).
|
|
//
|
|
TmpBootName = AllocateCopyPool (sizeof (L"Macintosh HD"), L"Macintosh HD");
|
|
} else {
|
|
TmpBootName = NULL;
|
|
}
|
|
|
|
if (TmpBootName != NULL) {
|
|
FreePool (BootEntry->Name);
|
|
BootEntry->Name = TmpBootName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (BootEntry->Name == NULL) {
|
|
FreePool (BootDirectoryName);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BootEntry->PathName = BootDirectoryName;
|
|
|
|
//
|
|
// Get user-specified or builtin content flavour.
|
|
//
|
|
if ((BootContext->PickerContext->PickerAttributes & OC_ATTR_USE_FLAVOUR_ICON) != 0) {
|
|
BootEntry->Flavour = InternalGetContentFlavour (FileSystem, BootDirectoryName);
|
|
}
|
|
|
|
if ((BootEntry->Flavour == NULL) || (AsciiStrCmp (BootEntry->Flavour, OC_FLAVOUR_AUTO) == 0)) {
|
|
switch (BootEntry->Type) {
|
|
case OC_BOOT_APPLE_OS:
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_APPLE_OS), OC_FLAVOUR_APPLE_OS);
|
|
if ((BootContext->PickerContext->PickerAttributes & OC_ATTR_USE_FLAVOUR_ICON) != 0) {
|
|
InternalGetAppleVersion (FileSystem, BootDirectoryName, AppleVersion);
|
|
}
|
|
|
|
break;
|
|
case OC_BOOT_APPLE_FW_UPDATE:
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_APPLE_FW), OC_FLAVOUR_APPLE_FW);
|
|
break;
|
|
case OC_BOOT_APPLE_RECOVERY:
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_APPLE_RECOVERY), OC_FLAVOUR_APPLE_RECOVERY);
|
|
break;
|
|
case OC_BOOT_APPLE_TIME_MACHINE:
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_APPLE_TIME_MACHINE), OC_FLAVOUR_APPLE_TIME_MACHINE);
|
|
break;
|
|
case OC_BOOT_WINDOWS:
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_WINDOWS), OC_FLAVOUR_WINDOWS);
|
|
break;
|
|
case OC_BOOT_UNKNOWN:
|
|
ContentFlavour = NULL;
|
|
break;
|
|
default:
|
|
DEBUG ((DEBUG_ERROR, "OCB: Entry kind %d unsupported for flavour\n", BootEntry->Type));
|
|
ContentFlavour = NULL;
|
|
break;
|
|
}
|
|
|
|
if ((BootEntry->Type & OC_BOOT_APPLE_ANY) != 0) {
|
|
if (ContentFlavour == NULL) {
|
|
ASSERT (FALSE);
|
|
} else if ( (AppleVersion[0] != '\0')
|
|
&& ((BootContext->PickerContext->PickerAttributes & OC_ATTR_USE_FLAVOUR_ICON) != 0)
|
|
)
|
|
{
|
|
//
|
|
// If 10.x(.y) remove .y and replace first . with _, otherwise remove from first .
|
|
//
|
|
Dot = AsciiStrStr (AppleVersion, ".");
|
|
if (Dot != NULL) {
|
|
if ( (Dot - AppleVersion == sizeof ("10") - 1)
|
|
&& OcAsciiStartsWith (AppleVersion, "10", FALSE)
|
|
)
|
|
{
|
|
*Dot++ = '_';
|
|
Dot = AsciiStrStr (Dot, ".");
|
|
if (Dot != NULL) {
|
|
*Dot = '\0';
|
|
}
|
|
} else {
|
|
*Dot = '\0';
|
|
}
|
|
}
|
|
|
|
ContentFlavour = InternalAddAppleVersion (ContentFlavour, AppleVersion);
|
|
}
|
|
}
|
|
|
|
if ((ContentFlavour == NULL) && (BootEntry->Flavour == NULL)) {
|
|
ContentFlavour = AllocateCopyPool (sizeof (OC_FLAVOUR_AUTO), OC_FLAVOUR_AUTO);
|
|
}
|
|
|
|
if (ContentFlavour != NULL) {
|
|
if (BootEntry->Flavour != NULL) {
|
|
FreePool (BootEntry->Flavour);
|
|
}
|
|
|
|
BootEntry->Flavour = ContentFlavour;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|