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; + + 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) + { + grub_free (dos); + grub_free (pe); + grub_free (section); + grub_free (val); + 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; -- 2.27.0 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel