/** @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 <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); | |
} |