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