]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.c
OvmfPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / OvmfPkg / Library / BasePciCapPciSegmentLib / BasePciCapPciSegmentLib.c
CommitLineData
6a744d40
LE
1/** @file\r
2 Plug a PciSegmentLib 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
6a744d40
LE
7**/\r
8\r
9#include <IndustryStandard/Pci23.h>\r
10\r
11#include <Library/BaseLib.h>\r
12#include <Library/MemoryAllocationLib.h>\r
13#include <Library/PciSegmentLib.h>\r
14\r
15#include "BasePciCapPciSegmentLib.h"\r
16\r
17\r
18/**\r
19 Read the config space of a given PCI device (both normal and extended).\r
20\r
21 SegmentDevReadConfig() performs as few config space accesses as possible\r
22 (without attempting 64-bit wide accesses).\r
23\r
24 @param[in] PciDevice Implementation-specific unique representation\r
25 of the PCI device in the PCI hierarchy.\r
26\r
27 @param[in] SourceOffset Source offset in the config space of the PCI\r
28 device to start reading from.\r
29\r
30 @param[out] DestinationBuffer Buffer to store the read data to.\r
31\r
32 @param[in] Size The number of bytes to transfer.\r
33\r
34 @retval RETURN_SUCCESS Size bytes have been transferred from config\r
35 space to DestinationBuffer.\r
36\r
37 @retval RETURN_UNSUPPORTED Accessing Size bytes from SourceOffset exceeds\r
38 the config space limit of the PCI device.\r
39 Although PCI_CAP_DEV_READ_CONFIG allows reading\r
40 fewer than Size bytes in this case,\r
41 SegmentDevReadConfig() will read none.\r
42**/\r
43STATIC\r
44RETURN_STATUS\r
45EFIAPI\r
46SegmentDevReadConfig (\r
47 IN PCI_CAP_DEV *PciDevice,\r
48 IN UINT16 SourceOffset,\r
49 OUT VOID *DestinationBuffer,\r
50 IN UINT16 Size\r
51 )\r
52{\r
53 SEGMENT_DEV *SegmentDev;\r
54 UINT16 ConfigSpaceSize;\r
55 UINT64 SourceAddress;\r
56\r
57 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
58 ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
59 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
60 //\r
61 // Note that all UINT16 variables below are promoted to INT32, and the\r
62 // addition and the comparison is carried out in INT32.\r
63 //\r
64 if (SourceOffset + Size > ConfigSpaceSize) {\r
65 return RETURN_UNSUPPORTED;\r
66 }\r
67 SourceAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
68 SegmentDev->BusNr, SegmentDev->DeviceNr,\r
69 SegmentDev->FunctionNr, SourceOffset);\r
70 PciSegmentReadBuffer (SourceAddress, Size, DestinationBuffer);\r
71 return RETURN_SUCCESS;\r
72}\r
73\r
74\r
75/**\r
76 Write the config space of a given PCI device (both normal and extended).\r
77\r
78 SegmentDevWriteConfig() performs as few config space accesses as possible\r
79 (without attempting 64-bit wide accesses).\r
80\r
81 @param[in] PciDevice Implementation-specific unique representation\r
82 of the PCI device in the PCI hierarchy.\r
83\r
84 @param[in] DestinationOffset Destination offset in the config space of the\r
85 PCI device to start writing at.\r
86\r
87 @param[in] SourceBuffer Buffer to read the data to be stored from.\r
88\r
89 @param[in] Size The number of bytes to transfer.\r
90\r
91 @retval RETURN_SUCCESS Size bytes have been transferred from\r
92 SourceBuffer to config space.\r
93\r
94 @retval RETURN_UNSUPPORTED Accessing Size bytes at DestinationOffset exceeds\r
95 the config space limit of the PCI device.\r
96 Although PCI_CAP_DEV_WRITE_CONFIG allows writing\r
97 fewer than Size bytes in this case,\r
98 SegmentDevWriteConfig() will write none.\r
99**/\r
100STATIC\r
101RETURN_STATUS\r
102EFIAPI\r
103SegmentDevWriteConfig (\r
104 IN PCI_CAP_DEV *PciDevice,\r
105 IN UINT16 DestinationOffset,\r
106 IN VOID *SourceBuffer,\r
107 IN UINT16 Size\r
108 )\r
109{\r
110 SEGMENT_DEV *SegmentDev;\r
111 UINT16 ConfigSpaceSize;\r
112 UINT64 DestinationAddress;\r
113\r
114 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
115 ConfigSpaceSize = (SegmentDev->MaxDomain == PciCapNormal ?\r
116 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
117 //\r
118 // Note that all UINT16 variables below are promoted to INT32, and the\r
119 // addition and the comparison is carried out in INT32.\r
120 //\r
121 if (DestinationOffset + Size > ConfigSpaceSize) {\r
122 return RETURN_UNSUPPORTED;\r
123 }\r
124 DestinationAddress = PCI_SEGMENT_LIB_ADDRESS (SegmentDev->SegmentNr,\r
125 SegmentDev->BusNr, SegmentDev->DeviceNr,\r
126 SegmentDev->FunctionNr, DestinationOffset);\r
127 PciSegmentWriteBuffer (DestinationAddress, Size, SourceBuffer);\r
128 return RETURN_SUCCESS;\r
129}\r
130\r
131\r
132/**\r
133 Create a PCI_CAP_DEV object from the PCI Segment:Bus:Device.Function\r
134 quadruplet. The config space accessors are based upon PciSegmentLib.\r
135\r
136 @param[in] MaxDomain If MaxDomain is PciCapExtended, then\r
137 PciDevice->ReadConfig() and PciDevice->WriteConfig()\r
138 will delegate extended config space accesses too to\r
139 PciSegmentReadBuffer() and PciSegmentWriteBuffer(),\r
140 respectively. Otherwise, PciDevice->ReadConfig() and\r
141 PciDevice->WriteConfig() will reject accesses to\r
142 extended config space with RETURN_UNSUPPORTED, without\r
143 calling PciSegmentReadBuffer() or\r
144 PciSegmentWriteBuffer(). By setting MaxDomain to\r
145 PciCapNormal, the platform can prevent undefined\r
146 PciSegmentLib behavior when the PCI root bridge under\r
147 the PCI device at Segment:Bus:Device.Function doesn't\r
148 support extended config space.\r
149\r
150 @param[in] Segment 16-bit wide segment number.\r
151\r
152 @param[in] Bus 8-bit wide bus number.\r
153\r
154 @param[in] Device 5-bit wide device number.\r
155\r
156 @param[in] Function 3-bit wide function number.\r
157\r
158 @param[out] PciDevice The PCI_CAP_DEV object constructed as described above.\r
159 PciDevice can be passed to the PciCapLib APIs.\r
160\r
161 @retval RETURN_SUCCESS PciDevice has been constructed and output.\r
162\r
163 @retval RETURN_INVALID_PARAMETER Device or Function does not fit in the\r
164 permitted number of bits.\r
165\r
166 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
167**/\r
168RETURN_STATUS\r
169EFIAPI\r
170PciCapPciSegmentDeviceInit (\r
171 IN PCI_CAP_DOMAIN MaxDomain,\r
172 IN UINT16 Segment,\r
173 IN UINT8 Bus,\r
174 IN UINT8 Device,\r
175 IN UINT8 Function,\r
176 OUT PCI_CAP_DEV **PciDevice\r
177 )\r
178{\r
179 SEGMENT_DEV *SegmentDev;\r
180\r
181 if (Device > PCI_MAX_DEVICE || Function > PCI_MAX_FUNC) {\r
182 return RETURN_INVALID_PARAMETER;\r
183 }\r
184\r
185 SegmentDev = AllocatePool (sizeof *SegmentDev);\r
186 if (SegmentDev == NULL) {\r
187 return RETURN_OUT_OF_RESOURCES;\r
188 }\r
189\r
190 SegmentDev->Signature = SEGMENT_DEV_SIG;\r
191 SegmentDev->MaxDomain = MaxDomain;\r
192 SegmentDev->SegmentNr = Segment;\r
193 SegmentDev->BusNr = Bus;\r
194 SegmentDev->DeviceNr = Device;\r
195 SegmentDev->FunctionNr = Function;\r
196 SegmentDev->BaseDevice.ReadConfig = SegmentDevReadConfig;\r
197 SegmentDev->BaseDevice.WriteConfig = SegmentDevWriteConfig;\r
198\r
199 *PciDevice = &SegmentDev->BaseDevice;\r
200 return RETURN_SUCCESS;\r
201}\r
202\r
203\r
204/**\r
205 Free the resources used by PciDevice.\r
206\r
207 @param[in] PciDevice The PCI_CAP_DEV object to free, originally produced by\r
208 PciCapPciSegmentDeviceInit().\r
209**/\r
210VOID\r
211EFIAPI\r
212PciCapPciSegmentDeviceUninit (\r
213 IN PCI_CAP_DEV *PciDevice\r
214 )\r
215{\r
216 SEGMENT_DEV *SegmentDev;\r
217\r
218 SegmentDev = SEGMENT_DEV_FROM_PCI_CAP_DEV (PciDevice);\r
219 FreePool (SegmentDev);\r
220}\r