bli: Implement initial Boot Loader Interface support
This patch makes Limine advertise its branding and the ESP partition GUID to systemd over the Boot Loader Interface specification. There's more to the Boot Loader Interface than these variables, but this acts as an initial implementation. See #473. BLI variables are initialised for all protocols, not just Linux. While currently only systemd makes use of it, this allows future Limine protocol kernels (or their userspace) to discover the ESP.
diff --git a/common/lib/bli.c b/common/lib/bli.c
new file mode 100644
index 00000000..ba0d70a3
--- /dev/null
+++ b/common/lib/bli.c
@@ -0,0 +1,36 @@
+#if defined (UEFI)
+
+#include <config.h>
+#include <efi.h>
+#include <lib/bli.h>
+#include <lib/guid.h>
+#include <lib/misc.h>
+
+#define LIMINE_BRAND L"Limine " LIMINE_VERSION
+
+static EFI_GUID bli_vendor_guid = { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } };
+
+void init_bli(void) {
+ gRT->SetVariable(L"LoaderInfo",
+ &bli_vendor_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(LIMINE_BRAND),
+ LIMINE_BRAND);
+
+ char part_uuid_str[37];
+ guid_to_string(&boot_volume->part_guid, part_uuid_str);
+
+ // Convert part_uuid_str to a wide-char string
+ wchar_t part_uuid[37];
+ for (size_t i = 0; i < 37; i++) {
+ part_uuid[i] = (wchar_t) part_uuid_str[i];
+ }
+
+ gRT->SetVariable(L"LoaderDevicePartUUID",
+ &bli_vendor_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof(part_uuid),
+ part_uuid);
+}
+
+#endif
diff --git a/common/lib/bli.h b/common/lib/bli.h
new file mode 100644
index 00000000..135dba5c
--- /dev/null
+++ b/common/lib/bli.h
@@ -0,0 +1,10 @@
+#ifndef LIB__BLI_H__
+#define LIB__BLI_H__
+
+#if defined (UEFI)
+
+void init_bli(void);
+
+#endif
+
+#endif
diff --git a/common/lib/guid.c b/common/lib/guid.c
index 2e9f4746..06730b0a 100644
--- a/common/lib/guid.c
+++ b/common/lib/guid.c
@@ -67,3 +67,29 @@ bool string_to_guid_mixed(struct guid *guid, const char *s) {
return true;
}
+
+static void uint_to_hex(uint32_t num, char *dest, int len) {
+ const char digits[] = "0123456789abcdef";
+ for (int i = 0; i < len; i++) {
+ dest[i] = digits[(num >> ((len - 1 - i) * 4)) & 0xf];
+ }
+}
+
+void guid_to_string(const struct guid *guid, char *s) {
+ uint_to_hex(guid->a, s, 8);
+ s[8] = '-';
+ uint_to_hex(guid->b, s + 9, 4);
+ s[13] = '-';
+ uint_to_hex(guid->c, s + 14, 4);
+ s[18] = '-';
+ uint_to_hex(guid->d[0], s + 19, 2);
+ uint_to_hex(guid->d[1], s + 21, 2);
+ s[23] = '-';
+ uint_to_hex(guid->d[2], s + 24, 2);
+ uint_to_hex(guid->d[3], s + 26, 2);
+ uint_to_hex(guid->d[4], s + 28, 2);
+ uint_to_hex(guid->d[5], s + 30, 2);
+ uint_to_hex(guid->d[6], s + 32, 2);
+ uint_to_hex(guid->d[7], s + 34, 2);
+ s[36] = 0;
+}
diff --git a/common/lib/guid.h b/common/lib/guid.h
index c360e88e..82b41c0f 100644
--- a/common/lib/guid.h
+++ b/common/lib/guid.h
@@ -14,5 +14,7 @@ struct guid {
bool is_valid_guid(const char *s);
bool string_to_guid_be(struct guid *guid, const char *s);
bool string_to_guid_mixed(struct guid *guid, const char *s);
+// Assumption: s must be big enough to fit 36 characters and a null byte
+void guid_to_string(const struct guid *guid, char *s);
#endif
diff --git a/common/menu.c b/common/menu.c
index 059b51fd..49b1c16a 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -12,6 +12,7 @@
#include <lib/gterm.h>
#include <lib/getchar.h>
#include <lib/uri.h>
+#include <lib/bli.h>
#include <mm/pmm.h>
#include <drivers/vbe.h>
#include <drivers/vga_textmode.h>
@@ -1165,6 +1166,9 @@ noreturn void boot(char *config) {
#if defined (__riscv)
init_riscv(config);
#endif
+#if defined (UEFI)
+ init_bli();
+#endif
char *cmdline = config_get_value(config, 0, "KERNEL_CMDLINE");
if (!cmdline) {
