1 /** @file
2 Plug a PciSegmentLib backend into PciCapLib, for config space access.
3
4 Copyright (C) 2018, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <IndustryStandard/Pci23.h>
10
11 #include <Library/BaseLib.h>
12 #include <Library/MemoryAllocationLib.h>
13 #include <Library/PciSegmentLib.h>
14
15 #include "BasePciCapPciSegmentLib.h"
16
17
18 /**
19 Read the config space of a given PCI device (both normal and extended).
20
21 SegmentDevReadConfig() performs as few config space accesses as possible
22 (without attempting 64-bit wide accesses).
23
24 @param[in] PciDevice Implementation-specific unique representation
25 of the PCI device in the PCI hierarchy.
26
27 @param[in] SourceOffset Source offset in the config space of the PCI
28 device to start reading from.
29
30 @param[out] DestinationBuffer Buffer to store the read data to.
31
32 @param[in] Size The number of bytes to transfer.
33
34 @retval RETURN_SUCCESS Size bytes have been transferred from config
35 space to DestinationBuffer.
36
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.
42 **/
43 STATIC
44 RETURN_STATUS
45 EFIAPI
SegmentDevReadConfig(IN PCI_CAP_DEV * PciDevice,IN UINT16 SourceOffset,OUT VOID * DestinationBuffer,IN UINT16 Size)46 SegmentDevReadConfig (
47 IN PCI_CAP_DEV *PciDevice,
48 IN UINT16 SourceOffset,
49 OUT VOID *DestinationBuffer,
50 IN UINT16 Size
51 )
52 {
53 SEGMENT_DEV *SegmentDev;
54 UINT16 ConfigSpaceSize;
55 UINT64 SourceAddress;
56
57 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
58 ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
59 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
60 //
61 // Note that all UINT16 variables below are promoted to INT32, and the
62 // addition and the comparison is carried out in INT32.
63 //
64 if (SourceOffset + Size > ConfigSpaceSize) {
65 return RETURN_UNSUPPORTED;
66 }
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;
72 }
73
74
75 /**
76 Write the config space of a given PCI device (both normal and extended).
77
78 SegmentDevWriteConfig() performs as few config space accesses as possible
79 (without attempting 64-bit wide accesses).
80
81 @param[in] PciDevice Implementation-specific unique representation
82 of the PCI device in the PCI hierarchy.
83
84 @param[in] DestinationOffset Destination offset in the config space of the
85 PCI device to start writing at.
86
87 @param[in] SourceBuffer Buffer to read the data to be stored from.
88
89 @param[in] Size The number of bytes to transfer.
90
91 @retval RETURN_SUCCESS Size bytes have been transferred from
92 SourceBuffer to config space.
93
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.
99 **/
100 STATIC
101 RETURN_STATUS
102 EFIAPI
SegmentDevWriteConfig(IN PCI_CAP_DEV * PciDevice,IN UINT16 DestinationOffset,IN VOID * SourceBuffer,IN UINT16 Size)103 SegmentDevWriteConfig (
104 IN PCI_CAP_DEV *PciDevice,
105 IN UINT16 DestinationOffset,
106 IN VOID *SourceBuffer,
107 IN UINT16 Size
108 )
109 {
110 SEGMENT_DEV *SegmentDev;
111 UINT16 ConfigSpaceSize;
112 UINT64 DestinationAddress;
113
114 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
115 ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?
116 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
117 //
118 // Note that all UINT16 variables below are promoted to INT32, and the
119 // addition and the comparison is carried out in INT32.
120 //
121 if (DestinationOffset + Size > ConfigSpaceSize) {
122 return RETURN_UNSUPPORTED;
123 }
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;
129 }
130
131
132 /**
133 Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function
134 quadruplet. The config space accessors are based upon PciSegmentLib.
135
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.
149
150 @param[in] Segment 16-bit wide segment number.
151
152 @param[in] Bus 8-bit wide bus number.
153
154 @param[in] Device 5-bit wide device number.
155
156 @param[in] Function 3-bit wide function number.
157
158 @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.
159 PciDevice can be passed to the PciCapLib APIs.
160
161 @retval RETURN_SUCCESS PciDevice has been constructed and output.
162
163 @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the
164 permitted number of bits.
165
166 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.
167 **/
168 RETURN_STATUS
169 EFIAPI
PciCapPciSegmentDeviceInit(IN PCI_CAP_DOMAIN MaxDomain,IN UINT16 Segment,IN UINT8 Bus,IN UINT8 Device,IN UINT8 Function,OUT PCI_CAP_DEV ** PciDevice)170 PciCapPciSegmentDeviceInit (
171 IN PCI_CAP_DOMAIN MaxDomain,
172 IN UINT16 Segment,
173 IN UINT8 Bus,
174 IN UINT8 Device,
175 IN UINT8 Function,
176 OUT PCI_CAP_DEV **PciDevice
177 )
178 {
179 SEGMENT_DEV *SegmentDev;
180
181 if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {
182 return RETURN_INVALID_PARAMETER;
183 }
184
185 SegmentDev = AllocatePool (sizeof *SegmentDev);
186 if (SegmentDev == NULL) {
187 return RETURN_OUT_OF_RESOURCES;
188 }
189
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;
198
199 *PciDevice = &SegmentDev->BaseDevice;
200 return RETURN_SUCCESS;
201 }
202
203
204 /**
205 Free the resources used by PciDevice.
206
207 @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by
208 PciCapPciSegmentDeviceInit().
209 **/
210 VOID
211 EFIAPI
PciCapPciSegmentDeviceUninit(IN PCI_CAP_DEV * PciDevice)212 PciCapPciSegmentDeviceUninit (
213 IN PCI_CAP_DEV *PciDevice
214 )
215 {
216 SEGMENT_DEV *SegmentDev;
217
218 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);
219 FreePool (SegmentDev);
220 }
221