Home | History | Annotate | Download | only in FdtPciHostBridgeLib
      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    *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
    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
    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
    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