• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
ProcessPciHost(OUT UINT64 * IoBase,OUT UINT64 * IoSize,OUT UINT64 * Mmio32Base,OUT UINT64 * Mmio32Size,OUT UINT64 * Mmio64Base,OUT UINT64 * Mmio64Size,OUT UINT32 * BusMin,OUT UINT32 * BusMax)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 (&gFdtClientProtocolGuid, NULL,
131                   (VOID **)&FdtClient);
132   ASSERT_EFI_ERROR (Status);
133 
134   Status = FdtClient->FindCompatibleNode (FdtClient, "pci-host-ecam-generic",
135                         &Node);
136   if (EFI_ERROR (Status)) {
137     DEBUG ((EFI_D_INFO,
138       "%a: No 'pci-host-ecam-generic' compatible DT node found\n",
139       __FUNCTION__));
140     return EFI_NOT_FOUND;
141   }
142 
143   DEBUG_CODE (
144     INT32 Tmp;
145 
146     //
147     // A DT can legally describe multiple PCI host bridges, but we are not
148     // equipped to deal with that. So assert that there is only one.
149     //
150     Status = FdtClient->FindNextCompatibleNode (FdtClient,
151                           "pci-host-ecam-generic", Node, &Tmp);
152     ASSERT (Status == EFI_NOT_FOUND);
153   );
154 
155   Status = FdtClient->GetNodeProperty (FdtClient, Node, "reg", &Prop, &Len);
156   if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT64)) {
157     DEBUG ((EFI_D_ERROR, "%a: 'reg' property not found or invalid\n",
158       __FUNCTION__));
159     return EFI_PROTOCOL_ERROR;
160   }
161 
162   //
163   // Fetch the ECAM window.
164   //
165   ConfigBase = SwapBytes64 (((CONST UINT64 *)Prop)[0]);
166   ConfigSize = SwapBytes64 (((CONST UINT64 *)Prop)[1]);
167 
168   //
169   // Fetch the bus range (note: inclusive).
170   //
171   Status = FdtClient->GetNodeProperty (FdtClient, Node, "bus-range", &Prop,
172                         &Len);
173   if (EFI_ERROR (Status) || Len != 2 * sizeof (UINT32)) {
174     DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",
175       __FUNCTION__));
176     return EFI_PROTOCOL_ERROR;
177   }
178   *BusMin = SwapBytes32 (((CONST UINT32 *)Prop)[0]);
179   *BusMax = SwapBytes32 (((CONST UINT32 *)Prop)[1]);
180 
181   //
182   // Sanity check: the config space must accommodate all 4K register bytes of
183   // all 8 functions of all 32 devices of all buses.
184   //
185   if (*BusMax < *BusMin || *BusMax - *BusMin == MAX_UINT32 ||
186       DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < *BusMax - *BusMin + 1) {
187     DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",
188       __FUNCTION__));
189     return EFI_PROTOCOL_ERROR;
190   }
191 
192   //
193   // Iterate over "ranges".
194   //
195   Status = FdtClient->GetNodeProperty (FdtClient, Node, "ranges", &Prop, &Len);
196   if (EFI_ERROR (Status) || Len == 0 ||
197       Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {
198     DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));
199     return EFI_PROTOCOL_ERROR;
200   }
201 
202   for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);
203        ++RecordIdx) {
204     CONST DTB_PCI_HOST_RANGE_RECORD *Record;
205 
206     Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;
207     switch (SwapBytes32 (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {
208     case DTB_PCI_HOST_RANGE_IO:
209       *IoBase = SwapBytes64 (Record->ChildBase);
210       *IoSize = SwapBytes64 (Record->Size);
211       IoTranslation = SwapBytes64 (Record->CpuBase) - *IoBase;
212 
213       ASSERT (PcdGet64 (PcdPciIoTranslation) == IoTranslation);
214       break;
215 
216     case DTB_PCI_HOST_RANGE_MMIO32:
217       *Mmio32Base = SwapBytes64 (Record->ChildBase);
218       *Mmio32Size = SwapBytes64 (Record->Size);
219       Mmio32Translation = SwapBytes64 (Record->CpuBase) - *Mmio32Base;
220 
221       if (*Mmio32Base > MAX_UINT32 || *Mmio32Size > MAX_UINT32 ||
222           *Mmio32Base + *Mmio32Size > SIZE_4GB) {
223         DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));
224         return EFI_PROTOCOL_ERROR;
225       }
226 
227       ASSERT (PcdGet64 (PcdPciMmio32Translation) == Mmio32Translation);
228 
229       if (Mmio32Translation != 0) {
230         DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "
231           "0x%Lx\n", __FUNCTION__, Mmio32Translation));
232         return EFI_UNSUPPORTED;
233       }
234 
235       break;
236 
237     case DTB_PCI_HOST_RANGE_MMIO64:
238       *Mmio64Base = SwapBytes64 (Record->ChildBase);
239       *Mmio64Size = SwapBytes64 (Record->Size);
240       Mmio64Translation = SwapBytes64 (Record->CpuBase) - *Mmio64Base;
241 
242       ASSERT (PcdGet64 (PcdPciMmio64Translation) == Mmio64Translation);
243 
244       if (Mmio64Translation != 0) {
245         DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO64 translation "
246           "0x%Lx\n", __FUNCTION__, Mmio64Translation));
247         return EFI_UNSUPPORTED;
248       }
249 
250       break;
251     }
252   }
253   if (*IoSize == 0 || *Mmio32Size == 0) {
254     DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,
255       (*IoSize == 0) ? "IO" : "MMIO32"));
256     return EFI_PROTOCOL_ERROR;
257   }
258 
259   //
260   // The dynamic PCD PcdPciExpressBaseAddress should have already been set,
261   // and should match the value we found in the DT node.
262   //
263   ASSERT (PcdGet64 (PcdPciExpressBaseAddress) == ConfigBase);
264 
265   DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "
266     "Io[0x%Lx+0x%Lx)@0x%Lx Mem32[0x%Lx+0x%Lx)@0x0 Mem64[0x%Lx+0x%Lx)@0x0\n",
267     __FUNCTION__, ConfigBase, ConfigSize, *BusMin, *BusMax, *IoBase, *IoSize,
268     IoTranslation, *Mmio32Base, *Mmio32Size, *Mmio64Base, *Mmio64Size));
269   return EFI_SUCCESS;
270 }
271 
272 STATIC PCI_ROOT_BRIDGE mRootBridge;
273 
274 /**
275   Return all the root bridge instances in an array.
276 
277   @param Count  Return the count of root bridge instances.
278 
279   @return All the root bridge instances in an array.
280           The array should be passed into PciHostBridgeFreeRootBridges()
281           when it's not used.
282 **/
283 PCI_ROOT_BRIDGE *
284 EFIAPI
PciHostBridgeGetRootBridges(UINTN * Count)285 PciHostBridgeGetRootBridges (
286   UINTN *Count
287   )
288 {
289   UINT64              IoBase, IoSize;
290   UINT64              Mmio32Base, Mmio32Size;
291   UINT64              Mmio64Base, Mmio64Size;
292   UINT32              BusMin, BusMax;
293   EFI_STATUS          Status;
294 
295   if (PcdGet64 (PcdPciExpressBaseAddress) == 0) {
296     DEBUG ((EFI_D_INFO, "%a: PCI host bridge not present\n", __FUNCTION__));
297 
298     *Count = 0;
299     return NULL;
300   }
301 
302   Status = ProcessPciHost (&IoBase, &IoSize, &Mmio32Base, &Mmio32Size,
303              &Mmio64Base, &Mmio64Size, &BusMin, &BusMax);
304   if (EFI_ERROR (Status)) {
305     DEBUG ((EFI_D_ERROR, "%a: failed to discover PCI host bridge: %r\n",
306       __FUNCTION__, Status));
307     *Count = 0;
308     return NULL;
309   }
310 
311   *Count = 1;
312 
313   mRootBridge.Segment               = 0;
314   mRootBridge.Supports              = EFI_PCI_ATTRIBUTE_ISA_IO_16 |
315                                       EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO |
316                                       EFI_PCI_ATTRIBUTE_VGA_IO_16  |
317                                       EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16;
318   mRootBridge.Attributes            = mRootBridge.Supports;
319 
320   mRootBridge.DmaAbove4G            = TRUE;
321   mRootBridge.NoExtendedConfigSpace = FALSE;
322   mRootBridge.ResourceAssigned      = FALSE;
323 
324   mRootBridge.AllocationAttributes  = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM;
325 
326   mRootBridge.Bus.Base              = BusMin;
327   mRootBridge.Bus.Limit             = BusMax;
328   mRootBridge.Io.Base               = IoBase;
329   mRootBridge.Io.Limit              = IoBase + IoSize - 1;
330   mRootBridge.Mem.Base              = Mmio32Base;
331   mRootBridge.Mem.Limit             = Mmio32Base + Mmio32Size - 1;
332 
333   if (sizeof (UINTN) == sizeof (UINT64)) {
334     mRootBridge.MemAbove4G.Base       = Mmio64Base;
335     mRootBridge.MemAbove4G.Limit      = Mmio64Base + Mmio64Size - 1;
336     if (Mmio64Size > 0) {
337       mRootBridge.AllocationAttributes |= EFI_PCI_HOST_BRIDGE_MEM64_DECODE;
338     }
339   } else {
340     //
341     // UEFI mandates a 1:1 virtual-to-physical mapping, so on a 32-bit
342     // architecture such as ARM, we will not be able to access 64-bit MMIO
343     // BARs unless they are allocated below 4 GB. So ignore the range above
344     // 4 GB in this case.
345     //
346     mRootBridge.MemAbove4G.Base       = MAX_UINT64;
347     mRootBridge.MemAbove4G.Limit      = 0;
348   }
349 
350   //
351   // No separate ranges for prefetchable and non-prefetchable BARs
352   //
353   mRootBridge.PMem.Base             = MAX_UINT64;
354   mRootBridge.PMem.Limit            = 0;
355   mRootBridge.PMemAbove4G.Base      = MAX_UINT64;
356   mRootBridge.PMemAbove4G.Limit     = 0;
357 
358   mRootBridge.DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mEfiPciRootBridgeDevicePath;
359 
360   return &mRootBridge;
361 }
362 
363 /**
364   Free the root bridge instances array returned from
365   PciHostBridgeGetRootBridges().
366 
367   @param Bridges The root bridge instances array.
368   @param Count   The count of the array.
369 **/
370 VOID
371 EFIAPI
PciHostBridgeFreeRootBridges(PCI_ROOT_BRIDGE * Bridges,UINTN Count)372 PciHostBridgeFreeRootBridges (
373   PCI_ROOT_BRIDGE *Bridges,
374   UINTN           Count
375   )
376 {
377   ASSERT (Count == 1);
378 }
379 
380 /**
381   Inform the platform that the resource conflict happens.
382 
383   @param HostBridgeHandle Handle of the Host Bridge.
384   @param Configuration    Pointer to PCI I/O and PCI memory resource
385                           descriptors. The Configuration contains the resources
386                           for all the root bridges. The resource for each root
387                           bridge is terminated with END descriptor and an
388                           additional END is appended indicating the end of the
389                           entire resources. The resource descriptor field
390                           values follow the description in
391                           EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
392                           .SubmitResources().
393 **/
394 VOID
395 EFIAPI
PciHostBridgeResourceConflict(EFI_HANDLE HostBridgeHandle,VOID * Configuration)396 PciHostBridgeResourceConflict (
397   EFI_HANDLE                        HostBridgeHandle,
398   VOID                              *Configuration
399   )
400 {
401   EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
402   UINTN                             RootBridgeIndex;
403   DEBUG ((EFI_D_ERROR, "PciHostBridge: Resource conflict happens!\n"));
404 
405   RootBridgeIndex = 0;
406   Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;
407   while (Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR) {
408     DEBUG ((EFI_D_ERROR, "RootBridge[%d]:\n", RootBridgeIndex++));
409     for (; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
410       ASSERT (Descriptor->ResType <
411               (sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr) /
412                sizeof (mPciHostBridgeLibAcpiAddressSpaceTypeStr[0])
413                )
414               );
415       DEBUG ((EFI_D_ERROR, " %s: Length/Alignment = 0x%lx / 0x%lx\n",
416               mPciHostBridgeLibAcpiAddressSpaceTypeStr[Descriptor->ResType],
417               Descriptor->AddrLen, Descriptor->AddrRangeMax
418               ));
419       if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
420         DEBUG ((EFI_D_ERROR, "     Granularity/SpecificFlag = %ld / %02x%s\n",
421                 Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,
422                 ((Descriptor->SpecificFlag &
423                   EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE
424                   ) != 0) ? L" (Prefetchable)" : L""
425                 ));
426       }
427     }
428     //
429     // Skip the END descriptor for root bridge
430     //
431     ASSERT (Descriptor->Desc == ACPI_END_TAG_DESCRIPTOR);
432     Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *)(
433                    (EFI_ACPI_END_TAG_DESCRIPTOR *)Descriptor + 1
434                    );
435   }
436 }
437