MdeModulePkg/PciHostBridge: Don't assume resources are fully NonExistent
authorRuiyu Ni <ruiyu.ni@intel.com>
Thu, 25 Feb 2016 09:13:31 +0000 (17:13 +0800)
committerLaszlo Ersek <lersek@redhat.com>
Fri, 26 Feb 2016 12:31:51 +0000 (13:31 +0100)
The patch removes the assumption that the resources claimed by root
bridges should not exist. Because resources might have been added:
1. by platform modules either in PEI through resource HOB, or in DXE,
   before the PCI host bridge driver runs.
2. Resources claimed by different root bridges may overlap so that
   resource adding operation for latter root bridges may fail if
   we assume the resource should not exist.

In real world, this patch is to fit OVMF platform needs because
different root bridges in OVMF platform shares the same resources.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
[lersek@redhat.com: intersection-based implementation]
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com>
MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridge.c

index edf042cc254790a931f8e0130c4b5a6fea92f9ac..ef0c4f2a5e7bbbd607844dc120b9c3e51fb68125 100644 (file)
@@ -28,6 +28,290 @@ GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mPciResourceTypeStr[] = {
   L"I/O", L"Mem", L"PMem", L"Mem64", L"PMem64", L"Bus"\r
 };\r
 \r
+/**\r
+  Ensure the compatibility of an IO space descriptor with the IO aperture.\r
+\r
+  The IO space descriptor can come from the GCD IO space map, or it can\r
+  represent a gap between two neighboring IO space descriptors. In the latter\r
+  case, the GcdIoType field is expected to be EfiGcdIoTypeNonExistent.\r
+\r
+  If the IO space descriptor already has type EfiGcdIoTypeIo, then no action is\r
+  taken -- it is by definition compatible with the aperture.\r
+\r
+  Otherwise, the intersection of the IO space descriptor is calculated with the\r
+  aperture. If the intersection is the empty set (no overlap), no action is\r
+  taken; the IO space descriptor is compatible with the aperture.\r
+\r
+  Otherwise, the type of the descriptor is investigated again. If the type is\r
+  EfiGcdIoTypeNonExistent (representing a gap, or a genuine descriptor with\r
+  such a type), then an attempt is made to add the intersection as IO space to\r
+  the GCD IO space map. This ensures continuity for the aperture, and the\r
+  descriptor is deemed compatible with the aperture.\r
+\r
+  Otherwise, the IO space descriptor is incompatible with the IO aperture.\r
+\r
+  @param[in] Base        Base address of the aperture.\r
+  @param[in] Length      Length of the aperture.\r
+  @param[in] Descriptor  The descriptor to ensure compatibility with the\r
+                         aperture for.\r
+\r
+  @retval EFI_SUCCESS            The descriptor is compatible. The GCD IO space\r
+                                 map may have been updated, for continuity\r
+                                 within the aperture.\r
+  @retval EFI_INVALID_PARAMETER  The descriptor is incompatible.\r
+  @return                        Error codes from gDS->AddIoSpace().\r
+**/\r
+EFI_STATUS\r
+IntersectIoDescriptor (\r
+  IN  UINT64                            Base,\r
+  IN  UINT64                            Length,\r
+  IN  CONST EFI_GCD_IO_SPACE_DESCRIPTOR *Descriptor\r
+  )\r
+{\r
+  UINT64                                IntersectionBase;\r
+  UINT64                                IntersectionEnd;\r
+  EFI_STATUS                            Status;\r
+\r
+  if (Descriptor->GcdIoType == EfiGcdIoTypeIo) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  IntersectionBase = MAX (Base, Descriptor->BaseAddress);\r
+  IntersectionEnd = MIN (Base + Length,\r
+                      Descriptor->BaseAddress + Descriptor->Length);\r
+  if (IntersectionBase >= IntersectionEnd) {\r
+    //\r
+    // The descriptor and the aperture don't overlap.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Descriptor->GcdIoType == EfiGcdIoTypeNonExistent) {\r
+    Status = gDS->AddIoSpace (EfiGcdIoTypeIo, IntersectionBase,\r
+                    IntersectionEnd - IntersectionBase);\r
+\r
+    DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE,\r
+      "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__,\r
+      IntersectionBase, IntersectionEnd, Status));\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u conflicts with "\r
+    "aperture [%Lx, %Lx)\n", gEfiCallerBaseName, __FUNCTION__,\r
+    Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length,\r
+    (UINT32)Descriptor->GcdIoType, Base, Base + Length));\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+  Add IO space to GCD.\r
+  The routine checks the GCD database and only adds those which are\r
+  not added in the specified range to GCD.\r
+\r
+  @param Base   Base address of the IO space.\r
+  @param Length Length of the IO space.\r
+\r
+  @retval EFI_SUCCES The IO space was added successfully.\r
+**/\r
+EFI_STATUS\r
+AddIoSpace (\r
+  IN  UINT64                        Base,\r
+  IN  UINT64                        Length\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  UINTN                             Index;\r
+  UINTN                             NumberOfDescriptors;\r
+  EFI_GCD_IO_SPACE_DESCRIPTOR       *IoSpaceMap;\r
+\r
+  Status = gDS->GetIoSpaceMap (&NumberOfDescriptors, &IoSpaceMap);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "%a: %a: GetIoSpaceMap(): %r\n",\r
+      gEfiCallerBaseName, __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
+    Status = IntersectIoDescriptor (Base, Length, &IoSpaceMap[Index]);\r
+    if (EFI_ERROR (Status)) {\r
+      goto FreeIoSpaceMap;\r
+    }\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    //\r
+    // Make sure there are adjacent descriptors covering [Base, Base + Length).\r
+    // It is possible that they have not been merged; merging can be prevented\r
+    // by allocation.\r
+    //\r
+    UINT64                      CheckBase;\r
+    EFI_STATUS                  CheckStatus;\r
+    EFI_GCD_IO_SPACE_DESCRIPTOR Descriptor;\r
+\r
+    for (CheckBase = Base;\r
+         CheckBase < Base + Length;\r
+         CheckBase = Descriptor.BaseAddress + Descriptor.Length) {\r
+      CheckStatus = gDS->GetIoSpaceDescriptor (CheckBase, &Descriptor);\r
+      ASSERT_EFI_ERROR (CheckStatus);\r
+      ASSERT (Descriptor.GcdIoType == EfiGcdIoTypeIo);\r
+    }\r
+    );\r
+\r
+FreeIoSpaceMap:\r
+  FreePool (IoSpaceMap);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Ensure the compatibility of a memory space descriptor with the MMIO aperture.\r
+\r
+  The memory space descriptor can come from the GCD memory space map, or it can\r
+  represent a gap between two neighboring memory space descriptors. In the\r
+  latter case, the GcdMemoryType field is expected to be\r
+  EfiGcdMemoryTypeNonExistent.\r
+\r
+  If the memory space descriptor already has type\r
+  EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the\r
+  required capabilities, then no action is taken -- it is by definition\r
+  compatible with the aperture.\r
+\r
+  Otherwise, the intersection of the memory space descriptor is calculated with\r
+  the aperture. If the intersection is the empty set (no overlap), no action is\r
+  taken; the memory space descriptor is compatible with the aperture.\r
+\r
+  Otherwise, the type of the descriptor is investigated again. If the type is\r
+  EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with\r
+  such a type), then an attempt is made to add the intersection as MMIO space\r
+  to the GCD memory space map, with the specified capabilities. This ensures\r
+  continuity for the aperture, and the descriptor is deemed compatible with the\r
+  aperture.\r
+\r
+  Otherwise, the memory space descriptor is incompatible with the MMIO\r
+  aperture.\r
+\r
+  @param[in] Base         Base address of the aperture.\r
+  @param[in] Length       Length of the aperture.\r
+  @param[in] Capabilities Capabilities required by the aperture.\r
+  @param[in] Descriptor   The descriptor to ensure compatibility with the\r
+                          aperture for.\r
+\r
+  @retval EFI_SUCCESS            The descriptor is compatible. The GCD memory\r
+                                 space map may have been updated, for\r
+                                 continuity within the aperture.\r
+  @retval EFI_INVALID_PARAMETER  The descriptor is incompatible.\r
+  @return                        Error codes from gDS->AddMemorySpace().\r
+**/\r
+EFI_STATUS\r
+IntersectMemoryDescriptor (\r
+  IN  UINT64                                Base,\r
+  IN  UINT64                                Length,\r
+  IN  UINT64                                Capabilities,\r
+  IN  CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor\r
+  )\r
+{\r
+  UINT64                                    IntersectionBase;\r
+  UINT64                                    IntersectionEnd;\r
+  EFI_STATUS                                Status;\r
+\r
+  if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo &&\r
+      (Descriptor->Capabilities & Capabilities) == Capabilities) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  IntersectionBase = MAX (Base, Descriptor->BaseAddress);\r
+  IntersectionEnd = MIN (Base + Length,\r
+                      Descriptor->BaseAddress + Descriptor->Length);\r
+  if (IntersectionBase >= IntersectionEnd) {\r
+    //\r
+    // The descriptor and the aperture don't overlap.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {\r
+    Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo,\r
+                    IntersectionBase, IntersectionEnd - IntersectionBase,\r
+                    Capabilities);\r
+\r
+    DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE,\r
+      "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__,\r
+      IntersectionBase, IntersectionEnd, Status));\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "\r
+    "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__,\r
+    Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length,\r
+    (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities,\r
+    Base, Base + Length, Capabilities));\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+  Add MMIO space to GCD.\r
+  The routine checks the GCD database and only adds those which are\r
+  not added in the specified range to GCD.\r
+\r
+  @param Base         Base address of the MMIO space.\r
+  @param Length       Length of the MMIO space.\r
+  @param Capabilities Capabilities of the MMIO space.\r
+\r
+  @retval EFI_SUCCES The MMIO space was added successfully.\r
+**/\r
+EFI_STATUS\r
+AddMemoryMappedIoSpace (\r
+  IN  UINT64                            Base,\r
+  IN  UINT64                            Length,\r
+  IN  UINT64                            Capabilities\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  UINTN                                 Index;\r
+  UINTN                                 NumberOfDescriptors;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR       *MemorySpaceMap;\r
+\r
+  Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((EFI_D_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n",\r
+      gEfiCallerBaseName, __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
+    Status = IntersectMemoryDescriptor (Base, Length, Capabilities,\r
+               &MemorySpaceMap[Index]);\r
+    if (EFI_ERROR (Status)) {\r
+      goto FreeMemorySpaceMap;\r
+    }\r
+  }\r
+\r
+  DEBUG_CODE (\r
+    //\r
+    // Make sure there are adjacent descriptors covering [Base, Base + Length).\r
+    // It is possible that they have not been merged; merging can be prevented\r
+    // by allocation and different capabilities.\r
+    //\r
+    UINT64                          CheckBase;\r
+    EFI_STATUS                      CheckStatus;\r
+    EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
+\r
+    for (CheckBase = Base;\r
+         CheckBase < Base + Length;\r
+         CheckBase = Descriptor.BaseAddress + Descriptor.Length) {\r
+      CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor);\r
+      ASSERT_EFI_ERROR (CheckStatus);\r
+      ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo);\r
+      ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities);\r
+    }\r
+    );\r
+\r
+FreeMemorySpaceMap:\r
+  FreePool (MemorySpaceMap);\r
+\r
+  return Status;\r
+}\r
+\r
 /**\r
 \r
   Entry point of this driver.\r
@@ -89,6 +373,7 @@ InitializePciHostBridge (
                   &gEfiPciHostBridgeResourceAllocationProtocolGuid, &HostBridge->ResAlloc,\r
                   NULL\r
                   );\r
+  ASSERT_EFI_ERROR (Status);\r
   if (EFI_ERROR (Status)) {\r
     FreePool (HostBridge);\r
     PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount);\r
@@ -109,11 +394,10 @@ InitializePciHostBridge (
     }\r
 \r
     if (RootBridges[Index].Io.Limit > RootBridges[Index].Io.Base) {\r
-      Status = gDS->AddIoSpace (\r
-                      EfiGcdIoTypeIo,\r
-                      RootBridges[Index].Io.Base,\r
-                      RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1\r
-                      );\r
+      Status = AddIoSpace (\r
+                 RootBridges[Index].Io.Base,\r
+                 RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1\r
+                 );\r
       ASSERT_EFI_ERROR (Status);\r
     }\r
 \r
@@ -130,12 +414,11 @@ InitializePciHostBridge (
 \r
     for (MemApertureIndex = 0; MemApertureIndex < sizeof (MemApertures) / sizeof (MemApertures[0]); MemApertureIndex++) {\r
       if (MemApertures[MemApertureIndex]->Limit > MemApertures[MemApertureIndex]->Base) {\r
-        Status = gDS->AddMemorySpace (\r
-                        EfiGcdMemoryTypeMemoryMappedIo,\r
-                        MemApertures[MemApertureIndex]->Base,\r
-                        MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,\r
-                        EFI_MEMORY_UC\r
-                        );\r
+        Status = AddMemoryMappedIoSpace (\r
+                   MemApertures[MemApertureIndex]->Base,\r
+                   MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,\r
+                   EFI_MEMORY_UC\r
+                   );\r
         ASSERT_EFI_ERROR (Status);\r
         Status = gDS->SetMemorySpaceAttributes (\r
                         MemApertures[MemApertureIndex]->Base,\r