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 | }
|
---|