Linux EFI development
 help / color / mirror / Atom feed
* [PATCH v2] efi/libstub: populate LoaderDevicePartUUID
@ 2026-09-03 21:19 Vincent Mailhol
  2026-09-04 16:12 ` Ard Biesheuvel
  0 siblings, 1 reply; 4+ messages in thread
From: Vincent Mailhol @ 2026-09-03 21:19 UTC (permalink / raw)
  To: Ard Biesheuvel, Ilias Apalodimas; +Cc: linux-kernel, linux-efi, Vincent Mailhol

The Boot Loader Interface [1] defines LoaderDevicePartUUID. That EFI
variable records the GPT partition UUID of the partition containing the
boot loader.

This is used, for example, by systemd-gpt-auto-generator [2] to identify
the disk the boot loader was launched from and automatically detect and
mount partitions on it.

GRUB [3] and systemd-boot [4] populate it, but when the kernel is
started directly by EFI firmware, there is no conventional external boot
loader to provide the variable. In that case, because the EFI stub
performs the boot loader role, it should provide the variable itself.

Parse the loaded image device path, extract the GUID signature from its
GPT HD() node and publish it under the Linux loader entry vendor GUID as
the volatile LoaderDevicePartUUID EFI variable. Do not overwrite an
existing variable, so a value supplied by an earlier boot stage keeps
precedence.

Install the efi_bli_set_variables() hook in both the generic efi-stub.c
path and the x86-specific x86-stub.c path.

Add CONFIG_EFI_STUB_BLI to make this new feature configurable.

For an x86_64 build using gcc 15.3.0, bloat-o-meter reports the
following difference between builds without and with CONFIG_EFI_STUB_BLI:

  add/remove: 5/0 grow/shrink: 1/0 up/down: 639/0 (639)
  Function                                     old     new   delta
  efi_bli_set_variables                          -     563    +563
  loader_entry_guid                              -      16     +16
  hex                                            -      16     +16
  guid_index                                     -      16     +16
  device_path_guid                               -      16     +16
  efi_stub_entry                              4180    4192     +12
  Total: Before=29223, After=29862, chg +2.19%

[1] The Boot Loader Interface
Link: https://systemd.io/BOOT_LOADER_INTERFACE/

[2] systemd-gpt-auto-generator
Link: https://www.freedesktop.org/software/systemd/man/latest/systemd-gpt-auto-generator.html

[3] GRUB -- §16.2 bli
Link: https://www.gnu.org/software/grub/manual/grub/html_node/bli_005fmodule.html

[4] systemd -- systemd-boot UEFI Boot Manager
Link: https://github.com/systemd/systemd/blob/main/docs/BOOT.md?plain=1#L111

Signed-off-by: Vincent Mailhol <mailhol@kernel.org>
---
Changes in v2:

  - Add CONFIG_EFI_STUB_BLI to make the BLI feature optional.

  - Use static storage for GUID initializers. This reduces the size by
    about 10% compared to v1.

  - Remove the redundant NULL check on image.

  - Move the nibble-to-ASCII-hex conversion out of efi_bli_guid_to_str()
    into the new efi_bli_nibble_to_hex().

  - Rename variables to be closer to the EFI specification.

Link to v1: https://lore.kernel.org/r/20260725-efi_stub_bli-v1-1-966e4748077f@kernel.org
---
 drivers/firmware/efi/Kconfig            |  15 +++++
 drivers/firmware/efi/libstub/Makefile   |   2 +
 drivers/firmware/efi/libstub/bli.c      | 114 ++++++++++++++++++++++++++++++++
 drivers/firmware/efi/libstub/efi-stub.c |   1 +
 drivers/firmware/efi/libstub/efistub.h  |   6 ++
 drivers/firmware/efi/libstub/x86-stub.c |   1 +
 6 files changed, 139 insertions(+)

diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 29e0729299f5..eff7c6164096 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -100,6 +100,21 @@ config EFI_ARMSTUB_DTB_LOADER
 	  functionality for bootloaders that do not have such support
 	  this option is necessary.
 
+config EFI_STUB_BLI
+	bool "EFI stub Boot Loader Interface support"
+	depends on EFI_STUB
+	default y
+	help
+	  The Boot Loader Interface defines EFI variables that expose
+	  information about the boot loader to the running OS.
+
+	  Enable this option to let the EFI stub populate the volatile
+	  LoaderDevicePartUUID EFI variable when it is missing. This allows
+	  user space services such as systemd-gpt-auto-generator to discover
+	  partitions on the disk from which the kernel was loaded.
+
+	  If unsure, say Y.
+
 config EFI_BOOTLOADER_CONTROL
 	tristate "EFI Bootloader Control"
 	select UCS2_STRING
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 77a2b2d74f3f..000a73a4bc52 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -75,6 +75,8 @@ libfdt-deps			:= fdt_rw.c fdt_ro.c fdt_wip.c fdt.c \
 lib-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdt.o \
 				     $(patsubst %.c,lib-%.o,$(libfdt-deps))
 
+lib-$(CONFIG_EFI_STUB_BLI)	+= bli.o
+
 $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
 	$(call if_changed_rule,cc_o_c)
 
diff --git a/drivers/firmware/efi/libstub/bli.c b/drivers/firmware/efi/libstub/bli.c
new file mode 100644
index 000000000000..d54eb42729a7
--- /dev/null
+++ b/drivers/firmware/efi/libstub/bli.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/efi.h>
+#include <linux/errno.h>
+#include <linux/unaligned.h>
+
+#include "efistub.h"
+
+struct efi_hd_dev_path {
+	struct efi_generic_dev_path header;
+	u32 partition_number;
+	u64 partition_start;
+	u64 partition_size;
+	efi_guid_t signature;
+	u8 partition_format;
+	u8 signature_type;
+} __packed;
+
+#define EFI_HD_PARTITION_FORMAT_GPT	2
+#define EFI_HD_SIGNATURE_TYPE_GUID	2
+
+static efi_char16_t efi_bli_nibble_to_hex(u8 nibble)
+{
+	static const char hex[16] __nonstring = "0123456789abcdef";
+
+	return hex[nibble];
+}
+
+static void efi_bli_guid_to_str(const efi_guid_t *guid, efi_char16_t *out)
+{
+	static const u8 guid_index[UUID_SIZE] = {
+		3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15,
+	};
+
+	for (int i = 0; i < ARRAY_SIZE(guid_index); i++) {
+		u8 byte = guid->b[guid_index[i]];
+
+		*out++ = efi_bli_nibble_to_hex(byte >> 4);
+		*out++ = efi_bli_nibble_to_hex(byte & 0xf);
+
+		switch (i) {
+		case 3:
+		case 5:
+		case 7:
+		case 9:
+			*out++ = L'-';
+		}
+	}
+
+	*out = L'\0';
+}
+
+static int efi_bli_dev_path_part_uuid(const efi_device_path_protocol_t *path,
+				      efi_char16_t *partuuid)
+{
+	const efi_device_path_protocol_t *node;
+	const struct efi_hd_dev_path *hd_node;
+	u16 node_len;
+
+	for (node = path;
+	     node->type != EFI_DEV_END_PATH && node->type != EFI_DEV_END_PATH2;
+	     node = (const void *)node + node_len) {
+		node_len = get_unaligned_le16(&node->length);
+
+		if (node_len < sizeof(*node))
+			return -EINVAL;
+
+		if (node->type != EFI_DEV_MEDIA ||
+		    node->sub_type != EFI_DEV_MEDIA_HARD_DRIVE)
+			continue;
+
+		if (node_len < sizeof(*hd_node))
+			return -EINVAL;
+
+		hd_node = (const struct efi_hd_dev_path *)node;
+		if (hd_node->partition_format != EFI_HD_PARTITION_FORMAT_GPT ||
+		    hd_node->signature_type != EFI_HD_SIGNATURE_TYPE_GUID)
+			continue;
+
+		efi_bli_guid_to_str(&hd_node->signature, partuuid);
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static void efi_bli_populate_loader_part_uuid(efi_loaded_image_t *image)
+{
+	static efi_guid_t device_path_guid = EFI_DEVICE_PATH_PROTOCOL_GUID;
+	static efi_guid_t loader_entry_guid = LINUX_EFI_LOADER_ENTRY_GUID;
+	efi_char16_t partuuid[UUID_STRING_LEN + 1];
+	unsigned long size = 0;
+	void *path;
+
+	if (get_efi_var(L"LoaderDevicePartUUID", &loader_entry_guid,
+			NULL, &size, NULL) != EFI_NOT_FOUND)
+		return;
+
+	if (efi_bs_call(handle_protocol, efi_table_attr(image, device_handle),
+			&device_path_guid, &path) != EFI_SUCCESS)
+		return;
+
+	if (efi_bli_dev_path_part_uuid(path, partuuid))
+		return;
+
+	set_efi_var(L"LoaderDevicePartUUID", &loader_entry_guid,
+		    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+		    sizeof(partuuid), partuuid);
+}
+
+void efi_bli_set_variables(efi_loaded_image_t *image)
+{
+	efi_bli_populate_loader_part_uuid(image);
+}
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index 42d6073bcd06..2a95f4ea104a 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -165,6 +165,7 @@ efi_status_t efi_stub_common(efi_handle_t handle,
 	dpy = setup_primary_display();
 
 	efi_retrieve_eventlog();
+	efi_bli_set_variables(image);
 
 	/* Ask the firmware to clear memory on unclean shutdown */
 	efi_enable_reset_attack_mitigation();
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index fd91fc15ec81..a4f961ea43a8 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1072,6 +1072,12 @@ efi_status_t efi_random_alloc(unsigned long size, unsigned long align,
 			      int memory_type, unsigned long alloc_min,
 			      unsigned long alloc_max);
 
+#ifdef CONFIG_EFI_STUB_BLI
+void efi_bli_set_variables(efi_loaded_image_t *image);
+#else
+static inline void efi_bli_set_variables(efi_loaded_image_t *image) { }
+#endif
+
 efi_status_t efi_random_get_seed(void);
 
 efi_status_t check_platform_features(void);
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..b762f7f37f28 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -1014,6 +1014,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
 	efi_random_get_seed();
 
 	efi_retrieve_eventlog();
+	efi_bli_set_variables(image);
 
 	setup_graphics(boot_params);
 

---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260724-efi_stub_bli-477289050225

Best regards,
--  
Vincent Mailhol <mailhol@kernel.org>


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-05 12:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 21:19 [PATCH v2] efi/libstub: populate LoaderDevicePartUUID Vincent Mailhol
2026-09-04 16:12 ` Ard Biesheuvel
2026-09-04 22:06   ` Vincent Mailhol
2026-09-05 12:05     ` Vincent Mailhol

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox