| /** | |
| Implement UnitTestBootLib using USB Class Boot option. This should be | |
| industry standard and should work on all platforms | |
| Copyright (c) Microsoft Corporation.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <PiDxe.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/UefiRuntimeServicesTableLib.h> | |
| #include <Library/UefiBootManagerLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Protocol/DevicePath.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| /** | |
| Set the boot manager to boot from a specific device on the next boot. This | |
| should be set only for the next boot and shouldn't require any manual clean up | |
| @retval EFI_SUCCESS Boot device for next boot was set. | |
| @retval EFI_UNSUPPORTED Setting the boot device for the next boot is not | |
| supportted. | |
| @retval Other Boot device for next boot can not be set. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SetBootNextDevice ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_BOOT_MANAGER_LOAD_OPTION NewOption; | |
| UINT32 Attributes; | |
| UINT8 *OptionalData; | |
| UINT32 OptionalDataSize; | |
| UINT16 BootNextValue; | |
| USB_CLASS_DEVICE_PATH UsbDp; | |
| EFI_DEVICE_PATH_PROTOCOL *DpEnd; | |
| EFI_DEVICE_PATH_PROTOCOL *Dp; | |
| BOOLEAN NewOptionValid; | |
| OptionalData = NULL; | |
| OptionalDataSize = 0; | |
| BootNextValue = 0xABCD; // this should be a safe number... | |
| DpEnd = NULL; | |
| Dp = NULL; | |
| NewOptionValid = FALSE; | |
| UsbDp.Header.Length[0] = (UINT8)(sizeof (USB_CLASS_DEVICE_PATH) & 0xff); | |
| UsbDp.Header.Length[1] = (UINT8)(sizeof (USB_CLASS_DEVICE_PATH) >> 8); | |
| UsbDp.Header.Type = MESSAGING_DEVICE_PATH; | |
| UsbDp.Header.SubType = MSG_USB_CLASS_DP; | |
| UsbDp.VendorId = 0xFFFF; | |
| UsbDp.ProductId = 0xFFFF; | |
| UsbDp.DeviceClass = 0xFF; | |
| UsbDp.DeviceSubClass = 0xFF; | |
| UsbDp.DeviceProtocol = 0xFF; | |
| Attributes = LOAD_OPTION_ACTIVE; | |
| DpEnd = AppendDevicePathNode (NULL, NULL); | |
| if (DpEnd == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: Unable to create device path. DpEnd is NULL.\n", __func__)); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto CLEANUP; | |
| } | |
| // @MRT --- Is this memory leak because we lose the old Dp memory | |
| Dp = AppendDevicePathNode ( | |
| DpEnd, | |
| (EFI_DEVICE_PATH_PROTOCOL *)&UsbDp | |
| ); | |
| if (Dp == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: Unable to create device path. Dp is NULL.\n", __func__)); | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto CLEANUP; | |
| } | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &NewOption, | |
| (UINTN)BootNextValue, | |
| LoadOptionTypeBoot, | |
| Attributes, | |
| L"Generic USB Class Device", | |
| Dp, | |
| OptionalData, | |
| OptionalDataSize | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Error creating load option. Status = %r\n", __func__, Status)); | |
| goto CLEANUP; | |
| } | |
| NewOptionValid = TRUE; | |
| DEBUG ((DEBUG_VERBOSE, "%a: Generic USB Class Device boot option created.\n", __func__)); | |
| Status = EfiBootManagerLoadOptionToVariable (&NewOption); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "%a: Error Saving boot option NV variable. Status = %r\n", __func__, Status)); | |
| goto CLEANUP; | |
| } | |
| // | |
| // Set Boot Next | |
| // | |
| Status = gRT->SetVariable ( | |
| L"BootNext", | |
| &gEfiGlobalVariableGuid, | |
| (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE), | |
| sizeof (BootNextValue), | |
| &(BootNextValue) | |
| ); | |
| DEBUG ((DEBUG_VERBOSE, "%a - Set BootNext Status (%r)\n", __func__, Status)); | |
| CLEANUP: | |
| if (Dp != NULL) { | |
| FreePool (Dp); | |
| } | |
| if (DpEnd != NULL) { | |
| FreePool (DpEnd); | |
| } | |
| if (NewOptionValid) { | |
| EfiBootManagerFreeLoadOption (&NewOption); | |
| } | |
| return Status; | |
| } |