From 87bc9afcef9d2fc27b8bcff46fa9c88e8962fcae Mon Sep 17 00:00:00 2001 From: srs5694 Date: Mon, 17 Dec 2012 19:12:52 -0500 Subject: [PATCH] Fixed screen-clearing bug; display fs type & size rather than blank when fs has no label; tweaks to graphics mode-setting code --- NEWS.txt | 15 ++- docs/refind/todo.html | 7 ++ libeg/screen.c | 83 +++++++--------- refind/global.h | 12 ++- refind/lib.c | 220 +++++++++++++++++++++++++++++++++--------- refind/main.c | 2 +- refind/screen.c | 9 +- 7 files changed, 241 insertions(+), 107 deletions(-) diff --git a/NEWS.txt b/NEWS.txt index 18712e8..5561a43 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -1,4 +1,17 @@ -0.5.2 (12/??/2012): +0.6.1 (12/??/2012): +------------------- + +- Instead of displaying a blank filesystem label when a filesystem has + none, rEFInd now displays the size of the filesystem, as in "boot + EFI\foo\bar.efi from 200 MiB volume" rather than "boot EFI\foo\bar.efi + from". For some filesystems, the filesystem type is included, as in "boot + EFI\foo\bar.efi from 200 MiB ext3 volume". + +- Fixed a bug that caused the screen to clear after displaying an error + message but before displaying the "Hit any key to continue" message when + a boot loader launch failed. + +0.6.0 (12/16/2012): ------------------- - Fixed a memory allocation bug that could cause a program crash when diff --git a/docs/refind/todo.html b/docs/refind/todo.html index 459f766..d890e43 100644 --- a/docs/refind/todo.html +++ b/docs/refind/todo.html @@ -165,6 +165,13 @@ href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com

from the partition data is harder than extracting the volume's label or counting up the filesystem numbers. +
  • Currently, if a filesystem's label comes up empty, rEFInd + substitutes the size, so you get displays like boot + EFI\foo\bar.efi from 90 GiB volume. I'd like to add more + checks to substitute the GPT partition label if the + filesystem label comes up empty, or add a filesystem type + identifier to the size.
  • +
  • The default_selection option in refind.conf could be improved by supporting a list of default options, so that if the first item isn't found, rEFInd will try to boot the second one in the list, diff --git a/libeg/screen.c b/libeg/screen.c index 90988cf..bb55c08 100644 --- a/libeg/screen.c +++ b/libeg/screen.c @@ -46,16 +46,6 @@ #define LibLocateProtocol EfiLibLocateProtocol #endif -// Scan for at least this many text-mode and graphics-mode display modes, even -// if the first modes are invalid. The number of text modes scanned must be at -// least 3 on some computers to properly list all the available modes when the -// user specifies an invalid value, since mode 1 (numbered from 0) is often -// invalid, but mode 2 (the third one) is usually valid. AFAIK, gaps never -// occur in graphics modes, although I've heard of one case that makes me think -// some exceptions may occur. -#define MIN_SCAN_TEXT_MODES 3 -#define MIN_SCAN_GRAPHICS_MODES 1 - // Console defines and variables static EFI_GUID ConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; @@ -130,36 +120,39 @@ BOOLEAN egSetScreenSize(IN OUT UINTN *ScreenWidth, IN OUT UINTN *ScreenHeight) { if (GraphicsOutput != NULL) { // GOP mode (UEFI) if (*ScreenHeight == 0) { // User specified a mode number (stored in *ScreenWidth); use it directly - ModeNum = (UINT32) *ScreenWidth; - Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); - if (Status == EFI_SUCCESS) { - Status = refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum); - if (Status == EFI_SUCCESS) { - ModeSet = TRUE; - *ScreenWidth = Info->HorizontalResolution; - *ScreenHeight = Info->VerticalResolution; - } - } - } else { // User specified width & height; must find mode - // Do a loop through the modes to see if the specified one is available; - // and if so, switch to it.... - do { + if (*ScreenWidth == GraphicsOutput->Mode->Mode) { // user requested current mode; do nothing + ModeSet = TRUE; + } else { + ModeNum = (UINT32) *ScreenWidth; Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); - if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info)) && - (Info->HorizontalResolution == *ScreenWidth) && (Info->VerticalResolution == *ScreenHeight)) { + if (Status == EFI_SUCCESS) { Status = refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum); - ModeSet = (Status == EFI_SUCCESS); - } // if - } while (((++ModeNum < MIN_SCAN_GRAPHICS_MODES) || (Status == EFI_SUCCESS)) && !ModeSet); -// while ((Status == EFI_SUCCESS) && (!ModeSet)) { -// Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); -// if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info)) && -// (Info->HorizontalResolution == *ScreenWidth) && (Info->VerticalResolution == *ScreenHeight)) { -// Status = refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum); -// ModeSet = (Status == EFI_SUCCESS); -// } // if -// ModeNum++; -// } // while() + if (Status == EFI_SUCCESS) { + ModeSet = TRUE; + *ScreenWidth = Info->HorizontalResolution; + *ScreenHeight = Info->VerticalResolution; + } // if set mode OK + } // if queried mode OK + } // if/else + + // User specified width & height; must find mode -- but only if change is required.... + } else { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, GraphicsOutput->Mode->Mode, &Size, &Info); + if ((Status == EFI_SUCCESS) && (Info->HorizontalResolution == *ScreenWidth) && + (Info->VerticalResolution == *ScreenHeight)) { + ModeSet = TRUE; // user requested current mode; do nothing + } else { + // Do a loop through the modes to see if the specified one is available; + // and if so, switch to it.... + do { + Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); + if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info)) && + (Info->HorizontalResolution == *ScreenWidth) && (Info->VerticalResolution == *ScreenHeight)) { + Status = refit_call2_wrapper(GraphicsOutput->SetMode, GraphicsOutput, ModeNum); + ModeSet = (Status == EFI_SUCCESS); + } // if + } while ((++ModeNum < GraphicsOutput->Mode->MaxMode) && !ModeSet); + } // if/else } // if/else if (ModeSet) { @@ -174,15 +167,7 @@ BOOLEAN egSetScreenSize(IN OUT UINTN *ScreenWidth, IN OUT UINTN *ScreenHeight) { if ((Status == EFI_SUCCESS) && (Info != NULL)) { Print(L"Mode %d: %d x %d\n", ModeNum, Info->HorizontalResolution, Info->VerticalResolution); } // else - } while ((++ModeNum < MIN_SCAN_GRAPHICS_MODES) || (Status == EFI_SUCCESS)); -// Status = EFI_SUCCESS; -// while (Status == EFI_SUCCESS) { -// Status = refit_call4_wrapper(GraphicsOutput->QueryMode, GraphicsOutput, ModeNum, &Size, &Info); -// if ((Status == EFI_SUCCESS) && (Size >= sizeof(*Info))) { -// Print(L"Mode %d: %d x %d\n", ModeNum, Info->HorizontalResolution, Info->VerticalResolution); -// } // else -// ModeNum++; -// } // while() + } while (++ModeNum < GraphicsOutput->Mode->MaxMode); PauseForKey(); SwitchToGraphics(); } // if() @@ -231,8 +216,8 @@ UINT32 egSetTextMode(UINT32 RequestedMode) { do { Status = refit_call4_wrapper(ST->ConOut->QueryMode, ST->ConOut, i, &Width, &Height); if (Status == EFI_SUCCESS) - Print(L"Mode: %d: %d x %d\n", i, Width, Height); - } while ((++i < MIN_SCAN_TEXT_MODES) || (Status == EFI_SUCCESS)); + Print(L"Mode %d: %d x %d\n", i, Width, Height); + } while (++i < ST->ConOut->Mode->MaxMode); PauseForKey(); SwitchToGraphics(); diff --git a/refind/global.h b/refind/global.h index a2db177..1d4a184 100644 --- a/refind/global.h +++ b/refind/global.h @@ -110,8 +110,15 @@ #define DEVICE_TYPE_BIOS 0x05 /* returned by legacy (BIOS) boot loaders */ #define DEVICE_TYPE_END 0x75 /* end of path */ -//TODO: may want to make this configurable via config file -//static UINT16 SupportedLegacyDevices[] = {BBS_HARDDISK, BBS_CDROM, BBS_USB}; +// Filesystem type identifiers. Not all are yet used.... +#define FS_TYPE_UNKNOWN 0 +#define FS_TYPE_FAT 1 +#define FS_TYPE_EXT2 2 +#define FS_TYPE_EXT3 3 +#define FS_TYPE_EXT4 4 +#define FS_TYPE_HFSPLUS 5 +#define FS_TYPE_REISERFS 6 +#define FS_TYPE_ISO9660 7 // // global definitions @@ -152,6 +159,7 @@ typedef struct { EFI_DEVICE_PATH *WholeDiskDevicePath; MBR_PARTITION_INFO *MbrPartitionTable; BOOLEAN IsReadable; + UINT32 FSType; } REFIT_VOLUME; typedef struct _refit_menu_entry { diff --git a/refind/lib.c b/refind/lib.c index 4eb8378..91a409b 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -64,6 +64,15 @@ EFI_DEVICE_PATH EndDevicePath[] = { //#define EndDevicePath DevicePath #endif +// "Magic" signatures for various filesystems +#define FAT_MAGIC 0xAA55 +#define EXT2_SUPER_MAGIC 0xEF53 +#define HFSPLUS_MAGIC1 0x2B48 +#define HFSPLUS_MAGIC2 0x5848 +#define REISERFS_SUPER_MAGIC 0x52654973 +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + // variables EFI_HANDLE SelfImageHandle; @@ -131,39 +140,6 @@ VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) { } // if allocation OK } // CleanUpPathNameSlashes() -// VOID CleanUpPathNameSlashes(IN OUT CHAR16 *PathName) { -// CHAR16 *NewName; -// UINTN i, FinalChar = 0; -// BOOLEAN LastWasSlash = FALSE; -// -// NewName = AllocateZeroPool(sizeof(CHAR16) * (StrLen(PathName) + 4)); -// if (NewName != NULL) { -// for (i = 0; i < StrLen(PathName); i++) { -// if ((PathName[i] == L'/') || (PathName[i] == L'\\')) { -// if ((!LastWasSlash) /* && (FinalChar != 0) */) -// NewName[FinalChar++] = L'\\'; -// LastWasSlash = TRUE; -// } else { -// if (FinalChar == 0) { -// NewName[FinalChar++] = L'\\'; -// } -// NewName[FinalChar++] = PathName[i]; -// LastWasSlash = FALSE; -// } // if/else -// } // for -// NewName[FinalChar] = 0; -// if ((FinalChar > 1) && (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); -// } // if allocation OK -// } // CleanUpPathNameSlashes() - // Splits an EFI device path into device and filename components. For instance, if InString is // PciRoot(0x0)/Pci(0x1f,0x2)/Ata(Secondary,Master,0x0)/HD(2,GPT,8314ae90-ada3-48e9-9c3b-09a88f80d921,0x96028,0xfa000)/\bzImage-3.5.1.efi, // this function will truncate that input to @@ -402,6 +378,83 @@ VOID ExtractLegacyLoaderPaths(EFI_DEVICE_PATH **PathList, UINTN MaxPaths, EFI_DE // volume functions // +// Return a pointer to a string containing a filesystem type name. If the +// filesystem type is unknown, a blank (but non-null) string is returned. +// The returned variable is a constant that should NOT be freed. +static CHAR16 *FSTypeName(IN UINT32 TypeCode) { + CHAR16 *retval = NULL; + + switch (TypeCode) { + case FS_TYPE_FAT: + retval = L" FAT"; + break; + case FS_TYPE_HFSPLUS: + retval = L" HFS+"; + break; + case FS_TYPE_EXT2: + retval = L" ext2"; + break; + case FS_TYPE_EXT3: + retval = L" ext3"; + break; + case FS_TYPE_EXT4: + retval = L" ext4"; + break; + case FS_TYPE_REISERFS: + retval = L" ReiserFS"; + break; + case FS_TYPE_ISO9660: + retval = L" ISO-9660"; + break; + default: + retval = L""; + break; + } // switch + return retval; +} // CHAR16 *FSTypeName() + +// Identify the filesystem type, if possible. Expects a Buffer containing +// the first few (normally 4096) bytes of the filesystem, and outputs a +// code representing the identified filesystem type. +static UINT32 IdentifyFilesystemType(IN UINT8 *Buffer, IN UINTN BufferSize) { + UINT32 FoundType = FS_TYPE_UNKNOWN; + UINT32 *Ext2Incompat, *Ext2Compat; + UINT16 *Magic16; + + if (Buffer != NULL) { + + if (BufferSize >= (1024 + 100)) { + Magic16 = (UINT16*) (Buffer + 1024 + 56); + if (*Magic16 == EXT2_SUPER_MAGIC) { // ext2/3/4 + Ext2Compat = (UINT32*) (Buffer + 1024 + 92); + Ext2Incompat = (UINT32*) (Buffer + 1024 + 96); + if ((*Ext2Incompat & 0x0040) || (*Ext2Incompat & 0x0200)) { // check for extents or flex_bg + return FS_TYPE_EXT4; + } else if (*Ext2Compat & 0x0004) { // check for journal + return FS_TYPE_EXT3; + } else { // none of these features; presume it's ext2... + return FS_TYPE_EXT2; + } + } + } // search for ext2/3/4 magic + + if (BufferSize >= 512) { + Magic16 = (UINT16*) (Buffer + 510); + if (*Magic16 == FAT_MAGIC) + return FS_TYPE_FAT; + } // search for FAT magic + + if (BufferSize >= (1024 + 2)) { + Magic16 = (UINT16*) (Buffer + 1024); + if ((*Magic16 == HFSPLUS_MAGIC1) || (*Magic16 == HFSPLUS_MAGIC2)) { + return FS_TYPE_HFSPLUS; + } + } // search for HFS+ magic + } // if (Buffer != NULL) + + return FoundType; +} + static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootable) { EFI_STATUS Status; @@ -426,6 +479,7 @@ static VOID ScanVolumeBootcode(IN OUT REFIT_VOLUME *Volume, OUT BOOLEAN *Bootabl Volume->BlockIOOffset, SECTOR_SIZE, SectorBuffer); if (!EFI_ERROR(Status)) { + Volume->FSType = IdentifyFilesystemType(SectorBuffer, SECTOR_SIZE); if (*((UINT16 *)(SectorBuffer + 510)) == 0xaa55 && SectorBuffer[0] != 0) { *Bootable = TRUE; Volume->HasBootCode = TRUE; @@ -574,6 +628,90 @@ static VOID ScanVolumeDefaultIcon(IN OUT REFIT_VOLUME *Volume) } // switch() } +// Return a string representing the input size in IEEE-1541 units. +// The calling function is responsible for freeing the allocated memory. +static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) { + float SizeInIeee; + UINTN Index = 0; + CHAR16 *Units, *Prefixes = L" KMGTPEZ"; + CHAR16 *TheValue; + + TheValue = AllocateZeroPool(sizeof(CHAR16) * 80); + if (TheValue != NULL) { + SizeInIeee = (float) SizeInBytes; + while ((SizeInIeee > 1024.0) && (Index < (StrLen(Prefixes) - 1))) { + Index++; + SizeInIeee /= 1024.0; + } // while + if (Prefixes[Index] == ' ') { + Units = StrDuplicate(L"-byte"); + } else { + Units = StrDuplicate(L" iB"); + Units[1] = Prefixes[Index]; + } // if/else + SPrint(TheValue, 79, L"%d%s", (UINTN) SizeInIeee, Units); + } // if + return TheValue; +} // CHAR16 *SizeInSIUnits() + +// Return a name for the volume. Ideally this should be the label for the +// filesystem it contains, but this function falls back to describing the +// filesystem by size (200 MiB, etc.) and/or type (ext2, HFS+, etc.), if +// this information can be extracted. +// The calling function is responsible for freeing the memory allocated +// for the name string. +static CHAR16 *GetVolumeName(IN REFIT_VOLUME *Volume) { + EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; + CHAR16 *FoundName = NULL; + CHAR16 *SISize, *TypeName; + + FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); + if (FileSystemInfoPtr != NULL) { + if ((FileSystemInfoPtr->VolumeLabel != NULL) && (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { + FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); + } + + // Special case: rEFInd HFS+ driver always returns label of "HFS+ volume", so wipe + // this so that we can build a new name that includes the size.... + if ((FoundName != NULL) && (StrCmp(FoundName, L"HFS+ volume") == 0) && (Volume->FSType == FS_TYPE_HFSPLUS)) { + MyFreePool(FoundName); + FoundName = NULL; + } // if rEFInd HFS+ driver suspected + + if (FoundName == NULL) { // filesystem has no name.... + FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); + if (FoundName != NULL) { + SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); + SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); + MyFreePool(SISize); + } // if allocated memory OK + } // if (FoundName == NULL) + + FreePool(FileSystemInfoPtr); + + } else { + FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); + if (FoundName != NULL) { + TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; fn returns constant + if (StrLen(TypeName) > 0) + SPrint(FoundName, 255, L"%s volume", FSTypeName(Volume->FSType)); + else + SPrint(FoundName, 255, L"unknown volume"); + } // if allocated memory OK + } // if + + // TODO: Above could be improved/extended, in case filesystem name is not found, + // such as: + // - use partition label + // - use or add disk/partition number (e.g., "(hd0,2)") + + // Desperate fallback name.... + if (FoundName == NULL) { + FoundName = StrDuplicate(L"unknown volume"); + } + return FoundName; +} // static CHAR16 *GetVolumeName() + VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) { EFI_STATUS Status; @@ -581,7 +719,6 @@ VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) EFI_DEVICE_PATH *DiskDevicePath, *RemainingDevicePath; EFI_HANDLE WholeDiskHandle; UINTN PartialLength; - EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; BOOLEAN Bootable; // get device path @@ -694,20 +831,7 @@ VOID ScanVolume(IN OUT REFIT_VOLUME *Volume) Volume->IsReadable = TRUE; } - // get volume name - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - if (FileSystemInfoPtr != NULL) { - Volume->VolName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); - 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 - // - name derived from file system type or partition type + Volume->VolName = GetVolumeName(Volume); // get custom volume icon if present if (FileExists(Volume->RootDir, VOLUME_BADGE_NAME)) diff --git a/refind/main.c b/refind/main.c index ab6efe6..ea8c337 100644 --- a/refind/main.c +++ b/refind/main.c @@ -118,7 +118,7 @@ static VOID AboutrEFInd(VOID) if (AboutMenu.EntryCount == 0) { AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT); - AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.0"); + AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.0.1"); AddMenuInfoLine(&AboutMenu, L""); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer"); AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith"); diff --git a/refind/screen.c b/refind/screen.c index 4656e01..7f485a4 100644 --- a/refind/screen.c +++ b/refind/screen.c @@ -193,9 +193,6 @@ VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title) DrawScreenHeader(Title); } // if/else - // show the header -// DrawScreenHeader(Title); - if (!UseGraphicsMode) SwitchToText(TRUE); @@ -205,9 +202,6 @@ VOID BeginExternalScreen(IN BOOLEAN UseGraphicsMode, IN CHAR16 *Title) VOID FinishExternalScreen(VOID) { - // Reset the screen resolution, in case external program changed it.... - SetupScreen(); - // make sure we clean up later GraphicsScreenDirty = TRUE; @@ -216,6 +210,9 @@ VOID FinishExternalScreen(VOID) PauseForKey(); } + // Reset the screen resolution, in case external program changed it.... + SetupScreen(); + // reset error flag haveError = FALSE; } -- 2.39.2