/* * 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 // Only use angled for Platform, else, xcode project won't compile #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" #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; XObjArray Volumes; // // 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; iLocateDevicePath (&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.getCloverDirPathAsXStringW().wc_str()); return EFI_SUCCESS; } void UninitRefitLib(void) { // called before running external programs to close open file handles ThemeX.closeThemeDir(); self.closeHandle(); selfOem.closeHandle(); UninitVolumes(); } EFI_STATUS ReinitRefitLib(void) { // called after reconnect drivers to re-open file handles self.reInitialize(); selfOem.reInitialize(); 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 } if ( Volume->ApfsFileSystemUUID.notEmpty() ) { DBG(" apfsFileSystemUUID=%s\n", Volume->ApfsFileSystemUUID.c_str()); } #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 ( 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); } //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(&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(&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(&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(IN CONST EFI_FILE *Root, IN 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(&(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