aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c')
-rw-r--r--roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c800
1 files changed, 800 insertions, 0 deletions
diff --git a/roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c b/roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
new file mode 100644
index 000000000..fd05ee5c2
--- /dev/null
+++ b/roms/edk2/OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
@@ -0,0 +1,800 @@
+/** @file
+ This driver implements EFI_PCI_HOT_PLUG_INIT_PROTOCOL, providing the PCI bus
+ driver with resource padding information, for PCIe hotplug purposes.
+
+ Copyright (C) 2016, Red Hat, Inc.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <IndustryStandard/Acpi10.h>
+#include <IndustryStandard/Q35MchIch9.h>
+#include <IndustryStandard/QemuPciBridgeCapabilities.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PciCapLib.h>
+#include <Library/PciCapPciSegmentLib.h>
+#include <Library/PciLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/PciHotPlugInit.h>
+#include <Protocol/PciRootBridgeIo.h>
+
+//
+// TRUE if the PCI platform supports extended config space, FALSE otherwise.
+//
+STATIC BOOLEAN mPciExtConfSpaceSupported;
+
+
+//
+// The protocol interface this driver produces.
+//
+// Refer to 12.6 "PCI Hot Plug PCI Initialization Protocol" in the Platform
+// Init 1.4a Spec, Volume 5.
+//
+STATIC EFI_PCI_HOT_PLUG_INIT_PROTOCOL mPciHotPlugInit;
+
+
+//
+// Resource padding template for the GetResourcePadding() protocol member
+// function.
+//
+// Refer to Table 8 "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 ApplyResourcePadding() function in the
+// edk2 PCI Bus UEFI_DRIVER.
+//
+// We can request padding for at most four resource types, each of which is
+// optional, independently of the others:
+// (a) bus numbers,
+// (b) IO space,
+// (c) non-prefetchable MMIO space (32-bit only),
+// (d) prefetchable MMIO space (either 32-bit or 64-bit, never both).
+//
+#pragma pack (1)
+typedef struct {
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR Padding[4];
+ EFI_ACPI_END_TAG_DESCRIPTOR EndDesc;
+} RESOURCE_PADDING;
+#pragma pack ()
+
+
+/**
+ Initialize a RESOURCE_PADDING object.
+
+ @param[out] ResourcePadding The caller-allocated RESOURCE_PADDING object to
+ initialize.
+**/
+STATIC
+VOID
+InitializeResourcePadding (
+ OUT RESOURCE_PADDING *ResourcePadding
+ )
+{
+ UINTN Index;
+
+ ZeroMem (ResourcePadding, sizeof *ResourcePadding);
+
+ //
+ // Fill in the Padding fields that don't vary across resource types.
+ //
+ for (Index = 0; Index < ARRAY_SIZE (ResourcePadding->Padding); ++Index) {
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
+
+ Descriptor = ResourcePadding->Padding + Index;
+ Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR;
+ Descriptor->Len = (UINT16)(
+ sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) -
+ OFFSET_OF (
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR,
+ ResType
+ )
+ );
+ }
+
+ //
+ // Fill in the End Tag.
+ //
+ ResourcePadding->EndDesc.Desc = ACPI_END_TAG_DESCRIPTOR;
+}
+
+
+/**
+ Set up a descriptor entry for reserving IO space.
+
+ @param[in,out] Descriptor The descriptor to configure. The caller shall have
+ initialized Descriptor earlier, with
+ InitializeResourcePadding().
+
+ @param[in] SizeExponent The size and natural alignment of the reservation
+ are determined by raising two to this power.
+**/
+STATIC
+VOID
+SetIoPadding (
+ IN OUT EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor,
+ IN UINTN SizeExponent
+ )
+{
+ Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO;
+ Descriptor->AddrLen = LShiftU64 (1, SizeExponent);
+ Descriptor->AddrRangeMax = Descriptor->AddrLen - 1;
+}
+
+
+/**
+ Set up a descriptor entry for reserving MMIO space.
+
+ @param[in,out] Descriptor The descriptor to configure. The caller shall
+ have initialized Descriptor earlier, with
+ InitializeResourcePadding().
+
+ @param[in] Prefetchable TRUE if the descriptor should reserve
+ prefetchable MMIO space. Pass FALSE for
+ reserving non-prefetchable MMIO space.
+
+ @param[in] ThirtyTwoBitOnly TRUE if the reservation should be limited to
+ 32-bit address space. FALSE if the reservation
+ can be satisfied from 64-bit address space.
+ ThirtyTwoBitOnly is ignored if Prefetchable is
+ FALSE; in that case ThirtyTwoBitOnly is always
+ considered TRUE.
+
+ @param[in] SizeExponent The size and natural alignment of the
+ reservation are determined by raising two to
+ this power.
+**/
+STATIC
+VOID
+SetMmioPadding (
+ IN OUT EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor,
+ IN BOOLEAN Prefetchable,
+ IN BOOLEAN ThirtyTwoBitOnly,
+ IN UINTN SizeExponent
+ )
+{
+ Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM;
+ if (Prefetchable) {
+ Descriptor->SpecificFlag =
+ EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE;
+ Descriptor->AddrSpaceGranularity = ThirtyTwoBitOnly ? 32 : 64;
+ } else {
+ Descriptor->SpecificFlag =
+ EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_NON_CACHEABLE;
+ Descriptor->AddrSpaceGranularity = 32;
+ }
+ Descriptor->AddrLen = LShiftU64 (1, SizeExponent);
+ Descriptor->AddrRangeMax = Descriptor->AddrLen - 1;
+}
+
+
+/**
+ Round up a positive 32-bit value to the next whole power of two, and return
+ the bit position of the highest bit set in the result. Equivalent to
+ ceil(log2(x)).
+
+ @param[in] Operand The 32-bit operand to evaluate.
+
+ @retval -1 Operand is zero.
+
+ @retval -1 Operand is positive, not a whole power of two, and rounding it
+ up to the next power of two does not fit into 32 bits.
+
+ @retval 0..31 Otherwise, return ceil(log2(Value)).
+**/
+STATIC
+INTN
+HighBitSetRoundUp32 (
+ IN UINT32 Operand
+ )
+{
+ INTN HighBit;
+
+ HighBit = HighBitSet32 (Operand);
+ if (HighBit == -1) {
+ //
+ // Operand is zero.
+ //
+ return HighBit;
+ }
+ if ((Operand & (Operand - 1)) != 0) {
+ //
+ // Operand is not a whole power of two.
+ //
+ ++HighBit;
+ }
+ return (HighBit < 32) ? HighBit : -1;
+}
+
+
+/**
+ Round up a positive 64-bit value to the next whole power of two, and return
+ the bit position of the highest bit set in the result. Equivalent to
+ ceil(log2(x)).
+
+ @param[in] Operand The 64-bit operand to evaluate.
+
+ @retval -1 Operand is zero.
+
+ @retval -1 Operand is positive, not a whole power of two, and rounding it
+ up to the next power of two does not fit into 64 bits.
+
+ @retval 0..63 Otherwise, return ceil(log2(Value)).
+**/
+STATIC
+INTN
+HighBitSetRoundUp64 (
+ IN UINT64 Operand
+ )
+{
+ INTN HighBit;
+
+ HighBit = HighBitSet64 (Operand);
+ if (HighBit == -1) {
+ //
+ // Operand is zero.
+ //
+ return HighBit;
+ }
+ if ((Operand & (Operand - 1)) != 0) {
+ //
+ // Operand is not a whole power of two.
+ //
+ ++HighBit;
+ }
+ return (HighBit < 64) ? HighBit : -1;
+}
+
+
+/**
+ Look up the QEMU-specific Resource Reservation capability in the conventional
+ config space of a Hotplug Controller (that is, PCI Bridge).
+
+ On error, the contents of ReservationHint are indeterminate.
+
+ @param[in] HpcPciAddress The address of the PCI Bridge -- Bus, Device,
+ Function -- in UEFI (not PciLib) encoding.
+
+ @param[out] ReservationHint The caller-allocated capability structure to
+ populate from the PCI Bridge's config space.
+
+ @retval EFI_SUCCESS The capability has been found, ReservationHint has
+ been populated.
+
+ @retval EFI_NOT_FOUND The capability is missing.
+
+ @return Error codes from PciCapPciSegmentLib and PciCapLib.
+**/
+STATIC
+EFI_STATUS
+QueryReservationHint (
+ IN CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *HpcPciAddress,
+ OUT QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION *ReservationHint
+)
+{
+ UINT16 PciVendorId;
+ EFI_STATUS Status;
+ PCI_CAP_DEV *PciDevice;
+ PCI_CAP_LIST *CapList;
+ UINT16 VendorInstance;
+ PCI_CAP *VendorCap;
+
+ //
+ // Check the vendor identifier.
+ //
+ PciVendorId = PciRead16 (
+ PCI_LIB_ADDRESS (
+ HpcPciAddress->Bus,
+ HpcPciAddress->Device,
+ HpcPciAddress->Function,
+ PCI_VENDOR_ID_OFFSET
+ )
+ );
+ if (PciVendorId != QEMU_PCI_BRIDGE_VENDOR_ID_REDHAT) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Parse the capabilities lists.
+ //
+ Status = PciCapPciSegmentDeviceInit (
+ mPciExtConfSpaceSupported ? PciCapExtended : PciCapNormal,
+ 0, // Segment
+ HpcPciAddress->Bus,
+ HpcPciAddress->Device,
+ HpcPciAddress->Function,
+ &PciDevice
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Status = PciCapListInit (PciDevice, &CapList);
+ if (EFI_ERROR (Status)) {
+ goto UninitPciDevice;
+ }
+
+ //
+ // Scan the vendor capability instances for the Resource Reservation
+ // capability.
+ //
+ VendorInstance = 0;
+ for (;;) {
+ UINT8 VendorLength;
+ UINT8 BridgeCapType;
+
+ Status = PciCapListFindCap (
+ CapList,
+ PciCapNormal,
+ EFI_PCI_CAPABILITY_ID_VENDOR,
+ VendorInstance++,
+ &VendorCap
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+
+ //
+ // Check the vendor capability length.
+ //
+ Status = PciCapRead (
+ PciDevice,
+ VendorCap,
+ OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length),
+ &VendorLength,
+ sizeof VendorLength
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+ if (VendorLength != sizeof *ReservationHint) {
+ continue;
+ }
+
+ //
+ // Check the vendor bridge capability type.
+ //
+ Status = PciCapRead (
+ PciDevice,
+ VendorCap,
+ OFFSET_OF (QEMU_PCI_BRIDGE_CAPABILITY_HDR, Type),
+ &BridgeCapType,
+ sizeof BridgeCapType
+ );
+ if (EFI_ERROR (Status)) {
+ goto UninitCapList;
+ }
+ if (BridgeCapType ==
+ QEMU_PCI_BRIDGE_CAPABILITY_TYPE_RESOURCE_RESERVATION) {
+ //
+ // We have a match.
+ //
+ break;
+ }
+ }
+
+ //
+ // Populate ReservationHint.
+ //
+ Status = PciCapRead (
+ PciDevice,
+ VendorCap,
+ 0, // SourceOffsetInCap
+ ReservationHint,
+ sizeof *ReservationHint
+ );
+
+UninitCapList:
+ PciCapListUninit (CapList);
+
+UninitPciDevice:
+ PciCapPciSegmentDeviceUninit (PciDevice);
+
+ return Status;
+}
+
+
+/**
+ Returns a list of root Hot Plug Controllers (HPCs) that require
+ initialization during the boot process.
+
+ This procedure returns a list of root HPCs. The PCI bus driver must
+ initialize these controllers during the boot process. The PCI bus driver may
+ or may not be able to detect these HPCs. If the platform includes a
+ PCI-to-CardBus bridge, it can be included in this list if it requires
+ initialization. The HpcList must be self consistent. An HPC cannot control
+ any of its parent buses. Only one HPC can control a PCI bus. Because this
+ list includes only root HPCs, no HPC in the list can be a child of another
+ HPC. This policy must be enforced by the EFI_PCI_HOT_PLUG_INIT_PROTOCOL.
+ The PCI bus driver may not check for such invalid conditions. The callee
+ allocates the buffer HpcList
+
+ @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
+ instance.
+ @param[out] HpcCount The number of root HPCs that were returned.
+ @param[out] HpcList The list of root HPCs. HpcCount defines the number of
+ elements in this list.
+
+ @retval EFI_SUCCESS HpcList was returned.
+ @retval EFI_OUT_OF_RESOURCES HpcList was not returned due to insufficient
+ resources.
+ @retval EFI_INVALID_PARAMETER HpcCount is NULL or HpcList is NULL.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetRootHpcList (
+ IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
+ OUT UINTN *HpcCount,
+ OUT EFI_HPC_LOCATION **HpcList
+ )
+{
+ if (HpcCount == NULL || HpcList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // There are no top-level (i.e., un-enumerable) hot-plug controllers in QEMU
+ // that would require special initialization.
+ //
+ *HpcCount = 0;
+ *HpcList = NULL;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Initializes one root Hot Plug Controller (HPC). This process may causes
+ initialization of its subordinate buses.
+
+ This function initializes the specified HPC. At the end of initialization,
+ the hot-plug slots or sockets (controlled by this HPC) are powered and are
+ connected to the bus. All the necessary registers in the HPC are set up. For
+ a Standard (PCI) Hot Plug Controller (SHPC), the registers that must be set
+ up are defined in the PCI Standard Hot Plug Controller and Subsystem
+ Specification.
+
+ @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
+ instance.
+ @param[in] HpcDevicePath The device path to the HPC that is being
+ initialized.
+ @param[in] HpcPciAddress The address of the HPC function on the PCI bus.
+ @param[in] Event The event that should be signaled when the HPC
+ initialization is complete. Set to NULL if the
+ caller wants to wait until the entire
+ initialization process is complete.
+ @param[out] HpcState The state of the HPC hardware. The state is
+ EFI_HPC_STATE_INITIALIZED or
+ EFI_HPC_STATE_ENABLED.
+
+ @retval EFI_SUCCESS If Event is NULL, the specific HPC was
+ successfully initialized. If Event is not
+ NULL, Event will be signaled at a later time
+ when initialization is complete.
+ @retval EFI_UNSUPPORTED This instance of
+ EFI_PCI_HOT_PLUG_INIT_PROTOCOL does not
+ support the specified HPC.
+ @retval EFI_OUT_OF_RESOURCES Initialization failed due to insufficient
+ resources.
+ @retval EFI_INVALID_PARAMETER HpcState is NULL.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+InitializeRootHpc (
+ IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath,
+ IN UINT64 HpcPciAddress,
+ IN EFI_EVENT Event, OPTIONAL
+ OUT EFI_HPC_STATE *HpcState
+ )
+{
+ //
+ // This function should never be called, due to the information returned by
+ // GetRootHpcList().
+ //
+ ASSERT (FALSE);
+
+ if (HpcState == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Returns the resource padding that is required by the PCI bus that is
+ controlled by the specified Hot Plug Controller (HPC).
+
+ This function returns the resource padding that is required by the PCI bus
+ that is controlled by the specified HPC. This member function is called for
+ all the root HPCs and nonroot HPCs that are detected by the PCI bus
+ enumerator. This function will be called before PCI resource allocation is
+ completed. This function must be called after all the root HPCs, with the
+ possible exception of a PCI-to-CardBus bridge, have completed
+ initialization.
+
+ @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
+ instance.
+ @param[in] HpcDevicePath The device path to the HPC.
+ @param[in] HpcPciAddress The address of the HPC function on the PCI bus.
+ @param[in] HpcState The state of the HPC hardware.
+ @param[out] Padding The amount of resource padding that is required
+ by the PCI bus under the control of the specified
+ HPC.
+ @param[out] Attributes Describes how padding is accounted for. The
+ padding is returned in the form of ACPI 2.0
+ resource descriptors.
+
+ @retval EFI_SUCCESS The resource padding was successfully
+ returned.
+ @retval EFI_UNSUPPORTED This instance of the
+ EFI_PCI_HOT_PLUG_INIT_PROTOCOL does not
+ support the specified HPC.
+ @retval EFI_NOT_READY This function was called before HPC
+ initialization is complete.
+ @retval EFI_INVALID_PARAMETER HpcState or Padding or Attributes is NULL.
+ @retval EFI_OUT_OF_RESOURCES ACPI 2.0 resource descriptors for Padding
+ cannot be allocated due to insufficient
+ resources.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetResourcePadding (
+ IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath,
+ IN UINT64 HpcPciAddress,
+ OUT EFI_HPC_STATE *HpcState,
+ OUT VOID **Padding,
+ OUT EFI_HPC_PADDING_ATTRIBUTES *Attributes
+ )
+{
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address;
+ BOOLEAN DefaultIo;
+ BOOLEAN DefaultMmio;
+ RESOURCE_PADDING ReservationRequest;
+ EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FirstResource;
+ EFI_STATUS ReservationHintStatus;
+ QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION ReservationHint;
+
+ Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;
+
+ DEBUG_CODE (
+ CHAR16 *DevicePathString;
+
+ DevicePathString = ConvertDevicePathToText (HpcDevicePath, FALSE, FALSE);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Address=%02x:%02x.%x DevicePath=%s\n",
+ __FUNCTION__, Address->Bus, Address->Device, Address->Function,
+ (DevicePathString == NULL) ? L"<unavailable>" : DevicePathString));
+
+ if (DevicePathString != NULL) {
+ FreePool (DevicePathString);
+ }
+ );
+
+ if (HpcState == NULL || Padding == NULL || Attributes == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DefaultIo = TRUE;
+ DefaultMmio = TRUE;
+
+ //
+ // Init ReservationRequest, and point FirstResource one past the last
+ // descriptor entry. We're going to build the entries backwards from
+ // ReservationRequest.EndDesc.
+ //
+ InitializeResourcePadding (&ReservationRequest);
+ FirstResource = ReservationRequest.Padding +
+ ARRAY_SIZE (ReservationRequest.Padding);
+
+ //
+ // Try to get the QEMU-specific Resource Reservation capability.
+ //
+ ReservationHintStatus = QueryReservationHint (Address, &ReservationHint);
+ if (!EFI_ERROR (ReservationHintStatus)) {
+ INTN HighBit;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: BusNumbers=0x%x Io=0x%Lx NonPrefetchable32BitMmio=0x%x\n"
+ "%a: Prefetchable32BitMmio=0x%x Prefetchable64BitMmio=0x%Lx\n",
+ __FUNCTION__,
+ ReservationHint.BusNumbers,
+ ReservationHint.Io,
+ ReservationHint.NonPrefetchable32BitMmio,
+ __FUNCTION__,
+ ReservationHint.Prefetchable32BitMmio,
+ ReservationHint.Prefetchable64BitMmio
+ ));
+
+ //
+ // (a) Reserve bus numbers.
+ //
+ switch (ReservationHint.BusNumbers) {
+ case 0:
+ //
+ // No reservation needed.
+ //
+ break;
+ case MAX_UINT32:
+ //
+ // Firmware default (unspecified). Treat it as "no reservation needed".
+ //
+ break;
+ default:
+ //
+ // Request the specified amount.
+ //
+ --FirstResource;
+ FirstResource->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS;
+ FirstResource->AddrLen = ReservationHint.BusNumbers;
+ break;
+ }
+
+ //
+ // (b) Reserve IO space.
+ //
+ switch (ReservationHint.Io) {
+ case 0:
+ //
+ // No reservation needed, disable our built-in.
+ //
+ DefaultIo = FALSE;
+ break;
+ case MAX_UINT64:
+ //
+ // Firmware default (unspecified). Stick with our built-in.
+ //
+ break;
+ default:
+ //
+ // Round the specified amount up to the next power of two. If rounding is
+ // successful, reserve the rounded value. Fall back to the default
+ // otherwise.
+ //
+ HighBit = HighBitSetRoundUp64 (ReservationHint.Io);
+ if (HighBit != -1) {
+ SetIoPadding (--FirstResource, (UINTN)HighBit);
+ DefaultIo = FALSE;
+ }
+ break;
+ }
+
+ //
+ // (c) Reserve non-prefetchable MMIO space (32-bit only).
+ //
+ switch (ReservationHint.NonPrefetchable32BitMmio) {
+ case 0:
+ //
+ // No reservation needed, disable our built-in.
+ //
+ DefaultMmio = FALSE;
+ break;
+ case MAX_UINT32:
+ //
+ // Firmware default (unspecified). Stick with our built-in.
+ //
+ break;
+ default:
+ //
+ // Round the specified amount up to the next power of two. If rounding is
+ // successful, reserve the rounded value. Fall back to the default
+ // otherwise.
+ //
+ HighBit = HighBitSetRoundUp32 (ReservationHint.NonPrefetchable32BitMmio);
+ if (HighBit != -1) {
+ SetMmioPadding (--FirstResource, FALSE, TRUE, (UINTN)HighBit);
+ DefaultMmio = FALSE;
+ }
+ break;
+ }
+
+ //
+ // (d) Reserve prefetchable MMIO space (either 32-bit or 64-bit, never
+ // both).
+ //
+ // For either space, we treat 0 as "no reservation needed", and the maximum
+ // value as "firmware default". The latter is unspecified, and we interpret
+ // it as the former.
+ //
+ // Otherwise, round the specified amount up to the next power of two. If
+ // rounding is successful, reserve the rounded value. Do not reserve
+ // prefetchable MMIO space otherwise.
+ //
+ if (ReservationHint.Prefetchable32BitMmio > 0 &&
+ ReservationHint.Prefetchable32BitMmio < MAX_UINT32) {
+ HighBit = HighBitSetRoundUp32 (ReservationHint.Prefetchable32BitMmio);
+ if (HighBit != -1) {
+ SetMmioPadding (--FirstResource, TRUE, TRUE, (UINTN)HighBit);
+ }
+ } else if (ReservationHint.Prefetchable64BitMmio > 0 &&
+ ReservationHint.Prefetchable64BitMmio < MAX_UINT64) {
+ HighBit = HighBitSetRoundUp64 (ReservationHint.Prefetchable64BitMmio);
+ if (HighBit != -1) {
+ SetMmioPadding (--FirstResource, TRUE, FALSE, (UINTN)HighBit);
+ }
+ }
+ }
+
+ if (DefaultIo) {
+ //
+ // Request defaults.
+ //
+ SetIoPadding (--FirstResource, (UINTN)HighBitSetRoundUp64 (512));
+ }
+
+ if (DefaultMmio) {
+ //
+ // Request defaults.
+ //
+ SetMmioPadding (
+ --FirstResource,
+ FALSE,
+ TRUE,
+ (UINTN)HighBitSetRoundUp32 (SIZE_2MB)
+ );
+ }
+
+ //
+ // Output a copy of ReservationRequest from the lowest-address populated
+ // entry until the end of the structure (including
+ // ReservationRequest.EndDesc). If no reservations are necessary, we'll only
+ // output the End Tag.
+ //
+ *Padding = AllocateCopyPool (
+ (UINT8 *)(&ReservationRequest + 1) - (UINT8 *)FirstResource,
+ FirstResource
+ );
+ if (*Padding == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Resource padding is required.
+ //
+ *HpcState = EFI_HPC_STATE_INITIALIZED | EFI_HPC_STATE_ENABLED;
+
+ //
+ // The padding should be applied at PCI bus level, and considered by upstream
+ // bridges, recursively.
+ //
+ *Attributes = EfiPaddingPciBus;
+ 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.
+ @return Error codes from lower level functions.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ mPciExtConfSpaceSupported = (PcdGet16 (PcdOvmfHostBridgePciDevId) ==
+ INTEL_Q35_MCH_DEVICE_ID);
+ mPciHotPlugInit.GetRootHpcList = GetRootHpcList;
+ mPciHotPlugInit.InitializeRootHpc = InitializeRootHpc;
+ mPciHotPlugInit.GetResourcePadding = GetResourcePadding;
+ Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
+ &gEfiPciHotPlugInitProtocolGuid, &mPciHotPlugInit, NULL);
+ return Status;
+}