**/\r
\r
#include <IndustryStandard/Acpi10.h>\r
+#include <IndustryStandard/QemuPciBridgeCapabilities.h>\r
\r
#include <Library/BaseLib.h>\r
#include <Library/BaseMemoryLib.h>\r
#include <Library/DebugLib.h>\r
#include <Library/DevicePathLib.h>\r
#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciLib.h>\r
#include <Library/UefiBootServicesTableLib.h>\r
\r
#include <Protocol/PciHotPlugInit.h>\r
}\r
\r
\r
+/**\r
+ Read a slice from conventional PCI config space at the given offset, then\r
+ advance the offset.\r
+\r
+ @param[in] PciAddress The address of the PCI Device -- Bus, Device, Function\r
+ -- in UEFI (not PciLib) encoding.\r
+\r
+ @param[in,out] Offset On input, the offset in conventional PCI config space\r
+ to start reading from. On output, the offset of the\r
+ first byte that was not read.\r
+\r
+ @param[in] Size The number of bytes to read.\r
+\r
+ @param[out] Buffer On output, the bytes read from PCI config space are\r
+ stored in this object.\r
+**/\r
+STATIC\r
+VOID\r
+ReadConfigSpace (\r
+ IN CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *PciAddress,\r
+ IN OUT UINT8 *Offset,\r
+ IN UINT8 Size,\r
+ OUT VOID *Buffer\r
+ )\r
+{\r
+ PciReadBuffer (\r
+ PCI_LIB_ADDRESS (\r
+ PciAddress->Bus,\r
+ PciAddress->Device,\r
+ PciAddress->Function,\r
+ *Offset\r
+ ),\r
+ Size,\r
+ Buffer\r
+ );\r
+ *Offset += Size;\r
+}\r
+\r
+\r
+/**\r
+ Convenience wrapper macro for ReadConfigSpace().\r
+\r
+ Given the following conditions:\r
+\r
+ - HeaderField is the first field in the structure pointed-to by Struct,\r
+\r
+ - Struct->HeaderField has been populated from the conventional PCI config\r
+ space of the PCI device identified by PciAddress,\r
+\r
+ - *Offset points one past HeaderField in the conventional PCI config space of\r
+ the PCI device identified by PciAddress,\r
+\r
+ populate the rest of *Struct from conventional PCI config space, starting at\r
+ *Offset. Finally, increment *Offset so that it point one past *Struct.\r
+\r
+ @param[in] PciAddress The address of the PCI Device -- Bus, Device, Function\r
+ -- in UEFI (not PciLib) encoding. Type: pointer to\r
+ CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS.\r
+\r
+ @param[in,out] Offset On input, the offset in conventional PCI config space\r
+ to start reading from; one past Struct->HeaderField.\r
+ On output, the offset of the first byte that was not\r
+ read; one past *Struct. Type: pointer to UINT8.\r
+\r
+ @param[out] Struct The structure to complete. Type: pointer to structure\r
+ object.\r
+\r
+ @param[in] HeaderField The name of the first field in *Struct, after which\r
+ *Struct should be populated. Type: structure member\r
+ identifier.\r
+**/\r
+#define COMPLETE_CONFIG_SPACE_STRUCT(PciAddress, Offset, Struct, HeaderField) \\r
+ ReadConfigSpace ( \\r
+ (PciAddress), \\r
+ (Offset), \\r
+ (UINT8)(sizeof *(Struct) - sizeof ((Struct)->HeaderField)), \\r
+ &((Struct)->HeaderField) + 1 \\r
+ )\r
+\r
+\r
+/**\r
+ Look up the QEMU-specific Resource Reservation capability in the conventional\r
+ config space of a Hotplug Controller (that is, PCI Bridge).\r
+\r
+ This function performs as few config space reads as possible.\r
+\r
+ @param[in] HpcPciAddress The address of the PCI Bridge -- Bus, Device,\r
+ Function -- in UEFI (not PciLib) encoding.\r
+\r
+ @param[out] ReservationHint The caller-allocated capability structure to\r
+ populate from the PCI Bridge's config space.\r
+\r
+ @retval EFI_SUCCESS The capability has been found, ReservationHint has\r
+ been populated.\r
+\r
+ @retval EFI_NOT_FOUND The capability is missing. The contents of\r
+ ReservationHint are now indeterminate.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+QueryReservationHint (\r
+ IN CONST EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *HpcPciAddress,\r
+ OUT QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION *ReservationHint\r
+)\r
+{\r
+ UINT16 PciVendorId;\r
+ UINT16 PciStatus;\r
+ UINT8 PciCapPtr;\r
+ UINT8 Offset;\r
+\r
+ //\r
+ // Check the vendor identifier.\r
+ //\r
+ PciVendorId = PciRead16 (\r
+ PCI_LIB_ADDRESS (\r
+ HpcPciAddress->Bus,\r
+ HpcPciAddress->Device,\r
+ HpcPciAddress->Function,\r
+ PCI_VENDOR_ID_OFFSET\r
+ )\r
+ );\r
+ if (PciVendorId != QEMU_PCI_BRIDGE_VENDOR_ID_REDHAT) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Check the Capabilities List bit in the PCI Status Register.\r
+ //\r
+ PciStatus = PciRead16 (\r
+ PCI_LIB_ADDRESS (\r
+ HpcPciAddress->Bus,\r
+ HpcPciAddress->Device,\r
+ HpcPciAddress->Function,\r
+ PCI_PRIMARY_STATUS_OFFSET\r
+ )\r
+ );\r
+ if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Fetch the start of the Capabilities List.\r
+ //\r
+ PciCapPtr = PciRead8 (\r
+ PCI_LIB_ADDRESS (\r
+ HpcPciAddress->Bus,\r
+ HpcPciAddress->Device,\r
+ HpcPciAddress->Function,\r
+ PCI_CAPBILITY_POINTER_OFFSET\r
+ )\r
+ );\r
+\r
+ //\r
+ // Scan the Capabilities List until we find the terminator element, or the\r
+ // Resource Reservation capability.\r
+ //\r
+ for (Offset = PciCapPtr & 0xFC;\r
+ Offset > 0;\r
+ Offset = ReservationHint->BridgeHdr.VendorHdr.Hdr.NextItemPtr & 0xFC) {\r
+ BOOLEAN EnoughRoom;\r
+\r
+ //\r
+ // Check if the Resource Reservation capability would fit into config space\r
+ // at this offset.\r
+ //\r
+ EnoughRoom = (BOOLEAN)(\r
+ Offset <= PCI_MAX_CONFIG_OFFSET - sizeof *ReservationHint\r
+ );\r
+\r
+ //\r
+ // Read the standard capability header so we can check the capability ID\r
+ // (if necessary) and advance to the next capability.\r
+ //\r
+ ReadConfigSpace (\r
+ HpcPciAddress,\r
+ &Offset,\r
+ (UINT8)sizeof ReservationHint->BridgeHdr.VendorHdr.Hdr,\r
+ &ReservationHint->BridgeHdr.VendorHdr.Hdr\r
+ );\r
+ if (!EnoughRoom ||\r
+ (ReservationHint->BridgeHdr.VendorHdr.Hdr.CapabilityID !=\r
+ EFI_PCI_CAPABILITY_ID_VENDOR)) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Read the rest of the vendor capability header so we can check the\r
+ // capability length.\r
+ //\r
+ COMPLETE_CONFIG_SPACE_STRUCT (\r
+ HpcPciAddress,\r
+ &Offset,\r
+ &ReservationHint->BridgeHdr.VendorHdr,\r
+ Hdr\r
+ );\r
+ if (ReservationHint->BridgeHdr.VendorHdr.Length !=\r
+ sizeof *ReservationHint) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Read the rest of the QEMU bridge capability header so we can check the\r
+ // capability type.\r
+ //\r
+ COMPLETE_CONFIG_SPACE_STRUCT (\r
+ HpcPciAddress,\r
+ &Offset,\r
+ &ReservationHint->BridgeHdr,\r
+ VendorHdr\r
+ );\r
+ if (ReservationHint->BridgeHdr.Type !=\r
+ QEMU_PCI_BRIDGE_CAPABILITY_TYPE_RESOURCE_RESERVATION) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Read the body of the reservation hint.\r
+ //\r
+ COMPLETE_CONFIG_SPACE_STRUCT (\r
+ HpcPciAddress,\r
+ &Offset,\r
+ ReservationHint,\r
+ BridgeHdr\r
+ );\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
/**\r
Returns a list of root Hot Plug Controllers (HPCs) that require\r
initialization during the boot process.\r
OUT EFI_HPC_PADDING_ATTRIBUTES *Attributes\r
)\r
{\r
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address;\r
BOOLEAN DefaultIo;\r
BOOLEAN DefaultMmio;\r
RESOURCE_PADDING ReservationRequest;\r
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FirstResource;\r
+ EFI_STATUS ReservationHintStatus;\r
+ QEMU_PCI_BRIDGE_CAPABILITY_RESOURCE_RESERVATION ReservationHint;\r
+\r
+ Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;\r
\r
DEBUG_CODE (\r
- EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address;\r
CHAR16 *DevicePathString;\r
\r
- Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;\r
DevicePathString = ConvertDevicePathToText (HpcDevicePath, FALSE, FALSE);\r
\r
DEBUG ((EFI_D_VERBOSE, "%a: Address=%02x:%02x.%x DevicePath=%s\n",\r
ARRAY_SIZE (ReservationRequest.Padding);\r
\r
//\r
- // (b) Reserve IO space.\r
+ // Try to get the QEMU-specific Resource Reservation capability.\r
//\r
+ ReservationHintStatus = QueryReservationHint (Address, &ReservationHint);\r
+ if (!EFI_ERROR (ReservationHintStatus)) {\r
+ INTN HighBit;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: BusNumbers=0x%x Io=0x%Lx NonPrefetchable32BitMmio=0x%x\n"\r
+ "%a: Prefetchable32BitMmio=0x%x Prefetchable64BitMmio=0x%Lx\n",\r
+ __FUNCTION__,\r
+ ReservationHint.BusNumbers,\r
+ ReservationHint.Io,\r
+ ReservationHint.NonPrefetchable32BitMmio,\r
+ __FUNCTION__,\r
+ ReservationHint.Prefetchable32BitMmio,\r
+ ReservationHint.Prefetchable64BitMmio\r
+ ));\r
+\r
+ //\r
+ // (a) Reserve bus numbers.\r
+ //\r
+ switch (ReservationHint.BusNumbers) {\r
+ case 0:\r
+ //\r
+ // No reservation needed.\r
+ //\r
+ break;\r
+ case MAX_UINT32:\r
+ //\r
+ // Firmware default (unspecified). Treat it as "no reservation needed".\r
+ //\r
+ break;\r
+ default:\r
+ //\r
+ // Request the specified amount.\r
+ //\r
+ --FirstResource;\r
+ FirstResource->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS;\r
+ FirstResource->AddrLen = ReservationHint.BusNumbers;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // (b) Reserve IO space.\r
+ //\r
+ switch (ReservationHint.Io) {\r
+ case 0:\r
+ //\r
+ // No reservation needed, disable our built-in.\r
+ //\r
+ DefaultIo = FALSE;\r
+ break;\r
+ case MAX_UINT64:\r
+ //\r
+ // Firmware default (unspecified). Stick with our built-in.\r
+ //\r
+ break;\r
+ default:\r
+ //\r
+ // Round the specified amount up to the next power of two. If rounding is\r
+ // successful, reserve the rounded value. Fall back to the default\r
+ // otherwise.\r
+ //\r
+ HighBit = HighBitSetRoundUp64 (ReservationHint.Io);\r
+ if (HighBit != -1) {\r
+ SetIoPadding (--FirstResource, (UINTN)HighBit);\r
+ DefaultIo = FALSE;\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // (c) Reserve non-prefetchable MMIO space (32-bit only).\r
+ //\r
+ switch (ReservationHint.NonPrefetchable32BitMmio) {\r
+ case 0:\r
+ //\r
+ // No reservation needed, disable our built-in.\r
+ //\r
+ DefaultMmio = FALSE;\r
+ break;\r
+ case MAX_UINT32:\r
+ //\r
+ // Firmware default (unspecified). Stick with our built-in.\r
+ //\r
+ break;\r
+ default:\r
+ //\r
+ // Round the specified amount up to the next power of two. If rounding is\r
+ // successful, reserve the rounded value. Fall back to the default\r
+ // otherwise.\r
+ //\r
+ HighBit = HighBitSetRoundUp32 (ReservationHint.NonPrefetchable32BitMmio);\r
+ if (HighBit != -1) {\r
+ SetMmioPadding (--FirstResource, FALSE, TRUE, (UINTN)HighBit);\r
+ DefaultMmio = FALSE;\r
+ }\r
+ break;\r
+ }\r
+\r
+ //\r
+ // (d) Reserve prefetchable MMIO space (either 32-bit or 64-bit, never\r
+ // both).\r
+ //\r
+ // For either space, we treat 0 as "no reservation needed", and the maximum\r
+ // value as "firmware default". The latter is unspecified, and we interpret\r
+ // it as the former.\r
+ //\r
+ // Otherwise, round the specified amount up to the next power of two. If\r
+ // rounding is successful, reserve the rounded value. Do not reserve\r
+ // prefetchable MMIO space otherwise.\r
+ //\r
+ if (ReservationHint.Prefetchable32BitMmio > 0 &&\r
+ ReservationHint.Prefetchable32BitMmio < MAX_UINT32) {\r
+ HighBit = HighBitSetRoundUp32 (ReservationHint.Prefetchable32BitMmio);\r
+ if (HighBit != -1) {\r
+ SetMmioPadding (--FirstResource, TRUE, TRUE, (UINTN)HighBit);\r
+ }\r
+ } else if (ReservationHint.Prefetchable64BitMmio > 0 &&\r
+ ReservationHint.Prefetchable64BitMmio < MAX_UINT64) {\r
+ HighBit = HighBitSetRoundUp64 (ReservationHint.Prefetchable64BitMmio);\r
+ if (HighBit != -1) {\r
+ SetMmioPadding (--FirstResource, TRUE, FALSE, (UINTN)HighBit);\r
+ }\r
+ }\r
+ }\r
+\r
if (DefaultIo) {\r
//\r
// Request defaults.\r
SetIoPadding (--FirstResource, (UINTN)HighBitSetRoundUp64 (512));\r
}\r
\r
- //\r
- // (c) Reserve non-prefetchable MMIO space (32-bit only).\r
- //\r
if (DefaultMmio) {\r
//\r
// Request defaults.\r