While the actual implementation (using qemu fw_cfg) is qemu-specific, the idea to store the boot order as configured by the VMM in EFI variables is not. So lets give the variables a more neutral name while we still can (i.e. no stable tag yet with the new feature). While being at it also fix the NNNN format (use %x instead of %d for consistency with BootNNNN). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
		
			
				
	
	
		
			2423 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2423 lines
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
 | 
						|
 | 
						|
  Copyright (C) 2012 - 2014, Red Hat, Inc.
 | 
						|
  Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
 | 
						|
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
**/
 | 
						|
 | 
						|
#include <Library/QemuFwCfgLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/UefiBootManagerLib.h>
 | 
						|
#include <Library/UefiBootServicesTableLib.h>
 | 
						|
#include <Library/UefiRuntimeServicesTableLib.h>
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/PrintLib.h>
 | 
						|
#include <Library/DevicePathLib.h>
 | 
						|
#include <Library/QemuBootOrderLib.h>
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Guid/GlobalVariable.h>
 | 
						|
#include <Guid/VirtioMmioTransport.h>
 | 
						|
 | 
						|
#include "ExtraRootBusMap.h"
 | 
						|
 | 
						|
/**
 | 
						|
  OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
 | 
						|
**/
 | 
						|
#define TRANSLATION_OUTPUT_SIZE  0x100
 | 
						|
 | 
						|
/**
 | 
						|
  Output buffer size for OpenFirmware to UEFI device path fragment translation,
 | 
						|
  in CHAR16's, for a sequence of PCI bridges.
 | 
						|
**/
 | 
						|
#define BRIDGE_TRANSLATION_OUTPUT_SIZE  0x40
 | 
						|
 | 
						|
/**
 | 
						|
  Numbers of nodes in OpenFirmware device paths that are required and examined.
 | 
						|
**/
 | 
						|
#define REQUIRED_PCI_OFW_NODES   2
 | 
						|
#define REQUIRED_MMIO_OFW_NODES  1
 | 
						|
#define EXAMINED_OFW_NODES       6
 | 
						|
 | 
						|
/**
 | 
						|
  Simple character classification routines, corresponding to POSIX class names
 | 
						|
  and ASCII encoding.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
IsAlnum (
 | 
						|
  IN  CHAR8  Chr
 | 
						|
  )
 | 
						|
{
 | 
						|
  return (('0' <= Chr && Chr <= '9') ||
 | 
						|
          ('A' <= Chr && Chr <= 'Z') ||
 | 
						|
          ('a' <= Chr && Chr <= 'z')
 | 
						|
          );
 | 
						|
}
 | 
						|
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
IsDriverNamePunct (
 | 
						|
  IN  CHAR8  Chr
 | 
						|
  )
 | 
						|
{
 | 
						|
  return (Chr == ',' ||  Chr == '.' || Chr == '_' ||
 | 
						|
          Chr == '+' || Chr == '-'
 | 
						|
          );
 | 
						|
}
 | 
						|
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
IsPrintNotDelim (
 | 
						|
  IN  CHAR8  Chr
 | 
						|
  )
 | 
						|
{
 | 
						|
  return (32 <= Chr && Chr <= 126 &&
 | 
						|
          Chr != '/' && Chr != '@' && Chr != ':');
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Utility types and functions.
 | 
						|
**/
 | 
						|
typedef struct {
 | 
						|
  CONST CHAR8    *Ptr; // not necessarily NUL-terminated
 | 
						|
  UINTN          Len;  // number of non-NUL characters
 | 
						|
} SUBSTRING;
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Check if Substring and String have identical contents.
 | 
						|
 | 
						|
  The function relies on the restriction that a SUBSTRING cannot have embedded
 | 
						|
  NULs either.
 | 
						|
 | 
						|
  @param[in] Substring  The SUBSTRING input to the comparison.
 | 
						|
 | 
						|
  @param[in] String     The ASCII string input to the comparison.
 | 
						|
 | 
						|
 | 
						|
  @return  Whether the inputs have identical contents.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
SubstringEq (
 | 
						|
  IN  SUBSTRING    Substring,
 | 
						|
  IN  CONST CHAR8  *String
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN        Pos;
 | 
						|
  CONST CHAR8  *Chr;
 | 
						|
 | 
						|
  Pos = 0;
 | 
						|
  Chr = String;
 | 
						|
 | 
						|
  while (Pos < Substring.Len && Substring.Ptr[Pos] == *Chr) {
 | 
						|
    ++Pos;
 | 
						|
    ++Chr;
 | 
						|
  }
 | 
						|
 | 
						|
  return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Parse a comma-separated list of hexadecimal integers into the elements of an
 | 
						|
  UINT64 array.
 | 
						|
 | 
						|
  Whitespace, "0x" prefixes, leading or trailing commas, sequences of commas,
 | 
						|
  or an empty string are not allowed; they are rejected.
 | 
						|
 | 
						|
  The function relies on ASCII encoding.
 | 
						|
 | 
						|
  @param[in]     UnitAddress  The substring to parse.
 | 
						|
 | 
						|
  @param[out]    Result       The array, allocated by the caller, to receive
 | 
						|
                              the parsed values. This parameter may be NULL if
 | 
						|
                              NumResults is zero on input.
 | 
						|
 | 
						|
  @param[in out] NumResults   On input, the number of elements allocated for
 | 
						|
                              Result. On output, the number of elements it has
 | 
						|
                              taken (or would have taken) to parse the string
 | 
						|
                              fully.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS            UnitAddress has been fully parsed.
 | 
						|
                                    NumResults is set to the number of parsed
 | 
						|
                                    values; the corresponding elements have
 | 
						|
                                    been set in Result. The rest of Result's
 | 
						|
                                    elements are unchanged.
 | 
						|
 | 
						|
  @retval RETURN_BUFFER_TOO_SMALL   UnitAddress has been fully parsed.
 | 
						|
                                    NumResults is set to the number of parsed
 | 
						|
                                    values, but elements have been stored only
 | 
						|
                                    up to the input value of NumResults, which
 | 
						|
                                    is less than what has been parsed.
 | 
						|
 | 
						|
  @retval RETURN_INVALID_PARAMETER  Parse error. The contents of Results is
 | 
						|
                                    indeterminate. NumResults has not been
 | 
						|
                                    changed.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
ParseUnitAddressHexList (
 | 
						|
  IN      SUBSTRING  UnitAddress,
 | 
						|
  OUT     UINT64     *Result,
 | 
						|
  IN OUT  UINTN      *NumResults
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN          Entry;    // number of entry currently being parsed
 | 
						|
  UINT64         EntryVal; // value being constructed for current entry
 | 
						|
  CHAR8          PrevChr;  // UnitAddress character previously checked
 | 
						|
  UINTN          Pos;      // current position within UnitAddress
 | 
						|
  RETURN_STATUS  Status;
 | 
						|
 | 
						|
  Entry    = 0;
 | 
						|
  EntryVal = 0;
 | 
						|
  PrevChr  = ',';
 | 
						|
 | 
						|
  for (Pos = 0; Pos < UnitAddress.Len; ++Pos) {
 | 
						|
    CHAR8  Chr;
 | 
						|
    INT8   Val;
 | 
						|
 | 
						|
    Chr = UnitAddress.Ptr[Pos];
 | 
						|
    Val = ('a' <= Chr && Chr <= 'f') ? (Chr - 'a' + 10) :
 | 
						|
          ('A' <= Chr && Chr <= 'F') ? (Chr - 'A' + 10) :
 | 
						|
          ('0' <= Chr && Chr <= '9') ? (Chr - '0') :
 | 
						|
          -1;
 | 
						|
 | 
						|
    if (Val >= 0) {
 | 
						|
      if (EntryVal > 0xFFFFFFFFFFFFFFFull) {
 | 
						|
        return RETURN_INVALID_PARAMETER;
 | 
						|
      }
 | 
						|
 | 
						|
      EntryVal = LShiftU64 (EntryVal, 4) | Val;
 | 
						|
    } else if (Chr == ',') {
 | 
						|
      if (PrevChr == ',') {
 | 
						|
        return RETURN_INVALID_PARAMETER;
 | 
						|
      }
 | 
						|
 | 
						|
      if (Entry < *NumResults) {
 | 
						|
        Result[Entry] = EntryVal;
 | 
						|
      }
 | 
						|
 | 
						|
      ++Entry;
 | 
						|
      EntryVal = 0;
 | 
						|
    } else {
 | 
						|
      return RETURN_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
    PrevChr = Chr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (PrevChr == ',') {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Entry < *NumResults) {
 | 
						|
    Result[Entry] = EntryVal;
 | 
						|
    Status        = RETURN_SUCCESS;
 | 
						|
  } else {
 | 
						|
    Status = RETURN_BUFFER_TOO_SMALL;
 | 
						|
  }
 | 
						|
 | 
						|
  ++Entry;
 | 
						|
 | 
						|
  *NumResults = Entry;
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  A simple array of Boot Option ID's.
 | 
						|
**/
 | 
						|
typedef struct {
 | 
						|
  UINT16    *Data;
 | 
						|
  UINTN     Allocated;
 | 
						|
  UINTN     Produced;
 | 
						|
} BOOT_ORDER;
 | 
						|
 | 
						|
/**
 | 
						|
  Array element tracking an enumerated boot option that has the
 | 
						|
  LOAD_OPTION_ACTIVE attribute.
 | 
						|
**/
 | 
						|
typedef struct {
 | 
						|
  CONST EFI_BOOT_MANAGER_LOAD_OPTION    *BootOption; // reference only, no
 | 
						|
                                                     //   ownership
 | 
						|
  BOOLEAN                               Appended;    // has been added to a
 | 
						|
                                                     //   BOOT_ORDER?
 | 
						|
} ACTIVE_OPTION;
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Append an active boot option to BootOrder, reallocating the latter if needed.
 | 
						|
 | 
						|
  @param[in out] BootOrder     The structure pointing to the array and holding
 | 
						|
                               allocation and usage counters.
 | 
						|
 | 
						|
  @param[in]     ActiveOption  The active boot option whose ID should be
 | 
						|
                               appended to the array.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS           ID of ActiveOption appended.
 | 
						|
 | 
						|
  @retval RETURN_OUT_OF_RESOURCES  Memory reallocation failed.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
BootOrderAppend (
 | 
						|
  IN OUT  BOOT_ORDER     *BootOrder,
 | 
						|
  IN OUT  ACTIVE_OPTION  *ActiveOption
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (BootOrder->Produced == BootOrder->Allocated) {
 | 
						|
    UINTN   AllocatedNew;
 | 
						|
    UINT16  *DataNew;
 | 
						|
 | 
						|
    ASSERT (BootOrder->Allocated > 0);
 | 
						|
    AllocatedNew = BootOrder->Allocated * 2;
 | 
						|
    DataNew      = ReallocatePool (
 | 
						|
                     BootOrder->Allocated * sizeof (*BootOrder->Data),
 | 
						|
                     AllocatedNew         * sizeof (*DataNew),
 | 
						|
                     BootOrder->Data
 | 
						|
                     );
 | 
						|
    if (DataNew == NULL) {
 | 
						|
      return RETURN_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    BootOrder->Allocated = AllocatedNew;
 | 
						|
    BootOrder->Data      = DataNew;
 | 
						|
  }
 | 
						|
 | 
						|
  BootOrder->Data[BootOrder->Produced++] =
 | 
						|
    (UINT16)ActiveOption->BootOption->OptionNumber;
 | 
						|
  ActiveOption->Appended = TRUE;
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Create an array of ACTIVE_OPTION elements for a boot option array.
 | 
						|
 | 
						|
  @param[in]  BootOptions      A boot option array, created with
 | 
						|
                               EfiBootManagerRefreshAllBootOption () and
 | 
						|
                               EfiBootManagerGetLoadOptions ().
 | 
						|
 | 
						|
  @param[in]  BootOptionCount  The number of elements in BootOptions.
 | 
						|
 | 
						|
  @param[out] ActiveOption     Pointer to the first element in the new array.
 | 
						|
                               The caller is responsible for freeing the array
 | 
						|
                               with FreePool() after use.
 | 
						|
 | 
						|
  @param[out] Count            Number of elements in the new array.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS           The ActiveOption array has been created.
 | 
						|
 | 
						|
  @retval RETURN_NOT_FOUND         No active entry has been found in
 | 
						|
                                   BootOptions.
 | 
						|
 | 
						|
  @retval RETURN_OUT_OF_RESOURCES  Memory allocation failed.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
CollectActiveOptions (
 | 
						|
  IN   CONST EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions,
 | 
						|
  IN   UINTN                               BootOptionCount,
 | 
						|
  OUT  ACTIVE_OPTION                       **ActiveOption,
 | 
						|
  OUT  UINTN                               *Count
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN  Index;
 | 
						|
  UINTN  ScanMode;
 | 
						|
 | 
						|
  *ActiveOption = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Scan the list twice:
 | 
						|
  // - count active entries,
 | 
						|
  // - store links to active entries.
 | 
						|
  //
 | 
						|
  for (ScanMode = 0; ScanMode < 2; ++ScanMode) {
 | 
						|
    *Count = 0;
 | 
						|
    for (Index = 0; Index < BootOptionCount; Index++) {
 | 
						|
      if ((BootOptions[Index].Attributes & LOAD_OPTION_ACTIVE) != 0) {
 | 
						|
        if (ScanMode == 1) {
 | 
						|
          (*ActiveOption)[*Count].BootOption = &BootOptions[Index];
 | 
						|
          (*ActiveOption)[*Count].Appended   = FALSE;
 | 
						|
        }
 | 
						|
 | 
						|
        ++*Count;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (ScanMode == 0) {
 | 
						|
      if (*Count == 0) {
 | 
						|
        return RETURN_NOT_FOUND;
 | 
						|
      }
 | 
						|
 | 
						|
      *ActiveOption = AllocatePool (*Count * sizeof **ActiveOption);
 | 
						|
      if (*ActiveOption == NULL) {
 | 
						|
        return RETURN_OUT_OF_RESOURCES;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  OpenFirmware device path node
 | 
						|
**/
 | 
						|
typedef struct {
 | 
						|
  SUBSTRING    DriverName;
 | 
						|
  SUBSTRING    UnitAddress;
 | 
						|
  SUBSTRING    DeviceArguments;
 | 
						|
} OFW_NODE;
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Parse an OpenFirmware device path node into the caller-allocated OFW_NODE
 | 
						|
  structure, and advance in the input string.
 | 
						|
 | 
						|
  The node format is mostly parsed after IEEE 1275-1994, 3.2.1.1 "Node names"
 | 
						|
  (a leading slash is expected and not returned):
 | 
						|
 | 
						|
    /driver-name@unit-address[:device-arguments][<LF>]
 | 
						|
 | 
						|
  A single trailing <LF> character is consumed but not returned. A trailing
 | 
						|
  <LF> or NUL character terminates the device path.
 | 
						|
 | 
						|
  The function relies on ASCII encoding.
 | 
						|
 | 
						|
  @param[in out] Ptr      Address of the pointer pointing to the start of the
 | 
						|
                          node string. After successful parsing *Ptr is set to
 | 
						|
                          the byte immediately following the consumed
 | 
						|
                          characters. On error it points to the byte that
 | 
						|
                          caused the error. The input string is never modified.
 | 
						|
 | 
						|
  @param[out]    OfwNode  The members of this structure point into the input
 | 
						|
                          string, designating components of the node.
 | 
						|
                          Separators are never included. If "device-arguments"
 | 
						|
                          is missing, then DeviceArguments.Ptr is set to NULL.
 | 
						|
                          All components that are present have nonzero length.
 | 
						|
 | 
						|
                          If the call doesn't succeed, the contents of this
 | 
						|
                          structure is indeterminate.
 | 
						|
 | 
						|
  @param[out]    IsFinal  In case of successful parsing, this parameter signals
 | 
						|
                          whether the node just parsed is the final node in the
 | 
						|
                          device path. The call after a final node will attempt
 | 
						|
                          to start parsing the next path. If the call doesn't
 | 
						|
                          succeed, then this parameter is not changed.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS            Parsing successful.
 | 
						|
 | 
						|
  @retval RETURN_NOT_FOUND          Parsing terminated. *Ptr was (and is)
 | 
						|
                                    pointing to an empty string.
 | 
						|
 | 
						|
  @retval RETURN_INVALID_PARAMETER  Parse error.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
ParseOfwNode (
 | 
						|
  IN OUT  CONST CHAR8  **Ptr,
 | 
						|
  OUT     OFW_NODE     *OfwNode,
 | 
						|
  OUT     BOOLEAN      *IsFinal
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN  AcceptSlash = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // A leading slash is expected. End of string is tolerated.
 | 
						|
  //
 | 
						|
  switch (**Ptr) {
 | 
						|
    case '\0':
 | 
						|
      return RETURN_NOT_FOUND;
 | 
						|
 | 
						|
    case '/':
 | 
						|
      ++*Ptr;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // driver-name
 | 
						|
  //
 | 
						|
  OfwNode->DriverName.Ptr = *Ptr;
 | 
						|
  OfwNode->DriverName.Len = 0;
 | 
						|
  while (OfwNode->DriverName.Len < 32 &&
 | 
						|
         (IsAlnum (**Ptr) || IsDriverNamePunct (**Ptr))
 | 
						|
         )
 | 
						|
  {
 | 
						|
    ++*Ptr;
 | 
						|
    ++OfwNode->DriverName.Len;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((OfwNode->DriverName.Len == 0) || (OfwNode->DriverName.Len == 32)) {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (SubstringEq (OfwNode->DriverName, "rom")) {
 | 
						|
    //
 | 
						|
    // bug compatibility hack
 | 
						|
    //
 | 
						|
    // qemu passes fw_cfg filenames as rom unit address.
 | 
						|
    // The filenames have slashes:
 | 
						|
    //      /rom@genroms/linuxboot_dma.bin
 | 
						|
    //
 | 
						|
    // Alow slashes in the unit address to avoid the parser trip up,
 | 
						|
    // so we can successfully parse the following lines (the rom
 | 
						|
    // entries themself are ignored).
 | 
						|
    //
 | 
						|
    AcceptSlash = TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // unit-address
 | 
						|
  //
 | 
						|
  if (**Ptr != '@') {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  ++*Ptr;
 | 
						|
 | 
						|
  OfwNode->UnitAddress.Ptr = *Ptr;
 | 
						|
  OfwNode->UnitAddress.Len = 0;
 | 
						|
  while (IsPrintNotDelim (**Ptr) || (AcceptSlash && **Ptr == '/')) {
 | 
						|
    ++*Ptr;
 | 
						|
    ++OfwNode->UnitAddress.Len;
 | 
						|
  }
 | 
						|
 | 
						|
  if (OfwNode->UnitAddress.Len == 0) {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // device-arguments, may be omitted
 | 
						|
  //
 | 
						|
  OfwNode->DeviceArguments.Len = 0;
 | 
						|
  if (**Ptr == ':') {
 | 
						|
    ++*Ptr;
 | 
						|
    OfwNode->DeviceArguments.Ptr = *Ptr;
 | 
						|
 | 
						|
    while (IsPrintNotDelim (**Ptr)) {
 | 
						|
      ++*Ptr;
 | 
						|
      ++OfwNode->DeviceArguments.Len;
 | 
						|
    }
 | 
						|
 | 
						|
    if (OfwNode->DeviceArguments.Len == 0) {
 | 
						|
      return RETURN_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    OfwNode->DeviceArguments.Ptr = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (**Ptr) {
 | 
						|
    case '\n':
 | 
						|
      ++*Ptr;
 | 
						|
    //
 | 
						|
    // fall through
 | 
						|
    //
 | 
						|
 | 
						|
    case '\0':
 | 
						|
      *IsFinal = TRUE;
 | 
						|
      break;
 | 
						|
 | 
						|
    case '/':
 | 
						|
      *IsFinal = FALSE;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_VERBOSE,
 | 
						|
    "%a: DriverName=\"%.*a\" UnitAddress=\"%.*a\" DeviceArguments=\"%.*a\"\n",
 | 
						|
    __FUNCTION__,
 | 
						|
    OfwNode->DriverName.Len,
 | 
						|
    OfwNode->DriverName.Ptr,
 | 
						|
    OfwNode->UnitAddress.Len,
 | 
						|
    OfwNode->UnitAddress.Ptr,
 | 
						|
    OfwNode->DeviceArguments.Len,
 | 
						|
    OfwNode->DeviceArguments.Ptr == NULL ? "" : OfwNode->DeviceArguments.Ptr
 | 
						|
    ));
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Translate a PCI-like array of OpenFirmware device nodes to a UEFI device path
 | 
						|
  fragment.
 | 
						|
 | 
						|
  @param[in]     OfwNode         Array of OpenFirmware device nodes to
 | 
						|
                                 translate, constituting the beginning of an
 | 
						|
                                 OpenFirmware device path.
 | 
						|
 | 
						|
  @param[in]     NumNodes        Number of elements in OfwNode.
 | 
						|
 | 
						|
  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
 | 
						|
                                 CreateExtraRootBusMap(), to be used for
 | 
						|
                                 translating positions of extra root buses to
 | 
						|
                                 bus numbers.
 | 
						|
 | 
						|
  @param[out]    Translated      Destination array receiving the UEFI path
 | 
						|
                                 fragment, allocated by the caller. If the
 | 
						|
                                 return value differs from RETURN_SUCCESS, its
 | 
						|
                                 contents is indeterminate.
 | 
						|
 | 
						|
  @param[in out] TranslatedSize  On input, the number of CHAR16's in
 | 
						|
                                 Translated. On RETURN_SUCCESS this parameter
 | 
						|
                                 is assigned the number of non-NUL CHAR16's
 | 
						|
                                 written to Translated. In case of other return
 | 
						|
                                 values, TranslatedSize is indeterminate.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS           Translation successful.
 | 
						|
 | 
						|
  @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
 | 
						|
                                   of bytes provided.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
 | 
						|
                                   be translated in the current implementation.
 | 
						|
 | 
						|
  @retval RETURN_PROTOCOL_ERROR    The initial OpenFirmware node refers to an
 | 
						|
                                   extra PCI root bus (by serial number) that
 | 
						|
                                   is invalid according to ExtraPciRoots.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
TranslatePciOfwNodes (
 | 
						|
  IN      CONST OFW_NODE            *OfwNode,
 | 
						|
  IN      UINTN                     NumNodes,
 | 
						|
  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,
 | 
						|
  OUT     CHAR16                    *Translated,
 | 
						|
  IN OUT  UINTN                     *TranslatedSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32  PciRoot;
 | 
						|
  CHAR8   *Comma;
 | 
						|
  UINTN   FirstNonBridge;
 | 
						|
  CHAR16  Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];
 | 
						|
  UINTN   BridgesLen;
 | 
						|
  UINT64  PciDevFun[2];
 | 
						|
  UINTN   NumEntries;
 | 
						|
  UINTN   Written;
 | 
						|
 | 
						|
  //
 | 
						|
  // Resolve the PCI root bus number.
 | 
						|
  //
 | 
						|
  // The initial OFW node for the main root bus (ie. bus number 0) is:
 | 
						|
  //
 | 
						|
  //   /pci@i0cf8
 | 
						|
  //
 | 
						|
  // For extra root buses, the initial OFW node is
 | 
						|
  //
 | 
						|
  //   /pci@i0cf8,4
 | 
						|
  //              ^
 | 
						|
  //              root bus serial number (not PCI bus number)
 | 
						|
  //
 | 
						|
  if ((NumNodes < REQUIRED_PCI_OFW_NODES) ||
 | 
						|
      !SubstringEq (OfwNode[0].DriverName, "pci")
 | 
						|
      )
 | 
						|
  {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  PciRoot = 0;
 | 
						|
  Comma   = ScanMem8 (
 | 
						|
              OfwNode[0].UnitAddress.Ptr,
 | 
						|
              OfwNode[0].UnitAddress.Len,
 | 
						|
              ','
 | 
						|
              );
 | 
						|
  if (Comma != NULL) {
 | 
						|
    SUBSTRING  PciRootSerialSubString;
 | 
						|
    UINT64     PciRootSerial;
 | 
						|
 | 
						|
    //
 | 
						|
    // Parse the root bus serial number from the unit address after the comma.
 | 
						|
    //
 | 
						|
    PciRootSerialSubString.Ptr = Comma + 1;
 | 
						|
    PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -
 | 
						|
                                 (PciRootSerialSubString.Ptr -
 | 
						|
                                  OfwNode[0].UnitAddress.Ptr);
 | 
						|
    NumEntries = 1;
 | 
						|
    if (RETURN_ERROR (
 | 
						|
          ParseUnitAddressHexList (
 | 
						|
            PciRootSerialSubString,
 | 
						|
            &PciRootSerial,
 | 
						|
            &NumEntries
 | 
						|
            )
 | 
						|
          ))
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Map the extra root bus's serial number to its actual bus number.
 | 
						|
    //
 | 
						|
    if (EFI_ERROR (
 | 
						|
          MapRootBusPosToBusNr (
 | 
						|
            ExtraPciRoots,
 | 
						|
            PciRootSerial,
 | 
						|
            &PciRoot
 | 
						|
            )
 | 
						|
          ))
 | 
						|
    {
 | 
						|
      return RETURN_PROTOCOL_ERROR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Translate a sequence of PCI bridges. For each bridge, the OFW node is:
 | 
						|
  //
 | 
						|
  //   pci-bridge@1e[,0]
 | 
						|
  //              ^   ^
 | 
						|
  //              PCI slot & function on the parent, holding the bridge
 | 
						|
  //
 | 
						|
  // and the UEFI device path node is:
 | 
						|
  //
 | 
						|
  //   Pci(0x1E,0x0)
 | 
						|
  //
 | 
						|
  FirstNonBridge = 1;
 | 
						|
  Bridges[0]     = L'\0';
 | 
						|
  BridgesLen     = 0;
 | 
						|
  do {
 | 
						|
    UINT64  BridgeDevFun[2];
 | 
						|
    UINTN   BridgesFreeBytes;
 | 
						|
 | 
						|
    if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    BridgeDevFun[1] = 0;
 | 
						|
    NumEntries      = sizeof BridgeDevFun / sizeof BridgeDevFun[0];
 | 
						|
    if (ParseUnitAddressHexList (
 | 
						|
          OfwNode[FirstNonBridge].UnitAddress,
 | 
						|
          BridgeDevFun,
 | 
						|
          &NumEntries
 | 
						|
          ) != RETURN_SUCCESS)
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];
 | 
						|
    Written          = UnicodeSPrintAsciiFormat (
 | 
						|
                         Bridges + BridgesLen,
 | 
						|
                         BridgesFreeBytes,
 | 
						|
                         "/Pci(0x%Lx,0x%Lx)",
 | 
						|
                         BridgeDevFun[0],
 | 
						|
                         BridgeDevFun[1]
 | 
						|
                         );
 | 
						|
    BridgesLen += Written;
 | 
						|
 | 
						|
    //
 | 
						|
    // There's no way to differentiate between "completely used up without
 | 
						|
    // truncation" and "truncated", so treat the former as the latter.
 | 
						|
    //
 | 
						|
    if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    ++FirstNonBridge;
 | 
						|
  } while (FirstNonBridge < NumNodes);
 | 
						|
 | 
						|
  if (FirstNonBridge == NumNodes) {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse the OFW nodes starting with the first non-bridge node.
 | 
						|
  //
 | 
						|
  PciDevFun[1] = 0;
 | 
						|
  NumEntries   = ARRAY_SIZE (PciDevFun);
 | 
						|
  if (ParseUnitAddressHexList (
 | 
						|
        OfwNode[FirstNonBridge].UnitAddress,
 | 
						|
        PciDevFun,
 | 
						|
        &NumEntries
 | 
						|
        ) != RETURN_SUCCESS
 | 
						|
      )
 | 
						|
  {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((NumNodes >= FirstNonBridge + 3) &&
 | 
						|
      SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&
 | 
						|
      SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
 | 
						|
      SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
 | 
						|
      )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (IDE disk, IDE CD-ROM):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/ide@1,1/drive@0/disk@0
 | 
						|
    //        ^         ^ ^       ^      ^
 | 
						|
    //        |         | |       |      master or slave
 | 
						|
    //        |         | |       primary or secondary
 | 
						|
    //        |         PCI slot & function holding IDE controller
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)
 | 
						|
    //                                                ^
 | 
						|
    //                                                fixed LUN
 | 
						|
    //
 | 
						|
    UINT64  Secondary;
 | 
						|
    UINT64  Slave;
 | 
						|
 | 
						|
    NumEntries = 1;
 | 
						|
    if ((ParseUnitAddressHexList (
 | 
						|
           OfwNode[FirstNonBridge + 1].UnitAddress,
 | 
						|
           &Secondary,
 | 
						|
           &NumEntries
 | 
						|
           ) != RETURN_SUCCESS) ||
 | 
						|
        (Secondary > 1) ||
 | 
						|
        (ParseUnitAddressHexList (
 | 
						|
           OfwNode[FirstNonBridge + 2].UnitAddress,
 | 
						|
           &Slave,
 | 
						|
           &NumEntries // reuse after previous single-element call
 | 
						|
           ) != RETURN_SUCCESS) ||
 | 
						|
        (Slave > 1)
 | 
						|
        )
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                Secondary ? "Secondary" : "Primary",
 | 
						|
                Slave ? "Slave" : "Master"
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 3) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
 | 
						|
             )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (Q35 SATA disk and CD-ROM):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0
 | 
						|
    //        ^                  ^  ^       ^      ^
 | 
						|
    //        |                  |  |       |      device number (fixed 0)
 | 
						|
    //        |                  |  |       channel (port) number
 | 
						|
    //        |                  PCI slot & function holding SATA HBA
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)
 | 
						|
    //                                   ^   ^      ^
 | 
						|
    //                                   |   |      LUN (always 0 on Q35)
 | 
						|
    //                                   |   port multiplier port number,
 | 
						|
    //                                   |   always 0xFFFF on Q35
 | 
						|
    //                                   channel (port) number
 | 
						|
    //
 | 
						|
    UINT64  Channel;
 | 
						|
 | 
						|
    NumEntries = 1;
 | 
						|
    if (RETURN_ERROR (
 | 
						|
          ParseUnitAddressHexList (
 | 
						|
            OfwNode[FirstNonBridge + 1].UnitAddress,
 | 
						|
            &Channel,
 | 
						|
            &NumEntries
 | 
						|
            )
 | 
						|
          ))
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                Channel
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 3) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")
 | 
						|
             )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (floppy disk):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/isa@1/fdc@03f0/floppy@0
 | 
						|
    //        ^         ^     ^           ^
 | 
						|
    //        |         |     |           A: or B:
 | 
						|
    //        |         |     ISA controller io-port (hex)
 | 
						|
    //        |         PCI slot holding ISA controller
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x1,0x0)/Floppy(0x0)
 | 
						|
    //                                    ^
 | 
						|
    //                                    ACPI UID
 | 
						|
    //
 | 
						|
    UINT64  AcpiUid;
 | 
						|
 | 
						|
    NumEntries = 1;
 | 
						|
    if ((ParseUnitAddressHexList (
 | 
						|
           OfwNode[FirstNonBridge + 2].UnitAddress,
 | 
						|
           &AcpiUid,
 | 
						|
           &NumEntries
 | 
						|
           ) != RETURN_SUCCESS) ||
 | 
						|
        (AcpiUid > 1)
 | 
						|
        )
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                AcpiUid
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 2) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")
 | 
						|
             )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (virtio-blk disk):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/scsi@6[,3]/disk@0,0
 | 
						|
    //        ^          ^  ^       ^ ^
 | 
						|
    //        |          |  |       fixed
 | 
						|
    //        |          |  PCI function corresponding to disk (optional)
 | 
						|
    //        |          PCI slot holding disk
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x6,0x0) -- if PCI function is 0 or absent
 | 
						|
    //   PciRoot(0x0)/Pci(0x6,0x3) -- if PCI function is present and nonzero
 | 
						|
    //
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1]
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 3) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")
 | 
						|
             )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (virtio-scsi disk):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
 | 
						|
    //        ^          ^             ^      ^ ^
 | 
						|
    //        |          |             |      | LUN
 | 
						|
    //        |          |             |      target
 | 
						|
    //        |          |             channel (unused, fixed 0)
 | 
						|
    //        |          PCI slot[, function] holding SCSI controller
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x7,0x0)/Scsi(0x2,0x3)
 | 
						|
    //                                        -- if PCI function is 0 or absent
 | 
						|
    //   PciRoot(0x0)/Pci(0x7,0x3)/Scsi(0x2,0x3)
 | 
						|
    //                                -- if PCI function is present and nonzero
 | 
						|
    //
 | 
						|
    UINT64  TargetLun[2];
 | 
						|
 | 
						|
    TargetLun[1] = 0;
 | 
						|
    NumEntries   = ARRAY_SIZE (TargetLun);
 | 
						|
    if (ParseUnitAddressHexList (
 | 
						|
          OfwNode[FirstNonBridge + 2].UnitAddress,
 | 
						|
          TargetLun,
 | 
						|
          &NumEntries
 | 
						|
          ) != RETURN_SUCCESS
 | 
						|
        )
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                TargetLun[0],
 | 
						|
                TargetLun[1]
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 2) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")
 | 
						|
             )
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (NVMe device):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0
 | 
						|
    //        ^                  ^  ^            ^ ^
 | 
						|
    //        |                  |  |            | Extended Unique Identifier
 | 
						|
    //        |                  |  |            | (EUI-64), big endian interp.
 | 
						|
    //        |                  |  |            namespace ID
 | 
						|
    //        |                  PCI slot & function holding NVMe controller
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)
 | 
						|
    //                                  ^   ^
 | 
						|
    //                                  |   octets of the EUI-64
 | 
						|
    //                                  |   in address order
 | 
						|
    //                                  namespace ID
 | 
						|
    //
 | 
						|
    UINT64  Namespace[2];
 | 
						|
    UINTN   RequiredEntries;
 | 
						|
    UINT8   *Eui64;
 | 
						|
 | 
						|
    RequiredEntries = ARRAY_SIZE (Namespace);
 | 
						|
    NumEntries      = RequiredEntries;
 | 
						|
    if ((ParseUnitAddressHexList (
 | 
						|
           OfwNode[FirstNonBridge + 1].UnitAddress,
 | 
						|
           Namespace,
 | 
						|
           &NumEntries
 | 
						|
           ) != RETURN_SUCCESS) ||
 | 
						|
        (NumEntries != RequiredEntries) ||
 | 
						|
        (Namespace[0] == 0) ||
 | 
						|
        (Namespace[0] >= MAX_UINT32)
 | 
						|
        )
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Eui64   = (UINT8 *)&Namespace[1];
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"
 | 
						|
                "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                Namespace[0],
 | 
						|
                Eui64[7],
 | 
						|
                Eui64[6],
 | 
						|
                Eui64[5],
 | 
						|
                Eui64[4],
 | 
						|
                Eui64[3],
 | 
						|
                Eui64[2],
 | 
						|
                Eui64[1],
 | 
						|
                Eui64[0]
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= FirstNonBridge + 2) &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "usb") &&
 | 
						|
             SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "storage"))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (usb-storage device in XHCI port):
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/usb@3[,1]/storage@2/channel@0/disk@0,0
 | 
						|
    //        ^         ^  ^          ^         ^      ^ ^
 | 
						|
    //        |         |  |          |         fixed  fixed
 | 
						|
    //        |         |  |          XHCI port number, 1-based
 | 
						|
    //        |         |  PCI function corresponding to XHCI (optional)
 | 
						|
    //        |         PCI slot holding XHCI
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x3,0x1)/USB(0x1,0x0)
 | 
						|
    //                        ^        ^
 | 
						|
    //                        |        XHCI port number in 0-based notation
 | 
						|
    //                        0x0 if PCI function is 0, or absent from OFW
 | 
						|
    //
 | 
						|
    RETURN_STATUS  ParseStatus;
 | 
						|
    UINT64         OneBasedXhciPort;
 | 
						|
 | 
						|
    NumEntries  = 1;
 | 
						|
    ParseStatus = ParseUnitAddressHexList (
 | 
						|
                    OfwNode[FirstNonBridge + 1].UnitAddress,
 | 
						|
                    &OneBasedXhciPort,
 | 
						|
                    &NumEntries
 | 
						|
                    );
 | 
						|
    if (RETURN_ERROR (ParseStatus) || (OneBasedXhciPort == 0)) {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/USB(0x%Lx,0x0)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1],
 | 
						|
                OneBasedXhciPort - 1
 | 
						|
                );
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Generic OpenFirmware device path for PCI devices:
 | 
						|
    //
 | 
						|
    //   /pci@i0cf8/ethernet@3[,2]
 | 
						|
    //        ^              ^
 | 
						|
    //        |              PCI slot[, function] holding Ethernet card
 | 
						|
    //        PCI root at system bus port, PIO
 | 
						|
    //
 | 
						|
    // UEFI device path prefix (dependent on presence of nonzero PCI function):
 | 
						|
    //
 | 
						|
    //   PciRoot(0x0)/Pci(0x3,0x0)
 | 
						|
    //   PciRoot(0x0)/Pci(0x3,0x2)
 | 
						|
    //
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",
 | 
						|
                PciRoot,
 | 
						|
                Bridges,
 | 
						|
                PciDevFun[0],
 | 
						|
                PciDevFun[1]
 | 
						|
                );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // There's no way to differentiate between "completely used up without
 | 
						|
  // truncation" and "truncated", so treat the former as the latter, and return
 | 
						|
  // success only for "some room left unused".
 | 
						|
  //
 | 
						|
  if (Written + 1 < *TranslatedSize) {
 | 
						|
    *TranslatedSize = Written;
 | 
						|
    return RETURN_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return RETURN_BUFFER_TOO_SMALL;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// A type providing easy raw access to the base address of a virtio-mmio
 | 
						|
// transport.
 | 
						|
//
 | 
						|
typedef union {
 | 
						|
  UINT64    Uint64;
 | 
						|
  UINT8     Raw[8];
 | 
						|
} VIRTIO_MMIO_BASE_ADDRESS;
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device
 | 
						|
  path fragment.
 | 
						|
 | 
						|
  @param[in]     OfwNode         Array of OpenFirmware device nodes to
 | 
						|
                                 translate, constituting the beginning of an
 | 
						|
                                 OpenFirmware device path.
 | 
						|
 | 
						|
  @param[in]     NumNodes        Number of elements in OfwNode.
 | 
						|
 | 
						|
  @param[out]    Translated      Destination array receiving the UEFI path
 | 
						|
                                 fragment, allocated by the caller. If the
 | 
						|
                                 return value differs from RETURN_SUCCESS, its
 | 
						|
                                 contents is indeterminate.
 | 
						|
 | 
						|
  @param[in out] TranslatedSize  On input, the number of CHAR16's in
 | 
						|
                                 Translated. On RETURN_SUCCESS this parameter
 | 
						|
                                 is assigned the number of non-NUL CHAR16's
 | 
						|
                                 written to Translated. In case of other return
 | 
						|
                                 values, TranslatedSize is indeterminate.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS           Translation successful.
 | 
						|
 | 
						|
  @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
 | 
						|
                                   of bytes provided.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
 | 
						|
                                   be translated in the current implementation.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
TranslateMmioOfwNodes (
 | 
						|
  IN      CONST OFW_NODE  *OfwNode,
 | 
						|
  IN      UINTN           NumNodes,
 | 
						|
  OUT     CHAR16          *Translated,
 | 
						|
  IN OUT  UINTN           *TranslatedSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  VIRTIO_MMIO_BASE_ADDRESS  VirtioMmioBase;
 | 
						|
  CHAR16                    VenHwString[60 + 1];
 | 
						|
  UINTN                     NumEntries;
 | 
						|
  UINTN                     Written;
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the base address of the virtio-mmio transport.
 | 
						|
  //
 | 
						|
  if ((NumNodes < REQUIRED_MMIO_OFW_NODES) ||
 | 
						|
      !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")
 | 
						|
      )
 | 
						|
  {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  NumEntries = 1;
 | 
						|
  if (ParseUnitAddressHexList (
 | 
						|
        OfwNode[0].UnitAddress,
 | 
						|
        &VirtioMmioBase.Uint64,
 | 
						|
        &NumEntries
 | 
						|
        ) != RETURN_SUCCESS
 | 
						|
      )
 | 
						|
  {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  UnicodeSPrintAsciiFormat (
 | 
						|
    VenHwString,
 | 
						|
    sizeof VenHwString,
 | 
						|
    "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)",
 | 
						|
    &gVirtioMmioTransportGuid,
 | 
						|
    VirtioMmioBase.Raw[0],
 | 
						|
    VirtioMmioBase.Raw[1],
 | 
						|
    VirtioMmioBase.Raw[2],
 | 
						|
    VirtioMmioBase.Raw[3],
 | 
						|
    VirtioMmioBase.Raw[4],
 | 
						|
    VirtioMmioBase.Raw[5],
 | 
						|
    VirtioMmioBase.Raw[6],
 | 
						|
    VirtioMmioBase.Raw[7]
 | 
						|
    );
 | 
						|
 | 
						|
  if ((NumNodes >= 2) &&
 | 
						|
      SubstringEq (OfwNode[1].DriverName, "disk"))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (virtio-blk disk):
 | 
						|
    //
 | 
						|
    //   /virtio-mmio@000000000a003c00/disk@0,0
 | 
						|
    //                ^                     ^ ^
 | 
						|
    //                |                     fixed
 | 
						|
    //                base address of virtio-mmio register block
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   <VenHwString>
 | 
						|
    //
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "%s",
 | 
						|
                VenHwString
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= 3) &&
 | 
						|
             SubstringEq (OfwNode[1].DriverName, "channel") &&
 | 
						|
             SubstringEq (OfwNode[2].DriverName, "disk"))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (virtio-scsi disk):
 | 
						|
    //
 | 
						|
    //   /virtio-mmio@000000000a003a00/channel@0/disk@2,3
 | 
						|
    //                ^                        ^      ^ ^
 | 
						|
    //                |                        |      | LUN
 | 
						|
    //                |                        |      target
 | 
						|
    //                |                        channel (unused, fixed 0)
 | 
						|
    //                base address of virtio-mmio register block
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   <VenHwString>/Scsi(0x2,0x3)
 | 
						|
    //
 | 
						|
    UINT64  TargetLun[2];
 | 
						|
 | 
						|
    TargetLun[1] = 0;
 | 
						|
    NumEntries   = ARRAY_SIZE (TargetLun);
 | 
						|
    if (ParseUnitAddressHexList (
 | 
						|
          OfwNode[2].UnitAddress,
 | 
						|
          TargetLun,
 | 
						|
          &NumEntries
 | 
						|
          ) != RETURN_SUCCESS
 | 
						|
        )
 | 
						|
    {
 | 
						|
      return RETURN_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "%s/Scsi(0x%Lx,0x%Lx)",
 | 
						|
                VenHwString,
 | 
						|
                TargetLun[0],
 | 
						|
                TargetLun[1]
 | 
						|
                );
 | 
						|
  } else if ((NumNodes >= 2) &&
 | 
						|
             SubstringEq (OfwNode[1].DriverName, "ethernet-phy"))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // OpenFirmware device path (virtio-net NIC):
 | 
						|
    //
 | 
						|
    //   /virtio-mmio@000000000a003e00/ethernet-phy@0
 | 
						|
    //                ^                             ^
 | 
						|
    //                |                             fixed
 | 
						|
    //                base address of virtio-mmio register block
 | 
						|
    //
 | 
						|
    // UEFI device path prefix:
 | 
						|
    //
 | 
						|
    //   <VenHwString>
 | 
						|
    //
 | 
						|
    Written = UnicodeSPrintAsciiFormat (
 | 
						|
                Translated,
 | 
						|
                *TranslatedSize * sizeof (*Translated), // BufferSize in bytes
 | 
						|
                "%s",
 | 
						|
                VenHwString
 | 
						|
                );
 | 
						|
  } else {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // There's no way to differentiate between "completely used up without
 | 
						|
  // truncation" and "truncated", so treat the former as the latter, and return
 | 
						|
  // success only for "some room left unused".
 | 
						|
  //
 | 
						|
  if (Written + 1 < *TranslatedSize) {
 | 
						|
    *TranslatedSize = Written;
 | 
						|
    return RETURN_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return RETURN_BUFFER_TOO_SMALL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Translate an array of OpenFirmware device nodes to a UEFI device path
 | 
						|
  fragment.
 | 
						|
 | 
						|
  @param[in]     OfwNode         Array of OpenFirmware device nodes to
 | 
						|
                                 translate, constituting the beginning of an
 | 
						|
                                 OpenFirmware device path.
 | 
						|
 | 
						|
  @param[in]     NumNodes        Number of elements in OfwNode.
 | 
						|
 | 
						|
  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
 | 
						|
                                 CreateExtraRootBusMap(), to be used for
 | 
						|
                                 translating positions of extra root buses to
 | 
						|
                                 bus numbers.
 | 
						|
 | 
						|
  @param[out]    Translated      Destination array receiving the UEFI path
 | 
						|
                                 fragment, allocated by the caller. If the
 | 
						|
                                 return value differs from RETURN_SUCCESS, its
 | 
						|
                                 contents is indeterminate.
 | 
						|
 | 
						|
  @param[in out] TranslatedSize  On input, the number of CHAR16's in
 | 
						|
                                 Translated. On RETURN_SUCCESS this parameter
 | 
						|
                                 is assigned the number of non-NUL CHAR16's
 | 
						|
                                 written to Translated. In case of other return
 | 
						|
                                 values, TranslatedSize is indeterminate.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS           Translation successful.
 | 
						|
 | 
						|
  @retval RETURN_BUFFER_TOO_SMALL  The translation does not fit into the number
 | 
						|
                                   of bytes provided.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED       The array of OpenFirmware device nodes can't
 | 
						|
                                   be translated in the current implementation.
 | 
						|
 | 
						|
  @retval RETURN_PROTOCOL_ERROR    The array of OpenFirmware device nodes has
 | 
						|
                                   been (partially) recognized, but it contains
 | 
						|
                                   a logic error / doesn't match system state.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
TranslateOfwNodes (
 | 
						|
  IN      CONST OFW_NODE            *OfwNode,
 | 
						|
  IN      UINTN                     NumNodes,
 | 
						|
  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,
 | 
						|
  OUT     CHAR16                    *Translated,
 | 
						|
  IN OUT  UINTN                     *TranslatedSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS  Status;
 | 
						|
 | 
						|
  Status = RETURN_UNSUPPORTED;
 | 
						|
 | 
						|
  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
 | 
						|
    Status = TranslatePciOfwNodes (
 | 
						|
               OfwNode,
 | 
						|
               NumNodes,
 | 
						|
               ExtraPciRoots,
 | 
						|
               Translated,
 | 
						|
               TranslatedSize
 | 
						|
               );
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Status == RETURN_UNSUPPORTED) &&
 | 
						|
      FeaturePcdGet (PcdQemuBootOrderMmioTranslation))
 | 
						|
  {
 | 
						|
    Status = TranslateMmioOfwNodes (
 | 
						|
               OfwNode,
 | 
						|
               NumNodes,
 | 
						|
               Translated,
 | 
						|
               TranslatedSize
 | 
						|
               );
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Translate an OpenFirmware device path fragment to a UEFI device path
 | 
						|
  fragment, and advance in the input string.
 | 
						|
 | 
						|
  @param[in out] Ptr             Address of the pointer pointing to the start
 | 
						|
                                 of the path string. After successful
 | 
						|
                                 translation (RETURN_SUCCESS) or at least
 | 
						|
                                 successful parsing (RETURN_UNSUPPORTED,
 | 
						|
                                 RETURN_BUFFER_TOO_SMALL), *Ptr is set to the
 | 
						|
                                 byte immediately following the consumed
 | 
						|
                                 characters. In other error cases, it points to
 | 
						|
                                 the byte that caused the error.
 | 
						|
 | 
						|
  @param[in]     ExtraPciRoots   An EXTRA_ROOT_BUS_MAP object created with
 | 
						|
                                 CreateExtraRootBusMap(), to be used for
 | 
						|
                                 translating positions of extra root buses to
 | 
						|
                                 bus numbers.
 | 
						|
 | 
						|
  @param[out]    Translated      Destination array receiving the UEFI path
 | 
						|
                                 fragment, allocated by the caller. If the
 | 
						|
                                 return value differs from RETURN_SUCCESS, its
 | 
						|
                                 contents is indeterminate.
 | 
						|
 | 
						|
  @param[in out] TranslatedSize  On input, the number of CHAR16's in
 | 
						|
                                 Translated. On RETURN_SUCCESS this parameter
 | 
						|
                                 is assigned the number of non-NUL CHAR16's
 | 
						|
                                 written to Translated. In case of other return
 | 
						|
                                 values, TranslatedSize is indeterminate.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS            Translation successful.
 | 
						|
 | 
						|
  @retval RETURN_BUFFER_TOO_SMALL   The OpenFirmware device path was parsed
 | 
						|
                                    successfully, but its translation did not
 | 
						|
                                    fit into the number of bytes provided.
 | 
						|
                                    Further calls to this function are
 | 
						|
                                    possible.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED        The OpenFirmware device path was parsed
 | 
						|
                                    successfully, but it can't be translated in
 | 
						|
                                    the current implementation. Further calls
 | 
						|
                                    to this function are possible.
 | 
						|
 | 
						|
  @retval RETURN_PROTOCOL_ERROR     The OpenFirmware device path has been
 | 
						|
                                    (partially) recognized, but it contains a
 | 
						|
                                    logic error / doesn't match system state.
 | 
						|
                                    Further calls to this function are
 | 
						|
                                    possible.
 | 
						|
 | 
						|
  @retval RETURN_NOT_FOUND          Translation terminated. On input, *Ptr was
 | 
						|
                                    pointing to the empty string or "HALT". On
 | 
						|
                                    output, *Ptr points to the empty string
 | 
						|
                                    (ie. "HALT" is consumed transparently when
 | 
						|
                                    present).
 | 
						|
 | 
						|
  @retval RETURN_INVALID_PARAMETER  Parse error. This is a permanent error.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
TranslateOfwPath (
 | 
						|
  IN OUT  CONST CHAR8               **Ptr,
 | 
						|
  IN      CONST EXTRA_ROOT_BUS_MAP  *ExtraPciRoots,
 | 
						|
  OUT     CHAR16                    *Translated,
 | 
						|
  IN OUT  UINTN                     *TranslatedSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN          NumNodes;
 | 
						|
  RETURN_STATUS  Status;
 | 
						|
  OFW_NODE       Node[EXAMINED_OFW_NODES];
 | 
						|
  BOOLEAN        IsFinal;
 | 
						|
  OFW_NODE       Skip;
 | 
						|
 | 
						|
  IsFinal  = FALSE;
 | 
						|
  NumNodes = 0;
 | 
						|
  if (AsciiStrCmp (*Ptr, "HALT") == 0) {
 | 
						|
    *Ptr  += 4;
 | 
						|
    Status = RETURN_NOT_FOUND;
 | 
						|
  } else {
 | 
						|
    Status = ParseOfwNode (Ptr, &Node[NumNodes], &IsFinal);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Status == RETURN_NOT_FOUND) {
 | 
						|
    DEBUG ((DEBUG_VERBOSE, "%a: no more nodes\n", __FUNCTION__));
 | 
						|
    return RETURN_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  while (Status == RETURN_SUCCESS && !IsFinal) {
 | 
						|
    ++NumNodes;
 | 
						|
    Status = ParseOfwNode (
 | 
						|
               Ptr,
 | 
						|
               (NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
 | 
						|
               &IsFinal
 | 
						|
               );
 | 
						|
  }
 | 
						|
 | 
						|
  switch (Status) {
 | 
						|
    case RETURN_SUCCESS:
 | 
						|
      ++NumNodes;
 | 
						|
      break;
 | 
						|
 | 
						|
    case RETURN_INVALID_PARAMETER:
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "%a: parse error\n", __FUNCTION__));
 | 
						|
      return RETURN_INVALID_PARAMETER;
 | 
						|
 | 
						|
    default:
 | 
						|
      ASSERT (0);
 | 
						|
  }
 | 
						|
 | 
						|
  Status = TranslateOfwNodes (
 | 
						|
             Node,
 | 
						|
             NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
 | 
						|
             ExtraPciRoots,
 | 
						|
             Translated,
 | 
						|
             TranslatedSize
 | 
						|
             );
 | 
						|
  switch (Status) {
 | 
						|
    case RETURN_SUCCESS:
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "%a: success: \"%s\"\n", __FUNCTION__, Translated));
 | 
						|
      break;
 | 
						|
 | 
						|
    case RETURN_BUFFER_TOO_SMALL:
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "%a: buffer too small\n", __FUNCTION__));
 | 
						|
      break;
 | 
						|
 | 
						|
    case RETURN_UNSUPPORTED:
 | 
						|
      DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));
 | 
						|
      break;
 | 
						|
 | 
						|
    case RETURN_PROTOCOL_ERROR:
 | 
						|
      DEBUG ((
 | 
						|
        DEBUG_VERBOSE,
 | 
						|
        "%a: logic error / system state mismatch\n",
 | 
						|
        __FUNCTION__
 | 
						|
        ));
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      ASSERT (0);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Connect devices based on the boot order retrieved from QEMU.
 | 
						|
 | 
						|
  Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
 | 
						|
  OpenFirmware device paths therein to UEFI device path fragments. Connect the
 | 
						|
  devices identified by the UEFI devpath prefixes as narrowly as possible, then
 | 
						|
  connect all their child devices, recursively.
 | 
						|
 | 
						|
  If this function fails, then platform BDS should fall back to
 | 
						|
  EfiBootManagerConnectAll(), or some other method for connecting any expected
 | 
						|
  boot devices.
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS            The "bootorder" fw_cfg file has been
 | 
						|
                                    parsed, and the referenced device-subtrees
 | 
						|
                                    have been connected.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.
 | 
						|
 | 
						|
  @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
 | 
						|
                                    file.
 | 
						|
 | 
						|
  @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.
 | 
						|
 | 
						|
  @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
 | 
						|
 | 
						|
  @return                           Error statuses propagated from underlying
 | 
						|
                                    functions.
 | 
						|
**/
 | 
						|
RETURN_STATUS
 | 
						|
EFIAPI
 | 
						|
ConnectDevicesFromQemu (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS         Status;
 | 
						|
  FIRMWARE_CONFIG_ITEM  FwCfgItem;
 | 
						|
  UINTN                 FwCfgSize;
 | 
						|
  CHAR8                 *FwCfg;
 | 
						|
  EFI_STATUS            EfiStatus;
 | 
						|
  EXTRA_ROOT_BUS_MAP    *ExtraPciRoots;
 | 
						|
  CONST CHAR8           *FwCfgPtr;
 | 
						|
  UINTN                 NumConnected;
 | 
						|
  UINTN                 TranslatedSize;
 | 
						|
  CHAR16                Translated[TRANSLATION_OUTPUT_SIZE];
 | 
						|
 | 
						|
  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  if (FwCfgSize == 0) {
 | 
						|
    return RETURN_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  FwCfg = AllocatePool (FwCfgSize);
 | 
						|
  if (FwCfg == NULL) {
 | 
						|
    return RETURN_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  QemuFwCfgSelectItem (FwCfgItem);
 | 
						|
  QemuFwCfgReadBytes (FwCfgSize, FwCfg);
 | 
						|
  if (FwCfg[FwCfgSize - 1] != '\0') {
 | 
						|
    Status = RETURN_INVALID_PARAMETER;
 | 
						|
    goto FreeFwCfg;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
 | 
						|
 | 
						|
  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
 | 
						|
    EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
 | 
						|
    if (EFI_ERROR (EfiStatus)) {
 | 
						|
      Status = (RETURN_STATUS)EfiStatus;
 | 
						|
      goto FreeFwCfg;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ExtraPciRoots = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Translate each OpenFirmware path to a UEFI devpath prefix.
 | 
						|
  //
 | 
						|
  FwCfgPtr       = FwCfg;
 | 
						|
  NumConnected   = 0;
 | 
						|
  TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
  Status         = TranslateOfwPath (
 | 
						|
                     &FwCfgPtr,
 | 
						|
                     ExtraPciRoots,
 | 
						|
                     Translated,
 | 
						|
                     &TranslatedSize
 | 
						|
                     );
 | 
						|
  while (!RETURN_ERROR (Status)) {
 | 
						|
    EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | 
						|
    EFI_HANDLE                Controller;
 | 
						|
 | 
						|
    //
 | 
						|
    // Convert the UEFI devpath prefix to binary representation.
 | 
						|
    //
 | 
						|
    ASSERT (Translated[TranslatedSize] == L'\0');
 | 
						|
    DevicePath = ConvertTextToDevicePath (Translated);
 | 
						|
    if (DevicePath == NULL) {
 | 
						|
      Status = RETURN_OUT_OF_RESOURCES;
 | 
						|
      goto FreeExtraPciRoots;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Advance along DevicePath, connecting the nodes individually, and asking
 | 
						|
    // drivers not to produce sibling nodes. Retrieve the controller handle
 | 
						|
    // associated with the full DevicePath -- this is the device that QEMU's
 | 
						|
    // OFW devpath refers to.
 | 
						|
    //
 | 
						|
    EfiStatus = EfiBootManagerConnectDevicePath (DevicePath, &Controller);
 | 
						|
    FreePool (DevicePath);
 | 
						|
    if (EFI_ERROR (EfiStatus)) {
 | 
						|
      Status = (RETURN_STATUS)EfiStatus;
 | 
						|
      goto FreeExtraPciRoots;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Because QEMU's OFW devpaths have lesser expressive power than UEFI
 | 
						|
    // devpaths (i.e., DevicePath is considered a prefix), connect the tree
 | 
						|
    // rooted at Controller, recursively. If no children are produced
 | 
						|
    // (EFI_NOT_FOUND), that's OK.
 | 
						|
    //
 | 
						|
    EfiStatus = gBS->ConnectController (Controller, NULL, NULL, TRUE);
 | 
						|
    if (EFI_ERROR (EfiStatus) && (EfiStatus != EFI_NOT_FOUND)) {
 | 
						|
      Status = (RETURN_STATUS)EfiStatus;
 | 
						|
      goto FreeExtraPciRoots;
 | 
						|
    }
 | 
						|
 | 
						|
    ++NumConnected;
 | 
						|
    //
 | 
						|
    // Move to the next OFW devpath.
 | 
						|
    //
 | 
						|
    TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
    Status         = TranslateOfwPath (
 | 
						|
                       &FwCfgPtr,
 | 
						|
                       ExtraPciRoots,
 | 
						|
                       Translated,
 | 
						|
                       &TranslatedSize
 | 
						|
                       );
 | 
						|
  }
 | 
						|
 | 
						|
  if ((Status == RETURN_NOT_FOUND) && (NumConnected > 0)) {
 | 
						|
    DEBUG ((
 | 
						|
      DEBUG_INFO,
 | 
						|
      "%a: %Lu OpenFirmware device path(s) connected\n",
 | 
						|
      __FUNCTION__,
 | 
						|
      (UINT64)NumConnected
 | 
						|
      ));
 | 
						|
    Status = RETURN_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
FreeExtraPciRoots:
 | 
						|
  if (ExtraPciRoots != NULL) {
 | 
						|
    DestroyExtraRootBusMap (ExtraPciRoots);
 | 
						|
  }
 | 
						|
 | 
						|
FreeFwCfg:
 | 
						|
  FreePool (FwCfg);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Write qemu boot order to uefi variables.
 | 
						|
 | 
						|
  Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate
 | 
						|
  the OpenFirmware device paths therein to UEFI device path fragments.
 | 
						|
 | 
						|
  On Success store the device path in VMMBootOrderNNNN variables.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
StoreQemuBootOrder (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS         Status;
 | 
						|
  FIRMWARE_CONFIG_ITEM  FwCfgItem;
 | 
						|
  UINTN                 FwCfgSize;
 | 
						|
  CHAR8                 *FwCfg;
 | 
						|
  EFI_STATUS            EfiStatus;
 | 
						|
  EXTRA_ROOT_BUS_MAP    *ExtraPciRoots;
 | 
						|
  CONST CHAR8           *FwCfgPtr;
 | 
						|
  UINTN                 TranslatedSize;
 | 
						|
  CHAR16                Translated[TRANSLATION_OUTPUT_SIZE];
 | 
						|
  UINTN                 VariableIndex = 0;
 | 
						|
  CHAR16                VariableName[20];
 | 
						|
 | 
						|
  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (FwCfgSize == 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  FwCfg = AllocatePool (FwCfgSize);
 | 
						|
  if (FwCfg == NULL) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  QemuFwCfgSelectItem (FwCfgItem);
 | 
						|
  QemuFwCfgReadBytes (FwCfgSize, FwCfg);
 | 
						|
  if (FwCfg[FwCfgSize - 1] != '\0') {
 | 
						|
    Status = RETURN_INVALID_PARAMETER;
 | 
						|
    goto FreeFwCfg;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
 | 
						|
 | 
						|
  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
 | 
						|
    EfiStatus = CreateExtraRootBusMap (&ExtraPciRoots);
 | 
						|
    if (EFI_ERROR (EfiStatus)) {
 | 
						|
      Status = (RETURN_STATUS)EfiStatus;
 | 
						|
      goto FreeFwCfg;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ExtraPciRoots = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Translate each OpenFirmware path to a UEFI devpath prefix.
 | 
						|
  //
 | 
						|
  FwCfgPtr       = FwCfg;
 | 
						|
  TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
  Status         = TranslateOfwPath (
 | 
						|
                     &FwCfgPtr,
 | 
						|
                     ExtraPciRoots,
 | 
						|
                     Translated,
 | 
						|
                     &TranslatedSize
 | 
						|
                     );
 | 
						|
  while (Status == EFI_SUCCESS ||
 | 
						|
         Status == EFI_UNSUPPORTED)
 | 
						|
  {
 | 
						|
    if (Status == EFI_SUCCESS) {
 | 
						|
      EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | 
						|
 | 
						|
      //
 | 
						|
      // Convert the UEFI devpath prefix to binary representation.
 | 
						|
      //
 | 
						|
      ASSERT (Translated[TranslatedSize] == L'\0');
 | 
						|
      DevicePath = ConvertTextToDevicePath (Translated);
 | 
						|
      if (DevicePath == NULL) {
 | 
						|
        Status = RETURN_OUT_OF_RESOURCES;
 | 
						|
        goto FreeExtraPciRoots;
 | 
						|
      }
 | 
						|
 | 
						|
      UnicodeSPrint (
 | 
						|
        VariableName,
 | 
						|
        sizeof (VariableName),
 | 
						|
        L"VMMBootOrder%04x",
 | 
						|
        VariableIndex++
 | 
						|
        );
 | 
						|
      DEBUG ((DEBUG_INFO, "%a: %s = %s\n", __FUNCTION__, VariableName, Translated));
 | 
						|
      gRT->SetVariable (
 | 
						|
             VariableName,
 | 
						|
             &gVMMBootOrderGuid,
 | 
						|
             EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
 | 
						|
             GetDevicePathSize (DevicePath),
 | 
						|
             DevicePath
 | 
						|
             );
 | 
						|
      FreePool (DevicePath);
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Move to the next OFW devpath.
 | 
						|
    //
 | 
						|
    TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
    Status         = TranslateOfwPath (
 | 
						|
                       &FwCfgPtr,
 | 
						|
                       ExtraPciRoots,
 | 
						|
                       Translated,
 | 
						|
                       &TranslatedSize
 | 
						|
                       );
 | 
						|
  }
 | 
						|
 | 
						|
FreeExtraPciRoots:
 | 
						|
  if (ExtraPciRoots != NULL) {
 | 
						|
    DestroyExtraRootBusMap (ExtraPciRoots);
 | 
						|
  }
 | 
						|
 | 
						|
FreeFwCfg:
 | 
						|
  FreePool (FwCfg);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Convert the UEFI DevicePath to full text representation with DevPathToText,
 | 
						|
  then match the UEFI device path fragment in Translated against it.
 | 
						|
 | 
						|
  @param[in] Translated        UEFI device path fragment, translated from
 | 
						|
                               OpenFirmware format, to search for.
 | 
						|
 | 
						|
  @param[in] TranslatedLength  The length of Translated in CHAR16's.
 | 
						|
 | 
						|
  @param[in] DevicePath        Boot option device path whose textual rendering
 | 
						|
                               to search in.
 | 
						|
 | 
						|
  @param[in] DevPathToText  Binary-to-text conversion protocol for DevicePath.
 | 
						|
 | 
						|
 | 
						|
  @retval TRUE   If Translated was found at the beginning of DevicePath after
 | 
						|
                 converting the latter to text.
 | 
						|
 | 
						|
  @retval FALSE  If DevicePath was NULL, or it could not be converted, or there
 | 
						|
                 was no match.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
BOOLEAN
 | 
						|
Match (
 | 
						|
  IN  CONST CHAR16              *Translated,
 | 
						|
  IN  UINTN                     TranslatedLength,
 | 
						|
  IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePath
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR16                    *Converted;
 | 
						|
  BOOLEAN                   Result;
 | 
						|
  VOID                      *FileBuffer;
 | 
						|
  UINTN                     FileSize;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *AbsDevicePath;
 | 
						|
  CHAR16                    *AbsConverted;
 | 
						|
  BOOLEAN                   Shortform;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *Node;
 | 
						|
 | 
						|
  Converted = ConvertDevicePathToText (
 | 
						|
                DevicePath,
 | 
						|
                FALSE, // DisplayOnly
 | 
						|
                FALSE  // AllowShortcuts
 | 
						|
                );
 | 
						|
  if (Converted == NULL) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  Result    = FALSE;
 | 
						|
  Shortform = FALSE;
 | 
						|
  //
 | 
						|
  // Expand the short-form device path to full device path
 | 
						|
  //
 | 
						|
  if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
 | 
						|
      (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // Harddrive shortform device path
 | 
						|
    //
 | 
						|
    Shortform = TRUE;
 | 
						|
  } else if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
 | 
						|
             (DevicePathSubType (DevicePath) == MEDIA_FILEPATH_DP))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // File-path shortform device path
 | 
						|
    //
 | 
						|
    Shortform = TRUE;
 | 
						|
  } else if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
 | 
						|
             (DevicePathSubType (DevicePath) == MSG_URI_DP))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // URI shortform device path
 | 
						|
    //
 | 
						|
    Shortform = TRUE;
 | 
						|
  } else {
 | 
						|
    for ( Node = DevicePath
 | 
						|
          ; !IsDevicePathEnd (Node)
 | 
						|
          ; Node = NextDevicePathNode (Node)
 | 
						|
          )
 | 
						|
    {
 | 
						|
      if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
 | 
						|
          ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
 | 
						|
           (DevicePathSubType (Node) == MSG_USB_WWID_DP)))
 | 
						|
      {
 | 
						|
        Shortform = TRUE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Attempt to expand any relative UEFI device path to
 | 
						|
  // an absolute device path first.
 | 
						|
  //
 | 
						|
  if (Shortform) {
 | 
						|
    FileBuffer = EfiBootManagerGetLoadOptionBuffer (
 | 
						|
                   DevicePath,
 | 
						|
                   &AbsDevicePath,
 | 
						|
                   &FileSize
 | 
						|
                   );
 | 
						|
    if (FileBuffer == NULL) {
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    FreePool (FileBuffer);
 | 
						|
    AbsConverted = ConvertDevicePathToText (AbsDevicePath, FALSE, FALSE);
 | 
						|
    FreePool (AbsDevicePath);
 | 
						|
    if (AbsConverted == NULL) {
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG ((
 | 
						|
      DEBUG_VERBOSE,
 | 
						|
      "%a: expanded relative device path \"%s\" for prefix matching\n",
 | 
						|
      __FUNCTION__,
 | 
						|
      Converted
 | 
						|
      ));
 | 
						|
    FreePool (Converted);
 | 
						|
    Converted = AbsConverted;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Is Translated a prefix of Converted?
 | 
						|
  //
 | 
						|
  Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
 | 
						|
  DEBUG ((
 | 
						|
    DEBUG_VERBOSE,
 | 
						|
    "%a: against \"%s\": %a\n",
 | 
						|
    __FUNCTION__,
 | 
						|
    Converted,
 | 
						|
    Result ? "match" : "no match"
 | 
						|
    ));
 | 
						|
Exit:
 | 
						|
  FreePool (Converted);
 | 
						|
  return Result;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Append some of the unselected active boot options to the boot order.
 | 
						|
 | 
						|
  This function should accommodate any further policy changes in "boot option
 | 
						|
  survival". Currently we're adding back everything that starts with neither
 | 
						|
  PciRoot() nor HD() nor a virtio-mmio VenHw() node.
 | 
						|
 | 
						|
  @param[in,out] BootOrder     The structure holding the boot order to
 | 
						|
                               complete. The caller is responsible for
 | 
						|
                               initializing (and potentially populating) it
 | 
						|
                               before calling this function.
 | 
						|
 | 
						|
  @param[in,out] ActiveOption  The array of active boot options to scan.
 | 
						|
                               Entries marked as Appended will be skipped.
 | 
						|
                               Those of the rest that satisfy the survival
 | 
						|
                               policy will be added to BootOrder with
 | 
						|
                               BootOrderAppend().
 | 
						|
 | 
						|
  @param[in]     ActiveCount   Number of elements in ActiveOption.
 | 
						|
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS  BootOrder has been extended with any eligible boot
 | 
						|
                          options.
 | 
						|
 | 
						|
  @return                 Error codes returned by BootOrderAppend().
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
RETURN_STATUS
 | 
						|
BootOrderComplete (
 | 
						|
  IN OUT  BOOT_ORDER     *BootOrder,
 | 
						|
  IN OUT  ACTIVE_OPTION  *ActiveOption,
 | 
						|
  IN      UINTN          ActiveCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS  Status;
 | 
						|
  UINTN          Idx;
 | 
						|
 | 
						|
  Status = RETURN_SUCCESS;
 | 
						|
  Idx    = 0;
 | 
						|
  while (!RETURN_ERROR (Status) && Idx < ActiveCount) {
 | 
						|
    if (!ActiveOption[Idx].Appended) {
 | 
						|
      CONST EFI_BOOT_MANAGER_LOAD_OPTION  *Current;
 | 
						|
      CONST EFI_DEVICE_PATH_PROTOCOL      *FirstNode;
 | 
						|
 | 
						|
      Current   = ActiveOption[Idx].BootOption;
 | 
						|
      FirstNode = Current->FilePath;
 | 
						|
      if (FirstNode != NULL) {
 | 
						|
        CHAR16         *Converted;
 | 
						|
        STATIC CHAR16  ConvFallBack[] = L"<unable to convert>";
 | 
						|
        BOOLEAN        Keep;
 | 
						|
 | 
						|
        Converted = ConvertDevicePathToText (FirstNode, FALSE, FALSE);
 | 
						|
        if (Converted == NULL) {
 | 
						|
          Converted = ConvFallBack;
 | 
						|
        }
 | 
						|
 | 
						|
        Keep = TRUE;
 | 
						|
        if ((DevicePathType (FirstNode) == MEDIA_DEVICE_PATH) &&
 | 
						|
            (DevicePathSubType (FirstNode) == MEDIA_HARDDRIVE_DP))
 | 
						|
        {
 | 
						|
          //
 | 
						|
          // drop HD()
 | 
						|
          //
 | 
						|
          Keep = FALSE;
 | 
						|
        } else if ((DevicePathType (FirstNode) == ACPI_DEVICE_PATH) &&
 | 
						|
                   (DevicePathSubType (FirstNode) == ACPI_DP))
 | 
						|
        {
 | 
						|
          ACPI_HID_DEVICE_PATH  *Acpi;
 | 
						|
 | 
						|
          Acpi = (ACPI_HID_DEVICE_PATH *)FirstNode;
 | 
						|
          if (((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) &&
 | 
						|
              (EISA_ID_TO_NUM (Acpi->HID) == 0x0a03))
 | 
						|
          {
 | 
						|
            //
 | 
						|
            // drop PciRoot() if we enabled the user to select PCI-like boot
 | 
						|
            // options, by providing translation for such OFW device path
 | 
						|
            // fragments
 | 
						|
            //
 | 
						|
            Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);
 | 
						|
          }
 | 
						|
        } else if ((DevicePathType (FirstNode) == HARDWARE_DEVICE_PATH) &&
 | 
						|
                   (DevicePathSubType (FirstNode) == HW_VENDOR_DP))
 | 
						|
        {
 | 
						|
          VENDOR_DEVICE_PATH  *VenHw;
 | 
						|
 | 
						|
          VenHw = (VENDOR_DEVICE_PATH *)FirstNode;
 | 
						|
          if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {
 | 
						|
            //
 | 
						|
            // drop virtio-mmio if we enabled the user to select boot options
 | 
						|
            // referencing such device paths
 | 
						|
            //
 | 
						|
            Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (Keep) {
 | 
						|
          Status = BootOrderAppend (BootOrder, &ActiveOption[Idx]);
 | 
						|
          if (!RETURN_ERROR (Status)) {
 | 
						|
            DEBUG ((
 | 
						|
              DEBUG_VERBOSE,
 | 
						|
              "%a: keeping \"%s\"\n",
 | 
						|
              __FUNCTION__,
 | 
						|
              Converted
 | 
						|
              ));
 | 
						|
          }
 | 
						|
        } else {
 | 
						|
          DEBUG ((
 | 
						|
            DEBUG_VERBOSE,
 | 
						|
            "%a: dropping \"%s\"\n",
 | 
						|
            __FUNCTION__,
 | 
						|
            Converted
 | 
						|
            ));
 | 
						|
        }
 | 
						|
 | 
						|
        if (Converted != ConvFallBack) {
 | 
						|
          FreePool (Converted);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ++Idx;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Delete Boot#### variables that stand for such active boot options that have
 | 
						|
  been dropped (ie. have not been selected by either matching or "survival
 | 
						|
  policy").
 | 
						|
 | 
						|
  @param[in]  ActiveOption  The array of active boot options to scan. Each
 | 
						|
                            entry not marked as appended will trigger the
 | 
						|
                            deletion of the matching Boot#### variable.
 | 
						|
 | 
						|
  @param[in]  ActiveCount   Number of elements in ActiveOption.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
VOID
 | 
						|
PruneBootVariables (
 | 
						|
  IN  CONST ACTIVE_OPTION  *ActiveOption,
 | 
						|
  IN  UINTN                ActiveCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN  Idx;
 | 
						|
 | 
						|
  for (Idx = 0; Idx < ActiveCount; ++Idx) {
 | 
						|
    if (!ActiveOption[Idx].Appended) {
 | 
						|
      CHAR16  VariableName[9];
 | 
						|
 | 
						|
      UnicodeSPrintAsciiFormat (
 | 
						|
        VariableName,
 | 
						|
        sizeof VariableName,
 | 
						|
        "Boot%04x",
 | 
						|
        ActiveOption[Idx].BootOption->OptionNumber
 | 
						|
        );
 | 
						|
 | 
						|
      //
 | 
						|
      // "The space consumed by the deleted variable may not be available until
 | 
						|
      // the next power cycle", but that's good enough.
 | 
						|
      //
 | 
						|
      gRT->SetVariable (
 | 
						|
             VariableName,
 | 
						|
             &gEfiGlobalVariableGuid,
 | 
						|
             0,   // Attributes, 0 means deletion
 | 
						|
             0,   // DataSize, 0 means deletion
 | 
						|
             NULL // Data
 | 
						|
             );
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 | 
						|
  Set the boot order based on configuration retrieved from QEMU.
 | 
						|
 | 
						|
  Attempt to retrieve the "bootorder" fw_cfg file from QEMU. Translate the
 | 
						|
  OpenFirmware device paths therein to UEFI device path fragments. Match the
 | 
						|
  translated fragments against the current list of boot options, and rewrite
 | 
						|
  the BootOrder NvVar so that it corresponds to the order described in fw_cfg.
 | 
						|
 | 
						|
  Platform BDS should call this function after connecting any expected boot
 | 
						|
  devices and calling EfiBootManagerRefreshAllBootOption ().
 | 
						|
 | 
						|
  @retval RETURN_SUCCESS            BootOrder NvVar rewritten.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED        QEMU's fw_cfg is not supported.
 | 
						|
 | 
						|
  @retval RETURN_NOT_FOUND          Empty or nonexistent "bootorder" fw_cfg
 | 
						|
                                    file, or no match found between the
 | 
						|
                                    "bootorder" fw_cfg file and BootOptionList.
 | 
						|
 | 
						|
  @retval RETURN_INVALID_PARAMETER  Parse error in the "bootorder" fw_cfg file.
 | 
						|
 | 
						|
  @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.
 | 
						|
 | 
						|
  @return                           Values returned by gBS->LocateProtocol ()
 | 
						|
                                    or gRT->SetVariable ().
 | 
						|
 | 
						|
**/
 | 
						|
RETURN_STATUS
 | 
						|
EFIAPI
 | 
						|
SetBootOrderFromQemu (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS         Status;
 | 
						|
  FIRMWARE_CONFIG_ITEM  FwCfgItem;
 | 
						|
  UINTN                 FwCfgSize;
 | 
						|
  CHAR8                 *FwCfg;
 | 
						|
  CONST CHAR8           *FwCfgPtr;
 | 
						|
 | 
						|
  BOOT_ORDER     BootOrder;
 | 
						|
  ACTIVE_OPTION  *ActiveOption;
 | 
						|
  UINTN          ActiveCount;
 | 
						|
 | 
						|
  EXTRA_ROOT_BUS_MAP  *ExtraPciRoots;
 | 
						|
 | 
						|
  UINTN                         TranslatedSize;
 | 
						|
  CHAR16                        Translated[TRANSLATION_OUTPUT_SIZE];
 | 
						|
  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions;
 | 
						|
  UINTN                         BootOptionCount;
 | 
						|
 | 
						|
  Status = QemuFwCfgFindFile ("bootorder", &FwCfgItem, &FwCfgSize);
 | 
						|
  if (Status != RETURN_SUCCESS) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  if (FwCfgSize == 0) {
 | 
						|
    return RETURN_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  FwCfg = AllocatePool (FwCfgSize);
 | 
						|
  if (FwCfg == NULL) {
 | 
						|
    return RETURN_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  QemuFwCfgSelectItem (FwCfgItem);
 | 
						|
  QemuFwCfgReadBytes (FwCfgSize, FwCfg);
 | 
						|
  if (FwCfg[FwCfgSize - 1] != '\0') {
 | 
						|
    Status = RETURN_INVALID_PARAMETER;
 | 
						|
    goto ErrorFreeFwCfg;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg:\n", __FUNCTION__));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a\n", FwCfg));
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: FwCfg: <end>\n", __FUNCTION__));
 | 
						|
  FwCfgPtr = FwCfg;
 | 
						|
 | 
						|
  BootOrder.Produced  = 0;
 | 
						|
  BootOrder.Allocated = 1;
 | 
						|
  BootOrder.Data      = AllocatePool (
 | 
						|
                          BootOrder.Allocated * sizeof (*BootOrder.Data)
 | 
						|
                          );
 | 
						|
  if (BootOrder.Data == NULL) {
 | 
						|
    Status = RETURN_OUT_OF_RESOURCES;
 | 
						|
    goto ErrorFreeFwCfg;
 | 
						|
  }
 | 
						|
 | 
						|
  BootOptions = EfiBootManagerGetLoadOptions (
 | 
						|
                  &BootOptionCount,
 | 
						|
                  LoadOptionTypeBoot
 | 
						|
                  );
 | 
						|
  if (BootOptions == NULL) {
 | 
						|
    Status = RETURN_NOT_FOUND;
 | 
						|
    goto ErrorFreeBootOrder;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = CollectActiveOptions (
 | 
						|
             BootOptions,
 | 
						|
             BootOptionCount,
 | 
						|
             &ActiveOption,
 | 
						|
             &ActiveCount
 | 
						|
             );
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    goto ErrorFreeBootOptions;
 | 
						|
  }
 | 
						|
 | 
						|
  if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {
 | 
						|
    Status = CreateExtraRootBusMap (&ExtraPciRoots);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ErrorFreeActiveOption;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ExtraPciRoots = NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // translate each OpenFirmware path
 | 
						|
  //
 | 
						|
  TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
  Status         = TranslateOfwPath (
 | 
						|
                     &FwCfgPtr,
 | 
						|
                     ExtraPciRoots,
 | 
						|
                     Translated,
 | 
						|
                     &TranslatedSize
 | 
						|
                     );
 | 
						|
  while (Status == RETURN_SUCCESS ||
 | 
						|
         Status == RETURN_UNSUPPORTED ||
 | 
						|
         Status == RETURN_PROTOCOL_ERROR ||
 | 
						|
         Status == RETURN_BUFFER_TOO_SMALL)
 | 
						|
  {
 | 
						|
    if (Status == RETURN_SUCCESS) {
 | 
						|
      UINTN  Idx;
 | 
						|
 | 
						|
      //
 | 
						|
      // match translated OpenFirmware path against all active boot options
 | 
						|
      //
 | 
						|
      for (Idx = 0; Idx < ActiveCount; ++Idx) {
 | 
						|
        if (!ActiveOption[Idx].Appended &&
 | 
						|
            Match (
 | 
						|
              Translated,
 | 
						|
              TranslatedSize, // contains length, not size, in CHAR16's here
 | 
						|
              ActiveOption[Idx].BootOption->FilePath
 | 
						|
              )
 | 
						|
            )
 | 
						|
        {
 | 
						|
          //
 | 
						|
          // match found, store ID and continue with next OpenFirmware path
 | 
						|
          //
 | 
						|
          Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);
 | 
						|
          if (Status != RETURN_SUCCESS) {
 | 
						|
            goto ErrorFreeExtraPciRoots;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      } // scanned all active boot options
 | 
						|
    }   // translation successful
 | 
						|
 | 
						|
    TranslatedSize = ARRAY_SIZE (Translated);
 | 
						|
    Status         = TranslateOfwPath (
 | 
						|
                       &FwCfgPtr,
 | 
						|
                       ExtraPciRoots,
 | 
						|
                       Translated,
 | 
						|
                       &TranslatedSize
 | 
						|
                       );
 | 
						|
  } // scanning of OpenFirmware paths done
 | 
						|
 | 
						|
  if ((Status == RETURN_NOT_FOUND) && (BootOrder.Produced > 0)) {
 | 
						|
    //
 | 
						|
    // No more OpenFirmware paths, some matches found: rewrite BootOrder NvVar.
 | 
						|
    // Some of the active boot options that have not been selected over fw_cfg
 | 
						|
    // should be preserved at the end of the boot order.
 | 
						|
    //
 | 
						|
    Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);
 | 
						|
    if (RETURN_ERROR (Status)) {
 | 
						|
      goto ErrorFreeExtraPciRoots;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // See Table 10 in the UEFI Spec 2.3.1 with Errata C for the required
 | 
						|
    // attributes.
 | 
						|
    //
 | 
						|
    Status = gRT->SetVariable (
 | 
						|
                    L"BootOrder",
 | 
						|
                    &gEfiGlobalVariableGuid,
 | 
						|
                    EFI_VARIABLE_NON_VOLATILE |
 | 
						|
                    EFI_VARIABLE_BOOTSERVICE_ACCESS |
 | 
						|
                    EFI_VARIABLE_RUNTIME_ACCESS,
 | 
						|
                    BootOrder.Produced * sizeof (*BootOrder.Data),
 | 
						|
                    BootOrder.Data
 | 
						|
                    );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      DEBUG ((
 | 
						|
        DEBUG_ERROR,
 | 
						|
        "%a: setting BootOrder: %r\n",
 | 
						|
        __FUNCTION__,
 | 
						|
        Status
 | 
						|
        ));
 | 
						|
      goto ErrorFreeExtraPciRoots;
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));
 | 
						|
    PruneBootVariables (ActiveOption, ActiveCount);
 | 
						|
  }
 | 
						|
 | 
						|
ErrorFreeExtraPciRoots:
 | 
						|
  if (ExtraPciRoots != NULL) {
 | 
						|
    DestroyExtraRootBusMap (ExtraPciRoots);
 | 
						|
  }
 | 
						|
 | 
						|
ErrorFreeActiveOption:
 | 
						|
  FreePool (ActiveOption);
 | 
						|
 | 
						|
ErrorFreeBootOptions:
 | 
						|
  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
 | 
						|
 | 
						|
ErrorFreeBootOrder:
 | 
						|
  FreePool (BootOrder.Data);
 | 
						|
 | 
						|
ErrorFreeFwCfg:
 | 
						|
  FreePool (FwCfg);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate the number of seconds we should be showing the FrontPage progress
 | 
						|
  bar for.
 | 
						|
 | 
						|
  @return  The TimeoutDefault argument for PlatformBdsEnterFrontPage().
 | 
						|
**/
 | 
						|
UINT16
 | 
						|
EFIAPI
 | 
						|
GetFrontPageTimeoutFromQemu (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  FIRMWARE_CONFIG_ITEM  BootMenuWaitItem;
 | 
						|
  UINTN                 BootMenuWaitSize;
 | 
						|
  UINT16                Timeout = PcdGet16 (PcdPlatformBootTimeOut);
 | 
						|
 | 
						|
  if (!QemuFwCfgIsAvailable ()) {
 | 
						|
    return Timeout;
 | 
						|
  }
 | 
						|
 | 
						|
  QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);
 | 
						|
  if (QemuFwCfgRead16 () == 0) {
 | 
						|
    //
 | 
						|
    // The user specified "-boot menu=off", or didn't specify "-boot
 | 
						|
    // menu=(on|off)" at all. Return the platform default.
 | 
						|
    //
 | 
						|
    return PcdGet16 (PcdPlatformBootTimeOut);
 | 
						|
  }
 | 
						|
 | 
						|
  if (RETURN_ERROR (
 | 
						|
        QemuFwCfgFindFile (
 | 
						|
          "etc/boot-menu-wait",
 | 
						|
          &BootMenuWaitItem,
 | 
						|
          &BootMenuWaitSize
 | 
						|
          )
 | 
						|
        ) ||
 | 
						|
      (BootMenuWaitSize != sizeof (UINT16)))
 | 
						|
  {
 | 
						|
    //
 | 
						|
    // "-boot menu=on" was specified without "splash-time=N". In this case,
 | 
						|
    // return three seconds if the platform default would cause us to skip the
 | 
						|
    // front page, and return the platform default otherwise.
 | 
						|
    //
 | 
						|
    if (Timeout == 0) {
 | 
						|
      Timeout = 3;
 | 
						|
    }
 | 
						|
 | 
						|
    return Timeout;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // "-boot menu=on,splash-time=N" was specified, where N is in units of
 | 
						|
  // milliseconds. The Intel BDS Front Page progress bar only supports whole
 | 
						|
  // seconds, round N up.
 | 
						|
  //
 | 
						|
  QemuFwCfgSelectItem (BootMenuWaitItem);
 | 
						|
  return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);
 | 
						|
}
 |