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