On Sun, Jul 27, 2025 at 01:54:33AM +0000, Alec Brown wrote: > From: Peter Jones <pjo...@redhat.com> > > The BootLoaderSpec (BLS) defines a scheme where different bootloaders can > share a format for boot items and a configuration directory that accepts > these common configurations as drop-in files. > > The BLS Specification: > https://uapi-group.org/specifications/specs/boot_loader_specification/ > > Signed-off-by: Peter Jones <pjo...@redhat.com> > Signed-off-by: Javier Martinez Canillas <javi...@redhat.com> > Signed-off-by: Will Thompson <w...@endlessm.com> > Signed-off-by: Alec Brown <alec.r.br...@oracle.com> > --- > bootstrap.conf | 1 + > docs/grub.texi | 67 +++ > grub-core/Makefile.core.def | 12 + > grub-core/commands/blsuki.c | 1032 ++++++++++++++++++++++++++++++++ > grub-core/commands/legacycfg.c | 4 +- > grub-core/commands/menuentry.c | 8 +- > grub-core/normal/main.c | 6 + > include/grub/menu.h | 15 + > include/grub/normal.h | 2 +- > 9 files changed, 1141 insertions(+), 6 deletions(-) > create mode 100644 grub-core/commands/blsuki.c
[...] > diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c > new file mode 100644 > index 000000000..c275dbb92 > --- /dev/null > +++ b/grub-core/commands/blsuki.c [...] > +static int > +blsuki_read_entry (const char *filename, > + const struct grub_dirhook_info *dirhook_info __attribute__ > ((__unused__)), > + void *data) > +{ > + grub_size_t path_len = 0, filename_len; > + grub_err_t err; > + char *p = NULL; > + grub_file_t f = NULL; > + grub_blsuki_entry_t *entry; > + struct read_entry_info *info = (struct read_entry_info *) data; > + > + grub_dprintf ("blsuki", "filename: \"%s\"\n", filename); > + > + filename_len = grub_strlen (filename); > + > + if (info->file != NULL) > + f = info->file; > + else > + { > + if (filename_len < BLS_EXT_LEN || > + grub_strcmp (filename + filename_len - BLS_EXT_LEN, ".conf") != 0) > + return 0; > + > + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); > + > + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); > + grub_free (p); > + if (f == NULL) > + goto finish; > + } > + > + entry = grub_zalloc (sizeof (*entry)); > + if (entry == NULL) > + goto finish; > + > + /* > + * If a file is opened before this function, the filename may have a path. > + * Since the filename is used for the ID of the GRUB menu entry, we can > + * remove the path. > + */ > + if (info->file != NULL) > + { > + char *slash; > + > + slash = grub_strrchr (filename, '/'); > + if (slash != NULL) > + { > + while (*slash == '/') > + slash++; I think this while is redundant. > + path_len = slash - filename; > + } > + } > + filename_len -= path_len; > + > + entry->filename = grub_strndup (filename + path_len, filename_len); > + if (entry->filename == NULL) > + { > + grub_free (entry); > + goto finish; > + } > + > + err = bls_parse_keyvals (f, entry); > + > + if (err == GRUB_ERR_NONE) > + blsuki_add_entry (entry); > + else > + grub_free (entry); > + > + finish: > + if (f != NULL) > + grub_file_close (f); > + > + return 0; > +} [...] > +static char * > +bls_get_devicetree (grub_blsuki_entry_t *entry) > +{ > + char *dt_path; > + char *dt_cmd = NULL; > + char *tmp; > + grub_size_t size; > + > + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); > + if (dt_path != NULL) > + { > + if (grub_add (sizeof ("devicetree "), grub_strlen (dt_path), &size) || > + grub_add (size, 1, &size)) > + { > + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating > device tree buffer size"); > + return NULL; > + } > + > + dt_cmd = grub_malloc (size); > + if (dt_cmd == NULL) > + return NULL; > + > + tmp = dt_cmd; > + tmp = grub_stpcpy (dt_cmd, "devicetree"); > + tmp = grub_stpcpy (tmp, " "); Hmmm... Why not tmp = grub_stpcpy (dt_cmd, "devicetree ")? Note space after "devicetree"... > + tmp = grub_stpcpy (tmp, dt_path); > + tmp = grub_stpcpy (tmp, "\n"); > + } > + > + return dt_cmd; > +} > + > +/* > + * This function puts together all of the commands generated from the > contents > + * of the BLS config file and creates a new entry in the GRUB boot menu. > + */ > +static void > +bls_create_entry (grub_blsuki_entry_t *entry) > +{ > + int argc = 0; > + const char **argv = NULL; > + char *title = NULL; > + char *linux_path = NULL; > + char *linux_cmd = NULL; > + char *initrd_cmd = NULL; > + char *dt_cmd = NULL; > + char *id = entry->filename; > + grub_size_t id_len; > + char *hotkey = NULL; > + char *users = NULL; > + char **classes = NULL; > + char **args = NULL; > + char *src = NULL; > + const char *sdval = NULL; > + int i; > + grub_size_t size; > + bool savedefault; > + > + linux_path = blsuki_get_val (entry, "linux", NULL); > + if (linux_path == NULL) > + { > + grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", > entry->filename); > + goto finish; > + } > + > + id_len = grub_strlen (id); > + if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, > ".conf") == 0) > + id[id_len - BLS_EXT_LEN] = '\0'; > + > + title = blsuki_get_val (entry, "title", NULL); > + hotkey = blsuki_get_val (entry, "grub_hotkey", NULL); > + users = blsuki_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++; > + 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) > + goto finish; > + > + argv[0] = (title != NULL) ? title : linux_path; > + for (i = 1; i < argc; i++) > + argv[i] = args[i-1]; > + argv[argc] = NULL; > + > + linux_cmd = bls_get_linux (entry); > + if (linux_cmd == NULL) > + goto finish; > + > + initrd_cmd = bls_get_initrd (entry); > + if (grub_errno != GRUB_ERR_NONE) > + goto finish; > + > + dt_cmd = bls_get_devicetree (entry); > + if (grub_errno != GRUB_ERR_NONE) > + goto finish; > + > + sdval = grub_env_get ("save_default"); This variable does not seem documented... And it seams BLS/UKI specific. So, probably blsuki_save_default... > + savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0)); s/savedefault/blsuki_save_default/ grub_env_get_bool() and you are done... > + src = grub_xasprintf ("%s%s%s%s", > + savedefault ? "savedefault\n" : "", > + linux_cmd, initrd_cmd ? initrd_cmd : "", > + dt_cmd ? dt_cmd : ""); > + > + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, > src, 0, entry); > + > + finish: > + grub_free (linux_cmd); > + grub_free (dt_cmd); > + grub_free (initrd_cmd); > + grub_free (classes); > + grub_free (args); > + grub_free (argv); > + grub_free (src); > +} > + > +/* > + * This function fills a find_entry_info struct passed in by the info > parameter. > + * If the dirname or devid parameters are set to NULL, the dirname and devid > + * fields in the info parameter will be set to default values. If info > already > + * has a value in the dev fields, we can compare it to the value passed in by > + * the devid parameter or the default devid to see if we need to open a new > + * device. > + */ > +static grub_err_t > +blsuki_set_find_entry_info (struct find_entry_info *info, const char > *dirname, const char *devid) > +{ > + grub_device_t dev; > + grub_fs_t fs; > + > + if (info == NULL) > + return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set"); > + > + if (devid == NULL) > + { > + devid = grub_env_get ("root"); > + if (devid == NULL) > + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't > set"), "root"); Huh... "variable `root' isn't set"... > + } > + > + /* Check that we aren't closing and opening the same device. */ > + if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0) > + { > + grub_device_close (info->dev); > + info->dev = NULL; > + } > + /* If we are using the same device, then we can skip this step and only > set the directory. */ > + if (info->dev == NULL) > + { > + grub_dprintf ("blsuki", "opening %s\n", devid); > + dev = grub_device_open (devid); > + if (dev == NULL) > + return grub_errno; > + > + grub_dprintf ("blsuki", "probing fs\n"); > + fs = grub_fs_probe (dev); > + if (fs == NULL) > + { > + grub_device_close (dev); > + return grub_errno; > + } > + > + info->devid = devid; > + info->dev = dev; > + info->fs = fs; > + } > + > + info->dirname = dirname; > + > + return GRUB_ERR_NONE; > +} [...] > +static bool > +blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, > int idx) > +{ > + const char *title; > + const char *def_entry_end; > + int def_idx; long def_idx; > + > + if (def_entry == NULL || *def_entry == '\0') > + return false; > + > + if (grub_strcmp (def_entry, entry->filename) == 0) > + return true; > + > + title = blsuki_get_val (entry, "title", NULL); > + > + if (title != NULL && grub_strcmp (def_entry, title) == 0) > + return true; > + > + def_idx = (int) grub_strtol (def_entry, &def_entry_end, 0); Drop cast from here... > + if (*def_entry_end != '\0') *def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX > + return false; > + > + if (def_idx == idx) (int) def_idx == idx > + return true; > + > + return false; > +} [...] > diff --git a/include/grub/menu.h b/include/grub/menu.h > index ee2b5e910..c25a0d16d 100644 > --- a/include/grub/menu.h > +++ b/include/grub/menu.h > @@ -20,6 +20,18 @@ > #ifndef GRUB_MENU_HEADER > #define GRUB_MENU_HEADER 1 > > +struct grub_blsuki_entry > +{ > + struct grub_blsuki_entry *next; > + struct grub_blsuki_entry **prev; > + struct keyval **keyvals; > + grub_size_t keyvals_size; > + int nkeyvals; > + char *filename; > + int visible; Why is not it bool? Daniel _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel