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