--- /dev/null
+/** @file\r
+ A simple DXE_DRIVER that causes the PCI Bus UEFI_DRIVER to allocate 64-bit\r
+ MMIO BARs above 4 GB, regardless of option ROM availability (as long as a CSM\r
+ is not present), conserving 32-bit MMIO aperture for 32-bit BARs.\r
+\r
+ Copyright (C) 2016, Red Hat, Inc.\r
+\r
+ This program and the accompanying materials are licensed and made available\r
+ under the terms and conditions of the BSD License which accompanies this\r
+ distribution. The full text of the license may be found at\r
+ http://opensource.org/licenses/bsd-license.php\r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include <IndustryStandard/Acpi10.h>\r
+#include <IndustryStandard/Pci22.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+#include <Protocol/IncompatiblePciDeviceSupport.h>\r
+#include <Protocol/LegacyBios.h>\r
+\r
+//\r
+// The Legacy BIOS protocol has been located.\r
+//\r
+STATIC BOOLEAN mLegacyBiosInstalled;\r
+\r
+//\r
+// The protocol interface this driver produces.\r
+//\r
+STATIC EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL\r
+ mIncompatiblePciDeviceSupport;\r
+\r
+//\r
+// Configuration template for the CheckDevice() protocol member function.\r
+//\r
+// Refer to Table 20 "ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage" in\r
+// the Platform Init 1.4a Spec, Volume 5.\r
+//\r
+// This structure is interpreted by the UpdatePciInfo() function in the edk2\r
+// PCI Bus UEFI_DRIVER.\r
+//\r
+#pragma pack (1)\r
+typedef struct {\r
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc;\r
+ EFI_ACPI_END_TAG_DESCRIPTOR EndDesc;\r
+} MMIO64_PREFERENCE;\r
+#pragma pack ()\r
+\r
+STATIC CONST MMIO64_PREFERENCE mConfiguration = {\r
+ //\r
+ // AddressSpaceDesc\r
+ //\r
+ {\r
+ ACPI_ADDRESS_SPACE_DESCRIPTOR, // Desc\r
+ (UINT16)( // Len\r
+ sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) -\r
+ OFFSET_OF (\r
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR,\r
+ ResType\r
+ )\r
+ ),\r
+ ACPI_ADDRESS_SPACE_TYPE_MEM, // ResType\r
+ PCI_ACPI_UNUSED, // GenFlag\r
+ PCI_ACPI_UNUSED, // SpecificFlag\r
+ 64, // AddrSpaceGranularity:\r
+ // aperture selection hint\r
+ // for BAR allocation\r
+ PCI_ACPI_UNUSED, // AddrRangeMin\r
+ PCI_BAR_OLD_ALIGN, // AddrRangeMax:\r
+ // no special alignment\r
+ // for affected BARs\r
+ PCI_BAR_ALL, // AddrTranslationOffset:\r
+ // hint covers all\r
+ // eligible BARs\r
+ PCI_BAR_NOCHANGE // AddrLen:\r
+ // use probed BAR size\r
+ },\r
+ //\r
+ // EndDesc\r
+ //\r
+ {\r
+ ACPI_END_TAG_DESCRIPTOR, // Desc\r
+ 0 // Checksum: to be ignored\r
+ }\r
+};\r
+\r
+//\r
+// The CheckDevice() member function has been called.\r
+//\r
+STATIC BOOLEAN mCheckDeviceCalled;\r
+\r
+\r
+/**\r
+ Notification callback for Legacy BIOS protocol installation.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+\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
+LegacyBiosInstalled (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;\r
+\r
+ ASSERT (!mCheckDeviceCalled);\r
+\r
+ Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid,\r
+ NULL /* Registration */, (VOID **)&LegacyBios);\r
+ if (EFI_ERROR (Status)) {\r
+ return;\r
+ }\r
+\r
+ mLegacyBiosInstalled = TRUE;\r
+\r
+ //\r
+ // Close the event and deregister this callback.\r
+ //\r
+ Status = gBS->CloseEvent (Event);\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r
+\r
+\r
+/**\r
+ Returns a list of ACPI resource descriptors that detail the special resource\r
+ configuration requirements for an incompatible PCI device.\r
+\r
+ Prior to bus enumeration, the PCI bus driver will look for the presence of\r
+ the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL. Only one instance of this\r
+ protocol can be present in the system. For each PCI device that the PCI bus\r
+ driver discovers, the PCI bus driver calls this function with the device's\r
+ vendor ID, device ID, revision ID, subsystem vendor ID, and subsystem device\r
+ ID. If the VendorId, DeviceId, RevisionId, SubsystemVendorId, or\r
+ SubsystemDeviceId value is set to (UINTN)-1, that field will be ignored. The\r
+ ID values that are not (UINTN)-1 will be used to identify the current device.\r
+\r
+ This function will only return EFI_SUCCESS. However, if the device is an\r
+ incompatible PCI device, a list of ACPI resource descriptors will be returned\r
+ in Configuration. Otherwise, NULL will be returned in Configuration instead.\r
+ The PCI bus driver does not need to allocate memory for Configuration.\r
+ However, it is the PCI bus driver's responsibility to free it. The PCI bus\r
+ driver then can configure this device with the information that is derived\r
+ from this list of resource nodes, rather than the result of BAR probing.\r
+\r
+ Only the following two resource descriptor types from the ACPI Specification\r
+ may be used to describe the incompatible PCI device resource requirements:\r
+ - QWORD Address Space Descriptor (ACPI 2.0, section 6.4.3.5.1; also ACPI 3.0)\r
+ - End Tag (ACPI 2.0, section 6.4.2.8; also ACPI 3.0)\r
+\r
+ The QWORD Address Space Descriptor can describe memory, I/O, and bus number\r
+ ranges for dynamic or fixed resources. The configuration of a PCI root bridge\r
+ is described with one or more QWORD Address Space Descriptors, followed by an\r
+ End Tag. See the ACPI Specification for details on the field values.\r
+\r
+ @param[in] This Pointer to the\r
+ EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL\r
+ instance.\r
+\r
+ @param[in] VendorId A unique ID to identify the manufacturer of\r
+ the PCI device. See the Conventional PCI\r
+ Specification 3.0 for details.\r
+\r
+ @param[in] DeviceId A unique ID to identify the particular PCI\r
+ device. See the Conventional PCI\r
+ Specification 3.0 for details.\r
+\r
+ @param[in] RevisionId A PCI device-specific revision identifier.\r
+ See the Conventional PCI Specification 3.0\r
+ for details.\r
+\r
+ @param[in] SubsystemVendorId Specifies the subsystem vendor ID. See the\r
+ Conventional PCI Specification 3.0 for\r
+ details.\r
+\r
+ @param[in] SubsystemDeviceId Specifies the subsystem device ID. See the\r
+ Conventional PCI Specification 3.0 for\r
+ details.\r
+\r
+ @param[out] Configuration A list of ACPI resource descriptors that\r
+ detail the configuration requirement.\r
+\r
+ @retval EFI_SUCCESS The function always returns EFI_SUCCESS.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+CheckDevice (\r
+ IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This,\r
+ IN UINTN VendorId,\r
+ IN UINTN DeviceId,\r
+ IN UINTN RevisionId,\r
+ IN UINTN SubsystemVendorId,\r
+ IN UINTN SubsystemDeviceId,\r
+ OUT VOID **Configuration\r
+ )\r
+{\r
+ mCheckDeviceCalled = TRUE;\r
+\r
+ //\r
+ // Unlike the general description of this protocol member suggests, there is\r
+ // nothing incompatible about the PCI devices that we'll match here. We'll\r
+ // match all PCI devices, and generate exactly one QWORD Address Space\r
+ // Descriptor for each. That descriptor will instruct the PCI Bus UEFI_DRIVER\r
+ // not to degrade 64-bit MMIO BARs for the device, even if a PCI option ROM\r
+ // BAR is present on the device.\r
+ //\r
+ // The concern captured in the PCI Bus UEFI_DRIVER is that a legacy BIOS boot\r
+ // (via a CSM) could dispatch a legacy option ROM on the device, which might\r
+ // have trouble with MMIO BARs that have been allocated outside of the 32-bit\r
+ // address space. But, if we don't support legacy option ROMs at all, then\r
+ // this problem cannot arise.\r
+ //\r
+ if (mLegacyBiosInstalled) {\r
+ //\r
+ // Don't interfere with resource degradation.\r
+ //\r
+ *Configuration = NULL;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // This member function is mis-specified actually: it is supposed to allocate\r
+ // memory, but as specified, it could not return an error status. Thankfully,\r
+ // the edk2 PCI Bus UEFI_DRIVER actually handles error codes; see the\r
+ // UpdatePciInfo() function.\r
+ //\r
+ *Configuration = AllocateCopyPool (sizeof mConfiguration, &mConfiguration);\r
+ if (*Configuration == NULL) {\r
+ DEBUG ((EFI_D_WARN,\r
+ "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n",\r
+ __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Entry point for this driver.\r
+\r
+ @param[in] ImageHandle Image handle of this driver.\r
+ @param[in] SystemTable Pointer to SystemTable.\r
+\r
+ @retval EFI_SUCESS Driver has loaded successfully.\r
+ @retval EFI_UNSUPPORTED PCI resource allocation has been disabled.\r
+ @retval EFI_UNSUPPORTED There is no 64-bit PCI MMIO aperture.\r
+ @return Error codes from lower level functions.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DriverInitialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT Event;\r
+ VOID *Registration;\r
+\r
+ //\r
+ // If the PCI Bus driver is not supposed to allocate resources, then it makes\r
+ // no sense to install a protocol that influences the resource allocation.\r
+ //\r
+ // Similarly, if there is no 64-bit PCI MMIO aperture, then 64-bit MMIO BARs\r
+ // have to be allocated under 4 GB unconditionally.\r
+ //\r
+ if (PcdGetBool (PcdPciDisableBusEnumeration) ||\r
+ PcdGet64 (PcdPciMmio64Size) == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Otherwise, create a protocol notify to see if a CSM is present. (With the\r
+ // CSM absent, the PCI Bus driver won't have to worry about allocating 64-bit\r
+ // MMIO BARs in the 32-bit MMIO aperture, for the sake of a legacy BIOS.)\r
+ //\r
+ // If the Legacy BIOS Protocol is present at the time of this driver starting\r
+ // up, we can mark immediately that the PCI Bus driver should perform the\r
+ // usual 64-bit MMIO BAR degradation.\r
+ //\r
+ // Otherwise, if the Legacy BIOS Protocol is absent at startup, it may be\r
+ // installed later. However, if it doesn't show up until the first\r
+ // EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL.CheckDevice() call from the\r
+ // PCI Bus driver, then it never will:\r
+ //\r
+ // 1. The following drivers are dispatched in some unspecified order:\r
+ // - PCI Host Bridge DXE_DRIVER,\r
+ // - PCI Bus UEFI_DRIVER,\r
+ // - this DXE_DRIVER,\r
+ // - Legacy BIOS DXE_DRIVER.\r
+ //\r
+ // 2. The DXE_CORE enters BDS.\r
+ //\r
+ // 3. The platform BDS connects the PCI Root Bridge IO instances (produced by\r
+ // the PCI Host Bridge DXE_DRIVER).\r
+ //\r
+ // 4. The PCI Bus UEFI_DRIVER enumerates resources and calls into this\r
+ // DXE_DRIVER (CheckDevice()).\r
+ //\r
+ // 5. This driver remembers if EFI_LEGACY_BIOS_PROTOCOL has been installed\r
+ // sometime during step 1 (produced by the Legacy BIOS DXE_DRIVER).\r
+ //\r
+ // For breaking this order, the Legacy BIOS DXE_DRIVER would have to install\r
+ // its protocol after the firmware enters BDS, which cannot happen.\r
+ //\r
+ Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,\r
+ LegacyBiosInstalled, NULL /* Context */, &Event);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->RegisterProtocolNotify (&gEfiLegacyBiosProtocolGuid, Event,\r
+ &Registration);\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseEvent;\r
+ }\r
+\r
+ Status = gBS->SignalEvent (Event);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ mIncompatiblePciDeviceSupport.CheckDevice = CheckDevice;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,\r
+ &gEfiIncompatiblePciDeviceSupportProtocolGuid,\r
+ &mIncompatiblePciDeviceSupport, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseEvent;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+CloseEvent:\r
+ if (!mLegacyBiosInstalled) {\r
+ EFI_STATUS CloseStatus;\r
+\r
+ CloseStatus = gBS->CloseEvent (Event);\r
+ ASSERT_EFI_ERROR (CloseStatus);\r
+ }\r
+\r
+ return Status;\r
+}\r