On 2/14/25 8:40 AM, Alec Brown wrote:
A Unified Kernel Image is a single UEFI PE file that combines a UEFI boot stub,
a Linux kernel image, an initrd, and further resources. The uki command will
locate where the uki file is and create a GRUB menu entry to load it.

Signed-off-by: Alec Brown <alec.r.br...@oracle.com>
---
  docs/grub.texi              |  26 ++
  grub-core/commands/blsuki.c | 712 +++++++++++++++++++++++++++---------
  include/grub/menu.h         |   2 +
  3 files changed, 562 insertions(+), 178 deletions(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index a8465fc0b..ecf261717 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4403,6 +4403,7 @@ you forget a command, you can run the command 
@command{help}
  * test::                        Check file types and compare values
  * true::                        Do nothing, successfully
  * trust::                       Add public key to list of trusted keys
+* uki::                         Load Unified Kernel Image menu entries
  * unset::                       Unset an environment variable
  @comment * vbeinfo::                     List available video modes
  * verify_detached::             Verify detached digital signature
@@ -5904,6 +5905,31 @@ Unset the environment variable @var{envvar}.
  @end deffn
+@node uki
+@subsection uki
+
+@deffn Command uki [@option{--path} dir] [@option{--show-default}] 
[@option{--show-non-default}] [@option{--entry} file]
+Load Unified Kernel Image entries into the GRUB menu.
+
+The @option{--path} option overrides the default path to the directory 
containing
+the UKI entries. If this option isn't used, the default location is
+/EFI/Linux in the EFI system partition.
+
+The @option{--show-default} option allows the default boot entry to be added 
to the
+GRUB menu from the UKI entries.
+
+The @option{--show-non-default} option allows non-default boot entries to be 
added to
+the GRUB menu from the UKI entries.
+
+The @option{--entry} option allows specific boot entries to be added to the 
GRUB menu
+from the UKI entries.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} 
options
+are used to filter which UKI entries are added to the GRUB menu. If none are
+used, all entries in the default location or the location specified by 
@option{--path}
+will be added to the GRUB menu.
+@end deffn
+
  @ignore
  @node vbeinfo
  @subsection vbeinfo
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
index 8a3c5818f..3c26f2a56 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -39,11 +39,23 @@
  #define GRUB_BOOT_DEVICE "/"
  #endif
+#ifdef GRUB_MACHINE_EFI
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/efi/pe32.h>
+#endif
+
  GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
-static const struct grub_arg_option opt[] =
+#define GRUB_BLS_CMD 1
+#define GRUB_UKI_CMD 2
+
+static int cmd_type = 0;
+
+static const struct grub_arg_option bls_opt[] =
    {
      {"path", 'p', 0, N_("Specify path to find BLS entries."), N_("DIR"), 
ARG_TYPE_PATHNAME},
      {"show-default", 'd', 0, N_("Allow the default BLS entry to be added to the 
GRUB menu."), 0, ARG_TYPE_NONE},
@@ -52,6 +64,17 @@ static const struct grub_arg_option opt[] =
      {0, 0, 0, 0, 0, 0}
    };
+#ifdef GRUB_MACHINE_EFI
+static const struct grub_arg_option uki_opt[] =
+  {
+    {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), 
ARG_TYPE_PATHNAME},
+    {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the 
GRUB menu."), 0, ARG_TYPE_NONE},
+    {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added 
to the GRUB menu."), 0, ARG_TYPE_NONE},
+    {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), 
N_("FILE"), ARG_TYPE_FILE},
+    {0, 0, 0, 0, 0, 0}
+  };
+#endif
+
  struct keyval
  {
    const char *key;
@@ -270,7 +293,7 @@ bls_read_entry (grub_file_t f, grub_blsuki_entry_t *entry)
          break;
        }
- separator[0] = '\0';
+      separator[0] = '\0';
do
        {
@@ -287,6 +310,183 @@ bls_read_entry (grub_file_t f, grub_blsuki_entry_t *entry)
    return rc;
  }
+#ifdef GRUB_MACHINE_EFI
+static int
+uki_read_entry (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+  struct grub_msdos_image_header *dos;
+  struct grub_pe_image_header *pe;
+  grub_off_t section_offset = 0;
+  struct grub_pe32_section_table *section;
+  struct grub_pe32_coff_header *coff_header;
+
+  dos = grub_zalloc (sizeof (*dos));
+  if (dos == NULL)
+    return 1;
+  if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos))
+    {
+      grub_dprintf ("blsuki", "failed to read UKI image header\n");
+      grub_free (dos);
+      return 1;
+    }
+  if (dos->msdos_magic != GRUB_PE32_MAGIC)
+    {
+      grub_dprintf ("blsuki", "plain image kernel not supported\n");
+      grub_free (dos);
+      return 1;
+    }
+  grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", 
dos->pe_image_header_offset);
+  pe = grub_zalloc (sizeof (*pe));
+  if (pe == NULL)
+    {
+      grub_free (dos);
+      return 1;
+    }
+  if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1
+      || grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe))
+    {
+      grub_dprintf ("blsuki", "failed to read COFF image header\n");
+      grub_free (dos);
+      grub_free (pe);
+      return 1;
+    }
+  if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC)
+    {
+      grub_dprintf ("blsuki", "non-native image not supported\n");
+      grub_free (dos);
+      grub_free (pe);
+      return 1;
+    }
+
+  coff_header = &(pe->coff_header);
+  section_offset = dos->pe_image_header_offset + sizeof (*pe);
+
+  for (int i = 0; i < coff_header->num_sections; i++)
+    {
+      char *val;
+      char *key;

I don't quite understand this. So every section found in the PE becomes a key/val pair for this GRUB menu option? It seems like you would not want to do that for non-UKI related sections (like data, reloc sections, etc).

+
+      section = grub_zalloc (sizeof (*section));
+      if (section == NULL)
+       {
+         grub_free (dos);
+         grub_free (pe);
+         return 1;
+       }
+      grub_file_seek (f, section_offset);
+      if (grub_file_read (f, section, sizeof (*section)) != sizeof (*section))
+       {
+         grub_dprintf ("blsuki", "failed to read section header\n");
+         grub_free (dos);
+         grub_free (pe);
+         grub_free (section);
+         return 1;
+       }
+
+      val = grub_zalloc (section->raw_data_size);
+      if (val == NULL)
+       {
+         grub_free (dos);
+         grub_free (pe);
+         grub_free (section);
+         return 1;
+       }
+      grub_file_seek (f, section->raw_data_offset);
+      if (grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) 
section->raw_data_size)

What exactly is being read as the "val" here? I thought the key/val pairs became part of the GRUB menu entry but it looks like the whole file section is being read.

+       {
+         grub_dprintf ("blsuki", "failed to read section\n");
+         grub_free (dos);
+         grub_free (pe);
+         grub_free (section);
+         grub_free (val);
+         return 1;
+       }
+
+      key = grub_strndup (section->name, 8);
+      if (key == NULL)
+       {
+         grub_free (dos);
+         grub_free (pe);
+         grub_free (section);
+         grub_free (val);

It seems like a lot of these frees () can be put in a common section at the end of the function and use gotos to exit this function.

+         return 1;
+       }
+      grub_dprintf ("blsuki", "section name: %s\n", key);
+      blsuki_add_keyval (entry, key, val);
+      section_offset += sizeof (*section);
+      grub_free (section);
+      grub_free (val);
+      grub_free (key);
+    }
+  return 0;
+}
+#endif
+
+static char *
+uki_read_osrel (char *content, grub_off_t *pos, char **key_ret, char **val_ret)
+{
+  char *line;
+  char *value;
+  grub_size_t linelen;
+
+ skip:
+  line = content + *pos;
+  if (*line == '\0')
+    return NULL;
+
+  linelen = 0;
+  while (line[linelen] != '\0' && !grub_strchr ("\n\r", line[linelen]))
+    linelen++;
+
+  /* Move pos to the next line */
+  *pos += linelen;
+  if (content[*pos] != '\0')
+    (*pos)++;
+
+  /* Skip empty line */
+  if (linelen == 0)
+    goto skip;
+
+  line[linelen] = '\0';
+
+  /* Remove leading white space */
+  while (grub_strchr (" \t", *line))
+    {
+      line++;
+      linelen--;
+    }
+
+  /* Remove trailing whitespace */
+  while (linelen > 0 && grub_strchr ("=", line[linelen - 1]))
+    linelen--;
+  line[linelen] = '\0';
+
+  if (*line == '#')
+    goto skip;
+
+  /* Split key/value */
+  value = line;
+  while (*value != '\0' && !grub_strchr ("=", *value))
+    value++;
+  if (*value == '\0')
+    goto skip;
+  *value = '\0';
+  value++;
+  while (*value != '\0' && grub_strchr ("=", *value))
+    value++;
+
+  /* Remove quotes from value */
+  if (value[0] == '\"' && line[linelen - 1] == '\"')
+    {
+      value++;
+      line[linelen - 1] = '\0';
+    }
+
+  *key_ret = line;
+  *val_ret = value;
+  return line;
+}
+
  struct read_entry_info
  {
    const char *devid;
@@ -299,10 +499,12 @@ read_entry (const char *filename,
            const struct grub_dirhook_info *dirhook_info __attribute__ 
((__unused__)),
            void *data)
  {
-  grub_size_t m = 0, n, ext_len = 5;
+  grub_size_t m = 0, n, ext_len;
    int rc = 0;
    char *p = NULL;
+  const char *ext = NULL;
    grub_file_t f = NULL;
+  enum grub_file_type file_type = 0;
    grub_blsuki_entry_t *entry;
    struct read_entry_info *info = (struct read_entry_info *) data;
@@ -310,6 +512,18 @@ read_entry (const char *filename, n = grub_strlen (filename); + if (cmd_type == GRUB_BLS_CMD)
+    {
+      ext = ".conf";
+      file_type = GRUB_FILE_TYPE_CONFIG;
+    }
+  else if (cmd_type == GRUB_UKI_CMD)
+    {
+      ext = ".efi";
+      file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
+    }
+  ext_len = grub_strlen (ext);
+
    if (info->file != NULL)
      {
        f = info->file;
@@ -319,15 +533,15 @@ read_entry (const char *filename,
        if (filename[0] == '.')
        return 0;
- if (n <= 5)
+      if (n <= ext_len)
        return 0;
- if (grub_strcmp (filename + n - ext_len, ".conf") != 0)
+      if (grub_strcmp (filename + n - ext_len, ext) != 0)
        return 0;
p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename);
-
-      f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG);
+      //GRUB_FILE_TYPE_EFI_CHAINLOADER_IMAGE for uki
+      f = grub_file_open (p, file_type);
        grub_free (p);
        if (f == NULL)
        goto finish;
@@ -364,7 +578,28 @@ read_entry (const char *filename,
        goto finish;
      }
- rc = bls_read_entry (f, entry);
+  entry->dirname = grub_strdup (info->dirname);
+  if (entry->dirname == NULL)
+    {
+      grub_free (entry);
+      goto finish;
+    }
+
+  entry->devid = grub_strdup (info->devid);
+  if (entry->devid == NULL)
+    {
+      grub_free (entry);
+      goto finish;
+    }
+
+  if (cmd_type == GRUB_BLS_CMD)
+    rc = bls_read_entry (f, entry);
+  else if (cmd_type == GRUB_UKI_CMD)
+    {
+#ifdef GRUB_MACHINE_EFI
+      rc = uki_read_entry (f, entry);
+#endif
+    }
if (rc == 0)
      blsuki_add_entry (entry);
@@ -565,7 +800,7 @@ create_entry (grub_blsuki_entry_t *entry)
  {
    int argc = 0;
    const char **argv = NULL;
-  char *title = NULL;
+  char *title = entry->filename;
    char *clinux = NULL;
    char *options = NULL;
    char **initrds = NULL;
@@ -592,224 +827,286 @@ create_entry (grub_blsuki_entry_t *entry)
    bool add_dt_prefix = false;
    bool savedefault;
- clinux = blsuki_get_val (entry, "linux", NULL);
+  if (cmd_type == GRUB_BLS_CMD)
+    clinux = blsuki_get_val (entry, "linux", NULL);
+  else if (cmd_type == GRUB_UKI_CMD)
+    clinux = blsuki_get_val (entry, ".linux", NULL);
    if (clinux == NULL)
      {
        grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", 
entry->filename);
        goto finish;
      }
- /* Strip the ".conf" off the end before we make it our "id" field. */
-  do
+  if (cmd_type == GRUB_BLS_CMD)
      {
-      dotconf = grub_strstr (dotconf, ".conf");
-    }
-  while (dotconf != NULL && dotconf[5] != '\0');
-  if (dotconf != NULL)
-    dotconf[0] = '\0';
-
-  title = blsuki_get_val (entry, "title", NULL);
-  options = expand_val (blsuki_get_val (entry, "options", NULL));
+      /* Strip the ".conf" off the end before we make it our "id" field. */
+      do
+       {
+         dotconf = grub_strstr (dotconf, ".conf");
+       }
+      while (dotconf != NULL && dotconf[5] != '\0');
+      if (dotconf != NULL)
+       dotconf[0] = '\0';
- if (options == NULL)
-    options = expand_val (grub_env_get ("default_kernelopts"));
+      title = blsuki_get_val (entry, "title", NULL);
+      options = expand_val (blsuki_get_val (entry, "options", NULL));
- initrds = blsuki_make_list (entry, "initrd", NULL);
+      if (options == NULL)
+       options = expand_val (grub_env_get ("default_kernelopts"));
- devicetree = expand_val (blsuki_get_val (entry, "devicetree", NULL));
+      initrds = blsuki_make_list (entry, "initrd", NULL);
- if (devicetree == NULL)
-    {
-      devicetree = expand_val (grub_env_get ("devicetree"));
-      add_dt_prefix = true;
-    }
+      devicetree = expand_val (blsuki_get_val (entry, "devicetree", NULL));
- hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
-  users = expand_val (blsuki_get_val (entry, "grub_users", NULL));
-  classes = blsuki_make_list (entry, "grub_class", NULL);
-  args = blsuki_make_list (entry, "grub_arg", &argc);
-
-  argc += 1;
-  if (grub_mul (argc + 1, sizeof (char *), &size))
-    {
-      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv 
list"));
-      goto finish;
-    }
-  argv = grub_malloc (size);
-  if (argv == NULL)
-    {
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate argv list");
-      goto finish;
-    }
-  argv[0] = title ? title : clinux;
-  for (i = 1; i < argc; i++)
-    argv[i] = args[i-1];
-  argv[argc] = NULL;
-
-  early_initrd = grub_env_get ("early_initrd");
-
-  grub_dprintf ("blsuki", "adding menu entry for \"%s\" with id \"%s\"\n",
-               title, id);
-  if (early_initrd != NULL)
-    {
-      early_initrds = early_initrd_list (early_initrd);
-      if (early_initrds == NULL)
+      if (devicetree == NULL)
        {
-         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to create early initrd 
list"));
-         goto finish;
+         devicetree = expand_val (grub_env_get ("devicetree"));
+         add_dt_prefix = true;
        }
- if (initrds != NULL && initrds[0] != NULL)
-       {
-         initrd_prefix = grub_strrchr (initrds[0], '/');
-         initrd_prefix = grub_strndup (initrds[0], initrd_prefix - initrds[0] 
+ 1);
-       }
-      else
+      hotkey = blsuki_get_val (entry, "grub_hotkey", NULL);
+      users = expand_val (blsuki_get_val (entry, "grub_users", NULL));
+      classes = blsuki_make_list (entry, "grub_class", NULL);
+      args = blsuki_make_list (entry, "grub_arg", &argc);
+
+      argc += 1;
+      if (grub_mul (argc + 1, sizeof (char *), &size))
        {
-         initrd_prefix = grub_strrchr (clinux, '/');
-         initrd_prefix = grub_strndup (clinux, initrd_prefix - clinux + 1);
+         grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv 
list"));
+         goto finish;
        }
-
-      if (initrd_prefix == NULL)
+      argv = grub_malloc (size);
+      if (argv == NULL)
        {
-         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd prefix 
buffer"));
+         grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate argv list");
          goto finish;
        }
-    }
+      argv[0] = title ? title : clinux;
+      for (i = 1; i < argc; i++)
+       argv[i] = args[i-1];
+      argv[argc] = NULL;
- if (early_initrds != NULL || initrds != NULL)
-    {
-      initrd_size = sizeof ("initrd");
+      early_initrd = grub_env_get ("early_initrd");
- for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+      grub_dprintf ("blsuki", "adding menu entry for \"%s\" with id \"%s\"\n",
+                   title, id);
+      if (early_initrd != NULL)
        {
-         if (grub_add (initrd_size, sizeof (" " GRUB_BOOT_DEVICE), 
&initrd_size) ||
-             grub_add (initrd_size, grub_strlen (initrd_prefix), &initrd_size) 
||
-             grub_add (initrd_size, grub_strlen (early_initrds[i]), 
&initrd_size) ||
-             grub_add (initrd_size, 1, &initrd_size))
+         early_initrds = early_initrd_list (early_initrd);
+         if (early_initrds == NULL)
            {
-             grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
initrd buffer size");
+             grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to create early initrd 
list"));
              goto finish;
            }
-       }
- for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
-       {
-         if (grub_add (initrd_size, sizeof (" " GRUB_BOOT_DEVICE), 
&initrd_size) ||
-             grub_add (initrd_size, grub_strlen (initrds[i]), &initrd_size) ||
-             grub_add (initrd_size, 1, &initrd_size))
+         if (initrds != NULL && initrds[0] != NULL)
            {
-             grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
initrd buffer size");
+             initrd_prefix = grub_strrchr (initrds[0], '/');
+             initrd_prefix = grub_strndup (initrds[0], initrd_prefix - 
initrds[0] + 1);
+           }
+         else
+           {
+             initrd_prefix = grub_strrchr (clinux, '/');
+             initrd_prefix = grub_strndup (clinux, initrd_prefix - clinux + 1);
+           }
+
+         if (initrd_prefix == NULL)
+           {
+             grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd 
prefix buffer"));
              goto finish;
            }
        }
- if (grub_add (initrd_size, 1, &initrd_size))
+      if (early_initrds != NULL || initrds != NULL)
        {
-         grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd 
buffer size");
-         goto finish;
-       }
+         initrd_size = sizeof ("initrd");
- initrd = grub_malloc (initrd_size);
-      if (initrd == NULL)
-       {
-         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd 
buffer"));
-         goto finish;
-       }
+         for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+           {
+             if (grub_add (initrd_size, sizeof (" " GRUB_BOOT_DEVICE), 
&initrd_size) ||
+                 grub_add (initrd_size, grub_strlen (initrd_prefix), 
&initrd_size) ||
+                 grub_add (initrd_size, grub_strlen (early_initrds[i]), 
&initrd_size) ||
+                 grub_add (initrd_size, 1, &initrd_size))
+               {
+                 grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
initrd buffer size");
+                 goto finish;
+               }
+           }
- tmp = grub_stpcpy (initrd, "initrd");
-      for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
-       {
-         grub_dprintf ("blsuki", "adding early initrd %s\n", early_initrds[i]);
-         tmp = frob_boot_device (tmp);
-         tmp = grub_stpcpy (tmp, initrd_prefix);
-         tmp = grub_stpcpy (tmp, early_initrds[i]);
-         grub_free (early_initrds[i]);
-       }
+         for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
+           {
+             if (grub_add (initrd_size, sizeof (" " GRUB_BOOT_DEVICE), 
&initrd_size) ||
+                 grub_add (initrd_size, grub_strlen (initrds[i]), 
&initrd_size) ||
+                 grub_add (initrd_size, 1, &initrd_size))
+               {
+                 grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
initrd buffer size");
+                 goto finish;
+               }
+           }
- for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
-       {
-         grub_dprintf ("blsuki", "adding initrd %s\n", initrds[i]);
-         tmp = frob_boot_device (tmp);
-         tmp = grub_stpcpy (tmp, initrds[i]);
-       }
-      tmp = grub_stpcpy (tmp, "\n");
-    }
+         if (grub_add (initrd_size, 1, &initrd_size))
+           {
+             grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
initrd buffer size");
+             goto finish;
+           }
- if (devicetree != NULL)
-    {
-      if (add_dt_prefix == true)
-       {
-         prefix = grub_strrchr (clinux, '/');
-         prefix = grub_strndup (clinux, prefix - clinux + 1);
-         if (prefix == NULL)
+         initrd = grub_malloc (initrd_size);
+         if (initrd == NULL)
            {
-             grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate prefix 
buffer"));
+             grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate initrd 
buffer"));
              goto finish;
            }
-       }
- if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (devicetree), &dt_size) ||
-         grub_add (dt_size, 1, &dt_size))
-       {
-         grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device 
tree buffer size");
-         goto finish;
+         tmp = grub_stpcpy (initrd, "initrd");
+         for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++)
+           {
+             grub_dprintf ("blsuki", "adding early initrd %s\n", 
early_initrds[i]);
+             tmp = frob_boot_device (tmp);
+             tmp = grub_stpcpy (tmp, initrd_prefix);
+             tmp = grub_stpcpy (tmp, early_initrds[i]);
+             grub_free (early_initrds[i]);
+           }
+
+         for (i = 0; initrds != NULL && initrds[i] != NULL; i++)
+           {
+             grub_dprintf ("blsuki", "adding initrd %s\n", initrds[i]);
+             tmp = frob_boot_device (tmp);
+             tmp = grub_stpcpy (tmp, initrds[i]);
+           }
+         tmp = grub_stpcpy (tmp, "\n");
        }
- if (add_dt_prefix == true)
+      if (devicetree != NULL)
        {
-         if (grub_add (dt_size, grub_strlen (prefix), &dt_size))
+         if (add_dt_prefix == true)
+           {
+             prefix = grub_strrchr (clinux, '/');
+             prefix = grub_strndup (clinux, prefix - clinux + 1);
+             if (prefix == NULL)
+               {
+                 grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate prefix 
buffer"));
+                 goto finish;
+               }
+           }
+
+         if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen 
(devicetree), &dt_size) ||
+             grub_add (dt_size, 1, &dt_size))
            {
              grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
device tree buffer size");
              goto finish;
            }
+
+         if (add_dt_prefix == true)
+           {
+             if (grub_add (dt_size, grub_strlen (prefix), &dt_size))
+               {
+                 grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating 
device tree buffer size");
+                 goto finish;
+               }
+           }
+
+         dt = grub_malloc (dt_size);
+         if (dt == NULL)
+           {
+             grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate device tree 
buffer"));
+             goto finish;
+           }
+         tmp = dt;
+         tmp = grub_stpcpy (dt, "devicetree");
+         tmp = frob_boot_device (tmp);
+         if (add_dt_prefix == true)
+           tmp = grub_stpcpy (tmp, prefix);
+         tmp = grub_stpcpy (tmp, devicetree);
+         tmp = grub_stpcpy (tmp, "\n");
+
+         grub_free (prefix);
        }
- dt = grub_malloc (dt_size);
-      if (dt == NULL)
-        {
-         grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("failed to allocate device tree 
buffer"));
-         goto finish;
-        }
-      tmp = dt;
-      tmp = grub_stpcpy (dt, "devicetree");
-      tmp = frob_boot_device (tmp);
-      if (add_dt_prefix == true)
-        tmp = grub_stpcpy (tmp, prefix);
-      tmp = grub_stpcpy (tmp, devicetree);
-      tmp = grub_stpcpy (tmp, "\n");
-
-      grub_free (prefix);
+      grub_dprintf ("blsuki", "devicetree %s for id:\"%s\"\n", dt, id);
+
+      sdval = grub_env_get ("save_default");
+      savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0));
+      src = grub_xasprintf ("%sload_video\n"
+                           "set gfxpayload=keep\n"
+                           "insmod gzio\n"
+                           "linux %s%s%s%s\n"
+                           "%s%s",
+                           savedefault ? "savedefault\n" : "",
+                           separate_boot ? GRUB_BOOT_DEVICE : "",
+                           clinux, options ? " " : "", options ? options : "",
+                           initrd ? initrd : "", dt ? dt : "");
      }
+  else if (cmd_type == GRUB_UKI_CMD)
+    {
+      char *osrel = NULL;
+      char *line;
+      char *key = NULL;
+      char *value = NULL;
+      grub_off_t pos = 0;
+
+      options = blsuki_get_val (entry, ".cmdline", NULL);
+      if (options == NULL)
+       {
+         grub_dprintf ("blsuki", "Skipping file %s with no '.cmdline' key.\n", 
entry->filename);
+         goto finish;
+       }
+      osrel = blsuki_get_val (entry, ".osrel", NULL);
+      if (osrel == NULL)
+       {
+         grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", 
entry->filename);
+         goto finish;
+       }
+
+      line = osrel;
+      while ((line = uki_read_osrel (osrel, &pos, &key, &value)))
+       {
+         if (grub_strcmp ("PRETTY_NAME", key) == 0)
+           {
+             title = value;
+             break;
+           }
+       }
- grub_dprintf ("blsuki", "devicetree %s for id:\"%s\"\n", dt, id);
-
-  sdval = grub_env_get ("save_default");
-  savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0));
-  src = grub_xasprintf ("%sload_video\n"
-                       "set gfxpayload=keep\n"
-                       "insmod gzio\n"
-                       "linux %s%s%s%s\n"
-                       "%s%s",
-                       savedefault ? "savedefault\n" : "",
-                       separate_boot ? GRUB_BOOT_DEVICE : "",
-                       clinux, options ? " " : "", options ? options : "",
-                       initrd ? initrd : "", dt ? dt : "");
+      argc += 1;
+      if (grub_mul (argc + 1, sizeof (char *), &size))
+       {
+         grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv 
list"));
+         goto finish;
+       }
+      argv = grub_malloc (size);
+      if (argv == NULL)
+       {
+         grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate argv list");
+         goto finish;
+       }
+      argv[0] = title;
+      argv[argc] = NULL;
+
+      src = grub_xasprintf ("set root=(%s)\n"
+                           "insmod gzio\n"
+                           "insmod chain\n"
+                           "chainloader %s/%s%s%s\n",
+                           entry->devid, entry->dirname,
+                           entry->filename, options ? " " : "", options ? options : 
"");
+    }
grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, entry); finish:
-  grub_free (dt);
-  grub_free (initrd);
-  grub_free (initrd_prefix);
-  grub_free (early_initrds);
-  grub_free (devicetree);
-  grub_free (initrds);
-  grub_free (options);
-  grub_free (classes);
-  grub_free (args);
+  if (cmd_type == GRUB_BLS_CMD)
+    {
+      grub_free (dt);
+      grub_free (initrd);
+      grub_free (initrd_prefix);
+      grub_free (early_initrds);
+      grub_free (devicetree);
+      grub_free (initrds);
+      grub_free (classes);
+      grub_free (args);
+    }
    grub_free (argv);
    grub_free (src);
+  grub_free (options);
  }
struct find_entry_info
@@ -832,7 +1129,12 @@ find_entry (struct find_entry_info *info)
    int r = 0;
if (dir == NULL)
-    dir = GRUB_BLS_CONFIG_PATH;
+    {
+      if (cmd_type == GRUB_BLS_CMD)
+       dir = GRUB_BLS_CONFIG_PATH;
+      else if (cmd_type == GRUB_UKI_CMD)
+       dir = GRUB_UKI_CONFIG_PATH;
+    }
read_entry_info.file = NULL;
    read_entry_info.dirname = dir;
@@ -855,11 +1157,17 @@ find_entry (struct find_entry_info *info)
    /*
     * If we aren't able to find BLS entries in the directory given by 
info->dirname,
     * we can fallback to the default location "/boot/loader/entries/" and see 
if we
-   * can find the files there.
+   * can find the files there. If we can't find UKI entries, fallback to
+   * "/boot/efi/EFI/Linux".
     */
    if (r != 0 && info->dirname == NULL && fallback == 0)
      {
-      read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
+      if (cmd_type == GRUB_BLS_CMD)
+       read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH;
+      else if (cmd_type == GRUB_UKI_CMD)
+       {
+         read_entry_info.dirname = GRUB_UKI_CONFIG_PATH;
+       }
        grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
                    dir, read_entry_info.dirname);
        fallback = 1;
@@ -871,11 +1179,13 @@ static grub_err_t
  blsuki_load_entries (char *path)
  {
    grub_size_t len;
+  grub_size_t ext_len;
    grub_fs_t fs;
    grub_device_t dev;
    static grub_err_t r;
    const char *devid = NULL;
    char *dir = NULL;
+  const char *ext = NULL;
    struct find_entry_info info = {
        .dev = NULL,
        .fs = NULL,
@@ -888,8 +1198,14 @@ blsuki_load_entries (char *path)
if (path != NULL)
      {
+      if (cmd_type == GRUB_BLS_CMD)
+       ext = ".conf";
+      else if (cmd_type == GRUB_UKI_CMD)
+       ext = ".efi";
+
        len = grub_strlen (path);
-      if (grub_strcmp (path + len - 5, ".conf") == 0)
+      ext_len = grub_strlen (ext);
+      if (grub_strcmp (path + len - ext_len, ext) == 0)
        {
          rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
          if (rei.file == NULL)
@@ -918,11 +1234,22 @@ blsuki_load_entries (char *path)
if (devid == NULL)
      {
+      if (cmd_type == GRUB_BLS_CMD)
+       {
  #ifdef GRUB_MACHINE_EMU
-      devid = "host";
+         devid = "host";
  #else
-      devid = grub_env_get ("root");
+         devid = grub_env_get ("root");
  #endif
+       }
+      else if (cmd_type == GRUB_UKI_CMD)
+       {
+#ifdef GRUB_MACHINE_EFI
+         grub_efi_loaded_image_t *image;
+         image = grub_efi_get_loaded_image (grub_efi_image_handle);
+         devid = grub_efidisk_get_device_name (image->device_handle);
+#endif
+       }
        if (devid == NULL)
        return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), 
"root");
      }
@@ -1013,8 +1340,7 @@ blsuki_create_entries (bool show_default, bool 
show_non_default, char *entry_id)
  }
static grub_err_t
-grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
-                char **args __attribute__ ((unused)))
+blsuki_cmd (grub_extcmd_context_t ctxt)
  {
    grub_err_t err;
    struct grub_arg_list *state = ctxt->state;
@@ -1023,6 +1349,7 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc 
__attribute__ ((unused)),
    bool show_default = false;
    bool show_non_default = false;
    bool all = true;
+  entries = NULL;
if (state[0].set)
      path = state[0].arg;
@@ -1048,23 +1375,52 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc 
__attribute__ ((unused)),
      }
err = blsuki_load_entries (path);
+
    if (err != GRUB_ERR_NONE)
      return err;
return blsuki_create_entries (show_default, show_non_default, entry_id);
  }
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+                char **args __attribute__ ((unused)))
+{
+  cmd_type = GRUB_BLS_CMD;
+  return blsuki_cmd (ctxt);
+}
+
  static grub_extcmd_t bls_cmd;
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+                char **args __attribute__ ((unused)))
+{
+  cmd_type = GRUB_UKI_CMD;
+  return blsuki_cmd (ctxt);
+}
+
+static grub_extcmd_t uki_cmd;
+#endif
+
  GRUB_MOD_INIT(blsuki)
  {
    bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
                                  N_("[-p|--path] DIR [-d|--show-default] 
[-n|--show-non-default] [-e|--entry] FILE"),
                                  N_("Import Boot Loader Specification 
snippets."),
-                                 opt);
+                                 bls_opt);
+#ifdef GRUB_MACHINE_EFI
+  uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0,
+                                 N_("[-p|--path] DIR [-d|--show-default] 
[-n|--show-non-default] [-e|--entry] FILE"),
+                                 N_("Import Unified Kernel Images"), uki_opt);
+#endif
  }
GRUB_MOD_FINI(blsuki)
  {
    grub_unregister_extcmd (bls_cmd);
+#ifdef GRUB_MACHINE_EFI
+  grub_unregister_extcmd (uki_cmd);
+#endif
  }
diff --git a/include/grub/menu.h b/include/grub/menu.h
index c25a0d16d..907373625 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -28,6 +28,8 @@ struct grub_blsuki_entry
    grub_size_t keyvals_size;
    int nkeyvals;
    char *filename;
+  char *dirname;
+  char *devid;
    int visible;
  };
  typedef struct grub_blsuki_entry grub_blsuki_entry_t;


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to