Replace the manual capability list parsing in OvmfPkg/PciHotPlugInitDxe with PciCapLib and PciCapPciSegmentLib API calls. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Jordan Justen <jordan.l.justen@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
		
			
				
	
	
		
			807 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			807 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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.
 | |
| 
 | |
|   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 <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 ((EFI_D_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;
 | |
| }
 |