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