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.
The Unified Kernel Image Specification: https://uapi-group.org/specifications/specs/unified_kernel_image/ Signed-off-by: Alec Brown <alec.r.br...@oracle.com> --- docs/grub.texi | 33 +++ grub-core/commands/blsuki.c | 463 +++++++++++++++++++++++++++++++++--- include/grub/menu.h | 2 + 3 files changed, 463 insertions(+), 35 deletions(-) diff --git a/docs/grub.texi b/docs/grub.texi index adab93668..9a63129c7 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -6491,6 +6491,7 @@ you forget a command, you can run the command @command{help} * tpm2_key_protector_clear:: Clear the TPM2 key protector * 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 @@ -8184,6 +8185,38 @@ Unset the environment variable @var{envvar}. @end deffn +@node uki +@subsection uki + +@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] +Load Unified Kernel Image (UKI) entries into the GRUB menu. Boot entries +generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the +GRUB menu. Also, entries generated from @command{uki} only generate in memory and don't +update @file{grub.cfg}. + +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. If no UKI entries are found, the +@option{--enable-fallback} option can be used to check for entries in the default +directory. + +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. + +References: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification} +@end deffn + @ignore @node vbeinfo @subsection vbeinfo diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c index bf2edc5ac..3f067281d 100644 --- a/grub-core/commands/blsuki.c +++ b/grub-core/commands/blsuki.c @@ -32,6 +32,12 @@ #include <grub/vercmp.h> #include <grub/lib/envblk.h> +#ifdef GRUB_MACHINE_EFI +#include <grub/efi/efi.h> +#include <grub/efi/disk.h> +#include <grub/efi/pe32.h> +#endif + #ifdef GRUB_MACHINE_EMU #include <grub/emu/misc.h> #define GRUB_BOOT_DEVICE "/boot" @@ -42,6 +48,13 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_BLS_CONFIG_PATH "/loader/entries/" +#define GRUB_UKI_CONFIG_PATH "/EFI/Linux" + +enum + { + BLSUKI_BLS_CMD, + BLSUKI_UKI_CMD, + }; static const struct grub_arg_option bls_opt[] = { @@ -53,6 +66,18 @@ static const struct grub_arg_option bls_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}, + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE}, + {"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; @@ -162,7 +187,7 @@ blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) * Find the value of the key named by keyname. If there are allowed to be * more than one, pass a pointer to an int set to -1 the first time, and pass * the same pointer through each time after, and it'll return them in sorted - * order as defined in the BLS fragment file. + * order. */ static char * blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) @@ -304,27 +329,243 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) return err; } +/* + * This function searches for the .cmdline, .osrel, and .linux sections of a + * UKI. We only need to store the data for the .cmdline and .osrel sections, + * but we also need to verify that the .linux section exists. + */ +#ifdef GRUB_MACHINE_EFI +static grub_err_t +uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) +{ + struct grub_msdos_image_header *dos = NULL; + struct grub_pe_image_header *pe = NULL; + grub_off_t section_offset = 0; + struct grub_pe32_section_table *section = NULL; + struct grub_pe32_coff_header *coff_header = NULL; + char *val = NULL; + char *key = NULL; + const char *target[] = {".cmdline", ".osrel", ".linux", NULL}; + bool has_linux = false; + grub_err_t err = GRUB_ERR_NONE; + + dos = grub_zalloc (sizeof (*dos)); + if (dos == NULL) + return grub_errno; + if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header"); + goto finish; + } + if (dos->msdos_magic != GRUB_PE32_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("plain image kernel not supported")); + goto finish; + } + + grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset); + pe = grub_zalloc (sizeof (*pe)); + if (pe == NULL) + { + err = grub_errno; + goto finish; + } + if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 || + grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); + goto finish; + } + if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported"); + goto finish; + } + + coff_header = &(pe->coff_header); + section_offset = dos->pe_image_header_offset + sizeof (*pe); + + for (int i = 0; i < coff_header->num_sections; i++) + { + key = NULL; + val = NULL; + section = grub_zalloc (sizeof (*section)); + if (section == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section_offset) == (grub_off_t) -1 || + grub_file_read (f, section, sizeof (*section)) != sizeof (*section)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header"); + goto finish; + } + + key = grub_strndup (section->name, 8); + if (key == NULL) + { + err = grub_errno; + goto finish; + } + + for (int j = 0; target[j] != NULL; j++) + { + if (grub_strcmp (key, target[j]) == 0) + { + /* + * We don't need to read the contents of the .linux PE section, but we + * should verify that the section exists. + */ + if (grub_strcmp (key, ".linux") == 0) + { + has_linux = true; + break; + } + + val = grub_zalloc (section->raw_data_size); + if (val == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 || + grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section"); + goto finish; + } + + err = blsuki_add_keyval (entry, key, val); + if (err != GRUB_ERR_NONE) + goto finish; + + break; + } + } + + section_offset += sizeof (*section); + grub_free (section); + grub_free (val); + grub_free (key); + section = NULL; + val = NULL; + key = NULL; + } + + if (has_linux == false) + err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section"); + + finish: + grub_free (dos); + grub_free (pe); + grub_free (section); + grub_free (val); + grub_free (key); + return err; +} +#endif + +/* + * This function obtains the keyval pairs when the .osrel data is input into + * the content parameter and returns the full line that it obtained the keyval + * pair from. + */ +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; + + for (;;) + { + line = content + *pos; + if (*line == '\0') + return NULL; + + linelen = 0; + while (line[linelen] != '\0' && line[linelen] != '\n' && line[linelen] != '\r') + linelen++; + + /* Move pos to the next line */ + *pos += linelen; + if (content[*pos] != '\0') + (*pos)++; + + /* Skip empty line */ + if (linelen == 0) + continue; + + line[linelen] = '\0'; + + /* Remove leading white space */ + while (linelen > 0 && (*line == ' ' || *line == '\t')) + { + line++; + linelen--; + } + + /* Remove trailing whitespace */ + while (linelen > 0 && (line[linelen - 1] == ' ' || line[linelen - 1] == '\t')) + linelen--; + line[linelen] = '\0'; + + if (*line == '#') + continue; + + /* Split key/value */ + value = line; + while (*value != '\0' && *value != '=') + value++; + if (*value == '\0') + continue; + *value = '\0'; + value++; + while (*value != '\0' && *value == '=') + value++; + + /* Remove quotes from value */ + if (*value == '\"' && line[linelen - 1] == '\"') + { + value++; + line[linelen - 1] = '\0'; + } + + *key_ret = line; + *val_ret = value; + break; + } + + return line; +} + struct read_entry_info { const char *devid; const char *dirname; + int cmd_type; grub_file_t file; }; /* * If a file hasn't already been opened, this function opens a BLS config file - * and initializes entry data before parsing keyvals and adding the entry to - * the list of BLS entries. + * or UKI and initializes entry data before parsing keyvals and adding the entry + * to the list of BLS or UKI entries. */ static int blsuki_read_entry (const char *filename, const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), void *data) { - grub_size_t prefix_len = 0, filename_len, ext_len = 5; - grub_err_t err; + grub_size_t prefix_len = 0, filename_len, ext_len; + grub_err_t err = GRUB_ERR_NONE; 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; @@ -332,16 +573,27 @@ blsuki_read_entry (const char *filename, filename_len = grub_strlen (filename); + if (info->cmd_type == BLSUKI_BLS_CMD) + { + ext = ".conf"; + file_type = GRUB_FILE_TYPE_CONFIG; + } + else if (info->cmd_type == BLSUKI_UKI_CMD) + { + ext = ".efi"; + file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE; + } + ext_len = grub_strlen (ext); + if (info->file != NULL) f = info->file; else { - if (filename_len < ext_len || grub_strcmp (filename + filename_len - ext_len, ".conf") != 0) + if (filename_len < ext_len || grub_strcmp (filename + filename_len - 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); + f = grub_file_open (p, file_type); grub_free (p); if (f == NULL) goto finish; @@ -381,7 +633,26 @@ blsuki_read_entry (const char *filename, goto finish; } - err = bls_parse_keyvals (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 (info->cmd_type == BLSUKI_BLS_CMD) + err = bls_parse_keyvals (f, entry); +#ifdef GRUB_MACHINE_EFI + else if (info->cmd_type == BLSUKI_UKI_CMD) + err = uki_parse_keyvals (f, entry); +#endif if (err == GRUB_ERR_NONE) blsuki_add_entry (entry); @@ -397,7 +668,7 @@ blsuki_read_entry (const char *filename, /* * This function returns a list of values that had the same key in the BLS - * config file. The number of entries in this list is returned by the len + * config file or UKI. The number of entries in this list is returned by the len * parameter. */ static char ** @@ -942,6 +1213,65 @@ bls_create_entry (grub_blsuki_entry_t *entry) grub_free (src); } +/* + * This function puts together the section data recieved from the UKI and + * generates a new entry un the GRUB boot menu. + */ +static void +uki_create_entry (grub_blsuki_entry_t *entry) +{ + const char **argv = NULL; + char *id = entry->filename; + char *title = NULL; + char *options = NULL; + char *osrel = NULL; + char *line; + char *key = NULL; + char *value = NULL; + char *src = NULL; + grub_off_t pos = 0; + + /* + * Although .osrel is listed as optional in the UKI specification, the .osrel + * section is needed to generate the GRUB menu entry title. + */ + 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; + } + } + + options = blsuki_get_val (entry, ".cmdline", NULL); + + argv = grub_zalloc (2 * sizeof (char *)); + if (argv == NULL) + goto finish; + argv[0] = title; + + src = grub_xasprintf ("chainloader (%s)%s/%s%s%s\n", + entry->devid, entry->dirname, + entry->filename, options ? " " : "", options ? options : ""); + + grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, entry); + + finish: + grub_free (argv); + grub_free (src); + grub_free (options); + grub_free (osrel); +} + struct find_entry_info { const char *dirname; @@ -959,7 +1289,7 @@ struct find_entry_info * device. */ static grub_err_t -blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid) +blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, int cmd_type) { grub_device_t dev; grub_fs_t fs; @@ -969,10 +1299,21 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c if (devid == NULL) { + if (cmd_type == BLSUKI_BLS_CMD) + { #ifdef GRUB_MACHINE_EMU - devid = "host"; + devid = "host"; #else - devid = grub_env_get ("root"); + devid = grub_env_get ("root"); +#endif + } +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + { + 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"); @@ -1011,15 +1352,16 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c } /* - * This function searches for BLS config files based on the data in the info - * parameter. If the fallback option is enabled, the default location will be - * checked for BLS config files if the first attempt fails. + * This function searches for BLS config files and UKIs based on the data in the + * info parameter. If the fallback option is enabled, the default location will + * be checked for BLS config files or UKIs if the first attempt fails. */ static grub_err_t -blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, int cmd_type) { struct read_entry_info read_entry_info; char *default_dir = NULL; + const char *cmd_dir = NULL; char *tmp; grub_size_t default_size; grub_fs_t dir_fs = NULL; @@ -1036,6 +1378,7 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) dir_dev = info->dev; dir_fs = info->fs; read_entry_info.devid = info->devid; + read_entry_info.cmd_type = cmd_type; r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, &read_entry_info); @@ -1048,19 +1391,25 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) /* * 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 + * "/EFI/Linux" on the EFI system partition. */ if (entries == NULL && fallback == false && enable_fallback == true) { - default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH); + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; + + default_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); default_dir = grub_malloc (default_size); if (default_dir == NULL) return grub_errno; tmp = blsuki_update_boot_device (default_dir); - tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + tmp = grub_stpcpy (tmp, cmd_dir); - blsuki_set_find_entry_info (info, default_dir, NULL); + blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type); grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", read_entry_info.dirname, info->dirname); fallback = true; @@ -1075,15 +1424,18 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback) } static grub_err_t -blsuki_load_entries (char *path, bool enable_fallback) +blsuki_load_entries (char *path, bool enable_fallback, int cmd_type) { grub_size_t len; + grub_size_t ext_len; static grub_err_t r; const char *devid = NULL; char *dir = NULL; char *default_dir = NULL; char *tmp; + const char *cmd_dir = NULL; grub_size_t dir_size; + const char *ext = NULL; struct find_entry_info info = { .dev = NULL, .fs = NULL, @@ -1092,12 +1444,19 @@ blsuki_load_entries (char *path, bool enable_fallback) struct read_entry_info rei = { .devid = NULL, .dirname = NULL, + .cmd_type = cmd_type, }; if (path != NULL) { + if (cmd_type == BLSUKI_BLS_CMD) + ext = ".conf"; + else if (cmd_type == BLSUKI_UKI_CMD) + ext = ".efi"; + len = grub_strlen (path); - if (len >= 5 && grub_strcmp (path + len - 5, ".conf") == 0) + ext_len = grub_strlen (ext); + if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0) { rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); if (rei.file == NULL) @@ -1126,19 +1485,24 @@ blsuki_load_entries (char *path, bool enable_fallback) if (dir == NULL) { - dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (GRUB_BLS_CONFIG_PATH); + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; + + dir_size = grub_strlen (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); default_dir = grub_malloc (dir_size); if (default_dir == NULL) return grub_errno; tmp = blsuki_update_boot_device (default_dir); - tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH); + tmp = grub_stpcpy (tmp, cmd_dir); dir = default_dir; } - r = blsuki_set_find_entry_info (&info, dir, devid); + r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type); if (r == GRUB_ERR_NONE) - r = blsuki_find_entry (&info, enable_fallback); + r = blsuki_find_entry (&info, enable_fallback, cmd_type); if (info.dev != NULL) grub_device_close (info.dev); @@ -1181,11 +1545,11 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int } /* - * This function creates a GRUB boot menu entry for each BLS entry in the - * entries list. + * This function creates a GRUB boot menu entry for each BLS or UKI entry in + * the entries list. */ static grub_err_t -blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) +blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, int cmd_type) { const char *def_entry = NULL; grub_blsuki_entry_t *entry = NULL; @@ -1204,7 +1568,10 @@ blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id) (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) { - bls_create_entry (entry); + if (cmd_type == BLSUKI_BLS_CMD) + bls_create_entry (entry); + else if (cmd_type == BLSUKI_UKI_CMD) + uki_create_entry (entry); entry->visible = 1; } idx++; @@ -1214,8 +1581,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, int cmd_type) { grub_err_t err; struct grub_arg_list *state = ctxt->state; @@ -1225,6 +1591,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; @@ -1251,24 +1618,50 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), show_non_default = true; } - err = blsuki_load_entries (path, enable_fallback); + err = blsuki_load_entries (path, enable_fallback, cmd_type); if (err != GRUB_ERR_NONE) return err; - return blsuki_create_entries (show_default, show_non_default, entry_id); + return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type); +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + return blsuki_cmd (ctxt, BLSUKI_BLS_CMD); } 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))) +{ + return blsuki_cmd (ctxt, BLSUKI_UKI_CMD); +} + +static grub_extcmd_t uki_cmd; +#endif + GRUB_MOD_INIT(blsuki) { bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0, N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), N_("Import Boot Loader Specification snippets."), bls_opt); +#ifdef GRUB_MACHINE_EFI + uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0, + N_("[-p|--path] DIR [-f|--enable-fallback] [-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