1 /** @file
2 This driver installs Single Segment Pci Configuration 2 PPI
3 to provide read, write and modify access to Pci configuration space in PEI phase.
4 To follow PI specification, these services also support access to the unaligned Pci address.
5
6 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <PiPei.h>
12 #include <Ppi/PciCfg2.h>
13 #include <Library/BaseLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/PciLib.h>
16 #include <Library/PeimEntryPoint.h>
17 #include <Library/PeiServicesLib.h>
18 #include <IndustryStandard/Pci.h>
19
20 /**
21 Convert EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS to PCI_LIB_ADDRESS.
22
23 @param Address PCI address with EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS format.
24
25 @return PCI address with PCI_LIB_ADDRESS format.
26
27 **/
28 UINTN
PciCfgAddressConvert(EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS * Address)29 PciCfgAddressConvert (
30 EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS *Address
31 )
32 {
33 if (Address->ExtendedRegister == 0) {
34 return PCI_LIB_ADDRESS (Address->Bus, Address->Device, Address->Function, Address->Register);
35 }
36
37 return PCI_LIB_ADDRESS (Address->Bus, Address->Device, Address->Function, Address->ExtendedRegister);
38 }
39
40 /**
41 Reads from a given location in the PCI configuration space.
42
43 @param PeiServices An indirect pointer to the PEI Services Table published by the PEI Foundation.
44 @param This Pointer to local data for the interface.
45 @param Width The width of the access. Enumerated in bytes.
46 See EFI_PEI_PCI_CFG_PPI_WIDTH above.
47 @param Address The physical address of the access. The format of
48 the address is described by EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS.
49 @param Buffer A pointer to the buffer of data.
50
51 @retval EFI_SUCCESS The function completed successfully.
52 @retval EFI_INVALID_PARAMETER The invalid access width.
53
54 **/
55 EFI_STATUS
56 EFIAPI
PciCfg2Read(IN CONST EFI_PEI_SERVICES ** PeiServices,IN CONST EFI_PEI_PCI_CFG2_PPI * This,IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,IN UINT64 Address,IN OUT VOID * Buffer)57 PciCfg2Read (
58 IN CONST EFI_PEI_SERVICES **PeiServices,
59 IN CONST EFI_PEI_PCI_CFG2_PPI *This,
60 IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,
61 IN UINT64 Address,
62 IN OUT VOID *Buffer
63 )
64 {
65 UINTN PciLibAddress;
66
67 PciLibAddress = PciCfgAddressConvert ((EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS *) &Address);
68
69 if (Width == EfiPeiPciCfgWidthUint8) {
70 *((UINT8 *) Buffer) = PciRead8 (PciLibAddress);
71 } else if (Width == EfiPeiPciCfgWidthUint16) {
72 if ((PciLibAddress & 0x01) == 0) {
73 //
74 // Aligned Pci address access
75 //
76 WriteUnaligned16 (((UINT16 *) Buffer), PciRead16 (PciLibAddress));
77 } else {
78 //
79 // Unaligned Pci address access, break up the request into byte by byte.
80 //
81 *((UINT8 *) Buffer) = PciRead8 (PciLibAddress);
82 *((UINT8 *) Buffer + 1) = PciRead8 (PciLibAddress + 1);
83 }
84 } else if (Width == EfiPeiPciCfgWidthUint32) {
85 if ((PciLibAddress & 0x03) == 0) {
86 //
87 // Aligned Pci address access
88 //
89 WriteUnaligned32 (((UINT32 *) Buffer), PciRead32 (PciLibAddress));
90 } else if ((PciLibAddress & 0x01) == 0) {
91 //
92 // Unaligned Pci address access, break up the request into word by word.
93 //
94 WriteUnaligned16 (((UINT16 *) Buffer), PciRead16 (PciLibAddress));
95 WriteUnaligned16 (((UINT16 *) Buffer + 1), PciRead16 (PciLibAddress + 2));
96 } else {
97 //
98 // Unaligned Pci address access, break up the request into byte by byte.
99 //
100 *((UINT8 *) Buffer) = PciRead8 (PciLibAddress);
101 *((UINT8 *) Buffer + 1) = PciRead8 (PciLibAddress + 1);
102 *((UINT8 *) Buffer + 2) = PciRead8 (PciLibAddress + 2);
103 *((UINT8 *) Buffer + 3) = PciRead8 (PciLibAddress + 3);
104 }
105 } else {
106 return EFI_INVALID_PARAMETER;
107 }
108
109 return EFI_SUCCESS;
110 }
111
112 /**
113 Write to a given location in the PCI configuration space.
114
115 @param PeiServices An indirect pointer to the PEI Services Table published by the PEI Foundation.
116 @param This Pointer to local data for the interface.
117 @param Width The width of the access. Enumerated in bytes.
118 See EFI_PEI_PCI_CFG_PPI_WIDTH above.
119 @param Address The physical address of the access. The format of
120 the address is described by EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS.
121 @param Buffer A pointer to the buffer of data.
122
123 @retval EFI_SUCCESS The function completed successfully.
124 @retval EFI_INVALID_PARAMETER The invalid access width.
125
126 **/
127 EFI_STATUS
128 EFIAPI
PciCfg2Write(IN CONST EFI_PEI_SERVICES ** PeiServices,IN CONST EFI_PEI_PCI_CFG2_PPI * This,IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,IN UINT64 Address,IN OUT VOID * Buffer)129 PciCfg2Write (
130 IN CONST EFI_PEI_SERVICES **PeiServices,
131 IN CONST EFI_PEI_PCI_CFG2_PPI *This,
132 IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,
133 IN UINT64 Address,
134 IN OUT VOID *Buffer
135 )
136 {
137 UINTN PciLibAddress;
138
139 PciLibAddress = PciCfgAddressConvert ((EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS *) &Address);
140
141 if (Width == EfiPeiPciCfgWidthUint8) {
142 PciWrite8 (PciLibAddress, *((UINT8 *) Buffer));
143 } else if (Width == EfiPeiPciCfgWidthUint16) {
144 if ((PciLibAddress & 0x01) == 0) {
145 //
146 // Aligned Pci address access
147 //
148 PciWrite16 (PciLibAddress, ReadUnaligned16 ((UINT16 *) Buffer));
149 } else {
150 //
151 // Unaligned Pci address access, break up the request into byte by byte.
152 //
153 PciWrite8 (PciLibAddress, *((UINT8 *) Buffer));
154 PciWrite8 (PciLibAddress + 1, *((UINT8 *) Buffer + 1));
155 }
156 } else if (Width == EfiPeiPciCfgWidthUint32) {
157 if ((PciLibAddress & 0x03) == 0) {
158 //
159 // Aligned Pci address access
160 //
161 PciWrite32 (PciLibAddress, ReadUnaligned32 ((UINT32 *) Buffer));
162 } else if ((PciLibAddress & 0x01) == 0) {
163 //
164 // Unaligned Pci address access, break up the request into word by word.
165 //
166 PciWrite16 (PciLibAddress, ReadUnaligned16 ((UINT16 *) Buffer));
167 PciWrite16 (PciLibAddress + 2, ReadUnaligned16 ((UINT16 *) Buffer + 1));
168 } else {
169 //
170 // Unaligned Pci address access, break up the request into byte by byte.
171 //
172 PciWrite8 (PciLibAddress, *((UINT8 *) Buffer));
173 PciWrite8 (PciLibAddress + 1, *((UINT8 *) Buffer + 1));
174 PciWrite8 (PciLibAddress + 2, *((UINT8 *) Buffer + 2));
175 PciWrite8 (PciLibAddress + 3, *((UINT8 *) Buffer + 3));
176 }
177 } else {
178 return EFI_INVALID_PARAMETER;
179 }
180
181 return EFI_SUCCESS;
182 }
183
184
185 /**
186 This function performs a read-modify-write operation on the contents from a given
187 location in the PCI configuration space.
188
189 @param PeiServices An indirect pointer to the PEI Services Table
190 published by the PEI Foundation.
191 @param This Pointer to local data for the interface.
192 @param Width The width of the access. Enumerated in bytes. Type
193 EFI_PEI_PCI_CFG_PPI_WIDTH is defined in Read().
194 @param Address The physical address of the access.
195 @param SetBits Points to value to bitwise-OR with the read configuration value.
196 The size of the value is determined by Width.
197 @param ClearBits Points to the value to negate and bitwise-AND with the read configuration value.
198 The size of the value is determined by Width.
199
200 @retval EFI_SUCCESS The function completed successfully.
201 @retval EFI_INVALID_PARAMETER The invalid access width.
202
203 **/
204 EFI_STATUS
205 EFIAPI
PciCfg2Modify(IN CONST EFI_PEI_SERVICES ** PeiServices,IN CONST EFI_PEI_PCI_CFG2_PPI * This,IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,IN UINT64 Address,IN VOID * SetBits,IN VOID * ClearBits)206 PciCfg2Modify (
207 IN CONST EFI_PEI_SERVICES **PeiServices,
208 IN CONST EFI_PEI_PCI_CFG2_PPI *This,
209 IN EFI_PEI_PCI_CFG_PPI_WIDTH Width,
210 IN UINT64 Address,
211 IN VOID *SetBits,
212 IN VOID *ClearBits
213 )
214 {
215 UINTN PciLibAddress;
216 UINT16 ClearValue16;
217 UINT16 SetValue16;
218 UINT32 ClearValue32;
219 UINT32 SetValue32;
220
221 PciLibAddress = PciCfgAddressConvert ((EFI_PEI_PCI_CFG_PPI_PCI_ADDRESS *) &Address);
222
223 if (Width == EfiPeiPciCfgWidthUint8) {
224 PciAndThenOr8 (PciLibAddress, (UINT8) (~(*(UINT8 *) ClearBits)), *((UINT8 *) SetBits));
225 } else if (Width == EfiPeiPciCfgWidthUint16) {
226 if ((PciLibAddress & 0x01) == 0) {
227 //
228 // Aligned Pci address access
229 //
230 ClearValue16 = (UINT16) (~ReadUnaligned16 ((UINT16 *) ClearBits));
231 SetValue16 = ReadUnaligned16 ((UINT16 *) SetBits);
232 PciAndThenOr16 (PciLibAddress, ClearValue16, SetValue16);
233 } else {
234 //
235 // Unaligned Pci address access, break up the request into byte by byte.
236 //
237 PciAndThenOr8 (PciLibAddress, (UINT8) (~(*(UINT8 *) ClearBits)), *((UINT8 *) SetBits));
238 PciAndThenOr8 (PciLibAddress + 1, (UINT8) (~(*((UINT8 *) ClearBits + 1))), *((UINT8 *) SetBits + 1));
239 }
240 } else if (Width == EfiPeiPciCfgWidthUint32) {
241 if ((PciLibAddress & 0x03) == 0) {
242 //
243 // Aligned Pci address access
244 //
245 ClearValue32 = (UINT32) (~ReadUnaligned32 ((UINT32 *) ClearBits));
246 SetValue32 = ReadUnaligned32 ((UINT32 *) SetBits);
247 PciAndThenOr32 (PciLibAddress, ClearValue32, SetValue32);
248 } else if ((PciLibAddress & 0x01) == 0) {
249 //
250 // Unaligned Pci address access, break up the request into word by word.
251 //
252 ClearValue16 = (UINT16) (~ReadUnaligned16 ((UINT16 *) ClearBits));
253 SetValue16 = ReadUnaligned16 ((UINT16 *) SetBits);
254 PciAndThenOr16 (PciLibAddress, ClearValue16, SetValue16);
255
256 ClearValue16 = (UINT16) (~ReadUnaligned16 ((UINT16 *) ClearBits + 1));
257 SetValue16 = ReadUnaligned16 ((UINT16 *) SetBits + 1);
258 PciAndThenOr16 (PciLibAddress + 2, ClearValue16, SetValue16);
259 } else {
260 //
261 // Unaligned Pci address access, break up the request into byte by byte.
262 //
263 PciAndThenOr8 (PciLibAddress, (UINT8) (~(*(UINT8 *) ClearBits)), *((UINT8 *) SetBits));
264 PciAndThenOr8 (PciLibAddress + 1, (UINT8) (~(*((UINT8 *) ClearBits + 1))), *((UINT8 *) SetBits + 1));
265 PciAndThenOr8 (PciLibAddress + 2, (UINT8) (~(*((UINT8 *) ClearBits + 2))), *((UINT8 *) SetBits + 2));
266 PciAndThenOr8 (PciLibAddress + 3, (UINT8) (~(*((UINT8 *) ClearBits + 3))), *((UINT8 *) SetBits + 3));
267 }
268 } else {
269 return EFI_INVALID_PARAMETER;
270 }
271
272 return EFI_SUCCESS;
273 }
274
275 EFI_PEI_PCI_CFG2_PPI gPciCfg2Ppi = {
276 PciCfg2Read,
277 PciCfg2Write,
278 PciCfg2Modify,
279 0
280 };
281
282 EFI_PEI_PPI_DESCRIPTOR gPciCfg2PpiList = {
283 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
284 &gEfiPciCfg2PpiGuid,
285 &gPciCfg2Ppi
286 };
287
288 /**
289 Module's entry function.
290 This routine will install EFI_PEI_PCI_CFG2_PPI.
291
292 @param FileHandle Handle of the file being invoked.
293 @param PeiServices Describes the list of possible PEI Services.
294
295 @return Whether success to install service.
296 **/
297 EFI_STATUS
298 EFIAPI
PeimInitializePciCfg(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)299 PeimInitializePciCfg (
300 IN EFI_PEI_FILE_HANDLE FileHandle,
301 IN CONST EFI_PEI_SERVICES **PeiServices
302 )
303 {
304 EFI_STATUS Status;
305
306 (**(EFI_PEI_SERVICES **)PeiServices).PciCfg = &gPciCfg2Ppi;
307 Status = PeiServicesInstallPpi (&gPciCfg2PpiList);
308 ASSERT_EFI_ERROR (Status);
309
310 return Status;
311 }
312