2 Map positions of extra PCI root buses to bus numbers.
4 Copyright (C) 2015, Red Hat, Inc.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
17 #include "ExtraRootBusMap.h"
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
27 // If there are no extra root buses in the system, then Count is 0, and
28 // BusNumbers is NULL.
30 struct EXTRA_ROOT_BUS_MAP_STRUCT
{
36 An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge
37 protocol device paths based on UID.
39 @param[in] UserStruct1 Pointer to the first ACPI_HID_DEVICE_PATH.
41 @param[in] UserStruct2 Pointer to the second ACPI_HID_DEVICE_PATH.
43 @retval <0 If UserStruct1 compares less than UserStruct2.
45 @retval 0 If UserStruct1 compares equal to UserStruct2.
47 @retval >0 If UserStruct1 compares greater than UserStruct2.
52 RootBridgePathCompare (
53 IN CONST VOID
*UserStruct1
,
54 IN CONST VOID
*UserStruct2
57 CONST ACPI_HID_DEVICE_PATH
*Acpi1
;
58 CONST ACPI_HID_DEVICE_PATH
*Acpi2
;
63 return Acpi1
->UID
< Acpi2
->UID
? -1 :
64 Acpi1
->UID
> Acpi2
->UID
? 1 :
69 An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge
70 protocol device path against a UID.
72 @param[in] StandaloneKey Pointer to the bare UINT32 UID.
74 @param[in] UserStruct Pointer to the ACPI_HID_DEVICE_PATH with the
77 @retval <0 If StandaloneKey compares less than UserStruct's key.
79 @retval 0 If StandaloneKey compares equal to UserStruct's key.
81 @retval >0 If StandaloneKey compares greater than UserStruct's key.
86 RootBridgePathKeyCompare (
87 IN CONST VOID
*StandaloneKey
,
88 IN CONST VOID
*UserStruct
92 CONST ACPI_HID_DEVICE_PATH
*Acpi
;
97 return *Uid
< Acpi
->UID
? -1 :
98 *Uid
> Acpi
->UID
? 1 :
103 Create a structure that maps the relative positions of PCI root buses to bus
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.
113 @param[out] ExtraRootBusMap The data structure implementing the map.
115 @retval EFI_SUCCESS ExtraRootBusMap has been populated.
117 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
119 @retval EFI_ALREADY_STARTED A duplicate root bus number has been found in
120 the system. (This should never happen.)
122 @return Error codes returned by
123 gBS->LocateHandleBuffer() and
124 gBS->HandleProtocol().
128 CreateExtraRootBusMap (
129 OUT EXTRA_ROOT_BUS_MAP
**ExtraRootBusMap
135 ORDERED_COLLECTION
*Collection
;
136 EXTRA_ROOT_BUS_MAP
*Map
;
138 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
141 // Handles and Collection are temporary / helper variables, while in Map we
142 // build the return value.
145 Status
= gBS
->LocateHandleBuffer (
147 &gEfiPciRootBridgeIoProtocolGuid
,
148 NULL
/* SearchKey */,
152 if (EFI_ERROR (Status
)) {
156 Collection
= OrderedCollectionInit (
157 RootBridgePathCompare
,
158 RootBridgePathKeyCompare
160 if (Collection
== NULL
) {
161 Status
= EFI_OUT_OF_RESOURCES
;
165 Map
= AllocateZeroPool (sizeof *Map
);
167 Status
= EFI_OUT_OF_RESOURCES
;
172 // Collect the ACPI device path protocols of the root bridges.
174 for (Idx
= 0; Idx
< NumHandles
; ++Idx
) {
175 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
177 Status
= gBS
->HandleProtocol (
179 &gEfiDevicePathProtocolGuid
,
182 if (EFI_ERROR (Status
)) {
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.
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))
196 Status
= OrderedCollectionInsert (Collection
, NULL
, DevicePath
);
197 if (EFI_ERROR (Status
)) {
205 if (Map
->Count
> 0) {
207 // At least one extra PCI root bus exists.
209 Map
->BusNumbers
= AllocatePool (Map
->Count
* sizeof *Map
->BusNumbers
);
210 if (Map
->BusNumbers
== NULL
) {
211 Status
= EFI_OUT_OF_RESOURCES
;
217 // Now collect the bus numbers of the extra PCI root buses into Map.
220 Entry
= OrderedCollectionMin (Collection
);
221 while (Idx
< Map
->Count
) {
222 ACPI_HID_DEVICE_PATH
*Acpi
;
224 ASSERT (Entry
!= NULL
);
225 Acpi
= OrderedCollectionUserStruct (Entry
);
226 Map
->BusNumbers
[Idx
] = Acpi
->UID
;
229 "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",
235 Entry
= OrderedCollectionNext (Entry
);
238 ASSERT (Entry
== NULL
);
240 *ExtraRootBusMap
= Map
;
241 Status
= EFI_SUCCESS
;
244 // Fall through in order to release temporaries.
248 if (EFI_ERROR (Status
)) {
249 if (Map
->BusNumbers
!= NULL
) {
250 FreePool (Map
->BusNumbers
);
257 for (Entry
= OrderedCollectionMin (Collection
); Entry
!= NULL
;
260 Entry2
= OrderedCollectionNext (Entry
);
261 OrderedCollectionDelete (Collection
, Entry
, NULL
);
264 OrderedCollectionUninit (Collection
);
273 Release a map created with CreateExtraRootBusMap().
275 @param[in] ExtraRootBusMap The map to release.
278 DestroyExtraRootBusMap (
279 IN EXTRA_ROOT_BUS_MAP
*ExtraRootBusMap
282 if (ExtraRootBusMap
->BusNumbers
!= NULL
) {
283 FreePool (ExtraRootBusMap
->BusNumbers
);
286 FreePool (ExtraRootBusMap
);
290 Map the position (serial number) of an extra PCI root bus to its bus number.
292 @param[in] ExtraRootBusMap The map created with CreateExtraRootBusMap();
294 @param[in] RootBusPos The extra PCI root bus position to map.
296 @param[out] RootBusNr The bus number belonging to the extra PCI root
297 bus identified by RootBusPos.
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.
304 @retval EFI_NOT_FOUND RootBusPos is not found in ExtraRootBusMap.
306 @retval EFI_SUCCESS Mapping successful.
309 MapRootBusPosToBusNr (
310 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraRootBusMap
,
311 IN UINT64 RootBusPos
,
312 OUT UINT32
*RootBusNr
315 if (RootBusPos
== 0) {
316 return EFI_INVALID_PARAMETER
;
319 if (RootBusPos
> ExtraRootBusMap
->Count
) {
320 return EFI_NOT_FOUND
;
323 *RootBusNr
= ExtraRootBusMap
->BusNumbers
[(UINTN
)RootBusPos
- 1];