]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c
ArmVirtPkg/ArmVirtQemu: switch to generic PciHostBridgeDxe
[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
90 OUT UINT64 *MmioBase,\r
91 OUT UINT64 *MmioSize,\r
92 OUT UINT32 *BusMin,\r
93 OUT UINT32 *BusMax\r
94 )\r
95{\r
96 FDT_CLIENT_PROTOCOL *FdtClient;\r
97 INT32 Node;\r
98 UINT64 ConfigBase, ConfigSize;\r
99 CONST VOID *Prop;\r
100 UINT32 Len;\r
101 UINT32 RecordIdx;\r
102 EFI_STATUS Status;\r
103 UINT64 IoTranslation;\r
104 UINT64 MmioTranslation;\r
105\r
106 //\r
107 // The following output arguments are initialized only in\r
108 // order to suppress '-Werror=maybe-uninitialized' warnings\r
109 // *incorrectly* emitted by some gcc versions.\r
110 //\r
111 *IoBase = 0;\r
112 *MmioBase = 0;\r
113 *BusMin = 0;\r
114 *BusMax = 0;\r
115\r
116 //\r
117 // *IoSize, *MmioSize and IoTranslation are initialized to zero because the\r
118 // logic below requires it. However, since they are also affected by the issue\r
119 // reported above, they are initialized early.\r
120 //\r
121 *IoSize = 0;\r
122 *MmioSize = 0;\r
123 IoTranslation = 0;\r
124\r
125 Status = gBS->LocateProtocol (&gFdtClientProtocolGuid, NULL,\r
126 (VOID **)&FdtClient);\r
127 ASSERT_EFI_ERROR (Status);\r
128\r
129 Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",\r
130 &Node);\r
131 if (EFI_ERROR (Status)) {\r
132 DEBUG ((EFI_D_INFO,\r
133 "%a: No 'pci-host-ecam-generic' compatible DT node found\n",\r
134 __FUNCTION__));\r
135 return EFI_NOT_FOUND;\r
136 }\r
137\r
138 DEBUG_CODE (\r
139 INT32 Tmp;\r
140\r
141 //\r
142 // A DT can legally describe multiple PCI host bridges, but we are not\r
143 // equipped to deal with that. So assert that there is only one.\r
144 //\r
145 Status = FdtClient->FindNextCompatibleNode (FdtClient,\r
146 "pci-host-ecam-generic", Node, &Tmp);\r
147 ASSERT (Status == EFI_NOT_FOUND);\r
148 );\r
149\r
150 Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);\r
151 if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {\r
152 DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",\r
153 __FUNCTION__));\r
154 return EFI_PROTOCOL_ERROR;\r
155 }\r
156\r
157 //\r
158 // Fetch the ECAM window.\r
159 //\r
160 ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);\r
161 ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);\r
162\r
163 //\r
164 // Fetch the bus range (note: inclusive).\r
165 //\r
166 Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,\r
167 &Len);\r
168 if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {\r
169 DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",\r
170 __FUNCTION__));\r
171 return EFI_PROTOCOL_ERROR;\r
172 }\r
173 *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);\r
174 *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);\r
175\r
176 //\r
177 // Sanity check: the config space must accommodate all 4K register bytes of\r
178 // all 8 functions of all 32 devices of all buses.\r
179 //\r
180 if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||\r
181 DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {\r
182 DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",\r
183 __FUNCTION__));\r
184 return EFI_PROTOCOL_ERROR;\r
185 }\r
186\r
187 //\r
188 // Iterate over "ranges".\r
189 //\r
190 Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);\r
191 if (EFI_ERROR (Status) || Len == 0 ||\r
192 Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {\r
193 DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
194 return EFI_PROTOCOL_ERROR;\r
195 }\r
196\r
197 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
198 ++RecordIdx) {\r
199 CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
200\r
201 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
202 switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
203 case DTB_PCI_HOST_RANGE_IO:\r
204 *IoBase = SwapBytes64 (Record->ChildBase);\r
205 *IoSize = SwapBytes64 (Record->Size);\r
206 IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;\r
207\r
208 ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);\r
209 break;\r
210\r
211 case DTB_PCI_HOST_RANGE_MMIO32:\r
212 *MmioBase = SwapBytes64 (Record->ChildBase);\r
213 *MmioSize = SwapBytes64 (Record->Size);\r
214 MmioTranslation = SwapBytes64 (Record->CpuBase) - *MmioBase;\r
215\r
216 if (*MmioBase > MAX_UINT32 || *MmioSize > MAX_UINT32 ||\r
217 *MmioBase + *MmioSize > SIZE_4GB) {\r
218 DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
219 return EFI_PROTOCOL_ERROR;\r
220 }\r
221\r
222 ASSERT (PcdGet64 (PcdPciMmio32Translation) == MmioTranslation);\r
223\r
224 if (MmioTranslation != 0) {\r
225 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "\r
226 "0x%Lx\n", __FUNCTION__, MmioTranslation));\r
227 return EFI_UNSUPPORTED;\r
228 }\r
229\r
230 break;\r
231 }\r
232 }\r
233 if (*IoSize == 0 || *MmioSize == 0) {\r
234 DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,\r
235 (*IoSize == 0) ? "IO" : "MMIO32"));\r
236 return EFI_PROTOCOL_ERROR;\r
237 }\r
238\r
239 //\r
240 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,\r
241 // and should match the value we found in the DT node.\r
242 //\r
243 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);\r
244\r
245 DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
246 "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x0\n", __FUNCTION__, ConfigBase,\r
247 ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize, IoTranslation, *MmioBase,\r
248 *MmioSize));\r
249 return EFI_SUCCESS;\r
250}\r
251\r
252STATIC PCI_ROOT_BRIDGE mRootBridge;\r
253\r
254/**\r
255 Return all the root bridge instances in an array.\r
256\r
257 @param Count Return the count of root bridge instances.\r
258\r
259 @return All the root bridge instances in an array.\r
260 The array should be passed into PciHostBridgeFreeRootBridges()\r
261 when it's not used.\r
262**/\r
263PCI_ROOT_BRIDGE *\r
264EFIAPI\r
265PciHostBridgeGetRootBridges (\r
266 UINTN *Count\r
267 )\r
268{\r
269 UINT64 IoBase, IoSize;\r
270 UINT64 Mmio32Base, Mmio32Size;\r
271 UINT32 BusMin, BusMax;\r
272 EFI_STATUS Status;\r
273\r
274 if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {\r
275 DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));\r
276\r
277 *Count = 0;\r
278 return NULL;\r
279 }\r
280\r
281 Status = ProcessPciHost (&IoBase, &IoSize, &Mmio32Base, &Mmio32Size, &BusMin,\r
282 &BusMax);\r
283 if (EFI_ERROR (Status)) {\r
284 DEBUG ((EFI_D_ERROR, "%a: failed to discover PCI host bridge: %r\n",\r
285 __FUNCTION__, Status));\r
286 *Count = 0;\r
287 return NULL;\r
288 }\r
289\r
290 *Count = 1;\r
291\r
292 mRootBridge.Segment = 0;\r
293 mRootBridge.Supports = EFI_PCI_ATTRIBUTE_ISA_IO_16 |\r
294 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |\r
295 EFI_PCI_ATTRIBUTE_VGA_IO_16 |\r
296 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;\r
297 mRootBridge.Attributes = mRootBridge.Supports;\r
298\r
299 mRootBridge.DmaAbove4G = FALSE;\r
300 mRootBridge.NoExtendedConfigSpace = FALSE;\r
301 mRootBridge.ResourceAssigned = FALSE;\r
302\r
303 mRootBridge.AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;\r
304\r
305 mRootBridge.Bus.Base = BusMin;\r
306 mRootBridge.Bus.Limit = BusMax;\r
307 mRootBridge.Io.Base = IoBase;\r
308 mRootBridge.Io.Limit = IoBase + IoSize - 1;\r
309 mRootBridge.Mem.Base = Mmio32Base;\r
310 mRootBridge.Mem.Limit = Mmio32Base + Mmio32Size - 1;\r
311 mRootBridge.MemAbove4G.Base = MAX_UINT64;\r
312 mRootBridge.MemAbove4G.Limit = 0;\r
313\r
314 //\r
315 // No separate ranges for prefetchable and non-prefetchable BARs\r
316 //\r
317 mRootBridge.PMem.Base = MAX_UINT64;\r
318 mRootBridge.PMem.Limit = 0;\r
319 mRootBridge.PMemAbove4G.Base = MAX_UINT64;\r
320 mRootBridge.PMemAbove4G.Limit = 0;\r
321\r
322 mRootBridge.DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath;\r
323\r
324 return &mRootBridge;\r
325}\r
326\r
327/**\r
328 Free the root bridge instances array returned from\r
329 PciHostBridgeGetRootBridges().\r
330\r
331 @param Bridges The root bridge instances array.\r
332 @param Count The count of the array.\r
333**/\r
334VOID\r
335EFIAPI\r
336PciHostBridgeFreeRootBridges (\r
337 PCI_ROOT_BRIDGE *Bridges,\r
338 UINTN Count\r
339 )\r
340{\r
341 ASSERT (Count == 1);\r
342}\r
343\r
344/**\r
345 Inform the platform that the resource conflict happens.\r
346\r
347 @param HostBridgeHandle Handle of the Host Bridge.\r
348 @param Configuration Pointer to PCI I/O and PCI memory resource\r
349 descriptors. The Configuration contains the resources\r
350 for all the root bridges. The resource for each root\r
351 bridge is terminated with END descriptor and an\r
352 additional END is appended indicating the end of the\r
353 entire resources. The resource descriptor field\r
354 values follow the description in\r
355 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL\r
356 .SubmitResources().\r
357**/\r
358VOID\r
359EFIAPI\r
360PciHostBridgeResourceConflict (\r
361 EFI_HANDLE HostBridgeHandle,\r
362 VOID *Configuration\r
363 )\r
364{\r
365 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
366 UINTN RootBridgeIndex;\r
367 DEBUG ((EFI_D_ERROR, "PciHostBridge: Resource conflict happens!\n"));\r
368\r
369 RootBridgeIndex = 0;\r
370 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;\r
371 while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {\r
372 DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));\r
373 for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {\r
374 ASSERT (Descriptor->ResType <\r
375 (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /\r
376 sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])\r
377 )\r
378 );\r
379 DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",\r
380 mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],\r
381 Descriptor->AddrLen, Descriptor->AddrRangeMax\r
382 ));\r
383 if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {\r
384 DEBUG ((EFI_D_ERROR, " Granularity/SpecificFlag = %ld / %02x%s\n",\r
385 Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,\r
386 ((Descriptor->SpecificFlag &\r
387 EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE\r
388 ) != 0) ? L" (Prefetchable)" : L""\r
389 ));\r
390 }\r
391 }\r
392 //\r
393 // Skip the END descriptor for root bridge\r
394 //\r
395 ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);\r
396 Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(\r
397 (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1\r
398 );\r
399 }\r
400}\r