VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.c@ 101291

Last change on this file since 101291 was 101291, checked in by vboxsync, 17 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

  • Property svn:eol-style set to native
File size: 13.4 KB
Line 
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)
29typedef 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
45STATIC
46EFI_STATUS
47MapGcdMmioSpace (
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 __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
85STATIC
86EFI_STATUS
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 {
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", __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
344/**
345 Return all the root bridge instances in an array.
346
347 @param Count Return the count of root bridge instances.
348
349 @return All the root bridge instances in an array.
350 The array should be passed into PciHostBridgeFreeRootBridges()
351 when it's not used.
352**/
353PCI_ROOT_BRIDGE *
354EFIAPI
355PciHostBridgeGetRootBridges (
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;
366 PCI_ROOT_BRIDGE_APERTURE Io;
367 PCI_ROOT_BRIDGE_APERTURE Mem;
368 PCI_ROOT_BRIDGE_APERTURE MemAbove4G;
369 PCI_ROOT_BRIDGE_APERTURE PMem;
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
447 return PciHostBridgeUtilityGetRootBridges (
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
463/**
464 Free the root bridge instances array returned from
465 PciHostBridgeGetRootBridges().
466
467 @param Bridges The root bridge instances array.
468 @param Count The count of the array.
469**/
470VOID
471EFIAPI
472PciHostBridgeFreeRootBridges (
473 PCI_ROOT_BRIDGE *Bridges,
474 UINTN Count
475 )
476{
477 PciHostBridgeUtilityFreeRootBridges (Bridges, Count);
478}
479
480/**
481 Inform the platform that the resource conflict happens.
482
483 @param HostBridgeHandle Handle of the Host Bridge.
484 @param Configuration Pointer to PCI I/O and PCI memory resource
485 descriptors. The Configuration contains the resources
486 for all the root bridges. The resource for each root
487 bridge is terminated with END descriptor and an
488 additional END is appended indicating the end of the
489 entire resources. The resource descriptor field
490 values follow the description in
491 EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
492 .SubmitResources().
493**/
494VOID
495EFIAPI
496PciHostBridgeResourceConflict (
497 EFI_HANDLE HostBridgeHandle,
498 VOID *Configuration
499 )
500{
501 PciHostBridgeUtilityResourceConflict (Configuration);
502}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette