| /** @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, 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 <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #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 <Library/CcProbeLib.h> | |
| #include <Protocol/IncompatiblePciDeviceSupport.h> | |
| // | |
| // 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. | |
| // | |
| // This structure looks like: | |
| // AddressDesc-1 + AddressDesc-2 + ... + AddressDesc-n + EndDesc | |
| // | |
| STATIC CONST EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR mMmio64Configuration = { | |
| 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 | |
| }; | |
| // | |
| // mOptionRomConfiguration is present only in Td guest. | |
| // Host VMM can inject option ROM which is untrusted in Td guest, | |
| // so PCI option ROM needs to be ignored. | |
| // According to "Table 20. ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage" | |
| // PI spec 1.7, type-specific flags can be set to 0 when | |
| // Address Translation Offset == 6 to skip device option ROM. | |
| // | |
| STATIC CONST EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR mOptionRomConfiguration = { | |
| 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, // Disable option roms SpecificFlag | |
| 64, // AddrSpaceGranularity: | |
| // aperture selection hint | |
| // for BAR allocation | |
| MAX_UINT64, // AddrRangeMin | |
| MAX_UINT64, // AddrRangeMax: | |
| // no special alignment | |
| // for affected BARs | |
| 6, // AddrTranslationOffset: | |
| // hint covers all | |
| // eligible BARs | |
| 0 // AddrLen: | |
| // use probed BAR size | |
| }; | |
| STATIC CONST EFI_ACPI_END_TAG_DESCRIPTOR mEndDesc = { | |
| ACPI_END_TAG_DESCRIPTOR, // Desc | |
| 0 // Checksum: to be ignored | |
| }; | |
| /** | |
| 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 | |
| ) | |
| { | |
| UINTN Length; | |
| UINT8 *Ptr; | |
| // | |
| // 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, we don't support legacy option ROMs at all, thus this | |
| // problem cannot arise. | |
| // | |
| // 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. | |
| // | |
| Length = sizeof mMmio64Configuration + sizeof mEndDesc; | |
| // | |
| // In Td guest OptionRom is not allowed. | |
| // | |
| if (CcProbe ()) { | |
| Length += sizeof mOptionRomConfiguration; | |
| } | |
| *Configuration = AllocateZeroPool (Length); | |
| if (*Configuration == NULL) { | |
| DEBUG (( | |
| DEBUG_WARN, | |
| "%a: 64-bit MMIO BARs may be degraded for PCI 0x%04x:0x%04x (rev %d)\n", | |
| __func__, | |
| (UINT32)VendorId, | |
| (UINT32)DeviceId, | |
| (UINT8)RevisionId | |
| )); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Ptr = (UINT8 *)(UINTN)*Configuration; | |
| CopyMem (Ptr, &mMmio64Configuration, sizeof mMmio64Configuration); | |
| Length = sizeof mMmio64Configuration; | |
| if (CcProbe ()) { | |
| CopyMem (Ptr + Length, &mOptionRomConfiguration, sizeof mOptionRomConfiguration); | |
| Length += sizeof mOptionRomConfiguration; | |
| } | |
| CopyMem (Ptr + Length, &mEndDesc, sizeof mEndDesc); | |
| 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; | |
| // | |
| // If there is no 64-bit PCI MMIO aperture, then 64-bit MMIO BARs have to be | |
| // allocated under 4 GB unconditionally. | |
| // | |
| if (PcdGet64 (PcdPciMmio64Size) == 0) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| mIncompatiblePciDeviceSupport.CheckDevice = CheckDevice; | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ImageHandle, | |
| &gEfiIncompatiblePciDeviceSupportProtocolGuid, | |
| &mIncompatiblePciDeviceSupport, | |
| NULL | |
| ); | |
| return Status; | |
| } |