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