]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PciHostBridgeLib/PciHostBridgeLib.c
OvmfPkg: match PCI config access to machine type (if not USE_OLD_PCI_HOST)
[mirror_edk2.git] / OvmfPkg / Library / PciHostBridgeLib / PciHostBridgeLib.c
CommitLineData
d85861d7
LE
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
46e46eaf
LE
17\r
18#include <IndustryStandard/Pci.h>\r
19\r
0dcd1b1b 20#include <Protocol/PciHostBridgeResourceAllocation.h>\r
a5ece62d
LE
21#include <Protocol/PciRootBridgeIo.h>\r
22\r
65de2ef5 23#include <Library/BaseMemoryLib.h>\r
d85861d7 24#include <Library/DebugLib.h>\r
1f4e2299 25#include <Library/DevicePathLib.h>\r
46e46eaf
LE
26#include <Library/MemoryAllocationLib.h>\r
27#include <Library/PciHostBridgeLib.h>\r
28#include <Library/PciLib.h>\r
29#include <Library/QemuFwCfgLib.h>\r
30\r
d85861d7 31\r
1f4e2299
LE
32#pragma pack(1)\r
33typedef struct {\r
34 ACPI_HID_DEVICE_PATH AcpiDevicePath;\r
35 EFI_DEVICE_PATH_PROTOCOL EndDevicePath;\r
36} OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH;\r
37#pragma pack ()\r
38\r
39\r
d85861d7
LE
40GLOBAL_REMOVE_IF_UNREFERENCED\r
41CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {\r
42 L"Mem", L"I/O", L"Bus"\r
43};\r
44\r
46e46eaf 45\r
1f4e2299
LE
46STATIC\r
47CONST\r
48OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH mRootBridgeDevicePathTemplate = {\r
49 {\r
50 {\r
51 ACPI_DEVICE_PATH,\r
52 ACPI_DP,\r
53 {\r
54 (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),\r
55 (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)\r
56 }\r
57 },\r
58 EISA_PNP_ID(0x0A03), // HID\r
59 0 // UID\r
60 },\r
61\r
62 {\r
63 END_DEVICE_PATH_TYPE,\r
64 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
65 {\r
66 END_DEVICE_PATH_LENGTH,\r
67 0\r
68 }\r
69 }\r
70};\r
71\r
72\r
46e46eaf
LE
73/**\r
74 Initialize a PCI_ROOT_BRIDGE structure.\r
75\r
76 param[in] RootBusNumber The bus number to store in RootBus.\r
77\r
78 param[in] MaxSubBusNumber The inclusive maximum bus number that can be\r
79 assigned to any subordinate bus found behind any\r
80 PCI bridge hanging off this root bus.\r
81\r
82 The caller is repsonsible for ensuring that\r
83 RootBusNumber <= MaxSubBusNumber. If\r
84 RootBusNumber equals MaxSubBusNumber, then the\r
85 root bus has no room for subordinate buses.\r
86\r
87 param[out] RootBus The PCI_ROOT_BRIDGE structure (allocated by the\r
88 caller) that should be filled in by this\r
89 function.\r
90\r
91 @retval EFI_SUCCESS Initialization successful. A device path\r
92 consisting of an ACPI device path node, with\r
93 UID = RootBusNumber, has been allocated and\r
94 linked into RootBus.\r
95\r
96 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
97**/\r
98STATIC\r
99EFI_STATUS\r
100InitRootBridge (\r
101 IN UINT8 RootBusNumber,\r
102 IN UINT8 MaxSubBusNumber,\r
103 OUT PCI_ROOT_BRIDGE *RootBus\r
104 )\r
105{\r
1f4e2299
LE
106 OVMF_PCI_ROOT_BRIDGE_DEVICE_PATH *DevicePath;\r
107\r
65de2ef5
LE
108 //\r
109 // Be safe if other fields are added to PCI_ROOT_BRIDGE later.\r
110 //\r
111 ZeroMem (RootBus, sizeof *RootBus);\r
112\r
113 RootBus->Segment = 0;\r
114\r
a5ece62d
LE
115 RootBus->Supports = EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO |\r
116 EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO |\r
117 EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
118 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
119 EFI_PCI_ATTRIBUTE_VGA_MEMORY |\r
120 EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
121 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
122 RootBus->Attributes = RootBus->Supports;\r
123\r
c789d61d
LE
124 RootBus->DmaAbove4G = FALSE;\r
125\r
0dcd1b1b
LE
126 RootBus->AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
127 RootBus->PMem.Base = 0;\r
128 RootBus->PMem.Limit = 0;\r
129 RootBus->PMemAbove4G.Base = 0;\r
130 RootBus->PMemAbove4G.Limit = 0;\r
131 RootBus->MemAbove4G.Base = 0;\r
132 RootBus->MemAbove4G.Limit = 0;\r
133\r
390951c2
LE
134 RootBus->Bus.Base = RootBusNumber;\r
135 RootBus->Bus.Limit = MaxSubBusNumber;\r
136 RootBus->Io.Base = PcdGet64 (PcdPciIoBase);\r
137 RootBus->Io.Limit = PcdGet64 (PcdPciIoBase) + (PcdGet64 (PcdPciIoSize) - 1);\r
138 RootBus->Mem.Base = PcdGet64 (PcdPciMmio32Base);\r
139 RootBus->Mem.Limit = PcdGet64 (PcdPciMmio32Base) +\r
140 (PcdGet64 (PcdPciMmio32Size) - 1);\r
141\r
cb81595b
LE
142 RootBus->NoExtendedConfigSpace = TRUE;\r
143\r
1f4e2299
LE
144 DevicePath = AllocateCopyPool (sizeof mRootBridgeDevicePathTemplate,\r
145 &mRootBridgeDevicePathTemplate);\r
146 if (DevicePath == NULL) {\r
147 DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));\r
148 return EFI_OUT_OF_RESOURCES;\r
149 }\r
150 DevicePath->AcpiDevicePath.UID = RootBusNumber;\r
151 RootBus->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;\r
152\r
153 DEBUG ((EFI_D_INFO,\r
154 "%a: populated root bus %d, with room for %d subordinate bus(es)\n",\r
155 __FUNCTION__, RootBusNumber, MaxSubBusNumber - RootBusNumber));\r
156 return EFI_SUCCESS;\r
46e46eaf
LE
157}\r
158\r
159\r
160/**\r
161 Uninitialize a PCI_ROOT_BRIDGE structure set up with InitRootBridge().\r
162\r
163 param[in] RootBus The PCI_ROOT_BRIDGE structure, allocated by the caller and\r
164 initialized with InitRootBridge(), that should be\r
165 uninitialized. This function doesn't free RootBus.\r
166**/\r
167STATIC\r
168VOID\r
169UninitRootBridge (\r
170 IN PCI_ROOT_BRIDGE *RootBus\r
171 )\r
172{\r
1f4e2299 173 FreePool (RootBus->DevicePath);\r
46e46eaf
LE
174}\r
175\r
176\r
d85861d7
LE
177/**\r
178 Return all the root bridge instances in an array.\r
179\r
180 @param Count Return the count of root bridge instances.\r
181\r
182 @return All the root bridge instances in an array.\r
183 The array should be passed into PciHostBridgeFreeRootBridges()\r
184 when it's not used.\r
185**/\r
186PCI_ROOT_BRIDGE *\r
187EFIAPI\r
188PciHostBridgeGetRootBridges (\r
189 UINTN *Count\r
190 )\r
191{\r
46e46eaf
LE
192 EFI_STATUS Status;\r
193 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
194 UINTN FwCfgSize;\r
195 UINT64 ExtraRootBridges;\r
196 PCI_ROOT_BRIDGE *Bridges;\r
197 UINTN Initialized;\r
198 UINTN LastRootBridgeNumber;\r
199 UINTN RootBridgeNumber;\r
200\r
d85861d7 201 *Count = 0;\r
46e46eaf
LE
202\r
203 //\r
204 // QEMU provides the number of extra root buses, shortening the exhaustive\r
205 // search below. If there is no hint, the feature is missing.\r
206 //\r
207 Status = QemuFwCfgFindFile ("etc/extra-pci-roots", &FwCfgItem, &FwCfgSize);\r
208 if (EFI_ERROR (Status) || FwCfgSize != sizeof ExtraRootBridges) {\r
209 ExtraRootBridges = 0;\r
210 } else {\r
211 QemuFwCfgSelectItem (FwCfgItem);\r
212 QemuFwCfgReadBytes (FwCfgSize, &ExtraRootBridges);\r
213\r
214 if (ExtraRootBridges > PCI_MAX_BUS) {\r
215 DEBUG ((EFI_D_ERROR, "%a: invalid count of extra root buses (%Lu) "\r
216 "reported by QEMU\n", __FUNCTION__, ExtraRootBridges));\r
217 return NULL;\r
218 }\r
219 DEBUG ((EFI_D_INFO, "%a: %Lu extra root buses reported by QEMU\n",\r
220 __FUNCTION__, ExtraRootBridges));\r
221 }\r
222\r
223 //\r
224 // Allocate the "main" root bridge, and any extra root bridges.\r
225 //\r
226 Bridges = AllocatePool ((1 + (UINTN)ExtraRootBridges) * sizeof *Bridges);\r
227 if (Bridges == NULL) {\r
228 DEBUG ((EFI_D_ERROR, "%a: %r\n", __FUNCTION__, EFI_OUT_OF_RESOURCES));\r
229 return NULL;\r
230 }\r
231 Initialized = 0;\r
232\r
233 //\r
234 // The "main" root bus is always there.\r
235 //\r
236 LastRootBridgeNumber = 0;\r
237\r
238 //\r
239 // Scan all other root buses. If function 0 of any device on a bus returns a\r
240 // VendorId register value different from all-bits-one, then that bus is\r
241 // alive.\r
242 //\r
243 for (RootBridgeNumber = 1;\r
244 RootBridgeNumber <= PCI_MAX_BUS && Initialized < ExtraRootBridges;\r
245 ++RootBridgeNumber) {\r
246 UINTN Device;\r
247\r
248 for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {\r
249 if (PciRead16 (PCI_LIB_ADDRESS (RootBridgeNumber, Device, 0,\r
250 PCI_VENDOR_ID_OFFSET)) != MAX_UINT16) {\r
251 break;\r
252 }\r
253 }\r
254 if (Device <= PCI_MAX_DEVICE) {\r
255 //\r
256 // Found the next root bus. We can now install the *previous* one,\r
257 // because now we know how big a bus number range *that* one has, for any\r
258 // subordinate buses that might exist behind PCI bridges hanging off it.\r
259 //\r
260 Status = InitRootBridge ((UINT8)LastRootBridgeNumber,\r
261 (UINT8)(RootBridgeNumber - 1), &Bridges[Initialized]);\r
262 if (EFI_ERROR (Status)) {\r
263 goto FreeBridges;\r
264 }\r
265 ++Initialized;\r
266 LastRootBridgeNumber = RootBridgeNumber;\r
267 }\r
268 }\r
269\r
270 //\r
271 // Install the last root bus (which might be the only, ie. main, root bus, if\r
272 // we've found no extra root buses).\r
273 //\r
274 Status = InitRootBridge ((UINT8)LastRootBridgeNumber, PCI_MAX_BUS,\r
275 &Bridges[Initialized]);\r
276 if (EFI_ERROR (Status)) {\r
277 goto FreeBridges;\r
278 }\r
279 ++Initialized;\r
280\r
281 *Count = Initialized;\r
282 return Bridges;\r
283\r
284FreeBridges:\r
285 while (Initialized > 0) {\r
286 --Initialized;\r
287 UninitRootBridge (&Bridges[Initialized]);\r
288 }\r
289\r
290 FreePool (Bridges);\r
d85861d7
LE
291 return NULL;\r
292}\r
293\r
46e46eaf 294\r
d85861d7
LE
295/**\r
296 Free the root bridge instances array returned from\r
297 PciHostBridgeGetRootBridges().\r
298\r
299 @param The root bridge instances array.\r
300 @param The count of the array.\r
301**/\r
302VOID\r
303EFIAPI\r
304PciHostBridgeFreeRootBridges (\r
305 PCI_ROOT_BRIDGE *Bridges,\r
306 UINTN Count\r
307 )\r
308{\r
dc4d6467
LE
309 if (Bridges == NULL && Count == 0) {\r
310 return;\r
311 }\r
312 ASSERT (Bridges != NULL && Count > 0);\r
313\r
314 do {\r
315 --Count;\r
316 UninitRootBridge (&Bridges[Count]);\r
317 } while (Count > 0);\r
318\r
319 FreePool (Bridges);\r
d85861d7
LE
320}\r
321\r
46e46eaf 322\r
d85861d7
LE
323/**\r
324 Inform the platform that the resource conflict happens.\r
325\r
326 @param HostBridgeHandle Handle of the Host Bridge.\r
327 @param Configuration Pointer to PCI I/O and PCI memory resource\r
328 descriptors. The Configuration contains the resources\r
329 for all the root bridges. The resource for each root\r
330 bridge is terminated with END descriptor and an\r
331 additional END is appended indicating the end of the\r
332 entire resources. The resource descriptor field\r
333 values follow the description in\r
334 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL\r
335 .SubmitResources().\r
336**/\r
337VOID\r
338EFIAPI\r
339PciHostBridgeResourceConflict (\r
340 EFI_HANDLE HostBridgeHandle,\r
341 VOID *Configuration\r
342 )\r
343{\r
344 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
345 UINTN RootBridgeIndex;\r
346 DEBUG ((EFI_D_ERROR, "PciHostBridge: Resource conflict happens!\n"));\r
347\r
348 RootBridgeIndex = 0;\r
349 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;\r
350 while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {\r
351 DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));\r
352 for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {\r
353 ASSERT (Descriptor->ResType <\r
354 (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /\r
355 sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])\r
356 )\r
357 );\r
358 DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",\r
359 mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],\r
360 Descriptor->AddrLen, Descriptor->AddrRangeMax\r
361 ));\r
362 if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {\r
363 DEBUG ((EFI_D_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",\r
364 Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,\r
365 ((Descriptor->SpecificFlag &\r
366 EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE\r
367 ) != 0) ? L" (Prefetchable)" : L""\r
368 ));\r
369 }\r
370 }\r
371 //\r
372 // Skip the END descriptor for root bridge\r
373 //\r
374 ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);\r
375 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(\r
376 (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1\r
377 );\r
378 }\r
379}\r