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");
