**/\r
\r
#include "BdsPlatform.h"\r
-#include "QemuBootOrder.h"\r
+#include <Library/QemuBootOrderLib.h>\r
\r
\r
//\r
VOID *mEmuVariableEventReg;\r
EFI_EVENT mEmuVariableEvent;\r
BOOLEAN mDetectVgaOnly;\r
+UINT16 mHostBridgeDevId;\r
\r
+//\r
+// Table of host IRQs matching PCI IRQs A-D\r
+// (for configuring PCI Interrupt Line register)\r
+//\r
+CONST UINT8 PciHostIrqs[] = {\r
+ 0x0a, 0x0a, 0x0b, 0x0b\r
+};\r
+\r
+//\r
+// Array Size macro\r
+//\r
+#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))\r
\r
//\r
// Type definitions\r
\r
\r
EFI_STATUS\r
+EFIAPI\r
ConnectRootBridge (\r
- VOID\r
+ IN EFI_HANDLE RootBridgeHandle,\r
+ IN VOID *Instance,\r
+ IN VOID *Context\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Connect RootBridge\r
-\r
-Arguments:\r
-\r
- None.\r
-\r
-Returns:\r
-\r
- EFI_SUCCESS - Connect RootBridge successfully.\r
- EFI_STATUS - Connect RootBridge fail.\r
-\r
---*/\r
{\r
- EFI_STATUS Status;\r
- EFI_HANDLE RootHandle;\r
+ EFI_STATUS Status;\r
\r
//\r
- // Make all the PCI_IO protocols on PCI Seg 0 show up\r
+ // Make the PCI bus driver connect the root bridge, non-recursively. This\r
+ // will produce a number of child handles with PciIo on them.\r
//\r
- BdsLibConnectDevicePath (gPlatformRootBridges[0]);\r
-\r
- Status = gBS->LocateDevicePath (\r
- &gEfiDevicePathProtocolGuid,\r
- &gPlatformRootBridges[0],\r
- &RootHandle\r
+ Status = gBS->ConnectController (\r
+ RootBridgeHandle, // ControllerHandle\r
+ NULL, // DriverImageHandle\r
+ NULL, // RemainingDevicePath -- produce all\r
+ // children\r
+ FALSE // Recursive\r
);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- Status = gBS->ConnectController (RootHandle, NULL, NULL, FALSE);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- return EFI_SUCCESS;\r
+ return Status;\r
}\r
\r
\r
}\r
\r
\r
-VOID\r
-PciInitialization (\r
+/**\r
+ Configure PCI Interrupt Line register for applicable devices\r
+ Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()\r
+\r
+ @param[in] Handle - Handle of PCI device instance\r
+ @param[in] PciIo - PCI IO protocol instance\r
+ @param[in] PciHdr - PCI Header register block\r
+\r
+ @retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SetPciIntLine (\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN PCI_TYPE00 *PciHdr\r
)\r
{\r
- //\r
- // Bus 0, Device 0, Function 0 - Host to PCI Bridge\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 0, 0, 0x3c), 0x00);\r
+ EFI_DEVICE_PATH_PROTOCOL *DevPathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;\r
+ UINTN RootSlot;\r
+ UINTN Idx;\r
+ UINT8 IrqLine;\r
+ EFI_STATUS Status;\r
+ UINT32 RootBusNumber;\r
\r
- //\r
- // Bus 0, Device 1, Function 0 - PCI to ISA Bridge\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x3c), 0x00);\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // LNKA routing target\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // LNKB routing target\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // LNKC routing target\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // LNKD routing target\r
+ Status = EFI_SUCCESS;\r
\r
- //\r
- // Bus 0, Device 1, Function 1 - IDE Controller\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 1, 0x3c), 0x00);\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 1, 0x0d), 0x40);\r
+ if (PciHdr->Device.InterruptPin != 0) {\r
\r
- //\r
- // Bus 0, Device 1, Function 3 - Power Managment Controller\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 3, 0x3c), 0x09);\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 1, 3, 0x3d), 0x01); // INTA\r
+ DevPathNode = DevicePathFromHandle (Handle);\r
+ ASSERT (DevPathNode != NULL);\r
+ DevPath = DevPathNode;\r
\r
- //\r
- // Bus 0, Device 2, Function 0 - Video Controller\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 2, 0, 0x3c), 0x00);\r
+ RootBusNumber = 0;\r
+ if (DevicePathType (DevPathNode) == ACPI_DEVICE_PATH &&\r
+ DevicePathSubType (DevPathNode) == ACPI_DP &&\r
+ ((ACPI_HID_DEVICE_PATH *)DevPathNode)->HID == EISA_PNP_ID(0x0A03)) {\r
+ RootBusNumber = ((ACPI_HID_DEVICE_PATH *)DevPathNode)->UID;\r
+ }\r
\r
- //\r
- // Bus 0, Device 3, Function 0 - Network Controller\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3c), 0x0a);\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3d), 0x01); // INTA (-> LNKC)\r
+ //\r
+ // Compute index into PciHostIrqs[] table by walking\r
+ // the device path and adding up all device numbers\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ RootSlot = 0;\r
+ Idx = PciHdr->Device.InterruptPin - 1;\r
+ while (!IsDevicePathEnd (DevPathNode)) {\r
+ if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&\r
+ DevicePathSubType (DevPathNode) == HW_PCI_DP) {\r
\r
- //\r
- // Bus 0, Device 5, Function 0 - RAM Memory\r
- //\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3c), 0x0b);\r
- PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3d), 0x01); // INTA (-> LNKA)\r
+ Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
+\r
+ //\r
+ // Unlike SeaBIOS, which starts climbing from the leaf device\r
+ // up toward the root, we traverse the device path starting at\r
+ // the root moving toward the leaf node.\r
+ // The slot number of the top-level parent bridge is needed for\r
+ // Q35 cases with more than 24 slots on the root bus.\r
+ //\r
+ if (Status != EFI_SUCCESS) {\r
+ Status = EFI_SUCCESS;\r
+ RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;\r
+ }\r
+ }\r
+\r
+ DevPathNode = NextDevicePathNode (DevPathNode);\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ if (RootBusNumber == 0 && RootSlot == 0) {\r
+ DEBUG((\r
+ EFI_D_ERROR,\r
+ "%a: PCI host bridge (00:00.0) should have no interrupts!\n",\r
+ __FUNCTION__\r
+ ));\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ //\r
+ // Final PciHostIrqs[] index calculation depends on the platform\r
+ // and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()\r
+ //\r
+ switch (mHostBridgeDevId) {\r
+ case INTEL_82441_DEVICE_ID:\r
+ Idx -= 1;\r
+ break;\r
+ case INTEL_Q35_MCH_DEVICE_ID:\r
+ //\r
+ // SeaBIOS contains the following comment:\r
+ // "Slots 0-24 rotate slot:pin mapping similar to piix above, but\r
+ // with a different starting index - see q35-acpi-dsdt.dsl.\r
+ //\r
+ // Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"\r
+ //\r
+ if (RootSlot > 24) {\r
+ //\r
+ // in this case, subtract back out RootSlot from Idx\r
+ // (SeaBIOS never adds it to begin with, but that would make our\r
+ // device path traversal loop above too awkward)\r
+ //\r
+ Idx -= RootSlot;\r
+ }\r
+ break;\r
+ default:\r
+ ASSERT (FALSE); // should never get here\r
+ }\r
+ Idx %= ARRAY_SIZE (PciHostIrqs);\r
+ IrqLine = PciHostIrqs[Idx];\r
+\r
+ DEBUG_CODE_BEGIN ();\r
+ {\r
+ CHAR16 *DevPathString;\r
+ STATIC CHAR16 Fallback[] = L"<failed to convert>";\r
+ UINTN Segment, Bus, Device, Function;\r
+\r
+ DevPathString = ConvertDevicePathToText (DevPath, FALSE, FALSE);\r
+ if (DevPathString == NULL) {\r
+ DevPathString = Fallback;\r
+ }\r
+ Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ DEBUG ((EFI_D_VERBOSE, "%a: [%02x:%02x.%x] %s -> 0x%02x\n", __FUNCTION__,\r
+ (UINT32)Bus, (UINT32)Device, (UINT32)Function, DevPathString,\r
+ IrqLine));\r
+\r
+ if (DevPathString != Fallback) {\r
+ FreePool (DevPathString);\r
+ }\r
+ }\r
+ DEBUG_CODE_END ();\r
+\r
+ //\r
+ // Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]\r
+ //\r
+ Status = PciIo->Pci.Write (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ PCI_INT_LINE_OFFSET,\r
+ 1,\r
+ &IrqLine\r
+ );\r
+ }\r
+\r
+ return Status;\r
}\r
\r
\r
VOID\r
-AcpiInitialization (\r
- VOID\r
+PciAcpiInitialization (\r
)\r
{\r
+ UINTN Pmba;\r
+\r
+ //\r
+ // Query Host Bridge DID to determine platform type\r
+ //\r
+ mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
+ switch (mHostBridgeDevId) {\r
+ case INTEL_82441_DEVICE_ID:\r
+ Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
+ //\r
+ // 00:01.0 ISA Bridge (PIIX4) LNK routing targets\r
+ //\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x60), 0x0b); // A\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x61), 0x0b); // B\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x62), 0x0a); // C\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 1, 0, 0x63), 0x0a); // D\r
+ break;\r
+ case INTEL_Q35_MCH_DEVICE_ID:\r
+ Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
+ //\r
+ // 00:1f.0 LPC Bridge (Q35) LNK routing targets\r
+ //\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x60), 0x0a); // A\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x61), 0x0a); // B\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x62), 0x0b); // C\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x63), 0x0b); // D\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x68), 0x0a); // E\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x69), 0x0a); // F\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6a), 0x0b); // G\r
+ PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 0, 0x6b), 0x0b); // H\r
+ break;\r
+ default:\r
+ DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
+ __FUNCTION__, mHostBridgeDevId));\r
+ ASSERT (FALSE);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Initialize PCI_INTERRUPT_LINE for applicable present PCI devices\r
+ //\r
+ VisitAllPciInstances (SetPciIntLine);\r
+\r
//\r
// Set ACPI SCI_EN bit in PMCNTRL\r
//\r
- IoOr16 ((PciRead32 (PCI_LIB_ADDRESS (0, 1, 3, 0x40)) & ~BIT0) + 4, BIT0);\r
+ IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);\r
}\r
\r
\r
//\r
BdsLibConnectAll ();\r
\r
- PciInitialization ();\r
- AcpiInitialization ();\r
+ PciAcpiInitialization ();\r
\r
//\r
// Clear the logo after all devices are connected.\r
}\r
\r
\r
+/**\r
+ Empty callback function executed when the EndOfDxe event group is signaled.\r
+\r
+ We only need this function because we'd like to signal EndOfDxe, and for that\r
+ we need to create an event, with a callback function.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context, which\r
+ is implementation-dependent.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+OnEndOfDxe (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+}\r
+\r
+\r
VOID\r
EFIAPI\r
PlatformBdsPolicyBehavior (\r
--*/\r
{\r
EFI_STATUS Status;\r
- UINT16 Timeout;\r
- EFI_EVENT UserInputDurationTime;\r
- LIST_ENTRY *Link;\r
- BDS_COMMON_OPTION *BootOption;\r
- UINTN Index;\r
- EFI_INPUT_KEY Key;\r
- EFI_TPL OldTpl;\r
EFI_BOOT_MODE BootMode;\r
+ EFI_EVENT EndOfDxeEvent;\r
\r
DEBUG ((EFI_D_INFO, "PlatformBdsPolicyBehavior\n"));\r
\r
- ConnectRootBridge ();\r
+ VisitAllInstancesOfProtocol (&gEfiPciRootBridgeIoProtocolGuid,\r
+ ConnectRootBridge, NULL);\r
+\r
+ //\r
+ // We can't signal End-of-Dxe earlier than this. Namely, End-of-Dxe triggers\r
+ // the preparation of S3 system information. That logic has a hard dependency\r
+ // on the presence of the FACS ACPI table. Since our ACPI tables are only\r
+ // installed after PCI enumeration completes, we must not trigger the S3 save\r
+ // earlier, hence we can't signal End-of-Dxe earlier.\r
+ //\r
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, OnEndOfDxe,\r
+ NULL /* NotifyContext */, &gEfiEndOfDxeEventGroupGuid,\r
+ &EndOfDxeEvent);\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->SignalEvent (EndOfDxeEvent);\r
+ gBS->CloseEvent (EndOfDxeEvent);\r
+ }\r
\r
if (PcdGetBool (PcdOvmfFlashVariablesEnable)) {\r
DEBUG ((EFI_D_INFO, "PlatformBdsPolicyBehavior: not restoring NvVars "\r
PlatformBdsRestoreNvVarsFromHardDisk ();\r
}\r
\r
- //\r
- // Init the time out value\r
- //\r
- Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
-\r
//\r
// Load the driver option as the driver option list\r
//\r
//\r
PlatformBdsNoConsoleAction ();\r
}\r
- //\r
- // Create a 300ms duration event to ensure user has enough input time to enter Setup\r
- //\r
- Status = gBS->CreateEvent (\r
- EVT_TIMER,\r
- 0,\r
- NULL,\r
- NULL,\r
- &UserInputDurationTime\r
- );\r
- ASSERT (Status == EFI_SUCCESS);\r
- Status = gBS->SetTimer (UserInputDurationTime, TimerRelative, 3000000);\r
- ASSERT (Status == EFI_SUCCESS);\r
+\r
//\r
// Memory test and Logo show\r
//\r
//\r
BdsLibBuildOptionFromVar (BootOptionList, L"BootOrder");\r
\r
- //\r
- // To give the User a chance to enter Setup here, if user set TimeOut is 0.\r
- // BDS should still give user a chance to enter Setup\r
- //\r
- // Connect first boot option, and then check user input before exit\r
- //\r
- for (Link = BootOptionList->ForwardLink; Link != BootOptionList;Link = Link->ForwardLink) {\r
- BootOption = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);\r
- if (!IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE)) {\r
- //\r
- // skip the header of the link list, becuase it has no boot option\r
- //\r
- continue;\r
- } else {\r
- //\r
- // Make sure the boot option device path connected, but ignore the BBS device path\r
- //\r
- if (DevicePathType (BootOption->DevicePath) != BBS_DEVICE_PATH) {\r
- BdsLibConnectDevicePath (BootOption->DevicePath);\r
- }\r
- break;\r
- }\r
- }\r
-\r
- //\r
- // Check whether the user input after the duration time has expired\r
- //\r
- OldTpl = EfiGetCurrentTpl();\r
- gBS->RestoreTPL (TPL_APPLICATION);\r
- gBS->WaitForEvent (1, &UserInputDurationTime, &Index);\r
- gBS->CloseEvent (UserInputDurationTime);\r
- Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
- gBS->RaiseTPL (OldTpl);\r
-\r
- if (!EFI_ERROR (Status)) {\r
- //\r
- // Enter Setup if user input\r
- //\r
- Timeout = 0xffff;\r
- PlatformBdsEnterFrontPage (Timeout, FALSE);\r
- }\r
-\r
- return ;\r
+ PlatformBdsEnterFrontPage (GetFrontPageTimeoutFromQemu(), TRUE);\r
}\r
\r
VOID\r