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