1 /**
2   Implement UnitTestBootLib using USB Class Boot option.  This should be
3   industry standard and should work on all platforms
4 
5   Copyright (c) Microsoft Corporation.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include <PiDxe.h>
10 #include <Library/DebugLib.h>
11 #include <Library/UefiRuntimeServicesTableLib.h>
12 #include <Library/UefiBootManagerLib.h>
13 #include <Library/DevicePathLib.h>
14 #include <Protocol/DevicePath.h>
15 #include <Library/MemoryAllocationLib.h>
16 
17 /**
18   Set the boot manager to boot from a specific device on the next boot. This
19   should be set only for the next boot and shouldn't require any manual clean up
20 
21   @retval EFI_SUCCESS      Boot device for next boot was set.
22   @retval EFI_UNSUPPORTED  Setting the boot device for the next boot is not
23                            supportted.
24   @retval Other            Boot device for next boot can not be set.
25 **/
26 EFI_STATUS
27 EFIAPI
SetBootNextDevice(VOID)28 SetBootNextDevice (
29    VOID
30   )
31 {
32   EFI_STATUS                    Status;
33   EFI_BOOT_MANAGER_LOAD_OPTION  NewOption;
34   UINT32                        Attributes;
35   UINT8                         *OptionalData;
36   UINT32                        OptionalDataSize;
37   UINT16                        BootNextValue;
38   USB_CLASS_DEVICE_PATH         UsbDp;
39   EFI_DEVICE_PATH_PROTOCOL      *DpEnd;
40   EFI_DEVICE_PATH_PROTOCOL      *Dp;
41   BOOLEAN                       NewOptionValid;
42 
43   OptionalData     = NULL;
44   OptionalDataSize = 0;
45   BootNextValue    = 0xABCD;  // this should be a safe number...
46   DpEnd            = NULL;
47   Dp               = NULL;
48   NewOptionValid   = FALSE;
49 
50   UsbDp.Header.Length[0] = (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) & 0xff);
51   UsbDp.Header.Length[1] = (UINT8)(sizeof(USB_CLASS_DEVICE_PATH) >> 8);
52   UsbDp.Header.Type      = MESSAGING_DEVICE_PATH;
53   UsbDp.Header.SubType   = MSG_USB_CLASS_DP;
54   UsbDp.VendorId         = 0xFFFF;
55   UsbDp.ProductId        = 0xFFFF;
56   UsbDp.DeviceClass      = 0xFF;
57   UsbDp.DeviceSubClass   = 0xFF;
58   UsbDp.DeviceProtocol   = 0xFF;
59 
60   Attributes = LOAD_OPTION_ACTIVE;
61 
62   DpEnd = AppendDevicePathNode (NULL, NULL);
63   if (DpEnd == NULL) {
64     DEBUG ((DEBUG_ERROR, "%a: Unable to create device path.  DpEnd is NULL.\n", __FUNCTION__));
65     Status = EFI_OUT_OF_RESOURCES;
66     goto CLEANUP;
67   }
68 
69   //@MRT --- Is this memory leak because we lose the old Dp memory
70   Dp = AppendDevicePathNode (
71          DpEnd,
72          (EFI_DEVICE_PATH_PROTOCOL *)&UsbDp
73          );
74   if (Dp == NULL) {
75     DEBUG((DEBUG_ERROR, "%a: Unable to create device path.  Dp is NULL.\n", __FUNCTION__));
76     Status = EFI_OUT_OF_RESOURCES;
77     goto CLEANUP;
78   }
79 
80   Status = EfiBootManagerInitializeLoadOption (
81              &NewOption,
82              (UINTN) BootNextValue,
83              LoadOptionTypeBoot,
84              Attributes,
85              L"Generic USB Class Device",
86              Dp,
87              OptionalData,
88              OptionalDataSize
89              );
90   if (EFI_ERROR (Status)) {
91     DEBUG ((DEBUG_ERROR, "%a: Error creating load option.  Status = %r\n", __FUNCTION__, Status));
92     goto CLEANUP;
93   }
94 
95   NewOptionValid = TRUE;
96   DEBUG ((DEBUG_VERBOSE, "%a: Generic USB Class Device boot option created.\n", __FUNCTION__));
97   Status = EfiBootManagerLoadOptionToVariable (&NewOption);
98   if (EFI_ERROR (Status)) {
99     DEBUG ((DEBUG_ERROR, "%a: Error Saving boot option NV variable. Status = %r\n", __FUNCTION__, Status));
100     goto CLEANUP;
101   }
102 
103   //
104   // Set Boot Next
105   //
106   Status = gRT->SetVariable (
107                   L"BootNext",
108                   &gEfiGlobalVariableGuid,
109                   (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE),
110                   sizeof(BootNextValue),
111                   &(BootNextValue)
112                   );
113 
114   DEBUG((DEBUG_VERBOSE, "%a - Set BootNext Status (%r)\n", __FUNCTION__, Status));
115 
116 CLEANUP:
117   if (Dp != NULL) {
118     FreePool (Dp);
119   }
120   if (DpEnd != NULL) {
121     FreePool (DpEnd);
122   }
123   if (NewOptionValid) {
124     EfiBootManagerFreeLoadOption (&NewOption);
125   }
126   return Status;
127 }
128