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