#include "icns.h"
#include "screen.h"
#include "refit_call_wrapper.h"
+#include "RemovableMedia.h"
// variables
// self recognition stuff
//
-// Converts forward slashes to backslashes and removes duplicate slashes.
+// Converts forward slashes to backslashes, removes duplicate slashes, and
+// removes slashes from both the start and end of the pathname.
// Necessary because some (buggy?) EFI implementations produce "\/" strings
-// in pathnames.
-static VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) {
+// in pathnames, because some user inputs can produce duplicate directory
+// separators, and because we want consistent start and end slashes for
+// directory comparisons. A special case: If the PathName refers to root,
+// return "/", since some firmware implementations flake out if this
+// isn't present.
+VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) {
CHAR16 *NewName;
- UINTN i, j = 0;
+ UINTN i, FinalChar = 0;
BOOLEAN LastWasSlash = FALSE;
- NewName = AllocateZeroPool(sizeof(CHAR16) * (StrLen(PathName) + 1));
+ NewName = AllocateZeroPool(sizeof(CHAR16) * (StrLen(PathName) + 2));
if (NewName != NULL) {
for (i = 0; i < StrLen(PathName); i++) {
if ((PathName[i] == L'/') || (PathName[i] == L'\\')) {
- if (!LastWasSlash)
- NewName[j++] = L'\\';
+ if ((!LastWasSlash) && (FinalChar != 0))
+ NewName[FinalChar++] = L'\\';
LastWasSlash = TRUE;
} else {
- NewName[j++] = PathName[i];
+ NewName[FinalChar++] = PathName[i];
LastWasSlash = FALSE;
} // if/else
} // for
- NewName[j] = 0;
+ NewName[FinalChar] = 0;
+ if ((FinalChar > 0) && (NewName[FinalChar - 1] == L'\\'))
+ NewName[--FinalChar] = 0;
+ if (FinalChar == 0) {
+ NewName[0] = L'\\';
+ NewName[1] = 0;
+ }
// Copy the transformed name back....
StrCpy(PathName, NewName);
FreePool(NewName);
{
EFI_STATUS Status;
CHAR16 *DevicePathAsString;
- UINTN i;
SelfImageHandle = ImageHandle;
Status = refit_call3_wrapper(BS->HandleProtocol, SelfImageHandle, &LoadedImageProtocol, (VOID **) &SelfLoadedImage);
// find the current directory
DevicePathAsString = DevicePathToStr(SelfLoadedImage->FilePath);
CleanUpPathNameSlashes(DevicePathAsString);
- if (DevicePathAsString != NULL) {
- for (i = StrLen(DevicePathAsString); (i > 0) && (DevicePathAsString[i] != '\\'); i--) ;
- DevicePathAsString[i] = 0;
- } else
- DevicePathAsString[0] = 0;
- SelfDirPath = StrDuplicate(DevicePathAsString);
+ if (SelfDirPath != NULL)
+ FreePool(SelfDirPath);
+ SelfDirPath = FindPath(DevicePathAsString);
FreePool(DevicePathAsString);
return FinishInitRefitLib();
{
ReinitVolumes();
- // Below two lines were in rEFIt, but seem to cause problems on
- // most systems. OTOH, my Mac Mini produces (apparently harmless)
- // errors about "(re)opening our installation volume" (see the
- // next function) when returning from programs when these two lines
- // are removed. On the gripping hand, the Mac SOMETIMES crashes
- // when launching a second program even with these lines removed.
- // TODO: Figure out cause of above weirdness and fix it more
- // reliably!
- /* if (SelfVolume != NULL && SelfVolume->RootDir != NULL)
- SelfRootDir = SelfVolume->RootDir; */
+ if ((ST->Hdr.Revision >> 16) == 1) {
+ // Below two lines were in rEFIt, but seem to cause system crashes or
+ // reboots when launching OSes after returning from programs on most
+ // systems. OTOH, my Mac Mini produces errors about "(re)opening our
+ // installation volume" (see the next function) when returning from
+ // programs when these two lines are removed, and it often crashes
+ // when returning from a program or when launching a second program
+ // with these lines removed. Therefore, the preceding if() statement
+ // executes these lines only on EFIs with a major version number of 1
+ // (which Macs have) and not with 2 (which UEFI PCs have). My selection
+ // of hardware on which to test is limited, though, so this may be the
+ // wrong test, or there may be a better way to fix this problem.
+ // TODO: Figure out cause of above weirdness and fix it more
+ // reliably!
+ if (SelfVolume != NULL && SelfVolume->RootDir != NULL)
+ SelfRootDir = SelfVolume->RootDir;
+ } // if
return FinishInitRefitLib();
}
VOID CreateList(OUT VOID ***ListPtr, OUT UINTN *ElementCount, IN UINTN InitialElementCount)
{
UINTN AllocateCount;
-
+
*ElementCount = InitialElementCount;
if (*ElementCount > 0) {
AllocateCount = (*ElementCount + 7) & ~7; // next multiple of 8
VOID FreeList(IN OUT VOID ***ListPtr, IN OUT UINTN *ElementCount)
{
UINTN i;
-
+
if (*ElementCount > 0) {
for (i = 0; i < *ElementCount; i++) {
// TODO: call a user-provided routine for each element here
EFI_LOADED_IMAGE *LoadedImage;
EFI_DEVICE_PATH *DevicePath;
BOOLEAN Seen;
-
+
MaxPaths--; // leave space for the terminating NULL pointer
-
+
// get all LoadedImage handles
Status = LibLocateHandle(ByProtocol, &LoadedImageProtocol, NULL,
&HandleCount, &Handles);
}
for (HandleIndex = 0; HandleIndex < HandleCount && PathCount < MaxPaths; HandleIndex++) {
Handle = Handles[HandleIndex];
-
+
Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &LoadedImageProtocol, (VOID **) &LoadedImage);
if (EFI_ERROR(Status))
continue; // This can only happen if the firmware scewed up, ignore it.
-
+
Status = refit_call3_wrapper(BS->HandleProtocol, LoadedImage->DeviceHandle, &DevicePathProtocol, (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;
}
if (Seen)
continue;
-
+
PathList[PathCount++] = AppendDevicePath(DevicePath, LegacyLoaderMediaPath);
}
FreePool(Handles);
-
+
if (HardcodedPathList) {
for (HardcodedIndex = 0; HardcodedPathList[HardcodedIndex] && PathCount < MaxPaths; HardcodedIndex++)
PathList[PathCount++] = HardcodedPathList[HardcodedIndex];
UINTN i;
MBR_PARTITION_INFO *MbrTable;
BOOLEAN MbrTableFound;
-
+
Volume->HasBootCode = FALSE;
Volume->OSIconName = NULL;
Volume->OSName = NULL;
*Bootable = FALSE;
-
+
if (Volume->BlockIO == NULL)
return;
if (Volume->BlockIO->Media->BlockSize > SECTOR_SIZE)
return; // our buffer is too small...
-
+
// look at the boot sector (this is used for both hard disks and El Torito images!)
Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks,
Volume->BlockIO, Volume->BlockIO->Media->MediaId,
Volume->BlockIOOffset, SECTOR_SIZE, SectorBuffer);
if (!EFI_ERROR(Status)) {
-
+
if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55 && SectorBuffer[0] != 0) {
*Bootable = TRUE;
Volume->HasBootCode = TRUE;
}
-
+
// detect specific boot codes
if (CompareMem(SectorBuffer + 2, "LILO", 4) == 0 ||
CompareMem(SectorBuffer + 6, "LILO", 4) == 0 ||
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"linux";
Volume->OSName = L"Linux";
-
+
} else if (FindMem(SectorBuffer, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) { // GRUB
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"grub,linux";
Volume->OSName = L"Linux";
-
+
+// // Below doesn't produce a bootable entry, so commented out for the moment....
+// // GRUB in BIOS boot partition:
+// } else if (FindMem(SectorBuffer, 512, "Geom\0Read\0 Error", 16) >= 0) {
+// Volume->HasBootCode = TRUE;
+// Volume->OSIconName = L"grub,linux";
+// Volume->OSName = L"Linux";
+// Volume->VolName = L"BIOS Boot Partition";
+// *Bootable = TRUE;
+
} else if ((*((UINT32 *)(SectorBuffer + 502)) == 0 &&
*((UINT32 *)(SectorBuffer + 506)) == 50000 &&
*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) ||
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"freebsd";
Volume->OSName = L"FreeBSD";
-
+
} else if (FindMem(SectorBuffer, 512, "!Loading", 8) >= 0 ||
FindMem(SectorBuffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"openbsd";
Volume->OSName = L"OpenBSD";
-
+
} else if (FindMem(SectorBuffer, 512, "Not a bootxx image", 18) >= 0 ||
*((UINT32 *)(SectorBuffer + 1028)) == 0x7886b6d1) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"netbsd";
Volume->OSName = L"NetBSD";
-
+
} else if (FindMem(SectorBuffer, SECTOR_SIZE, "NTLDR", 5) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"win";
Volume->OSName = L"Windows";
-
+
} else if (FindMem(SectorBuffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"winvista,win";
Volume->OSName = L"Windows";
-
+
} else if (FindMem(SectorBuffer, 512, "CPUBOOT SYS", 11) >= 0 ||
FindMem(SectorBuffer, 512, "KERNEL SYS", 11) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"freedos";
Volume->OSName = L"FreeDOS";
-
+
} else if (FindMem(SectorBuffer, 512, "OS2LDR", 6) >= 0 ||
FindMem(SectorBuffer, 512, "OS2BOOT", 7) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"ecomstation";
Volume->OSName = L"eComStation";
-
+
} else if (FindMem(SectorBuffer, 512, "Be Boot Loader", 14) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"beos";
Volume->OSName = L"BeOS";
-
+
} else if (FindMem(SectorBuffer, 512, "yT Boot Loader", 14) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSIconName = L"zeta,beos";
Volume->OSName = L"ZETA";
-
+
} else if (FindMem(SectorBuffer, 512, "\x04" "beos\x06" "system\x05" "zbeos", 18) >= 0 ||
FindMem(SectorBuffer, 512, "\x06" "system\x0c" "haiku_loader", 20) >= 0) {
Volume->HasBootCode = TRUE;
Volume->OSName = L"Haiku";
}
-
+
// 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
Print(L" Result of bootcode detection: %s %s (%s)\n",
Volume->HasBootCode ? L"bootable" : L"non-bootable",
Volume->OSName, Volume->OSIconName);
#endif
-
- if (FindMem(SectorBuffer, 512, "Non-system disk", 15) >= 0) // dummy FAT boot sector
+
+ // dummy FAT boot sector (created by OS X's newfs_msdos)
+ if (FindMem(SectorBuffer, 512, "Non-system disk", 15) >= 0)
+ Volume->HasBootCode = FALSE;
+
+ // dummy FAT boot sector (created by Linux's mkdosfs)
+ if (FindMem(SectorBuffer, 512, "This is not a bootable disk", 27) >= 0)
Volume->HasBootCode = FALSE;
-
+
+ // dummy FAT boot sector (created by Windows)
+ if (FindMem(SectorBuffer, 512, "Press any key to restart", 24) >= 0)
+ Volume->HasBootCode = FALSE;
+
// check for MBR partition table
if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55) {
MbrTableFound = FALSE;
}
}
-// default volume icon based on disk kind
+// default volume badge icon based on disk kind
static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume)
{
switch (Volume->DiskKind) {
// open the root directory of the volume
Volume->RootDir = LibOpenRoot(Volume->DeviceHandle);
if (Volume->RootDir == NULL) {
+ Volume->IsReadable = FALSE;
return;
+ } else {
+ Volume->IsReadable = TRUE;
}
// get volume name
FreePool(FileSystemInfoPtr);
}
+ if (Volume->VolName == NULL) {
+ Volume->VolName = StrDuplicate(L"Unknown");
+ }
// TODO: if no official volume name is found or it is empty, use something else, e.g.:
// - name from bytes 3 to 10 of the boot sector
// - partition number
BOOLEAN DirIterNext(IN OUT REFIT_DIR_ITER *DirIter, IN UINTN FilterMode, IN CHAR16 *FilePattern OPTIONAL,
OUT EFI_FILE_INFO **DirEntry)
{
+ BOOLEAN KeepGoing = TRUE;
+ UINTN i;
+ CHAR16 *OnePattern;
+
if (DirIter->LastFileInfo != NULL) {
FreePool(DirIter->LastFileInfo);
DirIter->LastFileInfo = NULL;
if (EFI_ERROR(DirIter->LastStatus))
return FALSE; // stop iteration
- for (;;) {
+ do {
DirIter->LastStatus = DirNextEntry(DirIter->DirHandle, &(DirIter->LastFileInfo), FilterMode);
if (EFI_ERROR(DirIter->LastStatus))
- return FALSE;
+ 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;
+ KeepGoing = FALSE;
+ i = 0;
+ while (KeepGoing && (OnePattern = FindCommaDelimited(FilePattern, i++)) != NULL) {
+ if (MetaiMatch(DirIter->LastFileInfo->FileName, OnePattern))
+ KeepGoing = FALSE;
+ } // while
// else continue loop
} else
break;
- }
+ } while (KeepGoing);
*DirEntry = DirIter->LastFileInfo;
return TRUE;
return FileName;
}
-VOID ReplaceExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension)
+// Replaces a filename extension of ".efi" with the specified string
+// (Extension). If the input Path doesn't end in ".efi", Extension
+// is added to the existing filename.
+VOID ReplaceEfiExtension(IN OUT CHAR16 *Path, IN CHAR16 *Extension)
{
- UINTN i;
+ UINTN PathLen;
- for (i = StrLen(Path); i >= 0; i--) {
- if (Path[i] == '.') {
- Path[i] = 0;
- break;
- }
- if (Path[i] == '\\' || Path[i] == '/')
- break;
- }
+ PathLen = StrLen(Path);
+ // Note: Do StriCmp() twice to work around Gigabyte Hybrid EFI case-sensitivity bug....
+ if ((PathLen >= 4) && ((StriCmp(&Path[PathLen - 4], L".efi") == 0) || (StriCmp(&Path[PathLen - 4], L".EFI") == 0))) {
+ Path[PathLen - 4] = 0;
+ } // if
StrCat(Path, Extension);
-}
+} // VOID ReplaceEfiExtension()
//
// memory string search
if (Second != NULL)
Length2 = StrLen(Second);
NewString = AllocatePool(sizeof(CHAR16) * (Length1 + Length2 + 2));
- NewString[0] = L'\0';
if (NewString != NULL) {
+ NewString[0] = L'\0';
if (*First != NULL) {
StrCat(NewString, *First);
if (AddChar) {
NewString[Length1 + 1] = 0;
} // if (AddChar)
} // if (*First != NULL)
- if (First != NULL)
+ if (Second != NULL)
StrCat(NewString, Second);
FreePool(*First);
*First = NewString;
} // if/else
} // static CHAR16* MergeStrings()
+// Takes an input pathname (*Path) and returns the part of the filename from
+// the final dot onwards, converted to lowercase. If the filename includes
+// no dots, or if the input is NULL, returns an empty (but allocated) string.
+// The calling function is responsible for freeing the memory associated with
+// the return value.
+CHAR16 *FindExtension(IN CHAR16 *Path) {
+ CHAR16 *Extension;
+ BOOLEAN Found = FALSE, FoundSlash = FALSE;
+ UINTN i;
+
+ Extension = AllocateZeroPool(sizeof(CHAR16));
+ if (Path) {
+ i = StrLen(Path);
+ while ((!Found) && (!FoundSlash) && (i >= 0)) {
+ if (Path[i] == L'.')
+ Found = TRUE;
+ else if ((Path[i] == L'/') || (Path[i] == L'\\'))
+ FoundSlash = TRUE;
+ if (!Found)
+ i--;
+ } // while
+ if (Found) {
+ MergeStrings(&Extension, &Path[i], 0);
+ StrLwr(Extension);
+ } // if (Found)
+ } // if
+ return (Extension);
+} // CHAR16 *FindExtension
+
// Takes an input pathname (*Path) and locates the final directory component
// of that name. For instance, if the input path is 'EFI\foo\bar.efi', this
// function returns the string 'foo'.
// Find the #Index element (numbered from 0) in a comma-delimited string
// of elements.
-// Returns the found element, or NULL if Index is out of range of InString
-// is NULL.
+// Returns the found element, or NULL if Index is out of range or InString
+// is NULL. Note that the calling function is responsible for freeing the
+// memory associated with the returned string pointer.
CHAR16 *FindCommaDelimited(IN CHAR16 *InString, IN UINTN Index) {
UINTN StartPos = 0, CurPos = 0;
BOOLEAN Found = FALSE;
else
CurPos++;
} // while
+ if (Index == 0)
+ FoundString = StrDuplicate(&InString[StartPos]);
+ if (FoundString != NULL)
+ FoundString[CurPos - StartPos] = 0;
} // if
- if (Index == 0)
- FoundString = StrDuplicate(&InString[StartPos]);
- if (FoundString != NULL)
- FoundString[CurPos - StartPos] = 0;
return (FoundString);
} // CHAR16 *FindCommaDelimited()
+
+
+static EFI_GUID AppleRemovableMediaGuid = APPLE_REMOVABLE_MEDIA_PROTOCOL_GUID;
+
+// Eject all removable media.
+// Returns TRUE if any media were ejected, FALSE otherwise.
+BOOLEAN EjectMedia(VOID) {
+ EFI_STATUS Status;
+ UINTN HandleIndex, HandleCount = 0, Ejected = 0;
+ EFI_HANDLE *Handles, Handle;
+ APPLE_REMOVABLE_MEDIA_PROTOCOL *Ejectable;
+
+ Status = LibLocateHandle(ByProtocol, &AppleRemovableMediaGuid, NULL, &HandleCount, &Handles);
+ if (EFI_ERROR(Status) || HandleCount == 0)
+ return (FALSE); // probably not an Apple system
+
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+ Handle = Handles[HandleIndex];
+ Status = refit_call3_wrapper(BS->HandleProtocol, Handle, &AppleRemovableMediaGuid, (VOID **) &Ejectable);
+ if (EFI_ERROR(Status))
+ continue;
+ Status = refit_call1_wrapper(Ejectable->Eject, Ejectable);
+ if (!EFI_ERROR(Status))
+ Ejected++;
+ }
+ FreePool(Handles);
+ return (Ejected > 0);
+} // VOID EjectMedia()