On 23 May 2018 at 22:21, Laszlo Ersek <[email protected]> wrote: > Add a library class, and a BASE lib instance, that are layered on top of > PciCapLib, and allow clients to plug a PciSegmentLib backend into > PciCapLib, for config space access. > > (Side note: >
For the record: I am fine with keeping this side note in the commit log. > The "MaxDomain" parameter is provided because, in practice, platforms > exist where a PCI Express device may show up on a root bridge such that > the root bridge doesn't support access to extended config space. Earlier > the same issue was handled for MdeModulePkg/PciHostBridgeDxe in commit > 014b472053ae3. However, that solution does not apply to the PciSegmentLib > class, because: > > (1) The config space accessor functions of the PciSegmentLib class, such > as PciSegmentReadBuffer(), have no way of informing the caller whether > access to extended config space actually succeeds. > > (For example, in the UefiPciSegmentLibPciRootBridgeIo instace, which > could in theory benefit from commit 014b472053ae3, the > EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Pci.Read() status code is explicitly > ignored, because there's no way for the lib instance to propagate it > to the PciSegmentLib caller. If the > EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Pci.Read() call fails, then > DxePciSegmentLibPciRootBridgeIoReadWorker() returns Data with > indeterminate value.) > > (2) There is no *general* way for any firmware platform to provide, or > use, a PciSegmentLib instance in which access to extended config space > always succeeds. > > In brief, on a platform where config space may be limited to 256 bytes, > access to extended config space through PciSegmentLib may invoke undefined > behavior; therefore PciCapPciSegmentLib must give platforms a way to > prevent such access.) > > Cc: Ard Biesheuvel <[email protected]> > Cc: Jordan Justen <[email protected]> > Contributed-under: TianoCore Contribution Agreement 1.1 > Signed-off-by: Laszlo Ersek <[email protected]> > --- > > Notes: > v2: > - move library from MdePkg to OvmfPkg, for initial introduction > > OvmfPkg/OvmfPkg.dec | 5 + > OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf | 35 +++ > OvmfPkg/Include/Library/PciCapPciSegmentLib.h | 82 > +++++++ > OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h | 47 > ++++ > OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c | 226 > ++++++++++++++++++++ > 5 files changed, 395 insertions(+) > > diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec > index 74818a2e2a19..fbec1cfe4a8e 100644 > --- a/OvmfPkg/OvmfPkg.dec > +++ b/OvmfPkg/OvmfPkg.dec > @@ -35,6 +35,11 @@ [LibraryClasses] > # 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 > diff --git > a/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf > b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf > new file mode 100644 > index 000000000000..e3cf5de49b15 > --- /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/Include/Library/PciCapPciSegmentLib.h > b/OvmfPkg/Include/Library/PciCapPciSegmentLib.h > new file mode 100644 > index 000000000000..6b6930288d16 > --- /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 <Library/PciCapLib.h> > + > + > +/** > + 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.h > b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.h > new file mode 100644 > index 000000000000..3ce15fe0fb57 > --- /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 <Library/DebugLib.h> > + > +#include <Library/PciCapPciSegmentLib.h> > + > +#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.c > b/OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c > new file mode 100644 > index 000000000000..57eb6b625b56 > --- /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 <IndustryStandard/Pci23.h> > + > +#include <Library/BaseLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/PciSegmentLib.h> > + > +#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); > +} > -- > 2.14.1.3.gb7cf6e02401b > > _______________________________________________ edk2-devel mailing list [email protected] https://lists.01.org/mailman/listinfo/edk2-devel

