* [PATCH v5 0/5] Add commands to load BLS and UKI files
@ 2025-07-27 1:54 Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
` (4 more replies)
0 siblings, 5 replies; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
v5:
- Added grub_strtok() to the GRUB.
- Removed vercmp patch and instead utilize filevercmp from gnulib.
- Adjusted documentation to include GRUB specific features.
- Removed environmental variables such as grub_env_get ("default_kernelopts"),
grub_env_get ("early_initrd"), etc.
- Added artificial limits.
- Replaced grub_strlen() with sizeof() for constant strings.
- Fixed bug with duplicate BLS entries if the blscfg command was called more
than once.
- Fixed various nits.
This patch set is introducing BootLoaderSpec support to upstream GRUB from
Fedora GRUB. I've also added a uki command to load Unified Kernel Images since
it shares similar code to loading BLS config files.
Alec Brown
Alec Brown (3):
kern/misc: Implement grub_strtok()
util/misc.c: Change offset type for grub_util_write_image_at()
blsuki: Add uki command to load Unified Kernel Image entries
Peter Jones (1):
blsuki: Add blscfg command to parse Boot Loader Specification snippets
Robbie Harwood (1):
blsuki: Check for mounted /boot in emu
bootstrap.conf | 1 +
docs/grub.texi | 100 ++++++++
grub-core/Makefile.core.def | 15 ++
grub-core/commands/blsuki.c | 1484 ++++++++++++++++++++++++++++++++++++++++
grub-core/commands/legacycfg.c | 4 +-
grub-core/commands/menuentry.c | 8 +-
grub-core/kern/misc.c | 62 +++++
grub-core/normal/main.c | 6 +
grub-core/osdep/linux/getroot.c | 8 +
grub-core/osdep/unix/getroot.c | 4 +-
include/grub/emu/misc.h | 2 +-
include/grub/menu.h | 17 ++
include/grub/misc.h | 3 +
include/grub/normal.h | 2 +-
include/grub/util/misc.h | 2 +-
util/misc.c | 2 +-
16 files changed, 1710 insertions(+), 10 deletions(-)
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
@ 2025-07-27 1:54 ` Alec Brown via Grub-devel
2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
` (2 more replies)
2025-07-27 1:54 ` [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
` (3 subsequent siblings)
4 siblings, 3 replies; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
Add the functions grub_strtok() and grub_strtok_r() to help parse strings into
tokens separated by characters in the 'delim' parameter. These functions are
present in gnulib but calling them directly from the gnulib code is quite
challenging since the call "#include <string.h>" would include the header file
grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h,
where strtok() and strtok_r() are declared. Since this overlap is quite
problematic, the simpler solution was to implement the code in the GRUB based
on gnulib's implementation. For more information on these functions, visit the
Linux Programmer's Manual("man strtok").
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++
include/grub/misc.h | 3 +++
2 files changed, 65 insertions(+)
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index 2b7922393..258f91893 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -401,6 +401,68 @@ grub_strword (const char *haystack, const char *needle)
return 0;
}
+char *
+grub_strtok_r (char *s, const char *delim, char **save_ptr)
+{
+ char *token;
+ const char *c;
+ bool is_delim;
+
+ if (s == NULL)
+ s = *save_ptr;
+
+ /* Scan leading delimiters. */
+ while (*s != '\0')
+ {
+ is_delim = false;
+ for (c = delim; *c != '\0'; c++)
+ {
+ if (*s == *c)
+ {
+ is_delim = true;
+ break;
+ }
+ }
+ if (is_delim == true)
+ s++;
+ else
+ break;
+ }
+
+ if (*s == '\0')
+ {
+ *save_ptr = s;
+ return NULL;
+ }
+
+ /* Find the end of the token. */
+ token = s;
+ while (*s != '\0')
+ {
+ for (c = delim; *c != '\0'; c++)
+ {
+ if (*s == *c)
+ {
+ *s = '\0';
+ *save_ptr = s + 1;
+ return token;
+ }
+ }
+ s++;
+ }
+
+ *save_ptr = s;
+ return token;
+}
+
+char *
+grub_strtok (char *s, const char *delim)
+{
+ static char *last;
+
+ return grub_strtok_r (s, delim, &last);
+}
+
int
grub_isspace (int c)
{
diff --git a/include/grub/misc.h b/include/grub/misc.h
index e087e7b3e..9522d7305 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -126,6 +126,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
int EXPORT_FUNC(grub_strword) (const char *s, const char *w);
+char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr);
+char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim);
+
/* Copied from gnulib.
Written by Bruno Haible <bruno@clisp.org>, 2005. */
static inline char *
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
@ 2025-07-27 1:54 ` Alec Brown via Grub-devel
2025-08-04 18:14 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at() Alec Brown via Grub-devel
` (2 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
From: Peter Jones <pjones@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 <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Will Thompson <wjt@endlessm.com>
Signed-off-by: Alec Brown <alec.r.brown@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 +
| 15 +
include/grub/normal.h | 2 +-
9 files changed, 1141 insertions(+), 6 deletions(-)
create mode 100644 grub-core/commands/blsuki.c
diff --git a/bootstrap.conf b/bootstrap.conf
index 3590aba99..8eff35b45 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -24,6 +24,7 @@ gnulib_modules="
argp
base64
error
+ filevercmp
fnmatch
getdelim
getline
diff --git a/docs/grub.texi b/docs/grub.texi
index 34b3484dc..1186f274f 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6419,6 +6419,7 @@ you forget a command, you can run the command @command{help}
* background_image:: Load background image for active terminal
* badram:: Filter out bad regions of RAM
* blocklist:: Print a block list
+* blscfg:: Load Boot Loader Specification menu entries
* boot:: Start up your operating system
* cat:: Show the contents of a file
* clear:: Clear the screen
@@ -6607,6 +6608,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}.
@end deffn
+@node blscfg
+@subsection blscfg
+
+@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file]
+Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries
+generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in
+the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and
+don't update @file{grub.cfg}.
+
+By default, the BLS entries are stored in the @file{/loader/entries} directory in the
+boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be
+used to check a different directory instead of the default location. If no BLS
+entries are found while using the @option{--path} option, the @option{--enable-fallback} option
+can be used to check for entries in the default location.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the BLS entries.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the BLS entries.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the BLS entries.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which BLS 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.
+
+A BLS config file example:
+@example
+# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf
+title Fedora 19 (Rawhide)
+sort-key fedora
+machine-id 6a9857a393724b7a981ebb5b8495b9ea
+version 3.8.0-2.fc19.x86_64
+options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet
+architecture x64
+linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux
+initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd
+@end example
+
+For more information on BLS entry keys as well as other information on BLS,
+see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional
+BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}).
+
+The @code{grub_class} key may be used any number of times to group menu entries into
+classes. Menu themes may display different classes using different styles.
+
+The @code{grub_users} key grants specific users access to specific menu
+entries. @xref{Security}.
+
+The @code{grub_hotkey} key associates a hotkey with a menu entry.
+@var{key} may be a single letter, or one of the aliases @samp{backspace},
+@samp{tab}, or @samp{delete}.
+
+The @code{grub_args} key can be used for any other argument to be passed as positonal
+parameters when the list of commands generated from the BLS config file are
+executed.
+
+Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be
+used with BLS config files for the GRUB but might not be compatible with other
+bootloaders.
+@end deffn
+
+
@node boot
@subsection boot
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index b3f71196a..996a1c2ae 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -845,6 +845,18 @@ module = {
common = commands/blocklist.c;
};
+module = {
+ name = blsuki;
+ common = commands/blsuki.c;
+ common = lib/gnulib/filevercmp.c;
+ enable = powerpc_ieee1275;
+ enable = efi;
+ enable = i386_pc;
+ enable = emu;
+ cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+ cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+};
+
module = {
name = boot;
common = commands/boot.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
@@ -0,0 +1,1032 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2025 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/list.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/extcmd.h>
+#include <grub/i18n.h>
+#include <grub/fs.h>
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/safemath.h>
+#include <grub/lib/envblk.h>
+#include <filevercmp.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+
+#define BLS_EXT_LEN (sizeof (".conf") - 1)
+
+#define BLSUKI_KEYVALS_MAX 10000
+
+static const struct grub_arg_option bls_opt[] =
+ {
+ {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME},
+ {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE},
+ {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE},
+ {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+struct keyval
+{
+ const char *key;
+ char *val;
+};
+
+struct read_entry_info
+{
+ const char *devid;
+ const char *dirname;
+ grub_file_t file;
+};
+
+struct find_entry_info
+{
+ const char *dirname;
+ const char *devid;
+ grub_device_t dev;
+ grub_fs_t fs;
+};
+
+static grub_blsuki_entry_t *entries;
+
+#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+
+/*
+ * This function will add a new keyval pair to a list of keyvals stored in the
+ * entry parameter.
+ */
+static grub_err_t
+blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val)
+{
+ char *k, *v;
+ struct keyval **kvs, *kv;
+ grub_size_t size;
+ int new_n = entry->nkeyvals + 1;
+
+ if (new_n > BLSUKI_KEYVALS_MAX)
+ return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs");
+
+ if (entry->keyvals_size == 0)
+ {
+ size = sizeof (struct keyval *);
+ kvs = grub_malloc (size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+ else if (entry->keyvals_size < new_n * sizeof (struct keyval *))
+ {
+ size = entry->keyvals_size * 2;
+ kvs = grub_realloc (entry->keyvals, size);
+ if (kvs == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values");
+
+ entry->keyvals = kvs;
+ entry->keyvals_size = size;
+ }
+
+ kv = grub_malloc (sizeof (struct keyval));
+ if (kv == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+
+ k = grub_strdup (key);
+ if (k == NULL)
+ {
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ v = grub_strdup (val);
+ if (v == NULL)
+ {
+ grub_free (k);
+ grub_free (kv);
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value");
+ }
+
+ kv->key = k;
+ kv->val = v;
+ entry->keyvals[entry->nkeyvals] = kv;
+ entry->nkeyvals = new_n;
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * Find the value of the key named by keyname. If there are allowed to be
+ * more than one, pass a pointer set to -1 to the last parameter 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.
+ */
+static char *
+blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
+{
+ int idx, start = (last != NULL) ? (*last + 1) : 0;
+ struct keyval *kv = NULL;
+ char *ret = NULL;
+
+ for (idx = start; idx < entry->nkeyvals; idx++)
+ {
+ kv = entry->keyvals[idx];
+
+ if (grub_strcmp (keyname, kv->key) == 0)
+ {
+ ret = kv->val;
+ break;
+ }
+ }
+
+ if (last != NULL)
+ {
+ if (idx == entry->nkeyvals)
+ *last = -1;
+ else
+ *last = idx;
+ }
+
+ return ret;
+}
+
+/*
+ * Add a new grub_blsuki_entry_t struct to the entries list and sort it's
+ * position on the list.
+ */
+static grub_err_t
+blsuki_add_entry (grub_blsuki_entry_t *entry)
+{
+ grub_blsuki_entry_t *e, *last = NULL;
+ int rc;
+
+ if (entries == NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ entries = entry;
+ return GRUB_ERR_NONE;
+ }
+
+ FOR_BLSUKI_ENTRIES (e)
+ {
+ rc = filevercmp (entry->filename, e->filename);
+ if (rc == 0)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename);
+
+ if (rc > 0)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry));
+ if (entry->next == entries)
+ {
+ entries = entry;
+ entry->prev = NULL;
+ }
+ else if (last != NULL)
+ last->next = entry;
+
+ return GRUB_ERR_NONE;
+ }
+ last = e;
+ }
+
+ if (last != NULL)
+ {
+ grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename);
+ last->next = entry;
+ entry->prev = &last;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This function parses each line of a BLS config file to obtain the key value
+ * pairs that will be used to setup the GRUB menu entries. The key value pair
+ * will be stored in a list in the entry parameter.
+ */
+static grub_err_t
+bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
+{
+ grub_err_t err = GRUB_ERR_NONE;
+
+ for (;;)
+ {
+ char *line, *key, *val;
+
+ line = grub_file_getline (f);
+ if (line == NULL)
+ break;
+
+ key = grub_strtok_r (line, " \t", &val);
+ if (key == NULL)
+ {
+ grub_free (line);
+ break;
+ }
+ if (*key == '#')
+ {
+ grub_free (line);
+ continue;
+ }
+
+ while (*val == ' ' || *val == '\t')
+ val++;
+
+ if (*val == '\0')
+ {
+ grub_free (line);
+ break;
+ }
+
+ err = blsuki_add_keyval (entry, key, val);
+ grub_free (line);
+ if (err != GRUB_ERR_NONE)
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * 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.
+ */
+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++;
+
+ 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;
+}
+
+/*
+ * 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
+ * parameter.
+ */
+static char **
+blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len)
+{
+ int last = -1;
+ char *val;
+ int nlist = 0;
+ char **list;
+
+ list = grub_zalloc (sizeof (char *));
+ if (list == NULL)
+ return NULL;
+
+ while (1)
+ {
+ char **new;
+
+ /*
+ * Since the same key might appear more than once, the 'last' variable
+ * starts at -1 and increments to indicate the last index in the list
+ * we obtained from blsuki_get_val().
+ */
+ val = blsuki_get_val (entry, key, &last);
+ if (val == NULL)
+ break;
+
+ new = grub_realloc (list, (nlist + 2) * sizeof (char *));
+ if (new == NULL)
+ break;
+
+ list = new;
+ list[nlist++] = val;
+ list[nlist] = NULL;
+ }
+
+ if (nlist == 0)
+ {
+ grub_free (list);
+ return NULL;
+ }
+
+ if (len != NULL)
+ *len = nlist;
+
+ return list;
+}
+
+/*
+ * This function appends a field to the end of a buffer. If the field given is
+ * an enviornmental variable, it gets the value stored for that variable and
+ * appends that to the buffer instead.
+ */
+static char *
+blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end)
+{
+ char *tmp;
+ const char *field;
+ grub_size_t size = 0;
+
+ tmp = grub_strndup (start, end - start + 1);
+ if (tmp == NULL)
+ return NULL;
+
+ field = tmp;
+
+ if (is_env_var == true)
+ {
+ field = grub_env_get (tmp);
+ if (field == NULL)
+ return buffer;
+ }
+
+ if (grub_add (grub_strlen (field), 1, &size))
+ return NULL;
+
+ if (buffer == NULL)
+ buffer = grub_zalloc (size);
+ else
+ {
+ if (grub_add (size, grub_strlen (buffer), &size))
+ return NULL;
+
+ buffer = grub_realloc (buffer, size);
+ }
+
+ if (buffer == NULL)
+ return NULL;
+
+ tmp = buffer + grub_strlen (buffer);
+ tmp = grub_stpcpy (tmp, field);
+
+ if (is_env_var == true)
+ tmp = grub_stpcpy (tmp, " ");
+
+ return buffer;
+}
+
+/*
+ * This function takes a value string, checks for environmental variables, and
+ * returns the value string with all environmental variables replaced with the
+ * value stored in the variable.
+ */
+static char *
+blsuki_expand_val (const char *value)
+{
+ char *buffer = NULL;
+ const char *start = value;
+ const char *end = value;
+ bool is_env_var = false;
+
+ if (value == NULL)
+ return NULL;
+
+ while (*value != '\0')
+ {
+ if (*value == '$')
+ {
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ is_env_var = true;
+ start = value + 1;
+ }
+ else if (is_env_var == true)
+ {
+ if (grub_isalnum (*value) == 0 && *value != '_')
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ is_env_var = false;
+ start = value;
+ if (*start == ' ')
+ start++;
+ }
+ }
+
+ end = value;
+ value++;
+ }
+
+ if (start != end)
+ {
+ buffer = blsuki_field_append (is_env_var, buffer, start, end);
+ if (buffer == NULL)
+ return NULL;
+ }
+
+ return buffer;
+}
+
+/*
+ * This function returns a string with the command to load a linux kernel with
+ * kernel command-line options based on what was specified in the BLS config
+ * file.
+ */
+static char *
+bls_get_linux (grub_blsuki_entry_t *entry)
+{
+ char *linux_path;
+ char *linux_cmd = NULL;
+ char *options = NULL;
+ char *tmp;
+ grub_size_t size;
+
+ linux_path = blsuki_get_val (entry, "linux", NULL);
+ options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
+
+ if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+
+ if (options != NULL)
+ {
+ if (grub_add (size, grub_strlen (options), &size) ||
+ grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
+ goto finish;
+ }
+ }
+
+ linux_cmd = grub_malloc (size);
+ if (linux_cmd == NULL)
+ goto finish;
+
+ tmp = linux_cmd;
+ tmp = grub_stpcpy (tmp, "linux ");
+ tmp = grub_stpcpy (tmp, linux_path);
+ if (options != NULL)
+ {
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, options);
+ }
+
+ tmp = grub_stpcpy (tmp, "\n");
+
+ finish:
+ grub_free (options);
+ return linux_cmd;
+}
+
+/*
+ * This function returns a string with the command to load all initrds for a
+ * linux kernel image based on the list provided by the BLS config file.
+ */
+static char *
+bls_get_initrd (grub_blsuki_entry_t *entry)
+{
+ char **initrd_list;
+ char *initrd_cmd = NULL;
+ char *tmp;
+ grub_size_t size;
+ int i;
+
+ initrd_list = blsuki_make_list (entry, "initrd", NULL);
+ if (initrd_list != NULL)
+ {
+ size = sizeof ("initrd");
+
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ if (grub_add (size, 1, &size) ||
+ grub_add (size, grub_strlen (initrd_list[i]), &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+ }
+
+ if (grub_add (size, 1, &size))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
+ goto finish;
+ }
+
+ initrd_cmd = grub_malloc (size);
+ if (initrd_cmd == NULL)
+ goto finish;
+
+ tmp = grub_stpcpy (initrd_cmd, "initrd");
+ for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
+ {
+ grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
+ tmp = grub_stpcpy (tmp, " ");
+ tmp = grub_stpcpy (tmp, initrd_list[i]);
+ }
+ tmp = grub_stpcpy (tmp, "\n");
+ }
+
+ finish:
+ grub_free (initrd_list);
+ return initrd_cmd;
+}
+
+/*
+ * This function returns a string with the command to load a device tree blob
+ * from the BLS config file.
+ */
+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, " ");
+ 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");
+ savedefault = ((NULL != sdval) && (grub_strcmp (sdval, "true") == 0));
+ 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");
+ }
+
+ /* 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;
+}
+
+/*
+ * 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.
+ */
+static void
+blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
+{
+ struct read_entry_info read_entry_info;
+ grub_fs_t dir_fs = NULL;
+ grub_device_t dir_dev = NULL;
+ bool fallback = false;
+ int r;
+
+ do
+ {
+ read_entry_info.file = NULL;
+ read_entry_info.dirname = info->dirname;
+
+ grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname);
+ dir_dev = info->dev;
+ dir_fs = info->fs;
+ read_entry_info.devid = info->devid;
+
+ r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry,
+ &read_entry_info);
+ if (r != 0)
+ {
+ grub_dprintf ("blsuki", "blsuki_read_entry returned error\n");
+ grub_errno = GRUB_ERR_NONE;
+ }
+
+ /*
+ * 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.
+ */
+ if (entries == NULL && fallback == false && enable_fallback == true)
+ {
+ blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
+ grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
+ read_entry_info.dirname, info->dirname);
+ fallback = true;
+ }
+ else
+ fallback = false;
+ }
+ while (fallback == true);
+}
+
+static grub_err_t
+blsuki_load_entries (char *path, bool enable_fallback)
+{
+ grub_size_t len;
+ static grub_err_t r;
+ const char *devid = NULL;
+ char *dir = NULL;
+ struct find_entry_info info = {
+ .dev = NULL,
+ .fs = NULL,
+ .dirname = NULL,
+ };
+ struct read_entry_info rei = {
+ .devid = NULL,
+ .dirname = NULL,
+ };
+
+ if (path != NULL)
+ {
+ len = grub_strlen (path);
+ if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0)
+ {
+ rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG);
+ if (rei.file == NULL)
+ return grub_errno;
+
+ /* blsuki_read_entry() closes the file. */
+ return blsuki_read_entry (path, NULL, &rei);
+ }
+ else if (path[0] == '(')
+ {
+ devid = path + 1;
+
+ dir = grub_strchr (path, ')');
+ if (dir == NULL)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path);
+
+ *dir = '\0';
+
+ /* Check if there is more than the devid in the path. */
+ if (dir + 1 < path + len)
+ dir = dir + 1;
+ }
+ else if (path[0] == '/')
+ dir = path;
+ }
+
+ if (dir == NULL)
+ dir = (char *) GRUB_BLS_CONFIG_PATH;
+
+ r = blsuki_set_find_entry_info (&info, dir, devid);
+ if (r == GRUB_ERR_NONE)
+ blsuki_find_entry (&info, enable_fallback);
+
+ if (info.dev != NULL)
+ grub_device_close (info.dev);
+
+ return r;
+}
+
+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;
+
+ 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);
+ if (*def_entry_end != '\0')
+ return false;
+
+ if (def_idx == idx)
+ return true;
+
+ return false;
+}
+
+/*
+ * This function creates a GRUB boot menu entry for each BLS entry in the
+ * entries list.
+ */
+static grub_err_t
+blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id)
+{
+ const char *def_entry = NULL;
+ grub_blsuki_entry_t *entry = NULL;
+ int idx = 0;
+
+ def_entry = grub_env_get ("default");
+
+ FOR_BLSUKI_ENTRIES(entry)
+ {
+ if (entry->visible == 1)
+ {
+ idx++;
+ continue;
+ }
+ if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) ||
+ (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);
+ entry->visible = 1;
+ }
+
+ idx++;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ grub_err_t err;
+ struct grub_arg_list *state = ctxt->state;
+ char *path = NULL;
+ char *entry_id = NULL;
+ bool enable_fallback = false;
+ bool show_default = false;
+ bool show_non_default = false;
+ bool all = true;
+ entries = NULL;
+
+ if (state[0].set)
+ path = state[0].arg;
+ if (state[1].set)
+ enable_fallback = true;
+ if (state[2].set)
+ {
+ show_default = true;
+ all = false;
+ }
+ if (state[3].set)
+ {
+ show_non_default = true;
+ all = false;
+ }
+ if (state[4].set)
+ {
+ entry_id = state[4].arg;
+ all = false;
+ }
+ if (all == true)
+ {
+ show_default = true;
+ show_non_default = true;
+ }
+
+ err = blsuki_load_entries (path, enable_fallback);
+ if (err != GRUB_ERR_NONE)
+ return err;
+
+ return blsuki_create_entries (show_default, show_non_default, entry_id);
+}
+
+static grub_extcmd_t bls_cmd;
+
+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);
+}
+
+GRUB_MOD_FINI(blsuki)
+{
+ grub_unregister_extcmd (bls_cmd);
+}
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index a768fa6e8..7d9f9eb3c 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -143,7 +143,7 @@ legacy_file (const char *filename)
args[0] = oldname;
grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy",
NULL, NULL,
- entrysrc, 0);
+ entrysrc, 0, NULL);
grub_free (args);
entrysrc[0] = 0;
grub_free (oldname);
@@ -204,7 +204,7 @@ legacy_file (const char *filename)
}
args[0] = entryname;
grub_normal_add_menu_entry (1, args, NULL, NULL, NULL,
- NULL, NULL, entrysrc, 0);
+ NULL, NULL, entrysrc, 0, NULL);
grub_free (args);
}
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 720e6d8ea..09749c415 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
char **classes, const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu)
+ int submenu, grub_blsuki_entry_t *blsuki)
{
int menu_hotkey = 0;
char **menu_args = NULL;
@@ -188,6 +188,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
(*last)->args = menu_args;
(*last)->sourcecode = menu_sourcecode;
(*last)->submenu = submenu;
+ (*last)->blsuki = blsuki;
menu->size++;
return GRUB_ERR_NONE;
@@ -286,7 +287,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
users,
ctxt->state[2].arg, 0,
ctxt->state[3].arg,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's',
+ NULL);
src = args[argc - 1];
args[argc - 1] = NULL;
@@ -303,7 +305,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args)
ctxt->state[0].args, ctxt->state[4].arg,
users,
ctxt->state[2].arg, prefix, src + 1,
- ctxt->extcmd->cmd->name[0] == 's');
+ ctxt->extcmd->cmd->name[0] == 's', NULL);
src[len - 1] = ch;
args[argc - 1] = src;
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index 96abfda2f..a9e7a09d1 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -21,6 +21,7 @@
#include <grub/net.h>
#include <grub/normal.h>
#include <grub/dl.h>
+#include <grub/menu.h>
#include <grub/misc.h>
#include <grub/file.h>
#include <grub/mm.h>
@@ -67,6 +68,11 @@ grub_normal_free_menu (grub_menu_t menu)
grub_free (entry->args);
}
+ if (entry->blsuki)
+ {
+ entry->blsuki->visible = 0;
+ }
+
grub_free ((void *) entry->id);
grub_free ((void *) entry->users);
grub_free ((void *) entry->title);
--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;
+};
+typedef struct grub_blsuki_entry grub_blsuki_entry_t;
+
struct grub_menu_entry_class
{
char *name;
@@ -60,6 +72,9 @@ struct grub_menu_entry
/* The next element. */
struct grub_menu_entry *next;
+
+ /* BLS used to populate the entry */
+ grub_blsuki_entry_t *blsuki;
};
typedef struct grub_menu_entry *grub_menu_entry_t;
diff --git a/include/grub/normal.h b/include/grub/normal.h
index 218cbabcc..d0150e3c2 100644
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes,
const char *id,
const char *users, const char *hotkey,
const char *prefix, const char *sourcecode,
- int submenu);
+ int submenu, grub_blsuki_entry_t *blsuki);
grub_err_t
grub_normal_set_password (const char *user, const char *password);
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at()
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
@ 2025-07-27 1:54 ` Alec Brown via Grub-devel
2025-08-04 18:16 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 4/5] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
4 siblings, 1 reply; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
Adding filevercmp support to grub-core/commands/blsuki.c from gnulib will cause
issues with the type of the offset parameter for grub_util_write_image_at() for
EMU builds. To fix this issue, we can change the type from off_t to grub_off_t.
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
include/grub/util/misc.h | 2 +-
util/misc.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h
index e9e0a6724..bfce06558 100644
--- a/include/grub/util/misc.h
+++ b/include/grub/util/misc.h
@@ -36,7 +36,7 @@ char *grub_util_read_image (const char *path);
void grub_util_load_image (const char *path, char *buf);
void grub_util_write_image (const char *img, size_t size, FILE *out,
const char *name);
-void grub_util_write_image_at (const void *img, size_t size, off_t offset,
+void grub_util_write_image_at (const void *img, size_t size, grub_off_t offset,
FILE *out, const char *name);
char *make_system_path_relative_to_its_root (const char *path);
diff --git a/util/misc.c b/util/misc.c
index 0f928e5b4..6e16a68d9 100644
--- a/util/misc.c
+++ b/util/misc.c
@@ -101,7 +101,7 @@ grub_util_read_image (const char *path)
}
void
-grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE *out,
+grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out,
const char *name)
{
grub_util_info ("writing 0x%" GRUB_HOST_PRIxLONG_LONG " bytes at offset 0x%"
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 4/5] blsuki: Check for mounted /boot in emu
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
` (2 preceding siblings ...)
2025-07-27 1:54 ` [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at() Alec Brown via Grub-devel
@ 2025-07-27 1:54 ` Alec Brown via Grub-devel
2025-08-04 18:31 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
4 siblings, 1 reply; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
From: Robbie Harwood <rharwood@redhat.com>
Irritatingly, BLS defines paths relative to the mountpoint of the
filesystem which contains its snippets, not / or any other fixed
location. So grub2-emu needs to know whether /boot is a separate
filesystem from / and conditionally prepend a path.
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
---
grub-core/Makefile.core.def | 3 ++
grub-core/commands/blsuki.c | 89 ++++++++++++++++++++++++++++++---
grub-core/osdep/linux/getroot.c | 8 +++
grub-core/osdep/unix/getroot.c | 4 +-
include/grub/emu/misc.h | 2 +-
5 files changed, 97 insertions(+), 9 deletions(-)
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 996a1c2ae..9488654fb 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -367,6 +367,9 @@ kernel = {
emu = kern/emu/cache_s.S;
emu = kern/emu/hostdisk.c;
emu = osdep/unix/hostdisk.c;
+ emu = osdep/relpath.c;
+ emu = osdep/getroot.c;
+ emu = osdep/unix/getroot.c;
emu = osdep/exec.c;
extra_dist = osdep/unix/exec.c;
emu = osdep/devmapper/hostdisk.c;
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
index c275dbb92..76615e9bc 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -32,6 +32,13 @@
#include <grub/lib/envblk.h>
#include <filevercmp.h>
+#ifdef GRUB_MACHINE_EMU
+#include <grub/emu/misc.h>
+#define GRUB_BOOT_DEVICE "/boot"
+#else
+#define GRUB_BOOT_DEVICE ""
+#endif
+
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
@@ -75,6 +82,40 @@ static grub_blsuki_entry_t *entries;
#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+#ifdef GRUB_MACHINE_EMU
+/*
+ * Cache probing in blsuki_update_boot_device().
+ */
+static int separate_boot = -1;
+#endif
+
+/*
+ * BLS appears to make paths relative to the filesystem that snippets are
+ * on, not /. Attempt to cope.
+ */
+static char *blsuki_update_boot_device (char *tmp)
+{
+#ifdef GRUB_MACHINE_EMU
+ char *ret;
+
+ if (separate_boot != -1)
+ goto probed;
+
+ separate_boot = 0;
+
+ ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE);
+
+ if (ret != NULL && ret[0] == '\0')
+ separate_boot = 1;
+
+ probed:
+ if (!separate_boot)
+ return tmp;
+#endif
+
+ return grub_stpcpy (tmp, GRUB_BOOT_DEVICE);
+}
+
/*
* This function will add a new keyval pair to a list of keyvals stored in the
* entry parameter.
@@ -527,7 +568,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
linux_path = blsuki_get_val (entry, "linux", NULL);
options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL));
- if (grub_add (sizeof ("linux "), grub_strlen (linux_path), &size))
+ if (grub_add (sizeof ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size");
goto finish;
@@ -549,6 +590,7 @@ bls_get_linux (grub_blsuki_entry_t *entry)
tmp = linux_cmd;
tmp = grub_stpcpy (tmp, "linux ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, linux_path);
if (options != NULL)
{
@@ -583,7 +625,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++)
{
- if (grub_add (size, 1, &size) ||
+ if (grub_add (size, sizeof (" " GRUB_BOOT_DEVICE) - 1, &size) ||
grub_add (size, grub_strlen (initrd_list[i]), &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size");
@@ -606,6 +648,7 @@ bls_get_initrd (grub_blsuki_entry_t *entry)
{
grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]);
tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, initrd_list[i]);
}
tmp = grub_stpcpy (tmp, "\n");
@@ -631,7 +674,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
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) ||
+ if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) ||
grub_add (size, 1, &size))
{
grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size");
@@ -645,6 +688,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
tmp = dt_cmd;
tmp = grub_stpcpy (dt_cmd, "devicetree");
tmp = grub_stpcpy (tmp, " ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, dt_path);
tmp = grub_stpcpy (tmp, "\n");
}
@@ -761,7 +805,11 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
if (devid == NULL)
{
+#ifdef GRUB_MACHINE_EMU
+ devid = "host";
+#else
devid = grub_env_get ("root");
+#endif
if (devid == NULL)
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "root");
}
@@ -803,10 +851,13 @@ blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, c
* parameter. If the fallback option is enabled, the default location will be
* checked for BLS config files if the first attempt fails.
*/
-static void
+static grub_err_t
blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
{
struct read_entry_info read_entry_info;
+ char *default_dir = NULL;
+ char *tmp;
+ grub_size_t default_size;
grub_fs_t dir_fs = NULL;
grub_device_t dir_dev = NULL;
bool fallback = false;
@@ -837,7 +888,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
*/
if (entries == NULL && fallback == false && enable_fallback == true)
{
- blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
+ default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
+ 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);
+
+ blsuki_set_find_entry_info (info, default_dir, NULL);
grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
read_entry_info.dirname, info->dirname);
fallback = true;
@@ -846,6 +905,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
fallback = false;
}
while (fallback == true);
+
+ grub_free (default_dir);
+ return GRUB_ERR_NONE;
}
static grub_err_t
@@ -855,6 +917,9 @@ blsuki_load_entries (char *path, bool enable_fallback)
static grub_err_t r;
const char *devid = NULL;
char *dir = NULL;
+ char *default_dir = NULL;
+ char *tmp;
+ grub_size_t dir_size;
struct find_entry_info info = {
.dev = NULL,
.fs = NULL,
@@ -896,15 +961,25 @@ blsuki_load_entries (char *path, bool enable_fallback)
}
if (dir == NULL)
- dir = (char *) GRUB_BLS_CONFIG_PATH;
+ {
+ dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
+ 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);
+ dir = default_dir;
+ }
r = blsuki_set_find_entry_info (&info, dir, devid);
if (r == GRUB_ERR_NONE)
- blsuki_find_entry (&info, enable_fallback);
+ r = blsuki_find_entry (&info, enable_fallback);
if (info.dev != NULL)
grub_device_close (info.dev);
+ grub_free (default_dir);
return r;
}
diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
index 7872a8d03..38fe110fe 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -131,6 +131,7 @@ struct mountinfo_entry
char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1];
};
+#ifdef GRUB_UTIL
static char **
grub_util_raid_getmembers (const char *name, int bootable)
{
@@ -191,6 +192,7 @@ grub_util_raid_getmembers (const char *name, int bootable)
return devicelist;
}
+#endif
/* Statting something on a btrfs filesystem always returns a virtual device
major/minor pair rather than the real underlying device, because btrfs
@@ -579,6 +581,7 @@ out:
return ret;
}
+#ifdef GRUB_UTIL
static char *
get_mdadm_uuid (const char *os_dev)
{
@@ -636,6 +639,7 @@ out:
return name;
}
+#endif
static int
grub_util_is_imsm_or_ddf (const char *os_dev)
@@ -975,6 +979,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st,
return path;
}
+#ifdef GRUB_UTIL
static char *
grub_util_get_raid_grub_dev (const char *os_dev)
{
@@ -1077,6 +1082,7 @@ grub_util_get_raid_grub_dev (const char *os_dev)
}
return grub_dev;
}
+#endif
enum grub_dev_abstraction_types
grub_util_get_dev_abstraction_os (const char *os_dev)
@@ -1093,6 +1099,7 @@ grub_util_get_dev_abstraction_os (const char *os_dev)
return GRUB_DEV_ABSTRACTION_NONE;
}
+#ifdef GRUB_UTIL
int
grub_util_pull_device_os (const char *os_dev,
enum grub_dev_abstraction_types ab)
@@ -1149,6 +1156,7 @@ grub_util_get_grub_dev_os (const char *os_dev)
return grub_dev;
}
+#endif
char *
grub_make_system_path_relative_to_its_root_os (const char *path)
diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
index c7aa202ab..c061735a4 100644
--- a/grub-core/osdep/unix/getroot.c
+++ b/grub-core/osdep/unix/getroot.c
@@ -16,8 +16,8 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
#include <config-util.h>
+#include <config.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -566,6 +566,7 @@ grub_guess_root_devices (const char *dir_in)
#endif
+#ifdef GRUB_UTIL
void
grub_util_pull_lvm_by_command (const char *os_dev)
{
@@ -662,6 +663,7 @@ out:
free (buf);
free (vgid);
}
+#endif
/* ZFS has similar problems to those of btrfs (see above). */
void
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
index fefbec499..ceedc7399 100644
--- a/include/grub/emu/misc.h
+++ b/include/grub/emu/misc.h
@@ -39,7 +39,7 @@ void grub_fini_all (void);
void grub_find_zpool_from_dir (const char *dir,
char **poolname, char **poolfs);
-char *grub_make_system_path_relative_to_its_root (const char *path)
+char *EXPORT_FUNC (grub_make_system_path_relative_to_its_root) (const char *path)
WARN_UNUSED_RESULT;
int
grub_util_device_is_mapped (const char *dev);
--
2.27.0
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
` (3 preceding siblings ...)
2025-07-27 1:54 ` [PATCH v5 4/5] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
@ 2025-07-27 1:54 ` Alec Brown via Grub-devel
2025-08-05 14:34 ` Daniel Kiper via Grub-devel
4 siblings, 1 reply; 15+ messages in thread
From: Alec Brown via Grub-devel @ 2025-07-27 1:54 UTC (permalink / raw)
To: grub-devel
Cc: Alec Brown, christopher.obbard, daniel.kiper, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
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.brown@oracle.com>
---
docs/grub.texi | 33 +++
grub-core/commands/blsuki.c | 453 +++++++++++++++++++++++++++++++++---
| 2 +
3 files changed, 450 insertions(+), 38 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 1186f274f..5d9217057 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6495,6 +6495,7 @@ you forget a command, you can run the command @command{help}
* tpm2_dump_pcr:: Dump TPM2 PCRs
* 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
@@ -8247,6 +8248,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} exists only in memory and don't
+update @file{grub.cfg}.
+
+By default, the UKI entries are stored in the @file{/EFI/Linux} directory in the EFI
+system partition. If UKI entries are stored elsewhere, the @option{--path} option can be
+used to check a different directory instead of the default location. If no UKI
+entries are found while using the @option{--path} option, the @option{--enable-fallback} option
+can be used to check for entries in the default location.
+
+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.
+
+For more information on UKI, see: @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 76615e9bc..86a72cd0b 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -32,6 +32,12 @@
#include <grub/lib/envblk.h>
#include <filevercmp.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,10 +48,19 @@
GRUB_MOD_LICENSE ("GPLv3+");
#define GRUB_BLS_CONFIG_PATH "/loader/entries/"
+#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
#define BLS_EXT_LEN (sizeof (".conf") - 1)
+#define UKI_EXT_LEN (sizeof (".efi") - 1)
#define BLSUKI_KEYVALS_MAX 10000
+#define UKI_SECTION_SIZE_MAX 10000
+
+enum blsuki_cmd_type
+ {
+ BLSUKI_BLS_CMD,
+ BLSUKI_UKI_CMD,
+ };
static const struct grub_arg_option bls_opt[] =
{
@@ -57,6 +72,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;
@@ -67,6 +94,7 @@ struct read_entry_info
{
const char *devid;
const char *dirname;
+ enum blsuki_cmd_type cmd_type;
grub_file_t file;
};
@@ -78,7 +106,7 @@ struct find_entry_info
grub_fs_t fs;
};
-static grub_blsuki_entry_t *entries;
+static grub_blsuki_entry_t *entries = NULL;
#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
@@ -183,7 +211,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 set to -1 to the last parameter 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.
+ * them in sorted order.
*/
static char *
blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
@@ -312,20 +340,213 @@ 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, "plain image kernel is 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;
+ }
+
+ if (section->raw_data_size > UKI_SECTION_SIZE_MAX)
+ {
+ err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected");
+ goto finish;
+ }
+
+ 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 osrel_ptr parameter and returns the keyval pair. Since we are using
+ * grub_strtok_r(), the osrel_ptr will be updated to the following line of
+ * osrel. This function returns NULL when it reaches the end of osrel.
+ */
+static char *
+uki_read_osrel (char **osrel_ptr, char **val_ret)
+{
+ char *key, *val;
+ grub_size_t val_size;
+
+ for (;;)
+ {
+ key = grub_strtok_r (NULL, "\n\r", osrel_ptr);
+ if (key == NULL)
+ return NULL;
+
+ /* Remove leading white space */
+ while (*key == ' ' || *key == '\t')
+ key++;
+
+ /* Skip commented lines */
+ if (*key == '#')
+ continue;
+
+ /* Split key/value */
+ key = grub_strtok_r (key, "=", &val);
+ if (key == NULL || *val == '\0')
+ continue;
+
+ /* Remove quotes from value */
+ val_size = grub_strlen (val);
+ if (*val == '\"' && val[val_size - 1] == '\"')
+ {
+ val[val_size - 1] = '\0';
+ val++;
+ }
+
+ *val_ret = val;
+ break;
+ }
+
+ return key;
+}
+
/*
* 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 path_len = 0, filename_len;
- grub_err_t err;
+ grub_size_t path_len = 0, ext_len = 0, filename_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;
@@ -333,17 +554,29 @@ blsuki_read_entry (const char *filename,
filename_len = grub_strlen (filename);
+ if (info->cmd_type == BLSUKI_BLS_CMD)
+ {
+ ext = ".conf";
+ ext_len = BLS_EXT_LEN;
+ file_type = GRUB_FILE_TYPE_CONFIG;
+ }
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
+ }
+
if (info->file != NULL)
f = info->file;
else
{
- if (filename_len < BLS_EXT_LEN ||
- grub_strcmp (filename + filename_len - BLS_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;
@@ -380,7 +613,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);
@@ -396,7 +648,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 **
@@ -786,6 +1038,63 @@ 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, *osrel_line;
+ char *key = NULL;
+ char *value = NULL;
+ char *src = NULL;
+
+ /*
+ * 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;
+ }
+
+ osrel_line = osrel;
+ while ((key = uki_read_osrel (&osrel_line, &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);
+}
+
/*
* 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
@@ -795,7 +1104,7 @@ bls_create_entry (grub_blsuki_entry_t *entry)
* 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, enum blsuki_cmd_type cmd_type)
{
grub_device_t dev;
grub_fs_t fs;
@@ -805,10 +1114,23 @@ 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);
+ if (image == NULL)
+ return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device"));
+ 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");
@@ -847,15 +1169,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, enum blsuki_cmd_type 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;
@@ -872,6 +1195,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);
@@ -884,19 +1208,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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
+ 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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (cmd_dir) - 2;
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;
@@ -911,15 +1241,17 @@ 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, enum blsuki_cmd_type cmd_type)
{
- grub_size_t len;
+ grub_size_t len, ext_len = 0;
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,
@@ -928,12 +1260,24 @@ 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";
+ ext_len = BLS_EXT_LEN;
+ }
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ }
+
len = grub_strlen (path);
- if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0)
+ 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)
@@ -962,19 +1306,24 @@ blsuki_load_entries (char *path, bool enable_fallback)
if (dir == NULL)
{
- dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
+ 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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (cmd_dir) - 2;
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);
@@ -1012,11 +1361,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, enum blsuki_cmd_type cmd_type)
{
const char *def_entry = NULL;
grub_blsuki_entry_t *entry = NULL;
@@ -1035,7 +1384,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;
}
@@ -1046,8 +1398,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, enum blsuki_cmd_type cmd_type)
{
grub_err_t err;
struct grub_arg_list *state = ctxt->state;
@@ -1084,24 +1435,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
}
--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
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
@ 2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
2025-08-01 13:03 ` Daniel Kiper via Grub-devel
2025-08-01 12:58 ` Daniel Kiper via Grub-devel
2025-08-01 13:51 ` Vladimir 'phcoder' Serbinenko
2 siblings, 1 reply; 15+ messages in thread
From: Frediano Ziglio via Grub-devel @ 2025-07-28 13:21 UTC (permalink / raw)
To: The development of GNU GRUB
Cc: Frediano Ziglio, Alec Brown, christopher.obbard, daniel.kiper,
jan.setjeeilers, mate.kukri, pjones, lsandova, mlewando,
ross.philipson, 93sam, phcoder
On Sun, Jul 27, 2025 at 2:57 AM Alec Brown via Grub-devel
<grub-devel@gnu.org> wrote:
>
> Add the functions grub_strtok() and grub_strtok_r() to help parse strings into
> tokens separated by characters in the 'delim' parameter. These functions are
> present in gnulib but calling them directly from the gnulib code is quite
> challenging since the call "#include <string.h>" would include the header file
> grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h,
> where strtok() and strtok_r() are declared. Since this overlap is quite
> problematic, the simpler solution was to implement the code in the GRUB based
> on gnulib's implementation. For more information on these functions, visit the
> Linux Programmer's Manual("man strtok").
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++
> include/grub/misc.h | 3 +++
> 2 files changed, 65 insertions(+)
>
> diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
> index 2b7922393..258f91893 100644
> --- a/grub-core/kern/misc.c
> +++ b/grub-core/kern/misc.c
> @@ -401,6 +401,68 @@ grub_strword (const char *haystack, const char *needle)
> return 0;
> }
>
> +char *
> +grub_strtok_r (char *s, const char *delim, char **save_ptr)
> +{
> + char *token;
> + const char *c;
> + bool is_delim;
> +
> + if (s == NULL)
> + s = *save_ptr;
> +
> + /* Scan leading delimiters. */
> + while (*s != '\0')
> + {
> + is_delim = false;
> + for (c = delim; *c != '\0'; c++)
> + {
> + if (*s == *c)
> + {
> + is_delim = true;
> + break;
> + }
> + }
> + if (is_delim == true)
> + s++;
> + else
> + break;
Would it be sensible to replace the above loop with a
for ( ; *s != '\0'; ++s )
if ( grub_strchr(delim, *s) == NULL )
break;
> + }
> +
> + if (*s == '\0')
> + {
> + *save_ptr = s;
> + return NULL;
> + }
> +
> + /* Find the end of the token. */
> + token = s;
> + while (*s != '\0')
> + {
> + for (c = delim; *c != '\0'; c++)
> + {
> + if (*s == *c)
> + {
> + *s = '\0';
> + *save_ptr = s + 1;
> + return token;
> + }
> + }
> + s++;
> + }
> +
> + *save_ptr = s;
> + return token;
> +}
> +
> +char *
> +grub_strtok (char *s, const char *delim)
> +{
> + static char *last;
> +
> + return grub_strtok_r (s, delim, &last);
> +}
> +
> int
> grub_isspace (int c)
> {
> diff --git a/include/grub/misc.h b/include/grub/misc.h
> index e087e7b3e..9522d7305 100644
> --- a/include/grub/misc.h
> +++ b/include/grub/misc.h
> @@ -126,6 +126,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
> char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
> int EXPORT_FUNC(grub_strword) (const char *s, const char *w);
>
> +char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr);
> +char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim);
> +
> /* Copied from gnulib.
> Written by Bruno Haible <bruno@clisp.org>, 2005. */
> static inline char *
Otherwise the patch looks good to me.
Frediano
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
@ 2025-08-01 12:58 ` Daniel Kiper via Grub-devel
2025-08-01 13:51 ` Vladimir 'phcoder' Serbinenko
2 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-01 12:58 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
On Sun, Jul 27, 2025 at 01:54:32AM +0000, Alec Brown wrote:
> Add the functions grub_strtok() and grub_strtok_r() to help parse strings into
> tokens separated by characters in the 'delim' parameter. These functions are
> present in gnulib but calling them directly from the gnulib code is quite
> challenging since the call "#include <string.h>" would include the header file
> grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h,
> where strtok() and strtok_r() are declared. Since this overlap is quite
> problematic, the simpler solution was to implement the code in the GRUB based
> on gnulib's implementation. For more information on these functions, visit the
> Linux Programmer's Manual("man strtok").
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
@ 2025-08-01 13:03 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-01 13:03 UTC (permalink / raw)
To: Frediano Ziglio
Cc: Daniel Kiper, The development of GNU GRUB, Alec Brown,
christopher.obbard, jan.setjeeilers, mate.kukri, pjones, lsandova,
mlewando, ross.philipson, 93sam, phcoder
On Mon, Jul 28, 2025 at 02:21:50PM +0100, Frediano Ziglio wrote:
> On Sun, Jul 27, 2025 at 2:57 AM Alec Brown via Grub-devel <grub-devel@gnu.org> wrote:
> >
> > Add the functions grub_strtok() and grub_strtok_r() to help parse strings into
> > tokens separated by characters in the 'delim' parameter. These functions are
> > present in gnulib but calling them directly from the gnulib code is quite
> > challenging since the call "#include <string.h>" would include the header file
> > grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h,
> > where strtok() and strtok_r() are declared. Since this overlap is quite
> > problematic, the simpler solution was to implement the code in the GRUB based
> > on gnulib's implementation. For more information on these functions, visit the
> > Linux Programmer's Manual("man strtok").
> >
> > Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> > ---
> > grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++
> > include/grub/misc.h | 3 +++
> > 2 files changed, 65 insertions(+)
> >
> > diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
> > index 2b7922393..258f91893 100644
> > --- a/grub-core/kern/misc.c
> > +++ b/grub-core/kern/misc.c
> > @@ -401,6 +401,68 @@ grub_strword (const char *haystack, const char *needle)
> > return 0;
> > }
> >
> > +char *
> > +grub_strtok_r (char *s, const char *delim, char **save_ptr)
> > +{
> > + char *token;
> > + const char *c;
> > + bool is_delim;
> > +
> > + if (s == NULL)
> > + s = *save_ptr;
> > +
> > + /* Scan leading delimiters. */
> > + while (*s != '\0')
> > + {
> > + is_delim = false;
> > + for (c = delim; *c != '\0'; c++)
> > + {
> > + if (*s == *c)
> > + {
> > + is_delim = true;
> > + break;
> > + }
> > + }
> > + if (is_delim == true)
> > + s++;
> > + else
> > + break;
>
> Would it be sensible to replace the above loop with a
>
> for ( ; *s != '\0'; ++s )
> if ( grub_strchr(delim, *s) == NULL )
> break;
Maybe it is sensible but we want bug to bug Gnulib compatible code here.
However, if you convince Gnulib maintainers to get that change I am more
than happy to get it to the GRUB too.
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
2025-08-01 12:58 ` Daniel Kiper via Grub-devel
@ 2025-08-01 13:51 ` Vladimir 'phcoder' Serbinenko
2025-08-04 16:40 ` Daniel Kiper via Grub-devel
2 siblings, 1 reply; 15+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2025-08-01 13:51 UTC (permalink / raw)
To: Alec Brown
Cc: The development of GRUB 2, christopher.obbard, Daniel Kiper,
Jan Setje-Eilers, Mate Kukri, Peter Jones, Leo Sandoval,
Marta Lewandowska, Ross Philipson, 93sam
[-- Attachment #1.1: Type: text/plain, Size: 3311 bytes --]
Please don't put it into kernel unless it's used by the kernel. lib/ would
be a better fit
Regards
Vladimir 'phcoder' Serbinenko
Le dim. 27 juil. 2025, 04:54, Alec Brown <alec.r.brown@oracle.com> a écrit :
> Add the functions grub_strtok() and grub_strtok_r() to help parse strings
> into
> tokens separated by characters in the 'delim' parameter. These functions
> are
> present in gnulib but calling them directly from the gnulib code is quite
> challenging since the call "#include <string.h>" would include the header
> file
> grub-core/lib/posix_wrap/string.h instead of grub-core/lib/gnulib/string.h,
> where strtok() and strtok_r() are declared. Since this overlap is quite
> problematic, the simpler solution was to implement the code in the GRUB
> based
> on gnulib's implementation. For more information on these functions, visit
> the
> Linux Programmer's Manual("man strtok").
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> grub-core/kern/misc.c | 62 +++++++++++++++++++++++++++++++++++++++++++
> include/grub/misc.h | 3 +++
> 2 files changed, 65 insertions(+)
>
> diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
> index 2b7922393..258f91893 100644
> --- a/grub-core/kern/misc.c
> +++ b/grub-core/kern/misc.c
> @@ -401,6 +401,68 @@ grub_strword (const char *haystack, const char
> *needle)
> return 0;
> }
>
> +char *
> +grub_strtok_r (char *s, const char *delim, char **save_ptr)
> +{
> + char *token;
> + const char *c;
> + bool is_delim;
> +
> + if (s == NULL)
> + s = *save_ptr;
> +
> + /* Scan leading delimiters. */
> + while (*s != '\0')
> + {
> + is_delim = false;
> + for (c = delim; *c != '\0'; c++)
> + {
> + if (*s == *c)
> + {
> + is_delim = true;
> + break;
> + }
> + }
> + if (is_delim == true)
> + s++;
> + else
> + break;
> + }
> +
> + if (*s == '\0')
> + {
> + *save_ptr = s;
> + return NULL;
> + }
> +
> + /* Find the end of the token. */
> + token = s;
> + while (*s != '\0')
> + {
> + for (c = delim; *c != '\0'; c++)
> + {
> + if (*s == *c)
> + {
> + *s = '\0';
> + *save_ptr = s + 1;
> + return token;
> + }
> + }
> + s++;
> + }
> +
> + *save_ptr = s;
> + return token;
> +}
> +
> +char *
> +grub_strtok (char *s, const char *delim)
> +{
> + static char *last;
> +
> + return grub_strtok_r (s, delim, &last);
> +}
> +
> int
> grub_isspace (int c)
> {
> diff --git a/include/grub/misc.h b/include/grub/misc.h
> index e087e7b3e..9522d7305 100644
> --- a/include/grub/misc.h
> +++ b/include/grub/misc.h
> @@ -126,6 +126,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c);
> char *EXPORT_FUNC(grub_strrchr) (const char *s, int c);
> int EXPORT_FUNC(grub_strword) (const char *s, const char *w);
>
> +char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char
> **save_ptr);
> +char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim);
> +
> /* Copied from gnulib.
> Written by Bruno Haible <bruno@clisp.org>, 2005. */
> static inline char *
> --
> 2.27.0
>
>
[-- Attachment #1.2: Type: text/html, Size: 4293 bytes --]
[-- Attachment #2: Type: text/plain, Size: 141 bytes --]
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 1/5] kern/misc: Implement grub_strtok()
2025-08-01 13:51 ` Vladimir 'phcoder' Serbinenko
@ 2025-08-04 16:40 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-04 16:40 UTC (permalink / raw)
To: Vladimir 'phcoder' Serbinenko
Cc: Daniel Kiper, Alec Brown, The development of GRUB 2,
christopher.obbard, Jan Setje-Eilers, Mate Kukri, Peter Jones,
Leo Sandoval, Marta Lewandowska, Ross Philipson, 93sam
On Fri, Aug 01, 2025 at 04:51:54PM +0300, Vladimir 'phcoder' Serbinenko wrote:
> Please don't put it into kernel unless it's used by the kernel. lib/ would be a
> better fit
Whole family of grub_str*() functions live in grub-core/kern/misc.c. So,
I am not sure why we should make an exception here. Additionally, we do
not care about the GRUB kernel size as we did in the past. So, due to
these reasons I would stick with grub-core/kern/misc.c for grub_strtok()
and grub_strtok_r().
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets
2025-07-27 1:54 ` [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
@ 2025-08-04 18:14 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-04 18:14 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
On Sun, Jul 27, 2025 at 01:54:33AM +0000, Alec Brown wrote:
> From: Peter Jones <pjones@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 <pjones@redhat.com>
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> Signed-off-by: Will Thompson <wjt@endlessm.com>
> Signed-off-by: Alec Brown <alec.r.brown@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
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at()
2025-07-27 1:54 ` [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at() Alec Brown via Grub-devel
@ 2025-08-04 18:16 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-04 18:16 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
On Sun, Jul 27, 2025 at 01:54:34AM +0000, Alec Brown wrote:
> Adding filevercmp support to grub-core/commands/blsuki.c from gnulib will cause
> issues with the type of the offset parameter for grub_util_write_image_at() for
> EMU builds. To fix this issue, we can change the type from off_t to grub_off_t.
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 4/5] blsuki: Check for mounted /boot in emu
2025-07-27 1:54 ` [PATCH v5 4/5] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
@ 2025-08-04 18:31 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-04 18:31 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
On Sun, Jul 27, 2025 at 01:54:35AM +0000, Alec Brown wrote:
> From: Robbie Harwood <rharwood@redhat.com>
>
> Irritatingly, BLS defines paths relative to the mountpoint of the
> filesystem which contains its snippets, not / or any other fixed
> location. So grub2-emu needs to know whether /boot is a separate
> filesystem from / and conditionally prepend a path.
>
> Signed-off-by: Robbie Harwood <rharwood@redhat.com>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> grub-core/Makefile.core.def | 3 ++
> grub-core/commands/blsuki.c | 89 ++++++++++++++++++++++++++++++---
> grub-core/osdep/linux/getroot.c | 8 +++
> grub-core/osdep/unix/getroot.c | 4 +-
> include/grub/emu/misc.h | 2 +-
> 5 files changed, 97 insertions(+), 9 deletions(-)
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 996a1c2ae..9488654fb 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -367,6 +367,9 @@ kernel = {
> emu = kern/emu/cache_s.S;
> emu = kern/emu/hostdisk.c;
> emu = osdep/unix/hostdisk.c;
> + emu = osdep/relpath.c;
> + emu = osdep/getroot.c;
> + emu = osdep/unix/getroot.c;
> emu = osdep/exec.c;
> extra_dist = osdep/unix/exec.c;
> emu = osdep/devmapper/hostdisk.c;
> diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
> index c275dbb92..76615e9bc 100644
> --- a/grub-core/commands/blsuki.c
> +++ b/grub-core/commands/blsuki.c
> @@ -32,6 +32,13 @@
> #include <grub/lib/envblk.h>
> #include <filevercmp.h>
>
> +#ifdef GRUB_MACHINE_EMU
> +#include <grub/emu/misc.h>
> +#define GRUB_BOOT_DEVICE "/boot"
> +#else
> +#define GRUB_BOOT_DEVICE ""
> +#endif
> +
> GRUB_MOD_LICENSE ("GPLv3+");
>
> #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> @@ -75,6 +82,40 @@ static grub_blsuki_entry_t *entries;
>
> #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
>
> +#ifdef GRUB_MACHINE_EMU
> +/*
> + * Cache probing in blsuki_update_boot_device().
> + */
> +static int separate_boot = -1;
You can make it a static in the function...
> +#endif
> +
> +/*
> + * BLS appears to make paths relative to the filesystem that snippets are
> + * on, not /. Attempt to cope.
> + */
> +static char *blsuki_update_boot_device (char *tmp)
> +{
> +#ifdef GRUB_MACHINE_EMU
> + char *ret;
> +
> + if (separate_boot != -1)
> + goto probed;
> +
> + separate_boot = 0;
> +
> + ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE);
> +
> + if (ret != NULL && ret[0] == '\0')
> + separate_boot = 1;
> +
> + probed:
> + if (!separate_boot)
> + return tmp;
> +#endif
> +
> + return grub_stpcpy (tmp, GRUB_BOOT_DEVICE);
> +}
[...]
> -static void
> +static grub_err_t
> blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> {
> struct read_entry_info read_entry_info;
> + char *default_dir = NULL;
> + char *tmp;
> + grub_size_t default_size;
> grub_fs_t dir_fs = NULL;
> grub_device_t dir_dev = NULL;
> bool fallback = false;
> @@ -837,7 +888,15 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> */
> if (entries == NULL && fallback == false && enable_fallback == true)
> {
> - blsuki_set_find_entry_info (info, GRUB_BLS_CONFIG_PATH, NULL);
> + default_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
s/2/1/?
> + 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);
> +
> + blsuki_set_find_entry_info (info, default_dir, NULL);
> grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
> read_entry_info.dirname, info->dirname);
> fallback = true;
> @@ -846,6 +905,9 @@ blsuki_find_entry (struct find_entry_info *info, bool enable_fallback)
> fallback = false;
> }
> while (fallback == true);
> +
> + grub_free (default_dir);
> + return GRUB_ERR_NONE;
> }
>
> static grub_err_t
> @@ -855,6 +917,9 @@ blsuki_load_entries (char *path, bool enable_fallback)
> static grub_err_t r;
> const char *devid = NULL;
> char *dir = NULL;
> + char *default_dir = NULL;
> + char *tmp;
> + grub_size_t dir_size;
> struct find_entry_info info = {
> .dev = NULL,
> .fs = NULL,
> @@ -896,15 +961,25 @@ blsuki_load_entries (char *path, bool enable_fallback)
> }
>
> if (dir == NULL)
> - dir = (char *) GRUB_BLS_CONFIG_PATH;
> + {
> + dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
> + 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);
> + dir = default_dir;
> + }
>
> r = blsuki_set_find_entry_info (&info, dir, devid);
> if (r == GRUB_ERR_NONE)
> - blsuki_find_entry (&info, enable_fallback);
> + r = blsuki_find_entry (&info, enable_fallback);
>
> if (info.dev != NULL)
> grub_device_close (info.dev);
>
> + grub_free (default_dir);
> return r;
> }
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries
2025-07-27 1:54 ` [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
@ 2025-08-05 14:34 ` Daniel Kiper via Grub-devel
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Kiper via Grub-devel @ 2025-08-05 14:34 UTC (permalink / raw)
To: Alec Brown
Cc: Daniel Kiper, grub-devel, christopher.obbard, jan.setjeeilers,
mate.kukri, pjones, lsandova, mlewando, ross.philipson, 93sam,
phcoder
On Sun, Jul 27, 2025 at 01:54:36AM +0000, 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.
>
> The Unified Kernel Image Specification:
> https://uapi-group.org/specifications/specs/unified_kernel_image/
>
> Signed-off-by: Alec Brown <alec.r.brown@oracle.com>
> ---
> docs/grub.texi | 33 +++
> grub-core/commands/blsuki.c | 453 +++++++++++++++++++++++++++++++++---
> include/grub/menu.h | 2 +
> 3 files changed, 450 insertions(+), 38 deletions(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index 1186f274f..5d9217057 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6495,6 +6495,7 @@ you forget a command, you can run the command @command{help}
> * tpm2_dump_pcr:: Dump TPM2 PCRs
> * 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
> @@ -8247,6 +8248,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} exists only in memory and don't
> +update @file{grub.cfg}.
> +
> +By default, the UKI entries are stored in the @file{/EFI/Linux} directory in the EFI
"entries"? I think you mean UKI "files"? The GRUB generates "UKI entires"
from "UKI files". I think this should be fixed in the doc snippet.
> +system partition. If UKI entries are stored elsewhere, the @option{--path} option can be
> +used to check a different directory instead of the default location. If no UKI
> +entries are found while using the @option{--path} option, the @option{--enable-fallback} option
> +can be used to check for entries in the default location.
> +
> +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.
> +
> +For more information on UKI, see: @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 76615e9bc..86a72cd0b 100644
> --- a/grub-core/commands/blsuki.c
> +++ b/grub-core/commands/blsuki.c
> @@ -32,6 +32,12 @@
> #include <grub/lib/envblk.h>
> #include <filevercmp.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,10 +48,19 @@
> GRUB_MOD_LICENSE ("GPLv3+");
>
> #define GRUB_BLS_CONFIG_PATH "/loader/entries/"
> +#define GRUB_UKI_CONFIG_PATH "/EFI/Linux"
>
> #define BLS_EXT_LEN (sizeof (".conf") - 1)
> +#define UKI_EXT_LEN (sizeof (".efi") - 1)
>
> #define BLSUKI_KEYVALS_MAX 10000
> +#define UKI_SECTION_SIZE_MAX 10000
I think these MAX values require some comments. Where do they come from?
Why not 20000 or 25000? This should be explained.
> +enum blsuki_cmd_type
> + {
> + BLSUKI_BLS_CMD,
> + BLSUKI_UKI_CMD,
> + };
>
> static const struct grub_arg_option bls_opt[] =
> {
> @@ -57,6 +72,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;
> @@ -67,6 +94,7 @@ struct read_entry_info
> {
> const char *devid;
> const char *dirname;
> + enum blsuki_cmd_type cmd_type;
> grub_file_t file;
> };
>
> @@ -78,7 +106,7 @@ struct find_entry_info
> grub_fs_t fs;
> };
>
> -static grub_blsuki_entry_t *entries;
> +static grub_blsuki_entry_t *entries = NULL;
>
> #define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
>
> @@ -183,7 +211,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 set to -1 to the last parameter 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.
> + * them in sorted order.
> */
> static char *
> blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last)
> @@ -312,20 +340,213 @@ 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, "plain image kernel is 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;
These two assignments seem redundant to me...
> + 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;
> + }
> +
> + if (section->raw_data_size > UKI_SECTION_SIZE_MAX)
> + {
> + err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected");
> + goto finish;
> + }
> +
> + 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 osrel_ptr parameter and returns the keyval pair. Since we are using
> + * grub_strtok_r(), the osrel_ptr will be updated to the following line of
> + * osrel. This function returns NULL when it reaches the end of osrel.
> + */
> +static char *
> +uki_read_osrel (char **osrel_ptr, char **val_ret)
> +{
> + char *key, *val;
> + grub_size_t val_size;
> +
> + for (;;)
> + {
> + key = grub_strtok_r (NULL, "\n\r", osrel_ptr);
> + if (key == NULL)
> + return NULL;
> +
> + /* Remove leading white space */
> + while (*key == ' ' || *key == '\t')
> + key++;
> +
> + /* Skip commented lines */
> + if (*key == '#')
> + continue;
> +
> + /* Split key/value */
> + key = grub_strtok_r (key, "=", &val);
> + if (key == NULL || *val == '\0')
> + continue;
> +
> + /* Remove quotes from value */
> + val_size = grub_strlen (val);
> + if (*val == '\"' && val[val_size - 1] == '\"')
Does it matter if somebody puts quotes into middle of the string?
What about single quotes?
> + {
> + val[val_size - 1] = '\0';
> + val++;
> + }
> +
> + *val_ret = val;
> + break;
> + }
> +
> + return key;
> +}
> +
> /*
> * 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 path_len = 0, filename_len;
> - grub_err_t err;
> + grub_size_t path_len = 0, ext_len = 0, filename_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;
>
> @@ -333,17 +554,29 @@ blsuki_read_entry (const char *filename,
>
> filename_len = grub_strlen (filename);
>
> + if (info->cmd_type == BLSUKI_BLS_CMD)
> + {
> + ext = ".conf";
> + ext_len = BLS_EXT_LEN;
> + file_type = GRUB_FILE_TYPE_CONFIG;
> + }
> + else if (info->cmd_type == BLSUKI_UKI_CMD)
> + {
> + ext = ".efi";
> + ext_len = UKI_EXT_LEN;
> + file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
Should not we fail here immediately on non-UEFI platforms?
Or probably it should be disabled in that case...
> + }
> +
> if (info->file != NULL)
> f = info->file;
> else
> {
> - if (filename_len < BLS_EXT_LEN ||
> - grub_strcmp (filename + filename_len - BLS_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;
> @@ -380,7 +613,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);
> @@ -396,7 +648,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 **
> @@ -786,6 +1038,63 @@ 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.
s/un/in/
> + */
> +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, *osrel_line;
> + char *key = NULL;
> + char *value = NULL;
> + char *src = NULL;
> +
> + /*
> + * 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;
> + }
> +
> + osrel_line = osrel;
> + while ((key = uki_read_osrel (&osrel_line, &value)))
(key = uki_read_osrel (&osrel_line, &value)) != NULL
> + {
> + 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 : "");
(options != NULL) ? " " : "",
(options != NULL) ? 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);
> +}
> +
> /*
> * 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
> @@ -795,7 +1104,7 @@ bls_create_entry (grub_blsuki_entry_t *entry)
> * 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, enum blsuki_cmd_type cmd_type)
> {
> grub_device_t dev;
> grub_fs_t fs;
> @@ -805,10 +1114,23 @@ 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;
Please add empty line here and/or...
grub_efi_loaded_image_t *image = grub_efi_get_loaded_image (grub_efi_image_handle);
> + image = grub_efi_get_loaded_image (grub_efi_image_handle);
> + if (image == NULL)
> + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device"));
> + 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");
> @@ -847,15 +1169,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, enum blsuki_cmd_type 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;
> @@ -872,6 +1195,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);
> @@ -884,19 +1208,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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
> + 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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (cmd_dir) - 2;
Oops... ... + strlen (cmd_dir) - 1;
> 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;
> @@ -911,15 +1241,17 @@ 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, enum blsuki_cmd_type cmd_type)
> {
> - grub_size_t len;
> + grub_size_t len, ext_len = 0;
> 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,
> @@ -928,12 +1260,24 @@ 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";
> + ext_len = BLS_EXT_LEN;
> + }
> + else if (cmd_type == BLSUKI_UKI_CMD)
> + {
> + ext = ".efi";
> + ext_len = UKI_EXT_LEN;
Missing EFI ifdefery...
In general it seems to me you are not consistent with it. I think more
code should be disabled for nun-UEFI case.
> + }
> +
> len = grub_strlen (path);
> - if (len >= BLS_EXT_LEN && grub_strcmp (path + len - BLS_EXT_LEN, ".conf") == 0)
> + 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)
> @@ -962,19 +1306,24 @@ blsuki_load_entries (char *path, bool enable_fallback)
>
> if (dir == NULL)
> {
> - dir_size = sizeof (GRUB_BOOT_DEVICE) + sizeof (GRUB_BLS_CONFIG_PATH) - 2;
> + 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 = sizeof (GRUB_BOOT_DEVICE) + sizeof (cmd_dir) - 2;
Again... ... + strlen (cmd_dir) - 1;
> 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);
> @@ -1012,11 +1361,11 @@ blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int
> }
Daniel
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-08-05 14:35 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-27 1:54 [PATCH v5 0/5] Add commands to load BLS and UKI files Alec Brown via Grub-devel
2025-07-27 1:54 ` [PATCH v5 1/5] kern/misc: Implement grub_strtok() Alec Brown via Grub-devel
2025-07-28 13:21 ` Frediano Ziglio via Grub-devel
2025-08-01 13:03 ` Daniel Kiper via Grub-devel
2025-08-01 12:58 ` Daniel Kiper via Grub-devel
2025-08-01 13:51 ` Vladimir 'phcoder' Serbinenko
2025-08-04 16:40 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 2/5] blsuki: Add blscfg command to parse Boot Loader Specification snippets Alec Brown via Grub-devel
2025-08-04 18:14 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 3/5] util/misc.c: Change offset type for grub_util_write_image_at() Alec Brown via Grub-devel
2025-08-04 18:16 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 4/5] blsuki: Check for mounted /boot in emu Alec Brown via Grub-devel
2025-08-04 18:31 ` Daniel Kiper via Grub-devel
2025-07-27 1:54 ` [PATCH v5 5/5] blsuki: Add uki command to load Unified Kernel Image entries Alec Brown via Grub-devel
2025-08-05 14:34 ` Daniel Kiper via Grub-devel
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.