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
|
---|
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
|
---|
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
|
---|
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
|
---|
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 | }
|
---|