**/\r
\r
#include <IndustryStandard/Pci22.h>\r
+#include <IndustryStandard/Virtio095.h>\r
+#include <Library/BootLogoLib.h>\r
#include <Library/DevicePathLib.h>\r
#include <Library/PcdLib.h>\r
#include <Library/QemuBootOrderLib.h>\r
#include <Library/UefiBootManagerLib.h>\r
#include <Protocol/DevicePath.h>\r
+#include <Protocol/FirmwareVolume2.h>\r
#include <Protocol/GraphicsOutput.h>\r
#include <Protocol/LoadedImage.h>\r
#include <Protocol/PciIo.h>\r
#include <Protocol/PciRootBridgeIo.h>\r
+#include <Protocol/VirtioDevice.h>\r
#include <Guid/EventGroup.h>\r
#include <Guid/RootBridgesConnectedEventGroup.h>\r
\r
}\r
\r
\r
+/**\r
+ This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
+ the VIRTIO_DEVICE_PROTOCOL level.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsVirtioRng (\r
+ IN EFI_HANDLE Handle,\r
+ IN CONST CHAR16 *ReportText\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
+\r
+ Status = gBS->HandleProtocol (Handle, &gVirtioDeviceProtocolGuid,\r
+ (VOID**)&VirtIo);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ return (BOOLEAN)(VirtIo->SubSystemDeviceId ==\r
+ VIRTIO_SUBSYSTEM_ENTROPY_SOURCE);\r
+}\r
+\r
+\r
+/**\r
+ This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
+ the EFI_PCI_IO_PROTOCOL level.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EFIAPI\r
+IsVirtioPciRng (\r
+ IN EFI_HANDLE Handle,\r
+ IN CONST CHAR16 *ReportText\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ UINT16 VendorId;\r
+ UINT16 DeviceId;\r
+ UINT8 RevisionId;\r
+ BOOLEAN Virtio10;\r
+ UINT16 SubsystemId;\r
+\r
+ Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,\r
+ (VOID**)&PciIo);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Read and check VendorId.\r
+ //\r
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,\r
+ 1, &VendorId);\r
+ if (EFI_ERROR (Status)) {\r
+ goto PciError;\r
+ }\r
+ if (VendorId != VIRTIO_VENDOR_ID) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Read DeviceId and RevisionId.\r
+ //\r
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,\r
+ 1, &DeviceId);\r
+ if (EFI_ERROR (Status)) {\r
+ goto PciError;\r
+ }\r
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
+ 1, &RevisionId);\r
+ if (EFI_ERROR (Status)) {\r
+ goto PciError;\r
+ }\r
+\r
+ //\r
+ // From DeviceId and RevisionId, determine whether the device is a\r
+ // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can\r
+ // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and\r
+ // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can\r
+ // only be sanity-checked, and SubsystemId will decide.\r
+ //\r
+ if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&\r
+ RevisionId >= 0x01) {\r
+ Virtio10 = TRUE;\r
+ } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {\r
+ Virtio10 = FALSE;\r
+ } else {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Read and check SubsystemId as dictated by Virtio10.\r
+ //\r
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,\r
+ PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);\r
+ if (EFI_ERROR (Status)) {\r
+ goto PciError;\r
+ }\r
+ if (Virtio10 && SubsystemId >= 0x40) {\r
+ return TRUE;\r
+ }\r
+ if (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE) {\r
+ return TRUE;\r
+ }\r
+ return FALSE;\r
+\r
+PciError:\r
+ DEBUG ((DEBUG_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));\r
+ return FALSE;\r
+}\r
+\r
+\r
/**\r
This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking\r
the matching driver to produce all first-level child handles.\r
}\r
\r
\r
+/**\r
+ Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options\r
+ whose device paths do not resolve exactly to an FvFile in the system.\r
+\r
+ This removes any boot options that point to binaries built into the firmware\r
+ and have become stale due to any of the following:\r
+ - FvMain's base address or size changed (historical),\r
+ - FvMain's FvNameGuid changed,\r
+ - the FILE_GUID of the pointed-to binary changed,\r
+ - the referenced binary is no longer built into the firmware.\r
+\r
+ EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only\r
+ avoids exact duplicates.\r
+**/\r
+STATIC\r
+VOID\r
+RemoveStaleFvFileOptions (\r
+ VOID\r
+ )\r
+{\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN BootOptionCount;\r
+ UINTN Index;\r
+\r
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,\r
+ LoadOptionTypeBoot);\r
+\r
+ for (Index = 0; Index < BootOptionCount; ++Index) {\r
+ EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE FvHandle;\r
+\r
+ //\r
+ // If the device path starts with neither MemoryMapped(...) nor Fv(...),\r
+ // then keep the boot option.\r
+ //\r
+ Node1 = BootOptions[Index].FilePath;\r
+ if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&\r
+ DevicePathSubType (Node1) == HW_MEMMAP_DP) &&\r
+ !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&\r
+ DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // If the second device path node is not FvFile(...), then keep the boot\r
+ // option.\r
+ //\r
+ Node2 = NextDevicePathNode (Node1);\r
+ if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||\r
+ DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Locate the Firmware Volume2 protocol instance that is denoted by the\r
+ // boot option. If this lookup fails (i.e., the boot option references a\r
+ // firmware volume that doesn't exist), then we'll proceed to delete the\r
+ // boot option.\r
+ //\r
+ SearchNode = Node1;\r
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,\r
+ &SearchNode, &FvHandle);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The firmware volume was found; now let's see if it contains the FvFile\r
+ // identified by GUID.\r
+ //\r
+ EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;\r
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;\r
+ UINTN BufferSize;\r
+ EFI_FV_FILETYPE FoundType;\r
+ EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
+ UINT32 AuthenticationStatus;\r
+\r
+ Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,\r
+ (VOID **)&FvProtocol);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;\r
+ //\r
+ // Buffer==NULL means we request metadata only: BufferSize, FoundType,\r
+ // FileAttributes.\r
+ //\r
+ Status = FvProtocol->ReadFile (\r
+ FvProtocol,\r
+ &FvFileNode->FvFileName, // NameGuid\r
+ NULL, // Buffer\r
+ &BufferSize,\r
+ &FoundType,\r
+ &FileAttributes,\r
+ &AuthenticationStatus\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // The FvFile was found. Keep the boot option.\r
+ //\r
+ continue;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Delete the boot option.\r
+ //\r
+ Status = EfiBootManagerDeleteLoadOptionVariable (\r
+ BootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
+ DEBUG_CODE (\r
+ CHAR16 *DevicePathString;\r
+\r
+ DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,\r
+ FALSE, FALSE);\r
+ DEBUG ((\r
+ EFI_ERROR (Status) ? EFI_D_WARN : EFI_D_VERBOSE,\r
+ "%a: removing stale Boot#%04x %s: %r\n",\r
+ __FUNCTION__,\r
+ (UINT32)BootOptions[Index].OptionNumber,\r
+ DevicePathString == NULL ? L"<unavailable>" : DevicePathString,\r
+ Status\r
+ ));\r
+ if (DevicePathString != NULL) {\r
+ FreePool (DevicePathString);\r
+ }\r
+ );\r
+ }\r
+\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+}\r
+\r
+\r
STATIC\r
VOID\r
PlatformRegisterOptionsAndKeys (\r
NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL\r
);\r
ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
- //\r
- // Register UEFI Shell\r
- //\r
- PlatformRegisterFvBootOption (\r
- PcdGetPtr (PcdShellFile), L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
- );\r
}\r
\r
\r
VOID\r
)\r
{\r
+ RETURN_STATUS PcdStatus;\r
+\r
//\r
// Signal EndOfDxe PI Event\r
//\r
EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
\r
+ //\r
+ // Dispatch deferred images after EndOfDxe event.\r
+ //\r
+ EfiBootManagerDispatchDeferredImages ();\r
+\r
//\r
// Locate the PCI root bridges and make the PCI bus driver connect each,\r
// non-recursively. This will produce a number of child handles with PciIo on\r
//\r
// Set the front page timeout from the QEMU configuration.\r
//\r
- PcdSet16 (PcdPlatformBootTimeOut, GetFrontPageTimeoutFromQemu ());\r
+ PcdStatus = PcdSet16S (PcdPlatformBootTimeOut,\r
+ GetFrontPageTimeoutFromQemu ());\r
+ ASSERT_RETURN_ERROR (PcdStatus);\r
\r
//\r
// Register platform-specific boot options and keyboard shortcuts.\r
//\r
PlatformRegisterOptionsAndKeys ();\r
+\r
+ //\r
+ // At this point, VIRTIO_DEVICE_PROTOCOL instances exist only for Virtio MMIO\r
+ // transports. Install EFI_RNG_PROTOCOL instances on Virtio MMIO RNG devices.\r
+ //\r
+ FilterAndProcess (&gVirtioDeviceProtocolGuid, IsVirtioRng, Connect);\r
+\r
+ //\r
+ // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
+ // instances on Virtio PCI RNG devices.\r
+ //\r
+ FilterAndProcess (&gEfiPciIoProtocolGuid, IsVirtioPciRng, Connect);\r
}\r
\r
/**\r
VOID\r
)\r
{\r
+ RETURN_STATUS Status;\r
+\r
//\r
// Show the splash screen.\r
//\r
- EnableQuietBoot (PcdGetPtr (PcdLogoFile));\r
+ BootLogoEnableLogo ();\r
\r
//\r
- // Connect the rest of the devices.\r
+ // Process QEMU's -kernel command line option. The kernel booted this way\r
+ // will receive ACPI tables: in PlatformBootManagerBeforeConsole(), we\r
+ // connected any and all PCI root bridges, and then signaled the ACPI\r
+ // platform driver.\r
//\r
- BdsLibConnectAll ();\r
+ TryRunningQemuKernel ();\r
\r
//\r
- // Process QEMU's -kernel command line option. Note that the kernel booted\r
- // this way should receive ACPI tables, which is why we connect all devices\r
- // first (see above) -- PCI enumeration blocks ACPI table installation, if\r
- // there is a PCI host.\r
+ // Connect the purported boot devices.\r
//\r
- TryRunningQemuKernel ();\r
+ Status = ConnectDevicesFromQemu ();\r
+ if (RETURN_ERROR (Status)) {\r
+ //\r
+ // Connect the rest of the devices.\r
+ //\r
+ EfiBootManagerConnectAll ();\r
+ }\r
\r
- BdsLibEnumerateAllBootOption (BootOptionList);\r
- SetBootOrderFromQemu (BootOptionList);\r
//\r
- // The BootOrder variable may have changed, reload the in-memory list with\r
- // it.\r
+ // Enumerate all possible boot options, then filter and reorder them based on\r
+ // the QEMU configuration.\r
//\r
- BdsLibBuildOptionFromVar (BootOptionList, L"BootOrder");\r
-}\r
-\r
-/**\r
- Hook point after a boot attempt succeeds. We don't expect a boot option to\r
- return, so the UEFI 2.0 specification defines that you will default to an\r
- interactive mode and stop processing the BootOrder list in this case. This\r
- is also a platform implementation and can be customized by IBV/OEM.\r
-\r
- @param Option Pointer to Boot Option that succeeded to boot.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsBootSuccess (\r
- IN BDS_COMMON_OPTION *Option\r
- )\r
-{\r
-}\r
+ EfiBootManagerRefreshAllBootOption ();\r
\r
-/**\r
- Hook point after a boot attempt fails.\r
-\r
- @param Option Pointer to Boot Option that failed to boot.\r
- @param Status Status returned from failed boot.\r
- @param ExitData Exit data returned from failed boot.\r
- @param ExitDataSize Exit data size returned from failed boot.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsBootFail (\r
- IN BDS_COMMON_OPTION *Option,\r
- IN EFI_STATUS Status,\r
- IN CHAR16 *ExitData,\r
- IN UINTN ExitDataSize\r
- )\r
-{\r
-}\r
+ //\r
+ // Register UEFI Shell\r
+ //\r
+ PlatformRegisterFvBootOption (\r
+ &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
+ );\r
\r
-/**\r
- This function locks platform flash that is not allowed to be updated during normal boot path.\r
- The flash layout is platform specific.\r
-**/\r
-VOID\r
-EFIAPI\r
-PlatformBdsLockNonUpdatableFlash (\r
- VOID\r
- )\r
-{\r
- return;\r
+ RemoveStaleFvFileOptions ();\r
+ SetBootOrderFromQemu ();\r
}\r
\r
/**\r
UINT16 TimeoutRemain\r
)\r
{\r
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
+ UINT16 Timeout;\r
+\r
+ Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
+\r
+ Black.Raw = 0x00000000;\r
+ White.Raw = 0x00FFFFFF;\r
+\r
+ BootLogoUpdateProgress (\r
+ White.Pixel,\r
+ Black.Pixel,\r
+ L"Start boot option",\r
+ White.Pixel,\r
+ (Timeout - TimeoutRemain) * 100 / Timeout,\r
+ 0\r
+ );\r
}\r