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