diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe')
2 files changed, 391 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c new file mode 100644 index 00000000..9b5cd9f3 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c @@ -0,0 +1,348 @@ +/** @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. + Copyright (c) 2017, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include <IndustryStandard/Acpi10.h> +#include <IndustryStandard/Pci22.h> + +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/PcdLib.h> +#include <Library/UefiBootServicesTableLib.h> + +#include <Protocol/IncompatiblePciDeviceSupport.h> +#include <Protocol/LegacyBios.h> + +// +// 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 + 0, // GenFlag + 0, // SpecificFlag + 64, // AddrSpaceGranularity: + // aperture selection hint + // for BAR allocation + 0, // AddrRangeMin + 0, // AddrRangeMax: + // no special alignment + // for affected BARs + MAX_UINT64, // AddrTranslationOffset: + // hint covers all + // eligible BARs + 0 // 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 ((DEBUG_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/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf b/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf new file mode 100644 index 00000000..805df1c7 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf @@ -0,0 +1,43 @@ +## @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. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[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] + 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 |