Home | History | Annotate | Download | only in QemuBootOrderLib
      1 /** @file
      2   Map positions of extra PCI root buses to bus numbers.
      3 
      4   Copyright (C) 2015, Red Hat, Inc.
      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, WITHOUT
     12   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 **/
     14 
     15 #include <Library/DebugLib.h>
     16 #include <Library/DevicePathLib.h>
     17 #include <Library/MemoryAllocationLib.h>
     18 #include <Library/OrderedCollectionLib.h>
     19 #include <Library/UefiBootServicesTableLib.h>
     20 #include <Protocol/DevicePath.h>
     21 #include <Protocol/PciRootBridgeIo.h>
     22 
     23 #include "ExtraRootBusMap.h"
     24 
     25 //
     26 // The BusNumbers field is an array with Count elements. The elements increase
     27 // strictry monotonically. Zero is not an element (because the zero bus number
     28 // belongs to the "main" root bus, never to an extra root bus). Offset N in the
     29 // array maps the extra root bus with position (N+1) to its bus number (because
     30 // the root bus with position 0 is always the main root bus, therefore we don't
     31 // store it).
     32 //
     33 // If there are no extra root buses in the system, then Count is 0, and
     34 // BusNumbers is NULL.
     35 //
     36 struct EXTRA_ROOT_BUS_MAP_STRUCT {
     37   UINT32 *BusNumbers;
     38   UINTN  Count;
     39 };
     40 
     41 
     42 /**
     43   An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge
     44   protocol device paths based on UID.
     45 
     46   @param[in] UserStruct1  Pointer to the first ACPI_HID_DEVICE_PATH.
     47 
     48   @param[in] UserStruct2  Pointer to the second ACPI_HID_DEVICE_PATH.
     49 
     50   @retval <0  If UserStruct1 compares less than UserStruct2.
     51 
     52   @retval  0  If UserStruct1 compares equal to UserStruct2.
     53 
     54   @retval >0  If UserStruct1 compares greater than UserStruct2.
     55 **/
     56 STATIC
     57 INTN
     58 EFIAPI
     59 RootBridgePathCompare (
     60   IN CONST VOID *UserStruct1,
     61   IN CONST VOID *UserStruct2
     62   )
     63 {
     64   CONST ACPI_HID_DEVICE_PATH *Acpi1;
     65   CONST ACPI_HID_DEVICE_PATH *Acpi2;
     66 
     67   Acpi1 = UserStruct1;
     68   Acpi2 = UserStruct2;
     69 
     70   return Acpi1->UID < Acpi2->UID ? -1 :
     71          Acpi1->UID > Acpi2->UID ?  1 :
     72          0;
     73 }
     74 
     75 
     76 /**
     77   An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge
     78   protocol device path against a UID.
     79 
     80   @param[in] StandaloneKey  Pointer to the bare UINT32 UID.
     81 
     82   @param[in] UserStruct     Pointer to the ACPI_HID_DEVICE_PATH with the
     83                             embedded UINT32 UID.
     84 
     85   @retval <0  If StandaloneKey compares less than UserStruct's key.
     86 
     87   @retval  0  If StandaloneKey compares equal to UserStruct's key.
     88 
     89   @retval >0  If StandaloneKey compares greater than UserStruct's key.
     90 **/
     91 STATIC
     92 INTN
     93 EFIAPI
     94 RootBridgePathKeyCompare (
     95   IN CONST VOID *StandaloneKey,
     96   IN CONST VOID *UserStruct
     97   )
     98 {
     99   CONST UINT32               *Uid;
    100   CONST ACPI_HID_DEVICE_PATH *Acpi;
    101 
    102   Uid  = StandaloneKey;
    103   Acpi = UserStruct;
    104 
    105   return *Uid < Acpi->UID ? -1 :
    106          *Uid > Acpi->UID ?  1 :
    107          0;
    108 }
    109 
    110 
    111 /**
    112   Create a structure that maps the relative positions of PCI root buses to bus
    113   numbers.
    114 
    115   In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their
    116   positions, in relative root bus number order, not by their actual PCI bus
    117   numbers. The ACPI HID device path nodes however that are associated with
    118   PciRootBridgeIo protocol instances in the system have their UID fields set to
    119   the bus numbers. Create a map that gives, for each extra PCI root bus's
    120   position (ie. "serial number") its actual PCI bus number.
    121 
    122   @param[out] ExtraRootBusMap  The data structure implementing the map.
    123 
    124   @retval EFI_SUCCESS           ExtraRootBusMap has been populated.
    125 
    126   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
    127 
    128   @retval EFI_ALREADY_STARTED   A duplicate root bus number has been found in
    129                                 the system. (This should never happen.)
    130 
    131   @return                       Error codes returned by
    132                                 gBS->LocateHandleBuffer() and
    133                                 gBS->HandleProtocol().
    134 
    135 **/
    136 EFI_STATUS
    137 CreateExtraRootBusMap (
    138   OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap
    139   )
    140 {
    141   EFI_STATUS               Status;
    142   UINTN                    NumHandles;
    143   EFI_HANDLE               *Handles;
    144   ORDERED_COLLECTION       *Collection;
    145   EXTRA_ROOT_BUS_MAP       *Map;
    146   UINTN                    Idx;
    147   ORDERED_COLLECTION_ENTRY *Entry, *Entry2;
    148 
    149   //
    150   // Handles and Collection are temporary / helper variables, while in Map we
    151   // build the return value.
    152   //
    153 
    154   Status = gBS->LocateHandleBuffer (ByProtocol,
    155                   &gEfiPciRootBridgeIoProtocolGuid, NULL /* SearchKey */,
    156                   &NumHandles, &Handles);
    157   if (EFI_ERROR (Status))  {
    158     return Status;
    159   }
    160 
    161   Collection = OrderedCollectionInit (RootBridgePathCompare,
    162                  RootBridgePathKeyCompare);
    163   if (Collection == NULL) {
    164     Status = EFI_OUT_OF_RESOURCES;
    165     goto FreeHandles;
    166   }
    167 
    168   Map = AllocateZeroPool (sizeof *Map);
    169   if (Map == NULL) {
    170     Status = EFI_OUT_OF_RESOURCES;
    171     goto FreeCollection;
    172   }
    173 
    174   //
    175   // Collect the ACPI device path protocols of the root bridges.
    176   //
    177   for (Idx = 0; Idx < NumHandles; ++Idx) {
    178     EFI_DEVICE_PATH_PROTOCOL *DevicePath;
    179 
    180     Status = gBS->HandleProtocol (Handles[Idx], &gEfiDevicePathProtocolGuid,
    181                     (VOID**)&DevicePath);
    182     if (EFI_ERROR (Status)) {
    183       goto FreeMap;
    184     }
    185 
    186     //
    187     // Examine if the device path is an ACPI HID one, and if so, if UID is
    188     // nonzero (ie. the root bridge that the bus number belongs to is "extra",
    189     // not the main one). In that case, link the device path into Collection.
    190     //
    191     if (DevicePathType (DevicePath) == ACPI_DEVICE_PATH &&
    192         DevicePathSubType (DevicePath) == ACPI_DP &&
    193         ((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID(0x0A03) &&
    194         ((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0) {
    195       Status = OrderedCollectionInsert (Collection, NULL, DevicePath);
    196       if (EFI_ERROR (Status)) {
    197         goto FreeMap;
    198       }
    199       ++Map->Count;
    200     }
    201   }
    202 
    203   if (Map->Count > 0) {
    204     //
    205     // At least one extra PCI root bus exists.
    206     //
    207     Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);
    208     if (Map->BusNumbers == NULL) {
    209       Status = EFI_OUT_OF_RESOURCES;
    210       goto FreeMap;
    211     }
    212   }
    213 
    214   //
    215   // Now collect the bus numbers of the extra PCI root buses into Map.
    216   //
    217   Idx = 0;
    218   Entry = OrderedCollectionMin (Collection);
    219   while (Idx < Map->Count) {
    220     ACPI_HID_DEVICE_PATH *Acpi;
    221 
    222     ASSERT (Entry != NULL);
    223     Acpi = OrderedCollectionUserStruct (Entry);
    224     Map->BusNumbers[Idx] = Acpi->UID;
    225     DEBUG ((EFI_D_VERBOSE,
    226       "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",
    227       __FUNCTION__, (UINT64)(Idx + 1), Acpi->UID));
    228     ++Idx;
    229     Entry = OrderedCollectionNext (Entry);
    230   }
    231   ASSERT (Entry == NULL);
    232 
    233   *ExtraRootBusMap = Map;
    234   Status = EFI_SUCCESS;
    235 
    236   //
    237   // Fall through in order to release temporaries.
    238   //
    239 
    240 FreeMap:
    241   if (EFI_ERROR (Status)) {
    242     if (Map->BusNumbers != NULL) {
    243       FreePool (Map->BusNumbers);
    244     }
    245     FreePool (Map);
    246   }
    247 
    248 FreeCollection:
    249   for (Entry = OrderedCollectionMin (Collection); Entry != NULL;
    250        Entry = Entry2) {
    251     Entry2 = OrderedCollectionNext (Entry);
    252     OrderedCollectionDelete (Collection, Entry, NULL);
    253   }
    254   OrderedCollectionUninit (Collection);
    255 
    256 FreeHandles:
    257   FreePool (Handles);
    258 
    259   return Status;
    260 }
    261 
    262 
    263 /**
    264   Release a map created with CreateExtraRootBusMap().
    265 
    266   @param[in] ExtraRootBusMap  The map to release.
    267 */
    268 VOID
    269 DestroyExtraRootBusMap (
    270   IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap
    271   )
    272 {
    273   if (ExtraRootBusMap->BusNumbers != NULL) {
    274     FreePool (ExtraRootBusMap->BusNumbers);
    275   }
    276   FreePool (ExtraRootBusMap);
    277 }
    278 
    279 /**
    280   Map the position (serial number) of an extra PCI root bus to its bus number.
    281 
    282   @param[in]  ExtraRootBusMap  The map created with CreateExtraRootBusMap();
    283 
    284   @param[in]  RootBusPos       The extra PCI root bus position to map.
    285 
    286   @param[out] RootBusNr        The bus number belonging to the extra PCI root
    287                                bus identified by RootBusPos.
    288 
    289   @retval EFI_INVALID_PARAMETER  RootBusPos is zero. The zero position
    290                                  identifies the main root bus, whose bus number
    291                                  is always zero, and is therefore never
    292                                  maintained in ExtraRootBusMap.
    293 
    294   @retval EFI_NOT_FOUND          RootBusPos is not found in ExtraRootBusMap.
    295 
    296   @retval EFI_SUCCESS            Mapping successful.
    297 **/
    298 EFI_STATUS
    299 MapRootBusPosToBusNr (
    300   IN  CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,
    301   IN  UINT64                   RootBusPos,
    302   OUT UINT32                   *RootBusNr
    303   )
    304 {
    305   if (RootBusPos == 0) {
    306     return EFI_INVALID_PARAMETER;
    307   }
    308   if (RootBusPos > ExtraRootBusMap->Count) {
    309     return EFI_NOT_FOUND;
    310   }
    311   *RootBusNr = ExtraRootBusMap->BusNumbers[RootBusPos - 1];
    312   return EFI_SUCCESS;
    313 }
    314