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
{
37 An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge
38 protocol device paths based on UID.
40 @param[in] UserStruct1 Pointer to the first ACPI_HID_DEVICE_PATH.
42 @param[in] UserStruct2 Pointer to the second ACPI_HID_DEVICE_PATH.
44 @retval <0 If UserStruct1 compares less than UserStruct2.
46 @retval 0 If UserStruct1 compares equal to UserStruct2.
48 @retval >0 If UserStruct1 compares greater than UserStruct2.
53 RootBridgePathCompare (
54 IN CONST VOID
*UserStruct1
,
55 IN CONST VOID
*UserStruct2
58 CONST ACPI_HID_DEVICE_PATH
*Acpi1
;
59 CONST ACPI_HID_DEVICE_PATH
*Acpi2
;
64 return Acpi1
->UID
< Acpi2
->UID
? -1 :
65 Acpi1
->UID
> Acpi2
->UID
? 1 :
71 An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge
72 protocol device path against a UID.
74 @param[in] StandaloneKey Pointer to the bare UINT32 UID.
76 @param[in] UserStruct Pointer to the ACPI_HID_DEVICE_PATH with the
79 @retval <0 If StandaloneKey compares less than UserStruct's key.
81 @retval 0 If StandaloneKey compares equal to UserStruct's key.
83 @retval >0 If StandaloneKey compares greater than UserStruct's key.
88 RootBridgePathKeyCompare (
89 IN CONST VOID
*StandaloneKey
,
90 IN CONST VOID
*UserStruct
94 CONST ACPI_HID_DEVICE_PATH
*Acpi
;
99 return *Uid
< Acpi
->UID
? -1 :
100 *Uid
> Acpi
->UID
? 1 :
106 Create a structure that maps the relative positions of PCI root buses to bus
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.
116 @param[out] ExtraRootBusMap The data structure implementing the map.
118 @retval EFI_SUCCESS ExtraRootBusMap has been populated.
120 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
122 @retval EFI_ALREADY_STARTED A duplicate root bus number has been found in
123 the system. (This should never happen.)
125 @return Error codes returned by
126 gBS->LocateHandleBuffer() and
127 gBS->HandleProtocol().
131 CreateExtraRootBusMap (
132 OUT EXTRA_ROOT_BUS_MAP
**ExtraRootBusMap
138 ORDERED_COLLECTION
*Collection
;
139 EXTRA_ROOT_BUS_MAP
*Map
;
141 ORDERED_COLLECTION_ENTRY
*Entry
, *Entry2
;
144 // Handles and Collection are temporary / helper variables, while in Map we
145 // build the return value.
148 Status
= gBS
->LocateHandleBuffer (ByProtocol
,
149 &gEfiPciRootBridgeIoProtocolGuid
, NULL
/* SearchKey */,
150 &NumHandles
, &Handles
);
151 if (EFI_ERROR (Status
)) {
155 Collection
= OrderedCollectionInit (RootBridgePathCompare
,
156 RootBridgePathKeyCompare
);
157 if (Collection
== NULL
) {
158 Status
= EFI_OUT_OF_RESOURCES
;
162 Map
= AllocateZeroPool (sizeof *Map
);
164 Status
= EFI_OUT_OF_RESOURCES
;
169 // Collect the ACPI device path protocols of the root bridges.
171 for (Idx
= 0; Idx
< NumHandles
; ++Idx
) {
172 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
174 Status
= gBS
->HandleProtocol (Handles
[Idx
], &gEfiDevicePathProtocolGuid
,
175 (VOID
**)&DevicePath
);
176 if (EFI_ERROR (Status
)) {
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.
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
)) {
197 if (Map
->Count
> 0) {
199 // At least one extra PCI root bus exists.
201 Map
->BusNumbers
= AllocatePool (Map
->Count
* sizeof *Map
->BusNumbers
);
202 if (Map
->BusNumbers
== NULL
) {
203 Status
= EFI_OUT_OF_RESOURCES
;
209 // Now collect the bus numbers of the extra PCI root buses into Map.
212 Entry
= OrderedCollectionMin (Collection
);
213 while (Idx
< Map
->Count
) {
214 ACPI_HID_DEVICE_PATH
*Acpi
;
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
));
223 Entry
= OrderedCollectionNext (Entry
);
225 ASSERT (Entry
== NULL
);
227 *ExtraRootBusMap
= Map
;
228 Status
= EFI_SUCCESS
;
231 // Fall through in order to release temporaries.
235 if (EFI_ERROR (Status
)) {
236 if (Map
->BusNumbers
!= NULL
) {
237 FreePool (Map
->BusNumbers
);
243 for (Entry
= OrderedCollectionMin (Collection
); Entry
!= NULL
;
245 Entry2
= OrderedCollectionNext (Entry
);
246 OrderedCollectionDelete (Collection
, Entry
, NULL
);
248 OrderedCollectionUninit (Collection
);
258 Release a map created with CreateExtraRootBusMap().
260 @param[in] ExtraRootBusMap The map to release.
263 DestroyExtraRootBusMap (
264 IN EXTRA_ROOT_BUS_MAP
*ExtraRootBusMap
267 if (ExtraRootBusMap
->BusNumbers
!= NULL
) {
268 FreePool (ExtraRootBusMap
->BusNumbers
);
270 FreePool (ExtraRootBusMap
);
274 Map the position (serial number) of an extra PCI root bus to its bus number.
276 @param[in] ExtraRootBusMap The map created with CreateExtraRootBusMap();
278 @param[in] RootBusPos The extra PCI root bus position to map.
280 @param[out] RootBusNr The bus number belonging to the extra PCI root
281 bus identified by RootBusPos.
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.
288 @retval EFI_NOT_FOUND RootBusPos is not found in ExtraRootBusMap.
290 @retval EFI_SUCCESS Mapping successful.
293 MapRootBusPosToBusNr (
294 IN CONST EXTRA_ROOT_BUS_MAP
*ExtraRootBusMap
,
295 IN UINT64 RootBusPos
,
296 OUT UINT32
*RootBusNr
299 if (RootBusPos
== 0) {
300 return EFI_INVALID_PARAMETER
;
302 if (RootBusPos
> ExtraRootBusMap
->Count
) {
303 return EFI_NOT_FOUND
;
305 *RootBusNr
= ExtraRootBusMap
->BusNumbers
[(UINTN
)RootBusPos
- 1];