diff --git a/OvmfPkg/Include/Library/PciCapPciSegmentLib.h b/OvmfPkg/Include/Library/PciCapPciSegmentLib.h new file mode 100644 index 0000000000..6b6930288d --- /dev/null +++ b/OvmfPkg/Include/Library/PciCapPciSegmentLib.h @@ -0,0 +1,82 @@ +/** @file + Library class layered on top of PciCapLib that allows clients to plug a + PciSegmentLib backend into PciCapLib, for config space access. + + Copyright (C) 2018, 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. +**/ + +#ifndef __PCI_CAP_PCI_SEGMENT_LIB_H__ +#define __PCI_CAP_PCI_SEGMENT_LIB_H__ + +#include + + +/** + Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function + quadruplet. The config space accessors are based upon PciSegmentLib. + + @param[in] MaxDomain If MaxDomain is PciCapExtended, then + PciDevice->ReadConfig() and PciDevice->WriteConfig() + will delegate extended config space accesses too to + PciSegmentReadBuffer() and PciSegmentWriteBuffer(), + respectively. Otherwise, PciDevice->ReadConfig() and + PciDevice->WriteConfig() will reject accesses to + extended config space with RETURN_UNSUPPORTED, without + calling PciSegmentReadBuffer() or + PciSegmentWriteBuffer(). By setting MaxDomain to + PciCapNormal, the platform can prevent undefined + PciSegmentLib behavior when the PCI root bridge under + the PCI device at Segment:Bus:Device.Function doesn't + support extended config space. + + @param[in] Segment 16-bit wide segment number. + + @param[in] Bus 8-bit wide bus number. + + @param[in] Device 5-bit wide device number. + + @param[in] Function 3-bit wide function number. + + @param[out] PciDevice The PCI_CAP_DEV object constructed as described above. + PciDevice can be passed to the PciCapLib APIs. + + @retval RETURN_SUCCESS PciDevice has been constructed and output. + + @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the + permitted number of bits. + + @retval RETURN_OUT_OF_RESOURCES Memory allocation failed. +**/ +RETURN_STATUS +EFIAPI +PciCapPciSegmentDeviceInit ( + IN PCI_CAP_DOMAIN MaxDomain, + IN UINT16 Segment, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Function, + OUT PCI_CAP_DEV **PciDevice + ); + + +/** + Free the resources used by PciDevice. + + @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by + PciCapPciSegmentDeviceInit(). +**/ +VOID +EFIAPI +PciCapPciSegmentDeviceUninit ( + IN PCI_CAP_DEV *PciDevice + ); + +#endif // __PCI_CAP_PCI_SEGMENT_LIB_H__ diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c new file mode 100644 index 0000000000..57eb6b625b --- /dev/null +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c @@ -0,0 +1,226 @@ +/** @file + Plug a PciSegmentLib backend into PciCapLib, for config space access. + + Copyright (C) 2018, 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 + +#include +#include +#include + +#include "BasePciCapPciSegmentLib.h" + + +/** + Read the config space of a given PCI device (both normal and extended). + + SegmentDevReadConfig() performs as few config space accesses as possible + (without attempting 64-bit wide accesses). + + @param[in] PciDevice Implementation-specific unique representation + of the PCI device in the PCI hierarchy. + + @param[in] SourceOffset Source offset in the config space of the PCI + device to start reading from. + + @param[out] DestinationBuffer Buffer to store the read data to. + + @param[in] Size The number of bytes to transfer. + + @retval RETURN_SUCCESS Size bytes have been transferred from config + space to DestinationBuffer. + + @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds + the config space limit of the PCI device. + Although PCI_CAP_DEV_READ_CONFIG allows reading + fewer than Size bytes in this case, + SegmentDevReadConfig() will read none. +**/ +STATIC +RETURN_STATUS +EFIAPI +SegmentDevReadConfig ( + IN PCI_CAP_DEV *PciDevice, + IN UINT16 SourceOffset, + OUT VOID *DestinationBuffer, + IN UINT16 Size + ) +{ + SEGMENT_DEV *SegmentDev; + UINT16 ConfigSpaceSize; + UINT64 SourceAddress; + + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice); + ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ? + PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET); + // + // Note that all UINT16 variables below are promoted to INT32, and the + // addition and the comparison is carried out in INT32. + // + if (SourceOffset + Size > ConfigSpaceSize) { + return RETURN_UNSUPPORTED; + } + SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr, + SegmentDev->BusNr, SegmentDev->DeviceNr, + SegmentDev->FunctionNr, SourceOffset); + PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer); + return RETURN_SUCCESS; +} + + +/** + Write the config space of a given PCI device (both normal and extended). + + SegmentDevWriteConfig() performs as few config space accesses as possible + (without attempting 64-bit wide accesses). + + @param[in] PciDevice Implementation-specific unique representation + of the PCI device in the PCI hierarchy. + + @param[in] DestinationOffset Destination offset in the config space of the + PCI device to start writing at. + + @param[in] SourceBuffer Buffer to read the data to be stored from. + + @param[in] Size The number of bytes to transfer. + + @retval RETURN_SUCCESS Size bytes have been transferred from + SourceBuffer to config space. + + @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds + the config space limit of the PCI device. + Although PCI_CAP_DEV_WRITE_CONFIG allows writing + fewer than Size bytes in this case, + SegmentDevWriteConfig() will write none. +**/ +STATIC +RETURN_STATUS +EFIAPI +SegmentDevWriteConfig ( + IN PCI_CAP_DEV *PciDevice, + IN UINT16 DestinationOffset, + IN VOID *SourceBuffer, + IN UINT16 Size + ) +{ + SEGMENT_DEV *SegmentDev; + UINT16 ConfigSpaceSize; + UINT64 DestinationAddress; + + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice); + ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ? + PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET); + // + // Note that all UINT16 variables below are promoted to INT32, and the + // addition and the comparison is carried out in INT32. + // + if (DestinationOffset + Size > ConfigSpaceSize) { + return RETURN_UNSUPPORTED; + } + DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr, + SegmentDev->BusNr, SegmentDev->DeviceNr, + SegmentDev->FunctionNr, DestinationOffset); + PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer); + return RETURN_SUCCESS; +} + + +/** + Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function + quadruplet. The config space accessors are based upon PciSegmentLib. + + @param[in] MaxDomain If MaxDomain is PciCapExtended, then + PciDevice->ReadConfig() and PciDevice->WriteConfig() + will delegate extended config space accesses too to + PciSegmentReadBuffer() and PciSegmentWriteBuffer(), + respectively. Otherwise, PciDevice->ReadConfig() and + PciDevice->WriteConfig() will reject accesses to + extended config space with RETURN_UNSUPPORTED, without + calling PciSegmentReadBuffer() or + PciSegmentWriteBuffer(). By setting MaxDomain to + PciCapNormal, the platform can prevent undefined + PciSegmentLib behavior when the PCI root bridge under + the PCI device at Segment:Bus:Device.Function doesn't + support extended config space. + + @param[in] Segment 16-bit wide segment number. + + @param[in] Bus 8-bit wide bus number. + + @param[in] Device 5-bit wide device number. + + @param[in] Function 3-bit wide function number. + + @param[out] PciDevice The PCI_CAP_DEV object constructed as described above. + PciDevice can be passed to the PciCapLib APIs. + + @retval RETURN_SUCCESS PciDevice has been constructed and output. + + @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the + permitted number of bits. + + @retval RETURN_OUT_OF_RESOURCES Memory allocation failed. +**/ +RETURN_STATUS +EFIAPI +PciCapPciSegmentDeviceInit ( + IN PCI_CAP_DOMAIN MaxDomain, + IN UINT16 Segment, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Function, + OUT PCI_CAP_DEV **PciDevice + ) +{ + SEGMENT_DEV *SegmentDev; + + if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) { + return RETURN_INVALID_PARAMETER; + } + + SegmentDev = AllocatePool (sizeof *SegmentDev); + if (SegmentDev == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + + SegmentDev->Signature = SEGMENT_DEV_SIG; + SegmentDev->MaxDomain = MaxDomain; + SegmentDev->SegmentNr = Segment; + SegmentDev->BusNr = Bus; + SegmentDev->DeviceNr = Device; + SegmentDev->FunctionNr = Function; + SegmentDev->BaseDevice.ReadConfig = SegmentDevReadConfig; + SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig; + + *PciDevice = &SegmentDev->BaseDevice; + return RETURN_SUCCESS; +} + + +/** + Free the resources used by PciDevice. + + @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by + PciCapPciSegmentDeviceInit(). +**/ +VOID +EFIAPI +PciCapPciSegmentDeviceUninit ( + IN PCI_CAP_DEV *PciDevice + ) +{ + SEGMENT_DEV *SegmentDev; + + SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice); + FreePool (SegmentDev); +} diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h new file mode 100644 index 0000000000..3ce15fe0fb --- /dev/null +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h @@ -0,0 +1,47 @@ +/** @file + Plug a PciSegmentLib backend into PciCapLib, for config space access -- + internal macro and type definitions. + + Copyright (C) 2018, 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. +**/ + +#ifndef __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__ +#define __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__ + +#include + +#include + +#define SEGMENT_DEV_SIG SIGNATURE_64 ('P', 'C', 'P', 'S', 'G', 'M', 'N', 'T') + +typedef struct { + // + // Signature identifying the derived class. + // + UINT64 Signature; + // + // Members added by the derived class, specific to the use of PciSegmentLib. + // + PCI_CAP_DOMAIN MaxDomain; + UINT16 SegmentNr; + UINT8 BusNr; + UINT8 DeviceNr; + UINT8 FunctionNr; + // + // Base class. + // + PCI_CAP_DEV BaseDevice; +} SEGMENT_DEV; + +#define SEGMENT_DEV_FROM_PCI_CAP_DEV(PciDevice) \ + CR (PciDevice, SEGMENT_DEV, BaseDevice, SEGMENT_DEV_SIG) + +#endif // __BASE_PCI_CAP_PCI_SEGMENT_LIB_H__ diff --git a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf new file mode 100644 index 0000000000..e3cf5de49b --- /dev/null +++ b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf @@ -0,0 +1,35 @@ +## @file +# Plug a PciSegmentLib backend into PciCapLib, for config space access. +# +# Copyright (C) 2018, 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. +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = BasePciCapPciSegmentLib + FILE_GUID = ED011855-AA31-43B9-ACC0-BF45A05C5985 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = PciCapPciSegmentLib + +[Sources] + BasePciCapPciSegmentLib.h + BasePciCapPciSegmentLib.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + MemoryAllocationLib + PciSegmentLib diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 74818a2e2a..fbec1cfe4a 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -35,6 +35,11 @@ # config space. PciCapLib|Include/Library/PciCapLib.h + ## @libraryclass Layered on top of PciCapLib, allows clients to plug a + # PciSegmentLib backend into PciCapLib, for config space + # access. + PciCapPciSegmentLib|Include/Library/PciCapPciSegmentLib.h + ## @libraryclass Access QEMU's firmware configuration interface # QemuFwCfgLib|Include/Library/QemuFwCfgLib.h