From 58c638330ab15c8ee465fa61e40c66234f9e7909 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:44 -0700 Subject: [PATCH 01/26] smbios: Refactor 32-bit code into an else statement In preparation for adding support for SMBIOS3 move this code into an else statement. There is no functional change. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- lib/smbios.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/smbios.c b/lib/smbios.c index 45480b01af..b417f8a905 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -589,14 +589,6 @@ ulong write_smbios_table(ulong addr) len += tmp; } - memcpy(se->anchor, "_SM_", 4); - se->length = sizeof(struct smbios_entry); - se->major_ver = SMBIOS_MAJOR_VER; - se->minor_ver = SMBIOS_MINOR_VER; - se->max_struct_size = max_struct_size; - memcpy(se->intermediate_anchor, "_DMI_", 5); - se->struct_table_length = len; - /* * We must use a pointer here so things work correctly on sandbox. The * user of this table is not aware of the mapping of addresses to @@ -612,16 +604,28 @@ ulong write_smbios_table(ulong addr) (unsigned long long)table_addr); addr = 0; goto out; + } else { + memcpy(se->anchor, "_SM_", 4); + se->length = sizeof(struct smbios_entry); + se->major_ver = SMBIOS_MAJOR_VER; + se->minor_ver = SMBIOS_MINOR_VER; + se->max_struct_size = max_struct_size; + memcpy(se->intermediate_anchor, "_DMI_", 5); + se->struct_table_length = len; + + se->struct_table_address = table_addr; + + se->struct_count = handle; + + /* calculate checksums */ + istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET; + isize = sizeof(struct smbios_entry) - + SMBIOS_INTERMEDIATE_OFFSET; + se->intermediate_checksum = table_compute_checksum(istart, + isize); + se->checksum = table_compute_checksum(se, + sizeof(struct smbios_entry)); } - se->struct_table_address = table_addr; - - se->struct_count = handle; - - /* calculate checksums */ - istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET; - isize = sizeof(struct smbios_entry) - SMBIOS_INTERMEDIATE_OFFSET; - se->intermediate_checksum = table_compute_checksum(istart, isize); - se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry)); out: unmap_sysmem(se); From f19cf8d43adb061f1e744447a4676322cd326829 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:45 -0700 Subject: [PATCH 02/26] smbios: Move the rest of the SMBIOS2 code Move all of this logic into the else clause, since it will not be used for SMBIOS3 Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- lib/smbios.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/smbios.c b/lib/smbios.c index b417f8a905..eea72670bd 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -544,9 +544,8 @@ static struct smbios_write_method smbios_write_funcs[] = { ulong write_smbios_table(ulong addr) { ofnode parent_node = ofnode_null(); - struct smbios_entry *se; + ulong table_addr, start_addr; struct smbios_ctx ctx; - ulong table_addr; ulong tables; int len = 0; int max_struct_size = 0; @@ -566,9 +565,7 @@ ulong write_smbios_table(ulong addr) /* 16 byte align the table address */ addr = ALIGN(addr, 16); - - se = map_sysmem(addr, sizeof(struct smbios_entry)); - memset(se, 0, sizeof(struct smbios_entry)); + start_addr = addr; addr += sizeof(struct smbios_entry); addr = ALIGN(addr, 16); @@ -603,8 +600,11 @@ ulong write_smbios_table(ulong addr) printf("WARNING: SMBIOS table_address overflow %llx\n", (unsigned long long)table_addr); addr = 0; - goto out; } else { + struct smbios_entry *se; + + se = map_sysmem(start_addr, sizeof(struct smbios_entry)); + memset(se, '\0', sizeof(struct smbios_entry)); memcpy(se->anchor, "_SM_", 4); se->length = sizeof(struct smbios_entry); se->major_ver = SMBIOS_MAJOR_VER; @@ -625,9 +625,8 @@ ulong write_smbios_table(ulong addr) isize); se->checksum = table_compute_checksum(se, sizeof(struct smbios_entry)); + unmap_sysmem(se); } -out: - unmap_sysmem(se); return addr; } From de4b91ca686f1bedf46319f31b626cfe26665810 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 31 Dec 2023 08:25:46 -0700 Subject: [PATCH 03/26] smbios: SMBIOS 3.0 (64-bit) Entry Point structure Add definition of the SMBIOS 3.0 (64-bit) Entry Point structure. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas Signed-off-by: Simon Glass --- include/smbios.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/smbios.h b/include/smbios.h index c9df2706f5..e601283d29 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -54,6 +54,32 @@ struct __packed smbios_entry { u8 bcd_rev; }; +/** + * struct smbios3_entry - SMBIOS 3.0 (64-bit) Entry Point structure + */ +struct __packed smbios3_entry { + /** @anchor: anchor string */ + u8 anchor[5]; + /** @checksum: checksum of the entry point structure */ + u8 checksum; + /** @length: length of the entry point structure */ + u8 length; + /** @major_ver: major version of the SMBIOS specification */ + u8 major_ver; + /** @minor_ver: minor version of the SMBIOS specification */ + u8 minor_ver; + /** @docrev: revision of the SMBIOS specification */ + u8 doc_rev; + /** @entry_point_rev: revision of the entry point structure */ + u8 entry_point_rev; + /** @reserved: reserved */ + u8 reserved; + /** maximum size of SMBIOS table */ + u32 max_struct_size; + /** @struct_table_address: 64-bit physical starting address */ + u64 struct_table_address; +}; + /* BIOS characteristics */ #define BIOS_CHARACTERISTICS_PCI_SUPPORTED (1 << 7) #define BIOS_CHARACTERISTICS_UPGRADEABLE (1 << 11) From 70924294f375c351339f029b9615b52608e04cf4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:47 -0700 Subject: [PATCH 04/26] smbios: Use SMBIOS 3.0 to support an address above 4GB When the SMBIOS table is written to an address above 4GB a 32-bit table address is not large enough. Use an SMBIOS3 table in that case. Note that we cannot use efi_allocate_pages() since this function has nothing to do with EFI. There is no equivalent function to allocate memory below 4GB in U-Boot. One solution would be to create a separate malloc() pool, or just always put the malloc() pool below 4GB. - Use log_debug() for warning - Rebase on Heinrich's smbios.h patch - Set the checksum for SMBIOS3 Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- include/smbios.h | 6 +++++- lib/smbios.c | 30 +++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/include/smbios.h b/include/smbios.h index e601283d29..77be58887a 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -12,7 +12,7 @@ /* SMBIOS spec version implemented */ #define SMBIOS_MAJOR_VER 3 -#define SMBIOS_MINOR_VER 0 +#define SMBIOS_MINOR_VER 7 enum { SMBIOS_STR_MAX = 64, /* Maximum length allowed for a string */ @@ -80,6 +80,10 @@ 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/smbios.c b/lib/smbios.c index eea72670bd..7f79d969c9 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -567,7 +567,11 @@ ulong write_smbios_table(ulong addr) addr = ALIGN(addr, 16); start_addr = addr; - addr += sizeof(struct smbios_entry); + /* + * So far we don't know which struct will be used, but they both end + * up using the same amount of 16-bit-aligned space + */ + addr += max(sizeof(struct smbios_entry), sizeof(struct smbios3_entry)); addr = ALIGN(addr, 16); tables = addr; @@ -590,16 +594,32 @@ ulong write_smbios_table(ulong addr) * We must use a pointer here so things work correctly on sandbox. The * user of this table is not aware of the mapping of addresses to * sandbox's DRAM buffer. + * + * Check the address of the end of the tables. If it is above 4GB then + * it is sensible to use SMBIOS3 even if the start of the table is below + * 4GB (this case is very unlikely to happen in practice) */ table_addr = (ulong)map_sysmem(tables, 0); - if (sizeof(table_addr) > sizeof(u32) && table_addr > (ulong)UINT_MAX) { + if (sizeof(table_addr) > sizeof(u32) && addr >= (ulong)UINT_MAX) { + struct smbios3_entry *se; /* * We need to put this >32-bit pointer into the table but the * field is only 32 bits wide. */ - printf("WARNING: SMBIOS table_address overflow %llx\n", - (unsigned long long)table_addr); - addr = 0; + log_debug("WARNING: Using SMBIOS3.0 due to table-address overflow %lx\n", + table_addr); + se = map_sysmem(start_addr, sizeof(struct smbios_entry)); + memset(se, '\0', sizeof(struct smbios_entry)); + memcpy(se->anchor, "_SM3_", 5); + se->length = sizeof(struct smbios3_entry); + se->major_ver = SMBIOS_MAJOR_VER; + se->minor_ver = SMBIOS_MINOR_VER; + se->doc_rev = 0; + se->entry_point_rev = 1; + se->max_struct_size = len; + se->struct_table_address = table_addr; + se->checksum = table_compute_checksum(se, + sizeof(struct smbios3_entry)); } else { struct smbios_entry *se; From b2b58e1ef5c3f60ce6db1d6192c265d4ee45b170 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:48 -0700 Subject: [PATCH 05/26] smbios: Correct gd_smbios_start() This should access arch-specific properties. Fix it and update the existing usage. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- include/asm-generic/global_data.h | 2 +- lib/efi_loader/efi_smbios.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 99bde9ec7e..fcc3c6e14c 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -553,7 +553,7 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #endif #ifdef CONFIG_SMBIOS -#define gd_smbios_start() gd->smbios_start +#define gd_smbios_start() gd->arch.smbios_start #define gd_set_smbios_start(addr) gd->arch.smbios_start = addr #else #define gd_smbios_start() 0UL diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index bbb8421ce1..49adc87e45 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -28,7 +28,7 @@ efi_status_t efi_smbios_register(void) ulong addr; efi_status_t ret; - addr = gd->arch.smbios_start; + addr = gd_smbios_start(); if (!addr) { log_err("No SMBIOS tables to install\n"); return EFI_NOT_FOUND; From 138e69149b84180753da4dca59d4cb4f03da3ca7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:49 -0700 Subject: [PATCH 06/26] efi: Use the correct GUID for the SMBIOS table EFI does not use the 'anchor string' to determine the SMBIOS table version, instead preferring to have two separate GUIDs. Use the correct one, depending on the table version. Call unmap_system() to balance to the use of map_sysmem() Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- include/efi_api.h | 4 ++++ lib/efi_loader/efi_smbios.c | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index 0e92cb8a7f..ab40b1b5dd 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -433,6 +433,10 @@ struct efi_runtime_services { EFI_GUID(0xeb9d2d31, 0x2d88, 0x11d3, \ 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) +#define SMBIOS3_TABLE_GUID \ + EFI_GUID(0xf2fd1544, 0x9794, 0x4a2c, \ + 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94) + #define EFI_LOAD_FILE_PROTOCOL_GUID \ EFI_GUID(0x56ec3091, 0x954c, 0x11d2, \ 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index 49adc87e45..5cbce6dc4e 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -14,6 +14,8 @@ #include #include +const efi_guid_t smbios3_guid = SMBIOS3_TABLE_GUID; + enum { TABLE_SIZE = SZ_4K, }; @@ -25,8 +27,10 @@ enum { */ efi_status_t efi_smbios_register(void) { + const efi_guid_t *guid; ulong addr; efi_status_t ret; + void *buf; addr = gd_smbios_start(); if (!addr) { @@ -42,8 +46,12 @@ efi_status_t efi_smbios_register(void) log_debug("EFI using SMBIOS tables at %lx\n", addr); /* Install SMBIOS information as configuration table */ - return efi_install_configuration_table(&smbios_guid, - map_sysmem(addr, 0)); + buf = map_sysmem(addr, 0); + guid = !memcmp(buf, "_SM_", 4) ? &smbios_guid : &smbios3_guid; + ret = efi_install_configuration_table(guid, buf); + unmap_sysmem(buf); + + return ret; } static int install_smbios_table(void) From 31f950a963a382d48193ecd79698f795b7603846 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:50 -0700 Subject: [PATCH 07/26] smbios: Require the caller to align the SMBIOS table All callers handle this alignment, so drop the unnecessary code. This simplifies things a little. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas --- include/smbios.h | 9 +++++---- lib/smbios.c | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/smbios.h b/include/smbios.h index 77be58887a..49de32fec2 100644 --- a/include/smbios.h +++ b/include/smbios.h @@ -258,12 +258,13 @@ static inline void fill_smbios_header(void *table, int type, * * This writes SMBIOS table at a given address. * - * @addr: start address to write SMBIOS table. If this is not - * 16-byte-aligned then it will be aligned before the table is - * written. + * @addr: start address to write SMBIOS table, 16-byte-alignment + * recommended. Note that while the SMBIOS tables themself have no alignment + * requirement, some systems may requires alignment. For example x86 systems + * which put tables at f0000 require 16-byte alignment + * * Return: end address of SMBIOS table (and start address for next entry) * or NULL in case of an error - * */ ulong write_smbios_table(ulong addr); diff --git a/lib/smbios.c b/lib/smbios.c index 7f79d969c9..cfd451e408 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -563,8 +563,6 @@ ulong write_smbios_table(ulong addr) ctx.dev = NULL; } - /* 16 byte align the table address */ - addr = ALIGN(addr, 16); start_addr = addr; /* From 1c5f6fa3883d18fbdaea53cae99e911748957bb0 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:51 -0700 Subject: [PATCH 08/26] smbios: Drop support for SMBIOS2 tables These tables are a pain since there is no way to handle memory above 4GB. Use SMBIOS3 always. This should hopefully not create problems on x86 devices, since SMBIOS3 was released seven years ago (2015). Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt Reviewed-by: Peter Robinson --- lib/smbios.c | 74 ++++++++++++---------------------------------------- 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/lib/smbios.c b/lib/smbios.c index cfd451e408..d9d52bd58d 100644 --- a/lib/smbios.c +++ b/lib/smbios.c @@ -545,13 +545,12 @@ ulong write_smbios_table(ulong addr) { ofnode parent_node = ofnode_null(); ulong table_addr, start_addr; + struct smbios3_entry *se; struct smbios_ctx ctx; ulong tables; int len = 0; int max_struct_size = 0; int handle = 0; - char *istart; - int isize; int i; ctx.node = ofnode_null(); @@ -565,12 +564,8 @@ ulong write_smbios_table(ulong addr) start_addr = addr; - /* - * So far we don't know which struct will be used, but they both end - * up using the same amount of 16-bit-aligned space - */ - addr += max(sizeof(struct smbios_entry), sizeof(struct smbios3_entry)); - addr = ALIGN(addr, 16); + /* move past the (so-far-unwritten) header to start writing structs */ + addr = ALIGN(addr + sizeof(struct smbios3_entry), 16); tables = addr; /* populate minimum required tables */ @@ -592,59 +587,22 @@ ulong write_smbios_table(ulong addr) * We must use a pointer here so things work correctly on sandbox. The * user of this table is not aware of the mapping of addresses to * sandbox's DRAM buffer. - * - * Check the address of the end of the tables. If it is above 4GB then - * it is sensible to use SMBIOS3 even if the start of the table is below - * 4GB (this case is very unlikely to happen in practice) */ table_addr = (ulong)map_sysmem(tables, 0); - if (sizeof(table_addr) > sizeof(u32) && addr >= (ulong)UINT_MAX) { - struct smbios3_entry *se; - /* - * We need to put this >32-bit pointer into the table but the - * field is only 32 bits wide. - */ - log_debug("WARNING: Using SMBIOS3.0 due to table-address overflow %lx\n", - table_addr); - se = map_sysmem(start_addr, sizeof(struct smbios_entry)); - memset(se, '\0', sizeof(struct smbios_entry)); - memcpy(se->anchor, "_SM3_", 5); - se->length = sizeof(struct smbios3_entry); - se->major_ver = SMBIOS_MAJOR_VER; - se->minor_ver = SMBIOS_MINOR_VER; - se->doc_rev = 0; - se->entry_point_rev = 1; - se->max_struct_size = len; - se->struct_table_address = table_addr; - se->checksum = table_compute_checksum(se, - sizeof(struct smbios3_entry)); - } else { - struct smbios_entry *se; - se = map_sysmem(start_addr, sizeof(struct smbios_entry)); - memset(se, '\0', sizeof(struct smbios_entry)); - memcpy(se->anchor, "_SM_", 4); - se->length = sizeof(struct smbios_entry); - se->major_ver = SMBIOS_MAJOR_VER; - se->minor_ver = SMBIOS_MINOR_VER; - se->max_struct_size = max_struct_size; - memcpy(se->intermediate_anchor, "_DMI_", 5); - se->struct_table_length = len; - - se->struct_table_address = table_addr; - - se->struct_count = handle; - - /* calculate checksums */ - istart = (char *)se + SMBIOS_INTERMEDIATE_OFFSET; - isize = sizeof(struct smbios_entry) - - SMBIOS_INTERMEDIATE_OFFSET; - se->intermediate_checksum = table_compute_checksum(istart, - isize); - se->checksum = table_compute_checksum(se, - sizeof(struct smbios_entry)); - unmap_sysmem(se); - } + /* now go back and write the SMBIOS3 header */ + se = map_sysmem(start_addr, sizeof(struct smbios_entry)); + memset(se, '\0', sizeof(struct smbios_entry)); + memcpy(se->anchor, "_SM3_", 5); + se->length = sizeof(struct smbios3_entry); + se->major_ver = SMBIOS_MAJOR_VER; + se->minor_ver = SMBIOS_MINOR_VER; + se->doc_rev = 0; + se->entry_point_rev = 1; + se->max_struct_size = len; + se->struct_table_address = table_addr; + se->checksum = table_compute_checksum(se, sizeof(struct smbios3_entry)); + unmap_sysmem(se); return addr; } From aa8499680c14afa80ed731b4cfcd6934b4f93712 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:52 -0700 Subject: [PATCH 09/26] efi: smbios: Drop support for SMBIOS2 tables Only the v3 table is supported now, so always use this when installing the EFI table. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_smbios.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index 5cbce6dc4e..5db342ee0d 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -27,7 +27,6 @@ enum { */ efi_status_t efi_smbios_register(void) { - const efi_guid_t *guid; ulong addr; efi_status_t ret; void *buf; @@ -47,8 +46,7 @@ efi_status_t efi_smbios_register(void) /* Install SMBIOS information as configuration table */ buf = map_sysmem(addr, 0); - guid = !memcmp(buf, "_SM_", 4) ? &smbios_guid : &smbios3_guid; - ret = efi_install_configuration_table(guid, buf); + ret = efi_install_configuration_table(&smbios3_guid, buf); unmap_sysmem(buf); return ret; From 5e3adc44a4da72b25fa78e9f9847a8c297b3ebaf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:53 -0700 Subject: [PATCH 10/26] acpi: Rename test dm_test_setup_ctx_and_base_tables() Use the word 'acpi' in this test so that it runs along with all the other ACPI tests. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- test/dm/acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dm/acpi.c b/test/dm/acpi.c index fb071902b4..1211e2f0e7 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -330,7 +330,7 @@ static int dm_test_acpi_basic(struct unit_test_state *uts) DM_TEST(dm_test_acpi_basic, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); /* Test setup_ctx_and_base_tables */ -static int dm_test_setup_ctx_and_base_tables(struct unit_test_state *uts) +static int dm_test_acpi_ctx_and_base_tables(struct unit_test_state *uts) { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; @@ -376,7 +376,7 @@ static int dm_test_setup_ctx_and_base_tables(struct unit_test_state *uts) return 0; } -DM_TEST(dm_test_setup_ctx_and_base_tables, +DM_TEST(dm_test_acpi_ctx_and_base_tables, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); /* Test 'acpi list' command */ From a8efebe71978b3b21e04c7f104987ada879e0800 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:54 -0700 Subject: [PATCH 11/26] acpi: Write pointers to tables instead of addresses Sandbox uses an API to map between addresses and pointers. This allows it to have (emulated) memory at zero and avoid arch-specific addressing details. It also allows memory-mapped peripherals to work. As an example, on many machines sandbox maps address 100 to pointer value 10000000. However this is not correct for ACPI, if sandbox starts another program (e.g EFI app) and passes it the tables. That app has no knowledge of sandbox's address mapping. So to make this work we want to store 10000000 as the value in the table. Add two new 'nomap' functions which clearly make this exeption to how sandbox works. This should allow EFI apps to access ACPI tables with sandbox, e.g. for testing purposes. Signed-off-by: Simon Glass Suggested-by: Heinrich Schuchardt --- arch/sandbox/include/asm/io.h | 16 ++++++++++++++++ arch/x86/lib/acpi_table.c | 6 +++--- cmd/acpi.c | 12 ++++++------ include/mapmem.h | 18 ++++++++++++++++++ lib/acpi/acpi.c | 12 ++++++------ lib/acpi/acpi_table.c | 4 ++-- lib/acpi/base.c | 4 ++-- test/dm/acpi.c | 10 +++++----- 8 files changed, 58 insertions(+), 24 deletions(-) diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index 3c0a102697..a23bd64994 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -231,5 +231,21 @@ static inline void unmap_sysmem(const void *vaddr) unmap_physmem(vaddr, MAP_WRBACK); } +/** + * nomap_sysmem() - pass through an address unchanged + * + * This is used to indicate an address which should NOT be mapped, e.g. in + * SMBIOS tables. Using this function instead of a case shows that the sandbox + * conversion has been done + */ +static inline void *nomap_sysmem(phys_addr_t paddr, unsigned long len) +{ + return (void *)(uintptr_t)paddr; +} + +static inline phys_addr_t nomap_to_sysmem(const void *ptr) +{ + return (phys_addr_t)(uintptr_t)ptr; +} #endif diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index c5b33dc65d..b366e44e33 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -197,7 +197,7 @@ int acpi_write_tcpa(struct acpi_ctx *ctx, const struct acpi_writer *entry) tcpa->platform_class = 0; tcpa->laml = size; - tcpa->lasa = map_to_sysmem(log); + tcpa->lasa = nomap_to_sysmem(log); /* (Re)calculate length and checksum */ current = (u32)tcpa + sizeof(struct acpi_tcpa); @@ -268,7 +268,7 @@ static int acpi_write_tpm2(struct acpi_ctx *ctx, /* Fill the log area size and start address fields. */ tpm2->laml = tpm2_log_len; - tpm2->lasa = map_to_sysmem(lasa); + tpm2->lasa = nomap_to_sysmem(lasa); /* Calculate checksum. */ header->checksum = table_compute_checksum(tpm2, header->length); @@ -430,7 +430,7 @@ int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) u32 *gnvs = (u32 *)((u32)ctx->dsdt + i); if (*gnvs == ACPI_GNVS_ADDR) { - *gnvs = map_to_sysmem(ctx->current); + *gnvs = nomap_to_sysmem(ctx->current); log_debug("Fix up global NVS in DSDT to %#08x\n", *gnvs); break; diff --git a/cmd/acpi.c b/cmd/acpi.c index 0c14409242..79e9335b5d 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -45,7 +45,7 @@ static int dump_table_name(const char *sig) if (!hdr) return -ENOENT; printf("%.*s @ %16lx\n", ACPI_NAME_LEN, hdr->signature, - (ulong)map_to_sysmem(hdr)); + (ulong)nomap_to_sysmem(hdr)); print_buffer(0, hdr, 1, hdr->length, 0); return 0; @@ -54,9 +54,9 @@ static int dump_table_name(const char *sig) static void list_fadt(struct acpi_fadt *fadt) { if (fadt->dsdt) - dump_hdr(map_sysmem(fadt->dsdt, 0)); + dump_hdr(nomap_sysmem(fadt->dsdt, 0)); if (fadt->firmware_ctrl) - dump_hdr(map_sysmem(fadt->firmware_ctrl, 0)); + dump_hdr(nomap_sysmem(fadt->firmware_ctrl, 0)); } static void list_rsdt(struct acpi_rsdp *rsdp) @@ -66,11 +66,11 @@ static void list_rsdt(struct acpi_rsdp *rsdp) struct acpi_xsdt *xsdt; if (rsdp->rsdt_address) { - rsdt = map_sysmem(rsdp->rsdt_address, 0); + rsdt = nomap_sysmem(rsdp->rsdt_address, 0); dump_hdr(&rsdt->header); } if (rsdp->xsdt_address) { - xsdt = map_sysmem(rsdp->xsdt_address, 0); + xsdt = nomap_sysmem(rsdp->xsdt_address, 0); dump_hdr(&xsdt->header); len = xsdt->header.length - sizeof(xsdt->header); count = len / sizeof(u64); @@ -91,7 +91,7 @@ static void list_rsdt(struct acpi_rsdp *rsdp) entry = rsdt->entry[i]; if (!entry) break; - hdr = map_sysmem(entry, 0); + hdr = nomap_sysmem(entry, 0); dump_hdr(hdr); if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) list_fadt((struct acpi_fadt *)hdr); diff --git a/include/mapmem.h b/include/mapmem.h index bb68b4c11a..f496c96d16 100644 --- a/include/mapmem.h +++ b/include/mapmem.h @@ -28,6 +28,24 @@ static inline phys_addr_t map_to_sysmem(const void *ptr) { return (phys_addr_t)(uintptr_t)ptr; } + +/** + * nomap_sysmem() - pass through an address unchanged + * + * This is used to indicate an address which should NOT be mapped, e.g. in + * SMBIOS tables. Using this function instead of a case shows that the sandbox + * conversion has been done + */ +static inline void *nomap_sysmem(phys_addr_t paddr, unsigned long len) +{ + return (void *)(uintptr_t)paddr; +} + +static inline phys_addr_t nomap_to_sysmem(const void *ptr) +{ + return (phys_addr_t)(uintptr_t)ptr; +} + # endif #endif /* __MAPMEM_H */ diff --git a/lib/acpi/acpi.c b/lib/acpi/acpi.c index 939a638bb5..bcafd77175 100644 --- a/lib/acpi/acpi.c +++ b/lib/acpi/acpi.c @@ -22,13 +22,13 @@ struct acpi_table_header *acpi_find_table(const char *sig) if (!rsdp) return NULL; if (rsdp->xsdt_address) { - xsdt = map_sysmem(rsdp->xsdt_address, 0); + xsdt = nomap_sysmem(rsdp->xsdt_address, 0); len = xsdt->header.length - sizeof(xsdt->header); count = len / sizeof(u64); } else { if (!rsdp->rsdt_address) return NULL; - rsdt = map_sysmem(rsdp->rsdt_address, 0); + rsdt = nomap_sysmem(rsdp->rsdt_address, 0); len = rsdt->header.length - sizeof(rsdt->header); count = len / sizeof(u32); } @@ -36,19 +36,19 @@ struct acpi_table_header *acpi_find_table(const char *sig) struct acpi_table_header *hdr; if (rsdp->xsdt_address) - hdr = map_sysmem(xsdt->entry[i], 0); + hdr = nomap_sysmem(xsdt->entry[i], 0); else - hdr = map_sysmem(rsdt->entry[i], 0); + hdr = nomap_sysmem(rsdt->entry[i], 0); if (!memcmp(hdr->signature, sig, ACPI_NAME_LEN)) return hdr; if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) { struct acpi_fadt *fadt = (struct acpi_fadt *)hdr; if (!memcmp(sig, "DSDT", ACPI_NAME_LEN) && fadt->dsdt) - return map_sysmem(fadt->dsdt, 0); + return nomap_sysmem(fadt->dsdt, 0); if (!memcmp(sig, "FACS", ACPI_NAME_LEN) && fadt->firmware_ctrl) - return map_sysmem(fadt->firmware_ctrl, 0); + return nomap_sysmem(fadt->firmware_ctrl, 0); } } diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index e74522e997..39dd53ec40 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -167,7 +167,7 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) } /* Add table to the RSDT */ - rsdt->entry[i] = map_to_sysmem(table); + rsdt->entry[i] = nomap_to_sysmem(table); /* Fix RSDT length or the kernel will assume invalid entries */ rsdt->header.length = sizeof(struct acpi_table_header) + @@ -185,7 +185,7 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) xsdt = ctx->xsdt; /* Add table to the XSDT */ - xsdt->entry[i] = map_to_sysmem(table); + xsdt->entry[i] = nomap_to_sysmem(table); /* Fix XSDT length */ xsdt->header.length = sizeof(struct acpi_table_header) + diff --git a/lib/acpi/base.c b/lib/acpi/base.c index 07b53e0c56..8b6af2bc43 100644 --- a/lib/acpi/base.c +++ b/lib/acpi/base.c @@ -24,10 +24,10 @@ void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, memcpy(rsdp->oem_id, OEM_ID, 6); if (rsdt) - rsdp->rsdt_address = map_to_sysmem(rsdt); + rsdp->rsdt_address = nomap_to_sysmem(rsdt); if (xsdt) - rsdp->xsdt_address = map_to_sysmem(xsdt); + rsdp->xsdt_address = nomap_to_sysmem(xsdt); rsdp->length = sizeof(struct acpi_rsdp); rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 1211e2f0e7..c53ebcdb1c 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -291,8 +291,8 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) /* Check that the pointers were added correctly */ for (i = 0; i < 3; i++) { - ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]); - ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]); + ut_asserteq(nomap_to_sysmem(dmar + i), ctx.rsdt->entry[i]); + ut_asserteq(nomap_to_sysmem(dmar + i), ctx.xsdt->entry[i]); } ut_asserteq(0, ctx.rsdt->entry[3]); ut_asserteq(0, ctx.xsdt->entry[3]); @@ -371,8 +371,8 @@ static int dm_test_acpi_ctx_and_base_tables(struct unit_test_state *uts) end = PTR_ALIGN((void *)xsdt + sizeof(*xsdt), 64); ut_asserteq_ptr(end, ctx.current); - ut_asserteq(map_to_sysmem(rsdt), rsdp->rsdt_address); - ut_asserteq(map_to_sysmem(xsdt), rsdp->xsdt_address); + ut_asserteq(nomap_to_sysmem(rsdt), rsdp->rsdt_address); + ut_asserteq(nomap_to_sysmem(xsdt), rsdp->xsdt_address); return 0; } @@ -445,7 +445,7 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) /* Now a real table */ console_record_reset(); run_command("acpi dump dmar", 0); - addr = ALIGN(map_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64); + addr = ALIGN(nomap_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64); ut_assert_nextline("DMAR @ %16lx", addr); ut_assert_nextlines_are_dump(0x30); ut_assert_console_end(); From 06ef8089f876b6dabf56caba31a05c003f03c629 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 31 Dec 2023 08:25:55 -0700 Subject: [PATCH 12/26] efi: Correct smbios-table installation At present this code allocates memory when writing the tables and then unnecessarily adds another memory map when installing it. Adjust the code to allocate the tables using the normal U-Boot mechanism. This avoids doing an EFI memory allocation early in U-Boot, which may use memory that would be overwritten by a 'load' command, for example. Signed-off-by: Simon Glass --- lib/efi_loader/efi_smbios.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/efi_loader/efi_smbios.c b/lib/efi_loader/efi_smbios.c index 5db342ee0d..eb6d2ba43c 100644 --- a/lib/efi_loader/efi_smbios.c +++ b/lib/efi_loader/efi_smbios.c @@ -54,27 +54,25 @@ efi_status_t efi_smbios_register(void) static int install_smbios_table(void) { - u64 addr; - efi_status_t ret; + ulong addr; + void *buf; if (!IS_ENABLED(CONFIG_GENERATE_SMBIOS_TABLE) || IS_ENABLED(CONFIG_X86)) return 0; - addr = SZ_4G; - ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, - EFI_RUNTIME_SERVICES_DATA, - efi_size_in_pages(TABLE_SIZE), &addr); - if (ret != EFI_SUCCESS) + /* Align the table to a 4KB boundary to keep EFI happy */ + buf = memalign(SZ_4K, TABLE_SIZE); + if (!buf) return log_msg_ret("mem", -ENOMEM); - addr = map_to_sysmem((void *)(uintptr_t)addr); + addr = map_to_sysmem(buf); if (!write_smbios_table(addr)) { log_err("Failed to write SMBIOS table\n"); return log_msg_ret("smbios", -EINVAL); } /* Make a note of where we put it */ - log_debug("SMBIOS tables written to %llx\n", addr); + log_debug("SMBIOS tables written to %lx\n", addr); gd->arch.smbios_start = addr; return 0; From 70fe23859437ffe4efe0793423937d8b78ebf9d6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 3 Jan 2024 18:49:19 -0700 Subject: [PATCH 13/26] fdt: Allow the devicetree to come from a bloblist Standard passage provides for a bloblist to be passed from one firmware phase to the next. That can be used to pass the devicetree along as well. Add an option to support this. Tests for this will be added as part of the Universal Payload work. Signed-off-by: Simon Glass Reviewed-by: Tom Rini Reviewed-by: Ilias Apalodimas --- doc/develop/devicetree/control.rst | 3 ++ include/fdtdec.h | 6 ++-- lib/fdtdec.c | 44 +++++++++++++++++++++++------- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/doc/develop/devicetree/control.rst b/doc/develop/devicetree/control.rst index cbb65c9b17..11c92d440f 100644 --- a/doc/develop/devicetree/control.rst +++ b/doc/develop/devicetree/control.rst @@ -108,6 +108,9 @@ If CONFIG_OF_BOARD is defined, a board-specific routine will provide the devicetree at runtime, for example if an earlier bootloader stage creates it and passes it to U-Boot. +If CONFIG_BLOBLIST is defined, the devicetree may come from a bloblist passed +from a previous stage, if present. + If CONFIG_SANDBOX is defined, then it will be read from a file on startup. Use the -d flag to U-Boot to specify the file to read, -D for the default and -T for the test devicetree, used to run sandbox unit tests. diff --git a/include/fdtdec.h b/include/fdtdec.h index bd1149f46d..e80de24076 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -72,7 +72,7 @@ struct bd_info; * U-Boot is packaged as an ELF file, e.g. for debugging purposes * @FDTSRC_ENV: Provided by the fdtcontroladdr environment variable. This should * be used for debugging/development only - * @FDTSRC_NONE: No devicetree at all + * @FDTSRC_BLOBLIST: Provided by a bloblist from an earlier phase */ enum fdt_source_t { FDTSRC_SEPARATE, @@ -80,6 +80,7 @@ enum fdt_source_t { FDTSRC_BOARD, FDTSRC_EMBED, FDTSRC_ENV, + FDTSRC_BLOBLIST, }; /* @@ -1190,7 +1191,8 @@ int fdtdec_resetup(int *rescan); * * The existing devicetree is available at gd->fdt_blob * - * @err internal error code if we fail to setup a DTB + * @err: 0 on success, -EEXIST if the devicetree is already correct, or other + * internal error code if we fail to setup a DTB * @returns new devicetree blob pointer */ void *board_fdt_blob_setup(int *err); diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 4016bf3c11..b2c59ab381 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -7,6 +7,10 @@ */ #ifndef USE_HOSTCC + +#define LOG_CATEGORY LOGC_DT + +#include #include #include #include @@ -86,6 +90,7 @@ static const char *const fdt_src_name[] = { [FDTSRC_BOARD] = "board", [FDTSRC_EMBED] = "embed", [FDTSRC_ENV] = "env", + [FDTSRC_BLOBLIST] = "bloblist", }; const char *fdtdec_get_srcname(void) @@ -1662,23 +1667,42 @@ static void setup_multi_dtb_fit(void) int fdtdec_setup(void) { - int ret; + int ret = -ENOENT; - /* The devicetree is typically appended to U-Boot */ - if (IS_ENABLED(CONFIG_OF_SEPARATE)) { - gd->fdt_blob = fdt_find_separate(); - gd->fdt_src = FDTSRC_SEPARATE; - } else { /* embed dtb in ELF file for testing / development */ - gd->fdt_blob = dtb_dt_embedded(); - gd->fdt_src = FDTSRC_EMBED; + /* If allowing a bloblist, check that first */ + if (CONFIG_IS_ENABLED(BLOBLIST)) { + ret = bloblist_maybe_init(); + if (!ret) { + gd->fdt_blob = bloblist_find(BLOBLISTT_CONTROL_FDT, 0); + if (gd->fdt_blob) { + gd->fdt_src = FDTSRC_BLOBLIST; + log_debug("Devicetree is in bloblist at %p\n", + gd->fdt_blob); + } else { + log_debug("No FDT found in bloblist\n"); + ret = -ENOENT; + } + } + } + + /* Otherwise, the devicetree is typically appended to U-Boot */ + if (ret) { + if (IS_ENABLED(CONFIG_OF_SEPARATE)) { + gd->fdt_blob = fdt_find_separate(); + gd->fdt_src = FDTSRC_SEPARATE; + } else { /* embed dtb in ELF file for testing / development */ + gd->fdt_blob = dtb_dt_embedded(); + gd->fdt_src = FDTSRC_EMBED; + } } /* Allow the board to override the fdt address. */ if (IS_ENABLED(CONFIG_OF_BOARD)) { gd->fdt_blob = board_fdt_blob_setup(&ret); - if (ret) + if (!ret) + gd->fdt_src = FDTSRC_BOARD; + else if (ret != -EEXIST) return ret; - gd->fdt_src = FDTSRC_BOARD; } /* Allow the early environment to override the fdt address */ From a083ae71e085535e2caa0fd5de991bddced6dc94 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 16 Dec 2023 09:11:57 +0100 Subject: [PATCH 14/26] acpi: use 64-bit addresses in FADT table Fields X_FIRMWAE_CTRL and X_DSDT must be 64bit wide. Convert pointers to to uintptr_t to fill these. If field X_FIRMWARE_CTRL is filled, field FIRMWARE must be ignored. If field X_DSDT is filled, field DSDT must be ignored. We should not fill unused fields. See the field definitions in chapter "5.2.9 Fixed ACPI Description Table (FADT)" of the ACPI Specification 6.5. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- arch/x86/cpu/baytrail/acpi.c | 9 +++------ arch/x86/cpu/quark/acpi.c | 9 +++------ arch/x86/cpu/tangier/acpi.c | 9 +++------ arch/x86/lib/acpi_table.c | 9 ++------- include/acpi/acpi_table.h | 6 ++---- 5 files changed, 13 insertions(+), 29 deletions(-) diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c index 4378846f8b..ccc4851b18 100644 --- a/arch/x86/cpu/baytrail/acpi.c +++ b/arch/x86/cpu/baytrail/acpi.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,6 @@ static int baytrail_write_fadt(struct acpi_ctx *ctx, header->length = sizeof(struct acpi_fadt); header->revision = 4; - fadt->firmware_ctrl = (u32)ctx->facs; - fadt->dsdt = (u32)ctx->dsdt; fadt->preferred_pm_profile = ACPI_PM_MOBILE; fadt->sci_int = 9; fadt->smi_cmd = 0; @@ -79,10 +78,8 @@ static int baytrail_write_fadt(struct acpi_ctx *ctx, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctl_l = (u32)ctx->facs; - fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)ctx->dsdt; - fadt->x_dsdt_h = 0; + fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); + fadt->x_dsdt = map_to_sysmem(ctx->dsdt); fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; diff --git a/arch/x86/cpu/quark/acpi.c b/arch/x86/cpu/quark/acpi.c index 9a2d682451..0e18ceab68 100644 --- a/arch/x86/cpu/quark/acpi.c +++ b/arch/x86/cpu/quark/acpi.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -26,8 +27,6 @@ static int quark_write_fadt(struct acpi_ctx *ctx, header->length = sizeof(struct acpi_fadt); header->revision = 4; - fadt->firmware_ctrl = (u32)ctx->facs; - fadt->dsdt = (u32)ctx->dsdt; fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; fadt->sci_int = 9; fadt->smi_cmd = 0; @@ -74,10 +73,8 @@ static int quark_write_fadt(struct acpi_ctx *ctx, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctl_l = (u32)ctx->facs; - fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)ctx->dsdt; - fadt->x_dsdt_h = 0; + fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); + fadt->x_dsdt = map_to_sysmem(ctx->dsdt); fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 1c667c7d56..1d37cc9e2b 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,6 @@ static int tangier_write_fadt(struct acpi_ctx *ctx, header->length = sizeof(struct acpi_fadt); header->revision = 6; - fadt->firmware_ctrl = (u32)ctx->facs; - fadt->dsdt = (u32)ctx->dsdt; fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; fadt->iapc_boot_arch = ACPI_FADT_VGA_NOT_PRESENT | @@ -45,10 +44,8 @@ static int tangier_write_fadt(struct acpi_ctx *ctx, fadt->minor_revision = 2; - fadt->x_firmware_ctl_l = (u32)ctx->facs; - fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)ctx->dsdt; - fadt->x_dsdt_h = 0; + fadt->x_firmware_ctrl = map_to_sysmem(ctx->facs); + fadt->x_dsdt = map_to_sysmem(ctx->dsdt); header->checksum = table_compute_checksum(fadt, header->length); diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index b366e44e33..5ecd3d4b65 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -572,13 +572,8 @@ void acpi_fadt_common(struct acpi_fadt *fadt, struct acpi_facs *facs, memcpy(header->aslc_id, ASLC_ID, 4); header->aslc_revision = 1; - fadt->firmware_ctrl = (unsigned long)facs; - fadt->dsdt = (unsigned long)dsdt; - - fadt->x_firmware_ctl_l = (unsigned long)facs; - fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (unsigned long)dsdt; - fadt->x_dsdt_h = 0; + fadt->x_firmware_ctrl = map_to_sysmem(facs); + fadt->x_dsdt = map_to_sysmem(dsdt); fadt->preferred_pm_profile = ACPI_PM_MOBILE; diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index 20ac3b51ba..e67562ef65 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -228,10 +228,8 @@ struct __packed acpi_fadt { u8 reset_value; u16 arm_boot_arch; u8 minor_revision; - u32 x_firmware_ctl_l; - u32 x_firmware_ctl_h; - u32 x_dsdt_l; - u32 x_dsdt_h; + u64 x_firmware_ctrl; + u64 x_dsdt; struct acpi_gen_regaddr x_pm1a_evt_blk; struct acpi_gen_regaddr x_pm1b_evt_blk; struct acpi_gen_regaddr x_pm1a_cnt_blk; From c5924b1cd3a6eb980cea59e5f663bb614686b55d Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 16 Dec 2023 09:11:58 +0100 Subject: [PATCH 15/26] cmd: acpi: fix listing DSDT and FACS If field X_FIRMWARE_CTRL is filled, field FIRMWARE must be ignored. If field X_DSDT is filled, field DSDT must be ignored. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Rebased on -next to use nomap: Signed-off-by: Simon Glass --- cmd/acpi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/acpi.c b/cmd/acpi.c index 79e9335b5d..1eca7fe773 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -53,9 +53,13 @@ static int dump_table_name(const char *sig) static void list_fadt(struct acpi_fadt *fadt) { - if (fadt->dsdt) + if (fadt->header.revision >= 3 && fadt->x_dsdt) + dump_hdr(nomap_sysmem(fadt->x_dsdt, 0)); + else if (fadt->dsdt) dump_hdr(nomap_sysmem(fadt->dsdt, 0)); - if (fadt->firmware_ctrl) + if (fadt->header.revision >= 3 && fadt->x_firmware_ctrl) + dump_hdr(nomap_sysmem(fadt->x_firmware_ctrl, 0)); + else if (fadt->firmware_ctrl) dump_hdr(nomap_sysmem(fadt->firmware_ctrl, 0)); } From c95ade8dcd1882d756c43375e91020562bc3886b Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 16 Dec 2023 09:11:59 +0100 Subject: [PATCH 16/26] cmd: acpi: check HW reduced flag in acpi list On non x86 platforms the hardware reduce flag must be set in the FADT table. Write an error message if the flag is missing. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Rebased on -next to use nomap, add hyphens: Signed-off-by: Simon Glass --- cmd/acpi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/acpi.c b/cmd/acpi.c index 1eca7fe773..65caaa5c98 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,9 @@ static void list_fadt(struct acpi_fadt *fadt) dump_hdr(nomap_sysmem(fadt->x_dsdt, 0)); else if (fadt->dsdt) dump_hdr(nomap_sysmem(fadt->dsdt, 0)); + if (!IS_ENABLED(CONFIG_X86) && + !(fadt->flags & ACPI_FADT_HW_REDUCED_ACPI)) + log_err("FADT not ACPI-hardware-reduced-compliant\n"); if (fadt->header.revision >= 3 && fadt->x_firmware_ctrl) dump_hdr(nomap_sysmem(fadt->x_firmware_ctrl, 0)); else if (fadt->firmware_ctrl) From f47c86f6d3b468a4d467814783937ee9162b92fc Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 16 Dec 2023 09:12:00 +0100 Subject: [PATCH 17/26] acpi: support 64bit in acpi_find_table for DSDT and FACS Use X_DSDT and X_FIRMWARE_CTRL if available. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Rebased on -next to use nomap: Signed-off-by: Simon Glass --- lib/acpi/acpi.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/acpi/acpi.c b/lib/acpi/acpi.c index bcafd77175..f4d5c1e25d 100644 --- a/lib/acpi/acpi.c +++ b/lib/acpi/acpi.c @@ -44,11 +44,30 @@ struct acpi_table_header *acpi_find_table(const char *sig) if (!memcmp(hdr->signature, "FACP", ACPI_NAME_LEN)) { struct acpi_fadt *fadt = (struct acpi_fadt *)hdr; - if (!memcmp(sig, "DSDT", ACPI_NAME_LEN) && fadt->dsdt) - return nomap_sysmem(fadt->dsdt, 0); - if (!memcmp(sig, "FACS", ACPI_NAME_LEN) && - fadt->firmware_ctrl) - return nomap_sysmem(fadt->firmware_ctrl, 0); + if (!memcmp(sig, "DSDT", ACPI_NAME_LEN)) { + void *dsdt; + + if (fadt->header.revision >= 3 && fadt->x_dsdt) + dsdt = nomap_sysmem(fadt->x_dsdt, 0); + else if (fadt->dsdt) + dsdt = nomap_sysmem(fadt->dsdt, 0); + else + dsdt = NULL; + return dsdt; + } + + if (!memcmp(sig, "FACS", ACPI_NAME_LEN)) { + void *facs; + + if (fadt->header.revision >= 3 && + fadt->x_firmware_ctrl) + facs = nomap_sysmem(fadt->x_firmware_ctrl, 0); + else if (fadt->firmware_ctrl) + facs = nomap_sysmem(fadt->firmware_ctrl, 0); + else + facs = NULL; + return facs; + } } } From b2f088c1210184b1ba11edaee0943ebd206e87e7 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:00 +0100 Subject: [PATCH 18/26] acpi: Kconfig symbol CONFIG_QFW_ACPI We have two implementations of write_acpi_tables(). One for writing ACPI tables based on ACPI_WRITER() entries another based on copying tables from QEMU. Create a symbol CONFIG_QFW_ACPI that signifies copying ACPI tables from QEMU and use it consistently. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/misc/Kconfig | 7 +++++++ drivers/misc/qfw.c | 4 ++-- lib/acpi/Makefile | 2 +- lib/acpi/acpi_writer.c | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ed7ecedd3a..e8e4400516 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -540,6 +540,13 @@ config QFW Hidden option to enable QEMU fw_cfg interface and uclass. This will be selected by either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. +config QFW_ACPI + bool + default y + depends on QFW && GENERATE_ACPI_TABLE && !SANDBOX + help + Hidden option to read ACPI tables from QEMU. + config QFW_PIO bool depends on QFW diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index e3b6b4cd74..307334faf4 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -21,7 +21,7 @@ #include #include -#if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX) +#ifdef QFW_ACPI /* * This function allocates memory for ACPI tables * @@ -259,7 +259,7 @@ ulong acpi_get_rsdp_addr(void) file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } -#endif +#endif /* QFW_ACPI */ static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, void *address) diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index c1c9675b5d..cc2868488a 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_table.o obj-y += acpi_writer.o # With QEMU the ACPI tables come from there, not from U-Boot -ifndef CONFIG_QEMU +ifndef CONFIG_QFW_ACPI obj-y += base.o obj-y += csrt.o obj-y += mcfg.o diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index a8dc207557..bbb9b54786 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -48,7 +48,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; } -#ifndef CONFIG_QEMU +#ifndef CONFIG_QFW_ACPI static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -115,7 +115,7 @@ ulong acpi_get_rsdp_addr(void) return map_to_sysmem(gd->acpi_ctx->rsdp); } -#endif /* QEMU */ +#endif /* QFW_ACPI */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) { From 27a66fc3f3102bf88df5d7569e3aae88355c6e47 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:01 +0100 Subject: [PATCH 19/26] acpi: carve out qfw_acpi.c Move the code related to copying tables from QEMU to a separate code module. Signed-off-by: Heinrich Schuchardt Reviewed-by: Tom Rini Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas --- drivers/misc/Makefile | 1 + drivers/misc/qfw.c | 240 ------------------------------------- drivers/misc/qfw_acpi.c | 256 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 240 deletions(-) create mode 100644 drivers/misc/qfw_acpi.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b67b82358a..cda701d38e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o ifdef CONFIG_QFW obj-y += qfw.o +obj-$(CONFIG_QFW_ACPI) += qfw_acpi.o obj-$(CONFIG_QFW_PIO) += qfw_pio.o obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o obj-$(CONFIG_SANDBOX) += qfw_sandbox.o diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index 307334faf4..db98619fdf 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -21,246 +21,6 @@ #include #include -#ifdef QFW_ACPI -/* - * This function allocates memory for ACPI tables - * - * @entry : BIOS linker command entry which tells where to allocate memory - * (either high memory or low memory) - * @addr : The address that should be used for low memory allcation. If the - * memory allocation request is 'ZONE_HIGH' then this parameter will - * be ignored. - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_allocate(struct udevice *dev, - struct bios_linker_entry *entry, ulong *addr) -{ - uint32_t size, align; - struct fw_file *file; - unsigned long aligned_addr; - - align = le32_to_cpu(entry->alloc.align); - /* align must be power of 2 */ - if (align & (align - 1)) { - printf("error: wrong alignment %u\n", align); - return -EINVAL; - } - - file = qfw_find_file(dev, entry->alloc.file); - if (!file) { - printf("error: can't find file %s\n", entry->alloc.file); - return -ENOENT; - } - - size = be32_to_cpu(file->cfg.size); - - /* - * ZONE_HIGH means we need to allocate from high memory, since - * malloc space is already at the end of RAM, so we directly use it. - * If allocation zone is ZONE_FSEG, then we use the 'addr' passed - * in which is low memory - */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { - aligned_addr = (unsigned long)memalign(align, size); - if (!aligned_addr) { - printf("error: allocating resource\n"); - return -ENOMEM; - } - if (aligned_addr < gd->arch.table_start_high) - gd->arch.table_start_high = aligned_addr; - if (aligned_addr + size > gd->arch.table_end_high) - gd->arch.table_end_high = aligned_addr + size; - - } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { - aligned_addr = ALIGN(*addr, align); - } else { - printf("error: invalid allocation zone\n"); - return -EINVAL; - } - - debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", - file->cfg.name, size, entry->alloc.zone, align, aligned_addr); - - qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, - (void *)aligned_addr); - file->addr = aligned_addr; - - /* adjust address for low memory allocation */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) - *addr = (aligned_addr + size); - - return 0; -} - -/* - * This function patches ACPI tables previously loaded - * by bios_linker_allocate() - * - * @entry : BIOS linker command entry which tells how to patch - * ACPI tables - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_add_pointer(struct udevice *dev, - struct bios_linker_entry *entry) -{ - struct fw_file *dest, *src; - uint32_t offset = le32_to_cpu(entry->pointer.offset); - uint64_t pointer = 0; - - dest = qfw_find_file(dev, entry->pointer.dest_file); - if (!dest || !dest->addr) - return -ENOENT; - src = qfw_find_file(dev, entry->pointer.src_file); - if (!src || !src->addr) - return -ENOENT; - - debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", - dest->addr, src->addr, offset, entry->pointer.size, pointer); - - memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); - pointer = le64_to_cpu(pointer); - pointer += (unsigned long)src->addr; - pointer = cpu_to_le64(pointer); - memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); - - return 0; -} - -/* - * This function updates checksum fields of ACPI tables previously loaded - * by bios_linker_allocate() - * - * @entry : BIOS linker command entry which tells where to update ACPI table - * checksums - * @return: 0 on success, or negative value on failure - */ -static int bios_linker_add_checksum(struct udevice *dev, - struct bios_linker_entry *entry) -{ - struct fw_file *file; - uint8_t *data, cksum = 0; - uint8_t *cksum_start; - - file = qfw_find_file(dev, entry->cksum.file); - if (!file || !file->addr) - return -ENOENT; - - data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); - cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); - cksum = table_compute_checksum(cksum_start, - le32_to_cpu(entry->cksum.length)); - *data = cksum; - - return 0; -} - -/* This function loads and patches ACPI tables provided by QEMU */ -ulong write_acpi_tables(ulong addr) -{ - int i, ret; - struct fw_file *file; - struct bios_linker_entry *table_loader; - struct bios_linker_entry *entry; - uint32_t size; - struct udevice *dev; - - ret = qfw_get_dev(&dev); - if (ret) { - printf("error: no qfw\n"); - return addr; - } - - /* make sure fw_list is loaded */ - ret = qfw_read_firmware_list(dev); - if (ret) { - printf("error: can't read firmware file list\n"); - return addr; - } - - file = qfw_find_file(dev, "etc/table-loader"); - if (!file) { - printf("error: can't find etc/table-loader\n"); - return addr; - } - - size = be32_to_cpu(file->cfg.size); - if ((size % sizeof(*entry)) != 0) { - printf("error: table-loader maybe corrupted\n"); - return addr; - } - - table_loader = malloc(size); - if (!table_loader) { - printf("error: no memory for table-loader\n"); - return addr; - } - - /* QFW always puts tables at high addresses */ - gd->arch.table_start_high = (ulong)table_loader; - gd->arch.table_end_high = (ulong)table_loader; - - qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); - - for (i = 0; i < (size / sizeof(*entry)); i++) { - entry = table_loader + i; - switch (le32_to_cpu(entry->command)) { - case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(dev, entry, &addr); - if (ret) - goto out; - break; - case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: - ret = bios_linker_add_pointer(dev, entry); - if (ret) - goto out; - break; - case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: - ret = bios_linker_add_checksum(dev, entry); - if (ret) - goto out; - break; - default: - break; - } - } - -out: - if (ret) { - struct fw_cfg_file_iter iter; - for (file = qfw_file_iter_init(dev, &iter); - !qfw_file_iter_end(&iter); - file = qfw_file_iter_next(&iter)) { - if (file->addr) { - free((void *)file->addr); - file->addr = 0; - } - } - } - - free(table_loader); - - gd_set_acpi_start(acpi_get_rsdp_addr()); - - return addr; -} - -ulong acpi_get_rsdp_addr(void) -{ - int ret; - struct fw_file *file; - struct udevice *dev; - - ret = qfw_get_dev(&dev); - if (ret) { - printf("error: no qfw\n"); - return 0; - } - - file = qfw_find_file(dev, "etc/acpi/rsdp"); - return file->addr; -} -#endif /* QFW_ACPI */ - static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, void *address) { diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c new file mode 100644 index 0000000000..6e14b2a504 --- /dev/null +++ b/drivers/misc/qfw_acpi.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Miao Yan + * (C) Copyright 2021 Asherah Connor + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This function allocates memory for ACPI tables + * + * @entry : BIOS linker command entry which tells where to allocate memory + * (either high memory or low memory) + * @addr : The address that should be used for low memory allcation. If the + * memory allocation request is 'ZONE_HIGH' then this parameter will + * be ignored. + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_allocate(struct udevice *dev, + struct bios_linker_entry *entry, ulong *addr) +{ + uint32_t size, align; + struct fw_file *file; + unsigned long aligned_addr; + + align = le32_to_cpu(entry->alloc.align); + /* align must be power of 2 */ + if (align & (align - 1)) { + printf("error: wrong alignment %u\n", align); + return -EINVAL; + } + + file = qfw_find_file(dev, entry->alloc.file); + if (!file) { + printf("error: can't find file %s\n", entry->alloc.file); + return -ENOENT; + } + + size = be32_to_cpu(file->cfg.size); + + /* + * ZONE_HIGH means we need to allocate from high memory, since + * malloc space is already at the end of RAM, so we directly use it. + * If allocation zone is ZONE_FSEG, then we use the 'addr' passed + * in which is low memory + */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + aligned_addr = (unsigned long)memalign(align, size); + if (!aligned_addr) { + printf("error: allocating resource\n"); + return -ENOMEM; + } + if (aligned_addr < gd->arch.table_start_high) + gd->arch.table_start_high = aligned_addr; + if (aligned_addr + size > gd->arch.table_end_high) + gd->arch.table_end_high = aligned_addr + size; + + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) { + aligned_addr = ALIGN(*addr, align); + } else { + printf("error: invalid allocation zone\n"); + return -EINVAL; + } + + debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", + file->cfg.name, size, entry->alloc.zone, align, aligned_addr); + + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, + (void *)aligned_addr); + file->addr = aligned_addr; + + /* adjust address for low memory allocation */ + if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + *addr = (aligned_addr + size); + + return 0; +} + +/* + * This function patches ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells how to patch + * ACPI tables + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_pointer(struct udevice *dev, + struct bios_linker_entry *entry) +{ + struct fw_file *dest, *src; + uint32_t offset = le32_to_cpu(entry->pointer.offset); + uint64_t pointer = 0; + + dest = qfw_find_file(dev, entry->pointer.dest_file); + if (!dest || !dest->addr) + return -ENOENT; + src = qfw_find_file(dev, entry->pointer.src_file); + if (!src || !src->addr) + return -ENOENT; + + debug("bios_linker_add_pointer: dest->addr 0x%lx, src->addr 0x%lx, offset 0x%x size %u, 0x%llx\n", + dest->addr, src->addr, offset, entry->pointer.size, pointer); + + memcpy(&pointer, (char *)dest->addr + offset, entry->pointer.size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src->addr; + pointer = cpu_to_le64(pointer); + memcpy((char *)dest->addr + offset, &pointer, entry->pointer.size); + + return 0; +} + +/* + * This function updates checksum fields of ACPI tables previously loaded + * by bios_linker_allocate() + * + * @entry : BIOS linker command entry which tells where to update ACPI table + * checksums + * @return: 0 on success, or negative value on failure + */ +static int bios_linker_add_checksum(struct udevice *dev, + struct bios_linker_entry *entry) +{ + struct fw_file *file; + uint8_t *data, cksum = 0; + uint8_t *cksum_start; + + file = qfw_find_file(dev, entry->cksum.file); + if (!file || !file->addr) + return -ENOENT; + + data = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.offset)); + cksum_start = (uint8_t *)(file->addr + le32_to_cpu(entry->cksum.start)); + cksum = table_compute_checksum(cksum_start, + le32_to_cpu(entry->cksum.length)); + *data = cksum; + + return 0; +} + +/* This function loads and patches ACPI tables provided by QEMU */ +ulong write_acpi_tables(ulong addr) +{ + int i, ret; + struct fw_file *file; + struct bios_linker_entry *table_loader; + struct bios_linker_entry *entry; + uint32_t size; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return addr; + } + + /* make sure fw_list is loaded */ + ret = qfw_read_firmware_list(dev); + if (ret) { + printf("error: can't read firmware file list\n"); + return addr; + } + + file = qfw_find_file(dev, "etc/table-loader"); + if (!file) { + printf("error: can't find etc/table-loader\n"); + return addr; + } + + size = be32_to_cpu(file->cfg.size); + if ((size % sizeof(*entry)) != 0) { + printf("error: table-loader maybe corrupted\n"); + return addr; + } + + table_loader = malloc(size); + if (!table_loader) { + printf("error: no memory for table-loader\n"); + return addr; + } + + /* QFW always puts tables at high addresses */ + gd->arch.table_start_high = (ulong)table_loader; + gd->arch.table_end_high = (ulong)table_loader; + + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); + + for (i = 0; i < (size / sizeof(*entry)); i++) { + entry = table_loader + i; + switch (le32_to_cpu(entry->command)) { + case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: + ret = bios_linker_allocate(dev, entry, &addr); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + ret = bios_linker_add_pointer(dev, entry); + if (ret) + goto out; + break; + case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + ret = bios_linker_add_checksum(dev, entry); + if (ret) + goto out; + break; + default: + break; + } + } + +out: + if (ret) { + struct fw_cfg_file_iter iter; + for (file = qfw_file_iter_init(dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { + if (file->addr) { + free((void *)file->addr); + file->addr = 0; + } + } + } + + free(table_loader); + + gd_set_acpi_start(acpi_get_rsdp_addr()); + + return addr; +} + +ulong acpi_get_rsdp_addr(void) +{ + int ret; + struct fw_file *file; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return 0; + } + + file = qfw_find_file(dev, "etc/acpi/rsdp"); + return file->addr; +} From 22fcd1da5511cef43ca2252c1d7b66c99416f0fd Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:02 +0100 Subject: [PATCH 20/26] arm: add ACPI fields to global data Add fields for the location of ACPI tables to the global data. Signed-off-by: Heinrich Schuchardt Reviewed-by: Ilias Apalodimas Reviewed-by: Simon Glass --- arch/arm/include/asm/global_data.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/global_data.h b/arch/arm/include/asm/global_data.h index 2bb978d668..452bcd1b8f 100644 --- a/arch/arm/include/asm/global_data.h +++ b/arch/arm/include/asm/global_data.h @@ -18,7 +18,12 @@ struct arch_global_data { #if defined(CONFIG_FSL_ESDHC) || defined(CONFIG_FSL_ESDHC_IMX) u32 sdhc_clk; #endif - +#if CONFIG_IS_ENABLED(ACPI) + ulong table_start; /* Start address of ACPI tables */ + ulong table_end; /* End address of ACPI tables */ + ulong table_start_high; /* Start address of high ACPI tables */ + ulong table_end_high; /* End address of high ACPI tables */ +#endif #if defined(CONFIG_FSL_ESDHC) u32 sdhc_per_clk; #endif From b7d029bb8ac0768e922b0de4dcedc38bccfd8545 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:03 +0100 Subject: [PATCH 21/26] riscv: add ACPI fields to global data Add fields for the location of ACPI tables to the global data. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- arch/riscv/include/asm/global_data.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h index d00247ad95..593d9276d3 100644 --- a/arch/riscv/include/asm/global_data.h +++ b/arch/riscv/include/asm/global_data.h @@ -33,6 +33,12 @@ struct arch_global_data { ulong available_harts; #endif #endif +#if CONFIG_IS_ENABLED(ACPI) + ulong table_start; /* Start address of ACPI tables */ + ulong table_end; /* End address of ACPI tables */ + ulong table_start_high; /* Start address of high ACPI tables */ + ulong table_end_high; /* End address of high ACPI tables */ +#endif #ifdef CONFIG_SMBIOS ulong smbios_start; /* Start address of SMBIOS table */ #endif From 638cc363484b2b8f95b2160b1fae84398df10dd1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:04 +0100 Subject: [PATCH 22/26] acpi: enable writing ACPI tables on QEMU Invoke write_acpi_tables() via EVT_LAST_STAGE_INIT on QEMU except on X86. X86 calls write_acpi_tables() in write_tables(). Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/misc/qfw_acpi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c index 6e14b2a504..7ffed1e8c0 100644 --- a/drivers/misc/qfw_acpi.c +++ b/drivers/misc/qfw_acpi.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -254,3 +256,26 @@ ulong acpi_get_rsdp_addr(void) file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } + +#ifndef CONFIG_X86 +static int evt_write_acpi_tables(void) +{ + ulong addr, end; + void *ptr; + + /* Reserve 64K for ACPI tables, aligned to a 4K boundary */ + ptr = memalign(SZ_4K, SZ_64K); + if (!ptr) + return -ENOMEM; + addr = map_to_sysmem(ptr); + + /* Generate ACPI tables */ + end = write_acpi_tables(addr); + gd->arch.table_start = addr; + gd->arch.table_end = addr; + + return 0; +} + +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, evt_write_acpi_tables); +#endif From 91970e83ed1b7cddec15559ec7b7df3f6d261066 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:05 +0100 Subject: [PATCH 23/26] riscv: add support for QEMU firmware tables Enable the QEMU firmware interface if ACPI tables are to be supported on the QEMU platform. Enable the QFW MMIO interface if the QEMU firmware interface is enabled. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas --- board/emulation/qemu-riscv/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/board/emulation/qemu-riscv/Kconfig b/board/emulation/qemu-riscv/Kconfig index cdd0d0d95f..d5f302ffda 100644 --- a/board/emulation/qemu-riscv/Kconfig +++ b/board/emulation/qemu-riscv/Kconfig @@ -33,6 +33,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select GENERIC_RISCV select SUPPORT_SPL + select QFW if ACPI + select QFW_MMIO if QFW imply AHCI imply SMP imply BOARD_LATE_INIT From b17e280bb2cedb466fe62840233711e85aa8a692 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:06 +0100 Subject: [PATCH 24/26] riscv: allow usage of ACPI Select CONFIG_SUPPORT_ACPI to allow usage of ACPI tables with RISC-V. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- arch/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/Kconfig b/arch/Kconfig index 2e0528d819..c23d57e4c4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -108,6 +108,7 @@ config PPC config RISCV bool "RISC-V architecture" select CREATE_ARCH_SYMLINK + select SUPPORT_ACPI select SUPPORT_OF_CONTROL select OF_CONTROL select DM From b8bb5704ae043db6040a60c23026949a9887e351 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:07 +0100 Subject: [PATCH 25/26] configs: qemu: add config fragment for ACPI Provide a configuration fragment to enable ACPI on QEMU. Signed-off-by: Heinrich Schuchardt Acked-by: Ilias Apalodimas Reviewed-by: Simon Glass --- MAINTAINERS | 1 + board/emulation/configs/acpi.config | 3 +++ doc/board/emulation/acpi.rst | 23 +++++++++++++++++++++++ doc/board/emulation/index.rst | 1 + 4 files changed, 28 insertions(+) create mode 100644 board/emulation/configs/acpi.config create mode 100644 doc/board/emulation/acpi.rst diff --git a/MAINTAINERS b/MAINTAINERS index 417061ae2f..0dedb428de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -53,6 +53,7 @@ Maintainers List (try to look for most precise areas first) ACPI: M: Simon Glass S: Maintained +F: board/emulation/configs/acpi.config F: cmd/acpi.c F: lib/acpi/ diff --git a/board/emulation/configs/acpi.config b/board/emulation/configs/acpi.config new file mode 100644 index 0000000000..b7ed811e33 --- /dev/null +++ b/board/emulation/configs/acpi.config @@ -0,0 +1,3 @@ +CONFIG_CMD_QFW=y +CONFIG_ACPI=y +CONFIG_GENERATE_ACPI_TABLE=y diff --git a/doc/board/emulation/acpi.rst b/doc/board/emulation/acpi.rst new file mode 100644 index 0000000000..17b68e1b78 --- /dev/null +++ b/doc/board/emulation/acpi.rst @@ -0,0 +1,23 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +ACPI on QEMU +============ + +QEMU can provide ACPI tables on ARM, RISC-V (since QEMU v8.0.0), and x86. + +The following U-Boot settings are needed for ACPI support:: + + CONFIG_CMD_QFW=y + CONFIG_ACPI=y + CONFIG_GENERATE_ACPI_TABLE=y + +On x86 these settings are already included in the defconfig files. ARM and +RISC-V default to use device-trees. + +Instead of updating the configuration manually you can add the configuration +fragment `acpi.config` to the make command for initializing the configuration. +E.g. + +.. code-block:: bash + + make qemu-riscv64_smode_defconfig acpi.config diff --git a/doc/board/emulation/index.rst b/doc/board/emulation/index.rst index 932c65adeb..d3d6b8f3d8 100644 --- a/doc/board/emulation/index.rst +++ b/doc/board/emulation/index.rst @@ -6,6 +6,7 @@ Emulation .. toctree:: :maxdepth: 1 + acpi blkdev ../../usage/semihosting qemu-arm From 8c4b37ce97c27ad4e7f83b028d4cd1396a51a36f Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 19 Dec 2023 16:04:08 +0100 Subject: [PATCH 26/26] arm: enable support for QEMU firmware tables Enable the QEMU firmware interface if ACPI tables are to be supported on the QEMU platform. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas --- board/emulation/qemu-arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/board/emulation/qemu-arm/Kconfig b/board/emulation/qemu-arm/Kconfig index ac2d078f42..e21c135e86 100644 --- a/board/emulation/qemu-arm/Kconfig +++ b/board/emulation/qemu-arm/Kconfig @@ -5,6 +5,7 @@ config TEXT_BASE config BOARD_SPECIFIC_OPTIONS # dummy def_bool y + select QFW if ACPI select QFW_MMIO if CMD_QFW imply VIRTIO_MMIO imply VIRTIO_PCI