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