]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.c
OvmfPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / OvmfPkg / Library / UefiPciCapPciIoLib / UefiPciCapPciIoLib.c
CommitLineData
02b9a834
LE
1/** @file\r
2 Plug an EFI_PCI_IO_PROTOCOL backend into PciCapLib, for config space access.\r
3\r
4 Copyright (C) 2018, Red Hat, Inc.\r
5\r
b26f0cf9 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
02b9a834
LE
7**/\r
8\r
9#include <Library/MemoryAllocationLib.h>\r
10\r
11#include "UefiPciCapPciIoLib.h"\r
12\r
13\r
14/**\r
15 Transfer bytes between the config space of a given PCI device and a memory\r
16 buffer.\r
17\r
18 ProtoDevTransferConfig() performs as few config space accesses as possible\r
19 (without attempting 64-bit wide accesses).\r
20\r
21 @param[in] PciIo The EFI_PCI_IO_PROTOCOL representation of the\r
22 PCI device.\r
23\r
24 @param[in] TransferFunction The EFI_PCI_IO_PROTOCOL_CONFIG function that\r
25 implements the transfer. The direction of the\r
26 transfer is inherent to TransferFunction.\r
27 TransferFunction() is required to return an\r
28 unspecified error if any sub-transfer within\r
29 Size bytes from ConfigOffset exceeds the config\r
30 space limit of the PCI device.\r
31\r
32 @param[in] ConfigOffset The offset in the config space of the PCI device\r
33 at which the transfer should commence.\r
34\r
35 @param[in,out] Buffer The memory buffer where the transfer should\r
36 occur.\r
37\r
38 @param[in] Size The number of bytes to transfer.\r
39\r
40 @retval EFI_SUCCESS Size bytes have been transferred between config space\r
41 and Buffer.\r
42\r
43 @return Error codes propagated from TransferFunction(). Fewer\r
44 than Size bytes may have been transferred.\r
45**/\r
46STATIC\r
47EFI_STATUS\r
48ProtoDevTransferConfig (\r
49 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
50 IN EFI_PCI_IO_PROTOCOL_CONFIG TransferFunction,\r
51 IN UINT16 ConfigOffset,\r
52 IN OUT UINT8 *Buffer,\r
53 IN UINT16 Size\r
54 )\r
55{\r
56 while (Size > 0) {\r
57 EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
58 UINT16 Count;\r
59 EFI_STATUS Status;\r
60 UINT16 Progress;\r
61\r
62 //\r
63 // Pick the largest access size that is allowed by the remaining transfer\r
64 // Size and by the alignment of ConfigOffset.\r
65 //\r
66 // When the largest access size is available, transfer as many bytes as\r
67 // possible in one iteration of the loop. Otherwise, transfer only one\r
68 // unit, to improve the alignment.\r
69 //\r
70 if (Size >= 4 && (ConfigOffset & 3) == 0) {\r
71 Width = EfiPciIoWidthUint32;\r
72 Count = Size >> Width;\r
73 } else if (Size >= 2 && (ConfigOffset & 1) == 0) {\r
74 Width = EfiPciIoWidthUint16;\r
75 Count = 1;\r
76 } else {\r
77 Width = EfiPciIoWidthUint8;\r
78 Count = 1;\r
79 }\r
80 Status = TransferFunction (PciIo, Width, ConfigOffset, Count, Buffer);\r
81 if (EFI_ERROR (Status)) {\r
82 return Status;\r
83 }\r
84 Progress = Count << Width;\r
85 ConfigOffset += Progress;\r
86 Buffer += Progress;\r
87 Size -= Progress;\r
88 }\r
89 return EFI_SUCCESS;\r
90}\r
91\r
92\r
93/**\r
94 Read the config space of a given PCI device (both normal and extended).\r
95\r
96 ProtoDevReadConfig() performs as few config space accesses as possible\r
97 (without attempting 64-bit wide accesses).\r
98\r
99 ProtoDevReadConfig() returns an unspecified error if accessing Size bytes\r
100 from SourceOffset exceeds the config space limit of the PCI device. Fewer\r
101 than Size bytes may have been read in this case.\r
102\r
103 @param[in] PciDevice Implementation-specific unique representation\r
104 of the PCI device in the PCI hierarchy.\r
105\r
106 @param[in] SourceOffset Source offset in the config space of the PCI\r
107 device to start reading from.\r
108\r
109 @param[out] DestinationBuffer Buffer to store the read data to.\r
110\r
111 @param[in] Size The number of bytes to transfer.\r
112\r
113 @retval RETURN_SUCCESS Size bytes have been transferred from config space to\r
114 DestinationBuffer.\r
115\r
116 @return Error codes propagated from\r
117 EFI_PCI_IO_PROTOCOL.Pci.Read(). Fewer than Size bytes\r
118 may have been read.\r
119**/\r
120STATIC\r
121RETURN_STATUS\r
122EFIAPI\r
123ProtoDevReadConfig (\r
124 IN PCI_CAP_DEV *PciDevice,\r
125 IN UINT16 SourceOffset,\r
126 OUT VOID *DestinationBuffer,\r
127 IN UINT16 Size\r
128 )\r
129{\r
130 PROTO_DEV *ProtoDev;\r
131\r
132 ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
133 return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Read,\r
134 SourceOffset, DestinationBuffer, Size);\r
135}\r
136\r
137\r
138/**\r
139 Write the config space of a given PCI device (both normal and extended).\r
140\r
141 ProtoDevWriteConfig() performs as few config space accesses as possible\r
142 (without attempting 64-bit wide accesses).\r
143\r
144 ProtoDevWriteConfig() returns an unspecified error if accessing Size bytes at\r
145 DestinationOffset exceeds the config space limit of the PCI device. Fewer\r
146 than Size bytes may have been written in this case.\r
147\r
148 @param[in] PciDevice Implementation-specific unique representation\r
149 of the PCI device in the PCI hierarchy.\r
150\r
151 @param[in] DestinationOffset Destination offset in the config space of the\r
152 PCI device to start writing at.\r
153\r
154 @param[in] SourceBuffer Buffer to read the data to be stored from.\r
155\r
156 @param[in] Size The number of bytes to transfer.\r
157\r
158 @retval RETURN_SUCCESS Size bytes have been transferred from SourceBuffer to\r
159 config space.\r
160\r
161 @return Error codes propagated from\r
162 EFI_PCI_IO_PROTOCOL.Pci.Write(). Fewer than Size\r
163 bytes may have been written.\r
164**/\r
165STATIC\r
166RETURN_STATUS\r
167EFIAPI\r
168ProtoDevWriteConfig (\r
169 IN PCI_CAP_DEV *PciDevice,\r
170 IN UINT16 DestinationOffset,\r
171 IN VOID *SourceBuffer,\r
172 IN UINT16 Size\r
173 )\r
174{\r
175 PROTO_DEV *ProtoDev;\r
176\r
177 ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
178 return ProtoDevTransferConfig (ProtoDev->PciIo, ProtoDev->PciIo->Pci.Write,\r
179 DestinationOffset, SourceBuffer, Size);\r
180}\r
181\r
182\r
183/**\r
184 Create a PCI_CAP_DEV object from an EFI_PCI_IO_PROTOCOL instance. The config\r
185 space accessors are based upon EFI_PCI_IO_PROTOCOL.Pci.Read() and\r
186 EFI_PCI_IO_PROTOCOL.Pci.Write().\r
187\r
188 @param[in] PciIo EFI_PCI_IO_PROTOCOL representation of the PCI device.\r
189\r
190 @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.\r
191 PciDevice can be passed to the PciCapLib APIs.\r
192\r
193 @retval EFI_SUCCESS PciDevice has been constructed and output.\r
194\r
195 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
196**/\r
197EFI_STATUS\r
198EFIAPI\r
199PciCapPciIoDeviceInit (\r
200 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
201 OUT PCI_CAP_DEV **PciDevice\r
202 )\r
203{\r
204 PROTO_DEV *ProtoDev;\r
205\r
206 ProtoDev = AllocatePool (sizeof *ProtoDev);\r
207 if (ProtoDev == NULL) {\r
208 return EFI_OUT_OF_RESOURCES;\r
209 }\r
210\r
211 ProtoDev->Signature = PROTO_DEV_SIG;\r
212 ProtoDev->PciIo = PciIo;\r
213 ProtoDev->BaseDevice.ReadConfig = ProtoDevReadConfig;\r
214 ProtoDev->BaseDevice.WriteConfig = ProtoDevWriteConfig;\r
215\r
216 *PciDevice = &ProtoDev->BaseDevice;\r
217 return EFI_SUCCESS;\r
218}\r
219\r
220\r
221/**\r
222 Free the resources used by PciDevice.\r
223\r
224 @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by\r
225 PciCapPciIoDeviceInit().\r
226**/\r
227VOID\r
228EFIAPI\r
229PciCapPciIoDeviceUninit (\r
230 IN PCI_CAP_DEV *PciDevice\r
231 )\r
232{\r
233 PROTO_DEV *ProtoDev;\r
234\r
235 ProtoDev = PROTO_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
236 FreePool (ProtoDev);\r
237}\r