From e3db8d60becb9842eb382d78863dd6f3d3756009 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 9 Jan 2024 09:36:44 +0100 Subject: [PATCH 1/6] lib: make table_compute_checksum() arguments const table_compute_checksum() does neither changes the content of the checksummed buffer nor the buffer length. Adding const to the definition makes the function wider usable. Signed-off-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- include/tables_csum.h | 2 +- lib/tables_csum.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tables_csum.h b/include/tables_csum.h index 4812333093a..9207e85f91b 100644 --- a/include/tables_csum.h +++ b/include/tables_csum.h @@ -17,6 +17,6 @@ * @len: configuration table size * @return: the 8-bit checksum */ -u8 table_compute_checksum(void *v, int len); +u8 table_compute_checksum(const void *v, const int len); #endif diff --git a/lib/tables_csum.c b/lib/tables_csum.c index 636aa596768..305b1ec31c5 100644 --- a/lib/tables_csum.c +++ b/lib/tables_csum.c @@ -5,9 +5,9 @@ #include -u8 table_compute_checksum(void *v, int len) +u8 table_compute_checksum(const void *v, const int len) { - u8 *bytes = v; + const u8 *bytes = v; u8 checksum = 0; int i; From dc2fe5d84ea1f285fa0ad1052deee742c8f593e3 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 26 Dec 2023 11:22:28 +0100 Subject: [PATCH 2/6] lib: smbios: verify_checksum() is duplicate The function verify_checksum() duplicates what table_compute_checksum() does. Replace it. table_compute_checksum() is always compiled. Fixes: 415eab0655a8 ("smbios: add parsing API") Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas --- lib/smbios-parser.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/smbios-parser.c b/lib/smbios-parser.c index b578c308408..4a3732bcf2e 100644 --- a/lib/smbios-parser.c +++ b/lib/smbios-parser.c @@ -6,21 +6,7 @@ #define LOG_CATEGORY LOGC_BOOT #include - -static inline int verify_checksum(const struct smbios_entry *e) -{ - /* - * Checksums for SMBIOS tables are calculated to have a value, so that - * the sum over all bytes yields zero (using unsigned 8 bit arithmetic). - */ - u8 *byte = (u8 *)e; - u8 sum = 0; - - for (int i = 0; i < e->length; i++) - sum += byte[i]; - - return sum; -} +#include const struct smbios_entry *smbios_entry(u64 address, u32 size) { @@ -32,7 +18,7 @@ const struct smbios_entry *smbios_entry(u64 address, u32 size) if (memcmp(entry->anchor, "_SM_", 4)) return NULL; - if (verify_checksum(entry)) + if (table_compute_checksum(entry, entry->length)) return NULL; return entry; From 0920bd50dc1d5e58a6afd74fcbd6aaf0405d7c65 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 28 Dec 2023 08:30:23 +0100 Subject: [PATCH 3/6] smbios: enable setting processor family > 0xff Many value of processor type exceed 0xff and have to be stored as u16 value. In the type 4 table set processor_family = 0xfe signaling that field processor_family2 is used and write the actual value into the processor_family2 field. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- lib/smbios.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/smbios.c b/lib/smbios.c index d9d52bd58d7..41aa936c4c4 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -467,7 +467,8 @@ static void smbios_write_type4_dm(struct smbios_type4 *t, } #endif - t->processor_family = processor_family; + t->processor_family = 0xfe; + t->processor_family2 = processor_family; t->processor_manufacturer = smbios_add_prop(ctx, NULL, vendor); t->processor_version = smbios_add_prop(ctx, NULL, name); } @@ -489,7 +490,6 @@ static int smbios_write_type4(ulong *current, int handle, t->l1_cache_handle = 0xffff; t->l2_cache_handle = 0xffff; t->l3_cache_handle = 0xffff; - t->processor_family2 = t->processor_family; len = t->length + smbios_string_table_len(ctx); *current += len; From 1b6228f28d9336620fb508ed198ad8be1eb7aa9c Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Thu, 28 Dec 2023 08:30:24 +0100 Subject: [PATCH 4/6] cpu: riscv: set correct SMBIOS processor family value The SMBIOS specification requires to set the processor family in the type 4 (Processor Information) table to specific values depending only on the bitness of the system (0x200 for RV32 and 0x201 for RV64). With this patch dmidecode shows Handle 0x0004, DMI type 4, 48 bytes Processor Information Socket Designation: Not Specified Type: Central Processor Family: RV64 for qemu-riscv64_smode_defconfig. Signed-off-by: Heinrich Schuchardt --- drivers/cpu/riscv_cpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/cpu/riscv_cpu.c b/drivers/cpu/riscv_cpu.c index d6484d7f4b4..034b9b49c05 100644 --- a/drivers/cpu/riscv_cpu.c +++ b/drivers/cpu/riscv_cpu.c @@ -98,6 +98,10 @@ static int riscv_cpu_bind(struct udevice *dev) /* save the hart id */ plat->cpu_id = dev_read_addr(dev); + if (IS_ENABLED(CONFIG_64BIT)) + plat->family = 0x201; + else + plat->family = 0x200; /* first examine the property in current cpu node */ ret = dev_read_u32(dev, "timebase-frequency", &plat->timebase_freq); /* if not found, then look at the parent /cpus node */ From efe441a0a3ad852b7f6921898775ed57f83f15d9 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 3 Jan 2024 09:07:20 +0100 Subject: [PATCH 5/6] smbios: smbios.h should not import ofnode.h The smbios.h include does not use any definitions from ofnode.h. So don't include it. As DECLARE_GLOBAL_DATA_PTR is no longer defined via dm/of.h we need to add it to efi_smbios.c. Add now missing includes to smbios-parser.c. Remove a superfluous check comparing the sizes of the SMBIOS 2.1 and SMBIOS 3.0 anchors. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- include/smbios.h | 6 +----- lib/efi_loader/efi_smbios.c | 3 +++ lib/smbios-parser.c | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/smbios.h b/include/smbios.h index 49de32fec2d..b507b9d9d72 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -8,7 +8,7 @@ #ifndef _SMBIOS_H_ #define _SMBIOS_H_ -#include +#include /* SMBIOS spec version implemented */ #define SMBIOS_MAJOR_VER 3 @@ -80,10 +80,6 @@ struct __packed smbios3_entry { u64 struct_table_address; }; -/* These two structures should use the same amount of 16-byte-aligned space */ -static_assert(ALIGN(16, sizeof(struct smbios_entry)) == - ALIGN(16, sizeof(struct smbios3_entry))); - /* BIOS characteristics */ #define BIOS_CHARACTERISTICS_PCI_SUPPORTED (1 << 7) #define BIOS_CHARACTERISTICS_UPGRADEABLE (1 << 11) diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index eb6d2ba43c9..b2ec1f79194 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -13,6 +13,9 @@ #include #include #include +#include + +DECLARE_GLOBAL_DATA_PTR; const efi_guid_t smbios3_guid = SMBIOS3_TABLE_GUID; diff --git a/lib/smbios-parser.c b/lib/smbios-parser.c index 4a3732bcf2e..e1180efae18 100644 --- a/lib/smbios-parser.c +++ b/lib/smbios-parser.c @@ -5,8 +5,11 @@ #define LOG_CATEGORY LOGC_BOOT +#include #include +#include #include +#include const struct smbios_entry *smbios_entry(u64 address, u32 size) { From 8aec7031112eba0dbfc8f23f9be11c081ea5cc56 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 3 Jan 2024 09:07:21 +0100 Subject: [PATCH 6/6] efi_loader: provide tool to dump SMBIOS table An EFI binary smbiosdump.efi is provided that can be used to check the SMBIOS table for consistency and to dump it as a file. The tool provides the following commands: check Check the SMBIOS table for consistency. exit Leave the tool. help Show available commands. save Save the SMBIOS table to a file on the EFI system partition. The file can be further analyzed with the dmidecode command line tool:: dmidecode --from-dump Specifying 'nocolor' as load option data suppresses colored output and clearing of the screen. Signed-off-by: Heinrich Schuchardt Acked-by: Ilias Apalodimas --- lib/efi_loader/Makefile | 7 + lib/efi_loader/smbiosdump.c | 622 ++++++++++++++++++++++++++++++++++++ 2 files changed, 629 insertions(+) create mode 100644 lib/efi_loader/smbiosdump.c diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 24d33d54099..25060bbd95a 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -16,6 +16,8 @@ CFLAGS_boothart.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_boothart.o := $(CFLAGS_NON_EFI) CFLAGS_helloworld.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_helloworld.o := $(CFLAGS_NON_EFI) +CFLAGS_smbiosdump.o := $(CFLAGS_EFI) -Os -ffreestanding +CFLAGS_REMOVE_smbiosdump.o := $(CFLAGS_NON_EFI) CFLAGS_dtbdump.o := $(CFLAGS_EFI) -Os -ffreestanding CFLAGS_REMOVE_dtbdump.o := $(CFLAGS_NON_EFI) CFLAGS_initrddump.o := $(CFLAGS_EFI) -Os -ffreestanding @@ -31,6 +33,11 @@ always += helloworld.efi targets += helloworld.o endif +ifneq ($(CONFIG_GENERATE_SMBIOS_TABLE),) +always += smbiosdump.efi +targets += smbiosdump.o +endif + ifeq ($(CONFIG_GENERATE_ACPI_TABLE),) always += dtbdump.efi targets += dtbdump.o diff --git a/lib/efi_loader/smbiosdump.c b/lib/efi_loader/smbiosdump.c new file mode 100644 index 00000000000..f0b901897e9 --- /dev/null +++ b/lib/efi_loader/smbiosdump.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2023, Heinrich Schuchardt + * + * smbiosdump.efi saves the SMBIOS table as file. + * + * Specifying 'nocolor' as load option data suppresses colored output and + * clearing of the screen. + */ + +#include +#include +#include +#include + +#define BUFFER_SIZE 64 + +static struct efi_simple_text_output_protocol *cerr; +static struct efi_simple_text_output_protocol *cout; +static struct efi_simple_text_input_protocol *cin; +static struct efi_boot_services *bs; +static efi_handle_t handle; +static struct efi_system_table *systable; +static const efi_guid_t smbios_guid = SMBIOS_TABLE_GUID; +static const efi_guid_t smbios3_guid = SMBIOS3_TABLE_GUID; +static const efi_guid_t loaded_image_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID; +static const efi_guid_t guid_simple_file_system_protocol = + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +static const efi_guid_t efi_system_partition_guid = PARTITION_SYSTEM_GUID; +static bool nocolor; + +/** + * color() - set foreground color + * + * @color: foreground color + */ +static void color(u8 color) +{ + if (!nocolor) + cout->set_attribute(cout, color | EFI_BACKGROUND_BLACK); +} + +/** + * print() - print string + * + * @string: text + */ +static void print(u16 *string) +{ + cout->output_string(cout, string); +} + +/** + * cls() - clear screen + */ +static void cls(void) +{ + if (nocolor) + print(u"\r\n"); + else + cout->clear_screen(cout); +} + +/** + * error() - print error string + * + * @string: error text + */ +static void error(u16 *string) +{ + color(EFI_LIGHTRED); + print(string); + color(EFI_LIGHTBLUE); +} + +/** + * efi_input_yn() - get answer to yes/no question + * + * Return: + * y or Y + * EFI_SUCCESS + * n or N + * EFI_ACCESS_DENIED + * ESC + * EFI_ABORTED + */ +static efi_status_t efi_input_yn(void) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_status_t ret; + + /* Drain the console input */ + ret = cin->reset(cin, true); + for (;;) { + ret = bs->wait_for_event(1, &cin->wait_for_key, &index); + if (ret != EFI_SUCCESS) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + return EFI_ABORTED; + default: + break; + } + /* Convert to lower case */ + switch (key.unicode_char | 0x20) { + case 'y': + return EFI_SUCCESS; + case 'n': + return EFI_ACCESS_DENIED; + default: + break; + } + } +} + +/** + * efi_input() - read string from console + * + * @buffer: input buffer + * @buffer_size: buffer size + * Return: status code + */ +static efi_status_t efi_input(u16 *buffer, efi_uintn_t buffer_size) +{ + struct efi_input_key key = {0}; + efi_uintn_t index; + efi_uintn_t pos = 0; + u16 outbuf[2] = u" "; + efi_status_t ret; + + /* Drain the console input */ + ret = cin->reset(cin, true); + *buffer = 0; + for (;;) { + ret = bs->wait_for_event(1, &cin->wait_for_key, &index); + if (ret != EFI_SUCCESS) + continue; + ret = cin->read_key_stroke(cin, &key); + if (ret != EFI_SUCCESS) + continue; + switch (key.scan_code) { + case 0x17: /* Escape */ + print(u"\r\nAborted\r\n"); + return EFI_ABORTED; + default: + break; + } + switch (key.unicode_char) { + case 0x08: /* Backspace */ + if (pos) { + buffer[pos--] = 0; + print(u"\b \b"); + } + break; + case 0x0a: /* Linefeed */ + case 0x0d: /* Carriage return */ + print(u"\r\n"); + return EFI_SUCCESS; + default: + break; + } + /* Ignore surrogate codes */ + if (key.unicode_char >= 0xD800 && key.unicode_char <= 0xDBFF) + continue; + if (key.unicode_char >= 0x20 && + pos < buffer_size - 1) { + *outbuf = key.unicode_char; + buffer[pos++] = key.unicode_char; + buffer[pos] = 0; + print(outbuf); + } + } +} + +/** + * skip_whitespace() - skip over leading whitespace + * + * @pos: UTF-16 string + * Return: pointer to first non-whitespace + */ +static u16 *skip_whitespace(u16 *pos) +{ + for (; *pos && *pos <= 0x20; ++pos) + ; + return pos; +} + +/** + * starts_with() - check if @string starts with @keyword + * + * @string: string to search for keyword + * @keyword: keyword to be searched + * Return: true fi @string starts with the keyword + */ +static bool starts_with(u16 *string, u16 *keyword) +{ + if (!string || !keyword) + return NULL; + + for (; *keyword; ++string, ++keyword) { + if (*string != *keyword) + return false; + } + return true; +} + +/** + * open_file_system() - open simple file system protocol + * + * file_system: interface of the simple file system protocol + * Return: status code + */ +static efi_status_t +open_file_system(struct efi_simple_file_system_protocol **file_system) +{ + struct efi_loaded_image *loaded_image; + efi_status_t ret; + efi_handle_t *handle_buffer = NULL; + efi_uintn_t count; + + ret = bs->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(u"Loaded image protocol not found\r\n"); + return ret; + } + + /* Open the simple file system protocol on the same partition */ + ret = bs->open_protocol(loaded_image->device_handle, + &guid_simple_file_system_protocol, + (void **)file_system, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret == EFI_SUCCESS) + return ret; + + /* Open the simple file system protocol on the UEFI system partition */ + ret = bs->locate_handle_buffer(BY_PROTOCOL, &efi_system_partition_guid, + NULL, &count, &handle_buffer); + if (ret == EFI_SUCCESS && handle_buffer) + ret = bs->open_protocol(handle_buffer[0], + &guid_simple_file_system_protocol, + (void **)file_system, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) + error(u"Failed to open simple file system protocol\r\n"); + if (handle) + bs->free_pool(handle_buffer); + + return ret; +} + +/** + * do_help() - print help + */ +static void do_help(void) +{ + error(u"check - check SMBIOS table\r\n"); + error(u"save - save SMBIOS table to file\r\n"); + error(u"exit - exit the shell\r\n"); +} + +/** + * get_config_table() - get configuration table + * + * @guid: GUID of the configuration table + * Return: pointer to configuration table or NULL + */ +static void *get_config_table(const efi_guid_t *guid) +{ + size_t i; + + for (i = 0; i < systable->nr_tables; ++i) { + if (!memcmp(guid, &systable->tables[i].guid, 16)) + return systable->tables[i].table; + } + + return NULL; +} + +/** + * checksum() - calculate checksum + * + * @buf: buffer to checksum + * @len: length of buffer + * Return: checksum + */ +u8 checksum(void *buf, int len) +{ + u8 ret = 0; + + for (u8 *ptr = buf; len; --len, ++ptr) + ret -= *ptr; + + return ret; +} + +/** + * do_check() - check SMBIOS table + * + * Return: status code + */ +efi_status_t do_check(void) +{ + struct smbios3_entry *smbios3_anchor; + void *table, *table_end; + u32 len; + + smbios3_anchor = get_config_table(&smbios3_guid); + if (smbios3_anchor) { + int r; + + r = memcmp(smbios3_anchor->anchor, "_SM3_", 5); + if (r) { + error(u"Invalid anchor string\n"); + return EFI_LOAD_ERROR; + } + print(u"Found SMBIOS 3 entry point\n"); + if (smbios3_anchor->length != 0x18) { + error(u"Invalid anchor length\n"); + return EFI_LOAD_ERROR; + } + if (checksum(smbios3_anchor, smbios3_anchor->length)) { + error(u"Invalid anchor checksum\n"); + return EFI_LOAD_ERROR; + } + table = (void *)(uintptr_t)smbios3_anchor->struct_table_address; + len = smbios3_anchor->max_struct_size; + } else { + struct smbios_entry *smbios_anchor; + int r; + + smbios_anchor = get_config_table(&smbios_guid); + if (!smbios_anchor) { + error(u"No SMBIOS table\n"); + return EFI_NOT_FOUND; + } + r = memcmp(smbios_anchor->anchor, "_SM_", 4); + if (r) { + error(u"Invalid anchor string\n"); + return EFI_LOAD_ERROR; + } + print(u"Found SMBIOS 2.1 entry point\n"); + if (smbios_anchor->length != 0x1f) { + error(u"Invalid anchor length\n"); + return EFI_LOAD_ERROR; + } + if (checksum(smbios_anchor, smbios_anchor->length)) { + error(u"Invalid anchor checksum\n"); + return EFI_LOAD_ERROR; + } + r = memcmp(smbios_anchor->intermediate_anchor, "_DMI_", 5); + if (r) { + error(u"Invalid intermediate anchor string\n"); + return EFI_LOAD_ERROR; + } + if (checksum(&smbios_anchor->intermediate_anchor, 0xf)) { + error(u"Invalid intermediate anchor checksum\n"); + return EFI_LOAD_ERROR; + } + table = (void *)(uintptr_t)smbios_anchor->struct_table_address; + len = smbios_anchor->struct_table_length; + } + + table_end = (void *)((u8 *)table + len); + for (struct smbios_header *pos = table; ;) { + u8 *str = (u8 *)pos + pos->length; + + if (!*str) + ++str; + while (*str) { + for (; *str; ++str) { + if ((void *)str >= table_end) { + error(u"Structure table length exceeded\n"); + return EFI_LOAD_ERROR; + } + } + ++str; + } + ++str; + if ((void *)str > table_end) { + error(u"Structure table length exceeded\n"); + return EFI_LOAD_ERROR; + } + if (pos->type == 0x7f) /* End of table */ + break; + pos = (struct smbios_header *)str; + } + + return EFI_SUCCESS; +} + +/** + * save_file() - save file to EFI system partition + * + * @filename: file name + * @buf: buffer to write + * @size: size of the buffer + */ +efi_status_t save_file(u16 *filename, void *buf, efi_uintn_t size) +{ + efi_uintn_t ret; + struct efi_simple_file_system_protocol *file_system; + struct efi_file_handle *root, *file; + + ret = open_file_system(&file_system); + if (ret != EFI_SUCCESS) + return ret; + + /* Open volume */ + ret = file_system->open_volume(file_system, &root); + if (ret != EFI_SUCCESS) { + error(u"Failed to open volume\r\n"); + return ret; + } + /* Check if file already exists */ + ret = root->open(root, &file, filename, EFI_FILE_MODE_READ, 0); + if (ret == EFI_SUCCESS) { + file->close(file); + print(u"Overwrite existing file (y/n)? "); + ret = efi_input_yn(); + print(u"\r\n"); + if (ret != EFI_SUCCESS) { + root->close(root); + error(u"Aborted by user\r\n"); + bs->free_pool(buf); + return ret; + } + } + + /* Create file */ + ret = root->open(root, &file, filename, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | + EFI_FILE_MODE_CREATE, EFI_FILE_ARCHIVE); + if (ret == EFI_SUCCESS) { + /* Write file */ + ret = file->write(file, &size, buf); + if (ret != EFI_SUCCESS) + error(u"Failed to write file\r\n"); + file->close(file); + } else { + error(u"Failed to open file\r\n"); + } + root->close(root); + + return ret; +} + +/** + * do_save() - save SMBIOS table + * + * @filename: file name + * Return: status code + */ +static efi_status_t do_save(u16 *filename) +{ + struct smbios3_entry *smbios3_anchor; + u8 *buf; + efi_uintn_t size; + efi_uintn_t ret; + + ret = do_check(); + if (ret != EFI_SUCCESS) + return ret; + + smbios3_anchor = get_config_table(&smbios3_guid); + if (smbios3_anchor) { + size = 0x20 + smbios3_anchor->max_struct_size; + ret = bs->allocate_pool(EFI_LOADER_DATA, size, (void **)&buf); + if (ret != EFI_SUCCESS) { + error(u"Out of memory\n"); + return ret; + } + + memset(buf, 0, size); + memcpy(buf, smbios3_anchor, smbios3_anchor->length); + memcpy(buf + 0x20, + (void *)(uintptr_t)smbios3_anchor->struct_table_address, + smbios3_anchor->max_struct_size); + + smbios3_anchor = (struct smbios3_entry *)buf; + smbios3_anchor->struct_table_address = 0x20; + smbios3_anchor->checksum += + checksum(smbios3_anchor, smbios3_anchor->length); + } else { + struct smbios_entry *smbios_anchor; + + smbios_anchor = get_config_table(&smbios_guid); + if (!smbios_anchor) { + /* Should not be reached after successful do_check() */ + error(u"No SMBIOS table\n"); + return EFI_NOT_FOUND; + } + + size = 0x20 + smbios_anchor->struct_table_length; + + ret = bs->allocate_pool(EFI_LOADER_DATA, size, (void **)&buf); + if (ret != EFI_SUCCESS) { + error(u"Out of memory\n"); + return ret; + } + + memset(buf, 0, size); + memcpy(buf, smbios_anchor, smbios_anchor->length); + memcpy(buf + 0x20, + (void *)(uintptr_t)smbios_anchor->struct_table_address, + smbios_anchor->struct_table_length); + + smbios_anchor = (struct smbios_entry *)buf; + smbios_anchor->struct_table_address = 0x20; + smbios_anchor->intermediate_checksum += + checksum(&smbios_anchor->intermediate_anchor, 0xf); + smbios_anchor->checksum += + checksum(smbios_anchor, smbios_anchor->length); + } + + filename = skip_whitespace(filename); + + ret = save_file(filename, buf, size); + + if (ret == EFI_SUCCESS) { + print(filename); + print(u" written\r\n"); + } + + bs->free_pool(buf); + + return ret; +} + +/** + * get_load_options() - get load options + * + * Return: load options or NULL + */ +static u16 *get_load_options(void) +{ + efi_status_t ret; + struct efi_loaded_image *loaded_image; + + ret = bs->open_protocol(handle, &loaded_image_guid, + (void **)&loaded_image, NULL, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) { + error(u"Loaded image protocol not found\r\n"); + return NULL; + } + + if (!loaded_image->load_options_size || !loaded_image->load_options) + return NULL; + + return loaded_image->load_options; +} + +/** + * command_loop - process user commands + */ +static void command_loop(void) +{ + for (;;) { + u16 command[BUFFER_SIZE]; + u16 *pos; + efi_uintn_t ret; + + print(u"=> "); + ret = efi_input(command, sizeof(command)); + if (ret == EFI_ABORTED) + break; + pos = skip_whitespace(command); + if (starts_with(pos, u"exit")) { + break; + } else if (starts_with(pos, u"check")) { + ret = do_check(); + if (ret == EFI_SUCCESS) + print(u"OK\n"); + } else if (starts_with(pos, u"save ")) { + do_save(pos + 5); + } else { + do_help(); + } + } +} + +/** + * efi_main() - entry point of the EFI application. + * + * @handle: handle of the loaded image + * @systab: system table + * Return: status code + */ +efi_status_t EFIAPI efi_main(efi_handle_t image_handle, + struct efi_system_table *systab) +{ + u16 *load_options; + + handle = image_handle; + systable = systab; + cerr = systable->std_err; + cout = systable->con_out; + cin = systable->con_in; + bs = systable->boottime; + load_options = get_load_options(); + + if (starts_with(load_options, u"nocolor")) + nocolor = true; + + color(EFI_WHITE); + cls(); + print(u"SMBIOS Dump\r\n===========\r\n\r\n"); + color(EFI_LIGHTBLUE); + + command_loop(); + + color(EFI_LIGHTGRAY); + cls(); + + return EFI_SUCCESS; +}