]> code.delx.au - refind/commitdiff
New files to support legacy BIOS boots on UEFI-based PCs
authorsrs5694 <srs5694@users.sourceforge.net>
Sun, 30 Sep 2012 18:27:16 +0000 (14:27 -0400)
committersrs5694 <srs5694@users.sourceforge.net>
Sun, 30 Sep 2012 18:27:16 +0000 (14:27 -0400)
EfiLib/BdsHelper.c [new file with mode: 0644]
EfiLib/BdsHelper.h [new file with mode: 0644]
EfiLib/BdsTianoCore.c [new file with mode: 0644]

diff --git a/EfiLib/BdsHelper.c b/EfiLib/BdsHelper.c
new file mode 100644 (file)
index 0000000..80eb7f2
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * EfiLib/BdsHelper.c
+ * Functions to call legacy BIOS API.
+ *
+ */
+
+
+#include "BdsHelper.h"
+
+/**
+    Internal helper function.
+
+    Update the BBS Table so that devices of DeviceType have their boot priority
+    updated to a high/bootable value.
+
+    See "DeviceType values" in 
+    http://www.intel.com/content/dam/doc/reference-guide/efi-compatibility-support-module-specification-v097.pdf
+
+    NOTE: This function should probably be refactored! Currently, all devices of
+    type are enabled. This should be updated so that only a specific device is
+    enabled. The wrong device could boot if there are multiple targets of the same
+    type.
+
+    @param DeviceType   The device type that we wish to enable
+**/
+VOID
+UpdateBbsTable (
+  IN UINT16     DeviceType
+  )
+{
+    UINT16  Idx;
+    EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+    EFI_GUID EfiLegacyBootProtocolGuid     = { 0xdb9a1e3d, 0x45cb, 0x4abb, { 0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d }};
+    EFI_STATUS                Status;
+    UINT16                       HddCount = 0;
+    HDD_INFO                     *HddInfo = NULL; 
+    UINT16                       BbsCount = 0; 
+    BBS_TABLE                 *LocalBbsTable = NULL;
+
+    Status = gBS->LocateProtocol (&EfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
+    if (EFI_ERROR (Status)) {
+      return;
+    }
+    
+    Status = LegacyBios->GetBbsInfo (LegacyBios, &HddCount, &HddInfo, &BbsCount, &LocalBbsTable);
+
+    //Print (L"\n");
+    //Print (L" NO  Prio bb/dd/ff cl/sc Type Stat segm:offs\n");
+    //Print (L"=============================================\n");
+
+    for (Idx = 0; Idx < BbsCount; Idx++) 
+    {
+     if(LocalBbsTable[Idx].DeviceType == 0){
+        continue;
+     }
+
+     // Set devices of a particular type to BootPriority of 0. I believe 0 is the highest priority
+     if(LocalBbsTable[Idx].DeviceType == DeviceType){
+        LocalBbsTable[Idx].BootPriority = 0;
+     }
+
+/*
+      Print (
+        L" %02x: %04x %02x/%02x/%02x %02x/%02x %04x %04x %04x:%04x\n",
+        (UINTN) Idx,
+        (UINTN) LocalBbsTable[Idx].BootPriority,
+        (UINTN) LocalBbsTable[Idx].Bus,
+        (UINTN) LocalBbsTable[Idx].Device,
+        (UINTN) LocalBbsTable[Idx].Function,
+        (UINTN) LocalBbsTable[Idx].Class,
+        (UINTN) LocalBbsTable[Idx].SubClass,
+        (UINTN) LocalBbsTable[Idx].DeviceType,
+        (UINTN) * (UINT16 *) &LocalBbsTable[Idx].StatusFlags,
+        (UINTN) LocalBbsTable[Idx].BootHandlerSegment,
+        (UINTN) LocalBbsTable[Idx].BootHandlerOffset,
+        (UINTN) ((LocalBbsTable[Idx].MfgStringSegment << 4) + LocalBbsTable[Idx].MfgStringOffset),
+        (UINTN) ((LocalBbsTable[Idx].DescStringSegment << 4) + LocalBbsTable[Idx].DescStringOffset)
+        );
+*/
+    }
+
+  //Print(L"\n");
+}
+
+// Internal helper function
+BOOLEAN ArrayContains(UINT16* Arr, UINT8 ArrLen, UINT8 Target)
+{
+    UINT8 i;
+    for(i = 0; i < ArrLen; i++)
+    {
+       if(Arr[i] == Target)
+          return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+    True if the DeviceType is supported by rEFInd, false otherwise.
+*/
+BOOLEAN IsBbsDeviceTypeSupported(UINT16 DeviceType)
+{
+    return ArrayContains(SupportedLegacyDevices, sizeof(SupportedLegacyDevices), DeviceType);
+}
+
+/**
+    Boot the legacy system with the boot option
+
+    @param  Option                 The legacy boot option which have BBS device path
+
+    @retval EFI_UNSUPPORTED        There is no legacybios protocol, do not support
+                                 legacy boot.
+    @retval EFI_STATUS             Return the status of LegacyBios->LegacyBoot ().
+
+**/
+EFI_STATUS
+BdsLibDoLegacyBoot (
+  IN  BDS_COMMON_OPTION           *Option
+  )
+{
+    EFI_STATUS                Status;
+    EFI_LEGACY_BIOS_PROTOCOL  *LegacyBios;
+    EFI_GUID EfiLegacyBootProtocolGuid     = { 0xdb9a1e3d, 0x45cb, 0x4abb, { 0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d }};
+    EFI_GUID EfiGlobalVariableGuid     = { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }};
+    BBS_BBS_DEVICE_PATH *OptionBBS;
+
+    Status = gBS->LocateProtocol (&EfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+    OptionBBS = (BBS_BBS_DEVICE_PATH *) Option->DevicePath;
+
+    //Print(L"\n\n");
+    //Print(L"Option->Name='%s'\n", Option->OptionName);
+    //Print(L"Option->Number='%d'\n", Option->OptionNumber);
+    //Print(L"Option->Description='%s'\n", Option->Description);
+    //Print(L"Option->LoadOptionsSize='%d'\n",Option->LoadOptionsSize);
+    //Print(L"Option->BootCurrent='%d'\n",Option->BootCurrent);
+    //Print(L"Option->DevicePath->Type= '%d'\n", Option->DevicePath->Type);
+    //Print(L"Option->DevicePath->SubType= '%d'\n", Option->DevicePath->SubType);
+    //Print(L"Option->DevicePath->Length[0]= '%d'\n", Option->DevicePath->Length[0]);
+    //Print(L"Option->DevicePath->Length[1]= '%d'\n", Option->DevicePath->Length[1]); 
+    //Print(L"OptionBBS->DeviceType='%d'\n",OptionBBS->DeviceType);
+    //Print(L"OptionBBS->StatusFlag='%d'\n",OptionBBS->StatusFlag);
+    //Print(L"OptionBBS->String[0]='%c'\n",OptionBBS->String[0]);
+    //Print(L"About to legacy boot!\n");
+    //PauseForKey();
+
+    UpdateBbsTable(OptionBBS->DeviceType); 
+
+    return LegacyBios->LegacyBoot (LegacyBios, (BBS_BBS_DEVICE_PATH *) Option->DevicePath, Option->LoadOptionsSize, Option->LoadOptions);
+}
+
diff --git a/EfiLib/BdsHelper.h b/EfiLib/BdsHelper.h
new file mode 100644 (file)
index 0000000..6739b85
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * EfiLib/BdsHelper.c
+ * Functions to call legacy BIOS API.
+ *
+ */
+
+#include "../include/tiano_includes.h"
+
+#ifndef _BDS_HELPER_H_
+#define _BDS_HELPER_H_
+
+//TODO: may want to make this configurable via config file
+static UINT16 SupportedLegacyDevices[] = {BBS_HARDDISK, BBS_CDROM, BBS_USB};
+
+
+/**
+  True if the DeviceType is supported by rEFInd, false otherwise.
+*/
+BOOLEAN IsBbsDeviceTypeSupported(UINT16 DeviceType);
+
+
+/**
+  Boot the legacy system with the boot option
+
+  @param  Option                 The legacy boot option which have BBS device path
+
+  @retval EFI_UNSUPPORTED        There is no legacybios protocol, do not support
+                                 legacy boot.
+  @retval EFI_STATUS             Return the status of LegacyBios->LegacyBoot ().
+
+**/
+EFI_STATUS
+BdsLibDoLegacyBoot (
+  IN  BDS_COMMON_OPTION           *Option
+  );
+
+#endif //_BDS_HELPER_H_
diff --git a/EfiLib/BdsTianoCore.c b/EfiLib/BdsTianoCore.c
new file mode 100644 (file)
index 0000000..2163b99
--- /dev/null
@@ -0,0 +1,201 @@
+/** @file
+  BDS Lib functions which relate with create or process the boot option.
+
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "../include/tiano_includes.h"
+
+/**
+  Build the boot#### or driver#### option from the VariableName, the
+  build boot#### or driver#### will also be linked to BdsCommonOptionList.
+
+  @param  BdsCommonOptionList   The header of the boot#### or driver#### option
+                                link list
+  @param  VariableName          EFI Variable name indicate if it is boot#### or
+                                driver####
+
+  @retval BDS_COMMON_OPTION     Get the option just been created
+  @retval NULL                  Failed to get the new option
+
+**/
+BDS_COMMON_OPTION *
+EFIAPI
+BdsLibVariableToOption (
+  IN OUT LIST_ENTRY                   *BdsCommonOptionList,
+  IN  CHAR16                          *VariableName
+  )
+{
+  UINT32                    Attribute;
+  UINT16                    FilePathSize;
+  UINT8                     *Variable;
+  UINT8                     *TempPtr;
+  UINTN                     VariableSize;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  BDS_COMMON_OPTION         *Option;
+  VOID                      *LoadOptions;
+  UINT32                    LoadOptionsSize;
+  CHAR16                    *Description;
+  UINT8                     NumOff;
+  EFI_GUID EfiGlobalVariableGuid     = { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }};
+
+  //
+  // Read the variable. We will never free this data.
+  //
+  Variable = BdsLibGetVariableAndSize (
+              VariableName,
+              &EfiGlobalVariableGuid,
+              &VariableSize
+              );
+  if (Variable == NULL) {
+    return NULL;
+  }
+  //
+  // Notes: careful defined the variable of Boot#### or
+  // Driver####, consider use some macro to abstract the code
+  //
+  //
+  // Get the option attribute
+  //
+  TempPtr   =  Variable;
+  Attribute =  *(UINT32 *) Variable;
+  TempPtr   += sizeof (UINT32);
+
+  //
+  // Get the option's device path size
+  //
+  FilePathSize =  *(UINT16 *) TempPtr;
+  TempPtr      += sizeof (UINT16);
+
+  //
+  // Get the option's description string
+  //
+  Description = (CHAR16 *) TempPtr;
+
+  //
+  // Get the option's description string size
+  //
+  TempPtr     += StrSize ((CHAR16 *) TempPtr);
+
+  //
+  // Get the option's device path
+  //
+  DevicePath =  (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+  TempPtr    += FilePathSize;
+
+  LoadOptions     = TempPtr;
+  LoadOptionsSize = (UINT32) (VariableSize - (UINTN) (TempPtr - Variable));
+
+  //
+  // The Console variables may have multiple device paths, so make
+  // an Entry for each one.
+  //
+  Option = AllocateZeroPool (sizeof (BDS_COMMON_OPTION));
+  if (Option == NULL) {
+    return NULL;
+  }
+
+  Option->Signature   = BDS_LOAD_OPTION_SIGNATURE;
+  Option->DevicePath  = AllocateZeroPool (GetDevicePathSize (DevicePath));
+  ASSERT(Option->DevicePath != NULL);
+  CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));
+
+  Option->Attribute   = Attribute;
+  Option->Description = AllocateZeroPool (StrSize (Description));
+  ASSERT(Option->Description != NULL);
+  CopyMem (Option->Description, Description, StrSize (Description));
+
+  Option->LoadOptions = AllocateZeroPool (LoadOptionsSize);
+  ASSERT(Option->LoadOptions != NULL);
+  CopyMem (Option->LoadOptions, LoadOptions, LoadOptionsSize);
+  Option->LoadOptionsSize = LoadOptionsSize;
+
+  //
+  // Get the value from VariableName Unicode string
+  // since the ISO standard assumes ASCII equivalent abbreviations, we can be safe in converting this
+  // Unicode stream to ASCII without any loss in meaning.
+  //
+  if (*VariableName == 'B') {
+    NumOff = (UINT8) (sizeof (L"Boot") / sizeof(CHAR16) - 1);
+    Option->BootCurrent = (UINT16) ((VariableName[NumOff]  -'0') * 0x1000);
+    Option->BootCurrent = (UINT16) (Option->BootCurrent + ((VariableName[NumOff+1]-'0') * 0x100));
+    Option->BootCurrent = (UINT16) (Option->BootCurrent +  ((VariableName[NumOff+2]-'0') * 0x10));
+    Option->BootCurrent = (UINT16) (Option->BootCurrent + ((VariableName[NumOff+3]-'0')));
+  }
+  //
+  // Insert active entry to BdsDeviceList
+  //
+  if ((Option->Attribute & LOAD_OPTION_ACTIVE) == LOAD_OPTION_ACTIVE) {
+    InsertTailList (BdsCommonOptionList, &Option->Link);
+    FreePool (Variable);
+    return Option;
+  }
+
+  FreePool (Variable);
+  FreePool (Option);
+  return NULL;
+
+}
+
+
+/**
+  Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
+  buffer, and the size of the buffer. If failure return NULL.
+
+  @param  Name                  String part of EFI variable name
+  @param  VendorGuid            GUID part of EFI variable name
+  @param  VariableSize          Returns the size of the EFI variable that was read
+
+  @return                       Dynamically allocated memory that contains a copy of the EFI variable
+                                Caller is responsible freeing the buffer.
+  @retval NULL                  Variable was not read
+
+**/
+VOID *
+EFIAPI
+BdsLibGetVariableAndSize (
+  IN  CHAR16              *Name,
+  IN  EFI_GUID            *VendorGuid,
+  OUT UINTN               *VariableSize
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       BufferSize;
+  VOID        *Buffer;
+
+  Buffer = NULL;
+
+  //
+  // Pass in a zero size buffer to find the required buffer size.
+  //
+  BufferSize  = 0;
+  Status      = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    //
+    // Allocate the buffer to return
+    //
+    Buffer = AllocateZeroPool (BufferSize);
+    if (Buffer == NULL) {
+      return NULL;
+    }
+    //
+    // Read variable into the allocated buffer.
+    //
+    Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
+    if (EFI_ERROR (Status)) {
+      BufferSize = 0;
+    }
+  }
+
+  *VariableSize = BufferSize;
+  return Buffer;
+}
+