]> code.delx.au - refind/commitdiff
Moved legacy functions from refind/main.c to their own file.
authorsrs5694 <srs5694@users.sourceforge.net>
Tue, 3 Feb 2015 17:26:07 +0000 (12:26 -0500)
committersrs5694 <srs5694@users.sourceforge.net>
Tue, 3 Feb 2015 17:26:07 +0000 (12:26 -0500)
refind/legacy.c [new file with mode: 0644]
refind/legacy.h [new file with mode: 0644]

diff --git a/refind/legacy.c b/refind/legacy.c
new file mode 100644 (file)
index 0000000..be79259
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * refind/legacy.c
+ * Functions related to BIOS/CSM/legacy booting
+ *
+ * Copyright (c) 2006 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.
+ */
+/*
+ * Modifications copyright (c) 2012-2015 Roderick W. Smith
+ *
+ * Modifications distributed under the terms of the GNU General Public
+ * License (GPL) version 3 (GPLv3), a copy of which must be distributed
+ * with this source code or binaries made from it.
+ *
+ */
+
+#include "global.h"
+#include "icns.h"
+#include "legacy.h"
+#include "lib.h"
+#include "menu.h"
+#include "refit_call_wrapper.h"
+#include "screen.h"
+#include "syslinux_mbr.h"
+#include "../EfiLib/BdsHelper.h"
+#include "../EfiLib/legacy.h"
+
+extern REFIT_MENU_ENTRY MenuEntryReturn;
+extern REFIT_MENU_SCREEN MainMenu;
+
+static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
+{
+    EFI_STATUS          Status;
+    UINT8               SectorBuffer[512];
+    MBR_PARTITION_INFO  *MbrTable, *EMbrTable;
+    UINT32              ExtBase, ExtCurrent, NextExtCurrent;
+    UINTN               LogicalPartitionIndex = 4;
+    UINTN               i;
+    BOOLEAN             HaveBootCode;
+
+    // read MBR
+    Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
+    if (EFI_ERROR(Status))
+        return Status;
+    if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
+        return EFI_NOT_FOUND;  // safety measure #1
+
+    // add boot code if necessary
+    HaveBootCode = FALSE;
+    for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
+        if (SectorBuffer[i] != 0) {
+            HaveBootCode = TRUE;
+            break;
+        }
+    }
+    if (!HaveBootCode) {
+        // no boot code found in the MBR, add the syslinux MBR code
+        SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
+        CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
+    }
+
+    // set the partition active
+    MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
+    ExtBase = 0;
+    for (i = 0; i < 4; i++) {
+        if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
+            return EFI_NOT_FOUND;   // safety measure #2
+        if (i == PartitionIndex)
+            MbrTable[i].Flags = 0x80;
+        else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
+            MbrTable[i].Flags = 0x80;
+            ExtBase = MbrTable[i].StartLBA;
+        } else
+            MbrTable[i].Flags = 0x00;
+    }
+
+    // write MBR
+    Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
+    if (EFI_ERROR(Status))
+        return Status;
+
+    if (PartitionIndex >= 4) {
+        // we have to activate a logical partition, so walk the EMBR chain
+
+        // NOTE: ExtBase was set above while looking at the MBR table
+        for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
+            // read current EMBR
+            Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
+            if (EFI_ERROR(Status))
+                return Status;
+            if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
+                return EFI_NOT_FOUND;  // safety measure #3
+
+            // scan EMBR, set appropriate partition active
+            EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
+            NextExtCurrent = 0;
+            for (i = 0; i < 4; i++) {
+                if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
+                    return EFI_NOT_FOUND;   // safety measure #4
+                if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
+                    break;
+                if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
+                    // link to next EMBR
+                    NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
+                    EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
+                    break;
+                } else {
+                    // logical partition
+                    EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
+                    LogicalPartitionIndex++;
+                }
+            }
+
+            // write current EMBR
+            Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
+            if (EFI_ERROR(Status))
+                return Status;
+
+            if (PartitionIndex < LogicalPartitionIndex)
+                break;  // stop the loop, no need to touch further EMBRs
+        }
+
+    }
+
+    return EFI_SUCCESS;
+} /* static EFI_STATUS ActivateMbrPartition() */
+
+static EFI_GUID AppleVariableVendorID = { 0x7C436110, 0xAB2A, 0x4BBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82 };
+
+static EFI_STATUS WriteBootDiskHint(IN EFI_DEVICE_PATH *WholeDiskDevicePath)
+{
+   EFI_STATUS          Status;
+
+   Status = refit_call5_wrapper(RT->SetVariable, L"BootCampHD", &AppleVariableVendorID,
+                                EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                                GetDevicePathSize(WholeDiskDevicePath), WholeDiskDevicePath);
+   if (EFI_ERROR(Status))
+      return Status;
+
+   return EFI_SUCCESS;
+}
+
+// early 2006 Core Duo / Core Solo models
+static UINT8 LegacyLoaderDevicePath1Data[] = {
+    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
+    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
+    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
+};
+// mid-2006 Mac Pro (and probably other Core 2 models)
+static UINT8 LegacyLoaderDevicePath2Data[] = {
+    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
+    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
+    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
+};
+// mid-2007 MBP ("Santa Rosa" based models)
+static UINT8 LegacyLoaderDevicePath3Data[] = {
+    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
+    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
+    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
+};
+// early-2008 MBA
+static UINT8 LegacyLoaderDevicePath4Data[] = {
+    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
+    0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
+    0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
+};
+// late-2008 MB/MBP (NVidia chipset)
+static UINT8 LegacyLoaderDevicePath5Data[] = {
+    0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
+    0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+    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 *LegacyLoaderList[] = {
+    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
+    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
+    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
+    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
+    (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
+    NULL
+};
+
+#define MAX_DISCOVERED_PATHS (16)
+
+VOID StartLegacy(IN LEGACY_ENTRY *Entry, IN CHAR16 *SelectionName)
+{
+    EFI_STATUS          Status;
+    EG_IMAGE            *BootLogoImage;
+    UINTN               ErrorInStep = 0;
+    EFI_DEVICE_PATH     *DiscoveredPathList[MAX_DISCOVERED_PATHS];
+
+    BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
+
+    BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
+    if (BootLogoImage != NULL)
+        BltImageAlpha(BootLogoImage,
+                      (UGAWidth  - BootLogoImage->Width ) >> 1,
+                      (UGAHeight - BootLogoImage->Height) >> 1,
+                      &StdBackgroundPixel);
+
+    if (Entry->Volume->IsMbrPartition)
+        ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
+
+    if (Entry->Volume->DiskKind != DISK_KIND_OPTICAL && Entry->Volume->WholeDiskDevicePath != NULL)
+       WriteBootDiskHint(Entry->Volume->WholeDiskDevicePath);
+
+    ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
+
+    StoreLoaderName(SelectionName);
+    Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE, FALSE);
+    if (Status == EFI_NOT_FOUND) {
+        if (ErrorInStep == 1) {
+            Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
+        } else if (ErrorInStep == 3) {
+            Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
+                  L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
+        }
+    }
+    FinishExternalScreen();
+} /* static VOID StartLegacy() */
+
+// Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
+VOID StartLegacyUEFI(LEGACY_ENTRY *Entry, CHAR16 *SelectionName)
+{
+    BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
+    StoreLoaderName(SelectionName);
+
+    BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
+    BdsLibDoLegacyBoot(Entry->BdsOption);
+
+    // If we get here, it means that there was a failure....
+    Print(L"Failure booting legacy (BIOS) OS.");
+    PauseForKey();
+    FinishExternalScreen();
+} // static VOID StartLegacyUEFI()
+
+static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
+{
+    LEGACY_ENTRY            *Entry, *SubEntry;
+    REFIT_MENU_SCREEN       *SubScreen;
+    CHAR16                  *VolDesc, *LegacyTitle;
+    CHAR16                  ShortcutLetter = 0;
+
+    if (LoaderTitle == NULL) {
+        if (Volume->OSName != NULL) {
+            LoaderTitle = Volume->OSName;
+            if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
+                ShortcutLetter = LoaderTitle[0];
+        } else
+            LoaderTitle = L"Legacy OS";
+    }
+    if (Volume->VolName != NULL)
+        VolDesc = Volume->VolName;
+    else
+        VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
+
+    LegacyTitle = AllocateZeroPool(256 * sizeof(CHAR16));
+    if (LegacyTitle != NULL)
+       SPrint(LegacyTitle, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
+    if (IsInSubstring(LegacyTitle, GlobalConfig.DontScanVolumes)) {
+       MyFreePool(LegacyTitle);
+       return NULL;
+    } // if
+
+    // prepare the menu entry
+    Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
+    Entry->me.Title = LegacyTitle;
+    Entry->me.Tag          = TAG_LEGACY;
+    Entry->me.Row          = 0;
+    Entry->me.ShortcutLetter = ShortcutLetter;
+    Entry->me.Image        = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
+    Entry->me.BadgeImage   = Volume->VolBadgeImage;
+    Entry->Volume          = Volume;
+    Entry->LoadOptions     = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
+                              ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
+    Entry->Enabled         = TRUE;
+
+    // create the submenu
+    SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
+    SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
+    SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
+    SubScreen->TitleImage = Entry->me.Image;
+    SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
+    if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
+       SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
+    } else {
+       SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
+    } // if/else
+
+    // default entry
+    SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
+    SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
+    SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
+    SubEntry->me.Tag          = TAG_LEGACY;
+    SubEntry->Volume          = Entry->Volume;
+    SubEntry->LoadOptions     = Entry->LoadOptions;
+    AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
+
+    AddMenuEntry(SubScreen, &MenuEntryReturn);
+    Entry->me.SubScreen = SubScreen;
+    AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
+    return Entry;
+} /* static LEGACY_ENTRY * AddLegacyEntry() */
+
+
+/**
+    Create a rEFInd boot option from a Legacy BIOS protocol option.
+*/
+static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
+{
+    LEGACY_ENTRY            *Entry, *SubEntry;
+    REFIT_MENU_SCREEN       *SubScreen;
+    CHAR16                  ShortcutLetter = 0;
+    CHAR16 *LegacyDescription = StrDuplicate(BdsOption->Description);
+
+    if (IsInSubstring(LegacyDescription, GlobalConfig.DontScanVolumes))
+       return NULL;
+
+    // Remove stray spaces, since many EFIs produce descriptions with lots of
+    // extra spaces, especially at the end; this throws off centering of the
+    // description on the screen....
+    LimitStringLength(LegacyDescription, 100);
+
+    // prepare the menu entry
+    Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
+    Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
+    SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
+    Entry->me.Tag          = TAG_LEGACY_UEFI;
+    Entry->me.Row          = 0;
+    Entry->me.ShortcutLetter = ShortcutLetter;
+    Entry->me.Image        = LoadOSIcon(L"legacy", L"legacy", TRUE);
+    Entry->LoadOptions     = (DiskType == BBS_CDROM) ? L"CD" :
+                             ((DiskType == BBS_USB) ? L"USB" : L"HD");
+    Entry->me.BadgeImage   = GetDiskBadge(DiskType);
+    Entry->BdsOption       = BdsOption;
+    Entry->Enabled         = TRUE;
+
+    // create the submenu
+    SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
+    SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
+    SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
+    SubScreen->TitleImage = Entry->me.Image;
+    SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
+    if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
+       SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
+    } else {
+       SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
+    } // if/else
+
+    // default entry
+    SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
+    SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
+    SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
+    SubEntry->me.Tag          = TAG_LEGACY_UEFI;
+    Entry->BdsOption          = BdsOption; 
+    AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
+
+    AddMenuEntry(SubScreen, &MenuEntryReturn);
+    Entry->me.SubScreen = SubScreen;
+    AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
+    return Entry;
+} /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
+
+/**
+    Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
+    In testing, protocol has not been implemented on Macs but has been
+    implemented on most UEFI PCs.
+    Restricts output to disks of the specified DiskType.
+*/
+static VOID ScanLegacyUEFI(IN UINTN DiskType)
+{
+    EFI_STATUS                Status;
+    EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+    UINT16                    *BootOrder = NULL;
+    UINTN                     Index = 0;
+    CHAR16                    BootOption[10];
+    UINTN                     BootOrderSize = 0;
+    CHAR16                    Buffer[20];
+    BDS_COMMON_OPTION         *BdsOption;
+    LIST_ENTRY                TempList;
+    BBS_BBS_DEVICE_PATH       *BbsDevicePath = NULL;
+    BOOLEAN                   SearchingForUsb = FALSE;
+
+    InitializeListHead (&TempList);
+    ZeroMem (Buffer, sizeof (Buffer));
+
+    // If LegacyBios protocol is not implemented on this platform, then
+    //we do not support this type of legacy boot on this machine.
+    Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
+    if (EFI_ERROR (Status))
+       return;
+
+    // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
+    // so we set DiskType inappropriately elsewhere in the program and
+    // "translate" it here.
+    if (DiskType == BBS_USB) {
+       DiskType = BBS_HARDDISK;
+       SearchingForUsb = TRUE;
+    } // if
+
+    // Grab the boot order
+    BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
+    if (BootOrder == NULL) {
+        BootOrderSize = 0;
+    }
+
+    Index = 0;
+    while (Index < BootOrderSize / sizeof (UINT16))
+    {
+        // Grab each boot option variable from the boot order, and convert
+        // the variable into a BDS boot option
+        UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
+        BdsOption = BdsLibVariableToOption (&TempList, BootOption);
+
+        if (BdsOption != NULL) {
+           BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
+           // Only add the entry if it is of a requested type (e.g. USB, HD)
+           // Two checks necessary because some systems return EFI boot loaders
+           // with a DeviceType value that would inappropriately include them
+           // as legacy loaders....
+           if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
+              // USB flash drives appear as hard disks with certain media flags set.
+              // Look for this, and if present, pass it on with the (technically
+              // incorrect, but internally useful) BBS_TYPE_USB flag set.
+              if (DiskType == BBS_HARDDISK) {
+                 if (SearchingForUsb && (BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) {
+                    AddLegacyEntryUEFI(BdsOption, BBS_USB);
+                 } else if (!SearchingForUsb && !(BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) {
+                    AddLegacyEntryUEFI(BdsOption, DiskType);
+                 }
+              } else {
+                 AddLegacyEntryUEFI(BdsOption, DiskType);
+              } // if/else
+           } // if
+        } // if (BdsOption != NULL)
+        Index++;
+    } // while
+} /* static VOID ScanLegacyUEFI() */
+
+static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
+   UINTN VolumeIndex2;
+   BOOLEAN ShowVolume, HideIfOthersFound;
+
+   ShowVolume = FALSE;
+   HideIfOthersFound = FALSE;
+   if (Volume->IsAppleLegacy) {
+      ShowVolume = TRUE;
+      HideIfOthersFound = TRUE;
+   } else if (Volume->HasBootCode) {
+      ShowVolume = TRUE;
+      if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
+         Volume->BlockIOOffset == 0 &&
+         Volume->OSName == NULL)
+         // this is a whole disk (MBR) entry; hide if we have entries for partitions
+         HideIfOthersFound = TRUE;
+   }
+   if (HideIfOthersFound) {
+      // check for other bootable entries on the same disk
+      for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
+         if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
+             Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
+            ShowVolume = FALSE;
+      }
+   }
+
+   if (ShowVolume)
+      AddLegacyEntry(NULL, Volume);
+} // static VOID ScanLegacyVolume()
+
+// Scan attached optical discs for legacy (BIOS) boot code
+// and add anything found to the list....
+VOID ScanLegacyDisc(VOID)
+{
+   UINTN                   VolumeIndex;
+   REFIT_VOLUME            *Volume;
+
+   if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
+      for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
+         Volume = Volumes[VolumeIndex];
+         if (Volume->DiskKind == DISK_KIND_OPTICAL)
+            ScanLegacyVolume(Volume, VolumeIndex);
+      } // for
+   } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
+      ScanLegacyUEFI(BBS_CDROM);
+   }
+} /* VOID ScanLegacyDisc() */
+
+// Scan internal hard disks for legacy (BIOS) boot code
+// and add anything found to the list....
+VOID ScanLegacyInternal(VOID)
+{
+    UINTN                   VolumeIndex;
+    REFIT_VOLUME            *Volume;
+
+    if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
+       for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
+           Volume = Volumes[VolumeIndex];
+           if (Volume->DiskKind == DISK_KIND_INTERNAL)
+               ScanLegacyVolume(Volume, VolumeIndex);
+       } // for
+    } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
+       // TODO: This actually picks up USB flash drives, too; try to find
+       // a way to differentiate the two....
+       ScanLegacyUEFI(BBS_HARDDISK);
+    }
+} /* VOID ScanLegacyInternal() */
+
+// Scan external disks for legacy (BIOS) boot code
+// and add anything found to the list....
+VOID ScanLegacyExternal(VOID)
+{
+   UINTN                   VolumeIndex;
+   REFIT_VOLUME            *Volume;
+
+   if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
+      for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
+         Volume = Volumes[VolumeIndex];
+         if (Volume->DiskKind == DISK_KIND_EXTERNAL)
+            ScanLegacyVolume(Volume, VolumeIndex);
+      } // for
+   } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
+      // TODO: This actually doesn't do anything useful; leaving in hopes of
+      // fixing it later....
+      ScanLegacyUEFI(BBS_USB);
+   }
+} /* VOID ScanLegacyExternal() */
+
+// Determine what (if any) type of legacy (BIOS) boot support is available
+VOID FindLegacyBootType(VOID) {
+   EFI_STATUS                Status;
+   EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+
+   GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
+
+   // UEFI-style legacy BIOS support is available only with some EFI implementations....
+   Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
+   if (!EFI_ERROR (Status))
+      GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
+
+   // Macs have their own system. If the firmware vendor code contains the
+   // string "Apple", assume it's available. Note that this overrides the
+   // UEFI type, and might yield false positives if the vendor string
+   // contains "Apple" as part of something bigger, so this isn't 100%
+   // perfect.
+   if (StriSubCmp(L"Apple", ST->FirmwareVendor))
+      GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
+} // VOID FindLegacyBootType
+
+// Warn the user if legacy OS scans are enabled but the firmware can't support them....
+VOID WarnIfLegacyProblems(VOID) {
+   BOOLEAN  found = FALSE;
+   UINTN    i = 0;
+
+   if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
+      do {
+         if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c' ||
+             GlobalConfig.ScanFor[i] == 'H' || GlobalConfig.ScanFor[i] == 'B' || GlobalConfig.ScanFor[i] == 'C')
+            found = TRUE;
+         i++;
+      } while ((i < NUM_SCAN_OPTIONS) && (!found));
+
+      if (found) {
+         Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
+         Print(L"(BIOS) boot options; however, this is not possible because your computer lacks\n");
+         Print(L"the necessary Compatibility Support Module (CSM) support or that support is\n");
+         Print(L"disabled in your firmware.\n");
+         PauseForKey();
+      } // if (found)
+   } // if no legacy support
+} // VOID WarnIfLegacyProblems()
+
diff --git a/refind/legacy.h b/refind/legacy.h
new file mode 100644 (file)
index 0000000..3da42f2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * refind/legacy.h
+ * Functions related to BIOS/CSM/legacy booting
+ *
+ * Copyright (c) 2006 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.
+ */
+/*
+ * Modifications copyright (c) 2012-2015 Roderick W. Smith
+ *
+ * Modifications distributed under the terms of the GNU General Public
+ * License (GPL) version 3 (GPLv3), a copy of which must be distributed
+ * with this source code or binaries made from it.
+ *
+ */
+
+#include "global.h"
+
+VOID StartLegacy(IN LEGACY_ENTRY *Entry, IN CHAR16 *SelectionName);
+VOID StartLegacyUEFI(LEGACY_ENTRY *Entry, CHAR16 *SelectionName);
+VOID ScanLegacyDisc(VOID);
+VOID ScanLegacyInternal(VOID);
+VOID ScanLegacyExternal(VOID);
+VOID FindLegacyBootType(VOID);
+VOID WarnIfLegacyProblems(VOID);