efi_loader: bootmgr: support BootNext and BootCurrent variable behavior
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Wed, 20 Mar 2019 00:07:55 +0000 (09:07 +0900)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Sun, 7 Apr 2019 12:17:06 +0000 (14:17 +0200)
See UEFI v2.7, section 3.1.2 for details of the specification.

With efidebug command, you can run any EFI boot option as follows:
  => efi boot add 1 SHELL ...
  => efi boot add 2 HELLO ...
  => efi boot order 1 2
  => efi bootmgr
     (starting SHELL ...)

  => efi boot next 2
  => efi bootmgr
     (starting HELLO ...)
  => env print -e
  <snip ...>
  BootCurrent: {boot,run}(blob)
  00000000:  02 00                    ..
  BootOrder: {boot,run}(blob)
  00000000:  01 00 02 00              ....

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
lib/efi_loader/efi_bootmgr.c

index 417016102b48e2fb14767da761f4da943d137b88..4fccadc5483d00212feb4ded6913faa5f8af79c0 100644 (file)
@@ -141,6 +141,7 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
        efi_deserialize_load_option(&lo, load_option);
 
        if (lo.attributes & LOAD_OPTION_ACTIVE) {
+               u32 attributes;
                efi_status_t ret;
 
                debug("%s: trying to load \"%ls\" from %pD\n",
@@ -151,6 +152,16 @@ static void *try_load_entry(uint16_t n, struct efi_device_path **device_path,
                if (ret != EFI_SUCCESS)
                        goto error;
 
+               attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                            EFI_VARIABLE_RUNTIME_ACCESS;
+               size = sizeof(n);
+               ret = EFI_CALL(efi_set_variable(
+                               L"BootCurrent",
+                               (efi_guid_t *)&efi_global_variable_guid,
+                               attributes, size, &n));
+               if (ret != EFI_SUCCESS)
+                       goto error;
+
                printf("Booting: %ls\n", lo.label);
                efi_dp_split_file_path(lo.file_path, device_path, file_path);
        }
@@ -162,21 +173,53 @@ error:
 }
 
 /*
- * Attempt to load, in the order specified by BootOrder EFI variable, the
- * available load-options, finding and returning the first one that can
- * be loaded successfully.
+ * Attempt to load from BootNext or in the order specified by BootOrder
+ * EFI variable, the available load-options, finding and returning
+ * the first one that can be loaded successfully.
  */
 void *efi_bootmgr_load(struct efi_device_path **device_path,
                       struct efi_device_path **file_path)
 {
-       uint16_t *bootorder;
+       u16 bootnext, *bootorder;
        efi_uintn_t size;
        void *image = NULL;
        int i, num;
+       efi_status_t ret;
 
        bs = systab.boottime;
        rs = systab.runtime;
 
+       /* BootNext */
+       bootnext = 0;
+       size = sizeof(bootnext);
+       ret = EFI_CALL(efi_get_variable(L"BootNext",
+                                       (efi_guid_t *)&efi_global_variable_guid,
+                                       NULL, &size, &bootnext));
+       if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+               /* BootNext does exist here */
+               if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
+                       printf("BootNext must be 16-bit integer\n");
+
+               /* delete BootNext */
+               ret = EFI_CALL(efi_set_variable(
+                                       L"BootNext",
+                                       (efi_guid_t *)&efi_global_variable_guid,
+                                       0, 0, &bootnext));
+
+               /* load BootNext */
+               if (ret == EFI_SUCCESS) {
+                       if (size == sizeof(u16)) {
+                               image = try_load_entry(bootnext, device_path,
+                                                      file_path);
+                               if (image)
+                                       return image;
+                       }
+               } else {
+                       printf("Deleting BootNext failed\n");
+               }
+       }
+
+       /* BootOrder */
        bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
        if (!bootorder) {
                printf("BootOrder not defined\n");