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