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