From: Ruiyu Ni Date: Mon, 11 May 2015 06:33:45 +0000 (+0000) Subject: MdeModulePkg: Process Sys Prep load options in BdsDxe driver. X-Git-Tag: edk2-stable201903~9852 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=1634214dbb06057e331b85727947683d3b3bf2f4 MdeModulePkg: Process Sys Prep load options in BdsDxe driver. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni Reviewed-by: Eric Dong git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17403 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/MdeModulePkg/Include/Library/UefiBootManagerLib.h b/MdeModulePkg/Include/Library/UefiBootManagerLib.h index 9b08364aaf..2ec80894ad 100644 --- a/MdeModulePkg/Include/Library/UefiBootManagerLib.h +++ b/MdeModulePkg/Include/Library/UefiBootManagerLib.h @@ -27,8 +27,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. // Load Option Type // typedef enum { - LoadOptionTypeBoot, LoadOptionTypeDriver, + LoadOptionTypeSysPrep, + LoadOptionTypeBoot, LoadOptionTypeMax } EFI_BOOT_MANAGER_LOAD_OPTION_TYPE; @@ -51,6 +52,7 @@ typedef struct { 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 + EFI_GUID VendorGuid; // // Used at runtime @@ -172,11 +174,11 @@ EfiBootManagerLoadOptionToVariable ( ); /** - This function will update the Boot####/Driver#### and the BootOrder/DriverOrder - to add a new load option. + This function will update the Boot####/Driver####/SysPrep#### and the + BootOrder/DriverOrder/SysPrepOrder 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. + @param Position Position of the new load option to put in the BootOrder/DriverOrder/SysPrepOrder. @retval EFI_SUCCESS The load option has been successfully added. @retval Others Error status returned by RT->SetVariable. @@ -458,17 +460,20 @@ EfiBootManagerConnectAll ( /** 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, + 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 CANNOT be + @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_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. - + @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. + @retval EFI_SECURITY_VIOLATION The user has no permission to start UEFI device + drivers on the DevicePath. **/ EFI_STATUS EFIAPI @@ -508,8 +513,12 @@ typedef enum { /** This function will connect all the console devices base on the console device variable ConIn, ConOut and ErrOut. + + @retval EFI_DEVICE_ERROR All the consoles were not connected due to an error. + @retval EFI_SUCCESS Success connect any one instance of the console + device path base on the variable ConVarName. **/ -VOID +EFI_STATUS EFIAPI EfiBootManagerConnectAllDefaultConsoles ( VOID @@ -654,4 +663,19 @@ EfiBootManagerFreeDriverHealthInfo ( UINTN Count ); +/** + Process (load and execute) the load option. + + @param LoadOption Pointer to the load option. + + @retval EFI_INVALID_PARAMETER The load option type is invalid, + or the load option file path doesn't point to a valid file. + @retval EFI_UNSUPPORTED The load option type is of LoadOptionTypeBoot. + @retval EFI_SUCCESS The load option is inactive, or successfully loaded and executed. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerProcessLoadOption ( + EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ); #endif diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index ddda9ae605..8d1a8c9d23 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -71,7 +71,7 @@ EfiBootManagerRegisterLegacyBootSupport ( **/ BM_BOOT_TYPE -BmBootTypeFromDevicePath ( +BmDevicePathType ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { @@ -144,22 +144,6 @@ BmBootTypeFromDevicePath ( 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. @@ -176,7 +160,7 @@ BmFindBootOptionInVariable ( EFI_STATUS Status; EFI_BOOT_MANAGER_LOAD_OPTION BootOption; UINTN OptionNumber; - CHAR16 OptionName[sizeof ("Boot####")]; + CHAR16 OptionName[BM_OPTION_NAME_LEN]; EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; UINTN BootOptionCount; UINTN Index; @@ -187,7 +171,10 @@ BmFindBootOptionInVariable ( // 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); + UnicodeSPrint ( + OptionName, sizeof (OptionName), L"%s%04x", + mBmLoadOptionName[OptionToFind->OptionType], OptionToFind->OptionNumber + ); Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption); if (!EFI_ERROR (Status)) { @@ -222,56 +209,45 @@ BmFindBootOptionInVariable ( } /** - 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. + Get the file buffer using a Memory Mapped Device Path. + + FV address may change across reboot. This routine promises the FV file device path is right. - @param DevicePath The Fv file device path to be fixed up. + @param DevicePath The Memory Mapped Device Path to get the file buffer. + @param FullPath Receive the updated FV Device Path pointint to the file. + @param FileSize Receive the file buffer size. + @return The file buffer. **/ -VOID -BmFixupMemmapFvFilePath ( - IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath +VOID * +BmGetFileBufferByMemmapFv ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize ) { EFI_STATUS Status; UINTN Index; - EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_DEVICE_PATH_PROTOCOL *FvFileNode; 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_HANDLE *FvHandles; EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + VOID *FileBuffer; - Node = *DevicePath; - Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); + FvFileNode = DevicePath; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &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); + FileBuffer = GetFileBufferByFilePath (TRUE, DevicePath, FileSize, &AuthenticationStatus); + if (FileBuffer != NULL) { + *FullPath = DuplicateDevicePath (DevicePath); } - return; + return FileBuffer; } - - Node = NextDevicePathNode (DevicePath); + FvFileNode = NextDevicePathNode (DevicePath); // // Firstly find the FV file in current FV @@ -281,12 +257,12 @@ BmFixupMemmapFvFilePath ( &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage ); - NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), Node); - BmFixupMemmapFvFilePath (&NewDevicePath); + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode); + FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize); + FreePool (NewDevicePath); - if (NewDevicePath != NULL) { - BmFreeAndSet ((VOID **) DevicePath, NewDevicePath); - return; + if (FileBuffer != NULL) { + return FileBuffer; } // @@ -297,34 +273,35 @@ BmFixupMemmapFvFilePath ( &gEfiFirmwareVolume2ProtocolGuid, NULL, &FvHandleCount, - &FvHandleBuffer + &FvHandles ); - for (Index = 0; Index < FvHandleCount; Index++) { - if (FvHandleBuffer[Index] == LoadedImage->DeviceHandle) { + for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) { + if (FvHandles[Index] == LoadedImage->DeviceHandle) { // // Skip current FV // continue; } - NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandleBuffer[Index]), Node); - BmFixupMemmapFvFilePath (&NewDevicePath); - - if (NewDevicePath != NULL) { - BmFreeAndSet ((VOID **) DevicePath, NewDevicePath); - return; - } + NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode); + FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize); + FreePool (NewDevicePath); } + + if (FvHandles != NULL) { + FreePool (FvHandles); + } + return FileBuffer; } /** - Check if it's of Fv file device path type. + Check if it's a Memory Mapped FV Device Path. - The function doesn't garentee the device path points to existing Fv file. + The function doesn't garentee the device path points to existing FV file. - @param DevicePath Input device path info. + @param DevicePath Input device path. - @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. + @retval TRUE The device path is a Memory Mapped FV Device Path. + @retval FALSE The device path is NOT a Memory Mapped FV Device Path. **/ BOOLEAN BmIsMemmapFvFilePath ( @@ -668,7 +645,7 @@ BmGetMiscDescription ( CHAR16 *Description; EFI_BLOCK_IO_PROTOCOL *BlockIo; - switch (BmBootTypeFromDevicePath (DevicePathFromHandle (Handle))) { + switch (BmDevicePathType (DevicePathFromHandle (Handle))) { case BmAcpiFloppyBoot: Description = L"Floppy"; break; @@ -824,26 +801,6 @@ BmMatchUsbWwid ( 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 @@ -944,97 +901,105 @@ BmFindUsbDevice ( 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 FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath Return the full device path of the load option after + short-form device path expanding. + Caller is responsible to free it. + @param FileSize Return the load option size. + @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer. - @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. + @return The load option buffer. Caller is responsible to free the memory. **/ VOID * -BmExpandUsbShortFormDevicePath ( - IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, - OUT UINTN *FileSize +BmExpandUsbDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize, + IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode ) { UINTN ParentDevicePathSize; - EFI_DEVICE_PATH_PROTOCOL *ShortformNode; EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; - EFI_HANDLE *UsbIoHandles; - UINTN UsbIoHandleCount; + EFI_HANDLE *Handles; + UINTN HandleCount; 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; - } + + ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath; + RemainingDevicePath = NextDevicePathNode (ShortformNode); + FileBuffer = NULL; + Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount); + + for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) { + FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath); + FileBuffer = BmGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize); + FreePool (FullDevicePath); } - ASSERT (!IsDevicePathEnd (ShortformNode)); - FullDevicePath = NULL; - ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) *DevicePath; - RemainingDevicePath = NextDevicePathNode (ShortformNode); - FileBuffer = NULL; - UsbIoHandles = BmFindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount); + if (Handles != NULL) { + FreePool (Handles); + } - 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; - } + return FileBuffer; +} + +/** + Save the partition DevicePath to the CachedDevicePath as the first instance. + + @param CachedDevicePath The device path cache. + @param DevicePath The partition device path to be cached. +**/ +VOID +BmCachePartitionDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + UINTN Count; + + if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) { + TempDevicePath = *CachedDevicePath; + *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath); + FreePool (TempDevicePath); } - if (UsbIoHandles != NULL) { - FreePool (UsbIoHandles); + if (*CachedDevicePath == NULL) { + *CachedDevicePath = DuplicateDevicePath (DevicePath); + return; } - if (FileBuffer == NULL) { + TempDevicePath = *CachedDevicePath; + *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath); + if (TempDevicePath != NULL) { + FreePool (TempDevicePath); + } + + // + // 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. + // + Count = 0; + TempDevicePath = *CachedDevicePath; + while (!IsDevicePathEnd (TempDevicePath)) { + TempDevicePath = NextDevicePathNode (TempDevicePath); // - // 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. + // Parse one instance // - 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); - } + while (!IsDevicePathEndType (TempDevicePath)) { + TempDevicePath = NextDevicePathNode (TempDevicePath); + } + Count++; + // + // If the CachedDevicePath variable contain too much instance, only remain 12 instances. + // + if (Count == 12) { + SetDevicePathEndNode (TempDevicePath); + break; } } - - BmFreeAndSet ((VOID **) DevicePath, FullDevicePath); - return FileBuffer; } /** @@ -1045,34 +1010,37 @@ BmExpandUsbShortFormDevicePath ( 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. + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath Return the full device path of the load option after + short-form device path expanding. + Caller is responsible to free it. + @param FileSize Return the load option size. + @return The load option buffer. Caller is responsible to free the memory. **/ -VOID -BmExpandPartitionShortFormDevicePath ( - IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath +VOID * +BmExpandPartitionDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize ) { EFI_STATUS Status; UINTN BlockIoHandleCount; EFI_HANDLE *BlockIoBuffer; - EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; + VOID *FileBuffer; EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; UINTN Index; - UINTN InstanceNum; EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; UINTN CachedDevicePathSize; - BOOLEAN DeviceExist; BOOLEAN NeedAdjust; EFI_DEVICE_PATH_PROTOCOL *Instance; UINTN Size; - FullDevicePath = NULL; + FileBuffer = NULL; // // Check if there is prestore 'HDDP' variable. // If exist, search the front path which point to partition node in the variable instants. @@ -1098,7 +1066,6 @@ BmExpandPartitionShortFormDevicePath ( if (CachedDevicePath != NULL) { TempNewDevicePath = CachedDevicePath; - DeviceExist = FALSE; NeedAdjust = FALSE; do { // @@ -1107,15 +1074,40 @@ BmExpandPartitionShortFormDevicePath ( // partial partition boot option. Second, check whether the instance could be connected. // Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); - if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) { + if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) { // // 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; + TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath)); + FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize); + FreePool (TempDevicePath); + + if (FileBuffer != NULL) { + // + // Adjust the 'HDDP' instances sequence if the matched one is not first one. + // + if (NeedAdjust) { + BmCachePartitionDevicePath (&CachedDevicePath, Instance); + // + // 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); + return FileBuffer; + } } } // @@ -1124,50 +1116,6 @@ BmExpandPartitionShortFormDevicePath ( 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; - } } // @@ -1184,80 +1132,36 @@ BmExpandPartitionShortFormDevicePath ( // 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) { + BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]); + if (BlockIoDevicePath == NULL) { continue; } - if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) { + if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) { // // Find the matched partition device path // - FullDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (*DevicePath)); + TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath)); + FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize); + FreePool (TempDevicePath); - // - // 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); - } + if (FileBuffer != NULL) { + BmCachePartitionDevicePath (&CachedDevicePath, 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. + // 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 // - 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 - ); + Status = gRT->SetVariable ( + L"HDDP", + &mBmHardDriveBootVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, + GetDevicePathSize (CachedDevicePath), + CachedDevicePath + ); - break; + break; + } } } @@ -1267,92 +1171,92 @@ BmExpandPartitionShortFormDevicePath ( if (BlockIoBuffer != NULL) { FreePool (BlockIoBuffer); } - BmFreeAndSet ((VOID **) DevicePath, FullDevicePath); + return FileBuffer; } /** - Algorithm follows the UEFI Spec chapter 3.4 Boot Mechanisms. - - @param DevicePath Device Path to a bootable device + Expand the media device path which points to a BlockIo or SimpleFileSystem instance + by appending EFI_REMOVABLE_MEDIA_FILE_NAME. - @return The bootable media handle. If the media on the DevicePath is not bootable, NULL will return. + @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance. + @param FullPath Return the full device path pointing to the load option. + @param FileSize Return the size of the load option. + @return The load option buffer. **/ -EFI_HANDLE -BmGetBootableDeviceHandle ( - IN EFI_DEVICE_PATH_PROTOCOL *DevicePath +VOID * +BmExpandMediaDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize ) { 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; + VOID *FileBuffer; + UINT32 AuthenticationStatus; // // 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); + TempDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); + if (!EFI_ERROR (Status)) { + ASSERT (IsDevicePathEnd (TempDevicePath)); + + TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); + FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus); + if (FileBuffer == NULL) { + FreePool (TempDevicePath); + TempDevicePath = NULL; } + *FullPath = TempDevicePath; + return FileBuffer; + } + + // + // For device boot option only pointing 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); + + // + // 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. + // + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); + ASSERT_EFI_ERROR (Status); + Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); + ASSERT_EFI_ERROR (Status); + 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; + FileBuffer = NULL; + *FullPath = NULL; + Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH; gBS->LocateHandleBuffer ( ByProtocol, &gEfiSimpleFileSystemProtocolGuid, @@ -1365,87 +1269,87 @@ BmGetBootableDeviceHandle ( // Get the device path size of SimpleFileSystem handle // TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); - TempSize = GetDevicePathSize (TempDevicePath)- END_DEVICE_PATH_LENGTH; + 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]; + TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); + FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus); + if (FileBuffer != NULL) { + *FullPath = TempDevicePath; break; } + FreePool (TempDevicePath); } } if (SimpleFileSystemHandles != NULL) { - FreePool(SimpleFileSystemHandles); + FreePool (SimpleFileSystemHandles); } - return ReturnHandle; + return FileBuffer; } /** - 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. + Get the load option by its device path. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath Return the full device path of the load option after + short-form device path expanding. + Caller is responsible to free it. + @param FileSize Return the load option size. + + @return The load option buffer. Caller is responsible to free the memory. **/ VOID * -BmLoadEfiBootOption ( - IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, - OUT UINTN *FileSize +BmGetLoadOptionBuffer ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize ) { EFI_HANDLE Handle; VOID *FileBuffer; UINT32 AuthenticationStatus; EFI_DEVICE_PATH_PROTOCOL *Node; + EFI_STATUS Status; - ASSERT ((FilePath != NULL) && (*FilePath != NULL) && (FileSize != NULL)); + ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL)); - EfiBootManagerConnectDevicePath (*FilePath, NULL); + EfiBootManagerConnectDevicePath (FilePath, NULL); + *FullPath = NULL; *FileSize = 0; FileBuffer = NULL; + + // + // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI + // + Node = FilePath; + Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); + if (EFI_ERROR (Status)) { + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle); + } + + if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { + return BmExpandMediaDevicePath (FilePath, FullPath, FileSize); + } + // // Expand the short-form device path to full device path // - if ((DevicePathType (*FilePath) == MEDIA_DEVICE_PATH) && - (DevicePathSubType (*FilePath) == MEDIA_HARDDRIVE_DP)) { + if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) { // // Expand the Harddrive device path // - BmExpandPartitionShortFormDevicePath (FilePath); - if (*FilePath == NULL) { - return NULL; - } - + return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize); } else { - for (Node = *FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { + 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))) { + ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) { break; } } @@ -1454,50 +1358,34 @@ BmLoadEfiBootOption ( // // Expand the USB WWID/Class device path // - FileBuffer = BmExpandUsbShortFormDevicePath (FilePath, FileSize); - if (FileBuffer == NULL) { - return NULL; + FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node); + if ((FileBuffer == NULL) && (FilePath == Node)) { + // + // 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. + // + BmConnectUsbShortFormDevicePath (FilePath); + FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node); } + return FileBuffer; } } // // 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 (BmIsMemmapFvFilePath (FilePath)) { + return BmGetFileBufferByMemmapFv (FilePath, FullPath, FileSize); } // - // 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. + // Directly reads the load option when it doesn't reside in simple file system instance (LoadFile/LoadFile2), + // or it directly points to a file in simple file system instance. // - 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); + FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus); + if (FileBuffer != NULL) { + *FullPath = DuplicateDevicePath (FilePath); } return FileBuffer; @@ -1547,7 +1435,7 @@ EfiBootManagerBoot ( return; } - if (BootOption->FilePath == NULL) { + if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) { BootOption->Status = EFI_INVALID_PARAMETER; return; } @@ -1557,7 +1445,7 @@ EfiBootManagerBoot ( // OptionNumber = BmFindBootOptionInVariable (BootOption); if (OptionNumber == LoadOptionNumberUnassigned) { - Status = BmGetFreeOptionNumber (L"BootOrder", &Uint16); + Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16); if (!EFI_ERROR (Status)) { // // Save the BootOption->OptionNumber to restore later @@ -1601,9 +1489,12 @@ EfiBootManagerBoot ( 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(); // + // Report Status Code to indicate ReadyToBoot was signalled + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); + // // 4. Repair system through DriverHealth protocol // BmRepairAllControllers (); @@ -1615,14 +1506,20 @@ EfiBootManagerBoot ( // 5. Load EFI boot option to ImageHandle // ImageHandle = NULL; - if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { + if (BmDevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { Status = EFI_NOT_FOUND; - FilePath = DuplicateDevicePath (BootOption->FilePath); - FileBuffer = BmLoadEfiBootOption (&FilePath, &FileSize); - if (FileBuffer != NULL) { - + FileBuffer = BmGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize); + DEBUG_CODE ( + if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) { + DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: ")); + BmPrintDp (BootOption->FilePath); + DEBUG ((EFI_D_INFO, " -> ")); + BmPrintDp (FilePath); + DEBUG ((EFI_D_INFO, "\n")); + } + ); + if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) { REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); - Status = gBS->LoadImage ( TRUE, gImageHandle, @@ -1631,7 +1528,11 @@ EfiBootManagerBoot ( FileSize, &ImageHandle ); + } + if (FileBuffer != NULL) { FreePool (FileBuffer); + } + if (FilePath != NULL) { FreePool (FilePath); } @@ -1790,63 +1691,44 @@ BmMatchPartitionDevicePathNode ( IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath ) { - HARDDRIVE_DEVICE_PATH *TmpHdPath; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - BOOLEAN Match; - EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode; + HARDDRIVE_DEVICE_PATH *Node; 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) + while (!IsDevicePathEnd (BlockIoDevicePath)) { + if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) && + (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP) ) { - BlockIoHdDevicePathNode = DevicePath; break; } - DevicePath = NextDevicePathNode (DevicePath); + BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath); } - if (BlockIoHdDevicePathNode == NULL) { + if (IsDevicePathEnd (BlockIoDevicePath)) { return FALSE; } + // // See if the harddrive device path in blockio matches the orig Hard Drive Node // - TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode; - Match = FALSE; + Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath; // - // Check for the match + // Match Signature and PartitionNumber. + // Unused bytes in Signature are initiaized with zeros. // - 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; + return (BOOLEAN) ( + (Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) && + (Node->MBRType == HardDriveDevicePath->MBRType) && + (Node->SignatureType == HardDriveDevicePath->SignatureType) && + (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0) + ); } /** diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c index 0172f959d7..9e3a683e25 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c @@ -102,12 +102,13 @@ EfiBootManagerConnectAll ( 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 - + @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. + @retval EFI_SECURITY_VIOLATION The user has no permission to start UEFI device + drivers on the DevicePath. **/ EFI_STATUS EFIAPI @@ -166,9 +167,8 @@ EfiBootManagerConnectDevicePath ( // 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 + // If ConnectController fails to find a driver, 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 @@ -177,7 +177,10 @@ EfiBootManagerConnectDevicePath ( // change, then avoid the dispatch, we have chance to continue the // next connection // - gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE); + Status = gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE); + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } if (MatchingHandle != NULL) { *MatchingHandle = Handle; } diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c index 49b99957e0..4f5c8b04c2 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c @@ -695,28 +695,42 @@ EfiBootManagerConnectAllConsoles ( /** This function will connect all the console devices base on the console device variable ConIn, ConOut and ErrOut. + + @retval EFI_DEVICE_ERROR All the consoles were not connected due to an error. + @retval EFI_SUCCESS Success connect any one instance of the console + device path base on the variable ConVarName. **/ -VOID +EFI_STATUS EFIAPI EfiBootManagerConnectAllDefaultConsoles ( VOID ) { + EFI_STATUS Status; + BOOLEAN OneConnected; BOOLEAN SystemTableUpdated; - EfiBootManagerConnectConsoleVariable (ConOut); + OneConnected = FALSE; + + Status = EfiBootManagerConnectConsoleVariable (ConOut); + if (!EFI_ERROR (Status)) { + OneConnected = TRUE; + } PERF_START (NULL, "ConOutReady", "BDS", 1); PERF_END (NULL, "ConOutReady", "BDS", 0); - EfiBootManagerConnectConsoleVariable (ConIn); + Status = EfiBootManagerConnectConsoleVariable (ConIn); + if (!EFI_ERROR (Status)) { + OneConnected = TRUE; + } PERF_START (NULL, "ConInReady", "BDS", 1); PERF_END (NULL, "ConInReady", "BDS", 0); - // - // The _ModuleEntryPoint err out var is legal. - // - EfiBootManagerConnectConsoleVariable (ErrOut); + Status = EfiBootManagerConnectConsoleVariable (ErrOut); + if (!EFI_ERROR (Status)) { + OneConnected = TRUE; + } PERF_START (NULL, "ErrOutReady", "BDS", 1); PERF_END (NULL, "ErrOutReady", "BDS", 0); @@ -745,4 +759,6 @@ EfiBootManagerConnectAllDefaultConsoles ( &gST->Hdr.CRC32 ); } + + return OneConnected ? EFI_SUCCESS : EFI_DEVICE_ERROR; } diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c index fc3e29a62e..27a8db733e 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c @@ -14,21 +14,77 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "InternalBm.h" +GLOBAL_REMOVE_IF_UNREFERENCED + CHAR16 *mBmLoadOptionName[] = { + L"Driver", + L"SysPrep", + L"Boot" + }; + +GLOBAL_REMOVE_IF_UNREFERENCED + CHAR16 *mBmLoadOptionOrderName[] = { + EFI_DRIVER_ORDER_VARIABLE_NAME, + EFI_SYS_PREP_ORDER_VARIABLE_NAME, + EFI_BOOT_ORDER_VARIABLE_NAME + }; + +/** + Call Visitor function for each variable in variable storage. + + @param Visitor Visitor function. + @param Context The context passed to Visitor function. +**/ +VOID +BmForEachVariable ( + VARIABLE_VISITOR Visitor, + VOID *Context + ) +{ + EFI_STATUS Status; + CHAR16 *Name; + EFI_GUID Guid; + UINTN NameSize; + UINTN NewNameSize; + + 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); + + Visitor (Name, &Guid, Context); + } + + FreePool (Name); +} + /** 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. + @param LoadOptionType The load option type. + @param FreeOptionNumber Return the minimal free option number. - @retval EFI_SUCCESS The option number is found + @retval EFI_SUCCESS The option number is found and will be returned. @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 + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType, + OUT UINT16 *FreeOptionNumber ) { @@ -38,13 +94,14 @@ BmGetFreeOptionNumber ( UINTN OptionOrderSize; UINT16 *BootNext; - if (FreeOptionNumber == NULL) { - return EFI_INVALID_PARAMETER; - } + ASSERT (FreeOptionNumber != NULL); + ASSERT (LoadOptionType == LoadOptionTypeDriver || + LoadOptionType == LoadOptionTypeBoot || + LoadOptionType == LoadOptionTypeSysPrep); - GetEfiGlobalVariable2 (OrderVariableName, (VOID **) &OptionOrder, &OptionOrderSize); + GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **) &OptionOrder, &OptionOrderSize); BootNext = NULL; - if (*OrderVariableName == L'B') { + if (LoadOptionType == LoadOptionTypeBoot) { GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL); } @@ -94,72 +151,7 @@ BmGetFreeOptionNumber ( } /** - 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. + Create the Boot####, Driver####, SysPrep####, variable from the load option. @param LoadOption Pointer to the load option. @@ -175,13 +167,14 @@ EfiBootManagerLoadOptionToVariable ( UINTN VariableSize; UINT8 *Variable; UINT8 *Ptr; - CHAR16 OptionName[sizeof ("Driver####")]; + CHAR16 OptionName[BM_OPTION_NAME_LEN]; CHAR16 *Description; CHAR16 NullChar; + UINT32 VariableAttributes; if ((Option->OptionNumber == LoadOptionNumberUnassigned) || (Option->FilePath == NULL) || - (Option->OptionType >= LoadOptionTypeMax) + ((UINT32) Option->OptionType >= LoadOptionTypeMax) ) { return EFI_INVALID_PARAMETER; } @@ -218,42 +211,108 @@ structure. Variable = AllocatePool (VariableSize); ASSERT (Variable != NULL); - + Ptr = Variable; - *(UINT32 *) Ptr = Option->Attributes; + WriteUnaligned32 ((UINT32 *) Ptr, Option->Attributes); Ptr += sizeof (Option->Attributes); - *(UINT16 *) Ptr = (UINT16) GetDevicePathSize (Option->FilePath); + + WriteUnaligned16 ((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 - ); + UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[Option->OptionType], Option->OptionNumber); + + VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE; return gRT->SetVariable ( OptionName, &gEfiGlobalVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + VariableAttributes, 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. + 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; +} + +/** + This function will register the new Boot####, Driver#### or SysPrep#### option. + After the *#### is updated, the *Order 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_SUCCESS The *#### 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. @@ -276,14 +335,18 @@ EfiBootManagerAddLoadOptionVariable ( return EFI_INVALID_PARAMETER; } + if (Option->OptionType != LoadOptionTypeDriver && + Option->OptionType != LoadOptionTypeSysPrep && + Option->OptionType != LoadOptionTypeBoot + ) { + 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 - ); + Status = BmGetFreeOptionNumber (Option->OptionType, &OptionNumber); if (EFI_ERROR (Status)) { return Status; } @@ -294,11 +357,7 @@ EfiBootManagerAddLoadOptionVariable ( return EFI_INVALID_PARAMETER; } - Status = BmAddOptionNumberToOrderVariable ( - Option->OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", - (UINT16) Option->OptionNumber, - Position - ); + Status = BmAddOptionNumberToOrderVariable (mBmLoadOptionOrderName[Option->OptionType], (UINT16) Option->OptionNumber, Position); if (!EFI_ERROR (Status)) { // // Save the Boot#### or Driver#### variable @@ -306,7 +365,7 @@ EfiBootManagerAddLoadOptionVariable ( Status = EfiBootManagerLoadOptionToVariable (Option); if (EFI_ERROR (Status)) { // - // Remove the #### from *Order variable when the Boot####/Driver#### cannot be saved. + // Remove the #### from *Order variable when the Driver####/SysPrep####/Boot#### cannot be saved. // EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType); } @@ -357,7 +416,7 @@ EfiBootManagerSortLoadOptionVariable ( } Status = gRT->SetVariable ( - OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", + mBmLoadOptionOrderName[OptionType], &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, LoadOptionCount * sizeof (UINT16), @@ -409,6 +468,10 @@ EfiBootManagerInitializeLoadOption ( return EFI_INVALID_PARAMETER; } + if ((UINT32) OptionType >= LoadOptionTypeMax) { + return EFI_INVALID_PARAMETER; + } + ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); Option->OptionNumber = OptionNumber; Option->OptionType = OptionType; @@ -461,58 +524,15 @@ BmFindLoadOption ( } /** - 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; -} + Delete the load option. -/** - 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 + @retval others Status of RT->SetVariable() **/ EFI_STATUS EFIAPI @@ -521,14 +541,42 @@ EfiBootManagerDeleteLoadOptionVariable ( IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType ) { - if ((OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) { + UINT16 *OptionOrder; + UINTN OptionOrderSize; + EFI_STATUS Status; + UINTN Index; + + if (((UINT32) OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) { return EFI_INVALID_PARAMETER; } - return BmDeleteOptionVariable ( - OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder", - (UINT16) OptionNumber - ); + Status = EFI_NOT_FOUND; + + if (OptionType == LoadOptionTypeDriver || OptionType == LoadOptionTypeSysPrep || OptionType == LoadOptionTypeBoot) { + // + // If the associated *Order exists, just remove the reference in *Order. + // + GetEfiGlobalVariable2 (mBmLoadOptionOrderName[OptionType], &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 ( + mBmLoadOptionOrderName[OptionType], + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + OptionOrderSize, + OptionOrder + ); + break; + } + } + if (OptionOrder != NULL) { + FreePool (OptionOrder); + } + } + + return Status; } /** @@ -551,7 +599,7 @@ BmCharToUint ( } ASSERT (FALSE); - return 0; + return (UINTN) -1; } /** @@ -645,10 +693,10 @@ BmStrSizeEx ( } /** - Validate the EFI Boot#### variable (VendorGuid/Name) + Validate the Boot####, Driver####, SysPrep#### variable (VendorGuid/Name) - @param Variable Boot#### variable data. - @param VariableSize Returns the size of the EFI variable that was read + @param Variable The variable data. + @param VariableSize The variable size. @retval TRUE The variable data is correct. @retval FALSE The variable data is corrupted. @@ -661,9 +709,8 @@ BmValidateOption ( ) { UINT16 FilePathSize; - UINT8 *TempPtr; EFI_DEVICE_PATH_PROTOCOL *DevicePath; - UINTN TempSize; + UINTN DescriptionSize; if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) { return FALSE; @@ -672,47 +719,100 @@ BmValidateOption ( // // Skip the option attribute // - TempPtr = Variable; - TempPtr += sizeof (UINT32); + Variable += sizeof (UINT32); // // Get the option's device path size // - FilePathSize = *(UINT16 *) TempPtr; - TempPtr += sizeof (UINT16); + FilePathSize = ReadUnaligned16 ((UINT16 *) Variable); + Variable += sizeof (UINT16); // // Get the option's description string size // - TempSize = BmStrSizeEx ((CHAR16 *) TempPtr, VariableSize - sizeof (UINT16) - sizeof (UINT32)); - TempPtr += TempSize; + DescriptionSize = BmStrSizeEx ((CHAR16 *) Variable, VariableSize - sizeof (UINT16) - sizeof (UINT32)); + Variable += DescriptionSize; // // Get the option's device path // - DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; - TempPtr += FilePathSize; + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Variable; // // Validation boot option variable. // - if ((FilePathSize == 0) || (TempSize == 0)) { + if ((FilePathSize == 0) || (DescriptionSize == 0)) { return FALSE; } - if (TempSize + FilePathSize + sizeof (UINT16) + sizeof (UINT32) > VariableSize) { + if (sizeof (UINT32) + sizeof (UINT16) + DescriptionSize + FilePathSize > VariableSize) { return FALSE; } return (BOOLEAN) (BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0); } +/** + Check whether the VariableName is a valid load option variable name + and return the load option type and option number. + + @param VariableName The name of the load option variable. + @param OptionType Return the load option type. + @param OptionNumber Return the load option number. + + @retval TRUE The variable name is valid; The load option type and + load option number is returned. + @retval FALSE The variable name is NOT valid. +**/ +BOOLEAN +BmIsValidLoadOptionVariableName ( + IN CHAR16 *VariableName, + OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType, + OUT UINT16 *OptionNumber + ) +{ + UINTN VariableNameLen; + UINTN Index; + UINTN Uint; + + VariableNameLen = StrLen (VariableName); + + if (VariableNameLen <= 4) { + return FALSE; + } + + for (Index = 0; Index < sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0]); Index++) { + if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[Index])) && + (StrnCmp (VariableName, mBmLoadOptionName[Index], VariableNameLen - 4) == 0) + ) { + break; + } + } + + if (Index == sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0])) { + return FALSE; + } + + *OptionType = (EFI_BOOT_MANAGER_LOAD_OPTION_TYPE) Index; + *OptionNumber = 0; + for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) { + Uint = BmCharToUint (VariableName[Index]); + if (Uint == -1) { + break; + } else { + *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10; + } + } + + return (BOOLEAN) (Index == VariableNameLen); +} + /** 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. + @param VariableName Variable name of the load option + @param VendorGuid Variable GUID of the load option + @param Option Return the load option. @retval EFI_SUCCESS Get the option just been created @retval EFI_NOT_FOUND Failed to get the new option @@ -720,22 +820,22 @@ BmValidateOption ( **/ EFI_STATUS EFIAPI -EfiBootManagerVariableToLoadOption ( - IN CHAR16 *VariableName, - IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option +EfiBootManagerVariableToLoadOptionEx ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option ) { EFI_STATUS Status; UINT32 Attribute; UINT16 FilePathSize; UINT8 *Variable; - UINT8 *TempPtr; + UINT8 *VariablePtr; UINTN VariableSize; EFI_DEVICE_PATH_PROTOCOL *FilePath; UINT8 *OptionalData; UINT32 OptionalDataSize; CHAR16 *Description; - UINT8 NumOff; EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; UINT16 OptionNumber; @@ -743,80 +843,62 @@ EfiBootManagerVariableToLoadOption ( return EFI_INVALID_PARAMETER; } + if (!BmIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) { + return EFI_INVALID_PARAMETER; + } + // // Read the variable // - GetEfiGlobalVariable2 (VariableName, (VOID **) &Variable, &VariableSize); + GetVariable2 (VariableName, VendorGuid, (VOID **) &Variable, &VariableSize); if (Variable == NULL) { return EFI_NOT_FOUND; } // - // Validate Boot#### variable data. + // Validate *#### 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); + VariablePtr = Variable; + Attribute = ReadUnaligned32 ((UINT32 *) VariablePtr); + VariablePtr += sizeof (UINT32); // // Get the option's device path size // - FilePathSize = *(UINT16 *) TempPtr; - TempPtr += sizeof (UINT16); + FilePathSize = ReadUnaligned16 ((UINT16 *) VariablePtr); + VariablePtr += sizeof (UINT16); // // Get the option's description string // - Description = (CHAR16 *) TempPtr; + Description = (CHAR16 *) VariablePtr; // // Get the option's description string size // - TempPtr += StrSize ((CHAR16 *) TempPtr); + VariablePtr += StrSize ((CHAR16 *) VariablePtr); // // Get the option's device path // - FilePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr; - TempPtr += FilePathSize; + FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr; + VariablePtr += FilePathSize; - OptionalDataSize = (UINT32) (VariableSize - (UINTN) (TempPtr - Variable)); + OptionalDataSize = (UINT32) (VariableSize - (UINTN) (VariablePtr - Variable)); if (OptionalDataSize == 0) { OptionalData = NULL; } else { - OptionalData = TempPtr; + OptionalData = VariablePtr; } - 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, @@ -828,11 +910,32 @@ EfiBootManagerVariableToLoadOption ( OptionalDataSize ); ASSERT_EFI_ERROR (Status); - + + CopyGuid (&Option->VendorGuid, VendorGuid); + FreePool (Variable); return Status; } +/** +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 + ) +{ + return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, 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. @@ -857,63 +960,56 @@ EfiBootManagerGetLoadOptions ( UINTN OptionOrderSize; UINTN Index; UINTN OptionIndex; - EFI_BOOT_MANAGER_LOAD_OPTION *Option; - CHAR16 OptionName[sizeof ("Driver####")]; + EFI_BOOT_MANAGER_LOAD_OPTION *Options; + CHAR16 OptionName[BM_OPTION_NAME_LEN]; 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; - } + if (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeSysPrep || LoadOptionType == LoadOptionTypeBoot) { + // + // Read the BootOrder, or DriverOrder variable. + // + GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **) &OptionOrder, &OptionOrderSize); + if (OptionOrder == NULL) { + return NULL; + } - *OptionCount = OptionOrderSize / sizeof (UINT16); + *OptionCount = OptionOrderSize / sizeof (UINT16); - Option = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); - ASSERT (Option != NULL); + Options = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); + ASSERT (Options != 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); + OptionIndex = 0; + for (Index = 0; Index < *OptionCount; Index++) { + OptionNumber = OptionOrder[Index]; + UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[LoadOptionType], OptionNumber); + + Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName)); + EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType); + } else { + ASSERT (Options[OptionIndex].OptionNumber == OptionNumber); + OptionIndex++; + } } - 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 (OptionOrder != NULL) { - FreePool (OptionOrder); - } + if (OptionIndex < *OptionCount) { + Options = ReallocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), Options); + ASSERT (Options != NULL); + *OptionCount = OptionIndex; + } - 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; + } else { + return NULL; } - return Option; + return Options; } /** @@ -980,3 +1076,173 @@ EfiBootManagerFreeLoadOptions ( return EFI_SUCCESS; } + +/** + Return whether the PE header of the load option is valid or not. + + @param[in] Type The load option type. + @param[in] FileBuffer The PE file buffer of the load option. + @param[in] FileSize The size of the load option file. + + @retval TRUE The PE header of the load option is valid. + @retval FALSE The PE header of the load option is not valid. +**/ +BOOLEAN +BmIsLoadOptionPeHeaderValid ( + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type, + IN VOID *FileBuffer, + IN UINTN FileSize + ) +{ + EFI_IMAGE_DOS_HEADER *DosHeader; + EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHeader; + EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHeader; + UINT16 Subsystem; + + if (FileBuffer == NULL || FileSize == 0) { + return FALSE; + } + + // + // Read dos header + // + DosHeader = (EFI_IMAGE_DOS_HEADER *) FileBuffer; + if (FileSize >= sizeof (EFI_IMAGE_DOS_HEADER) && + FileSize > DosHeader->e_lfanew && DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE + ) { + // + // Read and check PE signature + // + PeHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINT8 *) FileBuffer + DosHeader->e_lfanew); + if (FileSize >= DosHeader->e_lfanew + sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) && + PeHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE + ) { + // + // Check PE32 or PE32+ magic, and machine type + // + OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *) &PeHeader->Pe32.OptionalHeader; + if ((OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC || + OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) && + EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeHeader->Pe32.FileHeader.Machine) + ) { + // + // Check the Subsystem: + // Driver#### must be of type BootServiceDriver or RuntimeDriver + // SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application + // + Subsystem = OptionalHeader->Subsystem; + if ((Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) || + (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) || + (Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) || + (Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) + ) { + return TRUE; + } + } + } + } + + return FALSE; +} + +/** + Process (load and execute) the load option. + + @param LoadOption Pointer to the load option. + + @retval EFI_INVALID_PARAMETER The load option type is invalid, + or the load option file path doesn't point to a valid file. + @retval EFI_UNSUPPORTED The load option type is of LoadOptionTypeBoot. + @retval EFI_SUCCESS The load option is inactive, or successfully loaded and executed. +**/ +EFI_STATUS +EFIAPI +EfiBootManagerProcessLoadOption ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_HANDLE ImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; + VOID *FileBuffer; + UINTN FileSize; + + if ((UINT32) LoadOption->OptionType >= LoadOptionTypeMax) { + return EFI_INVALID_PARAMETER; + } + + if (LoadOption->OptionType == LoadOptionTypeBoot) { + return EFI_UNSUPPORTED; + } + + // + // If a load option is not marked as LOAD_OPTION_ACTIVE, + // the boot manager will not automatically load the option. + // + if ((LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0) { + return EFI_SUCCESS; + } + + Status = EFI_INVALID_PARAMETER; + + // + // Load and start the load option. + // + DEBUG (( + DEBUG_INFO | DEBUG_LOAD, "Process Load Option (%s%04x) ...\n", + mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber + )); + ImageHandle = NULL; + FileBuffer = BmGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize); + DEBUG_CODE ( + if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) { + DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: ")); + BmPrintDp (LoadOption->FilePath); + DEBUG ((EFI_D_INFO, " -> ")); + BmPrintDp (FilePath); + DEBUG ((EFI_D_INFO, "\n")); + } + ); + if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) { + Status = gBS->LoadImage ( + FALSE, + gImageHandle, + FilePath, + FileBuffer, + FileSize, + &ImageHandle + ); + } + if (FilePath != NULL) { + FreePool (FilePath); + } + if (FileBuffer != NULL) { + FreePool (FileBuffer); + } + + if (!EFI_ERROR (Status)) { + Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); + ASSERT_EFI_ERROR (Status); + + ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize; + ImageInfo->LoadOptions = LoadOption->OptionalData; + // + // Before calling the image, enable the Watchdog Timer for the 5-minute period + // + gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL); + + LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData); + DEBUG (( + DEBUG_INFO | DEBUG_LOAD, "Load Option (%s%04x) Return Status = %r\n", + mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status + )); + + // + // Clear the Watchdog Timer after the image returns + // + gBS->SetWatchdogTimer (0, 0, 0, NULL); + } + + return Status; +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c index 1cfb4bb039..30d955111e 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c @@ -120,146 +120,6 @@ BmMatchDevicePaths ( 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. @@ -505,3 +365,22 @@ BmSetVariableAndReportStatusCodeOnError ( return Status; } + +/** + 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); + } +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h index c8dc226058..c362d8d7cc 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h +++ b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h @@ -100,6 +100,37 @@ CHAR16 * IN EFI_HANDLE Handle ); +#define BM_OPTION_NAME_LEN sizeof ("SysPrep####") +extern CHAR16 *mBmLoadOptionName[]; + +typedef +VOID +(*VARIABLE_VISITOR) ( + CHAR16 *Name, + EFI_GUID *Guid, + VOID *Context + ); + +/** + Call Visitor function for each variable in variable storage. + + @param Visitor Visitor function. + @param Context The context passed to Visitor function. +**/ +VOID +ForEachVariable ( + VARIABLE_VISITOR Visitor, + VOID *Context + ); + +/** + Repair all the controllers according to the Driver Health status queried. +**/ +VOID +BmRepairAllControllers ( + VOID + ); + #define BM_HOTKEY_SIGNATURE SIGNATURE_32 ('b', 'm', 'h', 'k') typedef struct { UINT32 Signature; @@ -139,7 +170,7 @@ BmLoadEfiBootOption ( /** Get the Option Number that wasn't used. - @param OrderVariableName Could be L"BootOrder" or L"DriverOrder". + @param LoadOptionType Load option type. @param FreeOptionNumber To receive the minimal free option number. @retval EFI_SUCCESS The option number is found @@ -149,8 +180,8 @@ BmLoadEfiBootOption ( **/ EFI_STATUS BmGetFreeOptionNumber ( - IN CHAR16 *OrderVariableName, - OUT UINT16 *FreeOptionNumber + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType, + OUT UINT16 *FreeOptionNumber ); /** @@ -294,6 +325,42 @@ BmSetVariableAndReportStatusCodeOnError ( IN VOID *Data ); +/** + Get the load option by its device path. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath Return the full device path of the load option after + short-form device path expanding. + Caller is responsible to free it. + @param FileSize Return the load option size. + + @return The load option buffer. Caller is responsible to free the memory. +**/ +VOID * +BmGetLoadOptionBuffer ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT UINTN *FileSize + ); + +/** + Return whether the PE header of the load option is valid or not. + + @param[in] Type The load option type. + @param[in] FileBuffer The PE file buffer of the load option. + @param[in] FileSize The size of the load option file. + + @retval TRUE The PE header of the load option is valid. + @retval FALSE The PE header of the load option is not valid. +**/ +BOOLEAN +BmIsLoadOptionPeHeaderValid ( + IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type, + IN VOID *FileBuffer, + IN UINTN FileSize + ); + /** Function compares a device path data structure to that of all the nodes of a second device path instance. @@ -361,4 +428,14 @@ BmRepairAllControllers ( VOID ); +/** + Print the device path info. + + @param DevicePath The device path need to print. +**/ +VOID +BmPrintDp ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ); + #endif // _INTERNAL_BM_H_ diff --git a/MdeModulePkg/Universal/BdsDxe/BdsEntry.c b/MdeModulePkg/Universal/BdsDxe/BdsEntry.c index 7eebe84021..020f18d48c 100644 --- a/MdeModulePkg/Universal/BdsDxe/BdsEntry.c +++ b/MdeModulePkg/Universal/BdsDxe/BdsEntry.c @@ -47,6 +47,12 @@ CHAR16 *mReadOnlyVariables[] = { EFI_OS_INDICATIONS_SUPPORT_VARIABLE_NAME }; +CHAR16 *mBdsLoadOptionName[] = { + L"Driver", + L"SysPrep", + L"Boot" +}; + CHAR16 mRecoveryBoot[] = L"Recovery Boot"; /** Event to Connect ConIn. @@ -75,7 +81,7 @@ BdsDxeOnConnectConInCallBack ( // Should not enter this case, if enter, the keyboard will not work. // May need platfrom policy to connect keyboard. // - DEBUG ((EFI_D_WARN, "[Bds] ASSERT Connect ConIn failed!!!\n")); + DEBUG ((EFI_D_WARN, "[Bds] Connect ConIn failed - %r!!!\n", Status)); } } @@ -553,103 +559,55 @@ DefaultBootBehavior ( } /** - The function will go through the driver option link list, load and start - every driver the driver option device path point to. + The function will load and start every Driver####/SysPrep####. - @param DriverOption Input driver option array. - @param DriverOptionCount Input driver option count. + @param LoadOptions Load option array. + @param LoadOptionCount Load option count. **/ VOID -LoadDrivers ( - IN EFI_BOOT_MANAGER_LOAD_OPTION *DriverOption, - IN UINTN DriverOptionCount +ProcessLoadOptions ( + IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions, + IN UINTN LoadOptionCount ) { - EFI_STATUS Status; - UINTN Index; - EFI_HANDLE ImageHandle; - EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; - BOOLEAN ReconnectAll; + EFI_STATUS Status; + UINTN Index; + BOOLEAN ReconnectAll; + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType; ReconnectAll = FALSE; + LoadOptionType = LoadOptionTypeMax; // // Process the driver option // - for (Index = 0; Index < DriverOptionCount; Index++) { + for (Index = 0; Index < LoadOptionCount; Index++) { // - // If a load option is not marked as LOAD_OPTION_ACTIVE, - // the boot manager will not automatically load the option. + // All the load options in the array should be of the same type. // - if ((DriverOption[Index].Attributes & LOAD_OPTION_ACTIVE) == 0) { - continue; - } - - // - // If a driver load option is marked as LOAD_OPTION_FORCE_RECONNECT, - // then all of the EFI drivers in the system will be disconnected and - // reconnected after the last driver load option is processed. - // - if ((DriverOption[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0) { - ReconnectAll = TRUE; + if (LoadOptionType == LoadOptionTypeMax) { + LoadOptionType = LoadOptions[Index].OptionType; } - - // - // Make sure the driver path is connected. - // - EfiBootManagerConnectDevicePath (DriverOption[Index].FilePath, NULL); + ASSERT (LoadOptionType == LoadOptions[Index].OptionType); + ASSERT (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeSysPrep); - // - // Load and start the image that Driver#### describes - // - Status = gBS->LoadImage ( - FALSE, - gImageHandle, - DriverOption[Index].FilePath, - NULL, - 0, - &ImageHandle - ); + Status = EfiBootManagerProcessLoadOption (&LoadOptions[Index]); - if (!EFI_ERROR (Status)) { - gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); - - // - // Verify whether this image is a driver, if not, - // exit it and continue to parse next load option - // - if (ImageInfo->ImageCodeType != EfiBootServicesCode && ImageInfo->ImageCodeType != EfiRuntimeServicesCode) { - gBS->Exit (ImageHandle, EFI_INVALID_PARAMETER, 0, NULL); - continue; - } - - ImageInfo->LoadOptionsSize = DriverOption[Index].OptionalDataSize; - ImageInfo->LoadOptions = DriverOption[Index].OptionalData; - // - // Before calling the image, enable the Watchdog Timer for - // the 5 Minute period - // - gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); - - DriverOption[Index].Status = gBS->StartImage (ImageHandle, &DriverOption[Index].ExitDataSize, &DriverOption[Index].ExitData); - DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Driver Return Status = %r\n", DriverOption[Index].Status)); - - // - // Clear the Watchdog Timer after the image returns - // - gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); + if (!EFI_ERROR (Status) && ((LoadOptions[Index].Attributes & LOAD_OPTION_FORCE_RECONNECT) != 0)) { + ReconnectAll = TRUE; } } - + // - // Process the LOAD_OPTION_FORCE_RECONNECT driver option + // If a driver load option is marked as LOAD_OPTION_FORCE_RECONNECT, + // then all of the EFI drivers in the system will be disconnected and + // reconnected after the last driver load option is processed. // - if (ReconnectAll) { + if (ReconnectAll && LoadOptionType == LoadOptionTypeDriver) { EfiBootManagerDisconnectAll (); EfiBootManagerConnectAll (); } - } /** @@ -855,9 +813,8 @@ BdsEntry ( IN EFI_BDS_ARCH_PROTOCOL *This ) { - EFI_BOOT_MANAGER_LOAD_OPTION *DriverOption; - EFI_BOOT_MANAGER_LOAD_OPTION BootOption; - UINTN DriverOptionCount; + EFI_BOOT_MANAGER_LOAD_OPTION *LoadOptions; + UINTN LoadOptionCount; CHAR16 *FirmwareVendor; EFI_EVENT HotkeyTriggered; UINT64 OsIndication; @@ -867,8 +824,11 @@ BdsEntry ( UINT16 BootTimeOut; EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock; UINTN Index; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; UINT16 *BootNext; CHAR16 BootNextVariableName[sizeof ("Boot####")]; + EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu; + BOOLEAN BootFwUi; HotkeyTriggered = NULL; Status = EFI_SUCCESS; @@ -939,7 +899,7 @@ BdsEntry ( // Initialize L"BootOptionSupport" EFI global variable. // Lazy-ConIn implictly disables BDS hotkey. // - BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP; + BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP | EFI_BOOT_OPTION_SUPPORT_SYSPREP; if (!PcdGetBool (PcdConInConnectOnDemand)) { BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY; SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3); @@ -1010,11 +970,11 @@ BdsEntry ( EfiBootManagerStartHotkeyService (&HotkeyTriggered); // - // Load Driver Options + // Execute Driver Options // - DriverOption = EfiBootManagerGetLoadOptions (&DriverOptionCount, LoadOptionTypeDriver); - LoadDrivers (DriverOption, DriverOptionCount); - EfiBootManagerFreeLoadOptions (DriverOption, DriverOptionCount); + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeDriver); + ProcessLoadOptions (LoadOptions, LoadOptionCount); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); // // Connect consoles @@ -1059,23 +1019,28 @@ BdsEntry ( PERF_END (NULL, "PlatformBootManagerAfterConsole", "BDS", 0); DEBUG_CODE ( - EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; - UINTN BootOptionCount; - UINTN Index; - - BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); - DEBUG ((EFI_D_INFO, "[Bds]=============Dumping Boot Options=============\n")); - for (Index = 0; Index < BootOptionCount; Index++) { + UINTN Index; + EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType; + DEBUG ((EFI_D_INFO, "[Bds]=============Begin Load Options Dumping ...=============\n")); + for (LoadOptionType = 0; LoadOptionType < LoadOptionTypeMax; LoadOptionType++) { DEBUG (( - EFI_D_INFO, "[Bds]Boot%04x: %s \t\t 0x%04x\n", - BootOptions[Index].OptionNumber, - BootOptions[Index].Description, - BootOptions[Index].Attributes + EFI_D_INFO, " %s Options:\n", + mBdsLoadOptionName[LoadOptionType] )); + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionType); + for (Index = 0; Index < LoadOptionCount; Index++) { + DEBUG (( + EFI_D_INFO, " %s%04x: %s \t\t 0x%04x\n", + mBdsLoadOptionName[LoadOptionType], + LoadOptions[Index].OptionNumber, + LoadOptions[Index].Description, + LoadOptions[Index].Attributes + )); + } + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); } - DEBUG ((EFI_D_INFO, "[Bds]=============Dumping Boot Options Finished====\n")); - EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); - ); + DEBUG ((EFI_D_INFO, "[Bds]=============End Load Options Dumping=============\n")); + ); // // Boot to Boot Manager Menu when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot @@ -1088,10 +1053,15 @@ BdsEntry ( &DataSize, &OsIndication ); - if (!EFI_ERROR(Status) && ((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0)) { - // - // Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS - // + if (EFI_ERROR (Status)) { + OsIndication = 0; + } + + BootFwUi = (BOOLEAN) ((OsIndication & EFI_OS_INDICATIONS_BOOT_TO_FW_UI) != 0); + // + // Clear EFI_OS_INDICATIONS_BOOT_TO_FW_UI to acknowledge OS + // + if (BootFwUi) { OsIndication &= ~((UINT64) EFI_OS_INDICATIONS_BOOT_TO_FW_UI); Status = gRT->SetVariable ( L"OsIndications", @@ -1104,7 +1074,12 @@ BdsEntry ( // Changing the content without increasing its size with current variable implementation shouldn't fail. // ASSERT_EFI_ERROR (Status); + } + // + // Launch Boot Manager Menu directly when EFI_OS_INDICATIONS_BOOT_TO_FW_UI is set. Skip HotkeyBoot + // + if (BootFwUi) { // // Follow generic rule, Call BdsDxeOnConnectConInCallBack to connect ConIn before enter UI // @@ -1113,24 +1088,35 @@ BdsEntry ( } // - // Directly boot to Boot Manager Menu. + // Directly enter the setup page. + // BootManagerMenu always contains the correct information even call fails. // - EfiBootManagerGetBootManagerMenu (&BootOption); - EfiBootManagerBoot (&BootOption); - EfiBootManagerFreeLoadOption (&BootOption); - } else { - PERF_START (NULL, "BdsWait", "BDS", 0); - BdsWait (HotkeyTriggered); - PERF_END (NULL, "BdsWait", "BDS", 0); - - // - // BdsReadKeys() be removed after all keyboard drivers invoke callback in timer callback. - // - BdsReadKeys (); - - EfiBootManagerHotkeyBoot (); + EfiBootManagerGetBootManagerMenu (&BootManagerMenu); + EfiBootManagerBoot (&BootManagerMenu); + EfiBootManagerFreeLoadOption (&BootManagerMenu); } + // + // Execute SysPrep#### + // + LoadOptions = EfiBootManagerGetLoadOptions (&LoadOptionCount, LoadOptionTypeSysPrep); + ProcessLoadOptions (LoadOptions, LoadOptionCount); + EfiBootManagerFreeLoadOptions (LoadOptions, LoadOptionCount); + + // + // Execute Key#### + // + PERF_START (NULL, "BdsWait", "BDS", 0); + BdsWait (HotkeyTriggered); + PERF_END (NULL, "BdsWait", "BDS", 0); + + // + // BdsReadKeys() be removed after all keyboard drivers invoke callback in timer callback. + // + BdsReadKeys (); + + EfiBootManagerHotkeyBoot (); + // // Boot to "BootNext" //