Compare commits

..

33 commits

Author SHA1 Message Date
Joel Capitao
85de859da1
Do not include EFI dir in common subpackage
This fixes [1] which is part of [2]. We cannot ship
/usr/lib/efi/* in -common subpackage as bootupd does not expect this
directory for ppc64le arch.
We are moving the creation and configuration of the directory into
specific EFI macros only.

[1] e02b6e88c7
[2] https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1

Signed-off-by: Joel Capitao <jcapitao@redhat.com>
2025-12-11 08:35:43 +01:00
Leo Sandoval
16cb5b303d Increase EFI max allocation to max usable address
Resolves: #2263643

Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-12-10 12:20:17 -06:00
Leo Sandoval
70c5e62950 Add support for external environment block on Btrfs
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
Tested-by: Neal Gompa <ngompa@fedoraproject.org>
2025-11-26 11:22:14 -06:00
Leo Sandoval
a4ec822e7e blsuki: do not register blscfg command
Otherwise the blsuki.c blscfg cmd version replaces the one defined on
blscfg.c and the latter is the version we want to use.

Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-11-13 12:09:21 -06:00
Tom spot Callaway
6467d85178 rebuild for new fuse3 2025-11-04 16:20:54 -05:00
Leo Sandoval
f4663b0ddc Fix blsuki patch enumeration
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-10-27 11:38:18 -06:00
Leo Sandoval
4614a09b9f Include upstream blsuki related patches
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-10-15 15:26:32 -06:00
Leo Sandoval
b9b1d1e384 Include function name on debug and error print functions
With the following change, we see standard (grub_dprintf) and
error (grub_error) logs with the function name embedded (see below)
into the log which is particular useful when debugging

    commands/efi/tpm.c:grub_tpm_measure:281:tpm: log_event, pcr = 8, size = 0xb,

Including one more field on the print log impacts the binary sizes
and in turn their respective packages, the latter with an increase of
20k approximately (compared to 2.12-43) for most the the
packages.

Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-09-23 13:17:44 -06:00
Nicolas Frayer
730ce27747 gen_grub_cfgstub: Removed output messages
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-08-29 13:19:37 +02:00
Jan Stancek
f6bc25c3e7 55-set-boot-entry.install: move to 95
When KERNEL_INSTALL_STAGING_AREA is used (for example with
initrd_generator=mkosi-initrd), initrd gets installed to /boot
only later at 90-loaderentry.install.

Move the plugin to 95 so it runs after.

Related: #2389020
Signed-off-by: Jan Stancek <jstancek@redhat.com>
2025-08-20 22:03:31 +02:00
Marta Lewandowska
e02b6e88c7 Phase 1 of the bootloader updates proposal implementation
https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1

Signed-off-by: Marta Lewandoska <mlewando@redhat.com>
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-08-18 11:18:42 -06:00
Jan Stancek
354c77b195 55-set-boot-entry.install: fix initrd check
Fix where to look for initrd file, as /boot may not be a separate
partition, in which case adding /boot unconditionally leads to
a duplicate "/boot/boot/...".

Fix initrd line parsing to take only first word - it can contain
extra parameters, e.g. "$tuned_initrd", which are not part
of initrd filename.

Resolves: #2386118
Reported-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
Tested-by: Mikhail Gavrilov <mikhail.v.gavrilov@gmail.com>
Signed-off-by: Jan Stancek <jstancek@redhat.com>
2025-08-07 18:23:29 +02:00
FeRD (Frank Dana)
8f6e9e55c1 Suppress warnings about missing /etc/default/grub
Signed-off-by: FeRD (Frank Dana) <ferdnyc@gmail.com>
2025-08-01 18:48:37 +00:00
Yuxin Sun
06e458e5eb 20-grub.install: Skip BLS removal when entry type is type2
UKI(package is kernel-uki-virt) is a single, bootable file that bundles everything needed to start a Linux system.
It contains its own bootable stub and bypasses GRUB2 completely.
The kernel-core and kernel-uki-virt can coexist in one machine. And both of them call kernel-install remove <kversion> upon package
removal and this leads to the complete removal of both the traditional kernel & its artifacts(initramfs, BLS entry file,...).
For example, if the customer remove kernel-uki-virt, currently it also removes BLS entry which causes the regular kernel fails to boot up.
In https://github.com/systemd/systemd/pull/37897 it added --entry-type=type1|type2 option to kernel-install. type1 stands for normal kernel,
type2 stands for uki. When kernel-install is invoked with --entry-type=type2 which is for UKI, we should not remove the BLS entry.

Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
2025-07-31 10:52:12 +08:00
Leo Sandoval
8f843cc6ef Set correctly the memory attributes for the kernel PE sections
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-07-29 16:50:34 -06:00
Jan Stancek
42ca1a56a1 more robust kernel updates
Set boot entry only after sucessful ramdisk creation to prevent
booting into broken environments in cases where ramdisk is corrupt
or missing.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
2025-07-16 09:11:52 +02:00
Nicolas Frayer
01b4205642 spec: fix grub stub cfg creation script
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-07-08 12:40:07 +02:00
Yaakov Selkowitz
159b7b546a Remove package.cfg
This did not consistently work as intended, and the ELN automation can now
build this properly.

Signed-off-by: Yaakov Selkowitz <yselkowi@redhat.com>
2025-06-20 16:16:33 -04:00
Nicolas Frayer
828e1c5448 spec/posttrans: move grub config stub creation out of spec
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-06-12 21:12:40 +02:00
Nicolas Frayer
0e28d5ff18 osdep/linux/getroot: Detect DDF container similar to IMSM
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-06-05 10:56:47 +02:00
Nicolas Frayer
9400ee8e83 Some fixes addressing memory freeing and dereferencing
Resolves: #2368939
Resolves: #2368945
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-05-28 18:23:00 +02:00
Nicolas Frayer
9089407121 sbat: bump grub sbat
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-05-16 12:34:14 +02:00
Leo Sandoval
9fa1c31cb2 Fix cryptodisk vulnerabilities
Resolves: CVE-2025-4382
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-05-13 09:10:41 -06:00
Leo Sandoval
ffc692d90e Handle special kernel parameter characters properly
Resolves: #2362821
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-05-07 14:36:48 -06:00
Nicolas Frayer
5c361bf706 ppc/mkimage: SBAT support on powerpc
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-04-18 16:42:30 +02:00
Leo Sandoval
439310c622 do-sync: helper script to sync dist-git and git branches
The bootloader team manually synchronizes several git branches on the
rhboot/grub (git) repository based on dist-git (dist-git) repository. Although
this task is simple, it is a maintenance burden, usually done much after the
dist-git repository is updated, having out-of-sync git branches most of the
time.

The goal of the included script is to create a temporal branch based on
latest git branch then include those possible missing dist-git patches and
finally push it.

Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-04-08 16:00:08 +00:00
Nicolas Frayer
cbf45b1f24 ieee1275/ofnet: Fix grub_malloc() removed after added safe
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-03-25 19:17:46 +01:00
James Le Cuirot
42b2a048bf Fix the fallback mechanism when menu entries fail to boot
Patch 0098 accidentally broke the fallback mechanism, although it also
exposed another bug in upstream GRUB. The first of these new patches
fixes the upstream bug. It has already been merged there. The second
patch deals with the main issue.

Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
2025-03-25 15:16:32 +01:00
Nils Kattenbeck
c48d4f6719 Drop 'which' requirement
Since 28a7e597de0d5584f65e36f9588ff9041936e617, occurrences of 'which' were replaced by 'command -v'

Signed-off-by: Nils Kattenbeck <nilskemail+fedora@gmail.com>
2025-03-19 17:45:42 +00:00
Nicolas Frayer
50b73378ce powerpc: increase MIN RMA size for CAS negotiation
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-03-19 17:59:28 +01:00
Leo Sandoval
b04ed232e2 Remove NTFS attribute verification patch
The removed patch was part of the CVE patches ported recently into Fedora but
unfortunately this is causing segfaults when generating the grub configuration
file. At some point the same patch will come back with the corresponding fix but
for the time being, it is removed. Note that patch numeration did not change on
purpose: patch will be introduced again so need to mess with the numeration.

Resolves: #2350327

Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-03-10 13:16:08 -06:00
Nicolas Frayer
0c53016c97 fs/ext2: Rework out-of-bounds read for inline and external extents
Resolves: #2346804
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-02-26 11:32:30 +01:00
Leo Sandoval
5427c36328 Add Several CVE fixes
Resolves: CVE-2024-45781 CVE-2024-45783 CVE-2024-45778
Resolves: CVE-2024-45775 CVE-2024-45780 CVE-2024-45774
Resolves: CVE-2025-0690 CVE-2025-1118 CVE-2024-45782
Resolves: CVE-2025-0624 CVE-2024-45779 CVE-2024-45776
Resolves: CVE-2025-0622 CVE-2025-0677

Signed-off-by: Leo Sandoval <lsandova@redhat.com>
2025-02-19 10:10:45 -06:00
54 changed files with 6048 additions and 50 deletions

View file

@ -0,0 +1,66 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: James Le Cuirot <jlecuirot@microsoft.com>
Date: Thu, 24 Oct 2024 14:42:46 +0100
Subject: [PATCH] script/execute: Don't let trailing blank lines determine the
return code
grub_script_execute_sourcecode() parses and executes code one line at a
time, updating the return code each time because only the last line
determines the final status. However, trailing new lines were also
executed, masking any failure on the previous line. Fix this by only
trying to execute the command when there is actually one present.
This has presumably never been noticed because this code is not used by
regular functions, only in special cases like eval and menu entries. The
latter generally don't return at all, having booted an OS. When failing
to boot, upstream GRUB triggers the fallback mechanism regardless of the
return code.
We noticed the problem while using Red Hat's patches, which change this
behaviour to take account of the return code. In that case, a failure
takes you back to the menu rather than triggering a fallback.
Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
---
grub-core/script/execute.c | 5 ++++-
tests/grub_script_eval.in | 10 +++++++++-
2 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
index 014132703..3d26a3fe4 100644
--- a/grub-core/script/execute.c
+++ b/grub-core/script/execute.c
@@ -952,7 +952,10 @@ grub_script_execute_sourcecode (const char *source)
break;
}
- ret = grub_script_execute (parsed_script);
+ /* Don't let trailing blank lines determine the return code. */
+ if (parsed_script->cmd)
+ ret = grub_script_execute (parsed_script);
+
grub_script_free (parsed_script);
grub_free (line);
}
diff --git a/tests/grub_script_eval.in b/tests/grub_script_eval.in
index c97b78d77..9c6211042 100644
--- a/tests/grub_script_eval.in
+++ b/tests/grub_script_eval.in
@@ -3,4 +3,12 @@
eval echo "Hello world"
valname=tst
eval $valname=hi
-echo $tst
\ No newline at end of file
+echo $tst
+
+if eval "
+false
+"; then
+ echo should have failed
+else
+ echo failed as expected
+fi
--
2.48.1

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: James Le Cuirot <jlecuirot@microsoft.com>
Date: Thu, 24 Oct 2024 15:00:26 +0100
Subject: [PATCH] normal/menu: Check return code of the script when executing a
menu entry
Don't rely on grub_errno here because grub_script_execute_new_scope()
calls grub_print_error(), which always resets grub_errno back to
GRUB_ERR_NONE. It may also get reset by grub_wait_after_message().
This problem was observed when a "bad signature" error resulted in the
menu being redisplayed rather than the fallback mechanism being
triggered, although another change was also needed to fix it. This only
happens with Red Hat's patches because upstream GRUB triggers the
fallback mechanism regardless of the return code.
Signed-off-by: James Le Cuirot <jlecuirot@microsoft.com>
---
grub-core/normal/menu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
index 97687013c..a2703dabb 100644
--- a/grub-core/normal/menu.c
+++ b/grub-core/normal/menu.c
@@ -377,14 +377,14 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
if (ptr && ptr[0] && ptr[1])
grub_env_set ("default", ptr + 1);
- grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
+ err = grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
if (errs_before != grub_err_printed_errors)
grub_wait_after_message ();
errs_before = grub_err_printed_errors;
- if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+ if (err == GRUB_ERR_NONE && grub_loader_is_loaded ())
/* Implicit execution of boot, only if something is loaded. */
err = grub_command_execute ("boot", 0, 0);
--
2.48.1

View file

@ -0,0 +1,167 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Wed, 23 Oct 2024 17:54:32 +0530
Subject: [PATCH] grub-mkimage: Create new ELF note for SBAT
In order to store the SBAT data we create a new ELF note. The string
".sbat", zero-padded to 4 byte alignment, shall be entered in the name
field. The string "SBAT"'s ASCII values, 0x53424154, should be entered
in the type field.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
include/grub/util/mkimage.h | 4 ++--
util/grub-mkimagexx.c | 48 +++++++++++++++++++++++++++++++++++++++++++--
util/mkimage.c | 5 +++--
3 files changed, 51 insertions(+), 6 deletions(-)
diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
index 6f1da89b9b65..881e3031f41f 100644
--- a/include/grub/util/mkimage.h
+++ b/include/grub/util/mkimage.h
@@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path,
const struct grub_install_image_target_desc *image_target);
void
grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target,
- int note, size_t appsig_size, char **core_img, size_t *core_size,
+ int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size,
Elf32_Addr target_addr,
struct grub_mkimage_layout *layout);
void
grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target,
- int note, size_t appsig_size, char **core_img, size_t *core_size,
+ int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size,
Elf64_Addr target_addr,
struct grub_mkimage_layout *layout);
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 9488f052510a..b9930544889d 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -116,6 +116,14 @@ struct section_metadata
const char *strtab;
};
+#define GRUB_SBAT_NOTE_NAME ".sbat"
+#define GRUB_SBAT_NOTE_TYPE 0x53424154 /* "SBAT" */
+
+struct grub_sbat_note {
+ Elf32_Nhdr header;
+ char name[ALIGN_UP(sizeof(GRUB_SBAT_NOTE_NAME), 4)];
+};
+
static int
is_relocatable (const struct grub_install_image_target_desc *image_target)
{
@@ -217,7 +225,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
void
SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
- int note, size_t appsig_size, char **core_img, size_t *core_size,
+ int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size,
Elf_Addr target_addr,
struct grub_mkimage_layout *layout)
{
@@ -226,10 +234,17 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
Elf_Ehdr *ehdr;
Elf_Phdr *phdr;
Elf_Shdr *shdr;
- int header_size, footer_size = 0;
+ int header_size, footer_size = 0, footer_offset = 0;
int phnum = 1;
int shnum = 4;
int string_size = sizeof (".text") + sizeof ("mods") + 1;
+ char *footer;
+
+ if (sbat)
+ {
+ phnum++;
+ footer_size += ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4);
+ }
if (appsig_size)
{
@@ -263,6 +278,7 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
ehdr = (void *) elf_img;
phdr = (void *) (elf_img + sizeof (*ehdr));
shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr));
+ footer = elf_img + program_size + header_size;
memcpy (ehdr->e_ident, ELFMAG, SELFMAG);
ehdr->e_ident[EI_CLASS] = ELFCLASSXX;
if (!image_target->bigendian)
@@ -435,6 +451,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE);
phdr->p_memsz = 0;
phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+ footer = ptr;
+ footer_offset = XEN_NOTE_SIZE;
}
if (image_target->id == IMAGE_XEN_PVH)
@@ -468,6 +486,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
phdr->p_filesz = grub_host_to_target32 (XEN_PVH_NOTE_SIZE);
phdr->p_memsz = 0;
phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+ footer = ptr;
+ footer_offset = XEN_PVH_NOTE_SIZE;
}
if (note)
@@ -498,6 +518,30 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
phdr->p_filesz = grub_host_to_target32 (note_size);
phdr->p_memsz = 0;
phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+ footer = (elf_img + program_size + header_size + note_size);
+ footer_offset += note_size;
+ }
+
+ if (sbat)
+ {
+ int note_size = ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4);
+ struct grub_sbat_note *note_ptr = (struct grub_sbat_note *) footer;
+
+ note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_SBAT_NOTE_NAME));
+ note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(layout->sbat_size, 4));
+ note_ptr->header.n_type = grub_host_to_target32 (GRUB_SBAT_NOTE_TYPE);
+ memcpy (note_ptr->name, GRUB_SBAT_NOTE_NAME, sizeof (GRUB_SBAT_NOTE_NAME));
+ memcpy ((char *)(note_ptr + 1), sbat, layout->sbat_size);
+
+ phdr++;
+ phdr->p_type = grub_host_to_target32 (PT_NOTE);
+ phdr->p_flags = grub_host_to_target32 (PF_R);
+ phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+ phdr->p_vaddr = 0;
+ phdr->p_paddr = 0;
+ phdr->p_filesz = grub_host_to_target32 (note_size);
+ phdr->p_memsz = 0;
+ phdr->p_offset = grub_host_to_target32 (header_size + program_size + footer_offset);
}
if (appsig_size) {
diff --git a/util/mkimage.c b/util/mkimage.c
index f31fdefa814a..7fa6a7b21954 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -1848,6 +1848,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
case IMAGE_I386_IEEE1275:
{
grub_uint64_t target_addr;
+ char *sbat = NULL;
if (image_target->id == IMAGE_LOONGSON_ELF)
{
if (comp == GRUB_COMPRESSION_NONE)
@@ -1859,10 +1860,10 @@ grub_install_generate_image (const char *dir, const char *prefix,
else
target_addr = image_target->link_addr;
if (image_target->voidp_sizeof == 4)
- grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img,
+ grub_mkimage_generate_elf32 (image_target, note, appsig_size, sbat, &core_img,
&core_size, target_addr, &layout);
else
- grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img,
+ grub_mkimage_generate_elf64 (image_target, note, appsig_size, sbat, &core_img,
&core_size, target_addr, &layout);
}
break;

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Wed, 23 Oct 2024 17:54:33 +0530
Subject: [PATCH] grub-mkimage: Add SBAT metadata into ELF note for PowerPC
targets
The SBAT metadata is read from CSV file and transformed into an ELF note
with the -s option.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/mkimage.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/util/mkimage.c b/util/mkimage.c
index 7fa6a7b21954..f92949d1df25 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -957,8 +957,8 @@ grub_install_generate_image (const char *dir, const char *prefix,
total_module_size += dtb_size + sizeof (struct grub_module_header);
}
- if (sbat_path != NULL && image_target->id != IMAGE_EFI)
- grub_util_error (_(".sbat section can be embedded into EFI images only"));
+ if (sbat_path != NULL && (image_target->id != IMAGE_EFI && image_target->id != IMAGE_PPC))
+ grub_util_error (_("SBAT data can be added only to EFI or powerpc-ieee1275 images"));
if (disable_shim_lock)
total_module_size += sizeof (struct grub_module_header);
@@ -1849,6 +1849,13 @@ grub_install_generate_image (const char *dir, const char *prefix,
{
grub_uint64_t target_addr;
char *sbat = NULL;
+ if (sbat_path != NULL)
+ {
+ sbat_size = grub_util_get_image_size (sbat_path);
+ sbat = xmalloc (sbat_size);
+ grub_util_load_image (sbat_path, sbat);
+ layout.sbat_size = sbat_size;
+ }
if (image_target->id == IMAGE_LOONGSON_ELF)
{
if (comp == GRUB_COMPRESSION_NONE)

View file

@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Fri, 28 Feb 2025 17:00:53 +0300
Subject: [PATCH] kern/rescue_reader: Block the rescue mode until the CLI
authentication
This further mitigates potential misuse of the CLI after the
root device has been successfully unlocked via TPM.
Fixes: CVE-2025-4382
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/rescue_reader.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c
index 4259857ba9..a71ada8fb7 100644
--- a/grub-core/kern/rescue_reader.c
+++ b/grub-core/kern/rescue_reader.c
@@ -79,7 +79,7 @@ void __attribute__ ((noreturn))
grub_rescue_run (void)
{
/* Stall if the CLI has been disabled */
- if (grub_is_cli_disabled ())
+ if (grub_is_cli_disabled () || grub_is_cli_need_auth ())
{
grub_printf ("Rescue mode has been disabled...\n");

View file

@ -0,0 +1,133 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Sat, 1 Mar 2025 14:16:48 +0300
Subject: [PATCH] commands/search: Introduce the --cryptodisk-only argument
This allows users to restrict the "search" command's scope to
encrypted disks only.
Typically, this command is used to "rebase" $root and $prefix
before loading additional configuration files via "source" or
"configfile". Unfortunately, this leads to security problems,
like CVE-2023-4001, when an unexpected, attacker-controlled
device is chosen by the "search" command.
The --cryptodisk-only argument allows users to ensure that the
file system picked is encrypted.
This feature supports the CLI authentication, blocking bypass
attempts.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/search.c | 20 ++++++++++++++++++++
grub-core/commands/search_wrap.c | 7 ++++++-
grub-core/normal/main.c | 3 ++-
include/grub/search.h | 9 +++++----
4 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c
index d538b36219..84362b54b3 100644
--- a/grub-core/commands/search.c
+++ b/grub-core/commands/search.c
@@ -182,6 +182,26 @@ iterate_device (const char *name, void *data)
grub_device_close (dev);
}
+ /* Limit to encrypted disks when requested. */
+ if (ctx->flags & SEARCH_FLAGS_CRYPTODISK_ONLY)
+ {
+ grub_device_t dev;
+
+ dev = grub_device_open (name);
+ if (dev == NULL)
+ {
+ grub_errno = GRUB_ERR_NONE;
+ return 0;
+ }
+ if (dev->disk == NULL || dev->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ {
+ grub_device_close (dev);
+ grub_errno = GRUB_ERR_NONE;
+ return 0;
+ }
+ grub_device_close (dev);
+ }
+
/* Skip it if it's not the root device when requested. */
if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY)
{
diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c
index 4fe6440c65..ec2fb638f1 100644
--- a/grub-core/commands/search_wrap.c
+++ b/grub-core/commands/search_wrap.c
@@ -41,6 +41,7 @@ static const struct grub_arg_option options[] =
ARG_TYPE_STRING},
{"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0},
{"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0},
+ {"cryptodisk-only", 0, 0, N_("Only probe encrypted disks."), 0, 0},
{"root-dev-only", 'r', 0, N_("Only probe root device."), 0, 0},
{"hint", 'h', GRUB_ARG_OPTION_REPEATABLE,
N_("First try the device HINT. If HINT ends in comma, "
@@ -76,6 +77,7 @@ enum options
SEARCH_SET,
SEARCH_NO_FLOPPY,
SEARCH_EFIDISK_ONLY,
+ SEARCH_CRYPTODISK_ONLY,
SEARCH_ROOTDEV_ONLY,
SEARCH_HINT,
SEARCH_HINT_IEEE1275,
@@ -191,6 +193,9 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args)
if (state[SEARCH_EFIDISK_ONLY].set)
flags |= SEARCH_FLAGS_EFIDISK_ONLY;
+ if (state[SEARCH_CRYPTODISK_ONLY].set)
+ flags |= SEARCH_FLAGS_CRYPTODISK_ONLY;
+
if (state[SEARCH_ROOTDEV_ONLY].set)
flags |= SEARCH_FLAGS_ROOTDEV_ONLY;
@@ -215,7 +220,7 @@ GRUB_MOD_INIT(search)
cmd =
grub_register_extcmd ("search", grub_cmd_search,
GRUB_COMMAND_FLAG_EXTRACTOR | GRUB_COMMAND_ACCEPT_DASH,
- N_("[-f|-l|-u|-s|-n] [--hint HINT [--hint HINT] ...]"
+ N_("[-f|-l|-u|-s|-n] [--cryptodisk-only] [--hint HINT [--hint HINT] ...]"
" NAME"),
N_("Search devices by file, filesystem label"
" or filesystem UUID."
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
index dd20e51290..6f7290a897 100644
--- a/grub-core/normal/main.c
+++ b/grub-core/normal/main.c
@@ -615,7 +615,8 @@ static const char *features[] = {
"feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint",
"feature_default_font_path", "feature_all_video_module",
"feature_menuentry_id", "feature_menuentry_options", "feature_200_final",
- "feature_nativedisk_cmd", "feature_timeout_style"
+ "feature_nativedisk_cmd", "feature_timeout_style",
+ "feature_search_cryptodisk_only"
};
GRUB_MOD_INIT(normal)
diff --git a/include/grub/search.h b/include/grub/search.h
index 9343c66b13..b6b5c8be1f 100644
--- a/include/grub/search.h
+++ b/include/grub/search.h
@@ -21,10 +21,11 @@
enum search_flags
{
- SEARCH_FLAGS_NONE = 0,
- SEARCH_FLAGS_NO_FLOPPY = 1,
- SEARCH_FLAGS_EFIDISK_ONLY = 2,
- SEARCH_FLAGS_ROOTDEV_ONLY = 4
+ SEARCH_FLAGS_NONE = 0,
+ SEARCH_FLAGS_NO_FLOPPY = 1,
+ SEARCH_FLAGS_EFIDISK_ONLY = 2,
+ SEARCH_FLAGS_ROOTDEV_ONLY = 4,
+ SEARCH_FLAGS_CRYPTODISK_ONLY = 8
};
void grub_search_fs_file (const char *key, const char *var,

View file

@ -0,0 +1,133 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Sun, 2 Mar 2025 18:08:22 +0300
Subject: [PATCH] disk/diskfilter: Introduce the "cryptocheck" command
This command examines a given diskfilter device, e.g., an LVM disk,
and checks if underlying disks, physical volumes, are cryptodisks,
e.g., LUKS disks, this layout is called "LVM-on-LUKS".
The return value is 0 when all underlying disks (of a given device)
are cryptodisks (1 if at least one disk is unencrypted or in an
unknown state).
Users are encouraged to include the relevant check before loading
anything from an LVM disk that is supposed to be encrypted.
This further supports the CLI authentication, blocking bypass
attempts when booting from an encrypted LVM disk.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/disk/diskfilter.c | 75 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index fef0b815ab..216be6a782 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -20,6 +20,7 @@
#include <grub/dl.h>
#include <grub/disk.h>
#include <grub/mm.h>
+#include <grub/command.h>
#include <grub/err.h>
#include <grub/misc.h>
#include <grub/diskfilter.h>
@@ -1365,6 +1366,73 @@ grub_diskfilter_get_pv_from_disk (grub_disk_t disk,
}
#endif
+static int
+grub_diskfilter_check_pvs_encrypted (grub_disk_t disk, int *pvs_cnt)
+{
+ struct grub_diskfilter_lv *lv = disk->data;
+ struct grub_diskfilter_pv *pv;
+
+ *pvs_cnt = 0;
+
+ if (lv->vg->pvs)
+ for (pv = lv->vg->pvs; pv; pv = pv->next)
+ {
+ (*pvs_cnt)++;
+
+ if (pv->disk == NULL)
+ {
+ /* Can be a partially activated VG, bail out. */
+ return GRUB_ERR_TEST_FAILURE;
+ }
+
+ if (pv->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ {
+ /* All backing devices must be cryptodisks, stop. */
+ return GRUB_ERR_TEST_FAILURE;
+ }
+ }
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char **args)
+{
+ grub_disk_t disk;
+ int check_pvs_res;
+ int namelen;
+ int pvs_cnt;
+
+ if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected"));
+
+ namelen = grub_strlen (args[0]);
+ if (namelen > 2 && (args[0][0] == '(') && (args[0][namelen - 1] == ')'))
+ args[0][namelen - 1] = 0;
+ else
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("invalid disk: %s"),
+ args[0]);
+
+ if (!is_valid_diskfilter_name (&args[0][1]))
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unrecognized disk: %s"),
+ &args[0][1]);
+
+ disk = grub_disk_open (&args[0][1]);
+ if (disk == NULL)
+ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such disk: %s"),
+ &args[0][1]);
+
+ check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt);
+ grub_disk_close (disk);
+
+ grub_printf("%s is %sencrypted (%d pv%s examined)\n", &args[0][1],
+ (check_pvs_res == GRUB_ERR_NONE) ? "" : "un",
+ pvs_cnt,
+ (pvs_cnt > 1) ? "s" : "");
+
+ return check_pvs_res;
+}
+
static struct grub_disk_dev grub_diskfilter_dev =
{
.name = "diskfilter",
@@ -1381,14 +1449,21 @@ static struct grub_disk_dev grub_diskfilter_dev =
.next = 0
};
+static grub_command_t cmd;
+
GRUB_MOD_INIT(diskfilter)
{
grub_disk_dev_register (&grub_diskfilter_dev);
+ cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck,
+ N_("DEVICE"),
+ N_("Check if a logical volume resides on encrypted disks."));
}
GRUB_MOD_FINI(diskfilter)
{
grub_disk_dev_unregister (&grub_diskfilter_dev);
+ if (cmd != NULL)
+ grub_unregister_command (cmd);
free_array ();
}

View file

@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Sun, 2 Mar 2025 23:32:43 +0300
Subject: [PATCH] commands/search: Add the diskfilter support
When the --cryptodisk-only argument is given, also check the target
device using the "cryptocheck" command, if available.
This extends the checks to common layouts like LVM-on-LUKS, so the
--cryptodisk-only argument transparently handles such setups.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/search.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c
index 84362b54b3..1cf714ad7d 100644
--- a/grub-core/commands/search.c
+++ b/grub-core/commands/search.c
@@ -150,6 +150,36 @@ check_for_duplicate (const char *name, void *data)
return ret;
}
+static bool
+is_unencrypted_disk (grub_disk_t disk)
+{
+ grub_command_t cmd;
+ char *disk_str;
+ int disk_str_len;
+ int res;
+
+ if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ return false; /* This is (crypto) disk. */
+
+ if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID)
+ {
+ cmd = grub_command_find ("cryptocheck");
+ if (cmd == NULL) /* No diskfilter module loaded for some reason. */
+ return true;
+
+ disk_str_len = grub_strlen (disk->name) + 2 + 1;
+ disk_str = grub_malloc (disk_str_len);
+ if (disk_str == NULL) /* Something is wrong, better report as unencrypted. */
+ return true;
+
+ grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name);
+ res = cmd->func (cmd, 1, &disk_str);
+ grub_free (disk_str);
+ return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */
+ }
+ return true;
+}
+
/* Helper for FUNC_NAME. */
static int
iterate_device (const char *name, void *data)
@@ -193,7 +223,7 @@ iterate_device (const char *name, void *data)
grub_errno = GRUB_ERR_NONE;
return 0;
}
- if (dev->disk == NULL || dev->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ if (dev->disk == NULL || is_unencrypted_disk (dev->disk) == true)
{
grub_device_close (dev);
grub_errno = GRUB_ERR_NONE;

View file

@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Mon, 10 Mar 2025 15:33:46 +0300
Subject: [PATCH] docs: Document available crypto disks checks
Document the --cryptodisk-only argument. Also, document the
"cryptocheck" command invoked when that argument is processed.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 2d6d73a78d..93e23be99d 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4365,6 +4365,7 @@ you forget a command, you can run the command @command{help}
* configfile:: Load a configuration file
* cpuid:: Check for CPU features
* crc:: Compute or check CRC32 checksums
+* cryptocheck:: Check if a device is encrypted
* cryptomount:: Mount a crypto device
* cutmem:: Remove memory regions
* date:: Display or set current date and time
@@ -4674,6 +4675,16 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum}
(@pxref{hashsum}) for full description.
@end deffn
+@node cryptocheck
+@subsection cryptocheck
+
+@deffn Command cryptocheck device
+Check if a given diskfilter device is backed by encrypted devices
+(@pxref{cryptomount} for additional information).
+
+The command examines all backing devices, physical volumes, of a specified
+logical volume, like LVM2, and fails when at least one of them is unencrypted.
+@end deffn
@node cryptomount
@subsection cryptomount
@@ -5522,7 +5533,8 @@ unbootable. @xref{Using GPG-style digital signatures}, for more information.
@deffn Command search @
[@option{--file}|@option{--label}|@option{--fs-uuid}] @
- [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}] name
+ [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}|@option{--cryptodisk-only}] @
+ name
Search devices by file (@option{-f}, @option{--file}), filesystem label
(@option{-l}, @option{--label}), or filesystem UUID (@option{-u},
@option{--fs-uuid}).
@@ -5537,6 +5549,14 @@ devices, which can be slow.
The (@option{--efidisk-only}) option prevents searching any other devices then
EFI disks. This is typically used when chainloading to local EFI partition.
+The (@option{--cryptodisk-only}) option prevents searching any devices other
+than encrypted disks. This is typically used when booting from an encrypted
+file system to ensure that no code gets executed from an unencrypted device
+having the same filesystem UUID or label.
+
+This option implicitly invokes the command @command{cryptocheck}, if it is
+available (@pxref{cryptocheck} for additional information).
+
The @samp{search.file}, @samp{search.fs_label}, and @samp{search.fs_uuid}
commands are aliases for @samp{search --file}, @samp{search --label}, and
@samp{search --fs-uuid} respectively.

View file

@ -0,0 +1,147 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Tue, 6 May 2025 12:25:20 -0600
Subject: [PATCH] disk/cryptodisk: Add the "erase secrets" function
This commit adds the grub_cryptodisk_erasesecrets() function to wipe
master keys from all cryptodisks. This function is EFI-only.
Since there is no easy way to "force unmount" a given encrypted disk,
this function renders all mounted cryptodisks unusable. An attempt to
read them will return garbage.
This is why this function must be used in "no way back" conditions.
Currently, it is used when unloading the cryptodisk module and when
performing the "exit" command (it is often used to switch to the next
EFI application). This function is not called when performing the
"chainloader" command, because the callee may return to GRUB. For this
reason, users are encouraged to use "exit" instead of "chainloader" to
execute third-party boot applications.
This function does not guarantee that all secrets are wiped from RAM.
Console output, chunks from disk read requests and other may remain.
This function does not clear the IV prefix and rekey key for geli disks.
Also, this commit adds the relevant documentation improvements.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 6 ++++++
grub-core/commands/minicmd.c | 11 +++++++++++
grub-core/disk/cryptodisk.c | 28 ++++++++++++++++++++++++++++
include/grub/cryptodisk.h | 1 +
4 files changed, 46 insertions(+)
diff --git a/docs/grub.texi b/docs/grub.texi
index 93e23be99d..0af47b5ade 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4721,6 +4721,11 @@ namespace in addition to the cryptodisk namespace.
Support for plain encryption mode (plain dm-crypt) is provided via separate
@command{@pxref{plainmount}} command.
+
+On the EFI platform, GRUB tries to erase master keys from memory when the cryptodisk
+module is unloaded or the command @command{exit} is executed. All secrets remain in
+memory when the command @command{chainloader} is issued, because execution can
+return to GRUB on the EFI platform.
@end deffn
@node cutmem
@@ -6982,6 +6987,7 @@ USB support provides benefits similar to ATA (for USB disks) or AT (for USB
keyboards). In addition it allows USBserial.
Chainloading refers to the ability to load another bootloader through the same protocol
+and on some platforms, like EFI, allow that bootloader to return to the GRUB.
Hints allow faster disk discovery by already knowing in advance which is the disk in
question. On some platforms hints are correct unless you move the disk between boots.
diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c
index 9efb7718c3..eb786766a8 100644
--- a/grub-core/commands/minicmd.c
+++ b/grub-core/commands/minicmd.c
@@ -29,6 +29,10 @@
#include <grub/command.h>
#include <grub/i18n.h>
+#ifdef GRUB_MACHINE_EFI
+#include <grub/cryptodisk.h>
+#endif
+
GRUB_MOD_LICENSE ("GPLv3+");
/* cat FILE */
@@ -199,6 +203,13 @@ grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)),
retval = n;
}
+#ifdef GRUB_MACHINE_EFI
+ /*
+ * The "exit" command is often used to launch the next boot application.
+ * So, erase the secrets.
+ */
+ grub_cryptodisk_erasesecrets ();
+#endif
grub_exit (retval);
/* Not reached. */
}
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 8727760100..9597d2d3bd 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1677,6 +1677,31 @@ grub_cryptodisk_challenge_password (void)
return GRUB_ERR_NONE;
}
+
+void
+grub_cryptodisk_erasesecrets (void)
+{
+ grub_cryptodisk_t i;
+ grub_uint8_t *buf;
+
+ buf = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN);
+ if (buf == NULL)
+ grub_fatal ("grub_cryptodisk_erasesecrets: cannot allocate memory");
+
+ for (i = cryptodisk_list; i != NULL; i = i->next)
+ if (grub_cryptodisk_setkey (i, buf, i->keysize))
+ grub_fatal ("grub_cryptodisk_erasesecrets: cannot erase secrets for %s", i->source);
+ else
+ grub_printf ("Erased crypto secrets for %s\n", i->source);
+ /*
+ * Unfortunately, there is no way to "force unmount" a given disk, it may
+ * have mounted "child" disks as well, e.g., an LVM volume. So, this
+ * function MUST be called when there is no way back, e.g., when exiting.
+ * Otherwise, subsequent read calls for a cryptodisk will return garbage.
+ */
+
+ grub_free (buf);
+}
#endif /* GRUB_MACHINE_EFI */
struct grub_procfs_entry luks_script =
@@ -1700,6 +1725,9 @@ GRUB_MOD_INIT (cryptodisk)
GRUB_MOD_FINI (cryptodisk)
{
+#ifdef GRUB_MACHINE_EFI
+ grub_cryptodisk_erasesecrets ();
+#endif
grub_disk_dev_unregister (&grub_cryptodisk_dev);
cryptodisk_cleanup ();
grub_unregister_extcmd (cmd);
diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h
index d2572f8b05..ef307dfd5d 100644
--- a/include/grub/cryptodisk.h
+++ b/include/grub/cryptodisk.h
@@ -189,5 +189,6 @@ grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk);
#ifdef GRUB_MACHINE_EFI
grub_err_t grub_cryptodisk_challenge_password (void);
+void grub_cryptodisk_erasesecrets (void);
#endif
#endif

View file

@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Maxim Suhanov <dfirblog@gmail.com>
Date: Tue, 4 Mar 2025 15:27:59 +0300
Subject: [PATCH] disk/cryptodisk: Wipe the passphrase from memory
Switching to another EFI boot application while there are secrets in
RAM is dangerous, because not all firmware is wiping memory on free.
To reduce the attack surface, wipe the passphrase acquired when
unlocking an encrypted volume.
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/disk/cryptodisk.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 9597d2d3bd..26457095d3 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1164,6 +1164,7 @@ grub_cryptodisk_scan_device_real (const char *name,
if (askpass)
{
+ grub_memset (cargs->key_data, 0, cargs->key_len);
cargs->key_len = 0;
grub_free (cargs->key_data);
}

View file

@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang <mchang@suse.com>
Date: Fri, 14 Mar 2025 19:03:39 +0800
Subject: [PATCH] cryptocheck: Add --quiet option
The option can be used to suppress output if we only want to test the
return value of the command.
Also, mention this option in the documentation.
Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 4 +++-
grub-core/commands/search.c | 7 ++++++-
grub-core/disk/diskfilter.c | 25 +++++++++++++++++++------
3 files changed, 28 insertions(+), 8 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 0af47b5ade..ae01e8c994 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4678,12 +4678,14 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum}
@node cryptocheck
@subsection cryptocheck
-@deffn Command cryptocheck device
+@deffn Command cryptocheck [ @option{--quiet} ] device
Check if a given diskfilter device is backed by encrypted devices
(@pxref{cryptomount} for additional information).
The command examines all backing devices, physical volumes, of a specified
logical volume, like LVM2, and fails when at least one of them is unencrypted.
+
+The option @option{--quiet} can be given to suppress the output.
@end deffn
@node cryptomount
diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c
index 1cf714ad7d..c432c0ac6f 100644
--- a/grub-core/commands/search.c
+++ b/grub-core/commands/search.c
@@ -163,6 +163,9 @@ is_unencrypted_disk (grub_disk_t disk)
if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID)
{
+ char opt[] = "--quiet";
+ char *args[2];
+
cmd = grub_command_find ("cryptocheck");
if (cmd == NULL) /* No diskfilter module loaded for some reason. */
return true;
@@ -173,7 +176,9 @@ is_unencrypted_disk (grub_disk_t disk)
return true;
grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name);
- res = cmd->func (cmd, 1, &disk_str);
+ args[0] = opt;
+ args[1] = disk_str;
+ res = cmd->func (cmd, 2, args);
grub_free (disk_str);
return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */
}
diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 216be6a782..04b64aebc6 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -1402,6 +1402,19 @@ grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)),
int check_pvs_res;
int namelen;
int pvs_cnt;
+ int opt_quiet = 0;
+
+ if (argc == 2)
+ {
+ if (grub_strcmp (args[0], "--quiet") == 0)
+ {
+ opt_quiet = 1;
+ argc--;
+ args++;
+ }
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized option: %s"), args[0]);
+ }
if (argc != 1)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected"));
@@ -1424,11 +1437,11 @@ grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)),
check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt);
grub_disk_close (disk);
-
- grub_printf("%s is %sencrypted (%d pv%s examined)\n", &args[0][1],
- (check_pvs_res == GRUB_ERR_NONE) ? "" : "un",
- pvs_cnt,
- (pvs_cnt > 1) ? "s" : "");
+ if (!opt_quiet)
+ grub_printf ("%s is %sencrypted (%d pv%s examined)\n", &args[0][1],
+ (check_pvs_res == GRUB_ERR_NONE) ? "" : "un",
+ pvs_cnt,
+ (pvs_cnt > 1) ? "s" : "");
return check_pvs_res;
}
@@ -1456,7 +1469,7 @@ GRUB_MOD_INIT(diskfilter)
{
grub_disk_dev_register (&grub_diskfilter_dev);
cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck,
- N_("DEVICE"),
+ N_("[--quiet] DEVICE"),
N_("Check if a logical volume resides on encrypted disks."));
}

View file

@ -0,0 +1,291 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leo Sandoval <lsandova@redhat.com>
Date: Tue, 29 Jul 2025 09:43:37 -0600
Subject: [PATCH] Set correctly the memory attributes for the kernel PE
sections
Currently the whole kernel memory region is set to RO, so at some
point when execution is passed to the kernel, the latter faults on a
memory write access, e.g. zeroing .bss section. The proposed change
sets the memory attribute appropriately for each kernel PE section.
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
---
grub-core/loader/efi/linux.c | 170 +++++++++++++++++++++++++++-----------
grub-core/loader/i386/efi/linux.c | 5 +-
include/grub/efi/linux.h | 6 ++
3 files changed, 134 insertions(+), 47 deletions(-)
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
index ef55556f2d..111edf0e1d 100644
--- a/grub-core/loader/efi/linux.c
+++ b/grub-core/loader/efi/linux.c
@@ -36,6 +36,7 @@
#include <grub/efi/sb.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
+#include <grub/safemath.h>
#include <grub/verify.h>
GRUB_MOD_LICENSE ("GPLv3+");
@@ -202,22 +203,133 @@ grub_efi_check_nx_required (int *nx_required)
typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
grub_err_t
-grub_efi_linux_boot (grub_addr_t k_address, grub_size_t k_size,
+grub_efi_mem_set_att(grub_addr_t k_address, grub_size_t k_size,
+ grub_size_t k_start, int nx_supported)
+{
+ grub_addr_t k_start_address = k_address + k_start;
+
+ grub_uint64_t default_set_attrs = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X;
+ grub_uint64_t default_clear_attrs = 0;
+ grub_uint64_t stack_set_attrs = default_set_attrs;
+ grub_uint64_t stack_clear_attrs = default_clear_attrs;
+ grub_uint64_t kernel_set_attrs = default_set_attrs;
+ grub_uint64_t kernel_clear_attrs = default_clear_attrs;
+ grub_uint64_t attrs;
+
+ struct grub_msdos_image_header *header;
+ struct grub_pe_image_header *pe_image_header;
+ struct grub_pe32_coff_header *coff_header;
+ struct grub_pe32_section_table *section, *sections;
+ grub_uint16_t i;
+ grub_size_t sz;
+
+ header = (struct grub_msdos_image_header *)k_address;
+
+ if (grub_add ((grub_addr_t) header, header->pe_image_header_offset, &sz))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE image header address calculation"));
+
+ pe_image_header = (struct grub_pe_image_header *) (sz);
+
+ if (pe_image_header > (k_address + k_size))
+ return grub_error (GRUB_ERR_BAD_OS, N_("PE image header address is invalid"));
+
+ if (grub_memcmp (pe_image_header->signature, GRUB_PE32_SIGNATURE,
+ GRUB_PE32_SIGNATURE_SIZE) != 0)
+ return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid"));
+
+ coff_header = &(pe_image_header->coff_header);
+ grub_dprintf ("nx", "coff_header 0x%"PRIxGRUB_ADDR" machine %08x\n", (grub_addr_t)coff_header, coff_header->machine);
+
+ if (grub_add ((grub_addr_t) coff_header, sizeof (*coff_header), &sz) ||
+ grub_add (sz, coff_header->optional_header_size, &sz))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE sections calculation"));
+
+ sections = (struct grub_pe32_section_table *) (sz);
+
+ if (sections > (k_address + k_size))
+ return grub_error (GRUB_ERR_BAD_OS, N_("Section address is invalid"));
+
+ /* Parse the PE, remove W for code section, remove X for data sections, RO for the rest */
+ for (i = 0, section = sections; i < coff_header->num_sections; i++, section++)
+ {
+ kernel_set_attrs = default_set_attrs;
+ kernel_clear_attrs = default_clear_attrs;
+
+ if (nx_supported)
+ {
+ if (section->characteristics & GRUB_PE32_SCN_MEM_EXECUTE)
+ {
+ /* RX section */
+ kernel_set_attrs &= ~GRUB_MEM_ATTR_W;
+ kernel_clear_attrs |= GRUB_MEM_ATTR_W;
+ }
+ else if (section->characteristics & GRUB_PE32_SCN_MEM_WRITE)
+ {
+ /* RW section */
+ kernel_set_attrs &= ~GRUB_MEM_ATTR_X;
+ kernel_clear_attrs |= GRUB_MEM_ATTR_X;
+ }
+ else
+ {
+ /* RO section */
+ kernel_set_attrs &= ~GRUB_MEM_ATTR_W & ~GRUB_MEM_ATTR_X;
+ kernel_clear_attrs |= GRUB_MEM_ATTR_X | GRUB_MEM_ATTR_W ;
+ }
+ }
+
+ /* Make sure we are inside range */
+ if (grub_add ((grub_addr_t) k_address, section->raw_data_offset, &sz))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE Executable section calculation"));
+
+ grub_update_mem_attrs (sz, section->raw_data_size, kernel_set_attrs, kernel_clear_attrs);
+
+ grub_get_mem_attrs (sz, 4096, &attrs);
+ grub_dprintf ("nx", "permissions for section %s 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
+ section->name,
+ (grub_addr_t)sz,
+ (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
+ (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
+ (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
+ }
+
+ if (grub_stack_addr != (grub_addr_t)-1ll)
+ {
+ if (nx_supported)
+ {
+ stack_set_attrs &= ~GRUB_MEM_ATTR_X;
+ stack_clear_attrs |= GRUB_MEM_ATTR_X;
+ }
+
+ grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n",
+ grub_stack_addr, grub_stack_addr + grub_stack_size - 1,
+ (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-');
+
+ grub_update_mem_attrs (grub_stack_addr, grub_stack_size,
+ stack_set_attrs, stack_clear_attrs);
+
+ grub_get_mem_attrs (grub_stack_addr, 4096, &attrs);
+ grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
+ grub_stack_addr,
+ (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
+ (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
+ (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+
+grub_err_t
+grub_efi_linux_boot (grub_addr_t k_address, grub_size_t k_size, grub_size_t k_start,
grub_off_t h_offset, void *k_params,
int nx_supported)
{
+ grub_addr_t k_start_address = k_address + k_start;
grub_efi_loaded_image_t *loaded_image = NULL;
handover_func hf;
int offset = 0;
- grub_uint64_t stack_set_attrs = GRUB_MEM_ATTR_R |
- GRUB_MEM_ATTR_W |
- GRUB_MEM_ATTR_X;
- grub_uint64_t stack_clear_attrs = 0;
- grub_uint64_t kernel_set_attrs = stack_set_attrs;
- grub_uint64_t kernel_clear_attrs = stack_clear_attrs;
- grub_uint64_t attrs;
int nx_required = 0;
-
+
#ifdef __x86_64__
offset = 512;
#endif
@@ -241,47 +353,13 @@ grub_efi_linux_boot (grub_addr_t k_address, grub_size_t k_size,
if (nx_required && !nx_supported)
return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy"));
- if (nx_supported)
- {
- kernel_set_attrs &= ~GRUB_MEM_ATTR_W;
- kernel_clear_attrs |= GRUB_MEM_ATTR_W;
- stack_set_attrs &= ~GRUB_MEM_ATTR_X;
- stack_clear_attrs |= GRUB_MEM_ATTR_X;
- }
-
- grub_dprintf ("nx", "Setting attributes for 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to r%cx\n",
- k_address, k_address + k_size - 1,
- (kernel_set_attrs & GRUB_MEM_ATTR_W) ? 'w' : '-');
- grub_update_mem_attrs (k_address, k_size,
- kernel_set_attrs, kernel_clear_attrs);
-
- grub_get_mem_attrs (k_address, 4096, &attrs);
- grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
- (grub_addr_t)k_address,
- (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
- (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
- (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
- if (grub_stack_addr != (grub_addr_t)-1ll)
- {
- grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n",
- grub_stack_addr, grub_stack_addr + grub_stack_size - 1,
- (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-');
- grub_update_mem_attrs (grub_stack_addr, grub_stack_size,
- stack_set_attrs, stack_clear_attrs);
-
- grub_get_mem_attrs (grub_stack_addr, 4096, &attrs);
- grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
- grub_stack_addr,
- (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
- (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
- (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
- }
+ grub_efi_mem_set_att (k_address, k_size, k_start, nx_supported);
#if defined(__i386__) || defined(__x86_64__)
asm volatile ("cli");
#endif
- hf = (handover_func)((char *)k_address + h_offset + offset);
+ hf = (handover_func)((char *)k_start_address + h_offset + offset);
hf (grub_efi_image_handle, grub_efi_system_table, k_params);
return GRUB_ERR_BUG;
@@ -451,7 +529,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args,
grub_dprintf ("linux", "linux command line: '%s'\n", args);
- retval = grub_efi_linux_boot (addr, size, handover_offset,
+ retval = grub_efi_linux_boot (addr, size, 0, handover_offset,
(void *)addr, nx_supported);
/* Never reached... */
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
index abbf6b24f5..6c310d9879 100644
--- a/grub-core/loader/i386/efi/linux.c
+++ b/grub-core/loader/i386/efi/linux.c
@@ -41,6 +41,7 @@ static grub_command_t cmd_linuxefi, cmd_initrdefi;
struct grub_linuxefi_context {
void *kernel_mem;
grub_uint64_t kernel_size;
+ grub_uint64_t kernel_start;
grub_uint32_t handover_offset;
struct linux_kernel_params *params;
char *cmdline;
@@ -169,6 +170,7 @@ grub_linuxefi_boot (void *data)
return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem,
context->kernel_size,
+ context->kernel_start,
context->handover_offset,
context->params,
context->nx_supported);
@@ -527,7 +529,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
LOW_U32(kernel_mem));
lh->code32_start = LOW_U32(kernel_mem);
- grub_memcpy (kernel_mem, (char *)kernel + start, filelen - start);
+ grub_memcpy (kernel_mem, (char *)kernel, filelen);
lh->type_of_loader = 0x6;
grub_dprintf ("linux", "setting lh->type_of_loader = 0x%02x\n",
@@ -544,6 +546,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
goto fail;
context->kernel_mem = kernel_mem;
context->kernel_size = kernel_size;
+ context->kernel_start = start;
context->handover_offset = handover_offset;
context->params = params;
context->cmdline = cmdline;
diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
index 5b4e626c37..cd17be506a 100644
--- a/include/grub/efi/linux.h
+++ b/include/grub/efi/linux.h
@@ -27,6 +27,7 @@
grub_err_t
EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address,
grub_size_t kernel_size,
+ grub_size_t kernel_start,
grub_off_t handover_offset,
void *kernel_param, int nx_enabled);
@@ -38,4 +39,9 @@ EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr,
grub_err_t
EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required);
+grub_err_t
+EXPORT_FUNC(grub_efi_mem_set_att) (grub_addr_t k_address,
+ grub_size_t k_size,
+ grub_size_t k_start, int nx_supported);
+
#endif /* ! GRUB_EFI_LINUX_HEADER */

View file

@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leo Sandoval <lsandova@redhat.com>
Date: Fri, 11 Apr 2025 15:31:53 -0600
Subject: [PATCH] Include function name on debug and error print functions
Together with the line number, the debug log gives more context when
debugging.
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
---
grub-core/kern/err.c | 4 ++--
grub-core/kern/misc.c | 4 ++--
include/grub/err.h | 6 +++---
include/grub/misc.h | 5 +++--
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c
index aebfe0cf83..ba04b57fb7 100644
--- a/grub-core/kern/err.c
+++ b/grub-core/kern/err.c
@@ -38,14 +38,14 @@ static int grub_error_stack_assert;
#endif
grub_err_t
-grub_error (grub_err_t n, const char *file, const int line, const char *fmt, ...)
+grub_error (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...)
{
va_list ap;
int m;
grub_errno = n;
- m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%d:", file, line);
+ m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%s:%d:", file, function, line);
if (m < 0)
m = 0;
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index 69bd655f3d..541c7bb80c 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -248,7 +248,7 @@ grub_debug_enabled (const char * condition)
}
void
-grub_real_dprintf (const char *file, const int line, const char *condition,
+grub_real_dprintf (const char *file, const char *function, const int line, const char *condition,
const char *fmt, ...)
{
va_list args;
@@ -272,7 +272,7 @@ grub_real_dprintf (const char *file, const int line, const char *condition,
else
last_had_cr = 0;
#endif
- grub_printf ("%s:%d:%s: ", file, line, condition);
+ grub_printf ("%s:%s:%d:%s: ", file, function, line, condition);
va_start (args, fmt);
grub_vprintf (fmt, args);
va_end (args);
diff --git a/include/grub/err.h b/include/grub/err.h
index 7530f58b29..6379a6baf8 100644
--- a/include/grub/err.h
+++ b/include/grub/err.h
@@ -88,10 +88,10 @@ struct grub_error_saved
extern grub_err_t EXPORT_VAR(grub_errno);
extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG];
-grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const int line, const char *fmt, ...)
- __attribute__ ((format (GNU_PRINTF, 4, 5)));
+grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...)
+ __attribute__ ((format (GNU_PRINTF, 5, 6)));
-#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
+#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn));
diff --git a/include/grub/misc.h b/include/grub/misc.h
index 0429339ef3..2b9096a0b3 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -43,7 +43,7 @@
#define CONCAT(a, b) CONCAT_(a, b)
#endif
-#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__)
+#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__)
void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n);
char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src);
@@ -420,9 +420,10 @@ int EXPORT_FUNC(grub_puts_) (const char *s);
int EXPORT_FUNC(grub_debug_is_enabled) (void);
int EXPORT_FUNC(grub_debug_enabled) (const char *condition);
void EXPORT_FUNC(grub_real_dprintf) (const char *file,
+ const char *function,
const int line,
const char *condition,
- const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5)));
+ const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 5, 6)));
int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2)));
void EXPORT_FUNC(grub_qdprintf) (const char *condition,

View file

@ -0,0 +1,109 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alec Brown <alec.r.brown@oracle.com>
Date: Tue, 12 Aug 2025 03:45:32 +0000
Subject: [PATCH] kern/misc: Implement grub_strtok()
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>
---
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 69bd655f3d..c69fe7fb19 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -453,6 +453,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 0429339ef3..626f0c3535 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -134,6 +134,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 *

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alec Brown <alec.r.brown@oracle.com>
Date: Tue, 12 Aug 2025 03:45:34 +0000
Subject: [PATCH] util/misc.c: Change offset type for
grub_util_write_image_at()
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>
---
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 e9e0a6724a..bfce065583 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 0f928e5b49..6e16a68d9a 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%"

View file

@ -0,0 +1,342 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 12 Aug 2025 03:45:35 +0000
Subject: [PATCH] blsuki: Check for mounted /boot in emu
Irritatingly, BLS defines paths relative to the mountpoint of the
filesystem which contains its snippets, not / or any other fixed
location. So grub-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>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/Makefile.core.def | 3 ++
grub-core/commands/blsuki.c | 83 +++++++++++++++++++++++++++++++++++++----
grub-core/osdep/linux/getroot.c | 8 ++++
grub-core/osdep/unix/getroot.c | 4 +-
include/grub/emu/misc.h | 2 +-
5 files changed, 91 insertions(+), 9 deletions(-)
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 5e0e757344..885dd436de 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -376,6 +376,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 9440adb100..ed93a150b1 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/"
@@ -79,6 +86,34 @@ static grub_blsuki_entry_t *entries;
#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries)
+/*
+ * 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
+ static int separate_boot = -1;
+ 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 == 0)
+ 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.
@@ -526,7 +561,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;
@@ -548,6 +583,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)
{
@@ -582,7 +618,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");
@@ -605,6 +641,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");
@@ -630,7 +667,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");
@@ -643,6 +680,7 @@ bls_get_devicetree (grub_blsuki_entry_t *entry)
tmp = dt_cmd;
tmp = grub_stpcpy (dt_cmd, "devicetree ");
+ tmp = blsuki_update_boot_device (tmp);
tmp = grub_stpcpy (tmp, dt_path);
tmp = grub_stpcpy (tmp, "\n");
}
@@ -757,7 +795,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");
}
@@ -799,10 +841,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;
@@ -833,7 +878,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) - 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;
@@ -842,6 +895,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
@@ -851,6 +907,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,
@@ -892,15 +951,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 4bf386b590..8bf281c1d8 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -139,6 +139,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)
{
@@ -199,6 +200,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
@@ -700,6 +702,7 @@ out:
return ret;
}
+#ifdef GRUB_UTIL
static char *
get_mdadm_uuid (const char *os_dev)
{
@@ -757,6 +760,7 @@ out:
return name;
}
+#endif
static int
grub_util_is_imsm_or_ddf (const char *os_dev)
@@ -1096,6 +1100,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)
{
@@ -1198,6 +1203,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)
@@ -1214,6 +1220,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)
@@ -1270,6 +1277,7 @@ grub_util_get_grub_dev_os (const char *os_dev)
return grub_dev;
}
+#endif
static void *mp = NULL;
static void
diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c
index ee11b02fb6..62581ba601 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>
@@ -567,6 +567,7 @@ grub_guess_root_devices (const char *dir_in)
#endif
+#ifdef GRUB_UTIL
void
grub_util_pull_lvm_by_command (const char *os_dev)
{
@@ -663,6 +664,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 f3a712a8b2..59b8038c60 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);

View file

@ -0,0 +1,822 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alec Brown <alec.r.brown@oracle.com>
Date: Tue, 12 Aug 2025 03:45:36 +0000
Subject: [PATCH] blsuki: Add uki command to load Unified Kernel Image entries
A Unified Kernel Image (UKI) 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>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 36 +++-
grub-core/commands/blsuki.c | 476 ++++++++++++++++++++++++++++++++++++++++----
include/grub/menu.h | 2 +
3 files changed, 474 insertions(+), 40 deletions(-)
diff --git a/docs/grub.texi b/docs/grub.texi
index 336f8e87ed..531e847f32 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3344,7 +3344,8 @@ chain-loaded system, @pxref{drivemap}.
@subsection blsuki_save_default
If this variable is set, menu entries generated from BLS config files
-(@pxref{blscfg}) will be set as the default boot entry when selected.
+(@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot
+entry when selected.
@node check_appended_signatures
@subsection check_appended_signatures
@@ -4434,6 +4435,7 @@ you forget a command, you can run the command @command{help}
* true:: Do nothing, successfully
* trust:: Add public key to list of trusted keys
* trust_certificate:: Add an x509 certificate to the list of trusted certificates
+* uki:: Load Unified Kernel Image menu entries
* unset:: Unset an environment variable
@comment * vbeinfo:: List available video modes
* verify_appended:: Verify appended digital signature
@@ -6062,6 +6064,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) files 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 files are stored in the @file{/EFI/Linux} directory in the EFI
+system partition. If UKI files are stored elsewhere, the @option{--path} option can be
+used to check a different directory instead of the default location. If no UKI
+files are found while using the @option{--path} option, the @option{--enable-fallback} option can
+be used to check for files in the default location.
+
+The @option{--show-default} option allows the default boot entry to be added to the
+GRUB menu from the UKI files.
+
+The @option{--show-non-default} option allows non-default boot entries to be added to
+the GRUB menu from the UKI files.
+
+The @option{--entry} option allows specific boot entries to be added to the GRUB menu
+from the UKI files.
+
+The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options
+are used to filter which UKI files are added to the GRUB menu. If none are
+used, all files 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 ed93a150b1..cb00f936a7 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,14 +48,28 @@
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)
/*
* It is highly unlikely to ever receive a large amount of keyval pairs. A
* limit of 10000 is more than enough.
*/
#define BLSUKI_KEYVALS_MAX 10000
+/*
+ * The only sections we read are ".cmdline" and ".osrel". The ".cmdline"
+ * section has a size limit of 4096 and it would be very unlikely for the size
+ * of the ".osrel" section to be 5 times larger than 4096.
+ */
+#define UKI_SECTION_SIZE_MAX (5 * 4096)
+
+enum blsuki_cmd_type
+ {
+ BLSUKI_BLS_CMD,
+ BLSUKI_UKI_CMD,
+ };
static const struct grub_arg_option bls_opt[] =
{
@@ -61,6 +81,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;
@@ -71,6 +103,7 @@ struct read_entry_info
{
const char *devid;
const char *dirname;
+ enum blsuki_cmd_type cmd_type;
grub_file_t file;
};
@@ -82,7 +115,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)
@@ -181,7 +214,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)
@@ -310,20 +343,212 @@ bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry)
return err;
}
+#ifdef GRUB_MACHINE_EFI
+/*
+ * 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.
+ */
+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_DOS_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++)
+ {
+ 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;
+}
+
+/*
+ * 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[val_size - 1] == '\''))
+ {
+ val[val_size - 1] = '\0';
+ val++;
+ }
+
+ *val_ret = val;
+ break;
+ }
+
+ return key;
+}
+#endif
+
/*
* 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;
@@ -331,17 +556,31 @@ 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;
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (info->cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE;
+ }
+#endif
+
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;
@@ -373,7 +612,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);
@@ -389,7 +647,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 **
@@ -764,7 +1022,7 @@ bls_create_entry (grub_blsuki_entry_t *entry)
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);
+ grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, NULL, NULL, entry);
finish:
grub_free (linux_cmd);
@@ -776,6 +1034,70 @@ bls_create_entry (grub_blsuki_entry_t *entry)
grub_free (src);
}
+#ifdef GRUB_MACHINE_EFI
+/*
+ * This function puts together the section data received from the UKI and
+ * generates a new entry in 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;
+ bool blsuki_save_default;
+
+ /*
+ * 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)) != 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;
+
+ blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false);
+ src = grub_xasprintf ("%schainloader (%s)%s/%s%s%s\n",
+ blsuki_save_default ? "savedefault\n" : "",
+ entry->devid, entry->dirname,
+ entry->filename,
+ (options != NULL) ? " " : "",
+ (options != NULL) ? options : "");
+
+ grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, NULL, NULL, entry);
+
+ finish:
+ grub_free (argv);
+ grub_free (src);
+ grub_free (options);
+ grub_free (osrel);
+}
+#endif
+
/*
* 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
@@ -785,7 +1107,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;
@@ -795,10 +1117,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 = 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");
@@ -837,15 +1172,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;
@@ -862,6 +1198,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);
@@ -874,19 +1211,27 @@ 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) - 1;
+ if (cmd_type == BLSUKI_BLS_CMD)
+ cmd_dir = GRUB_BLS_CONFIG_PATH;
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+#endif
+
+ default_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
default_dir = grub_malloc (default_size);
if (default_dir == NULL)
return grub_errno;
tmp = blsuki_update_boot_device (default_dir);
- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+ tmp = grub_stpcpy (tmp, cmd_dir);
- blsuki_set_find_entry_info (info, default_dir, NULL);
+ blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type);
grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n",
read_entry_info.dirname, info->dirname);
fallback = true;
@@ -901,15 +1246,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,
@@ -918,12 +1265,26 @@ 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;
+ }
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ {
+ ext = ".efi";
+ ext_len = UKI_EXT_LEN;
+ }
+#endif
+
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)
@@ -952,19 +1313,26 @@ 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;
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ cmd_dir = GRUB_UKI_CONFIG_PATH;
+#endif
+
+ dir_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir);
default_dir = grub_malloc (dir_size);
if (default_dir == NULL)
return grub_errno;
tmp = blsuki_update_boot_device (default_dir);
- tmp = grub_stpcpy (tmp, GRUB_BLS_CONFIG_PATH);
+ tmp = grub_stpcpy (tmp, cmd_dir);
dir = default_dir;
}
- r = blsuki_set_find_entry_info (&info, dir, devid);
+ r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type);
if (r == GRUB_ERR_NONE)
- r = blsuki_find_entry (&info, enable_fallback);
+ r = blsuki_find_entry (&info, enable_fallback, cmd_type);
if (info.dev != NULL)
grub_device_close (info.dev);
@@ -1002,11 +1370,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;
@@ -1025,7 +1393,12 @@ 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);
+#ifdef GRUB_MACHINE_EFI
+ else if (cmd_type == BLSUKI_UKI_CMD)
+ uki_create_entry (entry);
+#endif
entry->visible = true;
}
@@ -1036,8 +1409,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;
@@ -1074,24 +1446,50 @@ grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
show_non_default = true;
}
- err = blsuki_load_entries (path, enable_fallback);
+ err = blsuki_load_entries (path, enable_fallback, cmd_type);
if (err != GRUB_ERR_NONE)
return err;
- return blsuki_create_entries (show_default, show_non_default, entry_id);
+ return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type);
+}
+
+static grub_err_t
+grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ return blsuki_cmd (ctxt, BLSUKI_BLS_CMD);
}
static grub_extcmd_t bls_cmd;
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ return blsuki_cmd (ctxt, BLSUKI_UKI_CMD);
+}
+
+static grub_extcmd_t uki_cmd;
+#endif
+
GRUB_MOD_INIT(blsuki)
{
bls_cmd = grub_register_extcmd ("blscfg", grub_cmd_blscfg, 0,
N_("[-p|--path] [-f|--enable-fallback] DIR [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
N_("Import Boot Loader Specification snippets."),
bls_opt);
+#ifdef GRUB_MACHINE_EFI
+ uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0,
+ N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"),
+ N_("Import Unified Kernel Images"), uki_opt);
+#endif
}
GRUB_MOD_FINI(blsuki)
{
grub_unregister_extcmd (bls_cmd);
+#ifdef GRUB_MACHINE_EFI
+ grub_unregister_extcmd (uki_cmd);
+#endif
}
diff --git a/include/grub/menu.h b/include/grub/menu.h
index 43a3c5809b..0e4978dc3b 100644
--- a/include/grub/menu.h
+++ b/include/grub/menu.h
@@ -39,6 +39,8 @@ struct grub_blsuki_entry
grub_size_t keyvals_size;
int nkeyvals;
char *filename;
+ char *dirname;
+ char *devid;
bool visible;
};
typedef struct grub_blsuki_entry grub_blsuki_entry_t;

View file

@ -0,0 +1,168 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Fri, 15 Nov 2024 15:34:29 +0800
Subject: [PATCH] posix_wrap: Tweaks in preparation for libtasn1
Cc: Vladimir Serbinenko <phcoder@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Gary Lin <glin@suse.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
---
grub-core/lib/posix_wrap/c-ctype.h | 114 +++++++++++++++++++++++++++++++++++++
grub-core/lib/posix_wrap/string.h | 21 +++++++
2 files changed, 135 insertions(+)
create mode 100644 grub-core/lib/posix_wrap/c-ctype.h
diff --git a/grub-core/lib/posix_wrap/c-ctype.h b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0000000000..5f8fc8ce3f
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2024 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/>.
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H 1
+
+#include <grub/misc.h>
+
+static inline bool
+c_isspace (int c)
+{
+ return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+ return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+ return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+ return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+ return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+ return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+ return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+ return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+ return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+ return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+ return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+ return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+ return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+ return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+ return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/string.h b/grub-core/lib/posix_wrap/string.h
index 1adb450b5a..d3e400d500 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
return grub_memchr (s, c, n);
}
+static inline char *
+strncat (char *dest, const char *src, grub_size_t n)
+{
+ const char *end;
+ char *str = dest;
+ grub_size_t src_len;
+
+ dest += grub_strlen (dest);
+
+ end = grub_memchr (src, '\0', n);
+ if (end != NULL)
+ src_len = (grub_size_t) (end - src);
+ else
+ src_len = n;
+
+ dest[src_len] = '\0';
+ grub_memcpy (dest, src, src_len);
+
+ return str;
+}
+
#define memcmp grub_memcmp
#define memcpy grub_memcpy
#define memmove grub_memmove

View file

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leo Sandoval <lsandova@redhat.com>
Date: Thu, 13 Nov 2025 11:57:52 -0600
Subject: [PATCH] blsuki: do not register blscfg command
The blscfg command is already defined on commands/blscfg.c so do not
register it on blsuki.c. Also, the 'uki' command is only intended for
EFI system so remove the other platforms from the module.
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
---
grub-core/Makefile.core.def | 2 --
grub-core/commands/blsuki.c | 4 ----
2 files changed, 6 deletions(-)
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 885dd436de..a545ee430b 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -893,8 +893,6 @@ module = {
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)';
};
diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c
index cb00f936a7..bcd0114d85 100644
--- a/grub-core/commands/blsuki.c
+++ b/grub-core/commands/blsuki.c
@@ -1475,10 +1475,6 @@ static grub_extcmd_t uki_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);
#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"),

View file

@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:32 +0800
Subject: [PATCH] misc: add z length modifier support
Add support for the 'z' length modifier in the printf code. This allows
printing of size_t and ssize_t values using %zu, %zd and related
formats. The parser maps 'z' to the correct integer width based on
sizeof (size_t).
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/misc.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index c1c77c300c..2568a5489b 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -30,6 +30,7 @@
#if DEBUG_WITH_TIMESTAMPS
#include <grub/time.h>
#endif
+#include <stddef.h>
union printf_arg
{
@@ -883,6 +884,9 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long));
COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *)
|| sizeof (int) == sizeof (void *));
+ COMPILE_TIME_ASSERT (sizeof (size_t) == sizeof (unsigned)
+ || sizeof (size_t) == sizeof (unsigned long)
+ || sizeof (size_t) == sizeof (unsigned long long));
fmt = fmt0;
while ((c = *fmt++) != 0)
@@ -917,11 +921,17 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
fmt++;
c = *fmt++;
+ if (c == 'z')
+ {
+ c = *fmt++;
+ goto do_count;
+ }
if (c == 'l')
c = *fmt++;
if (c == 'l')
c = *fmt++;
+ do_count:
switch (c)
{
case 'p':
@@ -1018,6 +1028,14 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args,
continue;
}
+ if (c == 'z')
+ {
+ c = *fmt++;
+ if (sizeof (size_t) == sizeof (unsigned long))
+ longfmt = 1;
+ else if (sizeof (size_t) == sizeof (unsigned long long))
+ longfmt = 2;
+ }
if (c == 'l')
{
c = *fmt++;
@@ -1194,6 +1212,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0,
}
c = *fmt++;
+ if (c == 'z')
+ c = *fmt++;
if (c == 'l')
c = *fmt++;
if (c == 'l')

View file

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:33 +0800
Subject: [PATCH] tests: add z modifier printf tests
Add unit tests for %zd, %zu and %zx to verify size_t and ssize_t
formatting matches system snprintf.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
tests/printf_unit_test.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c
index 098c29fd9c..f2de187b03 100644
--- a/tests/printf_unit_test.c
+++ b/tests/printf_unit_test.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <grub/test.h>
#include <grub/misc.h>
+#include <stdint.h>
#define MSG "printf test failed: %s, %s", real, expected
@@ -73,6 +74,15 @@ printf_test (void)
grub_snprintf (real, sizeof (real), "%%0%dd ", 1);
snprintf (expected, sizeof (expected), "%%0%dd ", 1);
grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42);
+ snprintf (expected, sizeof (expected), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42);
+ snprintf (expected, sizeof (expected), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
+ grub_snprintf (real, sizeof (real), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU);
+ snprintf (expected, sizeof (expected), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU);
+ grub_test_assert (strcmp (real, expected) == 0, MSG);
}
GRUB_UNIT_TEST ("printf_unit_test", printf_test);

View file

@ -0,0 +1,219 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:34 +0800
Subject: [PATCH] util/grub-editenv: add fs_envblk open helper
This patch adds the logic to locate and open an environment block that
is stored in a reserved area on the device. It introduces the function
fs_envblk_open() together with helper routines to read the block pointed
to by the env_block variable, and to create the block on disk when it
does not exist yet. When a block is created, the code records its
location inside the file based envblk by setting env_block in block list
syntax of offset plus size in sectors.
The env_block variable acts as a link from the file envblk to the raw
disk region so that later runs of grub-editenv can follow it and access
the external block. The helper is exposed through a small ops table
attached to fs_envblk so that later patches can call
fs_envblk->ops->open() without touching core code again. At this stage
variables are still stored in the file envblk and no redirection has
been applied.
In relation to this, the fs_envblk_spec table defines the file-system
specific layout of the reserved raw blocks used for environment storage.
It is prepared to facilitate integration in grub-editenv, with Btrfs to
be added in the future once its reserved area is defined.
An fs_envblk_init() helper is added to prepare it for using the ops with
its associated data context if the feature is available. It is not used
yet, but will be used later when a filesystem and its device are probed
to initialize the fs_envblk handle and enable access to the feature.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
---
util/grub-editenv.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 170 insertions(+)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index 948eec8a11..a544452e78 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -123,6 +123,176 @@ block, use `rm %s'."),
NULL, help_filter, NULL
};
+struct fs_envblk_spec {
+ const char *fs_name;
+ off_t offset;
+ size_t size;
+};
+typedef struct fs_envblk_spec fs_envblk_spec_t;
+
+static grub_envblk_t fs_envblk_open (grub_envblk_t envblk);
+
+struct fs_envblk_ops {
+ grub_envblk_t (*open) (grub_envblk_t);
+};
+typedef struct fs_envblk_ops fs_envblk_ops_t;
+
+struct fs_envblk {
+ fs_envblk_spec_t *spec;
+ fs_envblk_ops_t *ops;
+ const char *dev;
+};
+typedef struct fs_envblk *fs_envblk_t;
+
+static fs_envblk_ops_t fs_envblk_ops = {
+ .open = fs_envblk_open
+};
+
+/*
+ * fs_envblk_spec describes the file-system specific layout of reserved raw
+ * blocks used as environment blocks.
+ */
+static fs_envblk_spec_t fs_envblk_spec[] = {
+ { NULL, 0, 0 }
+};
+
+static fs_envblk_t fs_envblk = NULL;
+
+static void __attribute__ ((unused))
+fs_envblk_init (const char *fs_name, const char *dev)
+{
+ fs_envblk_spec_t *p;
+
+ if (fs_name == NULL || dev == NULL)
+ return;
+
+ for (p = fs_envblk_spec; p->fs_name != NULL; p++)
+ {
+ if (strcmp (fs_name, p->fs_name) == 0)
+ {
+ if (fs_envblk == NULL)
+ fs_envblk = xmalloc (sizeof (*fs_envblk));
+ fs_envblk->spec = p;
+ fs_envblk->dev = xstrdup (dev);
+ fs_envblk->ops = &fs_envblk_ops;
+ break;
+ }
+ }
+}
+
+static int
+read_env_block_var (const char *varname, const char *value, void *hook_data)
+{
+ grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data;
+ off_t off;
+ size_t sz;
+ char *p, *buf;
+ FILE *fp;
+
+ if (p_envblk == NULL || fs_envblk == NULL)
+ return 1;
+
+ if (strcmp (varname, "env_block") != 0)
+ return 0;
+
+ off = strtol (value, &p, 10);
+ if (*p == '+')
+ sz = strtol (p + 1, &p, 10);
+ else
+ return 0;
+
+ if (*p != '\0' || sz == 0)
+ return 0;
+
+ off <<= GRUB_DISK_SECTOR_BITS;
+ sz <<= GRUB_DISK_SECTOR_BITS;
+
+ fp = grub_util_fopen (fs_envblk->dev, "rb");
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ if (fseek (fp, off, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ buf = xmalloc (sz);
+ if ((fread (buf, 1, sz, fp)) != sz)
+ grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno));
+
+ fclose (fp);
+
+ *p_envblk = grub_envblk_open (buf, sz);
+
+ return 1;
+}
+
+static void
+create_env_on_block (void)
+{
+ FILE *fp;
+ char *buf;
+ const char *device;
+ off_t offset;
+ size_t size;
+
+ if (fs_envblk == NULL)
+ return;
+
+ device = fs_envblk->dev;
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ fp = grub_util_fopen (device, "r+b");
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno));
+
+ buf = xmalloc (size);
+ memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1);
+ memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1);
+
+ if (fseek (fp, offset, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno));
+
+ if (fwrite (buf, 1, size, fp) != size)
+ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno));
+
+ grub_util_file_sync (fp);
+ free (buf);
+ fclose (fp);
+}
+
+static grub_envblk_t
+fs_envblk_open (grub_envblk_t envblk)
+{
+ grub_envblk_t envblk_on_block = NULL;
+ char *val;
+ off_t offset;
+ size_t size;
+
+ if (envblk == NULL)
+ return NULL;
+
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
+
+ if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size)
+ return envblk_on_block;
+
+ create_env_on_block ();
+
+ offset = offset >> GRUB_DISK_SECTOR_BITS;
+ size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS;
+
+ val = xasprintf ("%lld+%zu", (long long) offset, size);
+ if (grub_envblk_set (envblk, "env_block", val) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
+ free (val);
+
+ return envblk_on_block;
+}
+
static grub_envblk_t
open_envblk_file (const char *name)
{

View file

@ -0,0 +1,91 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:35 +0800
Subject: [PATCH] util/grub-editenv: add fs_envblk write helper
This patch adds the function fs_envblk_write to update the reserved
environment block on disk. The helper takes an in memory envblk buffer
and writes it back to the device at the location defined by the
fs_envblk specification. It performs size checks and uses file sync to
ensure that the updated data is flushed.
The helper is also added into the fs_envblk ops table, together with the
open helper from the previous patch. With this change the basic input
and output path for an external environment block is complete. The
choice of which variables should be written externally will be handled
by later patches.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/grub-editenv.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index a544452e78..f4b69bb19f 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -131,9 +131,11 @@ struct fs_envblk_spec {
typedef struct fs_envblk_spec fs_envblk_spec_t;
static grub_envblk_t fs_envblk_open (grub_envblk_t envblk);
+static void fs_envblk_write (grub_envblk_t envblk);
struct fs_envblk_ops {
grub_envblk_t (*open) (grub_envblk_t);
+ void (*write) (grub_envblk_t);
};
typedef struct fs_envblk_ops fs_envblk_ops_t;
@@ -145,7 +147,8 @@ struct fs_envblk {
typedef struct fs_envblk *fs_envblk_t;
static fs_envblk_ops_t fs_envblk_ops = {
- .open = fs_envblk_open
+ .open = fs_envblk_open,
+ .write = fs_envblk_write
};
/*
@@ -381,6 +384,39 @@ write_envblk (const char *name, grub_envblk_t envblk)
fclose (fp);
}
+static void
+fs_envblk_write (grub_envblk_t envblk)
+{
+ FILE *fp;
+ const char *device;
+ off_t offset;
+ size_t size;
+
+ if (envblk == NULL)
+ return;
+
+ device = fs_envblk->dev;
+ offset = fs_envblk->spec->offset;
+ size = fs_envblk->spec->size;
+
+ if (grub_envblk_size (envblk) > size)
+ grub_util_error ("%s", _("environment block too small"));
+
+ fp = grub_util_fopen (device, "r+b");
+
+ if (fp == NULL)
+ grub_util_error (_("cannot open `%s': %s"), device, strerror (errno));
+
+ if (fseek (fp, offset, SEEK_SET) < 0)
+ grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno));
+
+ if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk))
+ grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno));
+
+ grub_util_file_sync (fp);
+ fclose (fp);
+}
+
static void
set_variables (const char *name, int argc, char *argv[])
{

View file

@ -0,0 +1,118 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:36 +0800
Subject: [PATCH] util/grub-editenv: wire set_variables to optional fs_envblk
This patch changes set_variables() so that it can use an external
environment block when one is present. The variable next_entry is
written into the external block, env_block is treated as read only, and
all other variables are written into the normal file based envblk.
A cleanup step is added to handle cases where GRUB at runtime writes
variables into the external block because file based updates are not
safe on a copy on write filesystem such as Btrfs. For example, the
savedefault command can update saved_entry, and on Btrfs GRUB will place
that update in the external block instead of the file envblk. If an
older copy remains in the external block, it would override the newer
value from the file envblk when GRUB first loads the file and then
applies the external block on top of it. To avoid this, whenever a
variable is updated in the file envblk, any same named key in the
external block is deleted.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/grub-editenv.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 56 insertions(+), 2 deletions(-)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index f4b69bb19f..be844bf902 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -417,12 +417,35 @@ fs_envblk_write (grub_envblk_t envblk)
fclose (fp);
}
+struct var_lookup_ctx {
+ const char *varname;
+ bool found;
+};
+typedef struct var_lookup_ctx var_lookup_ctx_t;
+
+static int
+var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data)
+{
+ var_lookup_ctx_t *ctx = (var_lookup_ctx_t *) hook_data;
+
+ if (grub_strcmp (ctx->varname, varname) == 0)
+ {
+ ctx->found = true;
+ return 1;
+ }
+ return 0;
+}
+
static void
set_variables (const char *name, int argc, char *argv[])
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+ if (fs_envblk != NULL)
+ envblk_on_block = fs_envblk->ops->open (envblk);
+
while (argc)
{
char *p;
@@ -433,15 +456,46 @@ set_variables (const char *name, int argc, char *argv[])
*(p++) = 0;
- if (! grub_envblk_set (envblk, argv[0], p))
- grub_util_error ("%s", _("environment block too small"));
+ if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL)
+ {
+ if (grub_envblk_set (envblk_on_block, argv[0], p) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+ goto next;
+ }
+ if (strcmp (argv[0], "env_block") == 0)
+ {
+ grub_util_warn (_("can't set env_block as it's read-only"));
+ goto next;
+ }
+
+ if (grub_envblk_set (envblk, argv[0], p) == 0)
+ grub_util_error ("%s", _("environment block too small"));
+
+ if (envblk_on_block != NULL)
+ {
+ var_lookup_ctx_t ctx = {
+ .varname = argv[0],
+ .found = false
+ };
+
+ grub_envblk_iterate (envblk_on_block, &ctx, var_lookup_iter);
+ if (ctx.found == true)
+ grub_envblk_delete (envblk_on_block, argv[0]);
+ }
+ next:
argc--;
argv++;
}
write_envblk (name, envblk);
grub_envblk_close (envblk);
+
+ if (envblk_on_block != NULL)
+ {
+ fs_envblk->ops->write (envblk_on_block);
+ grub_envblk_close (envblk_on_block);
+ }
}
static void

View file

@ -0,0 +1,56 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:37 +0800
Subject: [PATCH] util/grub-editenv: wire unset_variables to optional fs_envblk
This patch updates unset_variables() so that removals are also applied
to the external environment block when it is present. The code opens the
external block, deletes the same named keys there, and then writes the
external block back using fs_envblk_write(). The file based envblk is
still updated and written as before.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/grub-editenv.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index be844bf902..7ff1da9b40 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -502,18 +502,32 @@ static void
unset_variables (const char *name, int argc, char *argv[])
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+
+ if (fs_envblk != NULL)
+ envblk_on_block = fs_envblk->ops->open (envblk);
+
while (argc)
{
grub_envblk_delete (envblk, argv[0]);
+ if (envblk_on_block != NULL)
+ grub_envblk_delete (envblk_on_block, argv[0]);
+
argc--;
argv++;
}
write_envblk (name, envblk);
grub_envblk_close (envblk);
+
+ if (envblk_on_block != NULL)
+ {
+ fs_envblk->ops->write (envblk_on_block);
+ grub_envblk_close (envblk_on_block);
+ }
}
struct get_int_value_params {

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:38 +0800
Subject: [PATCH] util/grub-editenv: wire list_variables to optional fs_envblk
This patch updates list_variables() so that it also prints entries from
the external environment block when one is present. The function first
lists all variables from the file based envblk, then iterates over the
external envblk and prints those as well.
The output format remains the same as before. The change makes it
possible to inspect variables regardless of whether they are stored in
the file envblk or in the reserved block.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/grub-editenv.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index 7ff1da9b40..170d8f139a 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -358,10 +358,17 @@ static void
list_variables (const char *name)
{
grub_envblk_t envblk;
+ grub_envblk_t envblk_on_block = NULL;
envblk = open_envblk_file (name);
+ grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var);
grub_envblk_iterate (envblk, NULL, print_var);
grub_envblk_close (envblk);
+ if (envblk_on_block != NULL)
+ {
+ grub_envblk_iterate (envblk_on_block, NULL, print_var);
+ grub_envblk_close (envblk_on_block);
+ }
}
static void

View file

@ -0,0 +1,176 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:39 +0800
Subject: [PATCH] util/grub-editenv: add probe call for external envblk
This patch adds the probe_fs_envblk() function to identify the root
filesystem and invoke fs_envblk_init() with the probed filesystem type
and device. This checks if the feature is available and initializes the
handle, fs_envblk, to access the external environment block. It avoids
configurations with diskfilter or cryptodisk where filesystem blocks may
be remapped or encrypted.
The probe is only invoked when grub-editenv is working on the default
environment file path. This restriction ensures that probing and
possible raw device access are not triggered for arbitrary user supplied
paths, but only for the standard grubenv file. In that case the code
checks if the filename equals DEFAULT_ENVBLK_PATH and then calls
probe_fs_envblk with fs_envblk_spec. The result is stored in the global
fs_envblk handle. At this stage the external environment block is only
detected and recorded, and the behavior of grub-editenv is unchanged.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
---
util/grub-editenv.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 113 insertions(+), 2 deletions(-)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index 170d8f139a..201dc7ccd4 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -23,8 +23,11 @@
#include <grub/util/misc.h>
#include <grub/lib/envblk.h>
#include <grub/i18n.h>
-#include <grub/emu/hostfile.h>
+#include <grub/emu/hostdisk.h>
#include <grub/util/install.h>
+#include <grub/emu/getroot.h>
+#include <grub/fs.h>
+#include <grub/crypto.h>
#include <stdio.h>
#include <unistd.h>
@@ -161,7 +164,7 @@ static fs_envblk_spec_t fs_envblk_spec[] = {
static fs_envblk_t fs_envblk = NULL;
-static void __attribute__ ((unused))
+static void
fs_envblk_init (const char *fs_name, const char *dev)
{
fs_envblk_spec_t *p;
@@ -582,6 +585,111 @@ incr_variables (const char *name, int argc, char *argv[])
grub_envblk_close (envblk);
}
+static bool
+is_abstraction (grub_device_t dev)
+{
+ if (dev == NULL || dev->disk == NULL)
+ return false;
+
+ if (dev->disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID ||
+ dev->disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ return true;
+
+ return false;
+}
+
+static void
+probe_fs_envblk (fs_envblk_spec_t *spec)
+{
+ char **grub_devices = NULL;
+ char **curdev, **curdrive;
+ size_t ndev = 0;
+ char **grub_drives = NULL;
+ grub_device_t grub_dev = NULL;
+ grub_fs_t grub_fs = NULL;
+ bool have_abstraction = false;
+
+ grub_util_biosdisk_init (DEFAULT_DEVICE_MAP);
+ grub_init_all ();
+ grub_gcry_init_all ();
+
+ grub_lvm_fini ();
+ grub_mdraid09_fini ();
+ grub_mdraid1x_fini ();
+ grub_diskfilter_fini ();
+ grub_diskfilter_init ();
+ grub_mdraid09_init ();
+ grub_mdraid1x_init ();
+ grub_lvm_init ();
+
+ grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY);
+
+ if (grub_devices == NULL || grub_devices[0] == NULL)
+ {
+ grub_util_warn (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY);
+ goto cleanup;
+ }
+
+ for (curdev = grub_devices; *curdev != NULL; curdev++, ndev++)
+ grub_util_pull_device (*curdev);
+
+ grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0]));
+
+ for (curdev = grub_devices, curdrive = grub_drives; *curdev != NULL; curdev++,
+ curdrive++)
+ {
+ *curdrive = grub_util_get_grub_dev (*curdev);
+ if (*curdrive == NULL)
+ {
+ grub_util_warn (_("cannot find a GRUB drive for %s. Check your device.map"),
+ *curdev);
+ goto cleanup;
+ }
+ }
+ *curdrive = NULL;
+
+ grub_dev = grub_device_open (grub_drives[0]);
+ if (grub_dev == NULL)
+ {
+ grub_util_warn (_("cannot open device %s: %s"), grub_drives[0], grub_errmsg);
+ grub_errno = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+
+ grub_fs = grub_fs_probe (grub_dev);
+ if (grub_fs == NULL)
+ {
+ grub_util_warn (_("cannot probe fs for %s: %s"), grub_drives[0], grub_errmsg);
+ grub_errno = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+
+ have_abstraction = is_abstraction (grub_dev);
+ for (curdrive = grub_drives + 1; *curdrive != NULL && have_abstraction == false; curdrive++)
+ {
+ grub_device_t dev = grub_device_open (*curdrive);
+
+ if (dev == NULL)
+ continue;
+ have_abstraction = is_abstraction (dev);
+ grub_device_close (dev);
+ }
+
+ if (have_abstraction == false)
+ fs_envblk_init (grub_fs->name, grub_devices[0]);
+
+ cleanup:
+ if (grub_devices != NULL)
+ for (curdev = grub_devices; *curdev != NULL; curdev++)
+ free (*curdev);
+ free (grub_devices);
+ free (grub_drives);
+ grub_device_close (grub_dev);
+ grub_gcry_fini_all ();
+ grub_fini_all ();
+ grub_util_biosdisk_fini ();
+}
+
int
main (int argc, char *argv[])
{
@@ -613,6 +721,9 @@ main (int argc, char *argv[])
command = argv[curindex++];
}
+ if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0)
+ probe_fs_envblk (fs_envblk_spec);
+
if (strcmp (command, "create") == 0)
grub_util_create_envblk_file (filename);
else if (strcmp (command, "list") == 0)

View file

@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:40 +0800
Subject: [PATCH] btrfs: add environment block to reserved header area
This patch reserves space for the GRUB environment block inside the
Btrfs header. The block is placed at an offset of GRUB_ENV_BTRFS_OFFSET
, 256 KiB from the start of the device, and occupies one sector. To
protect the space, overflow guard sectors are placed before and after
the reserved block.
The Btrfs header already defines regions for bootloader use. By adding
this entry, GRUB gains a fixed and safe location to store the
environment block without conflicting with other structures in the
header.
Add Btrfs and its reserved area information to the fs_envblk_spec table.
With the groundworks done in previous patches, the function is now
complete and working in grub-editenv.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
---
grub-core/fs/btrfs.c | 10 +++++++++-
include/grub/fs.h | 2 ++
util/grub-editenv.c | 9 ++++++++-
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 3228e17886..261c413e9f 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -2650,11 +2650,16 @@ struct embed_region {
* https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
* The first 1 MiB on each device is unused with the exception of primary
* superblock that is on the offset 64 KiB and spans 4 KiB.
+ *
+ * Note: If this table is modified, also update
+ * util/grub-editenv.c::fs_envblk_spec, which describes the file-system
+ * specific layout of reserved raw blocks used as environment blocks so that
+ * both stay consistent.
*/
static const struct {
struct embed_region available;
- struct embed_region used[6];
+ struct embed_region used[9];
} btrfs_head = {
.available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */
.used = {
@@ -2662,6 +2667,9 @@ static const struct {
{GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */
{GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */
{GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) - 1, 1}, /* Overflow guard. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS), 1}, /* Environment Block. */
+ {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) + 1, 1}, /* Overflow guard. */
{GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */
{0, 0} /* Array terminator. */
}
diff --git a/include/grub/fs.h b/include/grub/fs.h
index df4c93b16f..89e4d2b9bc 100644
--- a/include/grub/fs.h
+++ b/include/grub/fs.h
@@ -132,4 +132,6 @@ grub_fs_unregister (grub_fs_t fs)
grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device);
+#define GRUB_ENV_BTRFS_OFFSET (256 * 1024)
+
#endif /* ! GRUB_FS_HEADER */
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index 201dc7ccd4..3c3f9d09f3 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -156,9 +156,16 @@ static fs_envblk_ops_t fs_envblk_ops = {
/*
* fs_envblk_spec describes the file-system specific layout of reserved raw
- * blocks used as environment blocks.
+ * blocks used as environment blocks. At present only Btrfs is supported. Other
+ * file-systems may be added if they provide a similar facility and avoid the
+ * limitation of writing to COW.
+ *
+ * Note: If this table is modified, also update
+ * grub-core/fs/btrfs.c::btrfs_head, which defines the layout in the Btrfs
+ * header and exports GRUB_ENV_BTRFS_OFFSET, so that both stay consistent.
*/
static fs_envblk_spec_t fs_envblk_spec[] = {
+ { "btrfs", GRUB_ENV_BTRFS_OFFSET, GRUB_DISK_SECTOR_SIZE },
{ NULL, 0, 0 }
};

View file

@ -0,0 +1,82 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:41 +0800
Subject: [PATCH] 00_header.in: wire grub.cfg to use env_block when present
This patch extends the generated grub.cfg so that it can use the
external environment block when the variable env_block is defined.
During boot, if env_block is set, grub.cfg builds a device path for it,
exports the variable, and then loads its contents in addition to the
normal grubenv file.
When GRUB writes variables such as next_entry or saved_entry, the save
commands are changed to write into env_block if it is set, and to fall
back to the grubenv file otherwise. In this way the external environment
block is used automatically, and existing commands like savedefault or
save_env do not need to change.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
util/grub.d/00_header.in | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index 3e1b77265d..a5d43929b8 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -58,6 +58,13 @@ if [ -f \${config_directory}/grubenv ]; then
elif [ -s \$prefix/grubenv ]; then
load_env
fi
+
+if [ "\${env_block}" ] ; then
+ set env_block="(\${root})\${env_block}"
+ export env_block
+ load_env -f "\${env_block}"
+fi
+
EOF
if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then
cat <<EOF
@@ -66,7 +73,11 @@ if cmostest $GRUB_BUTTON_CMOS_ADDRESS ; then
elif [ "\${next_entry}" ] ; then
set default="\${next_entry}"
set next_entry=
- save_env next_entry
+ if [ "\${env_block}" ] ; then
+ save_env -f "\${env_block}" next_entry
+ else
+ save_env next_entry
+ fi
set boot_once=true
else
set default="${GRUB_DEFAULT}"
@@ -77,7 +88,11 @@ else
if [ "\${next_entry}" ] ; then
set default="\${next_entry}"
set next_entry=
- save_env next_entry
+ if [ "\${env_block}" ] ; then
+ save_env -f "\${env_block}" next_entry
+ else
+ save_env next_entry
+ fi
set boot_once=true
else
set default="${GRUB_DEFAULT}"
@@ -105,7 +120,12 @@ fi
function savedefault {
if [ -z "\${boot_once}" ]; then
saved_entry="\${chosen}"
- save_env saved_entry
+ if [ "\${env_block}" ] ; then
+ save_env -f "\${env_block}" saved_entry
+ else
+ save_env saved_entry
+ fi
+
fi
}

View file

@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang via Grub-devel <grub-devel@gnu.org>
Date: Fri, 17 Oct 2025 17:01:43 +0800
Subject: [PATCH] btrfs: update doc link for bootloader support
The old wiki link is obsolete and no longer updated, change it to the
current documentation.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/fs/btrfs.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 261c413e9f..2222c57abd 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -2647,7 +2647,8 @@ struct embed_region {
};
/*
- * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT
+ * https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#man-btrfs5-bootloader-support
+ * or invoke "man 5 btrfs" and visit the "bootloader support" subsection.
* The first 1 MiB on each device is unused with the exception of primary
* superblock that is on the offset 64 KiB and spans 4 KiB.
*

View file

@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leo Sandoval <lsandova@redhat.com>
Date: Mon, 27 Oct 2025 11:18:45 -0600
Subject: [PATCH] docs: add Btrfs env block and special env vars
Update grub.texi to describe the external environment block in the
reserved area of Btrfs header used for grub-reboot and savedefault,
and add a section documenting the saved_entry, next_entry, and
env_block variables.
Signed-off-by: Michael Chang <mchang@suse.com>
Reviewed-by: Neal Gompa <ngompa13@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
docs/grub.texi | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/docs/grub.texi b/docs/grub.texi
index 531e847f32..d669972337 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3265,6 +3265,7 @@ GRUB. Others may be used freely in GRUB configuration files.
@menu
* Special environment variables::
* Environment block::
+* Special environment block variables::
@end menu
@@ -3881,9 +3882,63 @@ For safety reasons, this storage is only available when installed on a plain
disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and
using BIOS or EFI functions (no ATA, USB or IEEE1275).
+On Btrfs filesystems, a reserved area in the filesystem header may be used to
+store the environment block. This static block avoids the problems of updating
+a normal file on a copy-on-write filesystem, where writing raw block is not
+stable and requires metadata update. The reserved area provides a fixed
+location that GRUB can update directly, allowing commands such as
+@command{grub-reboot} and @samp{GRUB_SAVEDEFAULT} to function correctly on
+Btrfs volumes.
+
@command{grub2-mkconfig} uses this facility to implement
@samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}).
+@node Special environment block variables
+@section Special environment block variables
+
+These special variables are usually written to the environment block
+(@pxref{Environment block}) to customize the behavior of @file{grub.cfg}
+generated by @command{grub-mkconfig}.
+
+@menu
+* saved_entry::
+* next_entry::
+* env_block::
+@end menu
+
+@node saved_entry
+@subsection saved_entry
+
+The @var{saved_entry} variable sets the default boot entry in @file{grub.cfg}
+created by @command{grub-mkconfig}. It can be set with
+@command{grub-set-default} to choose a default entry, or at runtime with the
+@code{savedefault} function in grub.cfg to save the current entry as the new
+default. This may require write access by GRUB.
+
+@node next_entry
+@subsection next_entry
+
+The @var{next_entry} variable sets the boot entry for the next boot only. After
+it is used, GRUB clears the value so it is not reused. This requires write
+access to the environment block (@pxref{Environment block}) at runtime. The
+@command{grub-reboot} command is usually used instead of changing this variable
+directly.
+
+@node env_block
+@subsection env_block
+
+If the filesystem is Btrfs and the disk is not an abstracted device such as
+LVM, RAID, or encryption, the reserved space in the Btrfs header can be used as
+the environment block (@pxref{Environment block}). This provides a fixed raw
+block that GRUB can reliably write to. The @var{env_block} records this
+location in GRUB blocklist syntax (@pxref{Block list syntax}) so that
+@command{grub-editenv} and @file{grub.cfg} know how to access and use the
+external raw block.
+
+This variable is initialized when @file{grubenv} is first created by
+@command{grub-editenv} and is treated as read-only to avoid being overwritten
+with an unpredictable value.
+
@node Modules
@chapter Modules

View file

@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Date: Thu, 29 Aug 2024 13:01:02 +0200
Subject: [PATCH] kern/fs: Honour file->read_hook() in grub_fs_blocklist_read()
Unlike files accessed via a normal file system, the file->read_hook() is
not honoured when using blocklist notation.
This means that when trying to use a dedicated, 1 KiB, raw partition
for the environment block and hence does something like
save_env --file=(hd0,gpt9)0+2 X Y Z
this fails with "sparse file not allowed", which is rather unexpected,
as I've explicitly said exactly which blocks should be used. Adding
a little debugging reveals that grub_file_size(file) is 1024 as expected,
but total_length is 0, simply because the callback was never invoked, so
blocklists is an empty list.
Fix that by honouring the ->read_hook() set by the caller, also when
a "file" is specified with blocklist notation.
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Reviewed-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/fs.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c
index f25bace620..9f6f4ef9da 100644
--- a/grub-core/kern/fs.c
+++ b/grub-core/kern/fs.c
@@ -216,12 +216,15 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
grub_disk_addr_t sector;
grub_off_t offset;
grub_ssize_t ret = 0;
+ grub_disk_t disk = file->device->disk;
if (len > file->size - file->offset)
len = file->size - file->offset;
sector = (file->offset >> GRUB_DISK_SECTOR_BITS);
offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1));
+ disk->read_hook = file->read_hook;
+ disk->read_hook_data = file->read_hook_data;
for (p = file->data; p->length && len > 0; p++)
{
if (sector < p->length)
@@ -233,9 +236,12 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
>> GRUB_DISK_SECTOR_BITS) > p->length - sector)
size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset;
- if (grub_disk_read (file->device->disk, p->offset + sector, offset,
+ if (grub_disk_read (disk, p->offset + sector, offset,
size, buf) != GRUB_ERR_NONE)
- return -1;
+ {
+ ret = -1;
+ break;
+ }
ret += size;
len -= size;
@@ -245,6 +251,8 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
else
sector -= p->length;
}
+ disk->read_hook = NULL;
+ disk->read_hook_data = NULL;
return ret;
}

View file

@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Neal Gompa <ngompa@fedoraproject.org>
Date: Wed, 26 Nov 2025 08:20:26 -0500
Subject: [PATCH] util/grub.d/00_header.in: Prefer $dev instead of $root for
$env_block
In UEFI environments where GRUB loads a stub grub.cfg on the EFI
System Partition (ESP) that points to the real one on the operating
system volume, the $root value is set to the ESP instead of the
operating system volume.
This is particularly problematic for setups where the operating
system boot data is on a Btrfs volume and the external environment
block in the Btrfs bootloader space is used for grubenv data.
Using $dev in its place resolves this problem, since the stub
grub.cfg populates this variable. Use it if it exists, and
fall back to $root accordingly.
This is unlikely to be upstreamable, as it depends on the method
Fedora Linux and derivatives use to load the GRUB configuration.
Signed-off-by: Neal Gompa <ngompa@fedoraproject.org>
---
util/grub.d/00_header.in | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index a5d43929b8..01f1f4144e 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -60,7 +60,11 @@ elif [ -s \$prefix/grubenv ]; then
fi
if [ "\${env_block}" ] ; then
- set env_block="(\${root})\${env_block}"
+ if [ "\${dev}" ]; then
+ set env_block="(\${dev})\${env_block}"
+ else
+ set env_block="(\${root})\${env_block}"
+ fi
export env_block
load_env -f "\${env_block}"
fi

View file

@ -0,0 +1,89 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Neal Gompa <ngompa@fedoraproject.org>
Date: Wed, 26 Nov 2025 08:37:57 -0500
Subject: [PATCH] grub.d: Support the external environment block for automatic
hidden menu
This allows the automatic hidden menu (and boot counting) mechanism
to use an external environment block if it exists and is configured
to be used.
This is particularly important for supporting this mechanism on
systems with the operating system boot data on a Btrfs volume.
This is unlikely to be upstreamable, as it depends on Fedora's GRUB
hidden menu and boot counting patch sets.
Signed-off-by: Neal Gompa <ngompa@fedoraproject.org>
---
util/grub.d/08_fallback_counting.in | 6 +++++-
util/grub.d/10_reset_boot_success.in | 6 +++++-
util/grub.d/12_menu_auto_hide.in | 6 +++++-
util/grub.d/14_menu_show_once.in | 6 +++++-
4 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/util/grub.d/08_fallback_counting.in b/util/grub.d/08_fallback_counting.in
index 2e2c3ff7d3..272cdb6533 100644
--- a/util/grub.d/08_fallback_counting.in
+++ b/util/grub.d/08_fallback_counting.in
@@ -19,6 +19,10 @@ if [ -n "\${boot_counter}" -a "\${boot_success}" = "0" ]; then
else
decrement boot_counter
fi
- save_env boot_counter
+ if [ "\${env_block}" ]; then
+ save_env -f "\${env_block}" boot_counter
+ else
+ save_env boot_counter
+ fi
fi
EOF
diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in
index e73f4137b3..ca04f9b699 100644
--- a/util/grub.d/10_reset_boot_success.in
+++ b/util/grub.d/10_reset_boot_success.in
@@ -21,5 +21,9 @@ elif [ "\${boot_indeterminate}" = "1" ]; then
fi
# Reset boot_success for current boot
set boot_success=0
-save_env boot_success boot_indeterminate
+if [ "\${env_block}" ]; then
+ save_env -f "\${env_block}" boot_success boot_indeterminate
+else
+ save_env boot_success boot_indeterminate
+fi
EOF
diff --git a/util/grub.d/12_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in
index 6a7c0fa0d4..c2c207232e 100644
--- a/util/grub.d/12_menu_auto_hide.in
+++ b/util/grub.d/12_menu_auto_hide.in
@@ -16,7 +16,11 @@ cat << EOF
if [ x\$feature_timeout_style = xy ] ; then
if [ "\${menu_show_once}" ]; then
unset menu_show_once
- save_env menu_show_once
+ if [ "\${env_block}" ]; then
+ save_env -f "\${env_block}" menu_show_once
+ else
+ save_env menu_show_once
+ fi
set timeout_style=menu
set timeout=60
elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then
diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in
index 1cd7f36142..41b37b5ec7 100755
--- a/util/grub.d/14_menu_show_once.in
+++ b/util/grub.d/14_menu_show_once.in
@@ -7,7 +7,11 @@ if [ x\$feature_timeout_style = xy ]; then
set timeout_style=menu
set timeout="\${menu_show_once_timeout}"
unset menu_show_once_timeout
- save_env menu_show_once_timeout
+ if [ "\${env_block}" ]; then
+ save_env -f "\${env_block}" menu_show_once_timeout
+ else
+ save_env menu_show_once_timeout
+ fi
fi
fi
EOF

View file

@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Neal Gompa <ngompa@fedoraproject.org>
Date: Wed, 26 Nov 2025 08:48:10 -0500
Subject: [PATCH] grub-editenv: Make automatic hidden menu and boot counting
use envblk when available
This ensures the automatic hidden menu and boot counting settings
are set in the right place when an external environment block is used.
This is unlikely to be upstreamable, as it depends on Fedora's GRUB
hidden menu and boot counting patch sets.
Signed-off-by: Neal Gompa <ngompa@fedoraproject.org>
---
util/grub-editenv.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/util/grub-editenv.c b/util/grub-editenv.c
index 3c3f9d09f3..48611e4e17 100644
--- a/util/grub-editenv.c
+++ b/util/grub-editenv.c
@@ -473,7 +473,13 @@ set_variables (const char *name, int argc, char *argv[])
*(p++) = 0;
- if ((strcmp (argv[0], "next_entry") == 0) && envblk_on_block != NULL)
+ if (((strcmp (argv[0], "next_entry") == 0) ||
+ (strcmp (argv[0], "boot_counter") == 0) ||
+ (strcmp (argv[0], "boot_indeterminate") == 0) ||
+ (strcmp (argv[0], "boot_success") == 0) ||
+ (strcmp (argv[0], "menu_show_once") == 0) ||
+ (strcmp (argv[0], "menu_show_once_timeout") == 0))
+ && envblk_on_block != NULL)
{
if (grub_envblk_set (envblk_on_block, argv[0], p) == 0)
grub_util_error ("%s", _("environment block too small"));

View file

@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Leo Sandoval <lsandova@redhat.com>
Date: Fri, 28 Nov 2025 11:30:19 -0600
Subject: [PATCH] Increase EFI max allocation to max usable address
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
---
include/grub/x86_64/efi/memory.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h
index e81cfb3221..547e3f82f8 100644
--- a/include/grub/x86_64/efi/memory.h
+++ b/include/grub/x86_64/efi/memory.h
@@ -3,7 +3,7 @@
#if defined (__code_model_large__)
#define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__
-#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff
+#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
#else
#define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff
#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS

View file

@ -174,7 +174,7 @@ case "$COMMAND" in
fi
fi
if [ -n "$NEWDEFAULT" ]; then
grub2-editenv - set "saved_entry=${NEWDEFAULT}"
grub2-editenv - set "tmp_saved_entry=${NEWDEFAULT}"
fi
# this probably isn't the best place to do this, but it will do for now.
@ -193,7 +193,11 @@ case "$COMMAND" in
exit 77
;;
remove)
# If the boot entry type is type2, we are not removing the BLS entry
if [[ "x${KERNEL_INSTALL_BOOT_ENTRY_TYPE}" = "xtype2" ]]; then
[ "${KERNEL_INSTALL_VERBOSE}" -gt 0 ] && echo "The boot entry type is type2. Skip removing BLS entry."
exit 0
fi
if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then
BLS_TARGET="${BLS_DIR}/${MACHINE_ID}-${KERNEL_VERSION}.conf"
BLS_DEBUG="$(echo ${BLS_TARGET} | sed -e "s/${KERNEL_VERSION}/${KERNEL_VERSION}~debug/")"

44
95-set-boot-entry.install Executable file
View file

@ -0,0 +1,44 @@
#!/bin/bash
[[ -f /etc/default/grub ]] && . /etc/default/grub
[[ -f /etc/os-release ]] && . /etc/os-release
COMMAND="$1"
KERNEL_VERSION="$2"
BOOT_DIR_ABS="$3"
KERNEL_IMAGE="$4"
case "$COMMAND" in
add)
if [[ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]] || [[ ! -f /sbin/new-kernel-pkg ]]; then
entry=$(grub2-editenv list | grep tmp_saved_entry)
if [ -n "$entry" ]; then
BLS_ID=${entry##tmp_saved_entry=}
BLS_DIR="/boot/loader/entries"
BLS_TARGET="${BLS_DIR}/${BLS_ID}.conf"
LINUX="$(grep '^linux[ \t]' "${BLS_TARGET}" | sed -e 's,^linux[ \t]*,,' | awk '{print $1}')"
INITRD="$(grep '^initrd[ \t]' "${BLS_TARGET}" | sed -e 's,^initrd[ \t]*,,' | awk '{print $1}')"
ROOTPREFIX=""
[ -z "$(grub2-mkrelpath /boot)" ] && ROOTPREFIX="/boot"
if [ -e "${ROOTPREFIX}${LINUX}" -a -n "${INITRD}" ]; then
if [ ! -e "${ROOTPREFIX}${INITRD}" ]; then
echo "Error: ${ROOTPREFIX}${INITRD} not found."
exit 1
fi
if ! lsinitrd ${ROOTPREFIX}${INITRD} > /dev/null; then
echo "Error: ${ROOTPREFIX}${INITRD} appears to be corrupt."
exit 1
fi
fi
grub2-editenv - unset "tmp_saved_entry"
grub2-editenv - set "saved_entry=$BLS_ID"
fi
fi
;;
*)
;;
esac

View file

@ -5,7 +5,7 @@ if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then
fi
# Run grub2-mkconfig if ENABLE_BLSCFG is false
if grep -q '^GRUB_ENABLE_BLSCFG="*false"*\s*$' /etc/default/grub; then
if grep -sq '^GRUB_ENABLE_BLSCFG="*false"*\s*$' /etc/default/grub; then
RUN_MKCONFIG=true
DISABLE_BLS=true
fi
@ -28,7 +28,7 @@ if [[ $ARCH = "ppc64le" ]] && [ -d /sys/firmware/opal ]; then
fi
if [[ $DISABLE_BLS = "true" ]]; then
if grep -q '^GRUB_ENABLE_BLSCFG="*true"*\s*$' /etc/default/grub; then
if grep -sq '^GRUB_ENABLE_BLSCFG="*true"*\s*$' /etc/default/grub; then
sed -i 's/^GRUB_ENABLE_BLSCFG=.*/GRUB_ENABLE_BLSCFG=false/' /etc/default/grub
fi
fi

89
do-sync Executable file
View file

@ -0,0 +1,89 @@
#!/bin/bash
set -o nounset
set -o errexit
usage() {
echo "Usage $0 <pkgtool>:<dist_git_branch>:<git_branch>:<git_base_commit>"
echo "Generates a (rhboot) grub2 branch which contains latest patches from dist-git based on specific Fedora/RHEL version"
echo "Command expects a single parameter delimited with colons, with the following meanings:"
echo " pkgtool: Package tool, e.g. fedpkg, rhpkg, etc."
echo " dist_git_branch: The dist-git branch/release to sync"
echo " git_branch: The git branch/release to sync"
echo " git_base_commit: base commit where git_branch is based on"
echo
echo "Examples:"
echo
echo " $0 fedpkg:rawhide:fedora-43:grub-2.12"
echo " $0 fedpkg:f42:fedora-42:grub-2.12"
echo " $0 fedpkg:f41:fedora-41:grub-2.12"
echo " $0 fedpkg:f40:fedora-40:grub-2.12"
echo
echo "Also handles RHEL branches:"
echo
echo " $0 rhpkg:rhel-10-main:rhel-10-main:grub-2.12"
echo " $0 rhpkg:rhel-9-main:rhel-9-main:grub-2.06"
echo " $0 rhpkg:rhel-8-main:rhel-8-main:f28-branchpoint"
exit 1
}
# check params
[ $# -eq 1 ] || usage
args=$1
pkgtool=$(echo "$args" | awk -F ':' '{print $1}')
dist_git_branch=$(echo "$args" | awk -F ':' '{print $2}')
git_branch=$(echo "$args" | awk -F ':' '{print $3}')
git_base_commit=$(echo "$args" | awk -F ':' '{print $4}')
# working dirs
dist_git_dir=/tmp/grub/dist-git
git_dir=/tmp/grub/git
rm -rf $dist_git_dir $git_dir
mkdir -p $dist_git_dir $git_dir
pushd $dist_git_dir > /dev/null
{
# clone grub2 package anonymously
$pkgtool clone -a grub2 . 2>/dev/null
# create a local branch, all fine if already exists
git checkout -q -b "$dist_git_branch" origin/"$dist_git_branch" 2> /dev/null || true
}
popd > /dev/null
pushd $git_dir > /dev/null
{
git clone -q git@github.com:rhboot/grub2.git . 2>/dev/null
dist_git_sync_branch=${git_branch}-dist-git-$(date +"%Y-%m-%d-%H_%M_%S")
git checkout -q -b "$dist_git_sync_branch" "$git_base_commit"
while read -r line; do
p=$(echo "$line" | awk '{print $2}')
git am -q "$dist_git_dir/$p" 2>/dev/null
done < $dist_git_dir/grub.patches
# brach current git_branch based on upstream git_branch
git branch -D "$git_branch" 2>/dev/null || true
git checkout -q -b "$git_branch" origin/"$git_branch"
missing_commits=$(git cherry "${git_branch}" "${dist_git_sync_branch}" | grep ^+ | wc -l)
if [[ $missing_commits -eq 0 ]]; then
echo "Branch $git_branch is up-to-date, no need to sync"
else
echo "Missing commits ($missing_commits) which would be integrated in final branch"
git cherry "$git_branch" "$dist_git_sync_branch" -v | grep ^+
# finally cherry pick those missing patches
git cherry "$git_branch" "$dist_git_sync_branch" 2>/dev/null | grep ^+ | sed 's/\+ //' | xargs git cherry-pick 2>/dev/null
echo "Pushing latest commits"
cd "$git_dir"
git push -v origin "$git_branch"
fi
}
popd > /dev/null

26
gen_grub_cfgstub Normal file
View file

@ -0,0 +1,26 @@
#!/bin/sh
set -eu
if [ $# -ne 2 ]
then
echo "Missing argument"
echo "Usage: script.sh GRUB_HOME EFI_HOME"
exit 1
fi
GRUB_HOME=$1
EFI_HOME=$2
# create a stub grub2 config in EFI
BOOT_UUID=$(grub2-probe --target=fs_uuid "${GRUB_HOME}")
GRUB_DIR=$(grub2-mkrelpath "${GRUB_HOME}")
cat << EOF > "${EFI_HOME}"/grub.cfg.stb
search --no-floppy --root-dev-only --fs-uuid --set=dev ${BOOT_UUID}
set prefix=(\$dev)${GRUB_DIR}
export \$prefix
configfile \$prefix/grub.cfg
EOF
mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
chmod 0600 ${EFI_HOME}/grub.cfg

View file

@ -1,6 +1,12 @@
# vim:filetype=spec
# Modules always contain just 32-bit code
%global evr %{epoch}:%{version}-%{release}
%global _libdir %{_exec_prefix}/lib
%global os_id %(eval echo $(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/'))
%global grub_evr_dir %{_libdir}/efi/grub2/%{evr}
%global grub_efi_dir %{grub_evr_dir}/EFI/%{os_id}
%global _binaries_in_noarch_packages_terminate_build 0
#%%undefine _missing_build_ids_terminate_build
%{expand:%%{!?buildsubdir:%%global buildsubdir grub-%{tarversion}}}
@ -280,8 +286,6 @@
%global grubelfname core.elf
%endif
%global evr %{epoch}:%{version}-%{release}
%ifarch x86_64
%global with_efi_common 1
%global with_legacy_modules 1
@ -323,7 +327,7 @@ This subpackage provides support for rebuilding your own grub.efi. \
%{expand:%%{?!buildsubdir:%%define buildsubdir grub-%%{1}-%{tarversion}}}\
%{expand:%%package %%{1}-tools} \
Summary: Support tools for GRUB. \
Requires: gettext-runtime os-prober which file system-logos \
Requires: gettext-runtime os-prober file system-logos \
Requires: grub2-common = %{evr} \
Requires: grub2-tools-minimal = %{evr} \
Requires: os-prober >= 1.58-11 \
@ -514,7 +518,7 @@ install -m 644 %{1}.conf ${RPM_BUILD_ROOT}/etc/dnf/protected.d/ \
rm -f %{1}.conf \
%{nil}
%global grub_modules " all_video boot blscfg btrfs \\\
%global grub_modules " all_video boot blscfg btrfs blsuki \\\
cat configfile cryptodisk \\\
echo ext2 f2fs fat font \\\
gcry_rijndael gcry_rsa gcry_serpent \\\
@ -704,8 +708,9 @@ find . '(' -iname gdb_grub \\\
')' \\\
-exec cp {} $RPM_BUILD_ROOT/usr/lib/grub/%{grubaltefiarch}/ \\\; \
find $RPM_BUILD_ROOT -type f -iname "*.mod*" -exec chmod a-x {} '\;' \
install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \
install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \
install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \
install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \
install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \
%{expand:%%do_install_protected_file grub2-%{alt_package_arch}} \
cd .. \
%{nil}
@ -722,11 +727,14 @@ fi \
find $RPM_BUILD_ROOT -iname "*.module" -exec chmod a-x {} '\;' \
ln -s ../boot/grub2/grub.cfg \\\
$RPM_BUILD_ROOT%{_sysconfdir}/grub2-efi.cfg \
install -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_dir}/%{2} \
install -m 700 %{3} $RPM_BUILD_ROOT%{efi_esp_dir}/%{3} \
install -d -m 0700 ${RPM_BUILD_ROOT}%{grub_efi_dir}/ \
install -m 700 %{2} $RPM_BUILD_ROOT%{grub_efi_dir}/%{2} \
install -m 700 %{3} $RPM_BUILD_ROOT%{grub_efi_dir}/%{3} \
%ifarch %{arm} \
install -D -m 700 %{2} $RPM_BUILD_ROOT%{efi_esp_boot}/BOOTARM.EFI \
%endif \
install -D -m 700 %{SOURCE13} \\\
${RPM_BUILD_ROOT}/usr/bin/gen_grub_cfgstub \
install -D -m 700 unicode.pf2 \\\
${RPM_BUILD_ROOT}/boot/grub2/fonts/unicode.pf2 \
${RPM_BUILD_ROOT}/%{_bindir}/grub2-editenv \\\
@ -871,14 +879,18 @@ ln -s ../boot/grub2/grub.cfg \\\
%defattr(-,root,root,-) \
%config(noreplace) %{_sysconfdir}/grub2.cfg \
%config(noreplace) %{_sysconfdir}/grub2-efi.cfg \
%attr(0700,root,root) %verify(not mtime) %{efi_esp_dir}/%{2} \
%ghost %attr(0700,root,root) %{efi_esp_dir}/%{2} \
%dir %attr(0700,root,root) %{grub_efi_dir} \
%attr(0700,root,root) %{grub_efi_dir}/%{2} \
%ifarch %{arm} \
%attr(0700,root,root) %verify(not mtime) %{efi_esp_boot}/BOOTARM.EFI \
%endif \
%attr(0700,root,root)/boot/grub2/fonts \
%attr(0700,root,root)/usr/bin/gen_grub_cfgstub \
%dir %attr(0700,root,root)/boot/loader/entries \
%ghost %config(noreplace) %attr(0600,root,root)/boot/grub2/grub.cfg \
%ghost %config(noreplace) %verify(not mtime) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg \
%ghost %config(noreplace) %attr(0700,root,root)%{efi_esp_dir}/grub.cfg \
%ghost %config(noreplace) %attr(0600,root,root)%{grub_efi_dir}/grub.cfg \
%config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv \
%attr(0644,root,root) %config(noreplace) /etc/dnf/protected.d/grub2-%{1}.conf \
%{expand:%if 0%{?without_efi_modules} \
@ -896,7 +908,8 @@ ln -s ../boot/grub2/grub.cfg \\\
\
%{expand:%%files %{1}-cdboot} \
%defattr(-,root,root,-) \
%attr(0700,root,root) %verify(not mtime) %{efi_esp_dir}/%{3} \
%ghost %attr(0700,root,root) %{efi_esp_dir}/%{3} \
%attr(0700,root,root) %{grub_efi_dir}/%{3} \
%attr(0700,root,root)/boot/grub2/fonts \
%{nil}

View file

@ -359,10 +359,48 @@ Patch0359: 0359-kern-misc-Add-sanity-check-after-grub_strtoul-call.patch
Patch0360: 0360-loader-i386-linux-Cast-left-shift-to-grub_uint32_t.patch
Patch0361: 0361-loader-i386-bsd-Use-safe-math-to-avoid-underflow.patch
Patch0362: 0362-fs-ext2-Rework-out-of-bounds-read-for-inline-and-ext.patch
Patch0363: 0363-ieee1275-ofnet-Fix-grub_malloc-removed-after-added-s.patch
Patch0364: 0364-powerpc-increase-MIN-RMA-size-for-CAS-negotiation.patch
Patch0365: 0365-10_linux.in-escape-kernel-option-characters-properly.patch
Patch0366: 0366-blscfg-check-if-variable-is-escaped-before-consideri.patch
Patch0367: 0367-loader-efi-chainloader-Fix-double-free.patch
Patch0368: 0368-loader-efi-chainloader-Fix-null-pointer-dereference.patch
Patch0369: 0369-osdep-linux-getroot-Detect-DDF-container-similar-to-.patch
Patch0363: 0363-powerpc-increase-MIN-RMA-size-for-CAS-negotiation.patch
Patch0364: 0364-script-execute-Don-t-let-trailing-blank-lines-determ.patch
Patch0365: 0365-normal-menu-Check-return-code-of-the-script-when-exe.patch
Patch0366: 0366-ieee1275-ofnet-Fix-grub_malloc-removed-after-added-s.patch
Patch0367: 0367-grub-mkimage-Create-new-ELF-note-for-SBAT.patch
Patch0368: 0368-grub-mkimage-Add-SBAT-metadata-into-ELF-note-for-Pow.patch
Patch0369: 0369-10_linux.in-escape-kernel-option-characters-properly.patch
Patch0370: 0370-blscfg-check-if-variable-is-escaped-before-consideri.patch
Patch0371: 0371-kern-rescue_reader-Block-the-rescue-mode-until-the-C.patch
Patch0372: 0372-commands-search-Introduce-the-cryptodisk-only-argume.patch
Patch0373: 0373-disk-diskfilter-Introduce-the-cryptocheck-command.patch
Patch0374: 0374-commands-search-Add-the-diskfilter-support.patch
Patch0375: 0375-docs-Document-available-crypto-disks-checks.patch
Patch0376: 0376-disk-cryptodisk-Add-the-erase-secrets-function.patch
Patch0377: 0377-disk-cryptodisk-Wipe-the-passphrase-from-memory.patch
Patch0378: 0378-cryptocheck-Add-quiet-option.patch
Patch0379: 0379-loader-efi-chainloader-Fix-double-free.patch
Patch0380: 0380-loader-efi-chainloader-Fix-null-pointer-dereference.patch
Patch0381: 0381-osdep-linux-getroot-Detect-DDF-container-similar-to-.patch
Patch0382: 0382-Set-correctly-the-memory-attributes-for-the-kernel-P.patch
Patch0383: 0383-Include-function-name-on-debug-print-function.patch
Patch0384: 0384-kern-misc-Implement-grub_strtok.patch
Patch0385: 0385-blsuki-Add-blscfg-command-to-parse-Boot-Loader-Speci.patch
Patch0386: 0386-util-misc.c-Change-offset-type-for-grub_util_write_i.patch
Patch0387: 0387-blsuki-Check-for-mounted-boot-in-emu.patch
Patch0388: 0388-blsuki-Add-uki-command-to-load-Unified-Kernel-Image-.patch
Patch0389: 0389-posix_wrap-Tweaks-in-preparation-for-libtasn1.patch
Patch0390: 0390-blsuki-do-not-register-blscfg-command.patch
Patch0391: 0391-misc-add-z-length-modifier-support.patch
Patch0392: 0392-tests-add-z-modifier-printf-tests.patch
Patch0393: 0393-util-grub-editenv-add-fs_envblk-open-helper.patch
Patch0394: 0394-util-grub-editenv-add-fs_envblk-write-helper.patch
Patch0395: 0395-util-grub-editenv-wire-set_variables-to-optional-fs_.patch
Patch0396: 0396-util-grub-editenv-wire-unset_variables-to-optional-f.patch
Patch0397: 0397-util-grub-editenv-wire-list_variables-to-optional-fs.patch
Patch0398: 0398-util-grub-editenv-add-probe-call-for-external-envblk.patch
Patch0399: 0399-btrfs-add-environment-block-to-reserved-header-area.patch
Patch0400: 0400-00_header.in-wire-grub.cfg-to-use-env_block-when-pre.patch
Patch0401: 0401-btrfs-update-doc-link-for-bootloader-support.patch
Patch0402: 0402-docs-add-Btrfs-env-block-and-special-env-vars.patch
Patch0403: 0403-kern-fs-Honour-file-read_hook-in-grub_fs_blocklist_r.patch
Patch0404: 0404-util-grub.d-00_header.in-Prefer-dev-instead-of-root-.patch
Patch0405: 0405-grub.d-Support-the-external-environment-block-for-au.patch
Patch0406: 0406-grub-editenv-Make-automatic-hidden-menu-and-boot-cou.patch
Patch0407: 0407-Increase-EFI-max-allocation-to-max-usable-address.patch

View file

@ -17,7 +17,7 @@
Name: grub2
Epoch: 1
Version: 2.12
Release: 32%{?dist}
Release: 50%{?dist}
Summary: Bootloader with support for Linux, Multiboot and more
License: GPL-3.0-or-later
URL: http://www.gnu.org/software/grub/
@ -35,6 +35,8 @@ Source9: strtoull_test.c
Source10: 20-grub.install
Source11: grub.patches
Source12: sbat.csv.in
Source13: gen_grub_cfgstub
Source14: 95-set-boot-entry.install
%include %{SOURCE1}
@ -300,6 +302,7 @@ rm -vf ${RPM_BUILD_ROOT}/%{_sbindir}/grub2-macbless
# Install kernel-install scripts
install -d -m 0755 %{buildroot}%{_prefix}/lib/kernel/install.d/
install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE10}
install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE14}
install -D -m 0755 -t %{buildroot}%{_prefix}/lib/kernel/install.d/ %{SOURCE3}
install -d -m 0755 %{buildroot}%{_sysconfdir}/kernel/install.d/
# Install systemd user service to set the boot_success flag
@ -384,7 +387,7 @@ fi
%posttrans common
set -eu
EFI_HOME=%{efi_esp_dir}
EFI_HOME=%{grub_efi_dir}
GRUB_HOME=/boot/grub2
ESP_PATH=/boot/efi
@ -411,29 +414,30 @@ if test -f ${EFI_HOME}/grub.cfg; then
fi
# create a stub grub2 config in EFI
BOOT_UUID=$(grub2-probe --target=fs_uuid ${GRUB_HOME})
GRUB_DIR=$(grub2-mkrelpath ${GRUB_HOME})
cat << EOF > ${EFI_HOME}/grub.cfg.stb
search --no-floppy --root-dev-only --fs-uuid --set=dev ${BOOT_UUID}
set prefix=(\$dev)${GRUB_DIR}
export \$prefix
configfile \$prefix/grub.cfg
EOF
gen_grub_cfgstub $GRUB_HOME $EFI_HOME || :
if test -f ${EFI_HOME}/grubenv; then
cp -a ${EFI_HOME}/grubenv ${EFI_HOME}/grubenv.rpmsave
mv --force ${EFI_HOME}/grubenv ${GRUB_HOME}/grubenv
fi
# ensure we exit 0
mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg || :
%if 0%{with_efi_arch}
%posttrans efi-%{efiarch}
set -eu
# On image mode, bootupd takes care of installing bootloader updates to the ESP
if [[ ! -e "/run/ostree-booted" ]]; then
cp -a %{grub_efi_dir}/. %{efi_esp_dir} || :
fi
%endif
%files common -f grub.lang
%dir %{_libdir}/grub/
%dir %{_datarootdir}/grub/
%attr(0700,root,root) %dir %{_sysconfdir}/grub.d
%{_prefix}/lib/kernel/install.d/20-grub.install
%{_prefix}/lib/kernel/install.d/95-set-boot-entry.install
%{_prefix}/lib/kernel/install.d/99-grub-mkconfig.install
%dir %{_datarootdir}/grub
%exclude %{_datarootdir}/grub/*
@ -442,8 +446,7 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg || :
%dir /boot/grub2/themes/system
%attr(0700,root,root) %dir /boot/grub2
%exclude /boot/grub2/*
%dir %attr(0700,root,root) %{efi_esp_dir}
%exclude %{efi_esp_dir}/*
%exclude %{grub_efi_dir}/*
%ghost %config(noreplace) %verify(not size mode md5 mtime) /boot/grub2/grubenv
%license COPYING
%doc THANKS
@ -605,27 +608,88 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg || :
%endif
%changelog
* Thu Jun 5 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-32
* Wed Dec 10 2025 Joel Capitao <jcapitao@redhat.com> - 2.12-50
- Do not include EFI dir in common subpackage
- Resolves: rhbz#2420430
* Thu Nov 27 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-49
- Increase EFI max allocation to max usable address
- Resolves: #2263643
* Fri Nov 21 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-48
- Add support for external environment block on Btrfs
* Thu Nov 13 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-47
- blsuki: do not register blscfg command
* Tue Nov 4 2025 Tom Callaway <spot@fedoraproject.org> - 2.12-46
- rebuild for new fuse3
* Wed Oct 15 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-45
- Include upstream blsuki related patches
* Mon Sep 15 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-44
- Include function name on debug print function
* Fri Aug 29 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-43
- gen_grub_cfgstub: Removed output messages
* Wed Aug 20 2025 Jan Stancek <jstancek@redhat.com> - 2.12-42
- move 55-set-boot-entry.install to 95
- Related: #2389020
* Mon Aug 11 2025 Marta Lewandowska <mlewando@redhat.com> - 2.12-41
- Phase 1 of the bootloader updates proposal implementation
- https://fedoraproject.org/wiki/Changes/BootLoaderUpdatesPhase1
* Wed Aug 6 2025 Jan Stancek <jstancek@redhat.com> - 2.12-40
- 55-set-boot-entry.install: fix initrd check
- Resolves: #2386118
* Fri Aug 1 2025 FeRD (Frank Dana) <ferdnyc@gmail.com> - 2.12-39
- kernel-install: Suppress warnings about missing /etc/default/grub
file when attempting to grep its contents
* Tue Jul 29 2025 Leo Sandoval <lsandova@redhat.com> 2.12-38
- Set correctly the memory attributes for the kernel PE sections
* Tue Jul 8 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-37
- spec: fix grub stub cfg creation script
* Thu Jun 12 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-36
- spec: moved grub stub cfg creation into a script
* Thu Jun 5 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-35
- osdep/linux/getroot: Detect DDF container similar to IMSM
* Wed May 28 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-31
* Wed May 28 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-34
- Some fixes addressing memory freeing and dereferencing
- Resolves: #2368939
- Resolves: #2368945
* Tue May 27 2025 Leo Sandoval <lsandova@redhat.com> 2.12-30
* Fri May 16 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-33
- sbat: bump grub sbat
* Tue May 06 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-32
- Fix several cryptodisk vulnerabilities
- Resolves: CVE-2025-4382
* Tue May 6 2025 Leo Sandoval <lsandova@redhat.com> 2.12-31
- Handle special kernel parameter characters properly
- Resolves: #2362821
* Fri May 16 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-29
- sbat: bump grub sbat
* Fri Apr 18 2025 Nicolas Frayer <nfrayer@redhat.com> - 2.12-30
- ppc/mkimage: SBAT support on powerpc
* Thu Mar 27 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-28
- powerpc: increase MIN RMA size for CAS negotiation
* Tue Mar 25 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-27
* Tue Mar 25 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-29
- ieee1275/ofnet: Fix grub_malloc() removed after added safe
* Tue Mar 25 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-28
- Fix the fallback mechanism when menu entries fail to boot
* Wed Mar 19 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-27
- powerpc: increase MIN RMA size for CAS negotiation
* Mon Mar 10 2025 Leo Sandoval <lsandova@redhat.com> 2.12-26
- Remove 'fs/ntfs: Implement attribute verification' patch
- Resolves: #2350327
@ -634,13 +698,27 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg || :
- fs/ext2: Rework out-of-bounds read for inline and external extents
- Resolves: #2346804
* Tue Feb 18 2025 Leo Sandoval <lsandova@redhat.com> - 2.12-24
* Tue Feb 18 2025 Leo Sandoval <lsandova@redhat.com> - 2.02-24
- Add Several CVE fixes
- Resolves: CVE-2024-45781 CVE-2024-45783 CVE-2024-45778
- Resolves: CVE-2024-45775 CVE-2024-45780 CVE-2024-45774
- Resolves: CVE-2025-0690 CVE-2025-1118 CVE-2024-45782
- Resolves: CVE-2025-0624 CVE-2024-45779 CVE-2024-45776
- Resolves: CVE-2025-0622 CVE-2025-0677
- Related: #RHEL-79703
- Related: #RHEL-79708
- Related: #RHEL-79340
- Related: #RHEL-73786
- Related: #RHEL-79701
- Related: #RHEL-73784
- Related: #RHEL-79856
- Related: #RHEL-79874
- Related: #RHEL-79706
- Related: #RHEL-79836
- Related: #RHEL-79699
- Related: #RHEL-75736
- Related: #RHEL-79712
- Related: #RHEL-79848
* Tue Feb 11 2025 Nicolas Frayer <nfrayer@redhat.com> 2.12-23
- Revert commit for bootloader update proposal for now as not all components are ready

View file

@ -1,3 +0,0 @@
[koji]
targets = rawhide eln