2012-08-28 01:28:30 +02:00
|
|
|
/** @file
|
|
|
|
Rewrite the BootOrder NvVar based on QEMU's "bootorder" fw_cfg file.
|
|
|
|
|
|
|
|
Copyright (C) 2012, Red Hat, Inc.
|
|
|
|
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
|
|
distribution. The full text of the license may be found at
|
|
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include <Library/QemuFwCfgLib.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
#include <Library/GenericBdsLib.h>
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
|
|
#include <Library/PrintLib.h>
|
|
|
|
#include <Protocol/DevicePathToText.h>
|
|
|
|
#include <Guid/GlobalVariable.h>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
OpenFirmware to UEFI device path translation output buffer size in CHAR16's.
|
|
|
|
**/
|
|
|
|
#define TRANSLATION_OUTPUT_SIZE 0x100
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2012-10-08 09:33:25 +02:00
|
|
|
Numbers of nodes in OpenFirmware device paths that are required and examined.
|
2012-08-28 01:28:30 +02:00
|
|
|
**/
|
2012-10-08 09:33:25 +02:00
|
|
|
#define REQUIRED_OFW_NODES 2
|
|
|
|
#define EXAMINED_OFW_NODES 4
|
2012-08-28 01:28:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-10-03 22:22:17 +02:00
|
|
|
return (BOOLEAN)(Pos == Substring.Len && *Chr == '\0');
|
2012-08-28 01:28:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
Parse a comma-separated list of hexadecimal integers into the elements of an
|
|
|
|
UINT32 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 UINT32 *Result,
|
|
|
|
IN OUT UINTN *NumResults
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN Entry; // number of entry currently being parsed
|
|
|
|
UINT32 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 > 0xFFFFFFF) {
|
|
|
|
return RETURN_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
EntryVal = (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;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
Append BootOptionId 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] BootOptionId The value to append to the array.
|
|
|
|
|
|
|
|
|
|
|
|
@retval RETURN_SUCCESS BootOptionId appended.
|
|
|
|
|
|
|
|
@retval RETURN_OUT_OF_RESOURCES Memory reallocation failed.
|
|
|
|
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
RETURN_STATUS
|
|
|
|
BootOrderAppend (
|
|
|
|
IN OUT BOOT_ORDER *BootOrder,
|
|
|
|
IN UINT16 BootOptionId
|
|
|
|
)
|
|
|
|
{
|
|
|
|
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++] = BootOptionId;
|
|
|
|
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 successul 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
|
|
|
|
)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// unit-address
|
|
|
|
//
|
|
|
|
if (**Ptr != '@') {
|
|
|
|
return RETURN_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
++*Ptr;
|
|
|
|
|
|
|
|
OfwNode->UnitAddress.Ptr = *Ptr;
|
|
|
|
OfwNode->UnitAddress.Len = 0;
|
|
|
|
while (IsPrintNotDelim (**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 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[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
|
|
|
|
TranslateOfwNodes (
|
|
|
|
IN CONST OFW_NODE *OfwNode,
|
|
|
|
IN UINTN NumNodes,
|
|
|
|
OUT CHAR16 *Translated,
|
|
|
|
IN OUT UINTN *TranslatedSize
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT32 PciDevFun[2];
|
|
|
|
UINTN NumEntries;
|
|
|
|
UINTN Written;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get PCI device and optional PCI function. Assume a single PCI root.
|
|
|
|
//
|
2012-10-08 09:33:25 +02:00
|
|
|
if (NumNodes < REQUIRED_OFW_NODES ||
|
2012-08-28 01:28:30 +02:00
|
|
|
!SubstringEq (OfwNode[0].DriverName, "pci")
|
|
|
|
) {
|
|
|
|
return RETURN_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
PciDevFun[1] = 0;
|
|
|
|
NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);
|
|
|
|
if (ParseUnitAddressHexList (
|
|
|
|
OfwNode[1].UnitAddress,
|
|
|
|
PciDevFun,
|
|
|
|
&NumEntries
|
|
|
|
) != RETURN_SUCCESS
|
|
|
|
) {
|
|
|
|
return RETURN_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
2012-10-08 09:33:25 +02:00
|
|
|
if (NumNodes >= 4 &&
|
|
|
|
SubstringEq (OfwNode[1].DriverName, "ide") &&
|
2012-08-28 01:28:30 +02:00
|
|
|
SubstringEq (OfwNode[2].DriverName, "drive") &&
|
|
|
|
SubstringEq (OfwNode[3].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
|
|
|
|
//
|
|
|
|
UINT32 Secondary;
|
|
|
|
UINT32 Slave;
|
|
|
|
|
|
|
|
NumEntries = 1;
|
|
|
|
if (ParseUnitAddressHexList (
|
|
|
|
OfwNode[2].UnitAddress,
|
|
|
|
&Secondary,
|
|
|
|
&NumEntries
|
|
|
|
) != RETURN_SUCCESS ||
|
|
|
|
Secondary > 1 ||
|
|
|
|
ParseUnitAddressHexList (
|
|
|
|
OfwNode[3].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(0x0)/Pci(0x%x,0x%x)/Ata(%a,%a,0x0)",
|
|
|
|
PciDevFun[0],
|
|
|
|
PciDevFun[1],
|
|
|
|
Secondary ? "Secondary" : "Primary",
|
|
|
|
Slave ? "Slave" : "Master"
|
|
|
|
);
|
2012-10-08 09:33:25 +02:00
|
|
|
} else if (NumNodes >= 4 &&
|
|
|
|
SubstringEq (OfwNode[1].DriverName, "isa") &&
|
2012-08-28 01:28:30 +02:00
|
|
|
SubstringEq (OfwNode[2].DriverName, "fdc") &&
|
|
|
|
SubstringEq (OfwNode[3].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
|
|
|
|
//
|
|
|
|
UINT32 AcpiUid;
|
|
|
|
|
|
|
|
NumEntries = 1;
|
|
|
|
if (ParseUnitAddressHexList (
|
|
|
|
OfwNode[3].UnitAddress,
|
|
|
|
&AcpiUid,
|
|
|
|
&NumEntries
|
|
|
|
) != RETURN_SUCCESS ||
|
|
|
|
AcpiUid > 1
|
|
|
|
) {
|
|
|
|
return RETURN_UNSUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
Written = UnicodeSPrintAsciiFormat (
|
|
|
|
Translated,
|
|
|
|
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes
|
|
|
|
"PciRoot(0x0)/Pci(0x%x,0x%x)/Floppy(0x%x)",
|
|
|
|
PciDevFun[0],
|
|
|
|
PciDevFun[1],
|
|
|
|
AcpiUid
|
|
|
|
);
|
2012-10-08 09:33:12 +02:00
|
|
|
} else {
|
|
|
|
return RETURN_UNSUPPORTED;
|
|
|
|
}
|
2012-08-28 01:28:30 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// 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 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[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_NOT_FOUND Translation terminated, *Ptr was (and is)
|
|
|
|
pointing to an empty string.
|
|
|
|
|
|
|
|
@retval RETURN_INVALID_PARAMETER Parse error. This is a permanent error.
|
|
|
|
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
RETURN_STATUS
|
|
|
|
TranslateOfwPath (
|
|
|
|
IN OUT CONST CHAR8 **Ptr,
|
|
|
|
OUT CHAR16 *Translated,
|
|
|
|
IN OUT UINTN *TranslatedSize
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN NumNodes;
|
|
|
|
RETURN_STATUS Status;
|
2012-10-08 09:33:25 +02:00
|
|
|
OFW_NODE Node[EXAMINED_OFW_NODES];
|
2012-08-28 01:28:30 +02:00
|
|
|
BOOLEAN IsFinal;
|
|
|
|
OFW_NODE Skip;
|
|
|
|
|
|
|
|
NumNodes = 0;
|
|
|
|
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,
|
2012-10-08 09:33:25 +02:00
|
|
|
(NumNodes < EXAMINED_OFW_NODES) ? &Node[NumNodes] : &Skip,
|
2012-08-28 01:28:30 +02:00
|
|
|
&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,
|
2012-10-08 09:33:25 +02:00
|
|
|
NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,
|
2012-08-28 01:28:30 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ASSERT (0);
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
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 CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
|
|
IN CONST EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText
|
|
|
|
)
|
|
|
|
{
|
|
|
|
CHAR16 *Converted;
|
|
|
|
BOOLEAN Result;
|
|
|
|
|
|
|
|
Converted = DevPathToText->ConvertDevicePathToText (
|
|
|
|
DevicePath,
|
|
|
|
FALSE, // DisplayOnly
|
|
|
|
FALSE // AllowShortcuts
|
|
|
|
);
|
|
|
|
if (Converted == NULL) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Is Translated a prefix of Converted?
|
|
|
|
//
|
2012-10-03 22:22:17 +02:00
|
|
|
Result = (BOOLEAN)(StrnCmp (Converted, Translated, TranslatedLength) == 0);
|
2012-08-28 01:28:30 +02:00
|
|
|
DEBUG ((
|
|
|
|
DEBUG_VERBOSE,
|
|
|
|
"%a: against \"%s\": %a\n",
|
|
|
|
__FUNCTION__,
|
|
|
|
Converted,
|
|
|
|
Result ? "match" : "no match"
|
|
|
|
));
|
|
|
|
FreePool (Converted);
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
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 BootOptionList, and rewrite the BootOrder NvVar
|
|
|
|
so that it corresponds to the order described in fw_cfg.
|
|
|
|
|
|
|
|
@param[in] BootOptionList A boot option list, created with
|
|
|
|
BdsLibEnumerateAllBootOption ().
|
|
|
|
|
|
|
|
|
|
|
|
@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
|
|
|
|
SetBootOrderFromQemu (
|
|
|
|
IN CONST LIST_ENTRY *BootOptionList
|
|
|
|
)
|
|
|
|
{
|
|
|
|
RETURN_STATUS Status;
|
|
|
|
|
|
|
|
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;
|
|
|
|
|
|
|
|
FIRMWARE_CONFIG_ITEM FwCfgItem;
|
|
|
|
UINTN FwCfgSize;
|
|
|
|
CHAR8 *FwCfg;
|
|
|
|
CONST CHAR8 *FwCfgPtr;
|
|
|
|
|
|
|
|
BOOT_ORDER BootOrder;
|
|
|
|
|
|
|
|
UINTN TranslatedSize;
|
|
|
|
CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];
|
|
|
|
|
|
|
|
Status = gBS->LocateProtocol (
|
|
|
|
&gEfiDevicePathToTextProtocolGuid,
|
|
|
|
NULL, // optional registration key
|
|
|
|
(VOID **) &DevPathToText
|
|
|
|
);
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// translate each OpenFirmware path
|
|
|
|
//
|
|
|
|
TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
|
|
|
|
Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);
|
|
|
|
while (Status == RETURN_SUCCESS ||
|
|
|
|
Status == RETURN_UNSUPPORTED ||
|
|
|
|
Status == RETURN_BUFFER_TOO_SMALL) {
|
|
|
|
if (Status == RETURN_SUCCESS) {
|
|
|
|
CONST LIST_ENTRY *Link;
|
|
|
|
|
|
|
|
//
|
|
|
|
// match translated OpenFirmware path against all enumerated boot options
|
|
|
|
//
|
|
|
|
for (Link = BootOptionList->ForwardLink; Link != BootOptionList;
|
|
|
|
Link = Link->ForwardLink) {
|
|
|
|
CONST BDS_COMMON_OPTION *BootOption;
|
|
|
|
|
|
|
|
BootOption = CR (
|
|
|
|
Link,
|
|
|
|
BDS_COMMON_OPTION,
|
|
|
|
Link,
|
|
|
|
BDS_LOAD_OPTION_SIGNATURE
|
|
|
|
);
|
|
|
|
if (IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE) &&
|
|
|
|
Match (
|
|
|
|
Translated,
|
|
|
|
TranslatedSize, // contains length, not size, in CHAR16's here
|
|
|
|
BootOption->DevicePath,
|
|
|
|
DevPathToText
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
//
|
|
|
|
// match found, store ID and continue with next OpenFirmware path
|
|
|
|
//
|
|
|
|
Status = BootOrderAppend (&BootOrder, BootOption->BootCurrent);
|
|
|
|
if (Status != RETURN_SUCCESS) {
|
|
|
|
goto ErrorFreeBootOrder;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} // scanned all enumerated boot options
|
|
|
|
} // translation successful
|
|
|
|
|
|
|
|
TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);
|
|
|
|
Status = TranslateOfwPath (&FwCfgPtr, 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.
|
|
|
|
// 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
|
|
|
|
);
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"%a: setting BootOrder: %a\n",
|
|
|
|
__FUNCTION__,
|
|
|
|
Status == EFI_SUCCESS ? "success" : "error"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorFreeBootOrder:
|
|
|
|
FreePool (BootOrder.Data);
|
|
|
|
|
|
|
|
ErrorFreeFwCfg:
|
|
|
|
FreePool (FwCfg);
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|