2 Plug a PciSegmentLib backend into PciCapLib, for config space access.
4 Copyright (C) 2018, Red Hat, Inc.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <IndustryStandard/Pci23.h>
11 #include <Library/BaseLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/PciSegmentLib.h>
15 #include "BasePciCapPciSegmentLib.h"
19 Read the config space of a given PCI device (both normal and extended).
21 SegmentDevReadConfig() performs as few config space accesses as possible
22 (without attempting 64-bit wide accesses).
24 @param[in] PciDevice Implementation-specific unique representation
25 of the PCI device in the PCI hierarchy.
27 @param[in] SourceOffset Source offset in the config space of the PCI
28 device to start reading from.
30 @param[out] DestinationBuffer Buffer to store the read data to.
32 @param[in] Size The number of bytes to transfer.
34 @retval RETURN_SUCCESS Size bytes have been transferred from config
35 space to DestinationBuffer.
37 @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds
38 the config space limit of the PCI device.
39 Although PCI_CAP_DEV_READ_CONFIG allows reading
40 fewer than Size bytes in this case,
41 SegmentDevReadConfig() will read none.
46 SegmentDevReadConfig (
47 IN PCI_CAP_DEV
*PciDevice
,
48 IN UINT16 SourceOffset
,
49 OUT VOID
*DestinationBuffer
,
53 SEGMENT_DEV
*SegmentDev
;
54 UINT16 ConfigSpaceSize
;
57 SegmentDev
= SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice
);
58 ConfigSpaceSize
= (SegmentDev
->MaxDomain
== PciCapNormal
?
59 PCI_MAX_CONFIG_OFFSET
: PCI_EXP_MAX_CONFIG_OFFSET
);
61 // Note that all UINT16 variables below are promoted to INT32, and the
62 // addition and the comparison is carried out in INT32.
64 if (SourceOffset
+ Size
> ConfigSpaceSize
) {
65 return RETURN_UNSUPPORTED
;
67 SourceAddress
= PCI_SEGMENT_LIB_ADDRESS (SegmentDev
->SegmentNr
,
68 SegmentDev
->BusNr
, SegmentDev
->DeviceNr
,
69 SegmentDev
->FunctionNr
, SourceOffset
);
70 PciSegmentReadBuffer (SourceAddress
, Size
, DestinationBuffer
);
71 return RETURN_SUCCESS
;
76 Write the config space of a given PCI device (both normal and extended).
78 SegmentDevWriteConfig() performs as few config space accesses as possible
79 (without attempting 64-bit wide accesses).
81 @param[in] PciDevice Implementation-specific unique representation
82 of the PCI device in the PCI hierarchy.
84 @param[in] DestinationOffset Destination offset in the config space of the
85 PCI device to start writing at.
87 @param[in] SourceBuffer Buffer to read the data to be stored from.
89 @param[in] Size The number of bytes to transfer.
91 @retval RETURN_SUCCESS Size bytes have been transferred from
92 SourceBuffer to config space.
94 @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds
95 the config space limit of the PCI device.
96 Although PCI_CAP_DEV_WRITE_CONFIG allows writing
97 fewer than Size bytes in this case,
98 SegmentDevWriteConfig() will write none.
103 SegmentDevWriteConfig (
104 IN PCI_CAP_DEV
*PciDevice
,
105 IN UINT16 DestinationOffset
,
106 IN VOID
*SourceBuffer
,
110 SEGMENT_DEV
*SegmentDev
;
111 UINT16 ConfigSpaceSize
;
112 UINT64 DestinationAddress
;
114 SegmentDev
= SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice
);
115 ConfigSpaceSize
= (SegmentDev
->MaxDomain
== PciCapNormal
?
116 PCI_MAX_CONFIG_OFFSET
: PCI_EXP_MAX_CONFIG_OFFSET
);
118 // Note that all UINT16 variables below are promoted to INT32, and the
119 // addition and the comparison is carried out in INT32.
121 if (DestinationOffset
+ Size
> ConfigSpaceSize
) {
122 return RETURN_UNSUPPORTED
;
124 DestinationAddress
= PCI_SEGMENT_LIB_ADDRESS (SegmentDev
->SegmentNr
,
125 SegmentDev
->BusNr
, SegmentDev
->DeviceNr
,
126 SegmentDev
->FunctionNr
, DestinationOffset
);
127 PciSegmentWriteBuffer (DestinationAddress
, Size
, SourceBuffer
);
128 return RETURN_SUCCESS
;
133 Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
134 quadruplet. The config space accessors are based upon PciSegmentLib.
136 @param[in] MaxDomain If MaxDomain is PciCapExtended, then
137 PciDevice->ReadConfig() and PciDevice->WriteConfig()
138 will delegate extended config space accesses too to
139 PciSegmentReadBuffer() and PciSegmentWriteBuffer(),
140 respectively. Otherwise, PciDevice->ReadConfig() and
141 PciDevice->WriteConfig() will reject accesses to
142 extended config space with RETURN_UNSUPPORTED, without
143 calling PciSegmentReadBuffer() or
144 PciSegmentWriteBuffer(). By setting MaxDomain to
145 PciCapNormal, the platform can prevent undefined
146 PciSegmentLib behavior when the PCI root bridge under
147 the PCI device at Segment:Bus:Device.Function doesn't
148 support extended config space.
150 @param[in] Segment 16-bit wide segment number.
152 @param[in] Bus 8-bit wide bus number.
154 @param[in] Device 5-bit wide device number.
156 @param[in] Function 3-bit wide function number.
158 @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
159 PciDevice can be passed to the PciCapLib APIs.
161 @retval RETURN_SUCCESS PciDevice has been constructed and output.
163 @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the
164 permitted number of bits.
166 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
170 PciCapPciSegmentDeviceInit (
171 IN PCI_CAP_DOMAIN MaxDomain
,
176 OUT PCI_CAP_DEV
**PciDevice
179 SEGMENT_DEV
*SegmentDev
;
181 if (Device
> PCI_MAX_DEVICE
|| Function
> PCI_MAX_FUNC
) {
182 return RETURN_INVALID_PARAMETER
;
185 SegmentDev
= AllocatePool (sizeof *SegmentDev
);
186 if (SegmentDev
== NULL
) {
187 return RETURN_OUT_OF_RESOURCES
;
190 SegmentDev
->Signature
= SEGMENT_DEV_SIG
;
191 SegmentDev
->MaxDomain
= MaxDomain
;
192 SegmentDev
->SegmentNr
= Segment
;
193 SegmentDev
->BusNr
= Bus
;
194 SegmentDev
->DeviceNr
= Device
;
195 SegmentDev
->FunctionNr
= Function
;
196 SegmentDev
->BaseDevice
.ReadConfig
= SegmentDevReadConfig
;
197 SegmentDev
->BaseDevice
.WriteConfig
= SegmentDevWriteConfig
;
199 *PciDevice
= &SegmentDev
->BaseDevice
;
200 return RETURN_SUCCESS
;
205 Free the resources used by PciDevice.
207 @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
208 PciCapPciSegmentDeviceInit().
212 PciCapPciSegmentDeviceUninit (
213 IN PCI_CAP_DEV
*PciDevice
216 SEGMENT_DEV
*SegmentDev
;
218 SegmentDev
= SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice
);
219 FreePool (SegmentDev
);