#include <Library/PrintLib.h>\r
#include <Library/DevicePathLib.h>\r
#include <Library/QemuBootOrderLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
#include <Guid/GlobalVariable.h>\r
+#include <Guid/VirtioMmioTransport.h>\r
\r
+#include "ExtraRootBusMap.h"\r
\r
/**\r
OpenFirmware to UEFI device path translation output buffer size in CHAR16's.\r
**/\r
#define TRANSLATION_OUTPUT_SIZE 0x100\r
\r
+/**\r
+ Output buffer size for OpenFirmware to UEFI device path fragment translation,\r
+ in CHAR16's, for a sequence of PCI bridges.\r
+**/\r
+#define BRIDGE_TRANSLATION_OUTPUT_SIZE 0x40\r
\r
/**\r
Numbers of nodes in OpenFirmware device paths that are required and examined.\r
**/\r
#define REQUIRED_PCI_OFW_NODES 2\r
-#define EXAMINED_OFW_NODES 4\r
+#define REQUIRED_MMIO_OFW_NODES 1\r
+#define EXAMINED_OFW_NODES 6\r
\r
\r
/**\r
\r
@param[in] NumNodes Number of elements in OfwNode.\r
\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
@param[out] Translated Destination array receiving the UEFI path\r
fragment, allocated by the caller. If the\r
return value differs from RETURN_SUCCESS, its\r
@retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
be translated in the current implementation.\r
\r
+ @retval RETURN_PROTOCOL_ERROR The initial OpenFirmware node refers to an\r
+ extra PCI root bus (by serial number) that\r
+ is invalid according to ExtraPciRoots.\r
+\r
**/\r
STATIC\r
RETURN_STATUS\r
TranslatePciOfwNodes (\r
- IN CONST OFW_NODE *OfwNode,\r
- IN UINTN NumNodes,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
)\r
{\r
+ UINT32 PciRoot;\r
+ CHAR8 *Comma;\r
+ UINTN FirstNonBridge;\r
+ CHAR16 Bridges[BRIDGE_TRANSLATION_OUTPUT_SIZE];\r
+ UINTN BridgesLen;\r
UINT64 PciDevFun[2];\r
UINTN NumEntries;\r
UINTN Written;\r
\r
//\r
- // Get PCI device and optional PCI function. Assume a single PCI root.\r
+ // Resolve the PCI root bus number.\r
+ //\r
+ // The initial OFW node for the main root bus (ie. bus number 0) is:\r
+ //\r
+ // /pci@i0cf8\r
+ //\r
+ // For extra root buses, the initial OFW node is\r
+ //\r
+ // /pci@i0cf8,4\r
+ // ^\r
+ // root bus serial number (not PCI bus number)\r
//\r
if (NumNodes < REQUIRED_PCI_OFW_NODES ||\r
!SubstringEq (OfwNode[0].DriverName, "pci")\r
) {\r
return RETURN_UNSUPPORTED;\r
}\r
+\r
+ PciRoot = 0;\r
+ Comma = ScanMem8 (OfwNode[0].UnitAddress.Ptr, OfwNode[0].UnitAddress.Len,\r
+ ',');\r
+ if (Comma != NULL) {\r
+ SUBSTRING PciRootSerialSubString;\r
+ UINT64 PciRootSerial;\r
+\r
+ //\r
+ // Parse the root bus serial number from the unit address after the comma.\r
+ //\r
+ PciRootSerialSubString.Ptr = Comma + 1;\r
+ PciRootSerialSubString.Len = OfwNode[0].UnitAddress.Len -\r
+ (PciRootSerialSubString.Ptr -\r
+ OfwNode[0].UnitAddress.Ptr);\r
+ NumEntries = 1;\r
+ if (RETURN_ERROR (ParseUnitAddressHexList (PciRootSerialSubString,\r
+ &PciRootSerial, &NumEntries))) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Map the extra root bus's serial number to its actual bus number.\r
+ //\r
+ if (EFI_ERROR (MapRootBusPosToBusNr (ExtraPciRoots, PciRootSerial,\r
+ &PciRoot))) {\r
+ return RETURN_PROTOCOL_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Translate a sequence of PCI bridges. For each bridge, the OFW node is:\r
+ //\r
+ // pci-bridge@1e[,0]\r
+ // ^ ^\r
+ // PCI slot & function on the parent, holding the bridge\r
+ //\r
+ // and the UEFI device path node is:\r
+ //\r
+ // Pci(0x1E,0x0)\r
+ //\r
+ FirstNonBridge = 1;\r
+ Bridges[0] = L'\0';\r
+ BridgesLen = 0;\r
+ do {\r
+ UINT64 BridgeDevFun[2];\r
+ UINTN BridgesFreeBytes;\r
+\r
+ if (!SubstringEq (OfwNode[FirstNonBridge].DriverName, "pci-bridge")) {\r
+ break;\r
+ }\r
+\r
+ BridgeDevFun[1] = 0;\r
+ NumEntries = sizeof BridgeDevFun / sizeof BridgeDevFun[0];\r
+ if (ParseUnitAddressHexList (OfwNode[FirstNonBridge].UnitAddress,\r
+ BridgeDevFun, &NumEntries) != RETURN_SUCCESS) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ BridgesFreeBytes = sizeof Bridges - BridgesLen * sizeof Bridges[0];\r
+ Written = UnicodeSPrintAsciiFormat (Bridges + BridgesLen, BridgesFreeBytes,\r
+ "/Pci(0x%Lx,0x%Lx)", BridgeDevFun[0], BridgeDevFun[1]);\r
+ BridgesLen += Written;\r
+\r
+ //\r
+ // There's no way to differentiate between "completely used up without\r
+ // truncation" and "truncated", so treat the former as the latter.\r
+ //\r
+ if (BridgesLen + 1 == BRIDGE_TRANSLATION_OUTPUT_SIZE) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ ++FirstNonBridge;\r
+ } while (FirstNonBridge < NumNodes);\r
+\r
+ if (FirstNonBridge == NumNodes) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Parse the OFW nodes starting with the first non-bridge node.\r
+ //\r
PciDevFun[1] = 0;\r
NumEntries = sizeof (PciDevFun) / sizeof (PciDevFun[0]);\r
if (ParseUnitAddressHexList (\r
- OfwNode[1].UnitAddress,\r
+ OfwNode[FirstNonBridge].UnitAddress,\r
PciDevFun,\r
&NumEntries\r
) != RETURN_SUCCESS\r
return RETURN_UNSUPPORTED;\r
}\r
\r
- if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "ide") &&\r
- SubstringEq (OfwNode[2].DriverName, "drive") &&\r
- SubstringEq (OfwNode[3].DriverName, "disk")\r
+ if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "ide") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
) {\r
//\r
// OpenFirmware device path (IDE disk, IDE CD-ROM):\r
\r
NumEntries = 1;\r
if (ParseUnitAddressHexList (\r
- OfwNode[2].UnitAddress,\r
+ OfwNode[FirstNonBridge + 1].UnitAddress,\r
&Secondary,\r
&NumEntries\r
) != RETURN_SUCCESS ||\r
Secondary > 1 ||\r
ParseUnitAddressHexList (\r
- OfwNode[3].UnitAddress,\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
&Slave,\r
&NumEntries // reuse after previous single-element call\r
) != RETURN_SUCCESS ||\r
Written = UnicodeSPrintAsciiFormat (\r
Translated,\r
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Ata(%a,%a,0x0)",\r
+ PciRoot,\r
+ Bridges,\r
PciDevFun[0],\r
PciDevFun[1],\r
Secondary ? "Secondary" : "Primary",\r
Slave ? "Slave" : "Master"\r
);\r
- } else if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "isa") &&\r
- SubstringEq (OfwNode[2].DriverName, "fdc") &&\r
- SubstringEq (OfwNode[3].DriverName, "floppy")\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,2922") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "drive") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (Q35 SATA disk and CD-ROM):\r
+ //\r
+ // /pci@i0cf8/pci8086,2922@1f,2/drive@1/disk@0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | device number (fixed 0)\r
+ // | | | channel (port) number\r
+ // | PCI slot & function holding SATA HBA\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x1,0xFFFF,0x0)\r
+ // ^ ^ ^\r
+ // | | LUN (always 0 on Q35)\r
+ // | port multiplier port number,\r
+ // | always 0xFFFF on Q35\r
+ // channel (port) number\r
+ //\r
+ UINT64 Channel;\r
+\r
+ NumEntries = 1;\r
+ if (RETURN_ERROR (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 1].UnitAddress, &Channel,\r
+ &NumEntries))) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Sata(0x%Lx,0xFFFF,0x0)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Channel\r
+ );\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "isa") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "fdc") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "floppy")\r
) {\r
//\r
// OpenFirmware device path (floppy disk):\r
\r
NumEntries = 1;\r
if (ParseUnitAddressHexList (\r
- OfwNode[3].UnitAddress,\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
&AcpiUid,\r
&NumEntries\r
) != RETURN_SUCCESS ||\r
Written = UnicodeSPrintAsciiFormat (\r
Translated,\r
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Floppy(0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
PciDevFun[0],\r
PciDevFun[1],\r
AcpiUid\r
);\r
- } else if (NumNodes >= 3 &&\r
- SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[2].DriverName, "disk")\r
+ } else if (NumNodes >= FirstNonBridge + 2 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "disk")\r
) {\r
//\r
// OpenFirmware device path (virtio-blk disk):\r
Written = UnicodeSPrintAsciiFormat (\r
Translated,\r
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/HD(",\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/HD(",\r
+ PciRoot,\r
+ Bridges,\r
PciDevFun[0],\r
PciDevFun[1]\r
);\r
- } else if (NumNodes >= 4 &&\r
- SubstringEq (OfwNode[1].DriverName, "scsi") &&\r
- SubstringEq (OfwNode[2].DriverName, "channel") &&\r
- SubstringEq (OfwNode[3].DriverName, "disk")\r
+ } else if (NumNodes >= FirstNonBridge + 3 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "scsi") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "channel") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 2].DriverName, "disk")\r
) {\r
//\r
// OpenFirmware device path (virtio-scsi disk):\r
TargetLun[1] = 0;\r
NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
if (ParseUnitAddressHexList (\r
- OfwNode[3].UnitAddress,\r
+ OfwNode[FirstNonBridge + 2].UnitAddress,\r
TargetLun,\r
&NumEntries\r
) != RETURN_SUCCESS\r
Written = UnicodeSPrintAsciiFormat (\r
Translated,\r
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/Scsi(0x%Lx,0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
PciDevFun[0],\r
PciDevFun[1],\r
TargetLun[0],\r
TargetLun[1]\r
);\r
+ } else if (NumNodes >= FirstNonBridge + 2 &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 0].DriverName, "pci8086,5845") &&\r
+ SubstringEq (OfwNode[FirstNonBridge + 1].DriverName, "namespace")\r
+ ) {\r
+ //\r
+ // OpenFirmware device path (NVMe device):\r
+ //\r
+ // /pci@i0cf8/pci8086,5845@6[,1]/namespace@1,0\r
+ // ^ ^ ^ ^ ^\r
+ // | | | | Extended Unique Identifier\r
+ // | | | | (EUI-64), big endian interp.\r
+ // | | | namespace ID\r
+ // | PCI slot & function holding NVMe controller\r
+ // PCI root at system bus port, PIO\r
+ //\r
+ // UEFI device path:\r
+ //\r
+ // PciRoot(0x0)/Pci(0x6,0x1)/NVMe(0x1,00-00-00-00-00-00-00-00)\r
+ // ^ ^\r
+ // | octets of the EUI-64\r
+ // | in address order\r
+ // namespace ID\r
+ //\r
+ UINT64 Namespace[2];\r
+ UINTN RequiredEntries;\r
+ UINT8 *Eui64;\r
+\r
+ RequiredEntries = sizeof (Namespace) / sizeof (Namespace[0]);\r
+ NumEntries = RequiredEntries;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[FirstNonBridge + 1].UnitAddress,\r
+ Namespace,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS ||\r
+ NumEntries != RequiredEntries ||\r
+ Namespace[0] == 0 ||\r
+ Namespace[0] >= MAX_UINT32\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Eui64 = (UINT8 *)&Namespace[1];\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)/"\r
+ "NVMe(0x%Lx,%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x)",\r
+ PciRoot,\r
+ Bridges,\r
+ PciDevFun[0],\r
+ PciDevFun[1],\r
+ Namespace[0],\r
+ Eui64[7], Eui64[6], Eui64[5], Eui64[4],\r
+ Eui64[3], Eui64[2], Eui64[1], Eui64[0]\r
+ );\r
} else {\r
//\r
// Generic OpenFirmware device path for PCI devices:\r
Written = UnicodeSPrintAsciiFormat (\r
Translated,\r
*TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
- "PciRoot(0x0)/Pci(0x%Lx,0x%Lx)",\r
+ "PciRoot(0x%x)%s/Pci(0x%Lx,0x%Lx)",\r
+ PciRoot,\r
+ Bridges,\r
PciDevFun[0],\r
PciDevFun[1]\r
);\r
}\r
\r
\r
+//\r
+// A type providing easy raw access to the base address of a virtio-mmio\r
+// transport.\r
+//\r
+typedef union {\r
+ UINT64 Uint64;\r
+ UINT8 Raw[8];\r
+} VIRTIO_MMIO_BASE_ADDRESS;\r
+\r
+\r
/**\r
\r
- Translate an array of OpenFirmware device nodes to a UEFI device path\r
- fragment.\r
+ Translate an MMIO-like array of OpenFirmware device nodes to a UEFI device\r
+ path fragment.\r
\r
@param[in] OfwNode Array of OpenFirmware device nodes to\r
translate, constituting the beginning of an\r
**/\r
STATIC\r
RETURN_STATUS\r
-TranslateOfwNodes (\r
+TranslateMmioOfwNodes (\r
IN CONST OFW_NODE *OfwNode,\r
IN UINTN NumNodes,\r
OUT CHAR16 *Translated,\r
IN OUT UINTN *TranslatedSize\r
)\r
+{\r
+ VIRTIO_MMIO_BASE_ADDRESS VirtioMmioBase;\r
+ CHAR16 VenHwString[60 + 1];\r
+ UINTN NumEntries;\r
+ UINTN Written;\r
+\r
+ //\r
+ // Get the base address of the virtio-mmio transport.\r
+ //\r
+ if (NumNodes < REQUIRED_MMIO_OFW_NODES ||\r
+ !SubstringEq (OfwNode[0].DriverName, "virtio-mmio")\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ NumEntries = 1;\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[0].UnitAddress,\r
+ &VirtioMmioBase.Uint64,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ UnicodeSPrintAsciiFormat (VenHwString, sizeof VenHwString,\r
+ "VenHw(%g,%02X%02X%02X%02X%02X%02X%02X%02X)", &gVirtioMmioTransportGuid,\r
+ VirtioMmioBase.Raw[0], VirtioMmioBase.Raw[1], VirtioMmioBase.Raw[2],\r
+ VirtioMmioBase.Raw[3], VirtioMmioBase.Raw[4], VirtioMmioBase.Raw[5],\r
+ VirtioMmioBase.Raw[6], VirtioMmioBase.Raw[7]);\r
+\r
+ if (NumNodes >= 2 &&\r
+ SubstringEq (OfwNode[1].DriverName, "disk")) {\r
+ //\r
+ // OpenFirmware device path (virtio-blk disk):\r
+ //\r
+ // /virtio-mmio@000000000a003c00/disk@0,0\r
+ // ^ ^ ^\r
+ // | fixed\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // <VenHwString>/HD(\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/HD(",\r
+ VenHwString\r
+ );\r
+ } else if (NumNodes >= 3 &&\r
+ SubstringEq (OfwNode[1].DriverName, "channel") &&\r
+ SubstringEq (OfwNode[2].DriverName, "disk")) {\r
+ //\r
+ // OpenFirmware device path (virtio-scsi disk):\r
+ //\r
+ // /virtio-mmio@000000000a003a00/channel@0/disk@2,3\r
+ // ^ ^ ^ ^\r
+ // | | | LUN\r
+ // | | target\r
+ // | channel (unused, fixed 0)\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix:\r
+ //\r
+ // <VenHwString>/Scsi(0x2,0x3)\r
+ //\r
+ UINT64 TargetLun[2];\r
+\r
+ TargetLun[1] = 0;\r
+ NumEntries = sizeof (TargetLun) / sizeof (TargetLun[0]);\r
+ if (ParseUnitAddressHexList (\r
+ OfwNode[2].UnitAddress,\r
+ TargetLun,\r
+ &NumEntries\r
+ ) != RETURN_SUCCESS\r
+ ) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/Scsi(0x%Lx,0x%Lx)",\r
+ VenHwString,\r
+ TargetLun[0],\r
+ TargetLun[1]\r
+ );\r
+ } else if (NumNodes >= 2 &&\r
+ SubstringEq (OfwNode[1].DriverName, "ethernet-phy")) {\r
+ //\r
+ // OpenFirmware device path (virtio-net NIC):\r
+ //\r
+ // /virtio-mmio@000000000a003e00/ethernet-phy@0\r
+ // ^ ^\r
+ // | fixed\r
+ // base address of virtio-mmio register block\r
+ //\r
+ // UEFI device path prefix (dependent on presence of nonzero PCI function):\r
+ //\r
+ // <VenHwString>/MAC(\r
+ //\r
+ Written = UnicodeSPrintAsciiFormat (\r
+ Translated,\r
+ *TranslatedSize * sizeof (*Translated), // BufferSize in bytes\r
+ "%s/MAC(",\r
+ VenHwString\r
+ );\r
+ } else {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // There's no way to differentiate between "completely used up without\r
+ // truncation" and "truncated", so treat the former as the latter, and return\r
+ // success only for "some room left unused".\r
+ //\r
+ if (Written + 1 < *TranslatedSize) {\r
+ *TranslatedSize = Written;\r
+ return RETURN_SUCCESS;\r
+ }\r
+\r
+ return RETURN_BUFFER_TOO_SMALL;\r
+}\r
+\r
+\r
+/**\r
+\r
+ Translate an array of OpenFirmware device nodes to a UEFI device path\r
+ fragment.\r
+\r
+ @param[in] OfwNode Array of OpenFirmware device nodes to\r
+ translate, constituting the beginning of an\r
+ OpenFirmware device path.\r
+\r
+ @param[in] NumNodes Number of elements in OfwNode.\r
+\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
+ @param[out] Translated Destination array receiving the UEFI path\r
+ fragment, allocated by the caller. If the\r
+ return value differs from RETURN_SUCCESS, its\r
+ contents is indeterminate.\r
+\r
+ @param[in out] TranslatedSize On input, the number of CHAR16's in\r
+ Translated. On RETURN_SUCCESS this parameter\r
+ is assigned the number of non-NUL CHAR16's\r
+ written to Translated. In case of other return\r
+ values, TranslatedSize is indeterminate.\r
+\r
+\r
+ @retval RETURN_SUCCESS Translation successful.\r
+\r
+ @retval RETURN_BUFFER_TOO_SMALL The translation does not fit into the number\r
+ of bytes provided.\r
+\r
+ @retval RETURN_UNSUPPORTED The array of OpenFirmware device nodes can't\r
+ be translated in the current implementation.\r
+\r
+ @retval RETURN_PROTOCOL_ERROR The array of OpenFirmware device nodes has\r
+ been (partially) recognized, but it contains\r
+ a logic error / doesn't match system state.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+TranslateOfwNodes (\r
+ IN CONST OFW_NODE *OfwNode,\r
+ IN UINTN NumNodes,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
+ )\r
{\r
RETURN_STATUS Status;\r
\r
Status = RETURN_UNSUPPORTED;\r
\r
if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
- Status = TranslatePciOfwNodes (OfwNode, NumNodes, Translated,\r
+ Status = TranslatePciOfwNodes (OfwNode, NumNodes, ExtraPciRoots,\r
+ Translated, TranslatedSize);\r
+ }\r
+ if (Status == RETURN_UNSUPPORTED &&\r
+ FeaturePcdGet (PcdQemuBootOrderMmioTranslation)) {\r
+ Status = TranslateMmioOfwNodes (OfwNode, NumNodes, Translated,\r
TranslatedSize);\r
}\r
return Status;\r
characters. In other error cases, it points to\r
the byte that caused the error.\r
\r
+ @param[in] ExtraPciRoots An EXTRA_ROOT_BUS_MAP object created with\r
+ CreateExtraRootBusMap(), to be used for\r
+ translating positions of extra root buses to\r
+ bus numbers.\r
+\r
@param[out] Translated Destination array receiving the UEFI path\r
fragment, allocated by the caller. If the\r
return value differs from RETURN_SUCCESS, its\r
the current implementation. Further calls\r
to this function are possible.\r
\r
+ @retval RETURN_PROTOCOL_ERROR The OpenFirmware device path has been\r
+ (partially) recognized, but it contains a\r
+ logic error / doesn't match system state.\r
+ Further calls to this function are\r
+ possible.\r
+\r
@retval RETURN_NOT_FOUND Translation terminated. On input, *Ptr was\r
pointing to the empty string or "HALT". On\r
output, *Ptr points to the empty string\r
STATIC\r
RETURN_STATUS\r
TranslateOfwPath (\r
- IN OUT CONST CHAR8 **Ptr,\r
- OUT CHAR16 *Translated,\r
- IN OUT UINTN *TranslatedSize\r
+ IN OUT CONST CHAR8 **Ptr,\r
+ IN CONST EXTRA_ROOT_BUS_MAP *ExtraPciRoots,\r
+ OUT CHAR16 *Translated,\r
+ IN OUT UINTN *TranslatedSize\r
)\r
{\r
UINTN NumNodes;\r
Status = TranslateOfwNodes (\r
Node,\r
NumNodes < EXAMINED_OFW_NODES ? NumNodes : EXAMINED_OFW_NODES,\r
+ ExtraPciRoots,\r
Translated,\r
TranslatedSize);\r
switch (Status) {\r
DEBUG ((DEBUG_VERBOSE, "%a: unsupported\n", __FUNCTION__));\r
break;\r
\r
+ case RETURN_PROTOCOL_ERROR:\r
+ DEBUG ((DEBUG_VERBOSE, "%a: logic error / system state mismatch\n",\r
+ __FUNCTION__));\r
+ break;\r
+\r
default:\r
ASSERT (0);\r
}\r
\r
This function should accommodate any further policy changes in "boot option\r
survival". Currently we're adding back everything that starts with neither\r
- PciRoot() nor HD().\r
+ PciRoot() nor HD() nor a virtio-mmio VenHw() node.\r
\r
@param[in,out] BootOrder The structure holding the boot order to\r
complete. The caller is responsible for\r
//\r
Keep = !FeaturePcdGet (PcdQemuBootOrderPciTranslation);\r
}\r
+ } else if (DevicePathType(FirstNode) == HARDWARE_DEVICE_PATH &&\r
+ DevicePathSubType(FirstNode) == HW_VENDOR_DP) {\r
+ VENDOR_DEVICE_PATH *VenHw;\r
+\r
+ VenHw = (VENDOR_DEVICE_PATH *)FirstNode;\r
+ if (CompareGuid (&VenHw->Guid, &gVirtioMmioTransportGuid)) {\r
+ //\r
+ // drop virtio-mmio if we enabled the user to select boot options\r
+ // referencing such device paths\r
+ //\r
+ Keep = !FeaturePcdGet (PcdQemuBootOrderMmioTranslation);\r
+ }\r
}\r
\r
if (Keep) {\r
ACTIVE_OPTION *ActiveOption;\r
UINTN ActiveCount;\r
\r
+ EXTRA_ROOT_BUS_MAP *ExtraPciRoots;\r
+\r
UINTN TranslatedSize;\r
CHAR16 Translated[TRANSLATION_OUTPUT_SIZE];\r
\r
goto ErrorFreeBootOrder;\r
}\r
\r
+ if (FeaturePcdGet (PcdQemuBootOrderPciTranslation)) {\r
+ Status = CreateExtraRootBusMap (&ExtraPciRoots);\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorFreeActiveOption;\r
+ }\r
+ } else {\r
+ ExtraPciRoots = NULL;\r
+ }\r
+\r
//\r
// translate each OpenFirmware path\r
//\r
TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
- Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+ &TranslatedSize);\r
while (Status == RETURN_SUCCESS ||\r
Status == RETURN_UNSUPPORTED ||\r
+ Status == RETURN_PROTOCOL_ERROR ||\r
Status == RETURN_BUFFER_TOO_SMALL) {\r
if (Status == RETURN_SUCCESS) {\r
UINTN Idx;\r
//\r
Status = BootOrderAppend (&BootOrder, &ActiveOption[Idx]);\r
if (Status != RETURN_SUCCESS) {\r
- goto ErrorFreeActiveOption;\r
+ goto ErrorFreeExtraPciRoots;\r
}\r
break;\r
}\r
} // translation successful\r
\r
TranslatedSize = sizeof (Translated) / sizeof (Translated[0]);\r
- Status = TranslateOfwPath (&FwCfgPtr, Translated, &TranslatedSize);\r
+ Status = TranslateOfwPath (&FwCfgPtr, ExtraPciRoots, Translated,\r
+ &TranslatedSize);\r
} // scanning of OpenFirmware paths done\r
\r
if (Status == RETURN_NOT_FOUND && BootOrder.Produced > 0) {\r
//\r
Status = BootOrderComplete (&BootOrder, ActiveOption, ActiveCount);\r
if (RETURN_ERROR (Status)) {\r
- goto ErrorFreeActiveOption;\r
+ goto ErrorFreeExtraPciRoots;\r
}\r
\r
//\r
);\r
if (EFI_ERROR (Status)) {\r
DEBUG ((DEBUG_ERROR, "%a: setting BootOrder: %r\n", __FUNCTION__, Status));\r
- goto ErrorFreeActiveOption;\r
+ goto ErrorFreeExtraPciRoots;\r
}\r
\r
DEBUG ((DEBUG_INFO, "%a: setting BootOrder: success\n", __FUNCTION__));\r
PruneBootVariables (ActiveOption, ActiveCount);\r
}\r
\r
+ErrorFreeExtraPciRoots:\r
+ if (ExtraPciRoots != NULL) {\r
+ DestroyExtraRootBusMap (ExtraPciRoots);\r
+ }\r
+\r
ErrorFreeActiveOption:\r
FreePool (ActiveOption);\r
\r
\r
return Status;\r
}\r
+\r
+\r
+/**\r
+ Calculate the number of seconds we should be showing the FrontPage progress\r
+ bar for.\r
+\r
+ @return The TimeoutDefault argument for PlatformBdsEnterFrontPage().\r
+**/\r
+UINT16\r
+GetFrontPageTimeoutFromQemu (\r
+ VOID\r
+ )\r
+{\r
+ FIRMWARE_CONFIG_ITEM BootMenuWaitItem;\r
+ UINTN BootMenuWaitSize;\r
+\r
+ QemuFwCfgSelectItem (QemuFwCfgItemBootMenu);\r
+ if (QemuFwCfgRead16 () == 0) {\r
+ //\r
+ // The user specified "-boot menu=off", or didn't specify "-boot\r
+ // menu=(on|off)" at all. Return the platform default.\r
+ //\r
+ return PcdGet16 (PcdPlatformBootTimeOut);\r
+ }\r
+\r
+ if (RETURN_ERROR (QemuFwCfgFindFile ("etc/boot-menu-wait", &BootMenuWaitItem,\r
+ &BootMenuWaitSize)) ||\r
+ BootMenuWaitSize != sizeof (UINT16)) {\r
+ //\r
+ // "-boot menu=on" was specified without "splash-time=N". In this case,\r
+ // return three seconds if the platform default would cause us to skip the\r
+ // front page, and return the platform default otherwise.\r
+ //\r
+ UINT16 Timeout;\r
+\r
+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+ if (Timeout == 0) {\r
+ Timeout = 3;\r
+ }\r
+ return Timeout;\r
+ }\r
+\r
+ //\r
+ // "-boot menu=on,splash-time=N" was specified, where N is in units of\r
+ // milliseconds. The Intel BDS Front Page progress bar only supports whole\r
+ // seconds, round N up.\r
+ //\r
+ QemuFwCfgSelectItem (BootMenuWaitItem);\r
+ return (UINT16)((QemuFwCfgRead16 () + 999) / 1000);\r
+}\r