diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c new file mode 100644 index 0000000000..b6ff12842c --- /dev/null +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c @@ -0,0 +1,353 @@ +/** @file + A simple DXE_DRIVER that causes the PCI Bus UEFI_DRIVER to allocate 64-bit + MMIO BARs above 4 GB, regardless of option ROM availability (as long as a CSM + is not present), conserving 32-bit MMIO aperture for 32-bit BARs. + + Copyright (C) 2016, 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 +#include + +#include +#include +#include +#include + +#include +#include + +// +// The Legacy BIOS protocol has been located. +// +STATIC BOOLEAN mLegacyBiosInstalled; + +// +// The protocol interface this driver produces. +// +STATIC EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL + mIncompatiblePciDeviceSupport; + +// +// Configuration template for the CheckDevice() protocol member function. +// +// Refer to Table 20 "ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage" in +// the Platform Init 1.4a Spec, Volume 5. +// +// This structure is interpreted by the UpdatePciInfo() function in the edk2 +// PCI Bus UEFI_DRIVER. +// +#pragma pack (1) +typedef struct { + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR AddressSpaceDesc; + EFI_ACPI_END_TAG_DESCRIPTOR EndDesc; +} MMIO64_PREFERENCE; +#pragma pack () + +STATIC CONST MMIO64_PREFERENCE mConfiguration = { + // + // AddressSpaceDesc + // + { + ACPI_ADDRESS_SPACE_DESCRIPTOR, // Desc + (UINT16)( // Len + sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - + OFFSET_OF ( + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR, + ResType + ) + ), + ACPI_ADDRESS_SPACE_TYPE_MEM, // ResType + PCI_ACPI_UNUSED, // GenFlag + PCI_ACPI_UNUSED, // SpecificFlag + 64, // AddrSpaceGranularity: + // aperture selection hint + // for BAR allocation + PCI_ACPI_UNUSED, // AddrRangeMin + PCI_BAR_OLD_ALIGN, // AddrRangeMax: + // no special alignment + // for affected BARs + PCI_BAR_ALL, // AddrTranslationOffset: + // hint covers all + // eligible BARs + PCI_BAR_NOCHANGE // AddrLen: + // use probed BAR size + }, + // + // EndDesc + // + { + ACPI_END_TAG_DESCRIPTOR, // Desc + 0 // Checksum: to be ignored + } +}; + +// +// The CheckDevice() member function has been called. +// +STATIC BOOLEAN mCheckDeviceCalled; + + +/** + Notification callback for Legacy BIOS protocol installation. + + @param[in] Event Event whose notification function is being invoked. + + @param[in] Context The pointer to the notification function's context, which + is implementation-dependent. +**/ +STATIC +VOID +EFIAPI +LegacyBiosInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; + + ASSERT (!mCheckDeviceCalled); + + Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, + NULL /* Registration */, (VOID **)&LegacyBios); + if (EFI_ERROR (Status)) { + return; + } + + mLegacyBiosInstalled = TRUE; + + // + // Close the event and deregister this callback. + // + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); +} + + +/** + Returns a list of ACPI resource descriptors that detail the special resource + configuration requirements for an incompatible PCI device. + + Prior to bus enumeration, the PCI bus driver will look for the presence of + the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL. Only one instance of this + protocol can be present in the system. For each PCI device that the PCI bus + driver discovers, the PCI bus driver calls this function with the device's + vendor ID, device ID, revision ID, subsystem vendor ID, and subsystem device + ID. If the VendorId, DeviceId, RevisionId, SubsystemVendorId, or + SubsystemDeviceId value is set to (UINTN)-1, that field will be ignored. The + ID values that are not (UINTN)-1 will be used to identify the current device. + + This function will only return EFI_SUCCESS. However, if the device is an + incompatible PCI device, a list of ACPI resource descriptors will be returned + in Configuration. Otherwise, NULL will be returned in Configuration instead. + The PCI bus driver does not need to allocate memory for Configuration. + However, it is the PCI bus driver's responsibility to free it. The PCI bus + driver then can configure this device with the information that is derived + from this list of resource nodes, rather than the result of BAR probing. + + Only the following two resource descriptor types from the ACPI Specification + may be used to describe the incompatible PCI device resource requirements: + - QWORD Address Space Descriptor (ACPI 2.0, section 6.4.3.5.1; also ACPI 3.0) + - End Tag (ACPI 2.0, section 6.4.2.8; also ACPI 3.0) + + The QWORD Address Space Descriptor can describe memory, I/O, and bus number + ranges for dynamic or fixed resources. The configuration of a PCI root bridge + is described with one or more QWORD Address Space Descriptors, followed by an + End Tag. See the ACPI Specification for details on the field values. + + @param[in] This Pointer to the + EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL + instance. + + @param[in] VendorId A unique ID to identify the manufacturer of + the PCI device. See the Conventional PCI + Specification 3.0 for details. + + @param[in] DeviceId A unique ID to identify the particular PCI + device. See the Conventional PCI + Specification 3.0 for details. + + @param[in] RevisionId A PCI device-specific revision identifier. + See the Conventional PCI Specification 3.0 + for details. + + @param[in] SubsystemVendorId Specifies the subsystem vendor ID. See the + Conventional PCI Specification 3.0 for + details. + + @param[in] SubsystemDeviceId Specifies the subsystem device ID. See the + Conventional PCI Specification 3.0 for + details. + + @param[out] Configuration A list of ACPI resource descriptors that + detail the configuration requirement. + + @retval EFI_SUCCESS The function always returns EFI_SUCCESS. +**/ +STATIC +EFI_STATUS +EFIAPI +CheckDevice ( + IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This, + IN UINTN VendorId, + IN UINTN DeviceId, + IN UINTN RevisionId, + IN UINTN SubsystemVendorId, + IN UINTN SubsystemDeviceId, + OUT VOID **Configuration + ) +{ + mCheckDeviceCalled = TRUE; + + // + // Unlike the general description of this protocol member suggests, there is + // nothing incompatible about the PCI devices that we'll match here. We'll + // match all PCI devices, and generate exactly one QWORD Address Space + // Descriptor for each. That descriptor will instruct the PCI Bus UEFI_DRIVER + // not to degrade 64-bit MMIO BARs for the device, even if a PCI option ROM + // BAR is present on the device. + // + // The concern captured in the PCI Bus UEFI_DRIVER is that a legacy BIOS boot + // (via a CSM) could dispatch a legacy option ROM on the device, which might + // have trouble with MMIO BARs that have been allocated outside of the 32-bit + // address space. But, if we don't support legacy option ROMs at all, then + // this problem cannot arise. + // + if (mLegacyBiosInstalled) { + // + // Don't interfere with resource degradation. + // + *Configuration = NULL; + return EFI_SUCCESS; + } + + // + // This member function is mis-specified actually: it is supposed to allocate + // memory, but as specified, it could not return an error status. Thankfully, + // the edk2 PCI Bus UEFI_DRIVER actually handles error codes; see the + // UpdatePciInfo() function. + // + *Configuration = AllocateCopyPool (sizeof mConfiguration, &mConfiguration); + if (*Configuration == NULL) { + DEBUG ((EFI_D_WARN, + "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", + __FUNCTION__, (UINT32)VendorId, (UINT32)DeviceId, (UINT8)RevisionId)); + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + + +/** + Entry point for this driver. + + @param[in] ImageHandle Image handle of this driver. + @param[in] SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS Driver has loaded successfully. + @retval EFI_UNSUPPORTED PCI resource allocation has been disabled. + @retval EFI_UNSUPPORTED There is no 64-bit PCI MMIO aperture. + @return Error codes from lower level functions. + +**/ +EFI_STATUS +EFIAPI +DriverInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + VOID *Registration; + + // + // If the PCI Bus driver is not supposed to allocate resources, then it makes + // no sense to install a protocol that influences the resource allocation. + // + // Similarly, if there is no 64-bit PCI MMIO aperture, then 64-bit MMIO BARs + // have to be allocated under 4 GB unconditionally. + // + if (PcdGetBool (PcdPciDisableBusEnumeration) || + PcdGet64 (PcdPciMmio64Size) == 0) { + return EFI_UNSUPPORTED; + } + + // + // Otherwise, create a protocol notify to see if a CSM is present. (With the + // CSM absent, the PCI Bus driver won't have to worry about allocating 64-bit + // MMIO BARs in the 32-bit MMIO aperture, for the sake of a legacy BIOS.) + // + // If the Legacy BIOS Protocol is present at the time of this driver starting + // up, we can mark immediately that the PCI Bus driver should perform the + // usual 64-bit MMIO BAR degradation. + // + // Otherwise, if the Legacy BIOS Protocol is absent at startup, it may be + // installed later. However, if it doesn't show up until the first + // EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL.CheckDevice() call from the + // PCI Bus driver, then it never will: + // + // 1. The following drivers are dispatched in some unspecified order: + // - PCI Host Bridge DXE_DRIVER, + // - PCI Bus UEFI_DRIVER, + // - this DXE_DRIVER, + // - Legacy BIOS DXE_DRIVER. + // + // 2. The DXE_CORE enters BDS. + // + // 3. The platform BDS connects the PCI Root Bridge IO instances (produced by + // the PCI Host Bridge DXE_DRIVER). + // + // 4. The PCI Bus UEFI_DRIVER enumerates resources and calls into this + // DXE_DRIVER (CheckDevice()). + // + // 5. This driver remembers if EFI_LEGACY_BIOS_PROTOCOL has been installed + // sometime during step 1 (produced by the Legacy BIOS DXE_DRIVER). + // + // For breaking this order, the Legacy BIOS DXE_DRIVER would have to install + // its protocol after the firmware enters BDS, which cannot happen. + // + Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, + LegacyBiosInstalled, NULL /* Context */, &Event); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->RegisterProtocolNotify (&gEfiLegacyBiosProtocolGuid, Event, + &Registration); + if (EFI_ERROR (Status)) { + goto CloseEvent; + } + + Status = gBS->SignalEvent (Event); + ASSERT_EFI_ERROR (Status); + + mIncompatiblePciDeviceSupport.CheckDevice = CheckDevice; + Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, + &gEfiIncompatiblePciDeviceSupportProtocolGuid, + &mIncompatiblePciDeviceSupport, NULL); + if (EFI_ERROR (Status)) { + goto CloseEvent; + } + + return EFI_SUCCESS; + +CloseEvent: + if (!mLegacyBiosInstalled) { + EFI_STATUS CloseStatus; + + CloseStatus = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (CloseStatus); + } + + return Status; +} diff --git a/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf new file mode 100644 index 0000000000..883e45c174 --- /dev/null +++ b/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf @@ -0,0 +1,50 @@ +## @file +# A simple DXE_DRIVER that causes the PCI Bus UEFI_DRIVER to allocate 64-bit +# MMIO BARs above 4 GB, regardless of option ROM availability (as long as a CSM +# is not present), conserving 32-bit MMIO aperture for 32-bit BARs. +# +# Copyright (C) 2016, 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. +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = IncompatiblePciDeviceSupportDxe + FILE_GUID = F6697AC4-A776-4EE1-B643-1FEFF2B615BB + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = DriverInitialize + +[Sources] + IncompatiblePciDeviceSupport.c + +[Packages] + IntelFrameworkPkg/IntelFrameworkPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + DebugLib + MemoryAllocationLib + PcdLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiIncompatiblePciDeviceSupportProtocolGuid ## SOMETIMES_PRODUCES + gEfiLegacyBiosProtocolGuid ## NOTIFY + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration ## CONSUMES + gUefiOvmfPkgTokenSpaceGuid.PcdPciMmio64Size ## CONSUMES + +[Depex] + TRUE diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index ccb3ffd0a5..1eb5d0bc9b 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -548,6 +548,7 @@ UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf UefiCpuPkg/CpuDxe/CpuDxe.inf PcAtChipsetPkg/8254TimerDxe/8254Timer.inf + OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf { PciHostBridgeLib|OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index 594e84fd71..8ab1633f83 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -207,6 +207,7 @@ INF PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf INF UefiCpuPkg/CpuDxe/CpuDxe.inf INF PcAtChipsetPkg/8254TimerDxe/8254Timer.inf +INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf INF PcAtChipsetPkg/KbcResetDxe/Reset.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index ee681fb1fd..ea381a023f 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -557,6 +557,7 @@ UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf UefiCpuPkg/CpuDxe/CpuDxe.inf PcAtChipsetPkg/8254TimerDxe/8254Timer.inf + OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf { PciHostBridgeLib|OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index 36a8f7e279..85fead07e9 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -207,6 +207,7 @@ INF PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf INF UefiCpuPkg/CpuDxe/CpuDxe.inf INF PcAtChipsetPkg/8254TimerDxe/8254Timer.inf +INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf INF PcAtChipsetPkg/KbcResetDxe/Reset.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 6991662907..d37a206d52 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -555,6 +555,7 @@ UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf UefiCpuPkg/CpuDxe/CpuDxe.inf PcAtChipsetPkg/8254TimerDxe/8254Timer.inf + OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf { PciHostBridgeLib|OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index 4b1fa944ad..dd5ea30662 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -207,6 +207,7 @@ INF PcAtChipsetPkg/8259InterruptControllerDxe/8259.inf INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf INF UefiCpuPkg/CpuDxe/CpuDxe.inf INF PcAtChipsetPkg/8254TimerDxe/8254Timer.inf +INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf INF PcAtChipsetPkg/KbcResetDxe/Reset.inf