+++ /dev/null
-/** @file\r
- Map positions of extra PCI root buses to bus numbers.\r
-\r
- Copyright (C) 2015, Red Hat, Inc.\r
-\r
- This program and the accompanying materials are licensed and made available\r
- under the terms and conditions of the BSD License which accompanies this\r
- distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
- WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-**/\r
-\r
-#include <Library/DebugLib.h>\r
-#include <Library/DevicePathLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/OrderedCollectionLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-#include <Protocol/DevicePath.h>\r
-#include <Protocol/PciRootBridgeIo.h>\r
-\r
-#include "ExtraRootBusMap.h"\r
-\r
-//\r
-// The BusNumbers field is an array with Count elements. The elements increase\r
-// strictry monotonically. Zero is not an element (because the zero bus number\r
-// belongs to the "main" root bus, never to an extra root bus). Offset N in the\r
-// array maps the extra root bus with position (N+1) to its bus number (because\r
-// the root bus with position 0 is always the main root bus, therefore we don't\r
-// store it).\r
-//\r
-// If there are no extra root buses in the system, then Count is 0, and\r
-// BusNumbers is NULL.\r
-//\r
-struct EXTRA_ROOT_BUS_MAP_STRUCT {\r
- UINT32 *BusNumbers;\r
- UINTN Count;\r
-};\r
-\r
-\r
-/**\r
- An ORDERED_COLLECTION_USER_COMPARE function that compares root bridge\r
- protocol device paths based on UID.\r
-\r
- @param[in] UserStruct1 Pointer to the first ACPI_HID_DEVICE_PATH.\r
-\r
- @param[in] UserStruct2 Pointer to the second ACPI_HID_DEVICE_PATH.\r
-\r
- @retval <0 If UserStruct1 compares less than UserStruct2.\r
-\r
- @retval 0 If UserStruct1 compares equal to UserStruct2.\r
-\r
- @retval >0 If UserStruct1 compares greater than UserStruct2.\r
-**/\r
-STATIC\r
-INTN\r
-EFIAPI\r
-RootBridgePathCompare (\r
- IN CONST VOID *UserStruct1,\r
- IN CONST VOID *UserStruct2\r
- )\r
-{\r
- CONST ACPI_HID_DEVICE_PATH *Acpi1;\r
- CONST ACPI_HID_DEVICE_PATH *Acpi2;\r
-\r
- Acpi1 = UserStruct1;\r
- Acpi2 = UserStruct2;\r
-\r
- return Acpi1->UID < Acpi2->UID ? -1 :\r
- Acpi1->UID > Acpi2->UID ? 1 :\r
- 0;\r
-}\r
-\r
-\r
-/**\r
- An ORDERED_COLLECTION_KEY_COMPARE function that compares a root bridge\r
- protocol device path against a UID.\r
-\r
- @param[in] StandaloneKey Pointer to the bare UINT32 UID.\r
-\r
- @param[in] UserStruct Pointer to the ACPI_HID_DEVICE_PATH with the\r
- embedded UINT32 UID.\r
-\r
- @retval <0 If StandaloneKey compares less than UserStruct's key.\r
-\r
- @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
-\r
- @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
-**/\r
-STATIC\r
-INTN\r
-EFIAPI\r
-RootBridgePathKeyCompare (\r
- IN CONST VOID *StandaloneKey,\r
- IN CONST VOID *UserStruct\r
- )\r
-{\r
- CONST UINT32 *Uid;\r
- CONST ACPI_HID_DEVICE_PATH *Acpi;\r
-\r
- Uid = StandaloneKey;\r
- Acpi = UserStruct;\r
-\r
- return *Uid < Acpi->UID ? -1 :\r
- *Uid > Acpi->UID ? 1 :\r
- 0;\r
-}\r
-\r
-\r
-/**\r
- Create a structure that maps the relative positions of PCI root buses to bus\r
- numbers.\r
-\r
- In the "bootorder" fw_cfg file, QEMU refers to extra PCI root buses by their\r
- positions, in relative root bus number order, not by their actual PCI bus\r
- numbers. The ACPI HID device path nodes however that are associated with\r
- PciRootBridgeIo protocol instances in the system have their UID fields set to\r
- the bus numbers. Create a map that gives, for each extra PCI root bus's\r
- position (ie. "serial number") its actual PCI bus number.\r
-\r
- @param[out] ExtraRootBusMap The data structure implementing the map.\r
-\r
- @retval EFI_SUCCESS ExtraRootBusMap has been populated.\r
-\r
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
-\r
- @retval EFI_ALREADY_STARTED A duplicate root bus number has been found in\r
- the system. (This should never happen.)\r
-\r
- @return Error codes returned by\r
- gBS->LocateHandleBuffer() and\r
- gBS->HandleProtocol().\r
-\r
-**/\r
-EFI_STATUS\r
-CreateExtraRootBusMap (\r
- OUT EXTRA_ROOT_BUS_MAP **ExtraRootBusMap\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINTN NumHandles;\r
- EFI_HANDLE *Handles;\r
- ORDERED_COLLECTION *Collection;\r
- EXTRA_ROOT_BUS_MAP *Map;\r
- UINTN Idx;\r
- ORDERED_COLLECTION_ENTRY *Entry, *Entry2;\r
-\r
- //\r
- // Handles and Collection are temporary / helper variables, while in Map we\r
- // build the return value.\r
- //\r
-\r
- Status = gBS->LocateHandleBuffer (ByProtocol,\r
- &gEfiPciRootBridgeIoProtocolGuid, NULL /* SearchKey */,\r
- &NumHandles, &Handles);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- Collection = OrderedCollectionInit (RootBridgePathCompare,\r
- RootBridgePathKeyCompare);\r
- if (Collection == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto FreeHandles;\r
- }\r
-\r
- Map = AllocateZeroPool (sizeof *Map);\r
- if (Map == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto FreeCollection;\r
- }\r
-\r
- //\r
- // Collect the ACPI device path protocols of the root bridges.\r
- //\r
- for (Idx = 0; Idx < NumHandles; ++Idx) {\r
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
-\r
- Status = gBS->HandleProtocol (Handles[Idx], &gEfiDevicePathProtocolGuid,\r
- (VOID**)&DevicePath);\r
- if (EFI_ERROR (Status)) {\r
- goto FreeMap;\r
- }\r
-\r
- //\r
- // Examine if the device path is an ACPI HID one, and if so, if UID is\r
- // nonzero (ie. the root bridge that the bus number belongs to is "extra",\r
- // not the main one). In that case, link the device path into Collection.\r
- //\r
- if (DevicePathType (DevicePath) == ACPI_DEVICE_PATH &&\r
- DevicePathSubType (DevicePath) == ACPI_DP &&\r
- ((ACPI_HID_DEVICE_PATH *)DevicePath)->HID == EISA_PNP_ID(0x0A03) &&\r
- ((ACPI_HID_DEVICE_PATH *)DevicePath)->UID > 0) {\r
- Status = OrderedCollectionInsert (Collection, NULL, DevicePath);\r
- if (EFI_ERROR (Status)) {\r
- goto FreeMap;\r
- }\r
- ++Map->Count;\r
- }\r
- }\r
-\r
- if (Map->Count > 0) {\r
- //\r
- // At least one extra PCI root bus exists.\r
- //\r
- Map->BusNumbers = AllocatePool (Map->Count * sizeof *Map->BusNumbers);\r
- if (Map->BusNumbers == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto FreeMap;\r
- }\r
- }\r
-\r
- //\r
- // Now collect the bus numbers of the extra PCI root buses into Map.\r
- //\r
- Idx = 0;\r
- Entry = OrderedCollectionMin (Collection);\r
- while (Idx < Map->Count) {\r
- ACPI_HID_DEVICE_PATH *Acpi;\r
-\r
- ASSERT (Entry != NULL);\r
- Acpi = OrderedCollectionUserStruct (Entry);\r
- Map->BusNumbers[Idx] = Acpi->UID;\r
- DEBUG ((EFI_D_VERBOSE,\r
- "%a: extra bus position 0x%Lx maps to bus number (UID) 0x%x\n",\r
- __FUNCTION__, (UINT64)(Idx + 1), Acpi->UID));\r
- ++Idx;\r
- Entry = OrderedCollectionNext (Entry);\r
- }\r
- ASSERT (Entry == NULL);\r
-\r
- *ExtraRootBusMap = Map;\r
- Status = EFI_SUCCESS;\r
-\r
- //\r
- // Fall through in order to release temporaries.\r
- //\r
-\r
-FreeMap:\r
- if (EFI_ERROR (Status)) {\r
- if (Map->BusNumbers != NULL) {\r
- FreePool (Map->BusNumbers);\r
- }\r
- FreePool (Map);\r
- }\r
-\r
-FreeCollection:\r
- for (Entry = OrderedCollectionMin (Collection); Entry != NULL;\r
- Entry = Entry2) {\r
- Entry2 = OrderedCollectionNext (Entry);\r
- OrderedCollectionDelete (Collection, Entry, NULL);\r
- }\r
- OrderedCollectionUninit (Collection);\r
-\r
-FreeHandles:\r
- FreePool (Handles);\r
-\r
- return Status;\r
-}\r
-\r
-\r
-/**\r
- Release a map created with CreateExtraRootBusMap().\r
-\r
- @param[in] ExtraRootBusMap The map to release.\r
-*/\r
-VOID\r
-DestroyExtraRootBusMap (\r
- IN EXTRA_ROOT_BUS_MAP *ExtraRootBusMap\r
- )\r
-{\r
- if (ExtraRootBusMap->BusNumbers != NULL) {\r
- FreePool (ExtraRootBusMap->BusNumbers);\r
- }\r
- FreePool (ExtraRootBusMap);\r
-}\r
-\r
-/**\r
- Map the position (serial number) of an extra PCI root bus to its bus number.\r
-\r
- @param[in] ExtraRootBusMap The map created with CreateExtraRootBusMap();\r
-\r
- @param[in] RootBusPos The extra PCI root bus position to map.\r
-\r
- @param[out] RootBusNr The bus number belonging to the extra PCI root\r
- bus identified by RootBusPos.\r
-\r
- @retval EFI_INVALID_PARAMETER RootBusPos is zero. The zero position\r
- identifies the main root bus, whose bus number\r
- is always zero, and is therefore never\r
- maintained in ExtraRootBusMap.\r
-\r
- @retval EFI_NOT_FOUND RootBusPos is not found in ExtraRootBusMap.\r
-\r
- @retval EFI_SUCCESS Mapping successful.\r
-**/\r
-EFI_STATUS\r
-MapRootBusPosToBusNr (\r
- IN CONST EXTRA_ROOT_BUS_MAP *ExtraRootBusMap,\r
- IN UINT64 RootBusPos,\r
- OUT UINT32 *RootBusNr\r
- )\r
-{\r
- if (RootBusPos == 0) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
- if (RootBusPos > ExtraRootBusMap->Count) {\r
- return EFI_NOT_FOUND;\r
- }\r
- *RootBusNr = ExtraRootBusMap->BusNumbers[RootBusPos - 1];\r
- return EFI_SUCCESS;\r
-}\r