TianoCore EDK2 master
Loading...
Searching...
No Matches
FdtPciHostBridgeLib.c
Go to the documentation of this file.
1
9#include <PiDxe.h>
11#include <Library/DebugLib.h>
15#include <Library/PcdLib.h>
19
20#include <Protocol/FdtClient.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)
29typedef struct {
30 UINT32 Type;
31 UINT64 ChildBase;
32 UINT64 CpuBase;
33 UINT64 Size;
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
47MapGcdMmioSpace (
48 IN UINT64 Base,
49 IN UINT64 Size
50 )
51{
52 EFI_STATUS Status;
53
54 Status = gDS->AddMemorySpace (
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 __func__,
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 __func__,
77 Base,
78 Size
79 ));
80 }
81
82 return Status;
83}
84
87ProcessPciHost (
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 __func__
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 __func__
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 __func__
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 __func__
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", __func__));
228 return EFI_PROTOCOL_ERROR;
229 }
230
231 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
232 ++RecordIdx)
233 {
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", __func__));
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 __func__,
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 __func__,
286 Mmio64Translation
287 ));
288 return EFI_UNSUPPORTED;
289 }
290
291 break;
292 }
293 }
294
295 if (*Mmio32Size == 0) {
296 DEBUG ((DEBUG_ERROR, "%a: MMIO32 space empty\n", __func__));
297 return EFI_PROTOCOL_ERROR;
298 }
299
300 //
301 // The dynamic PCD PcdPciExpressBaseAddress should have already been set,
302 // and should match the value we found in the DT node.
303 //
304 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);
305
306 DEBUG ((
307 DEBUG_INFO,
308 "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
309 "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",
310 __func__,
311 ConfigBase,
312 ConfigSize,
313 *BusMin,
314 *BusMax,
315 *IoBase,
316 *IoSize,
317 IoTranslation,
318 *Mmio32Base,
319 *Mmio32Size,
320 *Mmio64Base,
321 *Mmio64Size
322 ));
323
324 // Map the ECAM space in the GCD memory map
325 Status = MapGcdMmioSpace (ConfigBase, ConfigSize);
326 ASSERT_EFI_ERROR (Status);
327 if (EFI_ERROR (Status)) {
328 return Status;
329 }
330
331 if (*IoSize != 0) {
332 //
333 // Map the MMIO window that provides I/O access - the PCI host bridge code
334 // is not aware of this translation and so it will only map the I/O view
335 // in the GCD I/O map.
336 //
337 Status = MapGcdMmioSpace (*IoBase + IoTranslation, *IoSize);
338 ASSERT_EFI_ERROR (Status);
339 }
340
341 return Status;
342}
343
354EFIAPI
356 UINTN *Count
357 )
358{
359 UINT64 IoBase, IoSize;
360 UINT64 Mmio32Base, Mmio32Size;
361 UINT64 Mmio64Base, Mmio64Size;
362 UINT32 BusMin, BusMax;
363 EFI_STATUS Status;
364 UINT64 Attributes;
365 UINT64 AllocationAttributes;
368 PCI_ROOT_BRIDGE_APERTURE MemAbove4G;
370 PCI_ROOT_BRIDGE_APERTURE PMemAbove4G;
371
372 if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {
373 DEBUG ((DEBUG_INFO, "%a: PCI host bridge not present\n", __func__));
374
375 *Count = 0;
376 return NULL;
377 }
378
379 Status = ProcessPciHost (
380 &IoBase,
381 &IoSize,
382 &Mmio32Base,
383 &Mmio32Size,
384 &Mmio64Base,
385 &Mmio64Size,
386 &BusMin,
387 &BusMax
388 );
389 if (EFI_ERROR (Status)) {
390 DEBUG ((
391 DEBUG_ERROR,
392 "%a: failed to discover PCI host bridge: %r\n",
393 __func__,
394 Status
395 ));
396 *Count = 0;
397 return NULL;
398 }
399
400 ZeroMem (&Io, sizeof (Io));
401 ZeroMem (&Mem, sizeof (Mem));
402 ZeroMem (&MemAbove4G, sizeof (MemAbove4G));
403 ZeroMem (&PMem, sizeof (PMem));
404 ZeroMem (&PMemAbove4G, sizeof (PMemAbove4G));
405
406 Attributes = EFI_PCI_ATTRIBUTE_ISA_IO_16 |
407 EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |
408 EFI_PCI_ATTRIBUTE_VGA_IO_16 |
409 EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
410
411 AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;
412
413 if (IoSize != 0) {
414 Io.Base = IoBase;
415 Io.Limit = IoBase + IoSize - 1;
416 } else {
417 Io.Base = MAX_UINT64;
418 Io.Limit = 0;
419 }
420
421 Mem.Base = Mmio32Base;
422 Mem.Limit = Mmio32Base + Mmio32Size - 1;
423
424 if ((sizeof (UINTN) == sizeof (UINT64)) && (Mmio64Size != 0)) {
425 MemAbove4G.Base = Mmio64Base;
426 MemAbove4G.Limit = Mmio64Base + Mmio64Size - 1;
427 AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;
428 } else {
429 //
430 // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit
431 // architecture such as ARM, we will not be able to access 64-bit MMIO
432 // BARs unless they are allocated below 4 GB. So ignore the range above
433 // 4 GB in this case.
434 //
435 MemAbove4G.Base = MAX_UINT64;
436 MemAbove4G.Limit = 0;
437 }
438
439 //
440 // No separate ranges for prefetchable and non-prefetchable BARs
441 //
442 PMem.Base = MAX_UINT64;
443 PMem.Limit = 0;
444 PMemAbove4G.Base = MAX_UINT64;
445 PMemAbove4G.Limit = 0;
446
448 Count,
449 Attributes,
450 AllocationAttributes,
451 TRUE,
452 FALSE,
453 BusMin,
454 BusMax,
455 &Io,
456 &Mem,
457 &MemAbove4G,
458 &PMem,
459 &PMemAbove4G
460 );
461}
462
470VOID
471EFIAPI
473 PCI_ROOT_BRIDGE *Bridges,
474 UINTN Count
475 )
476{
478}
479
494VOID
495EFIAPI
497 EFI_HANDLE HostBridgeHandle,
498 VOID *Configuration
499 )
500{
502}
UINT64 UINTN
VOID EFIAPI PciHostBridgeUtilityFreeRootBridges(IN PCI_ROOT_BRIDGE *Bridges, IN UINTN Count)
PCI_ROOT_BRIDGE *EFIAPI PciHostBridgeUtilityGetRootBridges(OUT UINTN *Count, IN UINT64 Attributes, IN UINT64 AllocationAttributes, IN BOOLEAN DmaAbove4G, IN BOOLEAN NoExtendedConfigSpace, IN UINTN BusMin, IN UINTN BusMax, IN PCI_ROOT_BRIDGE_APERTURE *Io, IN PCI_ROOT_BRIDGE_APERTURE *Mem, IN PCI_ROOT_BRIDGE_APERTURE *MemAbove4G, IN PCI_ROOT_BRIDGE_APERTURE *PMem, IN PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G)
VOID EFIAPI PciHostBridgeUtilityResourceConflict(IN VOID *Configuration)
UINT64 EFIAPI DivU64x32(IN UINT64 Dividend, IN UINT32 Divisor)
Definition: DivU64x32.c:29
UINT32 EFIAPI SwapBytes32(IN UINT32 Value)
Definition: SwapBytes32.c:25
UINT64 EFIAPI SwapBytes64(IN UINT64 Value)
Definition: SwapBytes64.c:25
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
EFI_DXE_SERVICES * gDS
PCI_ROOT_BRIDGE *EFIAPI PciHostBridgeGetRootBridges(UINTN *Count)
VOID EFIAPI PciHostBridgeFreeRootBridges(PCI_ROOT_BRIDGE *Bridges, UINTN Count)
VOID EFIAPI PciHostBridgeResourceConflict(EFI_HANDLE HostBridgeHandle, VOID *Configuration)
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define DEBUG_CODE(Expression)
Definition: DebugLib.h:590
#define PcdGet64(TokenName)
Definition: PcdLib.h:375
#define EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM
#define EFI_PCI_HOST_BRIDGE_MEM64_DECODE
@ EfiGcdMemoryTypeMemoryMappedIo
Definition: PiDxeCis.h:44
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
EFI_BOOT_SERVICES * gBS