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.
I chatted with Alec yesterday and we went over this patch. There are a
few things that need attention here, see below.
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
This function and the bls_read_entry() both just return 1 or 0 to
indicate failure or success. These functions could just be changed to
return a bool because of this.
+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++)
+ {
This loop is loading all the sections of the PE into the entry's kv pair
map. This includes large buffers for things like the .linux section.
Most of these sections are ignored later so this function should just
load the sections it cares about into the map - in this case just .osrel
and .cmdline.
This function could also indicate that it found the .linux section
(which is required) but not add it to the entry's list.
+ char *val;
+ char *key;
+
+ 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)
+ {
+ 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)
+ {
There should be a way to make a finish: block and collent all these
grub_free() calls there.
+ grub_free (dos);
+ grub_free (pe);
+ grub_free (section);
+ grub_free (val);
+ return 1;
+ }
+ grub_dprintf ("blsuki", "section name: %s\n", key);
Two things here:
1. The function blsuki_add_keyval() returns grub_err_t values but the
function is declared as returning an int. This should be changed.
2. The return value here needs to be checked.
+ 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
Missing ^ space here
+ 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)
+ {
Move this before the else if and then you can drop the extra brackets
and make it cleaner.
+#ifdef GRUB_MACHINE_EFI
+ rc = uki_read_entry (f, entry);
+#endif
+ }
if (rc == 0)
blsuki_add_entry (entry);
The UKI support got added directly to this function creating large
indented sections for BLS and UKI. Could the UKI support be moved to its
own function like so you have: bls_create_entry() and
uki_create_entry(). If that is reasonable to do (and it would make the
patch easier to read)...
@@ -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);
Make sure all of these are being freed at the end. I am not sure osrel
is getting freed.
+ 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