CloverBootloader/rEFIt_UEFI/refit/lib.cpp
2020-11-13 00:25:56 +03:00

1640 lines
56 KiB
C++

/*
* refit/lib.c
* General library functions
*
* Copyright (c) 2006-2009 Christoph Pfisterer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Christoph Pfisterer nor the names of the
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
#include "../include/OsType.h"
#include "lib.h"
#include "screen.h"
#include "../Platform/guid.h"
#include "../Platform/APFS.h"
#include "../refit/lib.h"
#include "../Platform/Settings.h"
#include "Self.h"
#include "SelfOem.h"
#include "../include/OC.h"
#ifndef DEBUG_ALL
#define DEBUG_LIB 1
#else
#define DEBUG_LIB DEBUG_ALL
#endif
#if DEBUG_LIB == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_LIB, __VA_ARGS__)
#endif
// variables
XTheme ThemeX;
//XStringW ThemePath;
BOOLEAN gThemeChanged = FALSE;
//BOOLEAN gBootArgsChanged = FALSE;
BOOLEAN gBootChanged = FALSE;
BOOLEAN gThemeOptionsChanged = FALSE;
REFIT_VOLUME *SelfVolume = NULL;
//REFIT_VOLUME **Volumes = NULL;
//UINTN VolumesCount = 0;
VolumesArrayClass Volumes;
//REFIT_VOLUME* VolumesArrayClass::getApfsPartitionWithUUID(const XString8& ApfsContainerUUID, const XString8& APFSTargetUUID)
//{
//}
//
// Unicode collation protocol interface
//
EFI_UNICODE_COLLATION_PROTOCOL *mUnicodeCollation = NULL;
// functions
static void UninitVolumes(void);
// S. Mtr
/* Function for parsing nodes from device path
* IN : DevicePath, sizeof(DevicePath)
* OUT: Size of cutted device path
* Description:
* Device path contains device nodes.
* From UEFI specification device node struct looks like:
*typedef struct {
* UINT8 Type; ///< 0x01 Hardware Device Path.
* ///< 0x02 ACPI Device Path.
* ///< 0x03 Messaging Device Path.
* ///< 0x04 Media Device Path.
* ///< 0x05 BIOS Boot Specification Device Path.
* ///< 0x7F End of Hardware Device Path.
*
* UINT8 SubType;///< Varies by Type
* ///< 0xFF End Entire Device Path, or
* ///< 0x01 End This Instance of a Device Path and start a new
* ///< Device Path.
*
* UINT8 Length[2]; ///< Specific Device Path data. Type and Sub-Type define
* ///< type of data. Size of data is included in Length.
*
* } EFI_DEVICE_PATH_PROTOCOL;
*/
UINTN
NodeParser (UINT8 *DevPath, UINTN PathSize, UINT8 Type)
{
UINTN i;
for (i=0; i<PathSize+1;){
if (DevPath[i] == Type)
{
//This type corresponds to Type
//So.. save position and exit from loop
PathSize = i;
break;
}
//Jump to the next device node type
i += (((UINT16)DevPath[i+3]<<8) | DevPath[i+2]);
}
return PathSize;
}
BOOLEAN MetaiMatch (
IN CONST CHAR16 *String,
IN CONST CHAR16 *Pattern
);
EFI_STATUS GetRootFromPath(IN EFI_DEVICE_PATH_PROTOCOL* DevicePath, OUT EFI_FILE **Root)
{
EFI_STATUS Status;
EFI_HANDLE NewHandle;
EFI_DEVICE_PATH_PROTOCOL* TmpDevicePath;
// DBG("Try to duplicate DevicePath\n");
TmpDevicePath = DuplicateDevicePath(DevicePath);
// DBG("TmpDevicePath found\n");
NewHandle = NULL;
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid,
&TmpDevicePath,
&NewHandle);
// DBG("volume handle found =%X\n", NewHandle);
CheckError(Status, L"while reopening volume handle");
*Root = EfiLibOpenRoot(NewHandle);
if (*Root == NULL) {
// DBG("volume Root Dir can't be reopened\n");
return EFI_NOT_FOUND;
}
if (FileExists(*Root, L"mach_kernel")) {
DBG("mach_kernel exists\n");
} else {
DBG("mach_kernel not exists\n");
}
return Status;
}
//
// self recognition stuff
//
EFI_STATUS InitRefitLib(IN EFI_HANDLE ImageHandle)
{
self.initialize(ImageHandle);
// DBG("SelfDirPath = %ls\n", self.getCloverDirFullPath().wc_str());
return EFI_SUCCESS;
}
void UninitRefitLib(void)
{
// called before running external programs to close open file handles
ThemeX.closeThemeDir();
selfOem.closeHandle();
self.closeHandle();
closeDebugLog();
UninitVolumes();
}
EFI_STATUS ReinitRefitLib(void)
{
// called after reconnect drivers to re-open file handles
self.reInitialize();
selfOem.reInitialize();
ReinitVolumes();
ThemeX.openThemeDir();
return EFI_SUCCESS;
}
//
// firmware device path discovery
//
//looks like not used anywhere
static UINT8 LegacyLoaderMediaPathData[] = {
0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
};
static EFI_DEVICE_PATH *LegacyLoaderMediaPath = (EFI_DEVICE_PATH *)LegacyLoaderMediaPathData;
EFI_STATUS ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DEVICE_PATH **HardcodedPathList)
{
EFI_STATUS Status;
UINTN HandleCount = 0;
UINTN HandleIndex, HardcodedIndex;
EFI_HANDLE *Handles = NULL;
EFI_HANDLE Handle;
UINTN PathCount = 0;
UINTN PathIndex;
EFI_LOADED_IMAGE *LoadedImage;
EFI_DEVICE_PATH *DevicePath;
BOOLEAN Seen;
MaxPaths--; // leave space for the terminating NULL pointer
// get all LoadedImage handles
Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiLoadedImageProtocolGuid, NULL,
&HandleCount, &Handles);
if (CheckError(Status, L"while listing LoadedImage handles")) {
if (HardcodedPathList) {
for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++)
PathList[PathCount++] = HardcodedPathList[HardcodedIndex];
}
PathList[PathCount] = NULL;
return Status;
}
for (HandleIndex = 0; HandleIndex < HandleCount && PathCount < MaxPaths; HandleIndex++) {
Handle = Handles[HandleIndex];
Status = gBS->HandleProtocol(Handle, &gEfiLoadedImageProtocolGuid, (void **) &LoadedImage);
if (EFI_ERROR(Status))
continue; // This can only happen if the firmware scewed up, ignore it.
Status = gBS->HandleProtocol(LoadedImage->DeviceHandle, &gEfiDevicePathProtocolGuid, (void **) &DevicePath);
if (EFI_ERROR(Status))
continue; // This happens, ignore it.
// Only grab memory range nodes
if (DevicePathType(DevicePath) != HARDWARE_DEVICE_PATH || DevicePathSubType(DevicePath) != HW_MEMMAP_DP)
continue;
// Check if we have this device path in the list already
// WARNING: This assumes the first node in the device path is unique!
Seen = FALSE;
for (PathIndex = 0; PathIndex < PathCount; PathIndex++) {
if (DevicePathNodeLength(DevicePath) != DevicePathNodeLength(PathList[PathIndex]))
continue;
if (CompareMem(DevicePath, PathList[PathIndex], DevicePathNodeLength(DevicePath)) == 0) {
Seen = TRUE;
break;
}
}
if (Seen)
continue;
PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath);
}
FreePool(Handles);
if (HardcodedPathList) {
for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++)
PathList[PathCount++] = HardcodedPathList[HardcodedIndex];
}
PathList[PathCount] = NULL;
return (PathCount > 0)?EFI_SUCCESS:EFI_NOT_FOUND;
}
//
// volume functions
//
static void ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootable)
{
EFI_STATUS Status;
UINT8 *SectorBuffer;
UINTN i;
//MBR_PARTITION_INFO *MbrTable;
//BOOLEAN MbrTableFound;
UINTN BlockSize = 0;
CHAR16 volumeName[255];
CHAR8 tmp[64];
UINT32 VCrc32;
// CHAR16 *kind = NULL;
Volume->HasBootCode = FALSE;
Volume->LegacyOS->IconName.setEmpty();
Volume->LegacyOS->Name.setEmpty();
// Volume->BootType = BOOTING_BY_MBR; //default value
Volume->BootType = BOOTING_BY_EFI;
*Bootable = FALSE;
if ((Volume->BlockIO == NULL) || (!Volume->BlockIO->Media->MediaPresent))
return;
ZeroMem((CHAR8*)&tmp[0], 64);
BlockSize = Volume->BlockIO->Media->BlockSize;
if (BlockSize > 2048)
return; // our buffer is too small... the bred of thieve of cable
SectorBuffer = (__typeof__(SectorBuffer))AllocateAlignedPages(EFI_SIZE_TO_PAGES (2048), 16); //align to 16 byte?! Poher
ZeroMem((CHAR8*)&SectorBuffer[0], 2048);
// look at the boot sector (this is used for both hard disks and El Torito images!)
Status = Volume->BlockIO->ReadBlocks(Volume->BlockIO, Volume->BlockIO->Media->MediaId,
Volume->BlockIOOffset /*start lba*/,
2048, SectorBuffer);
if (!EFI_ERROR(Status) && (SectorBuffer[1] != 0)) {
// calc crc checksum of first 2 sectors - it's used later for legacy boot BIOS drive num detection
// note: possible future issues with AF 4K disks
*Bootable = TRUE;
Volume->HasBootCode = TRUE; //we assume that all CD are bootable
/* DBG("check SectorBuffer\n");
for (i=0; i<32; i++) {
DBG("%2hhX ", SectorBuffer[i]);
}
DBG("\n"); */
VCrc32 = GetCrc32(SectorBuffer, 512 * 2);
Volume->DriveCRC32 = VCrc32;
//gBS->CalculateCrc32 (SectorBuffer, 2 * 512, &Volume->DriveCRC32);
/* switch (Volume->DiskKind ) {
case DISK_KIND_OPTICAL:
kind = L"DVD";
break;
case DISK_KIND_INTERNAL:
kind = L"HDD";
break;
case DISK_KIND_EXTERNAL:
kind = L"USB";
break;
default:
break;
}
DBG("Volume kind=%ls CRC=0x%hhX\n", kind, VCrc32); */
if (Volume->DiskKind == DISK_KIND_OPTICAL) { //CDROM
CHAR8* p = (CHAR8*)&SectorBuffer[8];
while (*p == 0x20) {
p++;
}
for (i=0; i<30 && (*p >= 0x20) && (*p <= 'z'); i++, p++) {
tmp[i] = *p;
}
tmp[i] = 0;
while ((i>0) && (tmp[--i] == 0x20)) {}
tmp[i+1] = 0;
// if (*p != 0) {
AsciiStrToUnicodeStrS((CHAR8*)&tmp[0], volumeName, 255);
// }
DBG("Detected name %ls\n", volumeName);
Volume->VolName.takeValueFrom(volumeName);
for (i=8; i<2000; i++) { //vendor search
if (SectorBuffer[i] == 'A') {
if (AsciiStrStr((CHAR8*)&SectorBuffer[i], "APPLE")) {
// StrCpy(Volume->VolName, volumeName);
DBG(" Found AppleDVD\n");
Volume->LegacyOS->Type = OSTYPE_OSX;
Volume->BootType = BOOTING_BY_CD;
Volume->LegacyOS->IconName = L"mac"_XSW;
break;
}
} else if (SectorBuffer[i] == 'M') {
if (AsciiStrStr((CHAR8*)&SectorBuffer[i], "MICROSOFT")) {
// StrCpy(Volume->VolName, volumeName);
DBG(" Found Windows DVD\n");
Volume->LegacyOS->Type = OSTYPE_WIN;
Volume->BootType = BOOTING_BY_CD;
Volume->LegacyOS->IconName = L"win"_XSW;
break;
}
} else if (SectorBuffer[i] == 'L') {
if (AsciiStrStr((CHAR8*)&SectorBuffer[i], "LINUX")) {
// Volume->DevicePath = DuplicateDevicePath(DevicePath);
// StrCpy(Volume->VolName, volumeName);
DBG(" Found Linux DVD\n");
Volume->LegacyOS->Type = OSTYPE_LIN;
Volume->BootType = BOOTING_BY_CD;
Volume->LegacyOS->IconName = L"linux"_XSW;
break;
}
}
}
}
//else HDD
else { //HDD
/*
// apianti - does this detect every partition as legacy?
if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55 && SectorBuffer[0] != 0) {
*Bootable = TRUE;
Volume->HasBootCode = TRUE;
// DBG("The volume has bootcode\n");
Volume->LegacyOS->IconName = L"legacy";
Volume->LegacyOS->Name = L"Legacy";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
}
// */
// detect specific boot codes
if (CompareMem(SectorBuffer + 2, "LILO", 4) == 0 ||
CompareMem(SectorBuffer + 6, "LILO", 4) == 0 ||
CompareMem(SectorBuffer + 3, "SYSLINUX", 8) == 0 ||
FindMem(SectorBuffer, 2048, "ISOLINUX", 8) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"linux"_XSW;
Volume->LegacyOS->Name = L"Linux"_XSW;
Volume->LegacyOS->Type = OSTYPE_LIN;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) { // GRUB
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"grub,linux"_XSW;
Volume->LegacyOS->Name = L"Linux"_XSW;
Volume->BootType = BOOTING_BY_PBR;
/*
} else if ((*((UINT32 *)(SectorBuffer)) == 0x4d0062e9 &&
*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) ||
FindMem(SectorBuffer, 2048, "BOOT ", 10) >= 0) { //reboot Clover
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"clover";
Volume->LegacyOS->Name = L"Clover";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
// DBG("Detected Clover FAT32 bootcode\n");
*/
} else if ((*((UINT32 *)(SectorBuffer + 502)) == 0 &&
*((UINT32 *)(SectorBuffer + 506)) == 50000 &&
*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) ||
FindMem(SectorBuffer, 2048, "Starting the BTX loader", 23) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"freebsd,linux"_XSW;
Volume->LegacyOS->Name = L"FreeBSD"_XSW;
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "!Loading", 8) >= 0 ||
FindMem(SectorBuffer, 2048, "/cdboot\0/CDBOOT\0", 16) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"openbsd,linux"_XSW;
Volume->LegacyOS->Name = L"OpenBSD"_XSW;
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "Not a bootxx image", 18) >= 0 ||
*((UINT32 *)(SectorBuffer + 1028)) == 0x7886b6d1) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"netbsd,linux"_XSW;
Volume->LegacyOS->Name = L"NetBSD"_XSW;
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 2048, "NTLDR", 5) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"win"_XSW;
Volume->LegacyOS->Name = L"Windows"_XSW;
Volume->LegacyOS->Type = OSTYPE_WIN;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 2048, "BOOTMGR", 7) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"vista,win"_XSW;
Volume->LegacyOS->Name = L"Windows"_XSW;
Volume->LegacyOS->Type = OSTYPE_WIN;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "CPUBOOT SYS", 11) >= 0 ||
FindMem(SectorBuffer, 512, "KERNEL SYS", 11) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"freedos,win"_XSW;
Volume->LegacyOS->Name = L"FreeDOS"_XSW;
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
/*
} else if (FindMem(SectorBuffer, 512, "OS2LDR", 6) >= 0 ||
FindMem(SectorBuffer, 512, "OS2BOOT", 7) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"ecomstation";
Volume->LegacyOS->Name = L"eComStation";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "Be Boot Loader", 14) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"beos";
Volume->LegacyOS->Name = L"BeOS";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "yT Boot Loader", 14) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"zeta";
Volume->LegacyOS->Name = L"ZETA";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
} else if (FindMem(SectorBuffer, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0 ||
FindMem(SectorBuffer, 512, "haiku_loader", 12) >= 0) {
Volume->HasBootCode = TRUE;
Volume->LegacyOS->IconName = L"haiku";
Volume->LegacyOS->Name = L"Haiku";
Volume->LegacyOS->Type = OSTYPE_VAR;
Volume->BootType = BOOTING_BY_PBR;
*/
}
}
// NOTE: If you add an operating system with a name that starts with 'W' or 'L', you
// need to fix AddLegacyEntry in main.c.
#if REFIT_DEBUG > 0
DBG(" Result of bootcode detection: %ls %ls (%ls)\n",
Volume->HasBootCode ? L"bootable" : L"non-bootable",
Volume->LegacyOS->Name.notEmpty() ? Volume->LegacyOS->Name.wc_str() : L"unknown",
Volume->LegacyOS->IconName.notEmpty() ? Volume->LegacyOS->IconName.wc_str() : L"legacy");
#endif
if (FindMem(SectorBuffer, 512, "Non-system disk", 15) >= 0) // dummy FAT boot sector
Volume->HasBootCode = FALSE;
#ifdef JIEF_DEBUG
//*Bootable = TRUE;
//Volume->HasBootCode = TRUE;
//Volume->LegacyOS->IconName = L"win";
//Volume->LegacyOS->Name = L"Windows";
//Volume->LegacyOS->Type = OSTYPE_WIN;
//Volume->BootType = BOOTING_BY_PBR;
#endif
// check for MBR partition table
/*
// apianti - this is littered with bugs and probably not needed lol
if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) {
MbrTableFound = FALSE;
MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
for (i = 0; i < 4; i++)
if (MbrTable[i].StartLBA && MbrTable[i].Size)
MbrTableFound = TRUE;
for (i = 0; i < 4; i++)
if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
MbrTableFound = FALSE;
if (MbrTableFound) {
Volume->MbrPartitionTable = (__typeof__(Volume->MbrPartitionTable))AllocatePool(4 * 16);
CopyMem(Volume->MbrPartitionTable, MbrTable, 4 * 16);
Volume->BootType = BOOTING_BY_MBR;
}
}
// */
}
// gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)SectorBuffer, 1);
// FreeAlignedPages((EFI_PHYSICAL_ADDRESS)(UINTN)SectorBuffer, 1);
FreeAlignedPages((void*)SectorBuffer, EFI_SIZE_TO_PAGES (2048));
}
//at start we have only Volume->DeviceHandle
static EFI_STATUS ScanVolume(IN OUT REFIT_VOLUME *Volume)
{
EFI_STATUS Status;
EFI_DEVICE_PATH *DevicePath, *NextDevicePath;
EFI_DEVICE_PATH *DiskDevicePath, *RemainingDevicePath = NULL;
HARDDRIVE_DEVICE_PATH *HdPath = NULL;
EFI_HANDLE WholeDiskHandle;
UINTN PartialLength = 0;
UINTN DevicePathSize;
// UINTN BufferSize = 255;
EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr;
EFI_FILE_INFO *RootInfo = NULL;
BOOLEAN Bootable;
// EFI_INPUT_KEY Key;
// get device path
DiskDevicePath = DevicePathFromHandle(Volume->DeviceHandle);
//Volume->DevicePath = DuplicateDevicePath(DevicePathFromHandle(Volume->DeviceHandle));
DevicePathSize = GetDevicePathSize (DiskDevicePath);
Volume->DevicePath = (__typeof__(Volume->DevicePath))AllocateAlignedPages(EFI_SIZE_TO_PAGES(DevicePathSize), 64);
CopyMem(Volume->DevicePath, DiskDevicePath, DevicePathSize);
Volume->DevicePathString = FileDevicePathToXStringW(Volume->DevicePath);
#if REFIT_DEBUG > 0
if (Volume->DevicePath != NULL) {
DBG(" %ls\n", FileDevicePathToXStringW(Volume->DevicePath).wc_str());
//#if REFIT_DEBUG >= 2
// DumpHex(1, 0, GetDevicePathSize(Volume->DevicePath), Volume->DevicePath);
//#endif
}
#else
DBG("\n");
#endif
Volume->ApfsFileSystemUUID = APFSPartitionUUIDExtractAsXString8(Volume->DevicePath); // NullXString8 if it's not an APFS volume
Volume->DiskKind = DISK_KIND_INTERNAL; // default
// get block i/o
Status = gBS->HandleProtocol(Volume->DeviceHandle, &gEfiBlockIoProtocolGuid, (void **) &(Volume->BlockIO));
if (EFI_ERROR(Status)) {
Volume->BlockIO = NULL;
// DBG(" Warning: Can't get BlockIO protocol.\n");
// WaitForSingleEvent (gST->ConIn->WaitForKey, 0);
// gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
return Status;
}
Bootable = FALSE;
if (Volume->BlockIO->Media->BlockSize == 2048){
// DBG(" Found optical drive\n");
Volume->DiskKind = DISK_KIND_OPTICAL;
Volume->BlockIOOffset = 0x10; // offset already applied for FS but not for blockio
ScanVolumeBootcode(Volume, &Bootable);
} else {
// DBG(" Found HD drive\n");
Volume->BlockIOOffset = 0;
// scan for bootcode and MBR table
ScanVolumeBootcode(Volume, &Bootable);
// DBG(" ScanVolumeBootcode success\n");
// detect device type
DevicePath = DuplicateDevicePath(Volume->DevicePath);
while (DevicePath != NULL && !IsDevicePathEndType(DevicePath)) {
NextDevicePath = NextDevicePathNode(DevicePath);
if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
((DevicePathSubType (DevicePath) == MSG_SATA_DP) ||
(DevicePathSubType (DevicePath) == MSG_NVME_NAMESPACE_DP) ||
(DevicePathSubType (DevicePath) == MSG_ATAPI_DP))) {
// DBG(" HDD volume\n");
Volume->DiskKind = DISK_KIND_INTERNAL;
break;
}
if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH &&
(DevicePathSubType(DevicePath) == MSG_USB_DP || DevicePathSubType(DevicePath) == MSG_USB_CLASS_DP)) {
// DBG(" USB volume\n");
Volume->DiskKind = DISK_KIND_EXTERNAL;
// break;
}
// FIREWIRE Devices
if (DevicePathType(DevicePath) == MESSAGING_DEVICE_PATH &&
(DevicePathSubType(DevicePath) == MSG_1394_DP || DevicePathSubType(DevicePath) == MSG_FIBRECHANNEL_DP)) {
// DBG(" FireWire volume\n");
Volume->DiskKind = DISK_KIND_FIREWIRE;
break;
}
// CD-ROM Devices
if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType(DevicePath) == MEDIA_CDROM_DP) {
// DBG(" CD-ROM volume\n");
Volume->DiskKind = DISK_KIND_OPTICAL; //it's impossible
break;
}
// VENDOR Specific Path
if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType(DevicePath) == MEDIA_VENDOR_DP) {
// DBG(" Vendor volume\n");
if ( Volume->ApfsFileSystemUUID.isEmpty() ) {
Volume->DiskKind = DISK_KIND_NODISK; // Jief, don't know why DISK_KIND_NODISK in that case. That prevents Recovery badge to appear. If it's not APFS, let's do it like it was before.
}
break;
}
// LEGACY CD-ROM
if (DevicePathType(DevicePath) == BBS_DEVICE_PATH &&
(DevicePathSubType(DevicePath) == BBS_BBS_DP || DevicePathSubType(DevicePath) == BBS_TYPE_CDROM)) {
// DBG(" Legacy CD-ROM volume\n");
Volume->DiskKind = DISK_KIND_OPTICAL;
break;
}
// LEGACY HARDDISK
if (DevicePathType(DevicePath) == BBS_DEVICE_PATH &&
(DevicePathSubType(DevicePath) == BBS_BBS_DP || DevicePathSubType(DevicePath) == BBS_TYPE_HARDDRIVE)) {
// DBG(" Legacy HDD volume\n");
Volume->DiskKind = DISK_KIND_INTERNAL;
break;
}
//one more we must take into account
// subtype = MSG_NVME_NAMESPACE_DP
// diskKind = NVME
//#define MSG_NVME_NAMESPACE_DP 0x17
DevicePath = NextDevicePath;
}
/* what is the bread?
// Bootable = TRUE;
if (DevicePathType(DevicePath) == MEDIA_DEVICE_PATH &&
DevicePathSubType(DevicePath) == MEDIA_VENDOR_DP) {
Volume->IsAppleLegacy = TRUE; // legacy BIOS device entry
// TODO: also check for Boot Camp GUID
//gEfiPartTypeSystemPartGuid
Bootable = FALSE; // this handle's BlockIO is just an alias for the whole device
DBG("AppleLegacy device\n");
}
*/
}
DevicePath = DuplicateDevicePath(Volume->DevicePath);
RemainingDevicePath = DevicePath; //initial value
//
// find the partition device path node
//
while (DevicePath && !IsDevicePathEnd (DevicePath)) {
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
(DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
HdPath = (HARDDRIVE_DEVICE_PATH *)DevicePath;
// break;
}
DevicePath = NextDevicePathNode (DevicePath);
}
// DBG("DevicePath scanned\n");
if (HdPath) {
// printf("Partition found %s\n", DevicePathToStr((EFI_DEVICE_PATH *)HdPath));
PartialLength = (UINTN)((UINT8 *)HdPath - (UINT8 *)(RemainingDevicePath));
if (PartialLength > 0x1000) {
PartialLength = sizeof(EFI_DEVICE_PATH); //something wrong here but I don't want to be freezed
// return EFI_SUCCESS;
}
DiskDevicePath = (EFI_DEVICE_PATH *)AllocatePool(PartialLength + sizeof(EFI_DEVICE_PATH));
CopyMem(DiskDevicePath, Volume->DevicePath, PartialLength);
CopyMem((UINT8 *)DiskDevicePath + PartialLength, DevicePath, sizeof(EFI_DEVICE_PATH)); //EndDevicePath
// DBG("WholeDevicePath %ls\n", DevicePathToStr(DiskDevicePath));
RemainingDevicePath = DiskDevicePath;
Status = gBS->LocateDevicePath(&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &WholeDiskHandle);
if (EFI_ERROR(Status)) {
DBG("Can't find WholeDevicePath: %s\n", efiStrError(Status));
} else {
Volume->WholeDiskDeviceHandle = WholeDiskHandle;
Volume->WholeDiskDevicePath = DuplicateDevicePath(RemainingDevicePath);
// look at the BlockIO protocol
Status = gBS->HandleProtocol(WholeDiskHandle, &gEfiBlockIoProtocolGuid, (void **) &Volume->WholeDiskBlockIO);
if (!EFI_ERROR(Status)) {
// DBG("WholeDiskBlockIO %hhX BlockSize=%d\n", Volume->WholeDiskBlockIO, Volume->WholeDiskBlockIO->Media->BlockSize);
// check the media block size
if (Volume->WholeDiskBlockIO->Media->BlockSize == 2048)
Volume->DiskKind = DISK_KIND_OPTICAL;
} else {
Volume->WholeDiskBlockIO = NULL;
// DBG("no WholeDiskBlockIO: %s\n", efiStrError(Status));
//CheckError(Status, L"from HandleProtocol");
}
}
FreePool(DiskDevicePath);
}
/* else {
DBG("HD path is not found\n"); //master volume!
}*/
// if (GlobalConfig.FastBoot) {
// return EFI_SUCCESS;
// }
if (!Bootable) {
#if REFIT_DEBUG > 0
if (Volume->HasBootCode){
DBG(" Volume considered non-bootable, but boot code is present\n");
// WaitForSingleEvent (gST->ConIn->WaitForKey, 0);
// gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
}
#endif
Volume->HasBootCode = FALSE;
}
// open the root directory of the volume
Volume->RootDir = EfiLibOpenRoot(Volume->DeviceHandle);
// DBG("Volume->RootDir OK\n");
if (Volume->RootDir == NULL) {
//Print(L"Error: Can't open volume.\n");
// TODO: signal that we had an error
//Slice - there is LegacyBoot volume
//properties are set before
// DBG("LegacyBoot volume\n");
if (HdPath) {
Volume->VolName = SWPrintf("Legacy HD%d", HdPath->PartitionNumber);
} else if (Volume->VolName.isEmpty()) {
Volume->VolName = L"Whole Disc Boot"_XSW;
}
if (Volume->LegacyOS->IconName.isEmpty())
Volume->LegacyOS->IconName = L"legacy"_XSW;
return EFI_SUCCESS;
}
if ( Volume->ApfsFileSystemUUID.notEmpty() ) {
APPLE_APFS_CONTAINER_INFO *ApfsContainerInfo;
APPLE_APFS_VOLUME_INFO *ApfsVolumeInfo;
Status = InternalGetApfsSpecialFileInfo(Volume->RootDir, &ApfsVolumeInfo, &ApfsContainerInfo);
if ( !EFI_ERROR(Status) ) {
//DBG("Status : %s, APFS role : %x\n", efiStrError(Status), ApfsVolumeInfo->Role);
Volume->ApfsRole = ApfsVolumeInfo->Role;
Volume->ApfsContainerUUID = GuidLEToXString8(ApfsContainerInfo->Uuid);
}else{
MsgLog("Status : %s, APFS role : %x\n", efiStrError(Status), ApfsVolumeInfo->Role);
}
}
if ( Volume->ApfsFileSystemUUID.notEmpty() ) {
DBG(" apfsFileSystemUUID=%s, ApfsContainerUUID=%s, ApfsRole=0x%x\n", Volume->ApfsFileSystemUUID.c_str(), Volume->ApfsContainerUUID.c_str(), Volume->ApfsRole);
}
if ( FileExists(Volume->RootDir, L"\\.VolumeLabel.txt") ) {
EFI_FILE* FileHandle;
Status = Volume->RootDir->Open(Volume->RootDir, &FileHandle, L"\\.VolumeLabel.txt", EFI_FILE_MODE_READ, 0);
if (!EFI_ERROR(Status)) {
CHAR8 Buffer[32+1];
UINTN BufferSize = sizeof(Buffer)-sizeof(CHAR8);
SetMem(Buffer, BufferSize+sizeof(CHAR8), 0);
Status = FileHandle->Read(FileHandle, &BufferSize, Buffer);
FileHandle->Close(FileHandle);
if (!EFI_ERROR(Status)) {
// strip line endings
while (BufferSize > 0 && (Buffer[BufferSize-1]=='\n' || Buffer[BufferSize-1]=='\r')) {
Buffer[--BufferSize]='\0';
}
Volume->VolLabel = SWPrintf("%s", Buffer);
}
}
}
// get volume name
if (Volume->VolName.isEmpty()) {
FileSystemInfoPtr = EfiLibFileSystemInfo(Volume->RootDir);
if (FileSystemInfoPtr) {
//DBG(" Volume name from FileSystem: '%ls'\n", FileSystemInfoPtr->VolumeLabel);
Volume->VolName.takeValueFrom(FileSystemInfoPtr->VolumeLabel);
FreePool(FileSystemInfoPtr);
}
}
if (Volume->VolName.isEmpty()) {
Volume->VolName = EfiLibFileSystemVolumeLabelInfo(Volume->RootDir);
//DBG(" Volume name from VolumeLabel: '%ls'\n", Volume->VolName.wc_str());
}
if (Volume->VolName.isEmpty()) {
RootInfo = EfiLibFileInfo (Volume->RootDir);
if (RootInfo) {
//DBG(" Volume name from RootFile: '%ls'\n", RootInfo->FileName);
Volume->VolName.takeValueFrom(RootInfo->FileName);
FreePool(RootInfo);
}
}
if ( Volume->VolName.isEmpty() || Volume->VolName.equal("\\") || Volume->VolName.equal(L"/") )
{
void *Instance;
if (!EFI_ERROR(gBS->HandleProtocol(Volume->DeviceHandle, &gEfiPartTypeSystemPartGuid, &Instance))) {
Volume->VolName = L"EFI"_XSW;
}
}
if (Volume->VolName.isEmpty()) {
// DBG("Create unknown name\n");
// WaitForSingleEvent (gST->ConIn->WaitForKey, 0);
// gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
if (HdPath) {
Volume->VolName.SWPrintf( "Unknown HD%d", HdPath->PartitionNumber);
// NOTE: this is normal for Apple's VenMedia device paths
} else {
Volume->VolName = L"Unknown HD"_XSW; //To be able to free it
}
}
// Browse all folders under root that looks like an UUID
if ( Volume->ApfsFileSystemUUID.notEmpty() )
{
REFIT_DIR_ITER DirIter;
EFI_FILE_INFO *DirEntry = NULL;
DirIterOpen(Volume->RootDir, L"\\", &DirIter);
while (DirIterNext(&DirIter, 1, L"*", &DirEntry)) {
if (DirEntry->FileName[0] == '.') {
//DBG("Skip dot entries: %ls\n", DirEntry->FileName);
continue;
}
if ( IsValidGuidAsciiString(LStringW(DirEntry->FileName)) ) {
Volume->ApfsTargetUUIDArray.Add(DirEntry->FileName);
}
}
DirIterClose(&DirIter);
}
DBG(" label : %ls\n", Volume->getVolLabelOrOSXVolumeNameOrVolName().wc_str());
//Status = GetOSVersion(Volume); NOTE: Sothor - We will find icon names later once we have found boot.efi on the volume //here we set Volume->IconName (tiger,leo,snow,lion,cougar, etc)
return EFI_SUCCESS;
}
static void ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry)
{
EFI_STATUS Status;
REFIT_VOLUME *Volume;
UINT32 ExtBase, ExtCurrent, NextExtCurrent;
UINTN i;
UINTN LogicalPartitionIndex = 4;
UINT8 *SectorBuffer;
BOOLEAN Bootable;
MBR_PARTITION_INFO *EMbrTable;
ExtBase = MbrEntry->StartLBA;
SectorBuffer = (__typeof__(SectorBuffer))AllocateAlignedPages (EFI_SIZE_TO_PAGES (512), WholeDiskVolume->BlockIO->Media->IoAlign);
for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
// read current EMBR
Status = WholeDiskVolume->BlockIO->ReadBlocks(WholeDiskVolume->BlockIO,
WholeDiskVolume->BlockIO->Media->MediaId,
ExtCurrent, 512, SectorBuffer);
if (EFI_ERROR(Status))
break;
if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
break;
EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
// scan logical partitions in this EMBR
NextExtCurrent = 0;
for (i = 0; i < 4; i++) {
if ((EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80) ||
EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
break;
if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
// set next ExtCurrent
NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
break;
} else {
// found a logical partition
Volume = new REFIT_VOLUME;
Volume->DiskKind = WholeDiskVolume->DiskKind;
Volume->IsMbrPartition = TRUE;
Volume->MbrPartitionIndex = LogicalPartitionIndex++;
Volume->VolName = SWPrintf("Partition %llu", Volume->MbrPartitionIndex + 1);
Volume->BlockIO = WholeDiskVolume->BlockIO;
Volume->BlockIOOffset = ExtCurrent + EMbrTable[i].StartLBA;
Volume->WholeDiskBlockIO = WholeDiskVolume->BlockIO;
Volume->WholeDiskDeviceHandle = WholeDiskVolume->DeviceHandle;
Bootable = FALSE;
ScanVolumeBootcode(Volume, &Bootable);
if (!Bootable)
Volume->HasBootCode = FALSE;
Volumes.AddReference(Volume, false);
// AddListElement((void ***) &Volumes, &VolumesCount, Volume);
}
}
}
gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)SectorBuffer, 1);
}
void ScanVolumes(void)
{
EFI_STATUS Status;
UINTN HandleCount = 0;
UINTN HandleIndex;
EFI_HANDLE *Handles = NULL;
REFIT_VOLUME *WholeDiskVolume;
UINTN VolumeIndex, VolumeIndex2;
MBR_PARTITION_INFO *MbrTable;
UINTN PartitionIndex;
UINT8 *SectorBuffer1, *SectorBuffer2;
UINTN SectorSum, i;
// EFI_DEVICE_PATH_PROTOCOL *VolumeDevicePath;
// EFI_GUID *Guid; //for debug only
// EFI_INPUT_KEY Key;
// DBG("Scanning volumes...\n");
DbgHeader("ScanVolumes");
// get all BlockIo handles
Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &HandleCount, &Handles);
if (Status == EFI_NOT_FOUND)
return;
DBG("Found %llu volumes with blockIO\n", HandleCount);
// first pass: collect information about all handles
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
REFIT_VOLUME* Volume = new REFIT_VOLUME;
Volume->LegacyOS = new LEGACY_OS;
Volume->DeviceHandle = Handles[HandleIndex];
if (Volume->DeviceHandle == self.getSelfDeviceHandle()) {
SelfVolume = Volume;
}
DBG("- [%02llu]: Volume:", HandleIndex);
Volume->Hidden = FALSE; // default to not hidden
Status = ScanVolume(Volume);
if (!EFI_ERROR(Status)) {
Volumes.AddReference(Volume, false);
for (size_t HVi = 0; HVi < gSettings.HVHideStrings.size(); HVi++) {
if ( Volume->DevicePathString.containsIC(gSettings.HVHideStrings[HVi]) ||
Volume->VolName.containsIC(gSettings.HVHideStrings[HVi])
) {
Volume->Hidden = TRUE;
DBG(" hiding this volume\n");
}
}
// Guid = FindGPTPartitionGuidInDevicePath(Volume->DevicePath);
if (Volume->LegacyOS->IconName.isEmpty()) {
Volume->LegacyOS->IconName = L"legacy"_XSW;
}
// DBG(" Volume '%ls', LegacyOS '%ls', LegacyIcon(s) '%ls', GUID = %s\n",
// Volume->VolName, Volume->LegacyOS->Name ? Volume->LegacyOS->Name : L"", Volume->LegacyOS->IconName, strguid(Guid));
if (SelfVolume == Volume) {
DBG(" This is SelfVolume !!\n");
}
} else {
DBG(" wrong volume Nr%llu?!\n", HandleIndex);
FreePool(Volume);
}
}
FreePool(Handles);
// DBG("Found %d volumes\n", VolumesCount);
if (SelfVolume == NULL){
DBG(" WARNING: SelfVolume not found"); //Slice - and what?
SelfVolume = new REFIT_VOLUME;
SelfVolume->DeviceHandle = self.getSelfDeviceHandle();
SelfVolume->DevicePath = DuplicateDevicePath(&self.getSelfDevicePath());
SelfVolume->RootDir = const_cast<EFI_FILE*>(&self.getSelfVolumeRootDir()); // TODO : SelfVolume->RootDir should be const ! we should duplicate ?
SelfVolume->DiskKind = DISK_KIND_BOOTER;
SelfVolume->VolName = L"Clover"_XSW;
SelfVolume->LegacyOS->Type = OSTYPE_EFI;
SelfVolume->HasBootCode = TRUE;
SelfVolume->BootType = BOOTING_BY_PBR;
// AddListElement((void ***) &Volumes, &VolumesCount, SelfVolume);
// DBG("SelfVolume Nr %d created\n", VolumesCount);
}
// second pass: relate partitions and whole disk devices
for (VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++) {
REFIT_VOLUME* Volume = &Volumes[VolumeIndex];
// check MBR partition table for extended partitions
if (Volume->BlockIO != NULL && Volume->WholeDiskBlockIO != NULL &&
Volume->BlockIO == Volume->WholeDiskBlockIO && Volume->BlockIOOffset == 0 &&
Volume->MbrPartitionTable != NULL) {
DBG(" Volume %llu has MBR\n", VolumeIndex);
MbrTable = Volume->MbrPartitionTable;
for (PartitionIndex = 0; PartitionIndex < 4; PartitionIndex++) {
if (IS_EXTENDED_PART_TYPE(MbrTable[PartitionIndex].Type)) {
ScanExtendedPartition(Volume, MbrTable + PartitionIndex);
}
}
}
// search for corresponding whole disk volume entry
WholeDiskVolume = NULL;
if (Volume->BlockIO != NULL && Volume->WholeDiskBlockIO != NULL &&
Volume->BlockIO != Volume->WholeDiskBlockIO) {
for (VolumeIndex2 = 0; VolumeIndex2 < Volumes.size(); VolumeIndex2++) {
if (Volumes[VolumeIndex2].BlockIO == Volume->WholeDiskBlockIO &&
Volumes[VolumeIndex2].BlockIOOffset == 0)
WholeDiskVolume = &Volumes[VolumeIndex2];
}
}
if (WholeDiskVolume != NULL && WholeDiskVolume->MbrPartitionTable != NULL) {
// check if this volume is one of the partitions in the table
MbrTable = WholeDiskVolume->MbrPartitionTable;
SectorBuffer1 = (__typeof__(SectorBuffer1))AllocateAlignedPages (EFI_SIZE_TO_PAGES (512), 16);
SectorBuffer2 = (__typeof__(SectorBuffer2))AllocateAlignedPages (EFI_SIZE_TO_PAGES (512), 16);
for (PartitionIndex = 0; PartitionIndex < 4; PartitionIndex++) {
// check size
if ((UINT64)(MbrTable[PartitionIndex].Size) != Volume->BlockIO->Media->LastBlock + 1)
continue;
// compare boot sector read through offset vs. directly
Status = Volume->BlockIO->ReadBlocks(Volume->BlockIO, Volume->BlockIO->Media->MediaId,
Volume->BlockIOOffset, 512, SectorBuffer1);
if (EFI_ERROR(Status))
break;
Status = Volume->WholeDiskBlockIO->ReadBlocks(Volume->WholeDiskBlockIO, Volume->WholeDiskBlockIO->Media->MediaId,
MbrTable[PartitionIndex].StartLBA, 512, SectorBuffer2);
if (EFI_ERROR(Status))
break;
if (CompareMem(SectorBuffer1, SectorBuffer2, 512) != 0)
continue;
SectorSum = 0;
for (i = 0; i < 512; i++)
SectorSum += SectorBuffer1[i];
if (SectorSum < 1000)
continue;
// TODO: mark entry as non-bootable if it is an extended partition
// now we're reasonably sure the association is correct...
Volume->IsMbrPartition = TRUE;
Volume->MbrPartitionTable = MbrTable;
Volume->MbrPartitionIndex = PartitionIndex;
if (Volume->VolName.isEmpty())
Volume->VolName = SWPrintf("Partition %llu", PartitionIndex + 1);
break;
}
gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)SectorBuffer1, 1);
gBS->FreePages((EFI_PHYSICAL_ADDRESS)(UINTN)SectorBuffer2, 1);
}
}
}
static void UninitVolumes(void)
{
REFIT_VOLUME *Volume;
UINTN VolumeIndex;
for (VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++) {
Volume = &Volumes[VolumeIndex];
if (Volume->RootDir != NULL) {
Volume->RootDir->Close(Volume->RootDir);
Volume->RootDir = NULL;
}
Volume->DeviceHandle = NULL;
Volume->BlockIO = NULL;
Volume->WholeDiskBlockIO = NULL;
Volume->WholeDiskDeviceHandle = NULL;
FreePool(Volume);
}
Volumes.setEmpty();
}
void ReinitVolumes(void)
{
EFI_STATUS Status;
REFIT_VOLUME *Volume;
UINTN VolumeIndex;
UINTN VolumesFound = 0;
const EFI_DEVICE_PATH *RemainingDevicePath;
EFI_HANDLE DeviceHandle, WholeDiskHandle;
for (VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++) {
Volume = &Volumes[VolumeIndex];
if (!Volume) {
continue;
}
DBG("Volume %llu at reinit found:\n", VolumeIndex);
DBG("Volume->DevicePath=%ls\n", FileDevicePathToXStringW(Volume->DevicePath).wc_str());
VolumesFound++;
if (Volume->DevicePath != NULL) {
// get the handle for that path
RemainingDevicePath = Volume->DevicePath;
Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, const_cast<EFI_DEVICE_PATH**>(&RemainingDevicePath), &DeviceHandle);
if (!EFI_ERROR(Status)) {
Volume->DeviceHandle = DeviceHandle;
// get the root directory
Volume->RootDir = EfiLibOpenRoot(Volume->DeviceHandle);
}
//else
// CheckError(Status, L"from LocateDevicePath");
}
if (Volume->WholeDiskDevicePath != NULL) {
// get the handle for that path
RemainingDevicePath = DuplicateDevicePath(Volume->WholeDiskDevicePath);
Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, const_cast<EFI_DEVICE_PATH**>(&RemainingDevicePath), &WholeDiskHandle);
if (!EFI_ERROR(Status)) {
Volume->WholeDiskBlockIO = (__typeof__(Volume->WholeDiskBlockIO))WholeDiskHandle;
// get the BlockIO protocol
Status = gBS->HandleProtocol(WholeDiskHandle, &gEfiBlockIoProtocolGuid, (void **) &Volume->WholeDiskBlockIO);
if (EFI_ERROR(Status)) {
Volume->WholeDiskBlockIO = NULL;
CheckError(Status, L"from HandleProtocol");
}
}
//else
// CheckError(Status, L"from LocateDevicePath");
}
}
// Jief : I'm not sure to understand the next line. Why would we change the count when we didn't change the array.
// This code is not currently not used.
// Beware if you want to reuse this.
// VolumesCount = VolumesFound;
}
REFIT_VOLUME *FindVolumeByName(IN CONST CHAR16 *VolName)
{
REFIT_VOLUME *Volume;
UINTN VolumeIndex;
if (!VolName) {
return NULL;
}
for (VolumeIndex = 0; VolumeIndex < Volumes.size(); VolumeIndex++) {
Volume = &Volumes[VolumeIndex];
if (!Volume) {
continue;
}
if (Volume->VolName.equal(VolName) == 0) {
return Volume;
}
}
return NULL;
}
//
// file and dir functions
//
BOOLEAN FileExists(IN CONST EFI_FILE *Root, IN CONST CHAR16 *RelativePath)
{
EFI_STATUS Status;
EFI_FILE *TestFile = NULL;
Status = Root->Open(Root, &TestFile, RelativePath, EFI_FILE_MODE_READ, 0);
if (Status == EFI_SUCCESS) {
if (TestFile && TestFile->Close) {
TestFile->Close(TestFile);
}
return TRUE;
}
return FALSE;
}
BOOLEAN FileExists(const EFI_FILE *Root, const XStringW& RelativePath)
{
return FileExists(Root, RelativePath.wc_str());
}
BOOLEAN FileExists(const EFI_FILE& Root, const XStringW& RelativePath)
{
return FileExists(&Root, RelativePath.wc_str());
}
BOOLEAN DeleteFile(const EFI_FILE *Root, IN CONST CHAR16 *RelativePath)
{
EFI_STATUS Status;
EFI_FILE *File;
EFI_FILE_INFO *FileInfo;
//DBG("DeleteFile: %ls\n", RelativePath);
// open file for read/write to see if it exists, need write for delete
Status = Root->Open(Root, &File, RelativePath, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
//DBG(" Open: %s\n", efiStrError(Status));
if (Status == EFI_SUCCESS) {
// exists - check if it is a file
FileInfo = EfiLibFileInfo(File);
if (FileInfo == NULL) {
// error
//DBG(" FileInfo is NULL\n");
File->Close(File);
return FALSE;
}
//DBG(" FileInfo attr: %hhX\n", FileInfo->Attribute);
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY) {
// it's directory - return error
//DBG(" File is DIR\n");
FreePool(FileInfo);
File->Close(File);
return FALSE;
}
FreePool(FileInfo);
// it's a file - delete it
//DBG(" File is file\n");
Status = File->Delete(File);
//DBG(" Delete: %s\n", efiStrError(Status));
return Status == EFI_SUCCESS;
}
return FALSE;
}
EFI_STATUS DirNextEntry(const EFI_FILE *Directory, IN OUT EFI_FILE_INFO **DirEntry, IN UINTN FilterMode)
{
EFI_STATUS Status;
void *Buffer;
UINTN LastBufferSize, BufferSize;
INTN IterCount;
for (;;) {
// free pointer from last call
if (*DirEntry != NULL) {
FreePool(*DirEntry);
*DirEntry = NULL;
}
// read next directory entry
LastBufferSize = BufferSize = 256;
Buffer = (__typeof__(Buffer))AllocateZeroPool(BufferSize);
for (IterCount = 0; ; IterCount++) {
Status = Directory->Read(Directory, &BufferSize, Buffer);
if (Status != EFI_BUFFER_TOO_SMALL || IterCount >= 4)
break;
if (BufferSize <= LastBufferSize) {
DBG("FS Driver requests bad buffer size %llu (was %llu), using %llu instead\n", BufferSize, LastBufferSize, LastBufferSize * 2);
BufferSize = LastBufferSize * 2;
#if REFIT_DEBUG > 0
} else {
DBG("Reallocating buffer from %llu to %llu\n", LastBufferSize, BufferSize);
#endif
}
Buffer = (__typeof__(Buffer))EfiReallocatePool(Buffer, LastBufferSize, BufferSize);
LastBufferSize = BufferSize;
}
if (EFI_ERROR(Status)) {
FreePool(Buffer);
break;
}
// check for end of listing
if (BufferSize == 0) { // end of directory listing
FreePool(Buffer);
break;
}
// entry is ready to be returned
*DirEntry = (EFI_FILE_INFO *)Buffer;
if (*DirEntry) {
// filter results
if (FilterMode == 1) { // only return directories
if (((*DirEntry)->Attribute & EFI_FILE_DIRECTORY))
break;
} else if (FilterMode == 2) { // only return files
if (((*DirEntry)->Attribute & EFI_FILE_DIRECTORY) == 0)
break;
} else // no filter or unknown filter -> return everything
break;
}
}
return Status;
}
void DirIterOpen(const EFI_FILE *BaseDir, IN CONST CHAR16 *RelativePath OPTIONAL, OUT REFIT_DIR_ITER *DirIter)
{
if (RelativePath == NULL) {
DirIter->LastStatus = EFI_SUCCESS;
DirIter->DirHandle = BaseDir;
DirIter->CloseDirHandle = FALSE;
} else {
DirIter->LastStatus = BaseDir->Open(BaseDir, const_cast<EFI_FILE**>(&(DirIter->DirHandle)), RelativePath, EFI_FILE_MODE_READ, 0);
DirIter->CloseDirHandle = EFI_ERROR(DirIter->LastStatus) ? FALSE : TRUE;
}
DirIter->LastFileInfo = NULL;
}
BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CONST CHAR16 *FilePattern OPTIONAL,
OUT EFI_FILE_INFO **DirEntry)
{
if (DirIter->LastFileInfo != NULL) {
FreePool(DirIter->LastFileInfo);
DirIter->LastFileInfo = NULL;
}
if (EFI_ERROR(DirIter->LastStatus))
return FALSE; // stop iteration
for (;;) {
DirIter->LastStatus = DirNextEntry(DirIter->DirHandle, &(DirIter->LastFileInfo), FilterMode);
if (EFI_ERROR(DirIter->LastStatus))
return FALSE;
if (DirIter->LastFileInfo == NULL) // end of listing
return FALSE;
if (FilePattern != NULL) {
if ((DirIter->LastFileInfo->Attribute & EFI_FILE_DIRECTORY))
break;
if (MetaiMatch(DirIter->LastFileInfo->FileName, FilePattern))
break;
// else continue loop
} else
break;
}
*DirEntry = DirIter->LastFileInfo;
return TRUE;
}
EFI_STATUS DirIterClose(IN OUT REFIT_DIR_ITER *DirIter)
{
if (DirIter->LastFileInfo != NULL) {
FreePool(DirIter->LastFileInfo);
DirIter->LastFileInfo = NULL;
}
if (DirIter->CloseDirHandle)
DirIter->DirHandle->Close(DirIter->DirHandle);
return DirIter->LastStatus;
}
//
// file name manipulation
//
BOOLEAN
MetaiMatch (
IN CONST CHAR16 *String,
IN CONST CHAR16 *Pattern
)
{
if (!mUnicodeCollation) {
// quick fix for driver loading on UEFIs without UnicodeCollation
//return FALSE;
return TRUE; //this is wrong anyway
}
return mUnicodeCollation->MetaiMatch (mUnicodeCollation, String, Pattern);
}
EFI_STATUS
InitializeUnicodeCollationProtocol (void)
{
EFI_STATUS Status;
if (mUnicodeCollation != NULL) {
return EFI_SUCCESS;
}
//
// BUGBUG: Proper impelmentation is to locate all Unicode Collation Protocol
// instances first and then select one which support English language.
// Current implementation just pick the first instance.
//
Status = gBS->LocateProtocol (
&gEfiUnicodeCollation2ProtocolGuid,
NULL,
(void **) &mUnicodeCollation
);
if (EFI_ERROR(Status)) {
Status = gBS->LocateProtocol (
&gEfiUnicodeCollationProtocolGuid,
NULL,
(void **) &mUnicodeCollation
);
}
return Status;
}
CONST CHAR16 * Basename(IN CONST CHAR16 *Path)
{
CONST CHAR16 *FileName;
UINTN i;
FileName = Path;
if (Path != NULL) {
for (i = StrLen(Path); i > 0; i--) {
if (Path[i-1] == '\\' || Path[i-1] == '/') {
FileName = Path + i;
break;
}
}
}
return FileName;
}
void ReplaceExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension)
{
INTN i;
for (i = StrLen(Path); i >= 0; i--) {
if (Path[i] == '.') {
Path[i] = 0;
break;
}
if (Path[i] == '\\' || Path[i] == '/')
break;
}
StrCatS(Path, StrLen(Path)/sizeof(CHAR16)+1, Extension);
}
CHAR16 * egFindExtension(IN CHAR16 *FileName)
{
INTN i;
for (i = StrLen(FileName); i >= 0; i--) {
if (FileName[i] == '.')
return FileName + i + 1;
if (FileName[i] == '/' || FileName[i] == '\\')
break;
}
return FileName + StrLen(FileName);
}
//
// memory string search
//
INTN FindMem(IN CONST void *Buffer, IN UINTN BufferLength, IN CONST void *SearchString, IN UINTN SearchStringLength)
{
CONST UINT8 *BufferPtr;
UINTN Offset;
BufferPtr = (CONST UINT8 *)Buffer;
BufferLength -= SearchStringLength;
for (Offset = 0; Offset < BufferLength; Offset++, BufferPtr++) {
if (CompareMem(BufferPtr, SearchString, SearchStringLength) == 0)
return (INTN)Offset;
}
return -1;
}
/**
This function converts an input device structure to a Unicode string.
@param DevPath A pointer to the device path structure.
@return A new allocated Unicode string that represents the device path.
**/
XStringW DevicePathToXStringW (
const EFI_DEVICE_PATH_PROTOCOL *DevPath
)
{
CHAR16* DevicePathStr = ConvertDevicePathToText (DevPath, TRUE, TRUE);
XStringW returnValue;
returnValue.stealValueFrom(DevicePathStr); // do not FreePool FilePath, it's now owned by returnValue
return returnValue;
}
//
// Aptio UEFI returns File DevPath as 2 nodes (dir, file)
// and DevicePathToStr connects them with /, but we need '\\'
XStringW FileDevicePathToXStringW(const EFI_DEVICE_PATH_PROTOCOL *DevPath)
{
CHAR16 *FilePath;
CHAR16 *Char;
CONST CHAR16 *Tail;
FilePath = ConvertDevicePathToText(DevPath, TRUE, TRUE);
// fix / into '\\'
if (FilePath != NULL) {
for (Char = FilePath; *Char != L'\0'; Char++) {
if (*Char == L'/') {
*Char = L'\\';
}
}
}
// "\\\\" into '\\'
Char = (CHAR16*)StrStr(FilePath, L"\\\\"); // cast is ok because FilePath is not const, and we know that StrStr returns a pointer in FilePath. Will disappear when using a string object instead of CHAR16*
while (Char != NULL) {
// StrCpyS(Char, 4, Char + 1); //can't overlap
Tail = Char + 1;
while (*Char != 0) {
*(Char++) = *(Tail++);
}
Char = (CHAR16*)StrStr(FilePath, L"\\\\"); // cast is ok because FilePath is not const, and we know that StrStr returns a pointer in FilePath. Will disappear when using a string object instead of CHAR16*
}
XStringW returnValue;
returnValue.stealValueFrom(FilePath); // do not FreePool FilePath, it's now owned by returnValue
return returnValue;
}
XStringW FileDevicePathFileToXStringW(const EFI_DEVICE_PATH_PROTOCOL *DevPath)
{
EFI_DEVICE_PATH_PROTOCOL *Node;
if (DevPath == NULL) {
return NullXStringW;
}
Node = (EFI_DEVICE_PATH_PROTOCOL *)DevPath;
while (!IsDevicePathEnd(Node)) {
if ((Node->Type == MEDIA_DEVICE_PATH) &&
(Node->SubType == MEDIA_FILEPATH_DP)) {
return FileDevicePathToXStringW(Node);
}
Node = NextDevicePathNode(Node);
}
return NullXStringW;
}
BOOLEAN DumpVariable(CHAR16* Name, EFI_GUID* Guid, INTN DevicePathAt)
{
UINTN dataSize = 0;
UINT8 *data = NULL;
UINTN i;
EFI_STATUS Status;
Status = gRT->GetVariable (Name, Guid, NULL, &dataSize, data);
if (Status == EFI_BUFFER_TOO_SMALL) {
data = (__typeof__(data))AllocateZeroPool(dataSize);
Status = gRT->GetVariable (Name, Guid, NULL, &dataSize, data);
if (EFI_ERROR(Status)) {
DBG("Can't get %ls, size=%llu\n", Name, dataSize);
FreePool(data);
data = NULL;
} else {
DBG("%ls var size=%llu\n", Name, dataSize);
for (i = 0; i < dataSize; i++) {
DBG("%02hhX ", data[i]);
}
DBG("\n");
if (DevicePathAt >= 0) {
DBG("%ls: %ls\n", Name, FileDevicePathToXStringW((EFI_DEVICE_PATH_PROTOCOL*)&data[DevicePathAt]).wc_str());
}
}
}
if (data) {
FreePool(data);
return TRUE;
}
return FALSE;
}
void DbgHeader(CONST CHAR8 *str)
{
CHAR8 strLog[50];
INTN len;
UINTN end = snprintf(strLog, 50, "=== [ %s ] ", str);
len = 50 - end;
SetMem(&strLog[end], len , '=');
strLog[49] = '\0';
DebugLog (1, "%s\n", strLog);
}
// EOF