]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
OvmfPkg/Csm/VideoDxe: Update to make it build for OVMF
[mirror_edk2.git] / ArmVirtPkg / Library / FdtPciHostBridgeLib / FdtPciHostBridgeLib.c
CommitLineData
d4cb9a30
AB
1/** @file\r
2 PCI Host Bridge Library instance for pci-ecam-generic DT nodes\r
3\r
4 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>\r
5\r
9792fb0e 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
d4cb9a30
AB
7\r
8**/\r
9#include <PiDxe.h>\r
10#include <Library/PciHostBridgeLib.h>\r
11#include <Library/DebugLib.h>\r
12#include <Library/DevicePathLib.h>\r
66e06a72 13#include <Library/DxeServicesTableLib.h>\r
d4cb9a30
AB
14#include <Library/MemoryAllocationLib.h>\r
15#include <Library/PcdLib.h>\r
16#include <Library/UefiBootServicesTableLib.h>\r
17\r
18#include <Protocol/FdtClient.h>\r
19#include <Protocol/PciRootBridgeIo.h>\r
20#include <Protocol/PciHostBridgeResourceAllocation.h>\r
21\r
22#pragma pack(1)\r
23typedef struct {\r
24 ACPI_HID_DEVICE_PATH AcpiDevicePath;\r
25 EFI_DEVICE_PATH_PROTOCOL EndDevicePath;\r
26} EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;\r
27#pragma pack ()\r
28\r
29STATIC EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mEfiPciRootBridgeDevicePath = {\r
30 {\r
31 {\r
32 ACPI_DEVICE_PATH,\r
33 ACPI_DP,\r
34 {\r
35 (UINT8) (sizeof(ACPI_HID_DEVICE_PATH)),\r
36 (UINT8) ((sizeof(ACPI_HID_DEVICE_PATH)) >> 8)\r
37 }\r
38 },\r
39 EISA_PNP_ID(0x0A03),\r
40 0\r
41 },\r
42\r
43 {\r
44 END_DEVICE_PATH_TYPE,\r
45 END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
46 {\r
47 END_DEVICE_PATH_LENGTH,\r
48 0\r
49 }\r
50 }\r
51};\r
52\r
53GLOBAL_REMOVE_IF_UNREFERENCED\r
54CHAR16 *mPciHostBridgeLibAcpiAddressSpaceTypeStr[] = {\r
55 L"Mem", L"I/O", L"Bus"\r
56};\r
57\r
58//\r
59// We expect the "ranges" property of "pci-host-ecam-generic" to consist of\r
60// records like this.\r
61//\r
62#pragma pack (1)\r
63typedef struct {\r
64 UINT32 Type;\r
65 UINT64 ChildBase;\r
66 UINT64 CpuBase;\r
67 UINT64 Size;\r
68} DTB_PCI_HOST_RANGE_RECORD;\r
69#pragma pack ()\r
70\r
71#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31\r
72#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30\r
73#define DTB_PCI_HOST_RANGE_ALIASED BIT29\r
74#define DTB_PCI_HOST_RANGE_MMIO32 BIT25\r
75#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)\r
76#define DTB_PCI_HOST_RANGE_IO BIT24\r
77#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)\r
78\r
66e06a72
AB
79STATIC\r
80EFI_STATUS\r
81MapGcdMmioSpace (\r
82 IN UINT64 Base,\r
83 IN UINT64 Size\r
84 )\r
85{\r
86 EFI_STATUS Status;\r
87\r
88 Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, Base, Size,\r
89 EFI_MEMORY_UC);\r
90 if (EFI_ERROR (Status)) {\r
91 DEBUG ((DEBUG_ERROR,\r
92 "%a: failed to add GCD memory space for region [0x%Lx+0x%Lx)\n",\r
93 __FUNCTION__, Base, Size));\r
94 return Status;\r
95 }\r
96\r
97 Status = gDS->SetMemorySpaceAttributes (Base, Size, EFI_MEMORY_UC);\r
98 if (EFI_ERROR (Status)) {\r
99 DEBUG ((DEBUG_ERROR,\r
100 "%a: failed to set memory space attributes for region [0x%Lx+0x%Lx)\n",\r
101 __FUNCTION__, Base, Size));\r
102 }\r
103 return Status;\r
104}\r
105\r
d4cb9a30
AB
106STATIC\r
107EFI_STATUS\r
108ProcessPciHost (\r
109 OUT UINT64 *IoBase,\r
110 OUT UINT64 *IoSize,\r
9d64ac23
AB
111 OUT UINT64 *Mmio32Base,\r
112 OUT UINT64 *Mmio32Size,\r
113 OUT UINT64 *Mmio64Base,\r
114 OUT UINT64 *Mmio64Size,\r
d4cb9a30
AB
115 OUT UINT32 *BusMin,\r
116 OUT UINT32 *BusMax\r
117 )\r
118{\r
119 FDT_CLIENT_PROTOCOL *FdtClient;\r
120 INT32 Node;\r
121 UINT64 ConfigBase, ConfigSize;\r
122 CONST VOID *Prop;\r
123 UINT32 Len;\r
124 UINT32 RecordIdx;\r
125 EFI_STATUS Status;\r
126 UINT64 IoTranslation;\r
9d64ac23
AB
127 UINT64 Mmio32Translation;\r
128 UINT64 Mmio64Translation;\r
d4cb9a30
AB
129\r
130 //\r
131 // The following output arguments are initialized only in\r
132 // order to suppress '-Werror=maybe-uninitialized' warnings\r
133 // *incorrectly* emitted by some gcc versions.\r
134 //\r
135 *IoBase = 0;\r
9d64ac23
AB
136 *Mmio32Base = 0;\r
137 *Mmio64Base = MAX_UINT64;\r
d4cb9a30
AB
138 *BusMin = 0;\r
139 *BusMax = 0;\r
140\r
141 //\r
9d64ac23 142 // *IoSize, *Mmio##Size and IoTranslation are initialized to zero because the\r
d4cb9a30
AB
143 // logic below requires it. However, since they are also affected by the issue\r
144 // reported above, they are initialized early.\r
145 //\r
146 *IoSize = 0;\r
9d64ac23
AB
147 *Mmio32Size = 0;\r
148 *Mmio64Size = 0;\r
d4cb9a30
AB
149 IoTranslation = 0;\r
150\r
151 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
152 (VOID **)&FdtClient);\r
153 ASSERT_EFI_ERROR (Status);\r
154\r
155 Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",\r
156 &Node);\r
157 if (EFI_ERROR (Status)) {\r
158 DEBUG ((EFI_D_INFO,\r
159 "%a: No 'pci-host-ecam-generic' compatible DT node found\n",\r
160 __FUNCTION__));\r
161 return EFI_NOT_FOUND;\r
162 }\r
163\r
164 DEBUG_CODE (\r
165 INT32 Tmp;\r
166\r
167 //\r
168 // A DT can legally describe multiple PCI host bridges, but we are not\r
169 // equipped to deal with that. So assert that there is only one.\r
170 //\r
171 Status = FdtClient->FindNextCompatibleNode (FdtClient,\r
172 "pci-host-ecam-generic", Node, &Tmp);\r
173 ASSERT (Status == EFI_NOT_FOUND);\r
174 );\r
175\r
176 Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);\r
177 if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {\r
178 DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",\r
179 __FUNCTION__));\r
180 return EFI_PROTOCOL_ERROR;\r
181 }\r
182\r
183 //\r
184 // Fetch the ECAM window.\r
185 //\r
186 ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);\r
187 ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);\r
188\r
189 //\r
190 // Fetch the bus range (note: inclusive).\r
191 //\r
192 Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,\r
193 &Len);\r
194 if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {\r
195 DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",\r
196 __FUNCTION__));\r
197 return EFI_PROTOCOL_ERROR;\r
198 }\r
199 *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);\r
200 *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);\r
201\r
202 //\r
203 // Sanity check: the config space must accommodate all 4K register bytes of\r
204 // all 8 functions of all 32 devices of all buses.\r
205 //\r
206 if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||\r
207 DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {\r
208 DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",\r
209 __FUNCTION__));\r
210 return EFI_PROTOCOL_ERROR;\r
211 }\r
212\r
213 //\r
214 // Iterate over "ranges".\r
215 //\r
216 Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);\r
217 if (EFI_ERROR (Status) || Len == 0 ||\r
218 Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {\r
219 DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
220 return EFI_PROTOCOL_ERROR;\r
221 }\r
222\r
223 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
224 ++RecordIdx) {\r
225 CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
226\r
227 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
228 switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
229 case DTB_PCI_HOST_RANGE_IO:\r
230 *IoBase = SwapBytes64 (Record->ChildBase);\r
231 *IoSize = SwapBytes64 (Record->Size);\r
232 IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;\r
233\r
234 ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);\r
235 break;\r
236\r
237 case DTB_PCI_HOST_RANGE_MMIO32:\r
9d64ac23
AB
238 *Mmio32Base = SwapBytes64 (Record->ChildBase);\r
239 *Mmio32Size = SwapBytes64 (Record->Size);\r
240 Mmio32Translation = SwapBytes64 (Record->CpuBase) - *Mmio32Base;\r
d4cb9a30 241\r
9d64ac23
AB
242 if (*Mmio32Base > MAX_UINT32 || *Mmio32Size > MAX_UINT32 ||\r
243 *Mmio32Base + *Mmio32Size > SIZE_4GB) {\r
d4cb9a30
AB
244 DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
245 return EFI_PROTOCOL_ERROR;\r
246 }\r
247\r
9d64ac23 248 ASSERT (PcdGet64 (PcdPciMmio32Translation) == Mmio32Translation);\r
d4cb9a30 249\r
9d64ac23 250 if (Mmio32Translation != 0) {\r
d4cb9a30 251 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "\r
9d64ac23
AB
252 "0x%Lx\n", __FUNCTION__, Mmio32Translation));\r
253 return EFI_UNSUPPORTED;\r
254 }\r
255\r
256 break;\r
257\r
258 case DTB_PCI_HOST_RANGE_MMIO64:\r
259 *Mmio64Base = SwapBytes64 (Record->ChildBase);\r
260 *Mmio64Size = SwapBytes64 (Record->Size);\r
261 Mmio64Translation = SwapBytes64 (Record->CpuBase) - *Mmio64Base;\r
262\r
263 ASSERT (PcdGet64 (PcdPciMmio64Translation) == Mmio64Translation);\r
264\r
265 if (Mmio64Translation != 0) {\r
266 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO64 translation "\r
267 "0x%Lx\n", __FUNCTION__, Mmio64Translation));\r
d4cb9a30
AB
268 return EFI_UNSUPPORTED;\r
269 }\r
270\r
271 break;\r
272 }\r
273 }\r
9d64ac23 274 if (*IoSize == 0 || *Mmio32Size == 0) {\r
d4cb9a30
AB
275 DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,\r
276 (*IoSize == 0) ? "IO" : "MMIO32"));\r
277 return EFI_PROTOCOL_ERROR;\r
278 }\r
279\r
280 //\r
281 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,\r
282 // and should match the value we found in the DT node.\r
283 //\r
284 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);\r
285\r
286 DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
9d64ac23
AB
287 "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",\r
288 __FUNCTION__, ConfigBase, ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize,\r
289 IoTranslation, *Mmio32Base, *Mmio32Size, *Mmio64Base, *Mmio64Size));\r
66e06a72
AB
290\r
291 // Map the ECAM space in the GCD memory map\r
292 Status = MapGcdMmioSpace (ConfigBase, ConfigSize);\r
293 ASSERT_EFI_ERROR (Status);\r
294 if (EFI_ERROR (Status)) {\r
295 return Status;\r
296 }\r
297\r
298 //\r
299 // Map the MMIO window that provides I/O access - the PCI host bridge code\r
300 // is not aware of this translation and so it will only map the I/O view\r
301 // in the GCD I/O map.\r
302 //\r
303 Status = MapGcdMmioSpace (*IoBase + IoTranslation, *IoSize);\r
304 ASSERT_EFI_ERROR (Status);\r
305\r
306 return Status;\r
d4cb9a30
AB
307}\r
308\r
309STATIC PCI_ROOT_BRIDGE mRootBridge;\r
310\r
311/**\r
312 Return all the root bridge instances in an array.\r
313\r
314 @param Count Return the count of root bridge instances.\r
315\r
316 @return All the root bridge instances in an array.\r
317 The array should be passed into PciHostBridgeFreeRootBridges()\r
318 when it's not used.\r
319**/\r
320PCI_ROOT_BRIDGE *\r
321EFIAPI\r
322PciHostBridgeGetRootBridges (\r
323 UINTN *Count\r
324 )\r
325{\r
326 UINT64 IoBase, IoSize;\r
327 UINT64 Mmio32Base, Mmio32Size;\r
9d64ac23 328 UINT64 Mmio64Base, Mmio64Size;\r
d4cb9a30
AB
329 UINT32 BusMin, BusMax;\r
330 EFI_STATUS Status;\r
331\r
332 if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
333 DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
334\r
335 *Count = 0;\r
336 return NULL;\r
337 }\r
338\r
9d64ac23
AB
339 Status = ProcessPciHost (&IoBase, &IoSize, &Mmio32Base, &Mmio32Size,\r
340 &Mmio64Base, &Mmio64Size, &BusMin, &BusMax);\r
d4cb9a30
AB
341 if (EFI_ERROR (Status)) {\r
342 DEBUG ((EFI_D_ERROR, "%a: failed to discover PCI host bridge: %r\n",\r
343 __FUNCTION__, Status));\r
344 *Count = 0;\r
345 return NULL;\r
346 }\r
347\r
348 *Count = 1;\r
349\r
350 mRootBridge.Segment = 0;\r
351 mRootBridge.Supports = EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
352 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
353 EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
354 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
355 mRootBridge.Attributes = mRootBridge.Supports;\r
356\r
4c0b2d25 357 mRootBridge.DmaAbove4G = TRUE;\r
d4cb9a30
AB
358 mRootBridge.NoExtendedConfigSpace = FALSE;\r
359 mRootBridge.ResourceAssigned = FALSE;\r
360\r
361 mRootBridge.AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
362\r
363 mRootBridge.Bus.Base = BusMin;\r
364 mRootBridge.Bus.Limit = BusMax;\r
365 mRootBridge.Io.Base = IoBase;\r
366 mRootBridge.Io.Limit = IoBase + IoSize - 1;\r
367 mRootBridge.Mem.Base = Mmio32Base;\r
368 mRootBridge.Mem.Limit = Mmio32Base + Mmio32Size - 1;\r
9d64ac23
AB
369\r
370 if (sizeof (UINTN) == sizeof (UINT64)) {\r
371 mRootBridge.MemAbove4G.Base = Mmio64Base;\r
372 mRootBridge.MemAbove4G.Limit = Mmio64Base + Mmio64Size - 1;\r
373 if (Mmio64Size > 0) {\r
374 mRootBridge.AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;\r
375 }\r
376 } else {\r
377 //\r
378 // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit\r
379 // architecture such as ARM, we will not be able to access 64-bit MMIO\r
380 // BARs unless they are allocated below 4 GB. So ignore the range above\r
381 // 4 GB in this case.\r
382 //\r
383 mRootBridge.MemAbove4G.Base = MAX_UINT64;\r
384 mRootBridge.MemAbove4G.Limit = 0;\r
385 }\r
d4cb9a30
AB
386\r
387 //\r
388 // No separate ranges for prefetchable and non-prefetchable BARs\r
389 //\r
390 mRootBridge.PMem.Base = MAX_UINT64;\r
391 mRootBridge.PMem.Limit = 0;\r
392 mRootBridge.PMemAbove4G.Base = MAX_UINT64;\r
393 mRootBridge.PMemAbove4G.Limit = 0;\r
394\r
395 mRootBridge.DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath;\r
396\r
397 return &mRootBridge;\r
398}\r
399\r
400/**\r
401 Free the root bridge instances array returned from\r
402 PciHostBridgeGetRootBridges().\r
403\r
404 @param Bridges The root bridge instances array.\r
405 @param Count The count of the array.\r
406**/\r
407VOID\r
408EFIAPI\r
409PciHostBridgeFreeRootBridges (\r
410 PCI_ROOT_BRIDGE *Bridges,\r
411 UINTN Count\r
412 )\r
413{\r
414 ASSERT (Count == 1);\r
415}\r
416\r
417/**\r
418 Inform the platform that the resource conflict happens.\r
419\r
420 @param HostBridgeHandle Handle of the Host Bridge.\r
421 @param Configuration Pointer to PCI I/O and PCI memory resource\r
422 descriptors. The Configuration contains the resources\r
423 for all the root bridges. The resource for each root\r
424 bridge is terminated with END descriptor and an\r
425 additional END is appended indicating the end of the\r
426 entire resources. The resource descriptor field\r
427 values follow the description in\r
428 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL\r
429 .SubmitResources().\r
430**/\r
431VOID\r
432EFIAPI\r
433PciHostBridgeResourceConflict (\r
434 EFI_HANDLE HostBridgeHandle,\r
435 VOID *Configuration\r
436 )\r
437{\r
438 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
439 UINTN RootBridgeIndex;\r
440 DEBUG ((EFI_D_ERROR, "PciHostBridge: Resource conflict happens!\n"));\r
441\r
442 RootBridgeIndex = 0;\r
443 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;\r
444 while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {\r
445 DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));\r
446 for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {\r
447 ASSERT (Descriptor->ResType <\r
448 (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /\r
449 sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])\r
450 )\r
451 );\r
452 DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",\r
453 mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],\r
454 Descriptor->AddrLen, Descriptor->AddrRangeMax\r
455 ));\r
456 if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {\r
457 DEBUG ((EFI_D_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",\r
458 Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,\r
459 ((Descriptor->SpecificFlag &\r
460 EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE\r
461 ) != 0) ? L" (Prefetchable)" : L""\r
462 ));\r
463 }\r
464 }\r
465 //\r
466 // Skip the END descriptor for root bridge\r
467 //\r
468 ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);\r
469 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(\r
470 (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1\r
471 );\r
472 }\r
473}\r