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