:: commit 03d62d883c4368af85b1301ed7317c44849e4737

no92 <no92.mail@gmail.com> — 2024-11-03 22:24

parents: 5efc523158

common: support SMBIOS-passed configs with a `limine:config:` prefix

This commit allows for passing configurations via SMBIOS. The first
encountered OEM string (type 11) that begins with a `limine:config:`
prefix is taken as the configuration. This takes priority over
configurations loaded from disk.
diff --git a/common/lib/config.c b/common/lib/config.c
index a0155b93..ab36f00c 100644
--- a/common/lib/config.c
+++ b/common/lib/config.c
@@ -1,5 +1,6 @@
 #include <stddef.h>
 #include <stdbool.h>
+#include <lib/acpi.h>
 #include <lib/config.h>
 #include <lib/libc.h>
 #include <lib/misc.h>
@@ -65,6 +66,66 @@ opened:
     return init_config(config_size);
 }
 
+struct smbios_struct_header {
+    uint8_t type;
+    uint8_t length;
+    uint16_t handle;
+} __attribute__((packed));
+
+static size_t smbios_struct_size(struct smbios_struct_header *hdr) {
+    const char *string_data = (void *)((uintptr_t)hdr + hdr->length);
+    size_t i = 1;
+    for (; string_data[i - 1] != '\0' || string_data[i] != '\0'; i++);
+    return hdr->length + i + 1;
+}
+
+bool init_config_smbios(void) {
+    struct smbios_entry_point_32 *smbios_entry_32 = NULL;
+    struct smbios_entry_point_64 *smbios_entry_64 = NULL;
+    acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64);
+    if (smbios_entry_32 == NULL && smbios_entry_64 == NULL) {
+        return false;
+    }
+
+    struct smbios_struct_header *hdr = NULL;
+    size_t struct_count = 0;
+    size_t struct_max_length = 0;
+
+    if (smbios_entry_64) {
+        hdr = (void *)(uintptr_t) smbios_entry_64->table_address;
+        struct_max_length = smbios_entry_64->max_structure_size;
+    } else {
+        hdr = (void *)(uintptr_t) smbios_entry_32->table_address;
+        struct_count = smbios_entry_32->number_of_structures;
+    }
+
+    size_t structure_bytes_processed = 0;
+    for (size_t struct_num = 0; hdr && (!struct_count || struct_num < struct_count); struct_num++) {
+        if (hdr->type == 127)
+            return false;
+
+        if (hdr->type == 11) {
+            const char *string_data = (void *)((uintptr_t) hdr + hdr->length);
+
+            size_t prefix_len = sizeof("limine:config:") - 1;
+            if (!strncmp(string_data, "limine:config:", prefix_len)) {
+                size_t config_size = strlen(string_data) - prefix_len + 1;
+                config_addr = ext_mem_alloc(config_size);
+                memcpy(config_addr, &string_data[prefix_len], config_size);
+                return !init_config(config_size);
+            }
+        }
+
+        if (struct_max_length && structure_bytes_processed + smbios_struct_size(hdr) >= struct_max_length)
+            return false;
+
+        structure_bytes_processed += smbios_struct_size(hdr);
+        hdr = (void *)((uintptr_t) hdr + smbios_struct_size(hdr));
+    }
+
+    return false;
+}
+
 #define NOT_CHILD      (-1)
 #define DIRECT_CHILD   0
 #define INDIRECT_CHILD 1
diff --git a/common/lib/config.h b/common/lib/config.h
index 54a5cfc3..4921d563 100644
--- a/common/lib/config.h
+++ b/common/lib/config.h
@@ -27,6 +27,7 @@ extern bool config_format_old;
 extern struct menu_entry *menu_tree;
 
 int init_config_disk(struct volume *part);
+bool init_config_smbios(void);
 int init_config(size_t config_size);
 
 char *config_get_value(const char *config, size_t index, const char *key);
diff --git a/common/menu.c b/common/menu.c
index b3bd70fc..8890040b 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -740,18 +740,21 @@ noreturn void _menu(bool first_run) {
     term_fallback();
 
     if (bad_config == false) {
+        if (!init_config_smbios()) {
+
 #if defined (UEFI)
-        if (init_config_disk(boot_volume)) {
+            if (init_config_disk(boot_volume)) {
 #endif
-        volume_iterate_parts(boot_volume,
-            if (!init_config_disk(_PART)) {
-                boot_volume = _PART;
-                break;
-            }
-        );
+            volume_iterate_parts(boot_volume,
+                if (!init_config_disk(_PART)) {
+                    boot_volume = _PART;
+                    break;
+                }
+            );
 #if defined (UEFI)
-        }
+            }
 #endif
+        }
     }
 
     char *quiet_str = config_get_value(NULL, 0, "QUIET");
tab: 248 wrap: offon