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