]> code.delx.au - refind/commitdiff
Limit length of firmware description string in Info screen, to avoid
authorsrs5694 <srs5694@users.sourceforge.net>
Sat, 3 May 2014 18:43:42 +0000 (14:43 -0400)
committersrs5694 <srs5694@users.sourceforge.net>
Sat, 3 May 2014 18:43:42 +0000 (14:43 -0400)
causing the whole page to be blank on 800x600 displays.

NEWS.txt
docs/refind/configfile.html
docs/refind/features.html
refind/gpt.c
refind/lib.c
refind/lib.h
refind/main.c

index eb424b080e5140c425f65085d765302b42698837..1f1024dbfd5ba49c8391458516f9944c2059ab00 100644 (file)
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,6 +1,11 @@
 0.7.10 (?/??/2014):
 -------------------
 
+- rEFInd now limits the length of the firmware name string shown in the
+  system information screen to 65 characters. This is done because at least
+  one EFI presents a longer string by default, and this causes the entire
+  information display to come up empty on 800x600 displays.
+
 - rEFInd now uses the partition's name (as stored in the GPT data
   structures) as a fallback for the filesystem's name if the latter can't
   be found. Exceptions are if the partition name is one of three generic
index 47e166149d7110f03d6e7a8dbf177e210c4a6d27..aa0bc96720cad5180e0ba6a4b40cf034f4ec9274 100644 (file)
@@ -320,7 +320,7 @@ timeout 20
 </tr>
 <tr>
    <td><tt>dont_scan_volumes</tt> or <tt>don't_scan_volumes</tt></td>
-   <td>filesystem label(s)</td>
+   <td>filesystem or partition label(s)</td>
    <td>Adds the specified volume or volumes to a volume "blacklist"&mdash;these filesystems are <i>not</i> scanned for EFI boot loaders. This may be useful to keep unwanted EFI boot entries, such as for a Macintosh recovery partition, from appearing on the main list of boot loaders. The default value is <tt>"Recovery HD", LRS_ESP</tt>, to keep the Mac OS X and Lenovo Windows recovery volumes from appearing. (These should get their own tools icon instead&mdash;see the <tt>showtools</tt> token.) Note that on a Macintosh with whole-disk encryption, you may need to uncomment this token and leave <tt>"Recovery HD"</tt> <i>off</i> the list to boot the system.</td>
 </tr>
 <tr>
@@ -406,8 +406,8 @@ default_selection elilo
 </tr>
 <tr>
    <td><tt>volume</tt></td>
-   <td>filesystem label</td>
-   <td>Sets the volume that's used for subsequent file accesses (by <tt>icon</tt> and <tt>loader</tt>, and by implication by <tt>initrd</tt> if <tt>loader</tt> follows <tt>volume</tt>). You pass this token a filesystem's label or a volume number. A filesystem label is typically displayed under the volume's icon in file managers and that rEFInd displays on its menu at the end of the boot prompt string. If this label isn't unique, the first volume with the specified label is used. The matching is nominally case-insensitive, but on some EFIs it's case-sensitive. If a filesystem has no label, you can use a volume number followed by a colon, such as <tt>0:</tt> to refer to the first filesystem or <tt>1:</tt> to refer to the second. The assignment of numbers is arbitrary and may not be consistent across boots, though. It might change if you insert an optical disc or plug in a USB flash drive, for instance. If this option is not set, the volume defaults to the one from which rEFInd launched.</td>
+   <td>filesystem label, partition label, GUID value, or filesystem number</td>
+   <td>Sets the volume that's used for subsequent file accesses (by <tt>icon</tt> and <tt>loader</tt>, and by implication by <tt>initrd</tt> if <tt>loader</tt> follows <tt>volume</tt>). You pass this token a filesystem's label, a partition's label, a partition's GUID, or a volume number. A filesystem or partition label is typically displayed under the volume's icon in file managers and rEFInd displays it on its menu at the end of the boot prompt string. If this label isn't unique, the first volume with the specified label is used. The matching is nominally case-insensitive, but on some EFIs it's case-sensitive. If a filesystem has no label, you can use a partition GUID number. You can also use a volume number followed by a colon, such as <tt>0:</tt> to refer to the first filesystem or <tt>1:</tt> to refer to the second. The assignment of numbers is arbitrary and may not be consistent across boots, though. It might change if you insert an optical disc or plug in a USB flash drive, for instance. If this option is not set, the volume defaults to the one from which rEFInd launched.</td>
 </tr>
 <tr>
    <td><tt>loader</tt></td>
@@ -476,9 +476,9 @@ menuentry "Windows via shell script" {
 
 <p>This example sets up three entries: one for Ubuntu Linux, one for Gentoo Linux, and one to launch a shell script. Note that the first two entries use different directory separators, simply to demonstrate the fact that it's possible. The Ubuntu entry sets no icon, since rEFInd will note that the boot loader is stored in the <tt>ubuntu</tt> directory, and it will automatically find the appropriate Ubuntu icon (<tt>os_ubuntu.icns</tt>). This option is, however, disabled, so no matching icon will appear when you reboot unless you first comment out or delete the <tt>disabled</tt> line.</p>
 
-<p class="sidebar"><b>Tip:</b> Under Linux, you can learn a filesystem's label by using <tt>blkid</tt>, as in <tt class="userinput">blkid /dev/sda1</tt>. The filesystem's label, if set, is identified by the keyword <tt>LABEL</tt> in the output.</p>
+<p class="sidebar"><b>Tip:</b> Under Linux, you can learn a filesystem's label by using <tt>blkid</tt>, as in <tt class="userinput">blkid /dev/sda1</tt>. The filesystem's label, if set, is identified by the keyword <tt>LABEL</tt> in the output. Some versions also return the partition's label and partition GUID (referred to as <tt>PARTUUID</tt> by <tt>blkid</tt>). You can obtain the partition's name and unique GUID using <tt>sgdisk</tt>, as in <tt class="userinput">sgdisk -i 1 /dev/sda</tt> to find the data on <tt>/dev/sda1</tt>.</p>
 
-<p>The Gentoo entry begins with an icon specification to be sure that the icon is loaded from the same volume as rEFInd. (If the icon were stored on the same filesystem as the kernel, you'd place the <tt>icon</tt> line after the <tt>volume</tt> line.) This entry uses the <tt>volume</tt> token to tell rEFInd to load the kernel and initial RAM disk file from the filesystem called <tt>G_KERNELS</tt>. It passes the filename for an initial RAM disk using the <tt>initrd</tt> line and free-form options using the <tt>options</tt> line. Note that the kernel filename does <i>not</i> include a <tt>.efi</tt> extension, which keeps rEFInd from picking up the kernel file in its auto-scans.</p>
+<p>The Gentoo entry begins with an icon specification to be sure that the icon is loaded from the same volume as rEFInd. (If the icon were stored on the same filesystem as the kernel, you'd place the <tt>icon</tt> line after the <tt>volume</tt> line.) This entry uses the <tt>volume</tt> token to tell rEFInd to load the kernel and initial RAM disk file from the filesystem or partition called <tt>G_KERNELS</tt>. It passes the filename for an initial RAM disk using the <tt>initrd</tt> line and free-form options using the <tt>options</tt> line. Note that the kernel filename does <i>not</i> include a <tt>.efi</tt> extension, which keeps rEFInd from picking up the kernel file in its auto-scans.</p>
 
 <p>The <tt>Windows via shell script</tt> entry may seem puzzling, but its purpose is to launch an OS (Windows in this case) after performing additional pre-boot initialization, which is handled by an EFI shell script. This works because you can pass the name of a shell script to an EFI shell&mdash;the script is named on the stanza's <tt>options</tt> line, using EFI file notation. The shell script, in turn, does whatever it needs to do and then launches the OS's boot loader:</p>
 
index fb2fc79b1b1eb072d3e3ece50962f3900bd9dee5..fc47944809ce224be38622c2aecc24d5c02e433e 100644 (file)
@@ -15,7 +15,7 @@
 href="mailto:rodsmith@rodsbooks.com">rodsmith@rodsbooks.com</a></p>
 
 <p>Originally written: 3/14/2012; last Web page update:
-4/20/2014, referencing rEFInd 0.7.9</p>
+4/20/2014, referencing rEFInd 0.7.10</p>
 
 <p>This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!</p>
 
@@ -183,6 +183,8 @@ lack a usable CSM.</li>
 
 <li>Improved flexibility in setting the default OS to boot. rEFInd enables specifying the default by any substring in the description. You can also specify multiple defaults, so that if the first isn't available, another will take its place (which is useful when using removable disks). You can also add time specifications to set a default to be used only during certain hours of the day.</li>
 
+<li>Support for partition names or GUID values as fallbacks for filesystem labels in certain configuration file settings. Partition names may be shown as values to be displayed as part of the descriptive text for boot tags on the main menu, too, if a filesystem has no label.</li>
+
 <li>The ability to fine-tune options passed to EFI boot loaders, via manual configuration.</li>
 
 <li>An option editor to enable you to edit the options passed to an EFI boot loader on a per-boot basis.</li>
index eb106b7960efa21141ac1a0d9343cbeb9b32712e..83dd6d3fd8e71efdf652bb2a1e790429ccad4116 100644 (file)
@@ -60,19 +60,25 @@ VOID ClearGptData(GPT_DATA *Data) {
 
 // TODO: Make this work on big-endian systems; at the moment, it contains
 // little-endian assumptions!
-// Returns TRUE if the GPT header data appear valid, FALSE otherwise.
+// Returns TRUE if the GPT protective MBR and header data appear valid,
+// FALSE otherwise.
 static BOOLEAN GptHeaderValid(GPT_DATA *GptData) {
    BOOLEAN IsValid;
    UINT32 CrcValue, StoredCrcValue;
    UINTN HeaderSize = sizeof(GPT_HEADER);
 
-   IsValid = ((GptData != NULL) && (GptData->ProtectiveMBR != NULL) && (GptData->Header != NULL));
-   IsValid = IsValid && (GptData->ProtectiveMBR->MBRSignature == 0xAA55);
+   if ((GptData == NULL) || (GptData->ProtectiveMBR == NULL) || (GptData->Header == NULL))
+      return FALSE;
+
+   IsValid = (GptData->ProtectiveMBR->MBRSignature == 0xAA55);
    IsValid = IsValid && ((GptData->ProtectiveMBR->partitions[0].type == 0xEE) ||
                          (GptData->ProtectiveMBR->partitions[1].type == 0xEE) ||
                          (GptData->ProtectiveMBR->partitions[2].type == 0xEE) ||
                          (GptData->ProtectiveMBR->partitions[3].type == 0xEE));
-   IsValid = IsValid && (GptData->Header->signature == 0x5452415020494645ULL);
+
+   IsValid = IsValid && ((GptData->Header->signature == 0x5452415020494645ULL) &&
+                         (GptData->Header->spec_revision == 0x00010000) &&
+                         (GptData->Header->entry_size == 128));
 
    // Looks good so far; check CRC value....
    if (IsValid) {
@@ -89,29 +95,32 @@ static BOOLEAN GptHeaderValid(GPT_DATA *GptData) {
    return IsValid;
 } // BOOLEAN GptHeaderValid()
 
-// Read GPT data from Volume and store it in Data. Note that this function
+// Read GPT data from Volume and store it in *Data. Note that this function
 // may be called on a Volume that is not in fact a GPT disk (an MBR disk,
 // a partition, etc.), in which case it will return EFI_LOAD_ERROR or some
 // other error condition. In this case, *Data will be left alone.
 // Note also that this function checks CRCs and does other sanity checks
 // on the input data, but does NOT resort to using the backup data if the
-// primary data structures are damaged.
+// primary data structures are damaged. The intent is that the function
+// be very conservative about reading GPT data. Currently (version 0.7.10),
+// rEFInd uses the data only to provide access to partition names. This is
+// non-critical data, so it's OK to return nothing, but having the program
+// hang on reading garbage or return nonsense could be very bad.
 EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) {
    EFI_STATUS Status = EFI_SUCCESS;
    UINT64     BufferSize;
-   UINT32     TableCrc;
    UINTN      i;
    GPT_DATA   *GptData; // Temporary holding storage; transferred to *Data later
 
    if ((Volume == NULL) || (Data == NULL))
-      Status = EFI_INVALID_PARAMETER;
+      return EFI_INVALID_PARAMETER;
 
    // get block i/o
    if ((Status == EFI_SUCCESS) && (Volume->BlockIO == NULL)) {
       Status = refit_call3_wrapper(BS->HandleProtocol, Volume->DeviceHandle, &BlockIoProtocol, (VOID **) &(Volume->BlockIO));
       if (EFI_ERROR(Status)) {
          Volume->BlockIO = NULL;
-         Print(L"Warning: Can't get BlockIO protocol.\n");
+         Print(L"Warning: Can't get BlockIO protocol in ReadGptData().\n");
          Status = EFI_NOT_READY;
       }
    } // if
@@ -129,7 +138,7 @@ EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) {
    // Read the MBR and store it in GptData->ProtectiveMBR.
    if (Status == EFI_SUCCESS) {
       Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
-                                   0, 512, (VOID*) GptData->ProtectiveMBR);
+                                   0, sizeof(MBR_RECORD), (VOID*) GptData->ProtectiveMBR);
    }
 
    // Read the GPT header and store it in GptData->Header.
@@ -138,29 +147,22 @@ EFI_STATUS ReadGptData(REFIT_VOLUME *Volume, GPT_DATA **Data) {
                                    1, sizeof(GPT_HEADER), GptData->Header);
    }
 
-   // If it looks like a valid protective MBR, try to do more with it....
+   // If it looks like a valid protective MBR & GPT header, try to do more with it....
    if (Status == EFI_SUCCESS) {
       if (GptHeaderValid(GptData)) {
          // Load actual GPT table....
          BufferSize = GptData->Header->entry_count * 128;
-         if (GptData->Entries != NULL)
-            MyFreePool(GptData->Entries);
          GptData->Entries = AllocatePool(BufferSize);
-         if (GptData->Entries == NULL) {
+         if (GptData->Entries == NULL)
             Status = EFI_OUT_OF_RESOURCES;
-         } // if
 
-         if (Status == EFI_SUCCESS) {
+         if (Status == EFI_SUCCESS)
             Status = refit_call5_wrapper(Volume->BlockIO->ReadBlocks, Volume->BlockIO, Volume->BlockIO->Media->MediaId,
                                          GptData->Header->entry_lba, BufferSize, GptData->Entries);
-         } // if
 
          // Check CRC status of table
-         if (Status == EFI_SUCCESS) {
-            TableCrc = crc32(0x0, GptData->Entries, BufferSize);
-            if (TableCrc != GptData->Header->entry_crc32)
-               Status = EFI_CRC_ERROR;
-         } // if
+         if ((Status == EFI_SUCCESS) && (crc32(0x0, GptData->Entries, BufferSize) != GptData->Header->entry_crc32))
+            Status = EFI_CRC_ERROR;
 
          // Now, ensure that every name is null-terminated....
          if (Status == EFI_SUCCESS) {
@@ -243,7 +245,5 @@ VOID AddPartitionTable(REFIT_VOLUME *Volume) {
       ClearGptData(GptData);
       NumTables = 0;
    } // if/else
-   Print(L"In AddPartitionTable(), total number of tables is %d\n", NumTables);
-   PauseForKey();
 } // VOID AddPartitionTable()
 
index dc1c1b32db62fde0ff1d825ec053da341fbc4a70..0c4089f5ff3fc49268d7bb4d94ba21f5bf8111b6 100644 (file)
@@ -1582,6 +1582,89 @@ CHAR16 *FindPath(IN CHAR16* FullPath) {
    return (PathOnly);
 }
 
+/*++
+ * 
+ * Routine Description:
+ *
+ *  Find a substring.
+ *
+ * Arguments: 
+ *
+ *  String      - Null-terminated string to search.
+ *  StrCharSet  - Null-terminated string to search for.
+ *
+ * Returns:
+ *  The address of the first occurrence of the matching substring if successful, or NULL otherwise.
+ * --*/
+CHAR16* MyStrStr (CHAR16  *String, CHAR16  *StrCharSet)
+{
+   CHAR16 *Src;
+   CHAR16 *Sub;
+
+   if ((String == NULL) || (StrCharSet == NULL))
+      return NULL;
+
+   Src = String;
+   Sub = StrCharSet;
+
+   while ((*String != L'\0') && (*StrCharSet != L'\0')) {
+      if (*String++ != *StrCharSet) {
+         String = ++Src;
+         StrCharSet = Sub;
+      } else {
+         StrCharSet++;
+      }
+   }
+   if (*StrCharSet == L'\0') {
+      return Src;
+   } else {
+      return NULL;
+   }
+} // CHAR16 *MyStrStr()
+
+// Restrict TheString to at most Limit characters.
+// Does this in two ways:
+// - Locates stretches of two or more spaces and compresses
+//   them down to one space.
+// - Truncates TheString
+// Returns TRUE if changes were made, FALSE otherwise
+BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit) {
+   CHAR16    *SubString, *TempString;
+   UINTN     i;
+   BOOLEAN   HasChanged = FALSE;
+
+   // SubString will be NULL or point WITHIN TheString
+   SubString = MyStrStr(TheString, L"  ");
+   while (SubString != NULL) {
+      i = 0;
+      while (SubString[i] == L' ')
+         i++;
+      if (i >= StrLen(SubString)) {
+         SubString[0] = '\0';
+         HasChanged = TRUE;
+      } else {
+         TempString = StrDuplicate(&SubString[i]);
+         if (TempString != NULL) {
+            StrCpy(&SubString[1], TempString);
+            MyFreePool(TempString);
+            HasChanged = TRUE;
+         } else {
+            // memory allocation problem; abort to avoid potentially infinite loop!
+            break;
+         } // if/else
+      } // if/else
+      SubString = MyStrStr(TheString, L"  ");
+   } // while
+
+   // If the string is still too long, truncate it....
+   if (StrLen(TheString) > Limit) {
+      TheString[Limit] = '\0';
+      HasChanged = TRUE;
+   } // if
+
+   return HasChanged;
+} // BOOLEAN LimitStringLength()
+
 // Takes an input loadpath, splits it into disk and filename components, finds a matching
 // DeviceVolume, and returns that and the filename (*loader).
 VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **DeviceVolume, OUT CHAR16 **loader) {
index 8a71249c3264df1e98d03bf531a1f6f1f9bf4c0c..be26dc7b733994cf20067ab329f5c416f84d93d5 100644 (file)
@@ -114,6 +114,7 @@ VOID MergeStrings(IN OUT CHAR16 **First, IN CHAR16 *Second, CHAR16 AddChar);
 CHAR16 *FindExtension(IN CHAR16 *Path);
 CHAR16 *FindLastDirName(IN CHAR16 *Path);
 CHAR16 *FindPath(IN CHAR16* FullPath);
+BOOLEAN LimitStringLength(CHAR16 *TheString, UINTN Limit);
 VOID FindVolumeAndFilename(IN EFI_DEVICE_PATH *loadpath, OUT REFIT_VOLUME **DeviceVolume, OUT CHAR16 **loader);
 BOOLEAN SplitVolumeAndFilename(IN OUT CHAR16 **Path, OUT CHAR16 **VolName);
 CHAR16 *FindNumbers(IN CHAR16 *InString);
index 64fe8985c310f91a51b4ecfd8dcd9b3c5823c56b..dcbb1fdead5f654eb49ccba13454541f6250d79d 100644 (file)
@@ -160,9 +160,11 @@ struct LOADER_LIST {
 
 static VOID AboutrEFInd(VOID)
 {
+    CHAR16 *FirmwareVendor;
+
     if (AboutMenu.EntryCount == 0) {
         AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
-        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.9.4");
+        AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.7.9.5");
         AddMenuInfoLine(&AboutMenu, L"");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
         AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012-2014 Roderick W. Smith");
@@ -179,7 +181,9 @@ static VOID AboutrEFInd(VOID)
 #else
         AddMenuInfoLine(&AboutMenu, L" Platform: unknown");
 #endif
-        AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16,
+        FirmwareVendor = StrDuplicate(ST->FirmwareVendor);
+        LimitStringLength(FirmwareVendor, 65); // More than ~65 causes empty info page on 800x600 display
+        AddMenuInfoLine(&AboutMenu, PoolPrint(L" Firmware: %s %d.%02d", FirmwareVendor, ST->FirmwareRevision >> 16,
                                               ST->FirmwareRevision & ((1 << 16) - 1)));
         AddMenuInfoLine(&AboutMenu, PoolPrint(L" Screen Output: %s", egScreenDescription()));
         AddMenuInfoLine(&AboutMenu, L"");
@@ -1094,7 +1098,7 @@ static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
    UINTN    i = 0;
    BOOLEAN  ScanIt = TRUE;
 
-   if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))
+   if ((IsIn(Volume->VolName, GlobalConfig.DontScanVolumes)) || (IsIn(Volume->PartName, GlobalConfig.DontScanVolumes)))
       return FALSE;
 
    if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))