/** @file Plug a PciSegmentLib backend into PciCapLib, for config space access. Copyright (C) 2018, Red Hat, Inc. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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); }