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