diff mbox

[edk2,v2,2/2] OvmfPkg/PlatformBootManagerLib: remove stale FvFile boot options

Message ID 1468343074-29596-3-git-send-email-lersek@redhat.com
State Superseded
Headers show

Commit Message

Laszlo Ersek July 12, 2016, 5:04 p.m. UTC
Removes any boot options that point to binaries built into the firmware
and have become stale due to any of the following:
- DXEFV's base address or size changed (historical),
- DXEFV's FvNameGuid changed,
- the FILE_GUID of the pointed-to binary changed,
- the referenced binary is no longer built into the firmware.

For example, multiple such "EFI Internal Shell" boot options can coexist.
They technically differ from each other, but may not describe any built-in
shell binary exactly. Such options can accumulate in a varstore over time,
and while they remain generally bootable (thanks to the efforts of
BmGetFileBufferByFvFilePath()), they look bad.

Filter out any stale options.

This functionality is not added to QemuBootOrderLib, because it is
independent from QEMU and fw_cfg.

Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Gary Lin <glin@suse.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>

---

Notes:
    v2:
    - Consider Fv(...) -- i.e., MEDIA_DEVICE_PATH / MEDIA_PIWG_FW_VOL_DP --
      first nodes as well. Do that for the sake of ArmVirtPkg too, which has
      just moved to Fv(...) as well. Rewrite / add some comments
      accordingly; update the commit message. [Ray]
    
    v1:
    - Once this patch converges, I'll port it to ArmVirtPkg.

 OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |   1 +
 OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c              | 130 ++++++++++++++++++++
 2 files changed, 131 insertions(+)

-- 
1.8.3.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox

Patch

diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
index ffa1288e4d1a..f3303b9f2cf3 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -72,6 +72,7 @@  [Protocols]
   gEfiS3SaveStateProtocolGuid                   # PROTOCOL SOMETIMES_CONSUMED
   gEfiDxeSmmReadyToLockProtocolGuid             # PROTOCOL SOMETIMES_PRODUCED
   gEfiLoadedImageProtocolGuid                   # PROTOCOL SOMETIMES_PRODUCED
+  gEfiFirmwareVolume2ProtocolGuid               # PROTOCOL SOMETIMES_CONSUMED
 
 [Guids]
   gEfiXenInfoGuid
diff --git a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
index 912c5ed1ece4..351fbc1e86c7 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
+++ b/OvmfPkg/Library/PlatformBootManagerLib/BdsPlatform.c
@@ -15,6 +15,7 @@ 
 #include "BdsPlatform.h"
 #include <Guid/XenInfo.h>
 #include <Guid/RootBridgesConnectedEventGroup.h>
+#include <Protocol/FirmwareVolume2.h>
 
 
 //
@@ -149,6 +150,134 @@  PlatformRegisterFvBootOption (
   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
 }
 
+/**
+  Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options
+  whose device paths do not resolve exactly to an FvFile in the system.
+
+  This removes any boot options that point to binaries built into the firmware
+  and have become stale due to any of the following:
+  - DXEFV's base address or size changed (historical),
+  - DXEFV's FvNameGuid changed,
+  - the FILE_GUID of the pointed-to binary changed,
+  - the referenced binary is no longer built into the firmware.
+
+  EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only
+  avoids exact duplicates.
+**/
+VOID
+RemoveStaleFvFileOptions (
+  VOID
+  )
+{
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+  UINTN                        BootOptionCount;
+  UINTN                        Index;
+
+  BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,
+                  LoadOptionTypeBoot);
+
+  for (Index = 0; Index < BootOptionCount; ++Index) {
+    EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;
+    EFI_STATUS               Status;
+    EFI_HANDLE               FvHandle;
+
+    //
+    // If the device path starts with neither MemoryMapped(...) nor Fv(...),
+    // then keep the boot option.
+    //
+    Node1 = BootOptions[Index].FilePath;
+    if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&
+          DevicePathSubType (Node1) == HW_MEMMAP_DP) &&
+        !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&
+          DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {
+      continue;
+    }
+
+    //
+    // If the second device path node is not FvFile(...), then keep the boot
+    // option.
+    //
+    Node2 = NextDevicePathNode (Node1);
+    if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||
+        DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {
+      continue;
+    }
+
+    //
+    // Locate the Firmware Volume2 protocol instance that is denoted by the
+    // boot option. If this lookup fails (i.e., the boot option references a
+    // firmware volume that doesn't exist), then we'll proceed to delete the
+    // boot option.
+    //
+    SearchNode = Node1;
+    Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,
+                    &SearchNode, &FvHandle);
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // The firmware volume was found; now let's see if it contains the FvFile
+      // identified by GUID.
+      //
+      EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol;
+      MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;
+      UINTN                             BufferSize;
+      EFI_FV_FILETYPE                   FoundType;
+      EFI_FV_FILE_ATTRIBUTES            FileAttributes;
+      UINT32                            AuthenticationStatus;
+
+      Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,
+                      (VOID **)&FvProtocol);
+      ASSERT_EFI_ERROR (Status);
+
+      FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;
+      //
+      // Buffer==NULL means we request metadata only: BufferSize, FoundType,
+      // FileAttributes.
+      //
+      Status = FvProtocol->ReadFile (
+                             FvProtocol,
+                             &FvFileNode->FvFileName, // NameGuid
+                             NULL,                    // Buffer
+                             &BufferSize,
+                             &FoundType,
+                             &FileAttributes,
+                             &AuthenticationStatus
+                             );
+      if (!EFI_ERROR (Status)) {
+        //
+        // The FvFile was found. Keep the boot option.
+        //
+        continue;
+      }
+    }
+
+    //
+    // Delete the boot option.
+    //
+    Status = EfiBootManagerDeleteLoadOptionVariable (
+               BootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+    DEBUG_CODE (
+      CHAR16 *DevicePathString;
+
+      DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,
+                           FALSE, FALSE);
+      DEBUG ((
+        EFI_ERROR (Status) ? EFI_D_WARN : EFI_D_VERBOSE,
+        "%a: removing stale Boot#%04x %s: %r\n",
+        __FUNCTION__,
+        (UINT32)BootOptions[Index].OptionNumber,
+        DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
+        Status
+        ));
+      if (DevicePathString != NULL) {
+        FreePool (DevicePathString);
+      }
+      );
+  }
+
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+}
+
 VOID
 PlatformRegisterOptionsAndKeys (
   VOID
@@ -1343,6 +1472,7 @@  Routine Description:
     PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE
     );
 
+  RemoveStaleFvFileOptions ();
   SetBootOrderFromQemu ();
 }