220 lines
7.5 KiB
C
220 lines
7.5 KiB
C
/** @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);
|
|
}
|