]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c
OvmfPkg: introduce PciCapPciSegmentLib
[mirror_edk2.git] / OvmfPkg / Library / BasePciCapPciSegmentLib / BasePciCapPciSegmentLib.c
diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c
new file mode 100644 (file)
index 0000000..57eb6b6
--- /dev/null
@@ -0,0 +1,226 @@
+/** @file\r
+  Plug a PciSegmentLib backend into PciCapLib, for config space access.\r
+\r
+  Copyright (C) 2018, Red Hat, Inc.\r
+\r
+  This program and the accompanying materials are licensed and made available\r
+  under the terms and conditions of the BSD License which accompanies this\r
+  distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
+  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+**/\r
+\r
+#include <IndustryStandard/Pci23.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PciSegmentLib.h>\r
+\r
+#include "BasePciCapPciSegmentLib.h"\r
+\r
+\r
+/**\r
+  Read the config space of a given PCI device (both normal and extended).\r
+\r
+  SegmentDevReadConfig() performs as few config space accesses as possible\r
+  (without attempting 64-bit wide accesses).\r
+\r
+  @param[in] PciDevice           Implementation-specific unique representation\r
+                                 of the PCI device in the PCI hierarchy.\r
+\r
+  @param[in] SourceOffset        Source offset in the config space of the PCI\r
+                                 device to start reading from.\r
+\r
+  @param[out] DestinationBuffer  Buffer to store the read data to.\r
+\r
+  @param[in] Size                The number of bytes to transfer.\r
+\r
+  @retval RETURN_SUCCESS      Size bytes have been transferred from config\r
+                              space to DestinationBuffer.\r
+\r
+  @retval RETURN_UNSUPPORTED  Accessing Size bytes from SourceOffset exceeds\r
+                              the config space limit of the PCI device.\r
+                              Although PCI_CAP_DEV_READ_CONFIG allows reading\r
+                              fewer than Size bytes in this case,\r
+                              SegmentDevReadConfig() will read none.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+SegmentDevReadConfig (\r
+  IN  PCI_CAP_DEV *PciDevice,\r
+  IN  UINT16      SourceOffset,\r
+  OUT VOID        *DestinationBuffer,\r
+  IN  UINT16      Size\r
+  )\r
+{\r
+  SEGMENT_DEV *SegmentDev;\r
+  UINT16      ConfigSpaceSize;\r
+  UINT64      SourceAddress;\r
+\r
+  SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+  ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
+                     PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
+  //\r
+  // Note that all UINT16 variables below are promoted to INT32, and the\r
+  // addition and the comparison is carried out in INT32.\r
+  //\r
+  if (SourceOffset + Size > ConfigSpaceSize) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
+                    SegmentDev->BusNr, SegmentDev->DeviceNr,\r
+                    SegmentDev->FunctionNr, SourceOffset);\r
+  PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);\r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Write the config space of a given PCI device (both normal and extended).\r
+\r
+  SegmentDevWriteConfig() performs as few config space accesses as possible\r
+  (without attempting 64-bit wide accesses).\r
+\r
+  @param[in] PciDevice          Implementation-specific unique representation\r
+                                of the PCI device in the PCI hierarchy.\r
+\r
+  @param[in] DestinationOffset  Destination offset in the config space of the\r
+                                PCI device to start writing at.\r
+\r
+  @param[in] SourceBuffer       Buffer to read the data to be stored from.\r
+\r
+  @param[in] Size               The number of bytes to transfer.\r
+\r
+  @retval RETURN_SUCCESS      Size bytes have been transferred from\r
+                              SourceBuffer to config space.\r
+\r
+  @retval RETURN_UNSUPPORTED  Accessing Size bytes at DestinationOffset exceeds\r
+                              the config space limit of the PCI device.\r
+                              Although PCI_CAP_DEV_WRITE_CONFIG allows writing\r
+                              fewer than Size bytes in this case,\r
+                              SegmentDevWriteConfig() will write none.\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+EFIAPI\r
+SegmentDevWriteConfig (\r
+  IN PCI_CAP_DEV *PciDevice,\r
+  IN UINT16      DestinationOffset,\r
+  IN VOID        *SourceBuffer,\r
+  IN UINT16      Size\r
+  )\r
+{\r
+  SEGMENT_DEV *SegmentDev;\r
+  UINT16      ConfigSpaceSize;\r
+  UINT64      DestinationAddress;\r
+\r
+  SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+  ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
+                     PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
+  //\r
+  // Note that all UINT16 variables below are promoted to INT32, and the\r
+  // addition and the comparison is carried out in INT32.\r
+  //\r
+  if (DestinationOffset + Size > ConfigSpaceSize) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
+                         SegmentDev->BusNr, SegmentDev->DeviceNr,\r
+                         SegmentDev->FunctionNr, DestinationOffset);\r
+  PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);\r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function\r
+  quadruplet. The config space accessors are based upon PciSegmentLib.\r
+\r
+  @param[in] MaxDomain   If MaxDomain is PciCapExtended, then\r
+                         PciDevice->ReadConfig() and PciDevice->WriteConfig()\r
+                         will delegate extended config space accesses too to\r
+                         PciSegmentReadBuffer() and PciSegmentWriteBuffer(),\r
+                         respectively. Otherwise, PciDevice->ReadConfig() and\r
+                         PciDevice->WriteConfig() will reject accesses to\r
+                         extended config space with RETURN_UNSUPPORTED, without\r
+                         calling PciSegmentReadBuffer() or\r
+                         PciSegmentWriteBuffer(). By setting MaxDomain to\r
+                         PciCapNormal, the platform can prevent undefined\r
+                         PciSegmentLib behavior when the PCI root bridge under\r
+                         the PCI device at Segment:Bus:Device.Function doesn't\r
+                         support extended config space.\r
+\r
+  @param[in] Segment     16-bit wide segment number.\r
+\r
+  @param[in] Bus         8-bit wide bus number.\r
+\r
+  @param[in] Device      5-bit wide device number.\r
+\r
+  @param[in] Function    3-bit wide function number.\r
+\r
+  @param[out] PciDevice  The PCI_CAP_DEV object constructed as described above.\r
+                         PciDevice can be passed to the PciCapLib APIs.\r
+\r
+  @retval RETURN_SUCCESS            PciDevice has been constructed and output.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Device or Function does not fit in the\r
+                                    permitted number of bits.\r
+\r
+  @retval RETURN_OUT_OF_RESOURCES   Memory allocation failed.\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+PciCapPciSegmentDeviceInit (\r
+  IN  PCI_CAP_DOMAIN MaxDomain,\r
+  IN  UINT16         Segment,\r
+  IN  UINT8          Bus,\r
+  IN  UINT8          Device,\r
+  IN  UINT8          Function,\r
+  OUT PCI_CAP_DEV    **PciDevice\r
+  )\r
+{\r
+  SEGMENT_DEV *SegmentDev;\r
+\r
+  if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
+\r
+  SegmentDev = AllocatePool (sizeof *SegmentDev);\r
+  if (SegmentDev == NULL) {\r
+    return RETURN_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  SegmentDev->Signature              = SEGMENT_DEV_SIG;\r
+  SegmentDev->MaxDomain              = MaxDomain;\r
+  SegmentDev->SegmentNr              = Segment;\r
+  SegmentDev->BusNr                  = Bus;\r
+  SegmentDev->DeviceNr               = Device;\r
+  SegmentDev->FunctionNr             = Function;\r
+  SegmentDev->BaseDevice.ReadConfig  = SegmentDevReadConfig;\r
+  SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;\r
+\r
+  *PciDevice = &SegmentDev->BaseDevice;\r
+  return RETURN_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Free the resources used by PciDevice.\r
+\r
+  @param[in] PciDevice  The PCI_CAP_DEV object to free, originally produced by\r
+                        PciCapPciSegmentDeviceInit().\r
+**/\r
+VOID\r
+EFIAPI\r
+PciCapPciSegmentDeviceUninit (\r
+  IN PCI_CAP_DEV *PciDevice\r
+  )\r
+{\r
+  SEGMENT_DEV *SegmentDev;\r
+\r
+  SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
+  FreePool (SegmentDev);\r
+}\r