]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/QemuBootOrderLib/ExtraRootBusMap.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / Library / QemuBootOrderLib / ExtraRootBusMap.c
1 /** @file
2 Map positions of extra PCI root buses to bus numbers.
3
4 Copyright (C) 2015, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <Library/DebugLib.h>
10 #include <Library/DevicePathLib.h>
11 #include <Library/MemoryAllocationLib.h>
12 #include <Library/OrderedCollectionLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 #include <Protocol/DevicePath.h>
15 #include <Protocol/PciRootBridgeIo.h>
16
17 #include "ExtraRootBusMap.h"
18
19 //
20 // The BusNumbers field is an array with Count elements. The elements increase
21 // strictry monotonically. Zero is not an element (because the zero bus number
22 // belongs to the "main" root bus, never to an extra root bus). Offset N in the
23 // array maps the extra root bus with position (N+1) to its bus number (because
24 // the root bus with position 0 is always the main root bus, therefore we don't
25 // store it).
26 //
27 // If there are no extra root buses in the system, then Count is 0, and
28 // BusNumbers is NULL.
29 //
30 struct EXTRA_ROOT_BUS_MAP_STRUCT {
31 UINT32 *BusNumbers;
32 UINTN Count;
33 };
34
35 /**
36 An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge
37 protocol device paths based on UID.
38
39 @param[in] UserStruct1 Pointer to the first ACPI_HID_DEVICE_PATH.
40
41 @param[in] UserStruct2 Pointer to the second ACPI_HID_DEVICE_PATH.
42
43 @retval <0 If UserStruct1 compares less than UserStruct2.
44
45 @retval 0 If UserStruct1 compares equal to UserStruct2.
46
47 @retval >0 If UserStruct1 compares greater than UserStruct2.
48 **/
49 STATIC
50 INTN
51 EFIAPI
52 RootBridgePathCompare (
53 IN CONST VOID *UserStruct1,
54 IN CONST VOID *UserStruct2
55 )
56 {
57 CONST ACPI_HID_DEVICE_PATH *Acpi1;
58 CONST ACPI_HID_DEVICE_PATH *Acpi2;
59
60 Acpi1 = UserStruct1;
61 Acpi2 = UserStruct2;
62
63 return Acpi1->UID < Acpi2->UID ? -1 :
64 Acpi1->UID > Acpi2->UID ? 1 :
65 0;
66 }
67
68 /**
69 An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge
70 protocol device path against a UID.
71
72 @param[in] StandaloneKey Pointer to the bare UINT32 UID.
73
74 @param[in] UserStruct Pointer to the ACPI_HID_DEVICE_PATH with the
75 embedded UINT32 UID.
76
77 @retval <0 If StandaloneKey compares less than UserStruct's key.
78
79 @retval 0 If StandaloneKey compares equal to UserStruct's key.
80
81 @retval >0 If StandaloneKey compares greater than UserStruct's key.
82 **/
83 STATIC
84 INTN
85 EFIAPI
86 RootBridgePathKeyCompare (
87 IN CONST VOID *StandaloneKey,
88 IN CONST VOID *UserStruct
89 )
90 {
91 CONST UINT32 *Uid;
92 CONST ACPI_HID_DEVICE_PATH *Acpi;
93
94 Uid = StandaloneKey;
95 Acpi = UserStruct;
96
97 return *Uid < Acpi->UID ? -1 :
98 *Uid > Acpi->UID ? 1 :
99 0;
100 }
101
102 /**
103 Create a structure that maps the relative positions of PCI root buses to bus
104 numbers.
105
106 In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their
107 positions, in relative root bus number order, not by their actual PCI bus
108 numbers. The ACPI HID device path nodes however that are associated with
109 PciRootBridgeIo protocol instances in the system have their UID fields set to
110 the bus numbers. Create a map that gives, for each extra PCI root bus's
111 position (ie. "serial number") its actual PCI bus number.
112
113 @param[out] ExtraRootBusMap The data structure implementing the map.
114
115 @retval EFI_SUCCESS ExtraRootBusMap has been populated.
116
117 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
118
119 @retval EFI_ALREADY_STARTED A duplicate root bus number has been found in
120 the system. (This should never happen.)
121
122 @return Error codes returned by
123 gBS->LocateHandleBuffer() and
124 gBS->HandleProtocol().
125
126 **/
127 EFI_STATUS
128 CreateExtraRootBusMap (
129 OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap
130 )
131 {
132 EFI_STATUS Status;
133 UINTN NumHandles;
134 EFI_HANDLE *Handles;
135 ORDERED_COLLECTION *Collection;
136 EXTRA_ROOT_BUS_MAP *Map;
137 UINTN Idx;
138 ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
139
140 //
141 // Handles and Collection are temporary / helper variables, while in Map we
142 // build the return value.
143 //
144
145 Status = gBS->LocateHandleBuffer (
146 ByProtocol,
147 &gEfiPciRootBridgeIoProtocolGuid,
148 NULL /* SearchKey */,
149 &NumHandles,
150 &Handles
151 );
152 if (EFI_ERROR (Status)) {
153 return Status;
154 }
155
156 Collection = OrderedCollectionInit (
157 RootBridgePathCompare,
158 RootBridgePathKeyCompare
159 );
160 if (Collection == NULL) {
161 Status = EFI_OUT_OF_RESOURCES;
162 goto FreeHandles;
163 }
164
165 Map = AllocateZeroPool (sizeof *Map);
166 if (Map == NULL) {
167 Status = EFI_OUT_OF_RESOURCES;
168 goto FreeCollection;
169 }
170
171 //
172 // Collect the ACPI device path protocols of the root bridges.
173 //
174 for (Idx = 0; Idx < NumHandles; ++Idx) {
175 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
176
177 Status = gBS->HandleProtocol (
178 Handles[Idx],
179 &gEfiDevicePathProtocolGuid,
180 (VOID **)&DevicePath
181 );
182 if (EFI_ERROR (Status)) {
183 goto FreeMap;
184 }
185
186 //
187 // Examine if the device path is an ACPI HID one, and if so, if UID is
188 // nonzero (ie. the root bridge that the bus number belongs to is "extra",
189 // not the main one). In that case, link the device path into Collection.
190 //
191 if ((DevicePathType (DevicePath) == ACPI_DEVICE_PATH) &&
192 (DevicePathSubType (DevicePath) == ACPI_DP) &&
193 (((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID (0x0A03)) &&
194 (((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0))
195 {
196 Status = OrderedCollectionInsert (Collection, NULL, DevicePath);
197 if (EFI_ERROR (Status)) {
198 goto FreeMap;
199 }
200
201 ++Map->Count;
202 }
203 }
204
205 if (Map->Count > 0) {
206 //
207 // At least one extra PCI root bus exists.
208 //
209 Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);
210 if (Map->BusNumbers == NULL) {
211 Status = EFI_OUT_OF_RESOURCES;
212 goto FreeMap;
213 }
214 }
215
216 //
217 // Now collect the bus numbers of the extra PCI root buses into Map.
218 //
219 Idx = 0;
220 Entry = OrderedCollectionMin (Collection);
221 while (Idx < Map->Count) {
222 ACPI_HID_DEVICE_PATH *Acpi;
223
224 ASSERT (Entry != NULL);
225 Acpi = OrderedCollectionUserStruct (Entry);
226 Map->BusNumbers[Idx] = Acpi->UID;
227 DEBUG ((
228 DEBUG_VERBOSE,
229 "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",
230 __FUNCTION__,
231 (UINT64)(Idx + 1),
232 Acpi->UID
233 ));
234 ++Idx;
235 Entry = OrderedCollectionNext (Entry);
236 }
237
238 ASSERT (Entry == NULL);
239
240 *ExtraRootBusMap = Map;
241 Status = EFI_SUCCESS;
242
243 //
244 // Fall through in order to release temporaries.
245 //
246
247 FreeMap:
248 if (EFI_ERROR (Status)) {
249 if (Map->BusNumbers != NULL) {
250 FreePool (Map->BusNumbers);
251 }
252
253 FreePool (Map);
254 }
255
256 FreeCollection:
257 for (Entry = OrderedCollectionMin (Collection); Entry != NULL;
258 Entry = Entry2)
259 {
260 Entry2 = OrderedCollectionNext (Entry);
261 OrderedCollectionDelete (Collection, Entry, NULL);
262 }
263
264 OrderedCollectionUninit (Collection);
265
266 FreeHandles:
267 FreePool (Handles);
268
269 return Status;
270 }
271
272 /**
273 Release a map created with CreateExtraRootBusMap().
274
275 @param[in] ExtraRootBusMap The map to release.
276 */
277 VOID
278 DestroyExtraRootBusMap (
279 IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap
280 )
281 {
282 if (ExtraRootBusMap->BusNumbers != NULL) {
283 FreePool (ExtraRootBusMap->BusNumbers);
284 }
285
286 FreePool (ExtraRootBusMap);
287 }
288
289 /**
290 Map the position (serial number) of an extra PCI root bus to its bus number.
291
292 @param[in] ExtraRootBusMap The map created with CreateExtraRootBusMap();
293
294 @param[in] RootBusPos The extra PCI root bus position to map.
295
296 @param[out] RootBusNr The bus number belonging to the extra PCI root
297 bus identified by RootBusPos.
298
299 @retval EFI_INVALID_PARAMETER RootBusPos is zero. The zero position
300 identifies the main root bus, whose bus number
301 is always zero, and is therefore never
302 maintained in ExtraRootBusMap.
303
304 @retval EFI_NOT_FOUND RootBusPos is not found in ExtraRootBusMap.
305
306 @retval EFI_SUCCESS Mapping successful.
307 **/
308 EFI_STATUS
309 MapRootBusPosToBusNr (
310 IN CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,
311 IN UINT64 RootBusPos,
312 OUT UINT32 *RootBusNr
313 )
314 {
315 if (RootBusPos == 0) {
316 return EFI_INVALID_PARAMETER;
317 }
318
319 if (RootBusPos > ExtraRootBusMap->Count) {
320 return EFI_NOT_FOUND;
321 }
322
323 *RootBusNr = ExtraRootBusMap->BusNumbers[(UINTN)RootBusPos - 1];
324 return EFI_SUCCESS;
325 }