diff options
author | Ruiyu Ni <ruiyu.ni@intel.com> | 2015-05-06 04:47:02 +0000 |
---|---|---|
committer | niruiyu <niruiyu@Edk2> | 2015-05-06 04:47:02 +0000 |
commit | 1d1122292572cbaf73d8e8d2d39d01a8a28da76a (patch) | |
tree | 701b29d2a1f1b178a77b45569793b85f595222ca | |
parent | 58183d391c1efd4488a64412be28ab9bfd25a7db (diff) | |
download | edk2-1d1122292572cbaf73d8e8d2d39d01a8a28da76a.tar.gz edk2-1d1122292572cbaf73d8e8d2d39d01a8a28da76a.tar.bz2 edk2-1d1122292572cbaf73d8e8d2d39d01a8a28da76a.zip |
MdeModulePkg: Add UefiBootManagerLib
UefiBootManagerLib provides:
load option library functions;
hot key library functions;
boot library functions;
connect and disconnect library functions;
driver health library functions.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17327 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r-- | MdeModulePkg/Include/Library/UefiBootManagerLib.h | 657 | ||||
-rw-r--r-- | MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf | 4 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c | 2278 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c | 318 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c | 748 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c | 578 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c | 1101 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c | 982 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c | 507 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c | 358 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h | 364 | ||||
-rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf | 110 | ||||
-rw-r--r-- | MdeModulePkg/MdeModulePkg.dec | 14 | ||||
-rw-r--r-- | MdeModulePkg/MdeModulePkg.dsc | 2 |
14 files changed, 8018 insertions, 3 deletions
diff --git a/MdeModulePkg/Include/Library/UefiBootManagerLib.h b/MdeModulePkg/Include/Library/UefiBootManagerLib.h new file mode 100644 index 0000000000..9b08364aaf --- /dev/null +++ b/MdeModulePkg/Include/Library/UefiBootManagerLib.h @@ -0,0 +1,657 @@ +/** @file + Provide Boot Manager related library APIs. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef _UEFI_BOOT_MANAGER_LIB_H_ +#define _UEFI_BOOT_MANAGER_LIB_H_ + +#include <Protocol/DriverHealth.h> +#include <Library/SortLib.h> + +// +// Boot Manager load option library functions. +// + +// +// Load Option Type +// +typedef enum { + LoadOptionTypeBoot, + LoadOptionTypeDriver, + LoadOptionTypeMax +} EFI_BOOT_MANAGER_LOAD_OPTION_TYPE; + +typedef enum { + LoadOptionNumberMax = 0x10000, + LoadOptionNumberUnassigned = LoadOptionNumberMax +} EFI_BOOT_MANAGER_LOAD_OPTION_NUMBER; + +// +// Common structure definition for DriverOption and BootOption +// +typedef struct { + // + // Data read from UEFI NV variables + // + UINTN OptionNumber; // #### numerical value, could be LoadOptionNumberUnassigned + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; // LoadOptionTypeBoot or LoadOptionTypeDriver + UINT32 Attributes; // Load Option Attributes + CHAR16 *Description; // Load Option Description + EFI_DEVICE_PATH_PROTOCOL *FilePath; // Load Option Device Path + UINT8 *OptionalData; // Load Option optional data to pass into image + UINT32 OptionalDataSize; // Load Option size of OptionalData + + // + // Used at runtime + // + EFI_STATUS Status; // Status returned from boot attempt gBS->StartImage () + CHAR16 *ExitData; // Exit data returned from gBS->StartImage () + UINTN ExitDataSize; // Size of ExitData +} EFI_BOOT_MANAGER_LOAD_OPTION; + +/** + Returns an array of load options based on the EFI variable + L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it. + #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. + + @param LoadOptionCount Returns number of entries in the array. + @param LoadOptionType The type of the load option. + + @retval NULL No load options exist. + @retval !NULL Array of load option entries. + +**/ +EFI_BOOT_MANAGER_LOAD_OPTION * +EFIAPI +EfiBootManagerGetLoadOptions ( + OUT UINTN *LoadOptionCount, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType + ); + +/** + Free an array of load options returned from EfiBootManagerGetLoadOptions(). + + @param LoadOptions Pointer to the array of load options to free. + @param LoadOptionCount Number of array entries in LoadOptions. + + @return EFI_SUCCESS LoadOptions was freed. + @return EFI_INVALID_PARAMETER LoadOptions is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeLoadOptions ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions, + IN UINTN LoadOptionCount + ); + +/** + Initialize a load option. + + @param Option Pointer to the load option to be initialized. + @param OptionNumber Option number of the load option. + @param OptionType Type of the load option. + @param Attributes Attributes of the load option. + @param Description Description of the load option. + @param FilePath Device path of the load option. + @param OptionalData Optional data of the load option. + @param OptionalDataSize Size of the optional data of the load option. + + @retval EFI_SUCCESS The load option was initialized successfully. + @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerInitializeLoadOption ( + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN OptionNumber, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, + IN UINT32 Attributes, + IN CHAR16 *Description, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN UINT8 *OptionalData, + IN UINT32 OptionalDataSize + ); + +/** + Free a load option created by EfiBootManagerInitializeLoadOption() + or EfiBootManagerVariableToLoadOption(). + + @param LoadOption Pointer to the load option to free. + CONCERN: Check Boot#### instead of BootOrder, optimize, spec clarify + @return EFI_SUCCESS LoadOption was freed. + @return EFI_INVALID_PARAMETER LoadOption is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeLoadOption ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ); + +/** + Initialize the load option from the VariableName. + + @param VariableName EFI Variable name which could be Boot#### or + Driver#### + @param LoadOption Pointer to the load option to be initialized + + @retval EFI_SUCCESS The option was created + @retval EFI_INVALID_PARAMETER VariableName or LoadOption is NULL. + @retval EFI_NOT_FOUND The variable specified by VariableName cannot be found. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerVariableToLoadOption ( + IN CHAR16 *VariableName, + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ); + +/** + Create the Boot#### or Driver#### variable from the load option. + + @param LoadOption Pointer to the load option. + + @retval EFI_SUCCESS The variable was created. + @retval Others Error status returned by RT->SetVariable. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerLoadOptionToVariable ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ); + +/** + This function will update the Boot####/Driver#### and the BootOrder/DriverOrder + to add a new load option. + + @param Option Pointer to load option to add. + @param Position Position of the new load option to put in the BootOrder/DriverOrder. + + @retval EFI_SUCCESS The load option has been successfully added. + @retval Others Error status returned by RT->SetVariable. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerAddLoadOptionVariable ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN Position + ); + +/** + Delete the load option according to the OptionNumber and OptionType. + + Only the BootOrder/DriverOrder is updated to remove the reference of the OptionNumber. + + @param OptionNumber Option number of the load option. + @param OptionType Type of the load option. + + @retval EFI_NOT_FOUND The load option cannot be found. + @retval EFI_SUCCESS The load option was deleted. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerDeleteLoadOptionVariable ( + IN UINTN OptionNumber, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType + ); + +/** + Sort the load options. The DriverOrder/BootOrder variables will be re-created to + reflect the new order. + + @param OptionType The type of the load option. + @param Comparator The comparator function pointer. +**/ +VOID +EFIAPI +EfiBootManagerSortLoadOptionVariable ( + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, + IN SORT_COMPARE CompareFunction + ); + +// +// Boot Manager hot key library functions. +// + +#pragma pack(1) +/// +/// EFI Key Option. +/// +typedef struct { + /// + /// Specifies options about how the key will be processed. + /// + EFI_BOOT_KEY_DATA KeyData; + /// + /// The CRC-32 which should match the CRC-32 of the entire EFI_LOAD_OPTION to + /// which BootOption refers. If the CRC-32s do not match this value, then this key + /// option is ignored. + /// + UINT32 BootOptionCrc; + /// + /// The Boot#### option which will be invoked if this key is pressed and the boot option + /// is active (LOAD_OPTION_ACTIVE is set). + /// + UINT16 BootOption; + /// + /// The key codes to compare against those returned by the + /// EFI_SIMPLE_TEXT_INPUT and EFI_SIMPLE_TEXT_INPUT_EX protocols. + /// The number of key codes (0-3) is specified by the EFI_KEY_CODE_COUNT field in KeyOptions. + /// + EFI_INPUT_KEY Keys[3]; + UINT16 OptionNumber; +} EFI_BOOT_MANAGER_KEY_OPTION; +#pragma pack() + +/** + Start the hot key service so that the key press can trigger the boot option. + + @param HotkeyTriggered Return the waitable event and it will be signaled + when a valid hot key is pressed. + + @retval EFI_SUCCESS The hot key service is started. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerStartHotkeyService ( + IN EFI_EVENT *HotkeyTriggered + ); + +// +// Modifier for EfiBootManagerAddKeyOptionVariable and EfiBootManagerDeleteKeyOptionVariable +// +#define EFI_BOOT_MANAGER_SHIFT_PRESSED 0x00000001 +#define EFI_BOOT_MANAGER_CONTROL_PRESSED 0x00000002 +#define EFI_BOOT_MANAGER_ALT_PRESSED 0x00000004 +#define EFI_BOOT_MANAGER_LOGO_PRESSED 0x00000008 +#define EFI_BOOT_MANAGER_MENU_KEY_PRESSED 0x00000010 +#define EFI_BOOT_MANAGER_SYS_REQ_PRESSED 0x00000020 + +/** + Add the key option. + It adds the key option variable and the key option takes affect immediately. + + @param AddedOption Return the added key option. + @param BootOptionNumber The boot option number for the key option. + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS The key option is added. + @retval EFI_ALREADY_STARTED The hot key is already used by certain key option. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerAddKeyOptionVariable ( + OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption, OPTIONAL + IN UINT16 BootOptionNumber, + IN UINT32 Modifier, + ... + ); + +/** + Delete the Key Option variable and unregister the hot key + + @param DeletedOption Return the deleted key options. + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS The key option is deleted. + @retval EFI_NOT_FOUND The key option cannot be found. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerDeleteKeyOptionVariable ( + IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL + IN UINT32 Modifier, + ... + ); + +/** + Register the key option to exit the waiting of the Boot Manager timeout. + Platform should ensure that the continue key option isn't conflict with + other boot key options. + + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS Successfully register the continue key option. + @retval EFI_ALREADY_STARTED The continue key option is already registered. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerRegisterContinueKeyOption ( + IN UINT32 Modifier, + ... + ); + +/** + Try to boot the boot option triggered by hot key. +**/ +VOID +EFIAPI +EfiBootManagerHotkeyBoot ( + VOID + ); +// +// Boot Manager boot library functions. +// + +/** + The function creates boot options for all possible bootable medias in the following order: + 1. Removable BlockIo - The boot option only points to the removable media + device, like USB key, DVD, Floppy etc. + 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, + like HardDisk. + 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting + SimpleFileSystem Protocol, but not supporting BlockIo + protocol. + 4. LoadFile - The boot option points to the media supporting + LoadFile protocol. + Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior + + The function won't delete the boot option not added by itself. +**/ +VOID +EFIAPI +EfiBootManagerRefreshAllBootOption ( + VOID + ); + +/** + Attempt to boot the EFI boot option. This routine sets L"BootCurent" and + signals the EFI ready to boot event. If the device path for the option starts + with a BBS device path a legacy boot is attempted. Short form device paths are + also supported via this rountine. A device path starting with + MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, MSG_USB_CLASS_DP gets expaned out + to find the first device that matches. If the BootOption Device Path + fails the removable media boot algorithm is attempted (\EFI\BOOTIA32.EFI, + \EFI\BOOTX64.EFI,... only one file type is tried per processor type) + + @param BootOption Boot Option to try and boot. + On return, BootOption->Status contains the boot status: + EFI_SUCCESS BootOption was booted + EFI_UNSUPPORTED BootOption isn't supported. + EFI_NOT_FOUND The BootOption was not found on the system + Others BootOption failed with this error status + +**/ +VOID +EFIAPI +EfiBootManagerBoot ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ); + +/** + Return the Boot Manager Menu. + + @param BootOption Return the Boot Manager Menu. + + @retval EFI_SUCCESS The Boot Manager Menu is successfully returned. + @retval EFI_NOT_FOUND The Boot Manager Menu is not found. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerGetBootManagerMenu ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ); + +/** + The function enumerates all the legacy boot options, creates them and + registers them in the BootOrder variable. +**/ +typedef +VOID +(EFIAPI *EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION) ( + VOID + ); + +/** + The function boots a legacy boot option. +**/ +typedef +VOID +(EFIAPI *EFI_BOOT_MANAGER_LEGACY_BOOT) ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ); + +/** + The function registers the legacy boot support capabilities. + + @param RefreshLegacyBootOption The function pointer to create all the legacy boot options. + @param LegacyBoot The function pointer to boot the legacy boot option. +**/ +VOID +EFIAPI +EfiBootManagerRegisterLegacyBootSupport ( + EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption, + EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot + ); + + +// +// Boot Manager connect and disconnect library functions +// + +/** + This function will connect all the system driver to controller + first, and then special connect the default console, this make + sure all the system controller available and the platform default + console connected. +**/ +VOID +EFIAPI +EfiBootManagerConnectAll ( + VOID + ); + +/** + This function will create all handles associate with every device + path node. If the handle associate with one device path node can not + be created successfully, then still give one chance to do the dispatch, + which load the missing drivers if possible. + + @param DevicePathToConnect The device path which will be connected, it CANNOT be + a multi-instance device path + @param MatchingHandle Return the controller handle closest to the DevicePathToConnect + + @retval EFI_INVALID_PARAMETER DevicePathToConnect is NULL. + @retval EFI_NOT_FOUND Failed to create all handles associate with every device path node. + @retval EFI_SUCCESS Successful to create all handles associate with every device path node. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePathToConnect, + OUT EFI_HANDLE *MatchingHandle OPTIONAL + ); + +/** + This function will disconnect all current system handles. + + gBS->DisconnectController() is invoked for each handle exists in system handle buffer. + If handle is a bus type handle, all childrens also are disconnected recursively by + gBS->DisconnectController(). +**/ +VOID +EFIAPI +EfiBootManagerDisconnectAll ( + VOID + ); + + +// +// Boot Manager console library functions +// + +typedef enum { + ConIn, + ConOut, + ErrOut, + ConInDev, + ConOutDev, + ErrOutDev, + ConsoleTypeMax +} CONSOLE_TYPE; + +/** + This function will connect all the console devices base on the console + device variable ConIn, ConOut and ErrOut. +**/ +VOID +EFIAPI +EfiBootManagerConnectAllDefaultConsoles ( + VOID + ); + +/** + This function updates the console variable based on ConVarName. It can + add or remove one specific console device path from the variable + + @param ConsoleType ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev. + @param CustomizedConDevicePath The console device path to be added to + the console variable. Cannot be multi-instance. + @param ExclusiveDevicePath The console device path to be removed + from the console variable. Cannot be multi-instance. + + @retval EFI_UNSUPPORTED The added device path is the same as a removed one. + @retval EFI_SUCCESS Successfully added or removed the device path from the + console variable. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerUpdateConsoleVariable ( + IN CONSOLE_TYPE ConsoleType, + IN EFI_DEVICE_PATH_PROTOCOL *CustomizedConDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *ExclusiveDevicePath + ); + +/** + Connect the console device base on the variable ConVarName, if + device path of the ConVarName is multi-instance device path, if + anyone of the instances is connected success, then this function + will return success. + + @param ConsoleType ConIn, ConOut or ErrOut. + + @retval EFI_NOT_FOUND There is not any console devices connected + success + @retval EFI_SUCCESS Success connect any one instance of the console + device path base on the variable ConVarName. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectConsoleVariable ( + IN CONSOLE_TYPE ConsoleType + ); + +/** + Query all the children of VideoController and return the device paths of all the + children that support GraphicsOutput protocol. + + @param VideoController PCI handle of video controller. + + @return Device paths of all the children that support GraphicsOutput protocol. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiBootManagerGetGopDevicePath ( + IN EFI_HANDLE VideoController + ); + +/** + Connect the platform active active video controller. + + @param VideoController PCI handle of video controller. + + @retval EFI_NOT_FOUND There is no active video controller. + @retval EFI_SUCCESS The video controller is connected. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectVideoController ( + EFI_HANDLE VideoController OPTIONAL + ); + +// +// Boot Manager driver health library functions. +// + +typedef struct { + EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth; + + /// + /// Driver relative handles + /// + EFI_HANDLE DriverHealthHandle; + EFI_HANDLE ControllerHandle; + EFI_HANDLE ChildHandle; + + /// + /// Driver health messages of the specify Driver + /// + EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList; + + /// + /// HII relative handles + /// + EFI_HII_HANDLE HiiHandle; + + /// + /// Driver Health status + /// + EFI_DRIVER_HEALTH_STATUS HealthStatus; +} EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO; + +/** + Return all the Driver Health information. + + When the cumulative health status of all the controllers managed by the + driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such + EFI_DRIVER_HEALTH_PROTOCOL instance. + Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO + entry. Additionally every child controller creates one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver. + + @param Count Return the count of the Driver Health information. + + @retval NULL No Driver Health information is returned. + @retval !NULL Pointer to the Driver Health information array. +**/ +EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO * +EFIAPI +EfiBootManagerGetDriverHealthInfo ( + UINTN *Count + ); + +/** + Free the Driver Health information array. + + @param DriverHealthInfo Pointer to array of the Driver Health information. + @param Count Count of the array. + + @retval EFI_SUCCESS The array is freed. + @retval EFI_INVALID_PARAMETER The array is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeDriverHealthInfo ( + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo, + UINTN Count + ); + +#endif diff --git a/MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf b/MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf index e372248fef..4aade32cf2 100644 --- a/MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf +++ b/MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf @@ -1,7 +1,7 @@ ## @file
# Library used for sorting routines.
#
-# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved. <BR>
+# Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved. <BR> #
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
@@ -19,7 +19,7 @@ FILE_GUID = 03F3331B-F12D-494f-BF37-E55A657F2497
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
- LIBRARY_CLASS = SortLib|UEFI_APPLICATION UEFI_DRIVER
+ LIBRARY_CLASS = SortLib|DXE_DRIVER UEFI_APPLICATION UEFI_DRIVER #
# VALID_ARCHITECTURES = IA32 X64 IPF EBC
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c new file mode 100644 index 0000000000..ddda9ae605 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -0,0 +1,2278 @@ +/** @file + Library functions which relates with booting. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +#define VENDOR_IDENTIFICATION_OFFSET 3 +#define VENDOR_IDENTIFICATION_LENGTH 8 +#define PRODUCT_IDENTIFICATION_OFFSET 11 +#define PRODUCT_IDENTIFICATION_LENGTH 16 + +CONST UINT16 mBmUsbLangId = 0x0409; // English +CHAR16 mBmUefiPrefix[] = L"UEFI "; + +EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL; +EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL; + +/// +/// This GUID is used for an EFI Variable that stores the front device pathes +/// for a partial device path that starts with the HD node. +/// +EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } }; +EFI_GUID mBmAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } }; + +/** + The function registers the legacy boot support capabilities. + + @param RefreshLegacyBootOption The function pointer to create all the legacy boot options. + @param LegacyBoot The function pointer to boot the legacy boot option. +**/ +VOID +EFIAPI +EfiBootManagerRegisterLegacyBootSupport ( + EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption, + EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot + ) +{ + mBmRefreshLegacyBootOption = RefreshLegacyBootOption; + mBmLegacyBoot = LegacyBoot; +} + +/** + For a bootable Device path, return its boot type. + + @param DevicePath The bootable device Path to check + + @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node + which HID is floppy device. + @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_ATAPI_DP. + @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_SATA_DP. + @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_SCSI_DP. + @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_USB_DP. + @retval MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node + and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP, + MSG_IPv4_DP or MSG_IPv6_DP. + @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported. + +**/ +BM_BOOT_TYPE +BmBootTypeFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_DEVICE_PATH_PROTOCOL *NextNode; + + ASSERT (DevicePath != NULL); + + for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) { + switch (DevicePathType (Node)) { + + case ACPI_DEVICE_PATH: + if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) { + return BmAcpiFloppyBoot; + } + break; + + case HARDWARE_DEVICE_PATH: + if (DevicePathSubType (Node) == HW_CONTROLLER_DP) { + return BmHardwareDeviceBoot; + } + break; + + case MESSAGING_DEVICE_PATH: + // + // Skip LUN device node + // + NextNode = Node; + do { + NextNode = NextDevicePathNode (NextNode); + } while ( + (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) && + (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP) + ); + + // + // If the device path not only point to driver device, it is not a messaging device path, + // + if (!IsDevicePathEndType (NextNode)) { + break; + } + + switch (DevicePathSubType (Node)) { + case MSG_ATAPI_DP: + return BmMessageAtapiBoot; + break; + + case MSG_SATA_DP: + return BmMessageSataBoot; + break; + + case MSG_USB_DP: + return BmMessageUsbBoot; + break; + + case MSG_SCSI_DP: + return BmMessageScsiBoot; + break; + + case MSG_MAC_ADDR_DP: + case MSG_VLAN_DP: + case MSG_IPv4_DP: + case MSG_IPv6_DP: + return BmMessageNetworkBoot; + break; + } + } + } + + return BmMiscBoot; +} + +/** + Free old buffer and reuse the pointer to return new buffer. + + @param Orig Pointer to the old buffer. + @param New Pointer to the new buffer. +**/ +VOID +BmFreeAndSet ( + VOID **Orig, + VOID *New + ) +{ + FreePool (*Orig); + *Orig = New; +} + +/** + Find the boot option in the NV storage and return the option number. + + @param OptionToFind Boot option to be checked. + + @return The option number of the found boot option. + +**/ +UINTN +BmFindBootOptionInVariable ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + UINTN OptionNumber; + CHAR16 OptionName[sizeof ("Boot####")]; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + UINTN Index; + + OptionNumber = LoadOptionNumberUnassigned; + + // + // Try to match the variable exactly if the option number is assigned + // + if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) { + UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToFind->OptionNumber); + Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption); + + if (!EFI_ERROR (Status)) { + ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber); + if ((OptionToFind->Attributes == BootOption.Attributes) && + (StrCmp (OptionToFind->Description, BootOption.Description) == 0) && + (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) && + (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) && + (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0) + ) { + OptionNumber = OptionToFind->OptionNumber; + } + EfiBootManagerFreeLoadOption (&BootOption); + } + } + + // + // The option number assigned is either incorrect or unassigned. + // + if (OptionNumber == LoadOptionNumberUnassigned) { + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + + Index = BmFindLoadOption (OptionToFind, BootOptions, BootOptionCount); + if (Index != -1) { + OptionNumber = BootOptions[Index].OptionNumber; + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + } + + return OptionNumber; +} + +/** + According to a file guild, check a Fv file device path is valid. If it is invalid, + try to return the valid device path. + FV address maybe changes for memory layout adjust from time to time, use this function + could promise the Fv file device path is right. + + @param DevicePath The Fv file device path to be fixed up. + +**/ +VOID +BmFixupMemmapFvFilePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_HANDLE FvHandle; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + UINTN Size; + EFI_FV_FILETYPE Type; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINT32 AuthenticationStatus; + UINTN FvHandleCount; + EFI_HANDLE *FvHandleBuffer; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + + Node = *DevicePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); + if (!EFI_ERROR (Status)) { + Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Fv); + ASSERT_EFI_ERROR (Status); + + Status = Fv->ReadFile ( + Fv, + EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node), + NULL, + &Size, + &Type, + &Attributes, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + BmFreeAndSet ((VOID **) DevicePath, NULL); + } + return; + } + + + Node = NextDevicePathNode (DevicePath); + + // + // Firstly find the FV file in current FV + // + gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &LoadedImage + ); + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), Node); + BmFixupMemmapFvFilePath (&NewDevicePath); + + if (NewDevicePath != NULL) { + BmFreeAndSet ((VOID **) DevicePath, NewDevicePath); + return; + } + + // + // Secondly find the FV file in all other FVs + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &FvHandleCount, + &FvHandleBuffer + ); + for (Index = 0; Index < FvHandleCount; Index++) { + if (FvHandleBuffer[Index] == LoadedImage->DeviceHandle) { + // + // Skip current FV + // + continue; + } + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandleBuffer[Index]), Node); + BmFixupMemmapFvFilePath (&NewDevicePath); + + if (NewDevicePath != NULL) { + BmFreeAndSet ((VOID **) DevicePath, NewDevicePath); + return; + } + } +} + +/** + Check if it's of Fv file device path type. + + The function doesn't garentee the device path points to existing Fv file. + + @param DevicePath Input device path info. + + @retval TRUE The device path is of Fv file device path type. + @retval FALSE The device path isn't of Fv file device path type. +**/ +BOOLEAN +BmIsMemmapFvFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *FileNode; + + if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) { + FileNode = NextDevicePathNode (DevicePath); + if ((DevicePathType (FileNode) == MEDIA_DEVICE_PATH) && (DevicePathSubType (FileNode) == MEDIA_PIWG_FW_FILE_DP)) { + return IsDevicePathEnd (NextDevicePathNode (FileNode)); + } + } + + return FALSE; +} + +/** + Check whether a USB device match the specified USB Class device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbClass The USB Class device path to match. + + @retval TRUE The USB device match the USB Class device path. + @retval FALSE The USB device does not match the USB Class device path. + +**/ +BOOLEAN +BmMatchUsbClass ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_CLASS_DEVICE_PATH *UsbClass + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; + + if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){ + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((UsbClass->VendorId != 0xffff) && + (UsbClass->VendorId != DevDesc.IdVendor)) { + return FALSE; + } + + if ((UsbClass->ProductId != 0xffff) && + (UsbClass->ProductId != DevDesc.IdProduct)) { + return FALSE; + } + + DeviceClass = DevDesc.DeviceClass; + DeviceSubClass = DevDesc.DeviceSubClass; + DeviceProtocol = DevDesc.DeviceProtocol; + if (DeviceClass == 0) { + // + // If Class in Device Descriptor is set to 0, use the Class, SubClass and + // Protocol in Interface Descriptor instead. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + + DeviceClass = IfDesc.InterfaceClass; + DeviceSubClass = IfDesc.InterfaceSubClass; + DeviceProtocol = IfDesc.InterfaceProtocol; + } + + // + // Check Class, SubClass and Protocol. + // + if ((UsbClass->DeviceClass != 0xff) && + (UsbClass->DeviceClass != DeviceClass)) { + return FALSE; + } + + if ((UsbClass->DeviceSubClass != 0xff) && + (UsbClass->DeviceSubClass != DeviceSubClass)) { + return FALSE; + } + + if ((UsbClass->DeviceProtocol != 0xff) && + (UsbClass->DeviceProtocol != DeviceProtocol)) { + return FALSE; + } + + return TRUE; +} + +/** + Eliminate the extra spaces in the Str to one space. + + @param Str Input string info. +**/ +VOID +BmEliminateExtraSpaces ( + IN CHAR16 *Str + ) +{ + UINTN Index; + UINTN ActualIndex; + + for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) { + if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) { + Str[ActualIndex++] = Str[Index]; + } + } + Str[ActualIndex] = L'\0'; +} + +/** + Try to get the controller's ATA/ATAPI description. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetDescriptionFromDiskInfo ( + IN EFI_HANDLE Handle + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_DISK_INFO_PROTOCOL *DiskInfo; + UINT32 BufferSize; + EFI_ATAPI_IDENTIFY_DATA IdentifyData; + EFI_SCSI_INQUIRY_DATA InquiryData; + CHAR16 *Description; + UINTN Length; + CONST UINTN ModelNameLength = 40; + CONST UINTN SerialNumberLength = 20; + CHAR8 *StrPtr; + UINT8 Temp; + + Description = NULL; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiDiskInfoProtocolGuid, + (VOID **) &DiskInfo + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || + CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) { + BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA); + Status = DiskInfo->Identify ( + DiskInfo, + &IdentifyData, + &BufferSize + ); + if (!EFI_ERROR (Status)) { + Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16)); + ASSERT (Description != NULL); + for (Index = 0; Index + 1 < ModelNameLength; Index += 2) { + Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1]; + Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index]; + } + + Length = Index; + Description[Length++] = L' '; + + for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) { + Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1]; + Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index]; + } + Length += Index; + Description[Length++] = L'\0'; + ASSERT (Length == ModelNameLength + SerialNumberLength + 2); + + BmEliminateExtraSpaces (Description); + } + } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) { + BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA); + Status = DiskInfo->Inquiry ( + DiskInfo, + &InquiryData, + &BufferSize + ); + if (!EFI_ERROR (Status)) { + Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16)); + ASSERT (Description != NULL); + + // + // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification + // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, + // Here combine the vendor identification and product identification to the description. + // + StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]); + Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH]; + StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0'; + AsciiStrToUnicodeStr (StrPtr, Description); + StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp; + + // + // Add one space at the middle of vendor information and product information. + // + Description[VENDOR_IDENTIFICATION_LENGTH] = L' '; + + StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]); + StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0'; + AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1); + + BmEliminateExtraSpaces (Description); + } + } + + return Description; +} + +/** + Try to get the controller's USB description. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetUsbDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_USB_IO_PROTOCOL *UsbIo; + CHAR16 NullChar; + CHAR16 *Manufacturer; + CHAR16 *Product; + CHAR16 *SerialNumber; + CHAR16 *Description; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + + Status = gBS->HandleProtocol ( + Handle, + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + NullChar = L'\0'; + + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return NULL; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrManufacturer, + &Manufacturer + ); + if (EFI_ERROR (Status)) { + Manufacturer = &NullChar; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrProduct, + &Product + ); + if (EFI_ERROR (Status)) { + Product = &NullChar; + } + + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + mBmUsbLangId, + DevDesc.StrSerialNumber, + &SerialNumber + ); + if (EFI_ERROR (Status)) { + SerialNumber = &NullChar; + } + + if ((Manufacturer == &NullChar) && + (Product == &NullChar) && + (SerialNumber == &NullChar) + ) { + return NULL; + } + + Description = AllocateZeroPool (StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber)); + ASSERT (Description != NULL); + StrCat (Description, Manufacturer); + StrCat (Description, L" "); + + StrCat (Description, Product); + StrCat (Description, L" "); + + StrCat (Description, SerialNumber); + + if (Manufacturer != &NullChar) { + FreePool (Manufacturer); + } + if (Product != &NullChar) { + FreePool (Product); + } + if (SerialNumber != &NullChar) { + FreePool (SerialNumber); + } + + BmEliminateExtraSpaces (Description); + + return Description; +} + +/** + Return the boot description for the controller based on the type. + + @param Handle Controller handle. + + @return The description string. +**/ +CHAR16 * +BmGetMiscDescription ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + CHAR16 *Description; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + switch (BmBootTypeFromDevicePath (DevicePathFromHandle (Handle))) { + case BmAcpiFloppyBoot: + Description = L"Floppy"; + break; + + case BmMessageAtapiBoot: + case BmMessageSataBoot: + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + ASSERT_EFI_ERROR (Status); + // + // Assume a removable SATA device should be the DVD/CD device + // + Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive"; + break; + + case BmMessageUsbBoot: + Description = L"USB Device"; + break; + + case BmMessageScsiBoot: + Description = L"SCSI Device"; + break; + + case BmHardwareDeviceBoot: + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + if (!EFI_ERROR (Status)) { + Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive"; + } else { + Description = L"Misc Device"; + } + break; + + default: + Description = L"Misc Device"; + break; + } + + return AllocateCopyPool (StrSize (Description), Description); +} + +BM_GET_BOOT_DESCRIPTION mBmGetBootDescription[] = { + BmGetUsbDescription, + BmGetDescriptionFromDiskInfo, + BmGetMiscDescription +}; + +/** + Check whether a USB device match the specified USB WWID device path. This + function follows "Load Option Processing" behavior in UEFI specification. + + @param UsbIo USB I/O protocol associated with the USB device. + @param UsbWwid The USB WWID device path to match. + + @retval TRUE The USB device match the USB WWID device path. + @retval FALSE The USB device does not match the USB WWID device path. + +**/ +BOOLEAN +BmMatchUsbWwid ( + IN EFI_USB_IO_PROTOCOL *UsbIo, + IN USB_WWID_DEVICE_PATH *UsbWwid + ) +{ + EFI_STATUS Status; + EFI_USB_DEVICE_DESCRIPTOR DevDesc; + EFI_USB_INTERFACE_DESCRIPTOR IfDesc; + UINT16 *LangIdTable; + UINT16 TableSize; + UINT16 Index; + CHAR16 *CompareStr; + UINTN CompareLen; + CHAR16 *SerialNumberStr; + UINTN Length; + + if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) || + (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) { + return FALSE; + } + + // + // Check Vendor Id and Product Id. + // + Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + if ((DevDesc.IdVendor != UsbWwid->VendorId) || + (DevDesc.IdProduct != UsbWwid->ProductId)) { + return FALSE; + } + + // + // Check Interface Number. + // + Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc); + if (EFI_ERROR (Status)) { + return FALSE; + } + if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) { + return FALSE; + } + + // + // Check Serial Number. + // + if (DevDesc.StrSerialNumber == 0) { + return FALSE; + } + + // + // Get all supported languages. + // + TableSize = 0; + LangIdTable = NULL; + Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize); + if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) { + return FALSE; + } + + // + // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters. + // + CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1); + CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16); + if (CompareStr[CompareLen - 1] == L'\0') { + CompareLen--; + } + + // + // Compare serial number in each supported language. + // + for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) { + SerialNumberStr = NULL; + Status = UsbIo->UsbGetStringDescriptor ( + UsbIo, + LangIdTable[Index], + DevDesc.StrSerialNumber, + &SerialNumberStr + ); + if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) { + continue; + } + + Length = StrLen (SerialNumberStr); + if ((Length >= CompareLen) && + (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) { + FreePool (SerialNumberStr); + return TRUE; + } + + FreePool (SerialNumberStr); + } + + return FALSE; +} + +/** + Print the device path info. + + @param DevicePath The device path need to print. + +**/ +VOID +BmPrintDp ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + CHAR16 *Str; + + Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE); + DEBUG ((EFI_D_INFO, "%s", Str)); + if (Str != NULL) { + FreePool (Str); + } +} + +/** + Find a USB device which match the specified short-form device path start with + USB Class or USB WWID device path. If ParentDevicePath is NULL, this function + will search in all USB devices of the platform. If ParentDevicePath is not NULL, + this function will only search in its child devices. + + @param DevicePath The device path that contains USB Class or USB WWID device path. + @param ParentDevicePathSize The length of the device path before the USB Class or + USB WWID device path. + @param UsbIoHandleCount A pointer to the count of the returned USB IO handles. + + @retval NULL The matched USB IO handles cannot be found. + @retval other The matched USB IO handles. + +**/ +EFI_HANDLE * +BmFindUsbDevice ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN UINTN ParentDevicePathSize, + OUT UINTN *UsbIoHandleCount + ) +{ + EFI_STATUS Status; + EFI_HANDLE *UsbIoHandles; + EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath; + EFI_USB_IO_PROTOCOL *UsbIo; + UINTN Index; + UINTN UsbIoDevicePathSize; + BOOLEAN Matched; + + ASSERT (UsbIoHandleCount != NULL); + + // + // Get all UsbIo Handles. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiUsbIoProtocolGuid, + NULL, + UsbIoHandleCount, + &UsbIoHandles + ); + if (EFI_ERROR (Status)) { + *UsbIoHandleCount = 0; + UsbIoHandles = NULL; + } + + for (Index = 0; Index < *UsbIoHandleCount; ) { + // + // Get the Usb IO interface. + // + Status = gBS->HandleProtocol( + UsbIoHandles[Index], + &gEfiUsbIoProtocolGuid, + (VOID **) &UsbIo + ); + UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]); + Matched = FALSE; + if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) { + UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH; + + // + // Compare starting part of UsbIoHandle's device path with ParentDevicePath. + // + if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) { + if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) || + BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) { + Matched = TRUE; + } + } + } + + if (!Matched) { + (*UsbIoHandleCount) --; + CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE)); + } else { + Index++; + } + } + + return UsbIoHandles; +} + +/** + Expand USB Class or USB WWID device path node to be full device path of a USB + device in platform. + + This function support following 4 cases: + 1) Boot Option device path starts with a USB Class or USB WWID device path, + and there is no Media FilePath device path in the end. + In this case, it will follow Removable Media Boot Behavior. + 2) Boot Option device path starts with a USB Class or USB WWID device path, + and ended with Media FilePath device path. + 3) Boot Option device path starts with a full device path to a USB Host Controller, + contains a USB Class or USB WWID device path node, while not ended with Media + FilePath device path. In this case, it will follow Removable Media Boot Behavior. + 4) Boot Option device path starts with a full device path to a USB Host Controller, + contains a USB Class or USB WWID device path node, and ended with Media + FilePath device path. + + @param DevicePath On input, a pointer to an allocated buffer that contains the + file device path. + On output, a pointer to an reallocated buffer that contains + the expanded device path. It would point to NULL if the file + cannot be read. + + @param FileSize A pointer to the file size. + + @retval !NULL The file buffer. + @retval NULL The input device path doesn't point to a valid file. +**/ +VOID * +BmExpandUsbShortFormDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT UINTN *FileSize + ) +{ + UINTN ParentDevicePathSize; + EFI_DEVICE_PATH_PROTOCOL *ShortformNode; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; + EFI_HANDLE *UsbIoHandles; + UINTN UsbIoHandleCount; + UINTN Index; + VOID *FileBuffer; + + // + // Search for USB Class or USB WWID device path node. + // + for ( ShortformNode = *DevicePath + ; !IsDevicePathEnd (ShortformNode) + ; ShortformNode = NextDevicePathNode (ShortformNode) + ) { + if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) || + (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))) { + break; + } + } + ASSERT (!IsDevicePathEnd (ShortformNode)); + + FullDevicePath = NULL; + ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) *DevicePath; + RemainingDevicePath = NextDevicePathNode (ShortformNode); + FileBuffer = NULL; + UsbIoHandles = BmFindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount); + + for (Index = 0; Index < UsbIoHandleCount; Index++) { + FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath); + DEBUG ((EFI_D_INFO, "[Bds] FullDp1[%d]:", Index)); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, "\n")); + FileBuffer = BmLoadEfiBootOption (&FullDevicePath, FileSize); + if (FileBuffer != NULL) { + DEBUG ((EFI_D_INFO, "-->")); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, FileBuffer != NULL ? " - Found\n" : "\n")); + break; + } + } + + if (UsbIoHandles != NULL) { + FreePool (UsbIoHandles); + } + + if (FileBuffer == NULL) { + // + // Boot Option device path starts with USB Class or USB WWID device path. + // For Boot Option device path which doesn't begin with the USB Class or + // USB WWID device path, it's not needed to connect again here. + // + if ((DevicePathType (*DevicePath) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (*DevicePath) == MSG_USB_CLASS_DP) || + (DevicePathSubType (*DevicePath) == MSG_USB_WWID_DP))) { + BmConnectUsbShortFormDevicePath (*DevicePath); + + UsbIoHandles = BmFindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount); + for (Index = 0; Index < UsbIoHandleCount; Index++) { + FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath); + DEBUG ((EFI_D_INFO, "[Bds] FullDp2[%d]:", Index)); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, "\n")); + FileBuffer = BmLoadEfiBootOption (&FullDevicePath, FileSize); + if (FileBuffer != NULL) { + DEBUG ((EFI_D_INFO, "-->")); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, FileBuffer != NULL ? " - Found\n" : "\n")); + break; + } + } + + if (UsbIoHandles != NULL) { + FreePool (UsbIoHandles); + } + } + } + + BmFreeAndSet ((VOID **) DevicePath, FullDevicePath); + return FileBuffer; +} + +/** + Expand a device path that starts with a hard drive media device path node to be a + full device path that includes the full hardware path to the device. We need + to do this so it can be booted. As an optimization the front match (the part point + to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable + so a connect all is not required on every boot. All successful history device path + which point to partition node (the front part) will be saved. + + @param DevicePath On input, a pointer to an allocated buffer that contains the + file device path. + On output, a pointer to an reallocated buffer that contains + the expanded device path. It would point to NULL if the file + cannot be read. + +**/ +VOID +BmExpandPartitionShortFormDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + EFI_STATUS Status; + UINTN BlockIoHandleCount; + EFI_HANDLE *BlockIoBuffer; + EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; + EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; + UINTN Index; + UINTN InstanceNum; + EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + UINTN CachedDevicePathSize; + BOOLEAN DeviceExist; + BOOLEAN NeedAdjust; + EFI_DEVICE_PATH_PROTOCOL *Instance; + UINTN Size; + + FullDevicePath = NULL; + // + // Check if there is prestore 'HDDP' variable. + // If exist, search the front path which point to partition node in the variable instants. + // If fail to find or 'HDDP' not exist, reconnect all and search in all system + // + GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize); + + // + // Delete the invalid 'HDDP' variable. + // + if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) { + FreePool (CachedDevicePath); + CachedDevicePath = NULL; + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + 0, + 0, + NULL + ); + ASSERT_EFI_ERROR (Status); + } + + if (CachedDevicePath != NULL) { + TempNewDevicePath = CachedDevicePath; + DeviceExist = FALSE; + NeedAdjust = FALSE; + do { + // + // Check every instance of the variable + // First, check whether the instance contain the partition node, which is needed for distinguishing multi + // partial partition boot option. Second, check whether the instance could be connected. + // + Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); + if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) { + // + // Connect the device path instance, the device path point to hard drive media device path node + // e.g. ACPI() /PCI()/ATA()/Partition() + // + Status = EfiBootManagerConnectDevicePath (Instance, NULL); + if (!EFI_ERROR (Status)) { + DeviceExist = TRUE; + break; + } + } + // + // Come here means the first instance is not matched + // + NeedAdjust = TRUE; + FreePool(Instance); + } while (TempNewDevicePath != NULL); + + if (DeviceExist) { + // + // Find the matched device path. + // Append the file path information from the boot option and return the fully expanded device path. + // + FullDevicePath = AppendDevicePath (Instance, NextDevicePathNode (*DevicePath)); + + // + // Adjust the 'HDDP' instances sequence if the matched one is not first one. + // + if (NeedAdjust) { + // + // First delete the matched instance. + // + TempNewDevicePath = CachedDevicePath; + CachedDevicePath = BmDelPartMatchInstance (CachedDevicePath, Instance); + FreePool (TempNewDevicePath); + + // + // Second, append the remaining path after the matched instance + // + TempNewDevicePath = CachedDevicePath; + CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath ); + FreePool (TempNewDevicePath); + // + // Save the matching Device Path so we don't need to do a connect all next time + // Failing to save only impacts performance next time expanding the short-form device path + // + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (CachedDevicePath), + CachedDevicePath + ); + } + + FreePool (Instance); + FreePool (CachedDevicePath); + FreePool (*DevicePath); + *DevicePath = FullDevicePath; + return; + } + } + + // + // If we get here we fail to find or 'HDDP' not exist, and now we need + // to search all devices in the system for a matched partition + // + EfiBootManagerConnectAll (); + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); + if (EFI_ERROR (Status)) { + BlockIoHandleCount = 0; + BlockIoBuffer = NULL; + } + // + // Loop through all the device handles that support the BLOCK_IO Protocol + // + for (Index = 0; Index < BlockIoHandleCount; Index++) { + + Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath); + if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) { + continue; + } + + if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) { + // + // Find the matched partition device path + // + FullDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (*DevicePath)); + + // + // Save the matched partition device path in 'HDDP' variable + // + if (CachedDevicePath != NULL) { + // + // Save the matched partition device path as first instance of 'HDDP' variable + // + if (BmMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) { + TempNewDevicePath = CachedDevicePath; + CachedDevicePath = BmDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath); + FreePool(TempNewDevicePath); + } + + if (CachedDevicePath != NULL) { + TempNewDevicePath = CachedDevicePath; + CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath); + FreePool(TempNewDevicePath); + } else { + CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath); + } + + // + // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller + // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger. + // + InstanceNum = 0; + ASSERT (CachedDevicePath != NULL); + TempNewDevicePath = CachedDevicePath; + while (!IsDevicePathEnd (TempNewDevicePath)) { + TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); + // + // Parse one instance + // + while (!IsDevicePathEndType (TempNewDevicePath)) { + TempNewDevicePath = NextDevicePathNode (TempNewDevicePath); + } + InstanceNum++; + // + // If the CachedDevicePath variable contain too much instance, only remain 12 instances. + // + if (InstanceNum >= 12) { + SetDevicePathEndNode (TempNewDevicePath); + break; + } + } + } else { + CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath); + } + + // + // Save the matching Device Path so we don't need to do a connect all next time + // Failing to save only impacts performance next time expanding the short-form device path + // + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (CachedDevicePath), + CachedDevicePath + ); + + break; + } + } + + if (CachedDevicePath != NULL) { + FreePool (CachedDevicePath); + } + if (BlockIoBuffer != NULL) { + FreePool (BlockIoBuffer); + } + BmFreeAndSet ((VOID **) DevicePath, FullDevicePath); +} + +/** + Algorithm follows the UEFI Spec chapter 3.4 Boot Mechanisms. + + @param DevicePath Device Path to a bootable device + + @return The bootable media handle. If the media on the DevicePath is not bootable, NULL will return. + +**/ +EFI_HANDLE +BmGetBootableDeviceHandle ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath; + EFI_HANDLE Handle; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + VOID *Buffer; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + UINTN Size; + UINTN TempSize; + EFI_HANDLE ReturnHandle; + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + EFI_IMAGE_DOS_HEADER DosHeader; + EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + + ReturnHandle = NULL; + UpdatedDevicePath = DevicePath; + + // + // Check whether the device is connected + // + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle); + if (EFI_ERROR (Status)) { + // + // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol, + // + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle); + if (EFI_ERROR (Status)) { + // + // Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly + // + UpdatedDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle); + gBS->ConnectController (Handle, NULL, NULL, TRUE); + } + } else { + // + // For removable device boot option, its contained device path only point to the removable device handle, + // should make sure all its children handles (its child partion or media handles) are created and connected. + // + gBS->ConnectController (Handle, NULL, NULL, TRUE); + // + // Get BlockIo protocol and check removable attribute + // + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo); + // + // Issue a dummy read to the device to check for media change. + // When the removable media is changed, any Block IO read/write will + // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is + // returned. After the Block IO protocol is reinstalled, subsequent + // Block IO read/write will success. + // + Buffer = AllocatePool (BlockIo->Media->BlockSize); + if (Buffer != NULL) { + BlockIo->ReadBlocks ( + BlockIo, + BlockIo->Media->MediaId, + 0, + BlockIo->Media->BlockSize, + Buffer + ); + FreePool(Buffer); + } + } + + // + // Detect the the default boot file from removable Media + // + Size = GetDevicePathSize(DevicePath) - END_DEVICE_PATH_LENGTH; + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NumberSimpleFileSystemHandles, + &SimpleFileSystemHandles + ); + for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { + // + // Get the device path size of SimpleFileSystem handle + // + TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); + TempSize = GetDevicePathSize (TempDevicePath)- END_DEVICE_PATH_LENGTH; + // + // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path + // + if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) { + // + // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media + // machinename is ia32, ia64, x64, ... + // + Hdr.Union = &HdrData; + Status = BmGetImageHeader ( + SimpleFileSystemHandles[Index], + EFI_REMOVABLE_MEDIA_FILE_NAME, + &DosHeader, + Hdr + ); + if (!EFI_ERROR (Status) && EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) && + (Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) + ) { + ReturnHandle = SimpleFileSystemHandles[Index]; + break; + } + } + } + + if (SimpleFileSystemHandles != NULL) { + FreePool(SimpleFileSystemHandles); + } + + return ReturnHandle; +} + +/** + Get the image file buffer data and buffer size by its device path. + + @param FilePath On input, a pointer to an allocated buffer that contains the + file device path. + On output the device path pointer could be modified to point to + a new allocated buffer that contains the full device path. + It could be caused by either short-form device path expanding, + or default boot file path appending. + @param FileSize A pointer to the size of the file buffer. + + @retval NULL The file can't be found. + @retval other The file buffer. The caller is responsible to free memory. +**/ +VOID * +BmLoadEfiBootOption ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, + OUT UINTN *FileSize + ) +{ + EFI_HANDLE Handle; + VOID *FileBuffer; + UINT32 AuthenticationStatus; + EFI_DEVICE_PATH_PROTOCOL *Node; + + ASSERT ((FilePath != NULL) && (*FilePath != NULL) && (FileSize != NULL)); + + EfiBootManagerConnectDevicePath (*FilePath, NULL); + + *FileSize = 0; + FileBuffer = NULL; + // + // Expand the short-form device path to full device path + // + if ((DevicePathType (*FilePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (*FilePath) == MEDIA_HARDDRIVE_DP)) { + // + // Expand the Harddrive device path + // + BmExpandPartitionShortFormDevicePath (FilePath); + if (*FilePath == NULL) { + return NULL; + } + + } else { + for (Node = *FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { + if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || + (DevicePathSubType (Node) == MSG_USB_WWID_DP))) { + break; + } + } + + if (!IsDevicePathEnd (Node)) { + // + // Expand the USB WWID/Class device path + // + FileBuffer = BmExpandUsbShortFormDevicePath (FilePath, FileSize); + if (FileBuffer == NULL) { + return NULL; + } + } + } + + // + // Fix up the boot option path if it points to a FV in memory map style of device path + // + if (BmIsMemmapFvFilePath (*FilePath)) { + BmFixupMemmapFvFilePath (FilePath); + if (*FilePath == NULL) { + return NULL; + } + } + + if (FileBuffer == NULL) { + FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus); + } + + // + // If we didn't find an image directly, we need to try as if it is a removable device boot option + // and load the image according to the default boot behavior. + // + if (FileBuffer == NULL) { + // + // check if there is a bootable media could be found in this device path, + // and get the bootable media handle + // + Handle = BmGetBootableDeviceHandle (*FilePath); + if (Handle != NULL) { + // + // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from the media + // machinename is ia32, ia64, x64, ... + // + BmFreeAndSet ((VOID **) FilePath, FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME)); + ASSERT (*FilePath != NULL); + FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus); + } + } + + if (FileBuffer == NULL) { + BmFreeAndSet ((VOID **) FilePath, NULL); + } + + return FileBuffer; +} + +/** + Attempt to boot the EFI boot option. This routine sets L"BootCurent" and + also signals the EFI ready to boot event. If the device path for the option + starts with a BBS device path a legacy boot is attempted via the registered + gLegacyBoot function. Short form device paths are also supported via this + rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, + MSG_USB_CLASS_DP gets expaned out to find the first device that matches. + If the BootOption Device Path fails the removable media boot algorithm + is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type + is tried per processor type) + + @param BootOption Boot Option to try and boot. + On return, BootOption->Status contains the boot status. + EFI_SUCCESS BootOption was booted + EFI_UNSUPPORTED A BBS device path was found with no valid callback + registered via EfiBootManagerInitialize(). + EFI_NOT_FOUND The BootOption was not found on the system + !EFI_SUCCESS BootOption failed with this error status + +**/ +VOID +EFIAPI +EfiBootManagerBoot ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; + UINT16 Uint16; + UINTN OptionNumber; + UINTN OriginalOptionNumber; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_HANDLE FvHandle; + VOID *FileBuffer; + UINTN FileSize; + EFI_BOOT_LOGO_PROTOCOL *BootLogo; + EFI_EVENT LegacyBootEvent; + + if (BootOption == NULL) { + return; + } + + if (BootOption->FilePath == NULL) { + BootOption->Status = EFI_INVALID_PARAMETER; + return; + } + + // + // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File") + // + OptionNumber = BmFindBootOptionInVariable (BootOption); + if (OptionNumber == LoadOptionNumberUnassigned) { + Status = BmGetFreeOptionNumber (L"BootOrder", &Uint16); + if (!EFI_ERROR (Status)) { + // + // Save the BootOption->OptionNumber to restore later + // + OptionNumber = Uint16; + OriginalOptionNumber = BootOption->OptionNumber; + BootOption->OptionNumber = OptionNumber; + Status = EfiBootManagerLoadOptionToVariable (BootOption); + BootOption->OptionNumber = OriginalOptionNumber; + } + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status)); + BootOption->Status = Status; + return ; + } + } + + // + // 2. Set BootCurrent + // + Uint16 = (UINT16) OptionNumber; + BmSetVariableAndReportStatusCodeOnError ( + L"BootCurrent", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (UINT16), + &Uint16 + ); + + // + // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute + // the boot option. + // + Node = BootOption->FilePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); + if (!EFI_ERROR (Status) && CompareGuid ( + EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node), + PcdGetPtr (PcdBootManagerMenuFile) + )) { + DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n")); + BmStopHotkeyService (NULL, NULL); + } else { + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); + EfiSignalEventReadyToBoot(); + // + // 4. Repair system through DriverHealth protocol + // + BmRepairAllControllers (); + } + + PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); + + // + // 5. Load EFI boot option to ImageHandle + // + ImageHandle = NULL; + if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { + Status = EFI_NOT_FOUND; + FilePath = DuplicateDevicePath (BootOption->FilePath); + FileBuffer = BmLoadEfiBootOption (&FilePath, &FileSize); + if (FileBuffer != NULL) { + + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); + + Status = gBS->LoadImage ( + TRUE, + gImageHandle, + FilePath, + FileBuffer, + FileSize, + &ImageHandle + ); + FreePool (FileBuffer); + FreePool (FilePath); + } + + if (EFI_ERROR (Status)) { + // + // Report Status Code to indicate that the failure to load boot option + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) + ); + BootOption->Status = Status; + return; + } + } + + // + // 6. Adjust the different type memory page number just before booting + // and save the updated info into the variable for next boot to use + // + if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) { + if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) { + BmSetMemoryTypeInformationVariable (); + } + } + + DEBUG_CODE_BEGIN(); + if (BootOption->Description == NULL) { + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n")); + } else { + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description)); + } + DEBUG_CODE_END(); + + // + // Check to see if we should legacy BOOT. If yes then do the legacy boot + // Write boot to OS performance data for Legacy boot + // + if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { + if (mBmLegacyBoot != NULL) { + // + // Write boot to OS performance data for legacy boot. + // + PERF_CODE ( + // + // Create an event to be signalled when Legacy Boot occurs to write performance data. + // + Status = EfiCreateEventLegacyBootEx( + TPL_NOTIFY, + BmWriteBootToOsPerformanceData, + NULL, + &LegacyBootEvent + ); + ASSERT_EFI_ERROR (Status); + ); + + mBmLegacyBoot (BootOption); + } else { + BootOption->Status = EFI_UNSUPPORTED; + } + + PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); + return; + } + + // + // Provide the image with its load options + // + Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); + ASSERT_EFI_ERROR (Status); + + ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize; + ImageInfo->LoadOptions = BootOption->OptionalData; + + // + // Clean to NULL because the image is loaded directly from the firmwares boot manager. + // + ImageInfo->ParentHandle = NULL; + + // + // Before calling the image, enable the Watchdog Timer for 5 minutes period + // + gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); + + // + // Write boot to OS performance data for UEFI boot + // + PERF_CODE ( + BmWriteBootToOsPerformanceData (NULL, NULL); + ); + + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); + + Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData); + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); + BootOption->Status = Status; + if (EFI_ERROR (Status)) { + // + // Report Status Code to indicate that boot failure + // + REPORT_STATUS_CODE ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED) + ); + } + PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); + + // + // Clear the Watchdog Timer after the image returns + // + gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); + + // + // Set Logo status invalid after trying one boot option + // + BootLogo = NULL; + Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo); + if (!EFI_ERROR (Status) && (BootLogo != NULL)) { + Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); + ASSERT_EFI_ERROR (Status); + } + + // + // Clear Boot Current + // + Status = gRT->SetVariable ( + L"BootCurrent", + &gEfiGlobalVariableGuid, + 0, + 0, + NULL + ); + // + // Deleting variable with current variable implementation shouldn't fail. + // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted, + // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND. + // + ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); +} + +/** + Check whether there is a instance in BlockIoDevicePath, which contain multi device path + instances, has the same partition node with HardDriveDevicePath device path + + @param BlockIoDevicePath Multi device path instances which need to check + @param HardDriveDevicePath A device path which starts with a hard drive media + device path. + + @retval TRUE There is a matched device path instance. + @retval FALSE There is no matched device path instance. + +**/ +BOOLEAN +BmMatchPartitionDevicePathNode ( + IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, + IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath + ) +{ + HARDDRIVE_DEVICE_PATH *TmpHdPath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + BOOLEAN Match; + EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode; + + if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { + return FALSE; + } + + // + // Make PreviousDevicePath == the device path node before the end node + // + DevicePath = BlockIoDevicePath; + BlockIoHdDevicePathNode = NULL; + + // + // find the partition device path node + // + while (!IsDevicePathEnd (DevicePath)) { + if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP) + ) { + BlockIoHdDevicePathNode = DevicePath; + break; + } + + DevicePath = NextDevicePathNode (DevicePath); + } + + if (BlockIoHdDevicePathNode == NULL) { + return FALSE; + } + // + // See if the harddrive device path in blockio matches the orig Hard Drive Node + // + TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode; + Match = FALSE; + + // + // Check for the match + // + if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) && + (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) { + switch (TmpHdPath->SignatureType) { + case SIGNATURE_TYPE_GUID: + Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature); + break; + case SIGNATURE_TYPE_MBR: + Match = (BOOLEAN) (*((UINT32 *) (&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0])))); + break; + default: + Match = FALSE; + break; + } + } + + return Match; +} + +/** + Emuerate all possible bootable medias in the following order: + 1. Removable BlockIo - The boot option only points to the removable media + device, like USB key, DVD, Floppy etc. + 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, + like HardDisk. + 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting + SimpleFileSystem Protocol, but not supporting BlockIo + protocol. + 4. LoadFile - The boot option points to the media supporting + LoadFile protocol. + Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior + + @param BootOptionCount Return the boot option count which has been found. + + @retval Pointer to the boot option array. +**/ +EFI_BOOT_MANAGER_LOAD_OPTION * +BmEnumerateBootOptions ( + UINTN *BootOptionCount + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINT16 NonBlockNumber; + UINTN HandleCount; + EFI_HANDLE *Handles; + EFI_BLOCK_IO_PROTOCOL *BlkIo; + UINTN Removable; + UINTN Index; + UINTN FunctionIndex; + CHAR16 *Temp; + CHAR16 *DescriptionPtr; + CHAR16 Description[30]; + + ASSERT (BootOptionCount != NULL); + + *BootOptionCount = 0; + BootOptions = NULL; + + // + // Parse removable block io followed by fixed block io + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiBlockIoProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + + for (Removable = 0; Removable < 2; Removable++) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlkIo + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Skip the logical partitions + // + if (BlkIo->Media->LogicalPartition) { + continue; + } + + // + // Skip the fixed block io then the removable block io + // + if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) { + continue; + } + + DescriptionPtr = NULL; + for (FunctionIndex = 0; FunctionIndex < sizeof (mBmGetBootDescription) / sizeof (mBmGetBootDescription[0]); FunctionIndex++) { + DescriptionPtr = mBmGetBootDescription[FunctionIndex] (Handles[Index]); + if (DescriptionPtr != NULL) { + break; + } + } + + if (DescriptionPtr == NULL) { + continue; + } + + // + // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix + // + Temp = AllocatePool (StrSize (DescriptionPtr) + sizeof (mBmUefiPrefix)); + ASSERT (Temp != NULL); + StrCpy (Temp, mBmUefiPrefix); + StrCat (Temp, DescriptionPtr); + FreePool (DescriptionPtr); + DescriptionPtr = Temp; + + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + DescriptionPtr, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + + FreePool (DescriptionPtr); + } + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + // + // Parse simple file system not based on block io + // + NonBlockNumber = 0; + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlkIo + ); + if (!EFI_ERROR (Status)) { + // + // Skip if the file system handle supports a BlkIo protocol, which we've handled in above + // + continue; + } + UnicodeSPrint (Description, sizeof (Description), NonBlockNumber > 0 ? L"%s %d" : L"%s", L"UEFI Non-Block Boot Device", NonBlockNumber); + + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + // + // Parse load file, assuming UEFI Network boot option + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiLoadFileProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + + UnicodeSPrint (Description, sizeof (Description), Index > 0 ? L"%s %d" : L"%s", L"UEFI Network", Index); + + BootOptions = ReallocatePool ( + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), + BootOptions + ); + ASSERT (BootOptions != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + &BootOptions[(*BootOptionCount)++], + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + Description, + DevicePathFromHandle (Handles[Index]), + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + } + + if (HandleCount != 0) { + FreePool (Handles); + } + + return BootOptions; +} + +/** + The function enumerates all boot options, creates them and registers them in the BootOrder variable. +**/ +VOID +EFIAPI +EfiBootManagerRefreshAllBootOption ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions; + UINTN NvBootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + UINTN Index; + + // + // Optionally refresh the legacy boot option + // + if (mBmRefreshLegacyBootOption != NULL) { + mBmRefreshLegacyBootOption (); + } + + BootOptions = BmEnumerateBootOptions (&BootOptionCount); + NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); + + // + // Mark the boot option as added by BDS by setting OptionalData to a special GUID + // + for (Index = 0; Index < BootOptionCount; Index++) { + BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); + BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID); + } + + // + // Remove invalid EFI boot options from NV + // + for (Index = 0; Index < NvBootOptionCount; Index++) { + if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || + (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP) + ) && + (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) && + CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid) + ) { + // + // Only check those added by BDS + // so that the boot options added by end-user or OS installer won't be deleted + // + if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) { + Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot); + // + // Deleting variable with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + } + } + } + + // + // Add new EFI boot options to NV + // + for (Index = 0; Index < BootOptionCount; Index++) { + if (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) { + EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1); + // + // Try best to add the boot options so continue upon failure. + // + } + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount); +} + +/** + This function is called to create the boot option for the Boot Manager Menu. + + The Boot Manager Menu is shown after successfully booting a boot option. + Assume the BootManagerMenuFile is in the same FV as the module links to this library. + + @param BootOption Return the boot option of the Boot Manager Menu + + @retval EFI_SUCCESS Successfully register the Boot Manager Menu. + @retval Status Return status of gRT->SetVariable (). BootOption still points + to the Boot Manager Menu even the Status is not EFI_SUCCESS. +**/ +EFI_STATUS +BmRegisterBootManagerMenu ( + OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + CHAR16 *Description; + UINTN DescriptionLength; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; + + Status = GetSectionFromFv ( + PcdGetPtr (PcdBootManagerMenuFile), + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **) &Description, + &DescriptionLength + ); + if (EFI_ERROR (Status)) { + Description = NULL; + } + + EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile)); + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &LoadedImage + ); + ASSERT_EFI_ERROR (Status); + DevicePath = AppendDevicePathNode ( + DevicePathFromHandle (LoadedImage->DeviceHandle), + (EFI_DEVICE_PATH_PROTOCOL *) &FileNode + ); + ASSERT (DevicePath != NULL); + + Status = EfiBootManagerInitializeLoadOption ( + BootOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, + (Description != NULL) ? Description : L"Boot Manager Menu", + DevicePath, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + FreePool (DevicePath); + if (Description != NULL) { + FreePool (Description); + } + + DEBUG_CODE ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + ); + + return EfiBootManagerAddLoadOptionVariable (BootOption, 0); +} + +/** + Return the boot option corresponding to the Boot Manager Menu. + It may automatically create one if the boot option hasn't been created yet. + + @param BootOption Return the Boot Manager Menu. + + @retval EFI_SUCCESS The Boot Manager Menu is successfully returned. + @retval Status Return status of gRT->SetVariable (). BootOption still points + to the Boot Manager Menu even the Status is not EFI_SUCCESS. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerGetBootManagerMenu ( + EFI_BOOT_MANAGER_LOAD_OPTION *BootOption + ) +{ + EFI_STATUS Status; + UINTN BootOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_HANDLE FvHandle; + + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + + for (Index = 0; Index < BootOptionCount; Index++) { + Node = BootOptions[Index].FilePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); + if (!EFI_ERROR (Status)) { + if (CompareGuid ( + EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node), + PcdGetPtr (PcdBootManagerMenuFile) + ) + ) { + Status = EfiBootManagerInitializeLoadOption ( + BootOption, + BootOptions[Index].OptionNumber, + BootOptions[Index].OptionType, + BootOptions[Index].Attributes, + BootOptions[Index].Description, + BootOptions[Index].FilePath, + BootOptions[Index].OptionalData, + BootOptions[Index].OptionalDataSize + ); + ASSERT_EFI_ERROR (Status); + break; + } + } + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + + // + // Automatically create the Boot#### for Boot Manager Menu when not found. + // + if (Index == BootOptionCount) { + return BmRegisterBootManagerMenu (BootOption); + } else { + return EFI_SUCCESS; + } +} + diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c new file mode 100644 index 0000000000..0172f959d7 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c @@ -0,0 +1,318 @@ +/** @file + Library functions which relate with connecting the device. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +/** + Connect all the drivers to all the controllers. + + This function makes sure all the current system drivers manage the correspoinding + controllers if have. And at the same time, makes sure all the system controllers + have driver to manage it if have. +**/ +VOID +BmConnectAllDriversToAllControllers ( + VOID + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + + do { + // + // Connect All EFI 1.10 drivers following EFI 1.10 algorithm + // + gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &HandleBuffer + ); + + for (Index = 0; Index < HandleCount; Index++) { + gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE); + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } + + // + // Check to see if it's possible to dispatch an more DXE drivers. + // The above code may have made new DXE drivers show up. + // If any new driver is dispatched (Status == EFI_SUCCESS) and we will try + // the connect again. + // + Status = gDS->Dispatch (); + + } while (!EFI_ERROR (Status)); +} + +/** + This function will connect all the system driver to controller + first, and then special connect the default console, this make + sure all the system controller available and the platform default + console connected. + +**/ +VOID +EFIAPI +EfiBootManagerConnectAll ( + VOID + ) +{ + // + // Connect the platform console first + // + EfiBootManagerConnectAllDefaultConsoles (); + + // + // Generic way to connect all the drivers + // + BmConnectAllDriversToAllControllers (); + + // + // Here we have the assumption that we have already had + // platform default console + // + EfiBootManagerConnectAllDefaultConsoles (); +} + +/** + This function will create all handles associate with every device + path node. If the handle associate with one device path node can not + be created successfully, then still give chance to do the dispatch, + which load the missing drivers if possible. + + @param DevicePathToConnect The device path which will be connected, it can be + a multi-instance device path + @param MatchingHandle Return the controller handle closest to the DevicePathToConnect + + @retval EFI_SUCCESS All handles associate with every device path node + have been created + @retval EFI_OUT_OF_RESOURCES There is no resource to create new handles + @retval EFI_NOT_FOUND Create the handle associate with one device path + node failed + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePathToConnect, + OUT EFI_HANDLE *MatchingHandle OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_HANDLE Handle; + EFI_HANDLE PreviousHandle; + EFI_TPL CurrentTpl; + + if (DevicePathToConnect == NULL) { + return EFI_INVALID_PARAMETER; + } + + CurrentTpl = EfiGetCurrentTpl (); + // + // Start the real work of connect with RemainingDevicePath + // + PreviousHandle = NULL; + do { + // + // Find the handle that best matches the Device Path. If it is only a + // partial match the remaining part of the device path is returned in + // RemainingDevicePath. + // + RemainingDevicePath = DevicePathToConnect; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &Handle); + if (!EFI_ERROR (Status)) { + if (Handle == PreviousHandle) { + // + // If no forward progress is made try invoking the Dispatcher. + // A new FV may have been added to the system an new drivers + // may now be found. + // Status == EFI_SUCCESS means a driver was dispatched + // Status == EFI_NOT_FOUND means no new drivers were dispatched + // + if (CurrentTpl == TPL_APPLICATION) { + Status = gDS->Dispatch (); + } else { + // + // Always return EFI_NOT_FOUND here + // to prevent dead loop when control handle is found but connection failded case + // + Status = EFI_NOT_FOUND; + } + } + + + if (!EFI_ERROR (Status)) { + PreviousHandle = Handle; + // + // Connect all drivers that apply to Handle and RemainingDevicePath, + // the Recursive flag is FALSE so only one level will be expanded. + // + // Do not check the connect status here, if the connect controller fail, + // then still give the chance to do dispatch, because partial + // RemainingDevicepath may be in the new FV + // + // 1. If the connect fail, RemainingDevicepath and handle will not + // change, so next time will do the dispatch, then dispatch's status + // will take effect + // 2. If the connect success, the RemainingDevicepath and handle will + // change, then avoid the dispatch, we have chance to continue the + // next connection + // + gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE); + if (MatchingHandle != NULL) { + *MatchingHandle = Handle; + } + } + } + // + // Loop until RemainingDevicePath is an empty device path + // + } while (!EFI_ERROR (Status) && !IsDevicePathEnd (RemainingDevicePath)); + + ASSERT (EFI_ERROR (Status) || IsDevicePathEnd (RemainingDevicePath)); + + return Status; +} + +/** + This function will disconnect all current system handles. + + gBS->DisconnectController() is invoked for each handle exists in system handle buffer. + If handle is a bus type handle, all childrens also are disconnected recursively by + gBS->DisconnectController(). +**/ +VOID +EFIAPI +EfiBootManagerDisconnectAll ( + VOID + ) +{ + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Index; + + // + // Disconnect all + // + gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &HandleBuffer + ); + for (Index = 0; Index < HandleCount; Index++) { + gBS->DisconnectController (HandleBuffer[Index], NULL, NULL); + } + + if (HandleBuffer != NULL) { + FreePool (HandleBuffer); + } +} + +/** + Connect the specific Usb device which match the short form device path, + and whose bus is determined by Host Controller (Uhci or Ehci). + + @param DevicePath A short-form device path that starts with the first + element being a USB WWID or a USB Class device + path + + @return EFI_INVALID_PARAMETER DevicePath is NULL pointer. + DevicePath is not a USB device path. + + @return EFI_SUCCESS Success to connect USB device + @return EFI_NOT_FOUND Fail to find handle for USB controller to connect. + +**/ +EFI_STATUS +BmConnectUsbShortFormDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN Index; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Class[3]; + BOOLEAN AtLeastOneConnected; + + // + // Check the passed in parameters + // + if (DevicePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) || + ((DevicePathSubType (DevicePath) != MSG_USB_CLASS_DP) && (DevicePathSubType (DevicePath) != MSG_USB_WWID_DP)) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // Find the usb host controller firstly, then connect with the remaining device path + // + AtLeastOneConnected = FALSE; + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol ( + Handles[Index], + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo + ); + if (!EFI_ERROR (Status)) { + // + // Check whether the Pci device is the wanted usb host controller + // + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 3, &Class); + if (!EFI_ERROR (Status) && + ((PCI_CLASS_SERIAL == Class[2]) && (PCI_CLASS_SERIAL_USB == Class[1])) + ) { + Status = gBS->ConnectController ( + Handles[Index], + NULL, + DevicePath, + FALSE + ); + if (!EFI_ERROR(Status)) { + AtLeastOneConnected = TRUE; + } + } + } + } + + if (Handles != NULL) { + FreePool (Handles); + } + } + + return AtLeastOneConnected ? EFI_SUCCESS : EFI_NOT_FOUND; +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c new file mode 100644 index 0000000000..49b99957e0 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c @@ -0,0 +1,748 @@ +/** @file + Library functions which contain all the code to connect console device. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +CHAR16 *mConVarName[] = { + L"ConIn", + L"ConOut", + L"ErrOut", + L"ConInDev", + L"ConOutDev", + L"ErrOutDev" +}; + +/** + Search out the video controller. + + @return PCI device path of the video controller. +**/ +EFI_HANDLE +BmGetVideoController ( + VOID + ) +{ + EFI_STATUS Status; + UINTN RootBridgeHandleCount; + EFI_HANDLE *RootBridgeHandleBuffer; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN RootBridgeIndex; + UINTN Index; + EFI_HANDLE VideoController; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + // + // Make all the PCI_IO protocols show up + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciRootBridgeIoProtocolGuid, + NULL, + &RootBridgeHandleCount, + &RootBridgeHandleBuffer + ); + if (EFI_ERROR (Status) || (RootBridgeHandleCount == 0)) { + return NULL; + } + + VideoController = NULL; + for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) { + gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE); + + // + // Start to check all the pci io to find the first video controller + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiPciIoProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + continue; + } + + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo); + if (!EFI_ERROR (Status)) { + // + // Check for all video controller + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + sizeof (Pci) / sizeof (UINT32), + &Pci + ); + if (!EFI_ERROR (Status) && IS_PCI_VGA (&Pci)) { + // TODO: use IS_PCI_DISPLAY?? + VideoController = HandleBuffer[Index]; + break; + } + } + } + FreePool (HandleBuffer); + + if (VideoController != NULL) { + break; + } + } + FreePool (RootBridgeHandleBuffer); + + return VideoController; +} + +/** + Query all the children of VideoController and return the device paths of all the + children that support GraphicsOutput protocol. + + @param VideoController PCI handle of video controller. + + @return Device paths of all the children that support GraphicsOutput protocol. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +EfiBootManagerGetGopDevicePath ( + IN EFI_HANDLE VideoController + ) +{ + UINTN Index; + EFI_STATUS Status; + EFI_GUID **ProtocolBuffer; + UINTN ProtocolBufferCount; + UINTN ProtocolIndex; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *Next; + EFI_DEVICE_PATH_PROTOCOL *Previous; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_DEVICE_PATH_PROTOCOL *GopPool; + EFI_DEVICE_PATH_PROTOCOL *ReturnDevicePath; + + + Status = gBS->ProtocolsPerHandle ( + VideoController, + &ProtocolBuffer, + &ProtocolBufferCount + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + GopPool = NULL; + + for (ProtocolIndex = 0; ProtocolIndex < ProtocolBufferCount; ProtocolIndex++) { + Status = gBS->OpenProtocolInformation ( + VideoController, + ProtocolBuffer[ProtocolIndex], + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + continue; + } + + for (Index = 0; Index < EntryCount; Index++) { + // + // Query all the children + // + if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + + Previous = NULL; + for (Next = DevicePath; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) { + Previous = Next; + } + ASSERT (Previous != NULL); + + if (DevicePathType (Previous) == ACPI_DEVICE_PATH && DevicePathSubType (Previous) == ACPI_ADR_DP) { + Status = gBS->OpenProtocol ( + OpenInfoBuffer[Index].ControllerHandle, + &gEfiGraphicsOutputProtocolGuid, + NULL, + NULL, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + // + // Append the device path to GOP pool when there is GOP protocol installed. + // + TempDevicePath = GopPool; + GopPool = AppendDevicePathInstance (GopPool, DevicePath); + gBS->FreePool (TempDevicePath); + } + } + + if (DevicePathType (Previous) == HARDWARE_DEVICE_PATH && DevicePathSubType (Previous) == HW_CONTROLLER_DP) { + // + // Recursively look for GOP child in this frame buffer handle + // + DEBUG ((EFI_D_INFO, "[Bds] Looking for GOP child deeper ... \n")); + TempDevicePath = GopPool; + ReturnDevicePath = EfiBootManagerGetGopDevicePath (OpenInfoBuffer[Index].ControllerHandle); + GopPool = AppendDevicePathInstance (GopPool, ReturnDevicePath); + gBS->FreePool (ReturnDevicePath); + gBS->FreePool (TempDevicePath); + } + } + } + + FreePool (OpenInfoBuffer); + } + + FreePool (ProtocolBuffer); + + return GopPool; +} + +/** + Connect the platform active active video controller. + + @param VideoController PCI handle of video controller. + + @retval EFI_NOT_FOUND There is no active video controller. + @retval EFI_SUCCESS The video controller is connected. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectVideoController ( + EFI_HANDLE VideoController OPTIONAL + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Gop; + + if (VideoController == NULL) { + // + // Get the platform vga device + // + VideoController = BmGetVideoController (); + } + + if (VideoController == NULL) { + return EFI_NOT_FOUND; + } + + // + // Try to connect the PCI device path, so that GOP dirver could start on this + // device and create child handles with GraphicsOutput Protocol installed + // on them, then we get device paths of these child handles and select + // them as possible console device. + // + gBS->ConnectController (VideoController, NULL, NULL, FALSE); + + Gop = EfiBootManagerGetGopDevicePath (VideoController); + if (Gop == NULL) { + return EFI_NOT_FOUND; + } + + EfiBootManagerUpdateConsoleVariable (ConOut, Gop, NULL); + FreePool (Gop); + + // + // Necessary for ConPlatform and ConSplitter driver to start up again after ConOut is updated. + // + return gBS->ConnectController (VideoController, NULL, NULL, TRUE); +} + +/** + Fill console handle in System Table if there are no valid console handle in. + + Firstly, check the validation of console handle in System Table. If it is invalid, + update it by the first console device handle from EFI console variable. + + @param VarName The name of the EFI console variable. + @param ConsoleGuid Specified Console protocol GUID. + @param ConsoleHandle On IN, console handle in System Table to be checked. + On OUT, new console handle in system table. + @param ProtocolInterface On IN, console protocol on console handle in System Table to be checked. + On OUT, new console protocol on new console handle in system table. + + @retval TRUE System Table has been updated. + @retval FALSE System Table hasn't been updated. + +**/ +BOOLEAN +BmUpdateSystemTableConsole ( + IN CHAR16 *VarName, + IN EFI_GUID *ConsoleGuid, + IN OUT EFI_HANDLE *ConsoleHandle, + IN OUT VOID **ProtocolInterface + ) +{ + EFI_STATUS Status; + UINTN DevicePathSize; + EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; + EFI_DEVICE_PATH_PROTOCOL *VarConsole; + EFI_DEVICE_PATH_PROTOCOL *Instance; + VOID *Interface; + EFI_HANDLE NewHandle; + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut; + + ASSERT (VarName != NULL); + ASSERT (ConsoleHandle != NULL); + ASSERT (ConsoleGuid != NULL); + ASSERT (ProtocolInterface != NULL); + + if (*ConsoleHandle != NULL) { + Status = gBS->HandleProtocol ( + *ConsoleHandle, + ConsoleGuid, + &Interface + ); + if (Status == EFI_SUCCESS && Interface == *ProtocolInterface) { + // + // If ConsoleHandle is valid and console protocol on this handle also + // also matched, just return. + // + return FALSE; + } + } + + // + // Get all possible consoles device path from EFI variable + // + GetEfiGlobalVariable2 (VarName, (VOID **) &VarConsole, NULL); + if (VarConsole == NULL) { + // + // If there is no any console device, just return. + // + return FALSE; + } + + FullDevicePath = VarConsole; + + do { + // + // Check every instance of the console variable + // + Instance = GetNextDevicePathInstance (&VarConsole, &DevicePathSize); + if (Instance == NULL) { + DEBUG ((EFI_D_ERROR, "[Bds] No valid console instance is found for %s!\n", VarName)); + // We should not ASSERT when all the console devices are removed. + // ASSERT_EFI_ERROR (EFI_NOT_FOUND); + FreePool (FullDevicePath); + return FALSE; + } + + // + // Find console device handle by device path instance + // + Status = gBS->LocateDevicePath ( + ConsoleGuid, + &Instance, + &NewHandle + ); + if (!EFI_ERROR (Status)) { + // + // Get the console protocol on this console device handle + // + Status = gBS->HandleProtocol ( + NewHandle, + ConsoleGuid, + &Interface + ); + if (!EFI_ERROR (Status)) { + // + // Update new console handle in System Table. + // + *ConsoleHandle = NewHandle; + *ProtocolInterface = Interface; + if (CompareGuid (ConsoleGuid, &gEfiSimpleTextOutProtocolGuid)) { + // + // If it is console out device, set console mode 80x25 if current mode is invalid. + // + TextOut = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *) Interface; + if (TextOut->Mode->Mode == -1) { + TextOut->SetMode (TextOut, 0); + } + } + return TRUE; + } + } + + } while (Instance != NULL); + + // + // No any available console devcie found. + // + return FALSE; +} + +/** + This function updates the console variable based on ConVarName. It can + add or remove one specific console device path from the variable + + @param ConsoleType ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev. + @param CustomizedConDevicePath The console device path to be added to + the console variable. Cannot be multi-instance. + @param ExclusiveDevicePath The console device path to be removed + from the console variable. Cannot be multi-instance. + + @retval EFI_UNSUPPORTED The added device path is the same as a removed one. + @retval EFI_SUCCESS Successfully added or removed the device path from the + console variable. + @retval others Return status of RT->SetVariable(). + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerUpdateConsoleVariable ( + IN CONSOLE_TYPE ConsoleType, + IN EFI_DEVICE_PATH_PROTOCOL *CustomizedConDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *ExclusiveDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *VarConsole; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + + if (ConsoleType >= sizeof (mConVarName) / sizeof (mConVarName[0])) { + return EFI_INVALID_PARAMETER; + } + + // + // Notes: check the device path point, here should check + // with compare memory + // + if (CustomizedConDevicePath == ExclusiveDevicePath) { + return EFI_UNSUPPORTED; + } + // + // Delete the ExclusiveDevicePath from current default console + // + GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &VarConsole, NULL); + // + // Initialize NewDevicePath + // + NewDevicePath = VarConsole; + + // + // If ExclusiveDevicePath is even the part of the instance in VarConsole, delete it. + // In the end, NewDevicePath is the final device path. + // + if (ExclusiveDevicePath != NULL && VarConsole != NULL) { + NewDevicePath = BmDelPartMatchInstance (VarConsole, ExclusiveDevicePath); + } + // + // Try to append customized device path to NewDevicePath. + // + if (CustomizedConDevicePath != NULL) { + if (!BmMatchDevicePaths (NewDevicePath, CustomizedConDevicePath)) { + // + // Check if there is part of CustomizedConDevicePath in NewDevicePath, delete it. + // + NewDevicePath = BmDelPartMatchInstance (NewDevicePath, CustomizedConDevicePath); + // + // In the first check, the default console variable will be _ModuleEntryPoint, + // just append current customized device path + // + TempNewDevicePath = NewDevicePath; + NewDevicePath = AppendDevicePathInstance (NewDevicePath, CustomizedConDevicePath); + if (TempNewDevicePath != NULL) { + FreePool(TempNewDevicePath); + } + } + } + + // + // Finally, Update the variable of the default console by NewDevicePath + // + Status = gRT->SetVariable ( + mConVarName[ConsoleType], + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS + | ((ConsoleType < ConInDev) ? EFI_VARIABLE_NON_VOLATILE : 0), + GetDevicePathSize (NewDevicePath), + NewDevicePath + ); + + if (VarConsole == NewDevicePath) { + if (VarConsole != NULL) { + FreePool(VarConsole); + } + } else { + if (VarConsole != NULL) { + FreePool(VarConsole); + } + if (NewDevicePath != NULL) { + FreePool(NewDevicePath); + } + } + + return Status; +} + + +/** + Connect the console device base on the variable ConsoleType. + + @param ConsoleType ConIn, ConOut or ErrOut. + + @retval EFI_NOT_FOUND There is not any console devices connected + success + @retval EFI_SUCCESS Success connect any one instance of the console + device path base on the variable ConVarName. + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerConnectConsoleVariable ( + IN CONSOLE_TYPE ConsoleType + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *StartDevicePath; + EFI_DEVICE_PATH_PROTOCOL *Instance; + EFI_DEVICE_PATH_PROTOCOL *Next; + EFI_DEVICE_PATH_PROTOCOL *CopyOfDevicePath; + UINTN Size; + BOOLEAN DeviceExist; + EFI_HANDLE Handle; + + if ((ConsoleType != ConIn) && (ConsoleType != ConOut) && (ConsoleType != ErrOut)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + DeviceExist = FALSE; + Handle = NULL; + + // + // Check if the console variable exist + // + GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &StartDevicePath, NULL); + if (StartDevicePath == NULL) { + return EFI_UNSUPPORTED; + } + + CopyOfDevicePath = StartDevicePath; + do { + // + // Check every instance of the console variable + // + Instance = GetNextDevicePathInstance (&CopyOfDevicePath, &Size); + if (Instance == NULL) { + FreePool (StartDevicePath); + return EFI_UNSUPPORTED; + } + + Next = Instance; + while (!IsDevicePathEndType (Next)) { + Next = NextDevicePathNode (Next); + } + + SetDevicePathEndNode (Next); + // + // Connect the USB console + // USB console device path is a short-form device path that + // starts with the first element being a USB WWID + // or a USB Class device path + // + if ((DevicePathType (Instance) == MESSAGING_DEVICE_PATH) && + ((DevicePathSubType (Instance) == MSG_USB_CLASS_DP) || (DevicePathSubType (Instance) == MSG_USB_WWID_DP)) + ) { + Status = BmConnectUsbShortFormDevicePath (Instance); + if (!EFI_ERROR (Status)) { + DeviceExist = TRUE; + } + } else { + for (Next = Instance; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) { + if (DevicePathType (Next) == ACPI_DEVICE_PATH && DevicePathSubType (Next) == ACPI_ADR_DP) { + break; + } else if (DevicePathType (Next) == HARDWARE_DEVICE_PATH && + DevicePathSubType (Next) == HW_CONTROLLER_DP && + DevicePathType (NextDevicePathNode (Next)) == ACPI_DEVICE_PATH && + DevicePathSubType (NextDevicePathNode (Next)) == ACPI_ADR_DP + ) { + break; + } + } + if (!IsDevicePathEnd (Next)) { + // + // For GOP device path, start the video driver with NULL remaining device path + // + SetDevicePathEndNode (Next); + Status = EfiBootManagerConnectDevicePath (Instance, &Handle); + if (!EFI_ERROR (Status)) { + gBS->ConnectController (Handle, NULL, NULL, TRUE); + } + } else { + Status = EfiBootManagerConnectDevicePath (Instance, NULL); + } + if (EFI_ERROR (Status)) { + // + // Delete the instance from the console varialbe + // + EfiBootManagerUpdateConsoleVariable (ConsoleType, NULL, Instance); + } else { + DeviceExist = TRUE; + } + } + FreePool(Instance); + } while (CopyOfDevicePath != NULL); + + FreePool (StartDevicePath); + + if (!DeviceExist) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + + +/** + This function will search every input/output device in current system, + and make every input/output device as potential console device. +**/ +VOID +EFIAPI +EfiBootManagerConnectAllConsoles ( + VOID + ) +{ + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *ConDevicePath; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + + Index = 0; + HandleCount = 0; + HandleBuffer = NULL; + ConDevicePath = NULL; + + // + // Update all the console variables + // + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + + for (Index = 0; Index < HandleCount; Index++) { + gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **) &ConDevicePath + ); + EfiBootManagerUpdateConsoleVariable (ConIn, ConDevicePath, NULL); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + HandleBuffer = NULL; + } + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextOutProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); + for (Index = 0; Index < HandleCount; Index++) { + gBS->HandleProtocol ( + HandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + (VOID **) &ConDevicePath + ); + EfiBootManagerUpdateConsoleVariable (ConOut, ConDevicePath, NULL); + EfiBootManagerUpdateConsoleVariable (ErrOut, ConDevicePath, NULL); + } + + if (HandleBuffer != NULL) { + FreePool(HandleBuffer); + } + + // + // Connect all console variables + // + EfiBootManagerConnectAllDefaultConsoles (); +} + + +/** + This function will connect all the console devices base on the console + device variable ConIn, ConOut and ErrOut. +**/ +VOID +EFIAPI +EfiBootManagerConnectAllDefaultConsoles ( + VOID + ) +{ + BOOLEAN SystemTableUpdated; + + EfiBootManagerConnectConsoleVariable (ConOut); + PERF_START (NULL, "ConOutReady", "BDS", 1); + PERF_END (NULL, "ConOutReady", "BDS", 0); + + + EfiBootManagerConnectConsoleVariable (ConIn); + PERF_START (NULL, "ConInReady", "BDS", 1); + PERF_END (NULL, "ConInReady", "BDS", 0); + + // + // The _ModuleEntryPoint err out var is legal. + // + EfiBootManagerConnectConsoleVariable (ErrOut); + PERF_START (NULL, "ErrOutReady", "BDS", 1); + PERF_END (NULL, "ErrOutReady", "BDS", 0); + + SystemTableUpdated = FALSE; + // + // Fill console handles in System Table if no console device assignd. + // + if (BmUpdateSystemTableConsole (L"ConIn", &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **) &gST->ConIn)) { + SystemTableUpdated = TRUE; + } + if (BmUpdateSystemTableConsole (L"ConOut", &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **) &gST->ConOut)) { + SystemTableUpdated = TRUE; + } + if (BmUpdateSystemTableConsole (L"ErrOut", &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **) &gST->StdErr)) { + SystemTableUpdated = TRUE; + } + + if (SystemTableUpdated) { + // + // Update the CRC32 in the EFI System Table header + // + gST->Hdr.CRC32 = 0; + gBS->CalculateCrc32 ( + (UINT8 *) &gST->Hdr, + gST->Hdr.HeaderSize, + &gST->Hdr.CRC32 + ); + } +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c b/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c new file mode 100644 index 0000000000..2b5257394e --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c @@ -0,0 +1,578 @@ +/** @file + Library functions which relates with driver health. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +GLOBAL_REMOVE_IF_UNREFERENCED + CHAR16 *mBmHealthStatusText[] = { + L"Healthy", + L"Repair Required", + L"Configuration Required", + L"Failed", + L"Reconnect Required", + L"Reboot Required" + }; + +/** + Return the controller name. + + @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved. + @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing. + This handle specifies the controller whose name is to be returned. + @param ChildHandle The handle of the child controller to retrieve the name of. This is an + optional parameter that may be NULL. It will be NULL for device drivers. + It will also be NULL for bus drivers that attempt to retrieve the name + of the bus controller. It will not be NULL for a bus driver that attempts + to retrieve the name of a child controller. + + @return A pointer to the Unicode string to return. This Unicode string is the name of the controller + specified by ControllerHandle and ChildHandle. +**/ +CHAR16 * +BmGetControllerName ( + IN EFI_HANDLE DriverHealthHandle, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + CHAR16 *ControllerName; + CHAR8 *LanguageVariable; + CHAR8 *BestLanguage; + BOOLEAN Iso639Language; + EFI_COMPONENT_NAME_PROTOCOL *ComponentName; + + ControllerName = NULL; + + // + // Locate Component Name (2) protocol on the driver binging handle. + // + Iso639Language = FALSE; + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiComponentName2ProtocolGuid, + (VOID **) &ComponentName + ); + if (EFI_ERROR (Status)) { + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiComponentNameProtocolGuid, + (VOID **) &ComponentName + ); + if (!EFI_ERROR (Status)) { + Iso639Language = TRUE; + } + } + + if (!EFI_ERROR (Status)) { + LanguageVariable = GetEfiGlobalVariable (Iso639Language ? L"Lang" : L"PlatformLang"); + BestLanguage = GetBestLanguage( + ComponentName->SupportedLanguages, + Iso639Language, + (LanguageVariable != NULL) ? LanguageVariable : "", + Iso639Language ? "eng" : "en-US", + NULL + ); + if (LanguageVariable != NULL) { + FreePool (LanguageVariable); + } + + Status = ComponentName->GetControllerName ( + ComponentName, + ControllerHandle, + ChildHandle, + BestLanguage, + &ControllerName + ); + } + + if (!EFI_ERROR (Status)) { + return AllocateCopyPool (StrSize (ControllerName), ControllerName); + } else { + return ConvertDevicePathToText ( + DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle), + FALSE, + FALSE + ); + } +} + +/** + Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol + + @param DriverHealthInfo Pointer to the Driver Health information entry. +**/ +VOID +BmDisplayMessages ( + IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo + ) +{ + UINTN Index; + EFI_STRING String; + CHAR16 *ControllerName; + + if (DriverHealthInfo->MessageList == NULL || + DriverHealthInfo->MessageList[0].HiiHandle == NULL) { + return; + } + + ControllerName = BmGetControllerName ( + DriverHealthInfo->DriverHealthHandle, + DriverHealthInfo->ControllerHandle, + DriverHealthInfo->ChildHandle + ); + + DEBUG ((EFI_D_INFO, "Controller: %s\n", ControllerName)); + Print (L"Controller: %s\n", ControllerName); + for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) { + String = HiiGetString ( + DriverHealthInfo->MessageList[Index].HiiHandle, + DriverHealthInfo->MessageList[Index].StringId, + NULL + ); + if (String != NULL) { + Print (L" %s\n", String); + DEBUG ((EFI_D_INFO, " %s\n", String)); + FreePool (String); + } + } + + if (ControllerName != NULL) { + FreePool (ControllerName); + } +} + +/** + The repair notify function. + @param Value A value between 0 and Limit that identifies the current progress + of the repair operation. + @param Limit The maximum value of Value for the current repair operation. + If Limit is 0, then the completion progress is indeterminate. + For example, a driver that wants to specify progress in percent + would use a Limit value of 100. + + @retval EFI_SUCCESS Successfully return from the notify function. +**/ +EFI_STATUS +EFIAPI +BmRepairNotify ( + IN UINTN Value, + IN UINTN Limit + ) +{ + DEBUG ((EFI_D_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit)); + Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit); + + return EFI_SUCCESS; +} + +/** + Collect the Driver Health status of a single controller. + + @param DriverHealthInfo A pointer to the array containing all of the platform driver health information. + @param Count Return the updated array count. + @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved. + @param ControllerHandle The handle of the controller.. + @param ChildHandle The handle of the child controller to retrieve the health + status on. This is an optional parameter that may be NULL. + + @retval Status The status returned from GetHealthStatus. + @retval EFI_ABORTED The health status is healthy so no further query is needed. + +**/ +EFI_STATUS +BmGetSingleControllerHealthStatus ( + IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo, + IN OUT UINTN *Count, + IN EFI_HANDLE DriverHealthHandle, + IN EFI_HANDLE ControllerHandle, OPTIONAL + IN EFI_HANDLE ChildHandle OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth; + EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList; + EFI_HII_HANDLE FormHiiHandle; + EFI_DRIVER_HEALTH_STATUS HealthStatus; + + ASSERT (DriverHealthHandle != NULL); + // + // Retrieve the Driver Health Protocol from DriverHandle + // + Status = gBS->HandleProtocol ( + DriverHealthHandle, + &gEfiDriverHealthProtocolGuid, + (VOID **) &DriverHealth + ); + ASSERT_EFI_ERROR (Status); + + + if (ControllerHandle == NULL) { + // + // If ControllerHandle is NULL, the return the cumulative health status of the driver + // + Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL); + if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) { + *DriverHealthInfo = ReallocatePool ( + (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + *DriverHealthInfo + ); + ASSERT (*DriverHealthInfo != NULL); + + (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle; + (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth; + (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus; + + *Count = *Count + 1; + + Status = EFI_ABORTED; + } + return Status; + } + + MessageList = NULL; + FormHiiHandle = NULL; + + // + // Collect the health status with the optional HII message list + // + Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle); + if (!EFI_ERROR (Status)) { + *DriverHealthInfo = ReallocatePool ( + (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO), + *DriverHealthInfo + ); + ASSERT (*DriverHealthInfo != NULL); + (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth; + (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle; + (*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle; + (*DriverHealthInfo)[*Count].ChildHandle = ChildHandle; + (*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle; + (*DriverHealthInfo)[*Count].MessageList = MessageList; + (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus; + + *Count = *Count + 1; + } + + return Status; +} + +/** + Return all the Driver Health information. + + When the cumulative health status of all the controllers managed by the + driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such + EFI_DRIVER_HEALTH_PROTOCOL instance. + Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO + entry. Additionally every child controller creates one + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver. + + @param Count Return the count of the Driver Health information. + + @retval NULL No Driver Health information is returned. + @retval !NULL Pointer to the Driver Health information array. +**/ +EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO * +EFIAPI +EfiBootManagerGetDriverHealthInfo ( + UINTN *Count + ) +{ + EFI_STATUS Status; + UINTN NumHandles; + EFI_HANDLE *DriverHealthHandles; + EFI_DRIVER_HEALTH_STATUS HealthStatus; + UINTN DriverHealthIndex; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN ControllerIndex; + UINTN ChildIndex; + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo; + + // + // Initialize local variables + // + *Count = 0; + DriverHealthInfo = NULL; + Handles = NULL; + DriverHealthHandles = NULL; + NumHandles = 0; + HandleCount = 0; + + HealthStatus = EfiDriverHealthStatusHealthy; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiDriverHealthProtocolGuid, + NULL, + &NumHandles, + &DriverHealthHandles + ); + + if (Status == EFI_NOT_FOUND || NumHandles == 0) { + // + // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND + // + return NULL; + } + + ASSERT_EFI_ERROR (Status); + ASSERT (DriverHealthHandles != NULL); + + // + // Check the health status of all controllers in the platform + // Start by looping through all the Driver Health Protocol handles in the handle database + // + for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) { + // + // Get the cumulative health status of the driver + // + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL); + if (EFI_ERROR (Status)) { + continue; + } + + // + // See if the list of all handles in the handle database has been retrieved + // + // + if (Handles == NULL) { + // + // Retrieve the list of all handles from the handle database + // + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &HandleCount, + &Handles + ); + ASSERT_EFI_ERROR (Status); + } + // + // Loop through all the controller handles in the handle database + // + for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) { + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Loop through all the child handles in the handle database + // + for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) { + Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]); + if (EFI_ERROR (Status)) { + continue; + } + } + } + } + + Status = EFI_SUCCESS; + + if (Handles != NULL) { + FreePool (Handles); + } + if (DriverHealthHandles != NULL) { + FreePool (DriverHealthHandles); + } + + return DriverHealthInfo; +} + +/** + Free the Driver Health information array. + + @param DriverHealthInfo Pointer to array of the Driver Health information. + @param Count Count of the array. + + @retval EFI_SUCCESS The array is freed. + @retval EFI_INVALID_PARAMETER The array is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeDriverHealthInfo ( + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo, + UINTN Count + ) +{ + UINTN Index; + + for (Index = 0; Index < Count; Index++) { + if (DriverHealthInfo[Index].MessageList != NULL) { + FreePool (DriverHealthInfo[Index].MessageList); + } + } + return gBS->FreePool (DriverHealthInfo); +} + +/** + Repair all the controllers according to the Driver Health status queried. +**/ +VOID +BmRepairAllControllers ( + VOID + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo; + EFI_DRIVER_HEALTH_STATUS HealthStatus; + UINTN Count; + UINTN Index; + BOOLEAN RepairRequired; + BOOLEAN ConfigurationRequired; + BOOLEAN ReconnectRequired; + BOOLEAN RebootRequired; + EFI_HII_HANDLE *HiiHandles; + EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + + // + // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check. + // + if (CompareGuid (PcdGetPtr (PcdDriverHealthConfigureForm), &gZeroGuid)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2); + ASSERT_EFI_ERROR (Status); + + do { + RepairRequired = FALSE; + ConfigurationRequired = FALSE; + + // + // Deal with Repair Required + // + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) { + ConfigurationRequired = TRUE; + } + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) { + RepairRequired = TRUE; + + BmDisplayMessages (&DriverHealthInfo[Index]); + + Status = DriverHealthInfo[Index].DriverHealth->Repair ( + DriverHealthInfo[Index].DriverHealth, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle, + BmRepairNotify + ); + if (!EFI_ERROR (Status) && !ConfigurationRequired) { + Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus ( + DriverHealthInfo[Index].DriverHealth, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle, + &HealthStatus, + NULL, + NULL + ); + if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) { + ConfigurationRequired = TRUE; + } + } + } + } + + if (ConfigurationRequired) { + HiiHandles = HiiGetHiiHandles (NULL); + if (HiiHandles != NULL) { + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + Status = FormBrowser2->SendForm ( + FormBrowser2, + &HiiHandles[Index], + 1, + PcdGetPtr (PcdDriverHealthConfigureForm), + 0, + NULL, + NULL + ); + if (!EFI_ERROR (Status)) { + break; + } + } + FreePool (HiiHandles); + } + } + + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + } while (RepairRequired || ConfigurationRequired); + + RebootRequired = FALSE; + ReconnectRequired = FALSE; + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + + BmDisplayMessages (&DriverHealthInfo[Index]); + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) { + Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL); + if (EFI_ERROR (Status)) { + // + // Disconnect failed. Need to promote reconnect to a reboot. + // + RebootRequired = TRUE; + } else { + gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE); + ReconnectRequired = TRUE; + } + } + + if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) { + RebootRequired = TRUE; + } + } + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + + + if (ReconnectRequired) { + BmRepairAllControllers (); + } + + DEBUG_CODE ( + CHAR16 *ControllerName; + + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + ControllerName = BmGetControllerName ( + DriverHealthInfo[Index].DriverHealthHandle, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle + ); + DEBUG (( + EFI_D_INFO, + "%02d: %s - %s\n", + Index, + ControllerName, + mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus] + )); + if (ControllerName != NULL) { + FreePool (ControllerName); + } + } + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + ); + + if (RebootRequired) { + DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n")); + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c new file mode 100644 index 0000000000..6a770f940c --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c @@ -0,0 +1,1101 @@ +/** @file + Hotkey library functions. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +// +// Lock for linked list +// +EFI_LOCK mBmHotkeyLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY); +LIST_ENTRY mBmHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList); +EFI_EVENT mBmHotkeyTriggered = NULL; +BOOLEAN mBmHotkeyServiceStarted = FALSE; +UINTN mBmHotkeySupportCount = 0; + +// +// Set OptionNumber as unassigned value to indicate the option isn't initialized +// +EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption = { LoadOptionNumberUnassigned }; + +EFI_BOOT_MANAGER_KEY_OPTION *mBmContinueKeyOption = NULL; +VOID *mBmTxtInExRegistration = NULL; + +/** + + Check whether the input key option is valid. + + @param KeyOption Input key option info. + + @retval TRUE Input key option is valid. + @retval FALSE Input key option is not valid. +**/ +BOOLEAN +BmIsKeyOptionValid ( + IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption +) +{ + UINT16 OptionName[sizeof (L"Boot####")]; + UINT8 *BootOption; + UINTN BootOptionSize; + UINT32 Crc; + + // + // Check whether corresponding Boot Option exist + // + UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", KeyOption->BootOption); + GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize); + + if (BootOption == NULL) { + return FALSE; + } + + // + // Check CRC for Boot Option + // + gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc); + FreePool (BootOption); + + return (BOOLEAN) (KeyOption->BootOptionCrc == Crc); +} + +/** + + Check whether the input variable is an key option variable. + + @param Name Input variable name. + @param Guid Input variable guid. + @param OptionNumber The option number of this key option variable. + + @retval TRUE Input variable is a key option variable. + @retval FALSE Input variable is not a key option variable. +**/ +BOOLEAN +BmIsKeyOptionVariable ( + CHAR16 *Name, + EFI_GUID *Guid, + UINT16 *OptionNumber + ) +{ + UINTN Index; + + if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) || + (StrSize (Name) != sizeof (L"Key####")) || + (StrnCmp (Name, L"Key", 3) != 0) + ) { + return FALSE; + } + + *OptionNumber = 0; + for (Index = 3; Index < 7; Index++) { + if ((Name[Index] >= L'0') && (Name[Index] <= L'9')) { + *OptionNumber = *OptionNumber * 16 + Name[Index] - L'0'; + } else if ((Name[Index] >= L'A') && (Name[Index] <= L'F')) { + *OptionNumber = *OptionNumber * 16 + Name[Index] - L'A' + 10; + } else { + return FALSE; + } + } + + return TRUE; +} + +/** + Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data. + + @param KeyOption The input key option info. + + @retval The buffer size of the key option data. +**/ +UINTN +BmSizeOfKeyOption ( + EFI_BOOT_MANAGER_KEY_OPTION *KeyOption + ) +{ + return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys) + + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY); +} + +/** + Return the array of key options. + + @param Count Return the number of key options. + + @retval NULL No key option. + @retval Other Pointer to the key options. +**/ +EFI_BOOT_MANAGER_KEY_OPTION * +BmGetKeyOptions ( + OUT UINTN *Count + ) +{ + EFI_STATUS Status; + UINTN Index; + CHAR16 *Name; + EFI_GUID Guid; + UINTN NameSize; + UINTN NewNameSize; + EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; + EFI_BOOT_MANAGER_KEY_OPTION *KeyOption; + UINT16 OptionNumber; + + if (Count == NULL) { + return NULL; + } + + *Count = 0; + KeyOptions = NULL; + + NameSize = sizeof (CHAR16); + Name = AllocateZeroPool (NameSize); + ASSERT (Name != NULL); + while (TRUE) { + NewNameSize = NameSize; + Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); + if (Status == EFI_BUFFER_TOO_SMALL) { + Name = ReallocatePool (NameSize, NewNameSize, Name); + ASSERT (Name != NULL); + Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); + NameSize = NewNameSize; + } + + if (Status == EFI_NOT_FOUND) { + break; + } + ASSERT_EFI_ERROR (Status); + + if (BmIsKeyOptionVariable (Name ,&Guid, &OptionNumber)) { + GetEfiGlobalVariable2 (Name, (VOID**) &KeyOption, NULL); + ASSERT (KeyOption != NULL); + if (BmIsKeyOptionValid (KeyOption)) { + KeyOptions = ReallocatePool ( + *Count * sizeof (EFI_BOOT_MANAGER_KEY_OPTION), + (*Count + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION), + KeyOptions + ); + ASSERT (KeyOptions != NULL); + // + // Insert the key option in order + // + for (Index = 0; Index < *Count; Index++) { + if (OptionNumber < KeyOptions[Index].OptionNumber) { + break; + } + } + CopyMem (&KeyOptions[Index + 1], &KeyOptions[Index], (*Count - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + CopyMem (&KeyOptions[Index], KeyOption, BmSizeOfKeyOption (KeyOption)); + KeyOptions[Index].OptionNumber = OptionNumber; + (*Count)++; + } + FreePool (KeyOption); + } + } + + FreePool (Name); + + return KeyOptions; +} + +/** + Callback function for event. + + @param Event Event for this callback function. + @param Context Context pass to this function. +**/ +VOID +EFIAPI +BmEmptyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + Check whether the bit is set in the value. + + @param Value The value need to be check. + @param Bit The bit filed need to be check. + + @retval TRUE The bit is set. + @retval FALSE The bit is not set. +**/ +BOOLEAN +BmBitSet ( + IN UINT32 Value, + IN UINT32 Bit + ) +{ + return (BOOLEAN) ((Value & Bit) != 0); +} + +/** + Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION. + + @param Modifier Input key info. + @param Args Va_list info. + @param KeyOption Key info which need to update. + + @retval EFI_SUCCESS Succeed to initialize the KeyData and Key[]. + @return EFI_INVALID_PARAMETER Input parameter error. +**/ +EFI_STATUS +BmInitializeKeyFields ( + IN UINT32 Modifier, + IN VA_LIST Args, + OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption + ) +{ + EFI_INPUT_KEY *Key; + + if (KeyOption == NULL) { + return EFI_INVALID_PARAMETER; + } + + Key = NULL; + while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) { + Key = VA_ARG (Args, EFI_INPUT_KEY *); + if (Key == NULL) { + break; + } + CopyMem ( + &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount], + Key, + sizeof (EFI_INPUT_KEY) + ); + KeyOption->KeyData.Options.InputKeyCount++; + } + + if (Key != NULL) { + // + // Too many keys + // + return EFI_INVALID_PARAMETER; + } + + if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED + | EFI_BOOT_MANAGER_CONTROL_PRESSED + | EFI_BOOT_MANAGER_ALT_PRESSED + | EFI_BOOT_MANAGER_LOGO_PRESSED + | EFI_BOOT_MANAGER_MENU_KEY_PRESSED + | EFI_BOOT_MANAGER_SYS_REQ_PRESSED + )) != 0) { + return EFI_INVALID_PARAMETER; + } + + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) { + KeyOption->KeyData.Options.ShiftPressed = 1; + } + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) { + KeyOption->KeyData.Options.ControlPressed = 1; + } + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) { + KeyOption->KeyData.Options.AltPressed = 1; + } + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) { + KeyOption->KeyData.Options.LogoPressed = 1; + } + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) { + KeyOption->KeyData.Options.MenuPressed = 1; + } + if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) { + KeyOption->KeyData.Options.SysReqPressed = 1; + } + + return EFI_SUCCESS; +} + +/** + Try to boot the boot option triggered by hot key. +**/ +VOID +EFIAPI +EfiBootManagerHotkeyBoot ( + VOID + ) +{ + if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) { + EfiBootManagerBoot (&mBmHotkeyBootOption); + EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption); + mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned; + } +} + +/** + This is the common notification function for HotKeys, it will be registered + with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle. + + @param KeyData A pointer to a buffer that is filled in with the keystroke + information for the key that was pressed. + + @retval EFI_SUCCESS KeyData is successfully processed. + @return EFI_NOT_FOUND Fail to find boot option variable. +**/ +EFI_STATUS +EFIAPI +BmHotkeyCallback ( + IN EFI_KEY_DATA *KeyData +) +{ + LIST_ENTRY *Link; + BM_HOTKEY *Hotkey; + CHAR16 OptionName[sizeof ("Boot####")]; + EFI_STATUS Status; + EFI_KEY_DATA *HotkeyData; + + if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) { + // + // Do not process sequential hotkey stroke until the current boot option returns + // + return EFI_SUCCESS; + } + + DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar)); + + EfiAcquireLock (&mBmHotkeyLock); + for ( Link = GetFirstNode (&mBmHotkeyList) + ; !IsNull (&mBmHotkeyList, Link) + ; Link = GetNextNode (&mBmHotkeyList, Link) + ) { + Hotkey = BM_HOTKEY_FROM_LINK (Link); + + // + // Is this Key Stroke we are waiting for? + // + ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0]))); + HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey]; + if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) && + (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) && + (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ? + (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE + ) + ) { + + // + // Receive an expecting key stroke, transit to next waiting state + // + Hotkey->WaitingKey++; + + if (Hotkey->WaitingKey == Hotkey->CodeCount) { + // + // Reset to initial waiting state + // + Hotkey->WaitingKey = 0; + // + // Received the whole key stroke sequence + // + Status = gBS->SignalEvent (mBmHotkeyTriggered); + ASSERT_EFI_ERROR (Status); + + if (!Hotkey->IsContinue) { + // + // Launch its BootOption + // + UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", Hotkey->BootOption); + Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption); + DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status)); + if (EFI_ERROR (Status)) { + mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned; + } + } else { + DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n")); + } + } + } else { + // + // Receive an unexpected key stroke, reset to initial waiting state + // + Hotkey->WaitingKey = 0; + } + + } + EfiReleaseLock (&mBmHotkeyLock); + + return EFI_SUCCESS; +} + +/** + Unregister hotkey notify list. + + @param Hotkey Hotkey list. + + @retval EFI_SUCCESS Unregister hotkey notify success. + @retval Others Unregister hotkey notify failed. +**/ +EFI_STATUS +BmUnregisterHotkeyNotify ( + IN BM_HOTKEY *Hotkey + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN KeyIndex; + EFI_HANDLE *Handles; + UINTN HandleCount; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + VOID *NotifyHandle; + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx); + ASSERT_EFI_ERROR (Status); + for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) { + Status = TxtInEx->RegisterKeyNotify ( + TxtInEx, + &Hotkey->KeyData[KeyIndex], + BmHotkeyCallback, + &NotifyHandle + ); + if (!EFI_ERROR (Status)) { + Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle); + DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status)); + } + } + } + + return EFI_SUCCESS; +} + +/** + Register hotkey notify list. + + @param TxtInEx Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol. + @param Hotkey Hotkey list. + + @retval EFI_SUCCESS Register hotkey notify success. + @retval Others Register hotkey notify failed. +**/ +EFI_STATUS +BmRegisterHotkeyNotify ( + IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx, + IN BM_HOTKEY *Hotkey + ) +{ + EFI_STATUS Status; + UINTN Index; + VOID *NotifyHandle; + + for (Index = 0; Index < Hotkey->CodeCount; Index++) { + Status = TxtInEx->RegisterKeyNotify ( + TxtInEx, + &Hotkey->KeyData[Index], + BmHotkeyCallback, + &NotifyHandle + ); + DEBUG (( + EFI_D_INFO, + "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n", + Hotkey->KeyData[Index].Key.ScanCode, + Hotkey->KeyData[Index].Key.UnicodeChar, + Hotkey->KeyData[Index].KeyState.KeyShiftState, + Hotkey->KeyData[Index].KeyState.KeyToggleState, + Status + )); + if (EFI_ERROR (Status)) { + // + // some of the hotkey registry failed + // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R + // + break; + } + } + + return EFI_SUCCESS; +} + +/** + Generate key shift state base on the input key option info. + + @param Depth Which key is checked. + @param KeyOption Input key option info. + @param KeyShiftState Input key shift state. + @param KeyShiftStates Return possible key shift state array. + @param KeyShiftStateCount Possible key shift state count. +**/ +VOID +BmGenerateKeyShiftState ( + IN UINTN Depth, + IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption, + IN UINT32 KeyShiftState, + IN UINT32 *KeyShiftStates, + IN UINTN *KeyShiftStateCount + ) +{ + switch (Depth) { + case 0: + if (KeyOption->KeyData.Options.ShiftPressed) { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount); + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount); + } else { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount); + } + break; + + case 1: + if (KeyOption->KeyData.Options.ControlPressed) { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount); + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount); + } else { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount); + } + break; + + case 2: + if (KeyOption->KeyData.Options.AltPressed) { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount); + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount); + } else { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount); + } + break; + case 3: + if (KeyOption->KeyData.Options.LogoPressed) { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount); + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount); + } else { + BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount); + } + break; + case 4: + if (KeyOption->KeyData.Options.MenuPressed) { + KeyShiftState |= EFI_MENU_KEY_PRESSED; + } + if (KeyOption->KeyData.Options.SysReqPressed) { + KeyShiftState |= EFI_SYS_REQ_PRESSED; + } + KeyShiftStates[*KeyShiftStateCount] = KeyShiftState; + (*KeyShiftStateCount)++; + break; + } +} + +/** + Add it to hot key database, register it to existing TxtInEx. + New TxtInEx will be automatically registered with all the hot key in dababase + + @param KeyOption Input key option info. +**/ +EFI_STATUS +BmProcessKeyOption ( + IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + EFI_HANDLE *Handles; + UINTN HandleCount; + UINTN HandleIndex; + UINTN Index; + BM_HOTKEY *Hotkey; + UINTN KeyIndex; + // + // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX + // + UINT32 KeyShiftStates[16]; + UINTN KeyShiftStateCount; + + if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) { + return EFI_UNSUPPORTED; + } + + KeyShiftStateCount = 0; + BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount); + ASSERT (KeyShiftStateCount <= sizeof (KeyShiftStates) / sizeof (KeyShiftStates[0])); + + EfiAcquireLock (&mBmHotkeyLock); + + for (Index = 0; Index < KeyShiftStateCount; Index++) { + Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY)); + ASSERT (Hotkey != NULL); + + Hotkey->Signature = BM_HOTKEY_SIGNATURE; + Hotkey->BootOption = KeyOption->BootOption; + Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption); + Hotkey->CodeCount = (UINT8) KeyOption->KeyData.Options.InputKeyCount; + + for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) { + CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY)); + Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index]; + } + InsertTailList (&mBmHotkeyList, &Hotkey->Link); + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleTextInputExProtocolGuid, + NULL, + &HandleCount, + &Handles + ); + for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { + Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx); + ASSERT_EFI_ERROR (Status); + BmRegisterHotkeyNotify (TxtInEx, Hotkey); + } + } + + EfiReleaseLock (&mBmHotkeyLock); + + return EFI_SUCCESS; +} + +/** + Callback function for SimpleTextInEx protocol install events + + @param Event the event that is signaled. + @param Context not used here. + +**/ +VOID +EFIAPI +BmTxtInExCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + EFI_HANDLE Handle; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + LIST_ENTRY *Link; + + while (TRUE) { + BufferSize = sizeof (EFI_HANDLE); + Status = gBS->LocateHandle ( + ByRegisterNotify, + NULL, + mBmTxtInExRegistration, + &BufferSize, + &Handle + ); + if (EFI_ERROR (Status)) { + // + // If no more notification events exist + // + return ; + } + + Status = gBS->HandleProtocol ( + Handle, + &gEfiSimpleTextInputExProtocolGuid, + (VOID **) &TxtInEx + ); + ASSERT_EFI_ERROR (Status); + + // + // Register the hot key notification for the existing items in the list + // + EfiAcquireLock (&mBmHotkeyLock); + for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) { + BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link)); + } + EfiReleaseLock (&mBmHotkeyLock); + } +} + +/** + Free the key options returned from BmGetKeyOptions. + + @param KeyOptions Pointer to the key options. + @param KeyOptionCount Number of the key options. + + @retval EFI_SUCCESS The key options are freed. + @retval EFI_NOT_FOUND KeyOptions is NULL. +**/ +EFI_STATUS +BmFreeKeyOptions ( + IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions, + IN UINTN KeyOptionCount + ) +{ + if (KeyOptions != NULL) { + FreePool (KeyOptions); + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Register the key option to exit the waiting of the Boot Manager timeout. + Platform should ensure that the continue key option isn't conflict with + other boot key options. + + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS Successfully register the continue key option. + @retval EFI_ALREADY_STARTED The continue key option is already registered. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerRegisterContinueKeyOption ( + IN UINT32 Modifier, + ... + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_KEY_OPTION KeyOption; + VA_LIST Args; + + if (mBmContinueKeyOption != NULL) { + return EFI_ALREADY_STARTED; + } + + ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + VA_START (Args, Modifier); + Status = BmInitializeKeyFields (Modifier, Args, &KeyOption); + VA_END (Args); + + if (!EFI_ERROR (Status)) { + mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption); + ASSERT (mBmContinueKeyOption != NULL); + if (mBmHotkeyServiceStarted) { + BmProcessKeyOption (mBmContinueKeyOption); + } + } + + return Status; +} + +/** + Stop the hotkey processing. + + @param Event Event pointer related to hotkey service. + @param Context Context pass to this function. +**/ +VOID +EFIAPI +BmStopHotkeyService ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Link; + BM_HOTKEY *Hotkey; + + DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n")); + gBS->CloseEvent (Event); + + EfiAcquireLock (&mBmHotkeyLock); + for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) { + Hotkey = BM_HOTKEY_FROM_LINK (Link); + BmUnregisterHotkeyNotify (Hotkey); + Link = RemoveEntryList (Link); + FreePool (Hotkey); + } + EfiReleaseLock (&mBmHotkeyLock); +} + +/** + Start the hot key service so that the key press can trigger the boot option. + + @param HotkeyTriggered Return the waitable event and it will be signaled + when a valid hot key is pressed. + + @retval EFI_SUCCESS The hot key service is started. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerStartHotkeyService ( + IN EFI_EVENT *HotkeyTriggered + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; + UINTN KeyOptionCount; + UINTN Index; + EFI_EVENT Event; + UINT32 *BootOptionSupport; + + Status = GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL); + ASSERT (BootOptionSupport != NULL); + + if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY) != 0) { + mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT)); + } + FreePool (BootOptionSupport); + + if (mBmHotkeySupportCount == 0) { + DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n")); + return EFI_UNSUPPORTED; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_WAIT, + TPL_CALLBACK, + BmEmptyFunction, + NULL, + &mBmHotkeyTriggered + ); + ASSERT_EFI_ERROR (Status); + + if (HotkeyTriggered != NULL) { + *HotkeyTriggered = mBmHotkeyTriggered; + } + + KeyOptions = BmGetKeyOptions (&KeyOptionCount); + for (Index = 0; Index < KeyOptionCount; Index ++) { + BmProcessKeyOption (&KeyOptions[Index]); + } + BmFreeKeyOptions (KeyOptions, KeyOptionCount); + + if (mBmContinueKeyOption != NULL) { + BmProcessKeyOption (mBmContinueKeyOption); + } + + EfiCreateProtocolNotifyEvent ( + &gEfiSimpleTextInputExProtocolGuid, + TPL_CALLBACK, + BmTxtInExCallback, + NULL, + &mBmTxtInExRegistration + ); + + Status = EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + BmStopHotkeyService, + NULL, + &Event + ); + ASSERT_EFI_ERROR (Status); + + + mBmHotkeyServiceStarted = TRUE; + return Status; +} + +/** + Add the key option. + It adds the key option variable and the key option takes affect immediately. + + @param AddedOption Return the added key option. + @param BootOptionNumber The boot option number for the key option. + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS The key option is added. + @retval EFI_ALREADY_STARTED The hot key is already used by certain key option. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerAddKeyOptionVariable ( + OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption, OPTIONAL + IN UINT16 BootOptionNumber, + IN UINT32 Modifier, + ... + ) +{ + EFI_STATUS Status; + VA_LIST Args; + VOID *BootOption; + UINTN BootOptionSize; + CHAR16 BootOptionName[sizeof (L"Boot####")]; + EFI_BOOT_MANAGER_KEY_OPTION KeyOption; + EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; + UINTN KeyOptionCount; + UINTN Index; + UINTN KeyOptionNumber; + CHAR16 KeyOptionName[sizeof (L"Key####")]; + + UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootOptionNumber); + GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize); + + if (BootOption == NULL) { + return EFI_NOT_FOUND; + } + + ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + KeyOption.BootOption = BootOptionNumber; + Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc); + ASSERT_EFI_ERROR (Status); + FreePool (BootOption); + + VA_START (Args, Modifier); + Status = BmInitializeKeyFields (Modifier, Args, &KeyOption); + VA_END (Args); + if (EFI_ERROR (Status)) { + return Status; + } + + KeyOptionNumber = LoadOptionNumberUnassigned; + // + // Check if the hot key sequence was defined already + // + KeyOptions = BmGetKeyOptions (&KeyOptionCount); + for (Index = 0; Index < KeyOptionCount; Index++) { + if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) && + (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) { + break; + } + + if ((KeyOptionNumber == LoadOptionNumberUnassigned) && + (KeyOptions[Index].OptionNumber > Index) + ){ + KeyOptionNumber = Index; + } + } + BmFreeKeyOptions (KeyOptions, KeyOptionCount); + + if (Index < KeyOptionCount) { + return EFI_ALREADY_STARTED; + } + + if (KeyOptionNumber == LoadOptionNumberUnassigned) { + KeyOptionNumber = KeyOptionCount; + } + + UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber); + + Status = gRT->SetVariable ( + KeyOptionName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + BmSizeOfKeyOption (&KeyOption), + &KeyOption + ); + if (!EFI_ERROR (Status)) { + // + // Return the Key Option in case needed by caller + // + if (AddedOption != NULL) { + CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + } + + // + // Register the newly added hot key + // Calling this function before EfiBootManagerStartHotkeyService doesn't + // need to call BmProcessKeyOption + // + if (mBmHotkeyServiceStarted) { + BmProcessKeyOption (&KeyOption); + } + } + + return Status; +} + +/** + Delete the Key Option variable and unregister the hot key + + @param DeletedOption Return the deleted key options. + @param Modifier Key shift state. + @param ... Parameter list of pointer of EFI_INPUT_KEY. + + @retval EFI_SUCCESS The key option is deleted. + @retval EFI_NOT_FOUND The key option cannot be found. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerDeleteKeyOptionVariable ( + IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL + IN UINT32 Modifier, + ... + ) +{ + EFI_STATUS Status; + UINTN Index; + VA_LIST Args; + EFI_BOOT_MANAGER_KEY_OPTION KeyOption; + EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; + UINTN KeyOptionCount; + LIST_ENTRY *Link; + BM_HOTKEY *Hotkey; + UINT32 ShiftState; + BOOLEAN Match; + CHAR16 KeyOptionName[sizeof (L"Key####")]; + + ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + VA_START (Args, Modifier); + Status = BmInitializeKeyFields (Modifier, Args, &KeyOption); + VA_END (Args); + + if (EFI_ERROR (Status)) { + return Status; + } + + EfiAcquireLock (&mBmHotkeyLock); + // + // Delete the key option from active hot key list + // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT + // + for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) { + Hotkey = BM_HOTKEY_FROM_LINK (Link); + Match = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount); + + for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) { + ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState; + if ( + (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) || + (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) || + (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) || + (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) || + (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) || + (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) || + (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0) + ) { + // + // Break when any field doesn't match + // + Match = FALSE; + break; + } + } + + if (Match) { + Link = RemoveEntryList (Link); + FreePool (Hotkey); + } else { + Link = GetNextNode (&mBmHotkeyList, Link); + } + } + + // + // Delete the key option from the variable + // + Status = EFI_NOT_FOUND; + KeyOptions = BmGetKeyOptions (&KeyOptionCount); + for (Index = 0; Index < KeyOptionCount; Index++) { + if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) && + (CompareMem ( + KeyOptions[Index].Keys, KeyOption.Keys, + KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0) + ) { + UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber); + Status = gRT->SetVariable ( + KeyOptionName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + 0, + NULL + ); + // + // Return the deleted key option in case needed by caller + // + if (DeletedOption != NULL) { + CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); + } + break; + } + } + BmFreeKeyOptions (KeyOptions, KeyOptionCount); + + EfiReleaseLock (&mBmHotkeyLock); + + return Status; +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c new file mode 100644 index 0000000000..fc3e29a62e --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c @@ -0,0 +1,982 @@ +/** @file + Load option library functions which relate with creating and processing load options. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +/** + Get the Option Number that wasn't used. + + @param OrderVariableName Could be L"BootOrder" or L"DriverOrder". + @param FreeOptionNumber To receive the minimal free option number. + + @retval EFI_SUCCESS The option number is found + @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used. + @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL + +**/ +EFI_STATUS +BmGetFreeOptionNumber ( + IN CHAR16 *OrderVariableName, + OUT UINT16 *FreeOptionNumber + ) +{ + + UINTN OptionNumber; + UINTN Index; + UINT16 *OptionOrder; + UINTN OptionOrderSize; + UINT16 *BootNext; + + if (FreeOptionNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + GetEfiGlobalVariable2 (OrderVariableName, (VOID **) &OptionOrder, &OptionOrderSize); + BootNext = NULL; + if (*OrderVariableName == L'B') { + GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL); + } + + for (OptionNumber = 0; + OptionNumber < OptionOrderSize / sizeof (UINT16) + + ((BootNext != NULL) ? 1 : 0); + OptionNumber++ + ) { + // + // Search in OptionOrder whether the OptionNumber exists + // + for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { + if (OptionNumber == OptionOrder[Index]) { + break; + } + } + + // + // We didn't find it in the ****Order array and it doesn't equal to BootNext + // Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1 + // + if ((Index == OptionOrderSize / sizeof (UINT16)) && + ((BootNext == NULL) || (OptionNumber != *BootNext)) + ) { + break; + } + } + if (OptionOrder != NULL) { + FreePool (OptionOrder); + } + + if (BootNext != NULL) { + FreePool (BootNext); + } + + // + // When BootOrder & BootNext conver all numbers in the range [0 ... 0xffff], + // OptionNumber equals to 0x10000 which is not valid. + // + ASSERT (OptionNumber <= 0x10000); + if (OptionNumber == 0x10000) { + return EFI_OUT_OF_RESOURCES; + } else { + *FreeOptionNumber = (UINT16) OptionNumber; + return EFI_SUCCESS; + } +} + +/** + Update order variable . + + @param OptionOrderName Order variable name which need to be updated. + @param OptionNumber Option number for the new option. + @param Position Position of the new load option to put in the ****Order variable. + + @retval EFI_SUCCESS The boot#### or driver#### have been successfully registered. + @retval EFI_ALREADY_STARTED The option number of Option is being used already. + @retval EFI_STATUS Return the status of gRT->SetVariable (). + +**/ +EFI_STATUS +BmAddOptionNumberToOrderVariable ( + IN CHAR16 *OptionOrderName, + IN UINT16 OptionNumber, + IN UINTN Position + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT16 *OptionOrder; + UINT16 *NewOptionOrder; + UINTN OptionOrderSize; + // + // Update the option order variable + // + GetEfiGlobalVariable2 (OptionOrderName, (VOID **) &OptionOrder, &OptionOrderSize); + + Status = EFI_SUCCESS; + for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { + if (OptionOrder[Index] == OptionNumber) { + Status = EFI_ALREADY_STARTED; + break; + } + } + + if (!EFI_ERROR (Status)) { + Position = MIN (Position, OptionOrderSize / sizeof (UINT16)); + + NewOptionOrder = AllocatePool (OptionOrderSize + sizeof (UINT16)); + ASSERT (NewOptionOrder != NULL); + if (OptionOrderSize != 0) { + CopyMem (NewOptionOrder, OptionOrder, Position * sizeof (UINT16)); + CopyMem (&NewOptionOrder[Position + 1], &OptionOrder[Position], OptionOrderSize - Position * sizeof (UINT16)); + } + NewOptionOrder[Position] = OptionNumber; + + Status = gRT->SetVariable ( + OptionOrderName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + OptionOrderSize + sizeof (UINT16), + NewOptionOrder + ); + FreePool (NewOptionOrder); + } + + if (OptionOrder != NULL) { + FreePool (OptionOrder); + } + + return Status; +} + +/** + Create the Boot#### or Driver#### variable from the load option. + + @param LoadOption Pointer to the load option. + + @retval EFI_SUCCESS The variable was created. + @retval Others Error status returned by RT->SetVariable. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerLoadOptionToVariable ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Option + ) +{ + UINTN VariableSize; + UINT8 *Variable; + UINT8 *Ptr; + CHAR16 OptionName[sizeof ("Driver####")]; + CHAR16 *Description; + CHAR16 NullChar; + + if ((Option->OptionNumber == LoadOptionNumberUnassigned) || + (Option->FilePath == NULL) || + (Option->OptionType >= LoadOptionTypeMax) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert NULL description to empty description + // + NullChar = L'\0'; + Description = Option->Description; + if (Description == NULL) { + Description = &NullChar; + } + + /* + UINT32 Attributes; + UINT16 FilePathListLength; + CHAR16 Description[]; + EFI_DEVICE_PATH_PROTOCOL FilePathList[]; + UINT8 OptionalData[]; +TODO: FilePathList[] IS: +A packed array of UEFI device paths. The first element of the +array is a device path that describes the device and location of the +Image for this load option. The FilePathList[0] is specific +to the device type. Other device paths may optionally exist in the +FilePathList, but their usage is OSV specific. Each element +in the array is variable length, and ends at the device path end +structure. + */ + VariableSize = sizeof (Option->Attributes) + + sizeof (UINT16) + + StrSize (Description) + + GetDevicePathSize (Option->FilePath) + + Option->OptionalDataSize; + + Variable = AllocatePool (VariableSize); + ASSERT (Variable != NULL); + + Ptr = Variable; + *(UINT32 *) Ptr = Option->Attributes; + Ptr += sizeof (Option->Attributes); + *(UINT16 *) Ptr = (UINT16) GetDevicePathSize (Option->FilePath); + Ptr += sizeof (UINT16); + CopyMem (Ptr, Description, StrSize (Description)); + Ptr += StrSize (Description); + CopyMem (Ptr, Option->FilePath, GetDevicePathSize (Option->FilePath)); + Ptr += GetDevicePathSize (Option->FilePath); + CopyMem (Ptr, Option->OptionalData, Option->OptionalDataSize); + + UnicodeSPrint ( + OptionName, + sizeof (OptionName), + (Option->OptionType == LoadOptionTypeBoot) ? L"Boot%04x" : L"Driver%04x", + Option->OptionNumber + ); + + return gRT->SetVariable ( + OptionName, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + VariableSize, + Variable + ); +} + +/** + This function will register the new boot#### or driver#### option. + After the boot#### or driver#### updated, the BootOrder or DriverOrder will also be updated. + + @param Option Pointer to load option to add. + @param Position Position of the new load option to put in the ****Order variable. + + @retval EFI_SUCCESS The boot#### or driver#### have been successfully registered. + @retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF. + @retval EFI_ALREADY_STARTED The option number of Option is being used already. + Note: this API only adds new load option, no replacement support. + @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used when the + option number specified in the Option is LoadOptionNumberUnassigned. + @retval EFI_STATUS Return the status of gRT->SetVariable (). + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerAddLoadOptionVariable ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN Position + ) +{ + EFI_STATUS Status; + UINT16 OptionNumber; + + if (Option == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Get the free option number if the option number is unassigned + // + if (Option->OptionNumber == LoadOptionNumberUnassigned) { + Status = BmGetFreeOptionNumber ( + Option->OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", + &OptionNumber + ); + if (EFI_ERROR (Status)) { + return Status; + } + Option->OptionNumber = OptionNumber; + } + + if (Option->OptionNumber >= LoadOptionNumberMax) { + return EFI_INVALID_PARAMETER; + } + + Status = BmAddOptionNumberToOrderVariable ( + Option->OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", + (UINT16) Option->OptionNumber, + Position + ); + if (!EFI_ERROR (Status)) { + // + // Save the Boot#### or Driver#### variable + // + Status = EfiBootManagerLoadOptionToVariable (Option); + if (EFI_ERROR (Status)) { + // + // Remove the #### from *Order variable when the Boot####/Driver#### cannot be saved. + // + EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType); + } + } + + return Status; +} + +/** + Sort the load option. The DriverOrder or BootOrder will be re-created to + reflect the new order. + + @param OptionType Load option type + @param CompareFunction The comparator +**/ +VOID +EFIAPI +EfiBootManagerSortLoadOptionVariable ( + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, + SORT_COMPARE CompareFunction + ) +{ + EFI_STATUS Status; + EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption; + UINTN LoadOptionCount; + UINTN Index; + UINT16 *OptionOrder; + + LoadOption = EfiBootManagerGetLoadOptions (&LoadOptionCount, OptionType); + + // + // Insertion sort algorithm + // + PerformQuickSort ( + LoadOption, + LoadOptionCount, + sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), + CompareFunction + ); + + // + // Create new ****Order variable + // + OptionOrder = AllocatePool (LoadOptionCount * sizeof (UINT16)); + ASSERT (OptionOrder != NULL); + for (Index = 0; Index < LoadOptionCount; Index++) { + OptionOrder[Index] = (UINT16) LoadOption[Index].OptionNumber; + } + + Status = gRT->SetVariable ( + OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + LoadOptionCount * sizeof (UINT16), + OptionOrder + ); + // + // Changing the *Order content without increasing its size with current variable implementation shouldn't fail. + // + ASSERT_EFI_ERROR (Status); + + FreePool (OptionOrder); + EfiBootManagerFreeLoadOptions (LoadOption, LoadOptionCount); +} + +/** + Initialize a load option. + + @param Option Pointer to the load option to be initialized. + @param OptionNumber Option number of the load option. + @param OptionType Type of the load option. + @param Attributes Attributes of the load option. + @param Description Description of the load option. + @param FilePath Device path of the load option. + @param OptionalData Optional data of the load option. + @param OptionalDataSize Size of the optional data of the load option. + + @retval EFI_SUCCESS The load option was initialized successfully. + @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerInitializeLoadOption ( + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN OptionNumber, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, + IN UINT32 Attributes, + IN CHAR16 *Description, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN UINT8 *OptionalData, OPTIONAL + IN UINT32 OptionalDataSize + ) +{ + if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (((OptionalData != NULL) && (OptionalDataSize == 0)) || + ((OptionalData == NULL) && (OptionalDataSize != 0))) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); + Option->OptionNumber = OptionNumber; + Option->OptionType = OptionType; + Option->Attributes = Attributes; + Option->Description = AllocateCopyPool (StrSize (Description), Description); + Option->FilePath = DuplicateDevicePath (FilePath); + if (OptionalData != NULL) { + Option->OptionalData = AllocateCopyPool (OptionalDataSize, OptionalData); + Option->OptionalDataSize = OptionalDataSize; + } + + return EFI_SUCCESS; +} + + +/** + Return the index of the load option in the load option array. + + The function consider two load options are equal when the + OptionType, Attributes, Description, FilePath and OptionalData are equal. + + @param Key Pointer to the load option to be found. + @param Array Pointer to the array of load options to be found. + @param Count Number of entries in the Array. + + @retval -1 Key wasn't found in the Array. + @retval 0 ~ Count-1 The index of the Key in the Array. +**/ +INTN +BmFindLoadOption ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, + IN UINTN Count + ) +{ + UINTN Index; + + for (Index = 0; Index < Count; Index++) { + if ((Key->OptionType == Array[Index].OptionType) && + (Key->Attributes == Array[Index].Attributes) && + (StrCmp (Key->Description, Array[Index].Description) == 0) && + (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) && + (Key->OptionalDataSize == Array[Index].OptionalDataSize) && + (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) { + return (INTN) Index; + } + } + + return -1; +} + +/** + Update the BootOrder or DriverOrder to delete OptionNumber . + + @param OptionOrderVariable Order variable name which need to be updated. + @param OptionNumber Indicate the option number of load option + + @retval EFI_NOT_FOUND The load option cannot be found + @retval EFI_SUCCESS The load option was deleted + @retval others Status of RT->SetVariable() +**/ +EFI_STATUS +BmDeleteOptionVariable ( + IN CHAR16 *OptionOrderVariable, + IN UINT16 OptionNumber + ) +{ + UINT16 *OptionOrder; + UINTN OptionOrderSize; + EFI_STATUS Status; + UINTN Index; + + Status = EFI_NOT_FOUND; + GetEfiGlobalVariable2 (OptionOrderVariable, (VOID **) &OptionOrder, &OptionOrderSize); + for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { + if (OptionOrder[Index] == OptionNumber) { + OptionOrderSize -= sizeof (UINT16); + CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16)); + Status = gRT->SetVariable ( + OptionOrderVariable, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + OptionOrderSize, + OptionOrder + ); + break; + } + } + if (OptionOrder != NULL) { + FreePool (OptionOrder); + } + + return Status; +} + +/** + Update the BootOrder or DriverOrder according to the OptionType to delete OptionNumber . + + @param OptionNumber Indicate the option number of load option + @param OptionType Indicate the type of load option + + @retval EFI_INVALID_PARAMETER OptionType or OptionNumber is invalid. + @retval EFI_NOT_FOUND The load option cannot be found + @retval EFI_SUCCESS The load option was deleted +**/ +EFI_STATUS +EFIAPI +EfiBootManagerDeleteLoadOptionVariable ( + IN UINTN OptionNumber, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType + ) +{ + if ((OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) { + return EFI_INVALID_PARAMETER; + } + + return BmDeleteOptionVariable ( + OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", + (UINT16) OptionNumber + ); +} + +/** + Convert a single character to number. + It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F' + + @param Char The input char which need to convert to int. +**/ +UINTN +BmCharToUint ( + IN CHAR16 Char + ) +{ + if ((Char >= L'0') && (Char <= L'9')) { + return (UINTN) (Char - L'0'); + } + + if ((Char >= L'A') && (Char <= L'F')) { + return (UINTN) (Char - L'A' + 0xA); + } + + ASSERT (FALSE); + return 0; +} + +/** + Returns the size of a device path in bytes. + + This function returns the size, in bytes, of the device path data structure + specified by DevicePath including the end of device path node. If DevicePath + is NULL, then 0 is returned. If the length of the device path is bigger than + MaxSize, also return 0 to indicate this is an invalidate device path. + + @param DevicePath A pointer to a device path data structure. + @param MaxSize Max valid device path size. If big than this size, + return error. + + @retval 0 An invalid device path. + @retval Others The size of a device path in bytes. + +**/ +UINTN +BmGetDevicePathSizeEx ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN UINTN MaxSize + ) +{ + UINTN Size; + UINTN NodeSize; + + if (DevicePath == NULL) { + return 0; + } + + // + // Search for the end of the device path structure + // + Size = 0; + while (!IsDevicePathEnd (DevicePath)) { + NodeSize = DevicePathNodeLength (DevicePath); + if (NodeSize == 0) { + return 0; + } + Size += NodeSize; + if (Size > MaxSize) { + return 0; + } + DevicePath = NextDevicePathNode (DevicePath); + } + Size += DevicePathNodeLength (DevicePath); + if (Size > MaxSize) { + return 0; + } + + return Size; +} + +/** + Returns the length of a Null-terminated Unicode string. If the length is + bigger than MaxStringLen, return length 0 to indicate that this is an + invalidate string. + + This function returns the number of Unicode characters in the Null-terminated + Unicode string specified by String. + + If String is NULL, then ASSERT(). + If String is not aligned on a 16-bit boundary, then ASSERT(). + + @param String A pointer to a Null-terminated Unicode string. + @param MaxStringLen Max string len in this string. + + @retval 0 An invalid string. + @retval Others The length of String. + +**/ +UINTN +BmStrSizeEx ( + IN CONST CHAR16 *String, + IN UINTN MaxStringLen + ) +{ + UINTN Length; + + ASSERT (String != NULL && MaxStringLen != 0); + ASSERT (((UINTN) String & BIT0) == 0); + + for (Length = 0; *String != L'\0' && MaxStringLen != Length; String++, Length+=2); + + if (*String != L'\0' && MaxStringLen == Length) { + return 0; + } + + return Length + 2; +} + +/** + Validate the EFI Boot#### variable (VendorGuid/Name) + + @param Variable Boot#### variable data. + @param VariableSize Returns the size of the EFI variable that was read + + @retval TRUE The variable data is correct. + @retval FALSE The variable data is corrupted. + +**/ +BOOLEAN +BmValidateOption ( + UINT8 *Variable, + UINTN VariableSize + ) +{ + UINT16 FilePathSize; + UINT8 *TempPtr; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN TempSize; + + if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) { + return FALSE; + } + + // + // Skip the option attribute + // + TempPtr = Variable; + TempPtr += sizeof (UINT32); + + // + // Get the option's device path size + // + FilePathSize = *(UINT16 *) TempPtr; + TempPtr += sizeof (UINT16); + + // + // Get the option's description string size + // + TempSize = BmStrSizeEx ((CHAR16 *) TempPtr, VariableSize - sizeof (UINT16) - sizeof (UINT32)); + TempPtr += TempSize; + + // + // Get the option's device path + // + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; + TempPtr += FilePathSize; + + // + // Validation boot option variable. + // + if ((FilePathSize == 0) || (TempSize == 0)) { + return FALSE; + } + + if (TempSize + FilePathSize + sizeof (UINT16) + sizeof (UINT32) > VariableSize) { + return FALSE; + } + + return (BOOLEAN) (BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0); +} + +/** + Build the Boot#### or Driver#### option from the VariableName. + + @param VariableName EFI Variable name indicate if it is Boot#### or + Driver#### + @param Option Return the Boot#### or Driver#### option. + + @retval EFI_SUCCESS Get the option just been created + @retval EFI_NOT_FOUND Failed to get the new option + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerVariableToLoadOption ( + IN CHAR16 *VariableName, + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option + ) +{ + EFI_STATUS Status; + UINT32 Attribute; + UINT16 FilePathSize; + UINT8 *Variable; + UINT8 *TempPtr; + UINTN VariableSize; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + UINT8 *OptionalData; + UINT32 OptionalDataSize; + CHAR16 *Description; + UINT8 NumOff; + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; + UINT16 OptionNumber; + + if ((VariableName == NULL) || (Option == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Read the variable + // + GetEfiGlobalVariable2 (VariableName, (VOID **) &Variable, &VariableSize); + if (Variable == NULL) { + return EFI_NOT_FOUND; + } + + // + // Validate Boot#### variable data. + // + if (!BmValidateOption(Variable, VariableSize)) { + FreePool (Variable); + return EFI_INVALID_PARAMETER; + } + + // + // Notes: careful defined the variable of Boot#### or + // Driver####, consider use some macro to abstract the code + // + // + // Get the option attribute + // + TempPtr = Variable; + Attribute = *(UINT32 *) Variable; + TempPtr += sizeof (UINT32); + + // + // Get the option's device path size + // + FilePathSize = *(UINT16 *) TempPtr; + TempPtr += sizeof (UINT16); + + // + // Get the option's description string + // + Description = (CHAR16 *) TempPtr; + + // + // Get the option's description string size + // + TempPtr += StrSize ((CHAR16 *) TempPtr); + + // + // Get the option's device path + // + FilePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; + TempPtr += FilePathSize; + + OptionalDataSize = (UINT32) (VariableSize - (UINTN) (TempPtr - Variable)); + if (OptionalDataSize == 0) { + OptionalData = NULL; + } else { + OptionalData = TempPtr; + } + + if (*VariableName == L'B') { + OptionType = LoadOptionTypeBoot; + NumOff = (UINT8) (sizeof (L"Boot") / sizeof (CHAR16) - 1); + } else { + OptionType = LoadOptionTypeDriver; + NumOff = (UINT8) (sizeof (L"Driver") / sizeof (CHAR16) - 1); + } + + // + // Get the value from VariableName Unicode string + // since the ISO standard assumes ASCII equivalent abbreviations, we can be safe in converting this + // Unicode stream to ASCII without any loss in meaning. + // + OptionNumber = (UINT16) (BmCharToUint (VariableName[NumOff+0]) * 0x1000) + + (UINT16) (BmCharToUint (VariableName[NumOff+1]) * 0x100) + + (UINT16) (BmCharToUint (VariableName[NumOff+2]) * 0x10) + + (UINT16) (BmCharToUint (VariableName[NumOff+3]) * 0x1); + + Status = EfiBootManagerInitializeLoadOption ( + Option, + OptionNumber, + OptionType, + Attribute, + Description, + FilePath, + OptionalData, + OptionalDataSize + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Variable); + return Status; +} + +/** + Returns an array of load options based on the EFI variable + L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it. + #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. + + @param LoadOptionCount Returns number of entries in the array. + @param LoadOptionType The type of the load option. + + @retval NULL No load options exist. + @retval !NULL Array of load option entries. + +**/ +EFI_BOOT_MANAGER_LOAD_OPTION * +EFIAPI +EfiBootManagerGetLoadOptions ( + OUT UINTN *OptionCount, + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType + ) +{ + EFI_STATUS Status; + UINT16 *OptionOrder; + UINTN OptionOrderSize; + UINTN Index; + UINTN OptionIndex; + EFI_BOOT_MANAGER_LOAD_OPTION *Option; + CHAR16 OptionName[sizeof ("Driver####")]; + UINT16 OptionNumber; + + *OptionCount = 0; + + // + // Read the BootOrder, or DriverOrder variable. + // + GetEfiGlobalVariable2 ( + (LoadOptionType == LoadOptionTypeBoot) ? L"BootOrder" : L"DriverOrder", + (VOID **) &OptionOrder, + &OptionOrderSize + ); + if (OptionOrder == NULL) { + return NULL; + } + + *OptionCount = OptionOrderSize / sizeof (UINT16); + + Option = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); + ASSERT (Option != NULL); + + OptionIndex = 0; + for (Index = 0; Index < *OptionCount; Index++) { + OptionNumber = OptionOrder[Index]; + if (LoadOptionType == LoadOptionTypeBoot) { + UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionNumber); + } else { + UnicodeSPrint (OptionName, sizeof (OptionName), L"Driver%04x", OptionNumber); + } + + Status = EfiBootManagerVariableToLoadOption (OptionName, &Option[OptionIndex]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName)); + EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionTypeBoot); + } else { + ASSERT (Option[OptionIndex].OptionNumber == OptionNumber); + OptionIndex++; + } + } + + if (OptionOrder != NULL) { + FreePool (OptionOrder); + } + + if (OptionIndex < *OptionCount) { + Option = ReallocatePool ( + *OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), + OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), + Option + ); + ASSERT (Option != NULL); + *OptionCount = OptionIndex; + } + + return Option; +} + +/** + Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library. + + @param LoadOption Pointer to boot option to Free. + + @return EFI_SUCCESS BootOption was freed + @return EFI_NOT_FOUND BootOption == NULL + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeLoadOption ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ) +{ + if (LoadOption == NULL) { + return EFI_NOT_FOUND; + } + + if (LoadOption->Description != NULL) { + FreePool (LoadOption->Description); + } + if (LoadOption->FilePath != NULL) { + FreePool (LoadOption->FilePath); + } + if (LoadOption->OptionalData != NULL) { + FreePool (LoadOption->OptionalData); + } + + return EFI_SUCCESS; +} + +/** + Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by + EfiBootManagerGetLoadOptions(). + + @param Option Pointer to boot option array to free. + @param OptionCount Number of array entries in BootOption + + @return EFI_SUCCESS BootOption was freed + @return EFI_NOT_FOUND BootOption == NULL + +**/ +EFI_STATUS +EFIAPI +EfiBootManagerFreeLoadOptions ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN OptionCount + ) +{ + UINTN Index; + + if (Option == NULL) { + return EFI_NOT_FOUND; + } + + for (Index = 0;Index < OptionCount; Index++) { + EfiBootManagerFreeLoadOption (&Option[Index]); + } + + FreePool (Option); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c new file mode 100644 index 0000000000..1cfb4bb039 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c @@ -0,0 +1,507 @@ +/** @file + Misc library functions. + +Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +/** + Delete the instance in Multi which matches partly with Single instance + + @param Multi A pointer to a multi-instance device path data + structure. + @param Single A pointer to a single-instance device path data + structure. + + @return This function will remove the device path instances in Multi which partly + match with the Single, and return the result device path. If there is no + remaining device path as a result, this function will return NULL. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmDelPartMatchInstance ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single + ) +{ + EFI_DEVICE_PATH_PROTOCOL *Instance; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + UINTN InstanceSize; + UINTN SingleDpSize; + + NewDevicePath = NULL; + TempNewDevicePath = NULL; + + if (Multi == NULL || Single == NULL) { + return Multi; + } + + Instance = GetNextDevicePathInstance (&Multi, &InstanceSize); + SingleDpSize = GetDevicePathSize (Single) - END_DEVICE_PATH_LENGTH; + InstanceSize -= END_DEVICE_PATH_LENGTH; + + while (Instance != NULL) { + + if (CompareMem (Instance, Single, MIN (SingleDpSize, InstanceSize)) != 0) { + // + // Append the device path instance which does not match with Single + // + TempNewDevicePath = NewDevicePath; + NewDevicePath = AppendDevicePathInstance (NewDevicePath, Instance); + if (TempNewDevicePath != NULL) { + FreePool(TempNewDevicePath); + } + } + FreePool(Instance); + Instance = GetNextDevicePathInstance (&Multi, &InstanceSize); + InstanceSize -= END_DEVICE_PATH_LENGTH; + } + + return NewDevicePath; +} + +/** + Function compares a device path data structure to that of all the nodes of a + second device path instance. + + @param Multi A pointer to a multi-instance device path data + structure. + @param Single A pointer to a single-instance device path data + structure. + + @retval TRUE If the Single device path is contained within Multi device path. + @retval FALSE The Single device path is not match within Multi device path. + +**/ +BOOLEAN +BmMatchDevicePaths ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single + ) +{ + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePathInst; + UINTN Size; + + if (Multi == NULL || Single == NULL) { + return FALSE; + } + + DevicePath = Multi; + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + + // + // Search for the match of 'Single' in 'Multi' + // + while (DevicePathInst != NULL) { + // + // If the single device path is found in multiple device paths, + // return success + // + if (CompareMem (Single, DevicePathInst, Size) == 0) { + FreePool (DevicePathInst); + return TRUE; + } + + FreePool (DevicePathInst); + DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size); + } + + return FALSE; +} + +/** + Get the headers (dos, image, optional header) from an image + + @param Device SimpleFileSystem device handle + @param FileName File name for the image + @param DosHeader Pointer to dos header + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @retval EFI_SUCCESS Successfully get the machine type. + @retval EFI_NOT_FOUND The file is not found. + @retval EFI_LOAD_ERROR File is not a valid image file. + +**/ +EFI_STATUS +BmGetImageHeader ( + IN EFI_HANDLE Device, + IN CHAR16 *FileName, + OUT EFI_IMAGE_DOS_HEADER *DosHeader, + OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; + EFI_FILE_HANDLE Root; + EFI_FILE_HANDLE ThisFile; + UINTN BufferSize; + UINT64 FileSize; + EFI_FILE_INFO *Info; + + Root = NULL; + ThisFile = NULL; + // + // Handle the file system interface to the device + // + Status = gBS->HandleProtocol ( + Device, + &gEfiSimpleFileSystemProtocolGuid, + (VOID *) &Volume + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = Volume->OpenVolume ( + Volume, + &Root + ); + if (EFI_ERROR (Status)) { + Root = NULL; + goto Done; + } + ASSERT (Root != NULL); + Status = Root->Open (Root, &ThisFile, FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + goto Done; + } + ASSERT (ThisFile != NULL); + + // + // Get file size + // + BufferSize = SIZE_OF_EFI_FILE_INFO + 200; + do { + Info = NULL; + Status = gBS->AllocatePool (EfiBootServicesData, BufferSize, (VOID **) &Info); + if (EFI_ERROR (Status)) { + goto Done; + } + Status = ThisFile->GetInfo ( + ThisFile, + &gEfiFileInfoGuid, + &BufferSize, + Info + ); + if (!EFI_ERROR (Status)) { + break; + } + if (Status != EFI_BUFFER_TOO_SMALL) { + FreePool (Info); + goto Done; + } + FreePool (Info); + } while (TRUE); + + FileSize = Info->FileSize; + FreePool (Info); + + // + // Read dos header + // + BufferSize = sizeof (EFI_IMAGE_DOS_HEADER); + Status = ThisFile->Read (ThisFile, &BufferSize, DosHeader); + if (EFI_ERROR (Status) || + BufferSize < sizeof (EFI_IMAGE_DOS_HEADER) || + FileSize <= DosHeader->e_lfanew || + DosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) { + Status = EFI_LOAD_ERROR; + goto Done; + } + + // + // Move to PE signature + // + Status = ThisFile->SetPosition (ThisFile, DosHeader->e_lfanew); + if (EFI_ERROR (Status)) { + Status = EFI_LOAD_ERROR; + goto Done; + } + + // + // Read and check PE signature + // + BufferSize = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION); + Status = ThisFile->Read (ThisFile, &BufferSize, Hdr.Pe32); + if (EFI_ERROR (Status) || + BufferSize < sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) || + Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + Status = EFI_LOAD_ERROR; + goto Done; + } + + // + // Check PE32 or PE32+ magic + // + if (Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC && + Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + Status = EFI_LOAD_ERROR; + goto Done; + } + + Done: + if (ThisFile != NULL) { + ThisFile->Close (ThisFile); + } + if (Root != NULL) { + Root->Close (Root); + } + return Status; +} + +/** + This routine adjust the memory information for different memory type and + save them into the variables for next boot. +**/ +VOID +BmSetMemoryTypeInformationVariable ( + VOID + ) +{ + EFI_STATUS Status; + EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation; + EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation; + UINTN VariableSize; + UINTN Index; + UINTN Index1; + UINT32 Previous; + UINT32 Current; + UINT32 Next; + EFI_HOB_GUID_TYPE *GuidHob; + BOOLEAN MemoryTypeInformationModified; + BOOLEAN MemoryTypeInformationVariableExists; + EFI_BOOT_MODE BootMode; + + MemoryTypeInformationModified = FALSE; + MemoryTypeInformationVariableExists = FALSE; + + + BootMode = GetBootModeHob (); + // + // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable. + // + if (BootMode == BOOT_IN_RECOVERY_MODE) { + return; + } + + // + // Only check the the Memory Type Information variable in the boot mode + // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type + // Information is not valid in this boot mode. + // + if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) { + VariableSize = 0; + Status = gRT->GetVariable ( + EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME, + &gEfiMemoryTypeInformationGuid, + NULL, + &VariableSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + MemoryTypeInformationVariableExists = TRUE; + } + } + + // + // Retrieve the current memory usage statistics. If they are not found, then + // no adjustments can be made to the Memory Type Information variable. + // + Status = EfiGetSystemConfigurationTable ( + &gEfiMemoryTypeInformationGuid, + (VOID **) &CurrentMemoryTypeInformation + ); + if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) { + return; + } + + // + // Get the Memory Type Information settings from Hob if they exist, + // PEI is responsible for getting them from variable and build a Hob to save them. + // If the previous Memory Type Information is not available, then set defaults + // + GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid); + if (GuidHob == NULL) { + // + // If Platform has not built Memory Type Info into the Hob, just return. + // + return; + } + PreviousMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob); + VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob); + + // + // Use a heuristic to adjust the Memory Type Information for the next boot + // + DEBUG ((EFI_D_INFO, "Memory Previous Current Next \n")); + DEBUG ((EFI_D_INFO, " Type Pages Pages Pages \n")); + DEBUG ((EFI_D_INFO, "====== ======== ======== ========\n")); + + for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { + + for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) { + if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) { + break; + } + } + if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) { + continue; + } + + // + // Previous is the number of pages pre-allocated + // Current is the number of pages actually needed + // + Previous = PreviousMemoryTypeInformation[Index].NumberOfPages; + Current = CurrentMemoryTypeInformation[Index1].NumberOfPages; + Next = Previous; + + // + // Inconsistent Memory Reserved across bootings may lead to S4 fail + // Write next varible to 125% * current when the pre-allocated memory is: + // 1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING + // 2. Less than the needed memory + // + if ((Current + (Current >> 1)) < Previous) { + if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { + Next = Current + (Current >> 2); + } + } else if (Current > Previous) { + Next = Current + (Current >> 2); + } + if (Next > 0 && Next < 4) { + Next = 4; + } + + if (Next != Previous) { + PreviousMemoryTypeInformation[Index].NumberOfPages = Next; + MemoryTypeInformationModified = TRUE; + } + + DEBUG ((EFI_D_INFO, " %02x %08x %08x %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next)); + } + + // + // If any changes were made to the Memory Type Information settings, then set the new variable value; + // Or create the variable in first boot. + // + if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) { + Status = BmSetVariableAndReportStatusCodeOnError ( + EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME, + &gEfiMemoryTypeInformationGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableSize, + PreviousMemoryTypeInformation + ); + + if (!EFI_ERROR (Status)) { + // + // If the Memory Type Information settings have been modified, then reset the platform + // so the new Memory Type Information setting will be used to guarantee that an S4 + // entry/resume cycle will not fail. + // + if (MemoryTypeInformationModified) { + DEBUG ((EFI_D_INFO, "Memory Type Information settings change. Warm Reset!!!\n")); + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } else { + DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n")); + } + } +} + +/** + Set the variable and report the error through status code upon failure. + + @param VariableName A Null-terminated string that is the name of the vendor's variable. + Each VariableName is unique for each VendorGuid. VariableName must + contain 1 or more characters. If VariableName is an empty string, + then EFI_INVALID_PARAMETER is returned. + @param VendorGuid A unique identifier for the vendor. + @param Attributes Attributes bitmask to set for the variable. + @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero + causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is + set, then a SetVariable() call with a DataSize of zero will not cause any change to + the variable value (the timestamp associated with the variable may be updated however + even if no new data value is provided,see the description of the + EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not + be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). + @param Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS + or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo + does NOT pass the validation check carried out by the firmware. + + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. +**/ +EFI_STATUS +BmSetVariableAndReportStatusCodeOnError ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + EDKII_SET_VARIABLE_STATUS *SetVariableStatus; + UINTN NameSize; + + Status = gRT->SetVariable ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + if (EFI_ERROR (Status)) { + NameSize = StrSize (VariableName); + SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize); + if (SetVariableStatus != NULL) { + CopyGuid (&SetVariableStatus->Guid, VendorGuid); + SetVariableStatus->NameSize = NameSize; + SetVariableStatus->DataSize = DataSize; + SetVariableStatus->SetStatus = Status; + SetVariableStatus->Attributes = Attributes; + CopyMem (SetVariableStatus + 1, VariableName, NameSize); + CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize); + + REPORT_STATUS_CODE_EX ( + EFI_ERROR_CODE, + PcdGet32 (PcdErrorCodeSetVariable), + 0, + NULL, + &gEdkiiStatusCodeDataTypeVariableGuid, + SetVariableStatus, + sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize + ); + + FreePool (SetVariableStatus); + } + } + + return Status; +} + diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c b/MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c new file mode 100644 index 0000000000..1fdb7f1fcb --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c @@ -0,0 +1,358 @@ +/** @file + This file include the file which can help to get the system + performance, all the function will only include if the performance + switch is set. + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalBm.h" + +PERF_HEADER mBmPerfHeader; +PERF_DATA mBmPerfData; +EFI_PHYSICAL_ADDRESS mBmAcpiLowMemoryBase = 0x0FFFFFFFFULL; + +/** + Get the short verion of PDB file name to be + used in performance data logging. + + @param PdbFileName The long PDB file name. + @param GaugeString The output string to be logged by performance logger. + +**/ +VOID +BmGetShortPdbFileName ( + IN CONST CHAR8 *PdbFileName, + OUT CHAR8 *GaugeString + ) +{ + UINTN Index; + UINTN Index1; + UINTN StartIndex; + UINTN EndIndex; + + if (PdbFileName == NULL) { + AsciiStrCpy (GaugeString, " "); + } else { + StartIndex = 0; + for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++) + ; + + for (Index = 0; PdbFileName[Index] != 0; Index++) { + if (PdbFileName[Index] == '\\') { + StartIndex = Index + 1; + } + + if (PdbFileName[Index] == '.') { + EndIndex = Index; + } + } + + Index1 = 0; + for (Index = StartIndex; Index < EndIndex; Index++) { + GaugeString[Index1] = PdbFileName[Index]; + Index1++; + if (Index1 == PERF_TOKEN_LENGTH - 1) { + break; + } + } + + GaugeString[Index1] = 0; + } + + return ; +} + +/** + Get the name from the Driver handle, which can be a handle with + EFI_LOADED_IMAGE_PROTOCOL or EFI_DRIVER_BINDING_PROTOCOL installed. + This name can be used in performance data logging. + + @param Handle Driver handle. + @param GaugeString The output string to be logged by performance logger. + +**/ +VOID +BmGetNameFromHandle ( + IN EFI_HANDLE Handle, + OUT CHAR8 *GaugeString + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *Image; + CHAR8 *PdbFileName; + EFI_DRIVER_BINDING_PROTOCOL *DriverBinding; + + AsciiStrCpy (GaugeString, " "); + + // + // Get handle name from image protocol + // + Status = gBS->HandleProtocol ( + Handle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &Image + ); + + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Handle, + &gEfiDriverBindingProtocolGuid, + (VOID **) &DriverBinding, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return ; + } + // + // Get handle name from image protocol + // + Status = gBS->HandleProtocol ( + DriverBinding->ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **) &Image + ); + } + + PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase); + + if (PdbFileName != NULL) { + BmGetShortPdbFileName (PdbFileName, GaugeString); + } + + return ; +} + +/** + + Writes performance data of booting into the allocated memory. + OS can process these records. + + @param Event The triggered event. + @param Context Context for this event. + +**/ +VOID +EFIAPI +BmWriteBootToOsPerformanceData ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + UINT32 LimitCount; + EFI_HANDLE *Handles; + UINTN NoHandles; + CHAR8 GaugeString[PERF_TOKEN_LENGTH]; + UINT8 *Ptr; + UINT32 Index; + UINT64 Ticker; + UINT64 Freq; + UINT32 Duration; + UINTN LogEntryKey; + CONST VOID *Handle; + CONST CHAR8 *Token; + CONST CHAR8 *Module; + UINT64 StartTicker; + UINT64 EndTicker; + UINT64 StartValue; + UINT64 EndValue; + BOOLEAN CountUp; + UINTN EntryIndex; + UINTN NumPerfEntries; + // + // List of flags indicating PerfEntry contains DXE handle + // + BOOLEAN *PerfEntriesAsDxeHandle; + UINTN VarSize; + + // + // Record the performance data for End of BDS + // + PERF_END(NULL, "BDS", NULL, 0); + + // + // Retrieve time stamp count as early as possible + // + Ticker = GetPerformanceCounter (); + + Freq = GetPerformanceCounterProperties (&StartValue, &EndValue); + + Freq = DivU64x32 (Freq, 1000); + + mBmPerfHeader.CpuFreq = Freq; + + // + // Record BDS raw performance data + // + if (EndValue >= StartValue) { + mBmPerfHeader.BDSRaw = Ticker - StartValue; + CountUp = TRUE; + } else { + mBmPerfHeader.BDSRaw = StartValue - Ticker; + CountUp = FALSE; + } + + if (mBmAcpiLowMemoryBase == 0x0FFFFFFFF) { + VarSize = sizeof (EFI_PHYSICAL_ADDRESS); + Status = gRT->GetVariable ( + L"PerfDataMemAddr", + &gPerformanceProtocolGuid, + NULL, + &VarSize, + &mBmAcpiLowMemoryBase + ); + if (EFI_ERROR (Status)) { + // + // Fail to get the variable, return. + // + return; + } + } + + // + // Put Detailed performance data into memory + // + Handles = NULL; + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &NoHandles, + &Handles + ); + if (EFI_ERROR (Status)) { + return ; + } + + Ptr = (UINT8 *) ((UINT32) mBmAcpiLowMemoryBase + sizeof (PERF_HEADER)); + LimitCount = (UINT32) (PERF_DATA_MAX_LENGTH - sizeof (PERF_HEADER)) / sizeof (PERF_DATA); + + NumPerfEntries = 0; + LogEntryKey = 0; + while ((LogEntryKey = GetPerformanceMeasurement ( + LogEntryKey, + &Handle, + &Token, + &Module, + &StartTicker, + &EndTicker)) != 0) { + NumPerfEntries++; + } + PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN)); + ASSERT (PerfEntriesAsDxeHandle != NULL); + + // + // Get DXE drivers performance + // + for (Index = 0; Index < NoHandles; Index++) { + Ticker = 0; + LogEntryKey = 0; + EntryIndex = 0; + while ((LogEntryKey = GetPerformanceMeasurement ( + LogEntryKey, + &Handle, + &Token, + &Module, + &StartTicker, + &EndTicker)) != 0) { + if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) { + PerfEntriesAsDxeHandle[EntryIndex] = TRUE; + } + EntryIndex++; + if ((Handle == Handles[Index]) && (EndTicker != 0)) { + if (StartTicker == 1) { + StartTicker = StartValue; + } + if (EndTicker == 1) { + EndTicker = StartValue; + } + Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); + } + } + + Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); + + if (Duration > 0) { + + BmGetNameFromHandle (Handles[Index], GaugeString); + + AsciiStrCpy (mBmPerfData.Token, GaugeString); + mBmPerfData.Duration = Duration; + + CopyMem (Ptr, &mBmPerfData, sizeof (PERF_DATA)); + Ptr += sizeof (PERF_DATA); + + mBmPerfHeader.Count++; + if (mBmPerfHeader.Count == LimitCount) { + goto Done; + } + } + } + + // + // Get inserted performance data + // + LogEntryKey = 0; + EntryIndex = 0; + while ((LogEntryKey = GetPerformanceMeasurement ( + LogEntryKey, + &Handle, + &Token, + &Module, + &StartTicker, + &EndTicker)) != 0) { + if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) { + + ZeroMem (&mBmPerfData, sizeof (PERF_DATA)); + + AsciiStrnCpy (mBmPerfData.Token, Token, PERF_TOKEN_LENGTH); + if (StartTicker == 1) { + StartTicker = StartValue; + } + if (EndTicker == 1) { + EndTicker = StartValue; + } + Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker); + + mBmPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq); + + CopyMem (Ptr, &mBmPerfData, sizeof (PERF_DATA)); + Ptr += sizeof (PERF_DATA); + + mBmPerfHeader.Count++; + if (mBmPerfHeader.Count == LimitCount) { + goto Done; + } + } + EntryIndex++; + } + +Done: + + FreePool (Handles); + FreePool (PerfEntriesAsDxeHandle); + + mBmPerfHeader.Signiture = PERFORMANCE_SIGNATURE; + + // + // Put performance data to Reserved memory + // + CopyMem ( + (UINTN *) (UINTN) mBmAcpiLowMemoryBase, + &mBmPerfHeader, + sizeof (PERF_HEADER) + ); + + return ; +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h new file mode 100644 index 0000000000..c8dc226058 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h @@ -0,0 +1,364 @@ +/** @file + BDS library definition, include the file and data structure + +Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _INTERNAL_BM_H_ +#define _INTERNAL_BM_H_ + +#include <PiDxe.h> + +#include <IndustryStandard/Pci.h> +#include <IndustryStandard/PeImage.h> +#include <IndustryStandard/Atapi.h> +#include <IndustryStandard/Scsi.h> + +#include <Protocol/PciRootBridgeIo.h> +#include <Protocol/BlockIo.h> +#include <Protocol/LoadedImage.h> +#include <Protocol/SimpleFileSystem.h> +#include <Protocol/LoadFile.h> +#include <Protocol/DevicePath.h> +#include <Protocol/SimpleTextIn.h> +#include <Protocol/SimpleTextInEx.h> +#include <Protocol/SimpleTextOut.h> +#include <Protocol/SimpleNetwork.h> +#include <Protocol/FirmwareVolume2.h> +#include <Protocol/PciIo.h> +#include <Protocol/GraphicsOutput.h> +#include <Protocol/UsbIo.h> +#include <Protocol/DiskInfo.h> +#include <Protocol/IdeControllerInit.h> +#include <Protocol/BootLogo.h> +#include <Protocol/DriverHealth.h> +#include <Protocol/FormBrowser2.h> + +#include <Guid/ZeroGuid.h> +#include <Guid/MemoryTypeInformation.h> +#include <Guid/FileInfo.h> +#include <Guid/GlobalVariable.h> +#include <Guid/Performance.h> +#include <Guid/StatusCodeDataTypeVariable.h> + +#include <Library/PrintLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DxeServicesTableLib.h> +#include <Library/HobLib.h> +#include <Library/BaseLib.h> +#include <Library/DevicePathLib.h> +#include <Library/PerformanceLib.h> +#include <Library/PcdLib.h> +#include <Library/PeCoffGetEntryPointLib.h> +#include <Library/UefiBootManagerLib.h> +#include <Library/TimerLib.h> +#include <Library/DxeServicesLib.h> +#include <Library/ReportStatusCodeLib.h> +#include <Library/CapsuleLib.h> +#include <Library/PerformanceLib.h> +#include <Library/HiiLib.h> + +#if !defined (EFI_REMOVABLE_MEDIA_FILE_NAME) + #if defined (MDE_CPU_EBC) + // + // Uefi specification only defines the default boot file name for IA32, X64 + // and IPF processor, so need define boot file name for EBC architecture here. + // + #define EFI_REMOVABLE_MEDIA_FILE_NAME L"\\EFI\\BOOT\\BOOTEBC.EFI" + #else + #error "Can not determine the default boot file name for unknown processor type!" + #endif +#endif + +typedef enum { + BmAcpiFloppyBoot, + BmHardwareDeviceBoot, + BmMessageAtapiBoot, + BmMessageSataBoot, + BmMessageUsbBoot, + BmMessageScsiBoot, + BmMessageNetworkBoot, + BmMiscBoot +} BM_BOOT_TYPE; + +typedef +CHAR16 * +(* BM_GET_BOOT_DESCRIPTION) ( + IN EFI_HANDLE Handle + ); + +#define BM_HOTKEY_SIGNATURE SIGNATURE_32 ('b', 'm', 'h', 'k') +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + BOOLEAN IsContinue; + UINT16 BootOption; + UINT8 CodeCount; + UINT8 WaitingKey; + EFI_KEY_DATA KeyData[3]; +} BM_HOTKEY; + +#define BM_HOTKEY_FROM_LINK(a) CR (a, BM_HOTKEY, Link, BM_HOTKEY_SIGNATURE) + +/** + Get the image file buffer data and buffer size by its device path. + + @param FilePath On input, a pointer to an allocated buffer containing the device + path of the file. + On output the pointer could be NULL when the function fails to + load the boot option, or could point to an allocated buffer containing + the device path of the file. + It could be updated by either short-form device path expanding, + or default boot file path appending. + Caller is responsible to free it when it's non-NULL. + @param FileSize A pointer to the size of the file buffer. + + @retval NULL File is NULL, or FileSize is NULL. Or, the file can't be found. + @retval other The file buffer. The caller is responsible to free the memory. +**/ +VOID * +BmLoadEfiBootOption ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, + OUT UINTN *FileSize + ); + +/** + Get the Option Number that wasn't used. + + @param OrderVariableName Could be L"BootOrder" or L"DriverOrder". + @param FreeOptionNumber To receive the minimal free option number. + + @retval EFI_SUCCESS The option number is found + @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used. + @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL + +**/ +EFI_STATUS +BmGetFreeOptionNumber ( + IN CHAR16 *OrderVariableName, + OUT UINT16 *FreeOptionNumber + ); + +/** + + Writes performance data of booting into the allocated memory. + OS can process these records. + + @param Event The triggered event. + @param Context Context for this event. + +**/ +VOID +EFIAPI +BmWriteBootToOsPerformanceData ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + Get the headers (dos, image, optional header) from an image + + @param Device SimpleFileSystem device handle + @param FileName File name for the image + @param DosHeader Pointer to dos header + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @retval EFI_SUCCESS Successfully get the machine type. + @retval EFI_NOT_FOUND The file is not found. + @retval EFI_LOAD_ERROR File is not a valid image file. + +**/ +EFI_STATUS +BmGetImageHeader ( + IN EFI_HANDLE Device, + IN CHAR16 *FileName, + OUT EFI_IMAGE_DOS_HEADER *DosHeader, + OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ); + +/** + This routine adjust the memory information for different memory type and + save them into the variables for next boot. +**/ +VOID +BmSetMemoryTypeInformationVariable ( + VOID + ); + +/** + Check whether there is a instance in BlockIoDevicePath, which contain multi device path + instances, has the same partition node with HardDriveDevicePath device path + + @param BlockIoDevicePath Multi device path instances which need to check + @param HardDriveDevicePath A device path which starts with a hard drive media + device path. + + @retval TRUE There is a matched device path instance. + @retval FALSE There is no matched device path instance. + +**/ +BOOLEAN +BmMatchPartitionDevicePathNode ( + IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, + IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath + ); + +/** + Connect the specific Usb device which match the short form device path. + + @param DevicePath A short-form device path that starts with the first + element being a USB WWID or a USB Class device + path + + @return EFI_INVALID_PARAMETER DevicePath is NULL pointer. + DevicePath is not a USB device path. + + @return EFI_SUCCESS Success to connect USB device + @return EFI_NOT_FOUND Fail to find handle for USB controller to connect. + +**/ +EFI_STATUS +BmConnectUsbShortFormDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + +/** + Stop the hotkey processing. + + @param Event Event pointer related to hotkey service. + @param Context Context pass to this function. +**/ +VOID +EFIAPI +BmStopHotkeyService ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Set the variable and report the error through status code upon failure. + + @param VariableName A Null-terminated string that is the name of the vendor's variable. + Each VariableName is unique for each VendorGuid. VariableName must + contain 1 or more characters. If VariableName is an empty string, + then EFI_INVALID_PARAMETER is returned. + @param VendorGuid A unique identifier for the vendor. + @param Attributes Attributes bitmask to set for the variable. + @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero + causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is + set, then a SetVariable() call with a DataSize of zero will not cause any change to + the variable value (the timestamp associated with the variable may be updated however + even if no new data value is provided,see the description of the + EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not + be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). + @param Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS + or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo + does NOT pass the validation check carried out by the firmware. + + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. +**/ +EFI_STATUS +BmSetVariableAndReportStatusCodeOnError ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + Function compares a device path data structure to that of all the nodes of a + second device path instance. + + @param Multi A pointer to a multi-instance device path data + structure. + @param Single A pointer to a single-instance device path data + structure. + + @retval TRUE If the Single device path is contained within Multi device path. + @retval FALSE The Single device path is not match within Multi device path. + +**/ +BOOLEAN +BmMatchDevicePaths ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single + ); + +/** + Delete the instance in Multi which matches partly with Single instance + + @param Multi A pointer to a multi-instance device path data + structure. + @param Single A pointer to a single-instance device path data + structure. + + @return This function will remove the device path instances in Multi which partly + match with the Single, and return the result device path. If there is no + remaining device path as a result, this function will return NULL. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +BmDelPartMatchInstance ( + IN EFI_DEVICE_PATH_PROTOCOL *Multi, + IN EFI_DEVICE_PATH_PROTOCOL *Single + ); + + +/** + Return the index of the load option in the load option array. + + The function consider two load options are equal when the + OptionType, Attributes, Description, FilePath and OptionalData are equal. + + @param Key Pointer to the load option to be found. + @param Array Pointer to the array of load options to be found. + @param Count Number of entries in the Array. + + @retval -1 Key wasn't found in the Array. + @retval 0 ~ Count-1 The index of the Key in the Array. +**/ +INTN +BmFindLoadOption ( + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, + IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, + IN UINTN Count + ); + +/** + Repair all the controllers according to the Driver Health status queried. +**/ +VOID +BmRepairAllControllers ( + VOID + ); + +#endif // _INTERNAL_BM_H_ diff --git a/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf new file mode 100644 index 0000000000..40372129c0 --- /dev/null +++ b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf @@ -0,0 +1,110 @@ +## @file +# Define and produce general Boot Manager related interfaces. +# +# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR> +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UefiBootManagerLib + FILE_GUID = 8D4752BC-595E-49a2-B4AF-F3F57B601DE9 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = UefiBootManagerLib|DXE_DRIVER UEFI_APPLICATION UEFI_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + BmPerformance.c + BmConnect.c + BmMisc.c + BmConsole.c + BmBoot.c + BmLoadOption.c + BmHotkey.c + BmDriverHealth.c + InternalBm.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + HobLib + PcdLib + BaseLib + UefiLib + TimerLib + DebugLib + PrintLib + BaseMemoryLib + DevicePathLib + PerformanceLib + PeCoffGetEntryPointLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + DxeServicesTableLib + MemoryAllocationLib + DxeServicesLib + ReportStatusCodeLib + PerformanceLib + HiiLib + BaseSortLib + +[Guids] + gEfiMemoryTypeInformationGuid ## CONSUMES ## GUID (The identifier of memory type information type in system table) + ## CONSUMES ## GUID HOB (The hob holding memory type information) + gEfiGlobalVariableGuid ## SOMETIMES_PRODUCES ## Variable:L"BootCurrent" (The boot option of current boot) + ## SOMETIMES_CONSUMES ## Variable:L"BootXX" (Boot option variable) + ## CONSUMES ## Variable:L"Timeout" (The time out value in second of showing progress bar) + ## SOMETIMES_CONSUMES ## Variable:L"BootOrder" (The boot option array) + ## SOMETIMES_CONSUMES ## Variable:L"DriverOrder" (The driver order list) + ## SOMETIMES_CONSUMES ## Variable:L"ConIn" (The device path of console in device) + ## SOMETIMES_CONSUMES ## Variable:L"ConOut" (The device path of console out device) + ## SOMETIMES_CONSUMES ## Variable:L"ErrOut" (The device path of error out device) + gEfiFileInfoGuid ## CONSUMES ## GUID + gPerformanceProtocolGuid ## SOMETIMES_PRODUCES ## Variable:L"PerfDataMemAddr" (The ACPI address of performance data) + gEdkiiStatusCodeDataTypeVariableGuid ## SOMETIMES_CONSUMES ## GUID + gEfiDiskInfoAhciInterfaceGuid ## SOMETIMES_CONSUMES ## GUID + gEfiDiskInfoIdeInterfaceGuid ## SOMETIMES_CONSUMES ## GUID + gEfiDiskInfoScsiInterfaceGuid ## SOMETIMES_CONSUMES ## GUID + gZeroGuid ## CONSUMES ## GUID + +[Protocols] + gEfiPciRootBridgeIoProtocolGuid ## CONSUMES + gEfiSimpleFileSystemProtocolGuid ## CONSUMES + gEfiLoadFileProtocolGuid ## CONSUMES + gEfiSimpleTextOutProtocolGuid ## CONSUMES + gEfiPciIoProtocolGuid ## CONSUMES + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiSimpleNetworkProtocolGuid ## CONSUMES + gEfiSimpleTextInProtocolGuid ## CONSUMES + gEfiBlockIoProtocolGuid ## CONSUMES + gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + gEfiDevicePathProtocolGuid ## CONSUMES + gEfiBootLogoProtocolGuid ## CONSUMES + gEfiSimpleTextInputExProtocolGuid ## CONSUMES + gEfiGraphicsOutputProtocolGuid ## SOMETIMES_CONSUMES + gEfiUsbIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiDiskInfoProtocolGuid ## SOMETIMES_CONSUMES + gEfiDriverHealthProtocolGuid ## SOMETIMES_CONSUMES + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm ## SOMETIMES_CONSUMES + diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 1edb49afbd..6e7247016c 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -105,6 +105,9 @@ ## @libraryclass Provides sorting functions
SortLib|Include/Library/SortLib.h
+ ## @libraryclass Provides core boot manager functions
+ UefiBootManagerLib|Include/Library/UefiBootManagerLib.h
+
[Guids]
## MdeModule package token space guid
# Include/Guid/MdeModulePkgTokenSpace.h
@@ -922,7 +925,16 @@ ## Serial Port Extended Transmit FIFO Size. The default is 64 bytes.
# @Prompt Serial Port Extended Transmit FIFO Size in Bytes
gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize|64|UINT32|0x00010068
-
+
+ ## This PCD points to the file name GUID of the BootManagerMenuApp
+ # Platform can customize the PCD to point to different application for Boot Manager Menu
+ gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0xdc, 0x5b, 0xc2, 0xee, 0xf2, 0x67, 0x95, 0x4d, 0xb1, 0xd5, 0xf8, 0x1b, 0x20, 0x39, 0xd1, 0x1d }|VOID*|0x0001006b
+
+ ## This PCD points to the formset GUID of the driver health management form
+ # The form will be popped up by BDS core when there are Configuration Required driver health intances.
+ # Platform can customize the PCD to point to different formset.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm|{ 0xf4, 0xd9, 0x96, 0x42, 0xfc, 0xf6, 0xde, 0x4d, 0x86, 0x85, 0x8c, 0xe2, 0xd7, 0x9d, 0x90, 0xf0 }|VOID*|0x0001006c
+
## The number of bytes between registers in serial device. The default is 1 byte.
# @Prompt Serial Port Register Stride in Bytes
gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride|1|UINT32|0x0001006B
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 0bf3cca871..f937e83eb5 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -46,6 +46,7 @@ CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+ BaseSortLib|MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf #
# UEFI & PI
#
@@ -267,6 +268,7 @@ MdeModulePkg/Library/PlatformHookLibSerialPortPpi/PlatformHookLibSerialPortPpi.inf
MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
+ MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf MdeModulePkg/Universal/CapsulePei/CapsulePei.inf
MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
|