diff --git a/common/lib/acpi.h b/common/lib/acpi.h index 695c9b14..b674e78d 100644 --- a/common/lib/acpi.h +++ b/common/lib/acpi.h @@ -98,6 +98,7 @@ struct smbios_entry_point_64 { /// Size of the largest SMBIOS structure, in bytes, and encompasses the /// structure’s formatted area and text strings uint16_t max_structure_size; + uint16_t padding; /// 64-bit physical starting address of the read-only SMBIOS Structure /// Table. uint64_t table_address; 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 #include +#include #include #include #include @@ -65,6 +66,66 @@ int init_config_disk(struct volume *part) { 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");