causing the whole page to be blank on 800x600 displays.
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
</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"—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—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>
</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>
<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—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>
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>
<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>
// 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) {
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
// 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.
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) {
ClearGptData(GptData);
NumTables = 0;
} // if/else
- Print(L"In AddPartitionTable(), total number of tables is %d\n", NumTables);
- PauseForKey();
} // VOID AddPartitionTable()
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) {
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);
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");
#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"");
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))