1 | /** @file
|
---|
2 | Provide common utility functions to PciHostBridgeLib instances in
|
---|
3 | ArmVirtPkg and OvmfPkg.
|
---|
4 |
|
---|
5 | Copyright (C) 2016, Red Hat, Inc.
|
---|
6 | Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
|
---|
7 | Copyright (c) 2020, Huawei Corporation. All rights reserved.<BR>
|
---|
8 |
|
---|
9 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
10 |
|
---|
11 | **/
|
---|
12 |
|
---|
13 | #include <IndustryStandard/Acpi10.h>
|
---|
14 | #include <IndustryStandard/Pci.h>
|
---|
15 | #include <Library/BaseMemoryLib.h>
|
---|
16 | #include <Library/DebugLib.h>
|
---|
17 | #include <Library/DevicePathLib.h>
|
---|
18 | #include <Library/MemoryAllocationLib.h>
|
---|
19 | #include <Library/PciHostBridgeUtilityLib.h>
|
---|
20 | #include <Library/PciLib.h>
|
---|
21 | #include <Library/QemuFwCfgLib.h>
|
---|
22 |
|
---|
23 |
|
---|
24 | #pragma pack(1)
|
---|
25 | typedef struct {
|
---|
26 | ACPI_HID_DEVICE_PATH AcpiDevicePath;
|
---|
27 | EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
|
---|
28 | } OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH;
|
---|
29 | #pragma pack ()
|
---|
30 |
|
---|
31 |
|
---|
32 | GLOBAL_REMOVE_IF_UNREFERENCED
|
---|
33 | CHAR16 *mPciHostBridgeUtilityLibAcpiAddressSpaceTypeStr[] = {
|
---|
34 | L"Mem", L"I/O", L"Bus"
|
---|
35 | };
|
---|
36 |
|
---|
37 |
|
---|
38 | STATIC
|
---|
39 | CONST
|
---|
40 | OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH mRootBridgeDevicePathTemplate = {
|
---|
41 | {
|
---|
42 | {
|
---|
43 | ACPI_DEVICE_PATH,
|
---|
44 | ACPI_DP,
|
---|
45 | {
|
---|
46 | (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),
|
---|
47 | (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)
|
---|
48 | }
|
---|
49 | },
|
---|
50 | EISA_PNP_ID(0x0A03), // HID
|
---|
51 | 0 // UID
|
---|
52 | },
|
---|
53 |
|
---|
54 | {
|
---|
55 | END_DEVICE_PATH_TYPE,
|
---|
56 | END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
---|
57 | {
|
---|
58 | END_DEVICE_PATH_LENGTH,
|
---|
59 | 0
|
---|
60 | }
|
---|
61 | }
|
---|
62 | };
|
---|
63 |
|
---|
64 |
|
---|
65 | /**
|
---|
66 | Utility function to initialize a PCI_ROOT_BRIDGE structure.
|
---|
67 |
|
---|
68 | @param[in] Supports Supported attributes.
|
---|
69 |
|
---|
70 | @param[in] Attributes Initial attributes.
|
---|
71 |
|
---|
72 | @param[in] AllocAttributes Allocation attributes.
|
---|
73 |
|
---|
74 | @param[in] DmaAbove4G DMA above 4GB memory.
|
---|
75 |
|
---|
76 | @param[in] NoExtendedConfigSpace No Extended Config Space.
|
---|
77 |
|
---|
78 | @param[in] RootBusNumber The bus number to store in RootBus.
|
---|
79 |
|
---|
80 | @param[in] MaxSubBusNumber The inclusive maximum bus number that can
|
---|
81 | be assigned to any subordinate bus found
|
---|
82 | behind any PCI bridge hanging off this
|
---|
83 | root bus.
|
---|
84 |
|
---|
85 | The caller is repsonsible for ensuring
|
---|
86 | that RootBusNumber <= MaxSubBusNumber. If
|
---|
87 | RootBusNumber equals MaxSubBusNumber, then
|
---|
88 | the root bus has no room for subordinate
|
---|
89 | buses.
|
---|
90 |
|
---|
91 | @param[in] Io IO aperture.
|
---|
92 |
|
---|
93 | @param[in] Mem MMIO aperture.
|
---|
94 |
|
---|
95 | @param[in] MemAbove4G MMIO aperture above 4G.
|
---|
96 |
|
---|
97 | @param[in] PMem Prefetchable MMIO aperture.
|
---|
98 |
|
---|
99 | @param[in] PMemAbove4G Prefetchable MMIO aperture above 4G.
|
---|
100 |
|
---|
101 | @param[out] RootBus The PCI_ROOT_BRIDGE structure (allocated
|
---|
102 | by the caller) that should be filled in by
|
---|
103 | this function.
|
---|
104 |
|
---|
105 | @retval EFI_SUCCESS Initialization successful. A device path
|
---|
106 | consisting of an ACPI device path node,
|
---|
107 | with UID = RootBusNumber, has been
|
---|
108 | allocated and linked into RootBus.
|
---|
109 |
|
---|
110 | @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
|
---|
111 | **/
|
---|
112 | EFI_STATUS
|
---|
113 | EFIAPI
|
---|
114 | PciHostBridgeUtilityInitRootBridge (
|
---|
115 | IN UINT64 Supports,
|
---|
116 | IN UINT64 Attributes,
|
---|
117 | IN UINT64 AllocAttributes,
|
---|
118 | IN BOOLEAN DmaAbove4G,
|
---|
119 | IN BOOLEAN NoExtendedConfigSpace,
|
---|
120 | IN UINT8 RootBusNumber,
|
---|
121 | IN UINT8 MaxSubBusNumber,
|
---|
122 | IN PCI_ROOT_BRIDGE_APERTURE *Io,
|
---|
123 | IN PCI_ROOT_BRIDGE_APERTURE *Mem,
|
---|
124 | IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,
|
---|
125 | IN PCI_ROOT_BRIDGE_APERTURE *PMem,
|
---|
126 | IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,
|
---|
127 | OUT PCI_ROOT_BRIDGE *RootBus
|
---|
128 | )
|
---|
129 | {
|
---|
130 | OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH *DevicePath;
|
---|
131 |
|
---|
132 | //
|
---|
133 | // Be safe if other fields are added to PCI_ROOT_BRIDGE later.
|
---|
134 | //
|
---|
135 | ZeroMem (RootBus, sizeof *RootBus);
|
---|
136 |
|
---|
137 | RootBus->Segment = 0;
|
---|
138 |
|
---|
139 | RootBus->Supports = Supports;
|
---|
140 | RootBus->Attributes = Attributes;
|
---|
141 |
|
---|
142 | RootBus->DmaAbove4G = DmaAbove4G;
|
---|
143 |
|
---|
144 | RootBus->AllocationAttributes = AllocAttributes;
|
---|
145 | RootBus->Bus.Base = RootBusNumber;
|
---|
146 | RootBus->Bus.Limit = MaxSubBusNumber;
|
---|
147 | CopyMem (&RootBus->Io, Io, sizeof (*Io));
|
---|
148 | CopyMem (&RootBus->Mem, Mem, sizeof (*Mem));
|
---|
149 | CopyMem (&RootBus->MemAbove4G, MemAbove4G, sizeof (*MemAbove4G));
|
---|
150 | CopyMem (&RootBus->PMem, PMem, sizeof (*PMem));
|
---|
151 | CopyMem (&RootBus->PMemAbove4G, PMemAbove4G, sizeof (*PMemAbove4G));
|
---|
152 |
|
---|
153 | RootBus->NoExtendedConfigSpace = NoExtendedConfigSpace;
|
---|
154 |
|
---|
155 | DevicePath = AllocateCopyPool (sizeof mRootBridgeDevicePathTemplate,
|
---|
156 | &mRootBridgeDevicePathTemplate);
|
---|
157 | if (DevicePath == NULL) {
|
---|
158 | DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));
|
---|
159 | return EFI_OUT_OF_RESOURCES;
|
---|
160 | }
|
---|
161 | DevicePath->AcpiDevicePath.UID = RootBusNumber;
|
---|
162 | RootBus->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;
|
---|
163 |
|
---|
164 | DEBUG ((DEBUG_INFO,
|
---|
165 | "%a: populated root bus %d, with room for %d subordinate bus(es)\n",
|
---|
166 | __FUNCTION__, RootBusNumber, MaxSubBusNumber - RootBusNumber));
|
---|
167 | return EFI_SUCCESS;
|
---|
168 | }
|
---|
169 |
|
---|
170 |
|
---|
171 | /**
|
---|
172 | Utility function to uninitialize a PCI_ROOT_BRIDGE structure set up with
|
---|
173 | PciHostBridgeUtilityInitRootBridge().
|
---|
174 |
|
---|
175 | @param[in] RootBus The PCI_ROOT_BRIDGE structure, allocated by the caller and
|
---|
176 | initialized with PciHostBridgeUtilityInitRootBridge(),
|
---|
177 | that should be uninitialized. This function doesn't free
|
---|
178 | RootBus.
|
---|
179 | **/
|
---|
180 | VOID
|
---|
181 | EFIAPI
|
---|
182 | PciHostBridgeUtilityUninitRootBridge (
|
---|
183 | IN PCI_ROOT_BRIDGE *RootBus
|
---|
184 | )
|
---|
185 | {
|
---|
186 | FreePool (RootBus->DevicePath);
|
---|
187 | }
|
---|
188 |
|
---|
189 |
|
---|
190 | /**
|
---|
191 | Utility function to return all the root bridge instances in an array.
|
---|
192 |
|
---|
193 | @param[out] Count The number of root bridge instances.
|
---|
194 |
|
---|
195 | @param[in] Attributes Initial attributes.
|
---|
196 |
|
---|
197 | @param[in] AllocAttributes Allocation attributes.
|
---|
198 |
|
---|
199 | @param[in] DmaAbove4G DMA above 4GB memory.
|
---|
200 |
|
---|
201 | @param[in] NoExtendedConfigSpace No Extended Config Space.
|
---|
202 |
|
---|
203 | @param[in] BusMin Minimum Bus number, inclusive.
|
---|
204 |
|
---|
205 | @param[in] BusMax Maximum Bus number, inclusive.
|
---|
206 |
|
---|
207 | @param[in] Io IO aperture.
|
---|
208 |
|
---|
209 | @param[in] Mem MMIO aperture.
|
---|
210 |
|
---|
211 | @param[in] MemAbove4G MMIO aperture above 4G.
|
---|
212 |
|
---|
213 | @param[in] PMem Prefetchable MMIO aperture.
|
---|
214 |
|
---|
215 | @param[in] PMemAbove4G Prefetchable MMIO aperture above 4G.
|
---|
216 |
|
---|
217 | @return All the root bridge instances in an array.
|
---|
218 | **/
|
---|
219 | PCI_ROOT_BRIDGE *
|
---|
220 | EFIAPI
|
---|
221 | PciHostBridgeUtilityGetRootBridges (
|
---|
222 | OUT UINTN *Count,
|
---|
223 | IN UINT64 Attributes,
|
---|
224 | IN UINT64 AllocationAttributes,
|
---|
225 | IN BOOLEAN DmaAbove4G,
|
---|
226 | IN BOOLEAN NoExtendedConfigSpace,
|
---|
227 | IN UINTN BusMin,
|
---|
228 | IN UINTN BusMax,
|
---|
229 | IN PCI_ROOT_BRIDGE_APERTURE *Io,
|
---|
230 | IN PCI_ROOT_BRIDGE_APERTURE *Mem,
|
---|
231 | IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,
|
---|
232 | IN PCI_ROOT_BRIDGE_APERTURE *PMem,
|
---|
233 | IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G
|
---|
234 | )
|
---|
235 | {
|
---|
236 | EFI_STATUS Status;
|
---|
237 | FIRMWARE_CONFIG_ITEM FwCfgItem;
|
---|
238 | UINTN FwCfgSize;
|
---|
239 | UINT64 ExtraRootBridges;
|
---|
240 | PCI_ROOT_BRIDGE *Bridges;
|
---|
241 | UINTN Initialized;
|
---|
242 | UINTN LastRootBridgeNumber;
|
---|
243 | UINTN RootBridgeNumber;
|
---|
244 |
|
---|
245 | *Count = 0;
|
---|
246 |
|
---|
247 | if (BusMin > BusMax || BusMax > PCI_MAX_BUS) {
|
---|
248 | DEBUG ((DEBUG_ERROR, "%a: invalid bus range with BusMin %Lu and BusMax "
|
---|
249 | "%Lu\n", __FUNCTION__, (UINT64)BusMin, (UINT64)BusMax));
|
---|
250 | return NULL;
|
---|
251 | }
|
---|
252 |
|
---|
253 | //
|
---|
254 | // QEMU provides the number of extra root buses, shortening the exhaustive
|
---|
255 | // search below. If there is no hint, the feature is missing.
|
---|
256 | //
|
---|
257 | Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize);
|
---|
258 | if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) {
|
---|
259 | ExtraRootBridges = 0;
|
---|
260 | } else {
|
---|
261 | QemuFwCfgSelectItem (FwCfgItem);
|
---|
262 | QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges);
|
---|
263 |
|
---|
264 | //
|
---|
265 | // Validate the number of extra root bridges. As BusMax is inclusive, the
|
---|
266 | // max bus count is (BusMax - BusMin + 1). From that, the "main" root bus
|
---|
267 | // is always a given, so the max count for the "extra" root bridges is one
|
---|
268 | // less, i.e. (BusMax - BusMin). If the QEMU hint exceeds that, we have
|
---|
269 | // invalid behavior.
|
---|
270 | //
|
---|
271 | if (ExtraRootBridges > BusMax - BusMin) {
|
---|
272 | DEBUG ((DEBUG_ERROR, "%a: invalid count of extra root buses (%Lu) "
|
---|
273 | "reported by QEMU\n", __FUNCTION__, ExtraRootBridges));
|
---|
274 | return NULL;
|
---|
275 | }
|
---|
276 | DEBUG ((DEBUG_INFO, "%a: %Lu extra root buses reported by QEMU\n",
|
---|
277 | __FUNCTION__, ExtraRootBridges));
|
---|
278 | }
|
---|
279 |
|
---|
280 | //
|
---|
281 | // Allocate the "main" root bridge, and any extra root bridges.
|
---|
282 | //
|
---|
283 | Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges);
|
---|
284 | if (Bridges == NULL) {
|
---|
285 | DEBUG ((DEBUG_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));
|
---|
286 | return NULL;
|
---|
287 | }
|
---|
288 | Initialized = 0;
|
---|
289 |
|
---|
290 | //
|
---|
291 | // The "main" root bus is always there.
|
---|
292 | //
|
---|
293 | LastRootBridgeNumber = BusMin;
|
---|
294 |
|
---|
295 | //
|
---|
296 | // Scan all other root buses. If function 0 of any device on a bus returns a
|
---|
297 | // VendorId register value different from all-bits-one, then that bus is
|
---|
298 | // alive.
|
---|
299 | //
|
---|
300 | for (RootBridgeNumber = BusMin + 1;
|
---|
301 | RootBridgeNumber <= BusMax && Initialized < ExtraRootBridges;
|
---|
302 | ++RootBridgeNumber) {
|
---|
303 | UINTN Device;
|
---|
304 |
|
---|
305 | for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {
|
---|
306 | if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0,
|
---|
307 | PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) {
|
---|
308 | break;
|
---|
309 | }
|
---|
310 | }
|
---|
311 | if (Device <= PCI_MAX_DEVICE) {
|
---|
312 | //
|
---|
313 | // Found the next root bus. We can now install the *previous* one,
|
---|
314 | // because now we know how big a bus number range *that* one has, for any
|
---|
315 | // subordinate buses that might exist behind PCI bridges hanging off it.
|
---|
316 | //
|
---|
317 | Status = PciHostBridgeUtilityInitRootBridge (
|
---|
318 | Attributes,
|
---|
319 | Attributes,
|
---|
320 | AllocationAttributes,
|
---|
321 | DmaAbove4G,
|
---|
322 | NoExtendedConfigSpace,
|
---|
323 | (UINT8) LastRootBridgeNumber,
|
---|
324 | (UINT8) (RootBridgeNumber - 1),
|
---|
325 | Io,
|
---|
326 | Mem,
|
---|
327 | MemAbove4G,
|
---|
328 | PMem,
|
---|
329 | PMemAbove4G,
|
---|
330 | &Bridges[Initialized]
|
---|
331 | );
|
---|
332 | if (EFI_ERROR (Status)) {
|
---|
333 | goto FreeBridges;
|
---|
334 | }
|
---|
335 | ++Initialized;
|
---|
336 | LastRootBridgeNumber = RootBridgeNumber;
|
---|
337 | }
|
---|
338 | }
|
---|
339 |
|
---|
340 | //
|
---|
341 | // Install the last root bus (which might be the only, ie. main, root bus, if
|
---|
342 | // we've found no extra root buses).
|
---|
343 | //
|
---|
344 | Status = PciHostBridgeUtilityInitRootBridge (
|
---|
345 | Attributes,
|
---|
346 | Attributes,
|
---|
347 | AllocationAttributes,
|
---|
348 | DmaAbove4G,
|
---|
349 | NoExtendedConfigSpace,
|
---|
350 | (UINT8) LastRootBridgeNumber,
|
---|
351 | (UINT8) BusMax,
|
---|
352 | Io,
|
---|
353 | Mem,
|
---|
354 | MemAbove4G,
|
---|
355 | PMem,
|
---|
356 | PMemAbove4G,
|
---|
357 | &Bridges[Initialized]
|
---|
358 | );
|
---|
359 | if (EFI_ERROR (Status)) {
|
---|
360 | goto FreeBridges;
|
---|
361 | }
|
---|
362 | ++Initialized;
|
---|
363 |
|
---|
364 | *Count = Initialized;
|
---|
365 | return Bridges;
|
---|
366 |
|
---|
367 | FreeBridges:
|
---|
368 | while (Initialized > 0) {
|
---|
369 | --Initialized;
|
---|
370 | PciHostBridgeUtilityUninitRootBridge (&Bridges[Initialized]);
|
---|
371 | }
|
---|
372 |
|
---|
373 | FreePool (Bridges);
|
---|
374 | return NULL;
|
---|
375 | }
|
---|
376 |
|
---|
377 |
|
---|
378 | /**
|
---|
379 | Utility function to free root bridge instances array from
|
---|
380 | PciHostBridgeUtilityGetRootBridges().
|
---|
381 |
|
---|
382 | @param[in] Bridges The root bridge instances array.
|
---|
383 | @param[in] Count The count of the array.
|
---|
384 | **/
|
---|
385 | VOID
|
---|
386 | EFIAPI
|
---|
387 | PciHostBridgeUtilityFreeRootBridges (
|
---|
388 | IN PCI_ROOT_BRIDGE *Bridges,
|
---|
389 | IN UINTN Count
|
---|
390 | )
|
---|
391 | {
|
---|
392 | if (Bridges == NULL && Count == 0) {
|
---|
393 | return;
|
---|
394 | }
|
---|
395 | ASSERT (Bridges != NULL && Count > 0);
|
---|
396 |
|
---|
397 | do {
|
---|
398 | --Count;
|
---|
399 | PciHostBridgeUtilityUninitRootBridge (&Bridges[Count]);
|
---|
400 | } while (Count > 0);
|
---|
401 |
|
---|
402 | FreePool (Bridges);
|
---|
403 | }
|
---|
404 |
|
---|
405 |
|
---|
406 | /**
|
---|
407 | Utility function to inform the platform that the resource conflict happens.
|
---|
408 |
|
---|
409 | @param[in] Configuration Pointer to PCI I/O and PCI memory resource
|
---|
410 | descriptors. The Configuration contains the
|
---|
411 | resources for all the root bridges. The resource
|
---|
412 | for each root bridge is terminated with END
|
---|
413 | descriptor and an additional END is appended
|
---|
414 | indicating the end of the entire resources. The
|
---|
415 | resource descriptor field values follow the
|
---|
416 | description in
|
---|
417 | EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
|
---|
418 | .SubmitResources().
|
---|
419 | **/
|
---|
420 | VOID
|
---|
421 | EFIAPI
|
---|
422 | PciHostBridgeUtilityResourceConflict (
|
---|
423 | IN VOID *Configuration
|
---|
424 | )
|
---|
425 | {
|
---|
426 | EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
|
---|
427 | UINTN RootBridgeIndex;
|
---|
428 | DEBUG ((DEBUG_ERROR, "PciHostBridge: Resource conflict happens!\n"));
|
---|
429 |
|
---|
430 | RootBridgeIndex = 0;
|
---|
431 | Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;
|
---|
432 | while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {
|
---|
433 | DEBUG ((DEBUG_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));
|
---|
434 | for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
|
---|
435 | ASSERT (Descriptor->ResType <
|
---|
436 | ARRAY_SIZE (mPciHostBridgeUtilityLibAcpiAddressSpaceTypeStr)
|
---|
437 | );
|
---|
438 | DEBUG ((DEBUG_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",
|
---|
439 | mPciHostBridgeUtilityLibAcpiAddressSpaceTypeStr[Descriptor->ResType],
|
---|
440 | Descriptor->AddrLen, Descriptor->AddrRangeMax
|
---|
441 | ));
|
---|
442 | if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
|
---|
443 | DEBUG ((DEBUG_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",
|
---|
444 | Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,
|
---|
445 | ((Descriptor->SpecificFlag &
|
---|
446 | EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE
|
---|
447 | ) != 0) ? L" (Prefetchable)" : L""
|
---|
448 | ));
|
---|
449 | }
|
---|
450 | }
|
---|
451 | //
|
---|
452 | // Skip the END descriptor for root bridge
|
---|
453 | //
|
---|
454 | ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);
|
---|
455 | Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(
|
---|
456 | (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1
|
---|
457 | );
|
---|
458 | }
|
---|
459 | }
|
---|
460 |
|
---|