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