From e1722fcb7d3fcb62b41f0feda4ac2c7d27d7c1f1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:36 -0700 Subject: x86: Allow any arch to generate ACPI tables These have sadly found their way to ARM now. Allow any arch to support generating ACPI tables. Disable this for the tools build. Signed-off-by: Simon Glass --- arch/Kconfig | 2 ++ arch/x86/Kconfig | 9 --------- configs/tools-only_defconfig | 1 + lib/Kconfig | 18 ++++++++++++++++++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 343db9e..b5aa83d 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -152,6 +152,7 @@ config SANDBOX select SYS_CACHE_SHIFT_4 select IRQ select SUPPORT_EXTENSION_SCAN + select SUPPORT_ACPI imply BITREVERSE select BLOBLIST imply LTO @@ -217,6 +218,7 @@ config X86 select HAVE_PRIVATE_LIBGCC select OF_CONTROL select PCI + select SUPPORT_ACPI select SUPPORT_OF_CONTROL select SYS_CACHE_SHIFT_6 select TIMER diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b8d8ee3..7cbfd6c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -770,15 +770,6 @@ config GENERATE_MP_TABLE multiprocessing as well as symmetric I/O interrupt handling with the local APIC and I/O APIC. -config GENERATE_ACPI_TABLE - bool "Generate an ACPI (Advanced Configuration and Power Interface) table" - select QFW if QEMU - help - The Advanced Configuration and Power Interface (ACPI) specification - provides an open standard for device configuration and management - by the operating system. It defines platform-independent interfaces - for configuration and power management monitoring. - config ACPI_GNVS_EXTERNAL bool help diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig index abb0024..b78e509 100644 --- a/configs/tools-only_defconfig +++ b/configs/tools-only_defconfig @@ -18,6 +18,7 @@ CONFIG_MISC_INIT_F=y CONFIG_OF_CONTROL=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y # CONFIG_NET is not set +# CONFIG_GENERATE_ACPI_TABLE is not set # CONFIG_ACPIGEN is not set CONFIG_AXI=y CONFIG_AXI_SANDBOX=y diff --git a/lib/Kconfig b/lib/Kconfig index 38051cc..7f6a754 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -202,6 +202,24 @@ config LIB_HW_RAND endchoice +config SUPPORT_ACPI + bool + help + Enable this if your arch or board can support generating ACPI + (Advanced Configuration and Power Interface) tables. In this case + U-Boot can generate these tables and pass them to the Operating + System. + +config GENERATE_ACPI_TABLE + bool "Generate an ACPI (Advanced Configuration and Power Interface) table" + depends on SUPPORT_ACPI + select QFW if QEMU + help + The Advanced Configuration and Power Interface (ACPI) specification + provides an open standard for device configuration and management + by the operating system. It defines platform-independent interfaces + for configuration and power management monitoring. + config SPL_TINY_MEMSET bool "Use a very small memset() in SPL" help -- cgit v1.1 From 233f0e35a3536102e13bed924a1c4aa33726f244 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:37 -0700 Subject: x86: Move the acpi table to generic global_data Allow this to be used on any arch. Also convert to using macros so that we can check the CONFIG option in C code. Signed-off-by: Simon Glass --- arch/sandbox/include/asm/global_data.h | 1 - arch/x86/include/asm/global_data.h | 1 - cmd/acpi.c | 6 +++--- include/asm-generic/global_data.h | 8 ++++++++ lib/acpi/acpi_table.c | 2 +- test/dm/acpi.c | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h index f95ddb0..f4ce72d 100644 --- a/arch/sandbox/include/asm/global_data.h +++ b/arch/sandbox/include/asm/global_data.h @@ -13,7 +13,6 @@ struct arch_global_data { uint8_t *ram_buf; /* emulated RAM buffer */ void *text_base; /* pointer to base of text region */ - ulong acpi_start; /* Start address of ACPI tables */ }; #include diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 3e40445..23693f8 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -122,7 +122,6 @@ struct arch_global_data { struct fsp_header *fsp_s_hdr; /* Pointer to FSP-S header */ #endif void *itss_priv; /* Private ITSS data pointer */ - ulong acpi_start; /* Start address of ACPI tables */ ulong coreboot_table; /* Address of coreboot table */ }; diff --git a/cmd/acpi.c b/cmd/acpi.c index 9c3462b..1d30299 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -47,7 +47,7 @@ struct acpi_table_header *find_table(const char *sig) struct acpi_rsdt *rsdt; int len, i, count; - rsdp = map_sysmem(gd->arch.acpi_start, 0); + rsdp = map_sysmem(gd_acpi_start(), 0); if (!rsdp) return NULL; rsdt = map_sysmem(rsdp->rsdt_address, 0); @@ -143,12 +143,12 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, { struct acpi_rsdp *rsdp; - rsdp = map_sysmem(gd->arch.acpi_start, 0); + rsdp = map_sysmem(gd_acpi_start(), 0); if (!rsdp) { printf("No ACPI tables present\n"); return 0; } - printf("ACPI tables start at %lx\n", gd->arch.acpi_start); + printf("ACPI tables start at %lx\n", gd_acpi_start()); list_rsdp(rsdp); return 0; diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 104282b..c2f8fad 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -456,6 +456,10 @@ struct global_data { * @acpi_ctx: ACPI context pointer */ struct acpi_ctx *acpi_ctx; + /** + * @acpi_start: Start address of ACPI tables + */ + ulong acpi_start; #endif #if CONFIG_IS_ENABLED(GENERATE_SMBIOS_TABLE) /** @@ -512,8 +516,12 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #ifdef CONFIG_GENERATE_ACPI_TABLE #define gd_acpi_ctx() gd->acpi_ctx +#define gd_acpi_start() gd->acpi_start +#define gd_set_acpi_start(addr) gd->acpi_start = addr #else #define gd_acpi_ctx() NULL +#define gd_acpi_start() 0UL +#define gd_set_acpi_start(addr) #endif #if CONFIG_IS_ENABLED(MULTI_DTB_FIT) diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index d168540..3a72718 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -260,7 +260,7 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) /* Align ACPI tables to 16 byte */ acpi_align(ctx); - gd->arch.acpi_start = map_to_sysmem(ctx->current); + gd_set_acpi_start(map_to_sysmem(ctx->current)); /* We need at least an RSDP and an RSDT Table */ ctx->rsdp = ctx->current; diff --git a/test/dm/acpi.c b/test/dm/acpi.c index c51073c..804124d 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -320,7 +320,7 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) buf = memalign(64, BUF_SIZE); ut_assertnonnull(buf); acpi_setup_base_tables(&ctx, buf + 4); - ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd->arch.acpi_start); + ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd_acpi_start()); rsdp = buf + 16; ut_asserteq_ptr(rsdp, ctx.rsdp); -- cgit v1.1 From 015372359030ac96a1eb3f2b3724144a09d8271d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:38 -0700 Subject: arm: Allow supporting ACPI-table generation Some ARM boards are using ACPI now. It seems that U-Boot should support this method. Add ARM to the list of archs which can generate ACPI tables. Signed-off-by: Simon Glass --- arch/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/Kconfig b/arch/Kconfig index b5aa83d..c9608cd 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -62,6 +62,7 @@ config ARM select ARCH_SUPPORTS_LTO select CREATE_ARCH_SYMLINK select HAVE_PRIVATE_LIBGCC if !ARM64 + select SUPPORT_ACPI select SUPPORT_OF_CONTROL config M68K -- cgit v1.1 From 27ba6289fe3db6f2be1fa787575348154a32341f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:39 -0700 Subject: x86: Tidy up use of CONFIG_ACPIGEN This is enabled for quite a few boards which don't create ACPI tables. Tidy this up by dropping the option for some boards. Signed-off-by: Simon Glass --- arch/Kconfig | 2 +- configs/cherryhill_defconfig | 1 + configs/chromebook_link64_defconfig | 1 + configs/chromebook_link_defconfig | 1 + configs/chromebook_samus_tpl_defconfig | 1 + configs/coreboot64_defconfig | 1 + configs/coreboot_defconfig | 1 + configs/crownbay_defconfig | 1 + configs/efi-x86_payload32_defconfig | 1 + configs/efi-x86_payload64_defconfig | 1 + configs/slimbootloader_defconfig | 1 + 11 files changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/Kconfig b/arch/Kconfig index c9608cd..c5dd7fc 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -257,7 +257,7 @@ config X86 imply PCH imply PHYSMEM imply RTC_MC146818 - imply ACPIGEN if !QEMU + imply ACPIGEN if !QEMU && !EFI_APP imply SYSINFO if GENERATE_SMBIOS_TABLE imply SYSINFO_SMBIOS if GENERATE_SMBIOS_TABLE imply TIMESTAMP diff --git a/configs/cherryhill_defconfig b/configs/cherryhill_defconfig index 73120b5..7bfbcf6 100644 --- a/configs/cherryhill_defconfig +++ b/configs/cherryhill_defconfig @@ -40,6 +40,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set CONFIG_CPU=y CONFIG_RTL8169=y CONFIG_SPI=y diff --git a/configs/chromebook_link64_defconfig b/configs/chromebook_link64_defconfig index 7059be8..a575437 100644 --- a/configs/chromebook_link64_defconfig +++ b/configs/chromebook_link64_defconfig @@ -64,6 +64,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set CONFIG_CPU=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_INTEL=y diff --git a/configs/chromebook_link_defconfig b/configs/chromebook_link_defconfig index a6efb19..4bb52b6 100644 --- a/configs/chromebook_link_defconfig +++ b/configs/chromebook_link_defconfig @@ -55,6 +55,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set CONFIG_CPU=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_INTEL=y diff --git a/configs/chromebook_samus_tpl_defconfig b/configs/chromebook_samus_tpl_defconfig index e35ef47..6839d8c 100644 --- a/configs/chromebook_samus_tpl_defconfig +++ b/configs/chromebook_samus_tpl_defconfig @@ -74,6 +74,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y # CONFIG_NET is not set CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set CONFIG_CPU=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_DW=y diff --git a/configs/coreboot64_defconfig b/configs/coreboot64_defconfig index 8146569..8b88a08 100644 --- a/configs/coreboot64_defconfig +++ b/configs/coreboot64_defconfig @@ -46,6 +46,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set # CONFIG_PCI_PNP is not set CONFIG_SOUND=y CONFIG_SOUND_I8254=y diff --git a/configs/coreboot_defconfig b/configs/coreboot_defconfig index a12e4cd..621b736 100644 --- a/configs/coreboot_defconfig +++ b/configs/coreboot_defconfig @@ -41,6 +41,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set # CONFIG_PCI_PNP is not set CONFIG_SOUND=y CONFIG_SOUND_I8254=y diff --git a/configs/crownbay_defconfig b/configs/crownbay_defconfig index 650d768..f7dc932 100644 --- a/configs/crownbay_defconfig +++ b/configs/crownbay_defconfig @@ -48,6 +48,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set CONFIG_CPU=y CONFIG_E1000=y CONFIG_SOUND=y diff --git a/configs/efi-x86_payload32_defconfig b/configs/efi-x86_payload32_defconfig index d7be957..ed6fed2 100644 --- a/configs/efi-x86_payload32_defconfig +++ b/configs/efi-x86_payload32_defconfig @@ -39,6 +39,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set # CONFIG_PCI_PNP is not set # CONFIG_GZIP is not set CONFIG_EFI=y diff --git a/configs/efi-x86_payload64_defconfig b/configs/efi-x86_payload64_defconfig index 36dd064..1d4d1f3 100644 --- a/configs/efi-x86_payload64_defconfig +++ b/configs/efi-x86_payload64_defconfig @@ -39,6 +39,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set # CONFIG_PCI_PNP is not set # CONFIG_GZIP is not set CONFIG_EFI=y diff --git a/configs/slimbootloader_defconfig b/configs/slimbootloader_defconfig index fb2091b..1597616 100644 --- a/configs/slimbootloader_defconfig +++ b/configs/slimbootloader_defconfig @@ -23,6 +23,7 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_TFTP_TSIZE=y CONFIG_REGMAP=y CONFIG_SYSCON=y +# CONFIG_ACPIGEN is not set # CONFIG_PCI_PNP is not set CONFIG_CONSOLE_SCROLL_LINES=5 # CONFIG_GZIP is not set -- cgit v1.1 From 0679cca5072d6959b51f45c00cb5bf59d75a9329 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:40 -0700 Subject: sandbox: Allow building with GENERATE_ACPI_TABLE At present this option is missing a header file, a function prototype and the qfw driver needs a header included. Fix these problems so we can enable this option on sandbox. This will increase the build coverage. Signed-off-by: Simon Glass --- arch/sandbox/include/asm/acpi_table.h | 2 ++ arch/sandbox/include/asm/tables.h | 0 drivers/misc/qfw.c | 1 + 3 files changed, 3 insertions(+) create mode 100644 arch/sandbox/include/asm/tables.h diff --git a/arch/sandbox/include/asm/acpi_table.h b/arch/sandbox/include/asm/acpi_table.h index 921c7f4..ae17f6c 100644 --- a/arch/sandbox/include/asm/acpi_table.h +++ b/arch/sandbox/include/asm/acpi_table.h @@ -6,4 +6,6 @@ #ifndef __ASM_ACPI_TABLE_H__ #define __ASM_ACPI_TABLE_H__ +ulong write_acpi_tables(ulong start); + #endif /* __ASM_ACPI_TABLE_H__ */ diff --git a/arch/sandbox/include/asm/tables.h b/arch/sandbox/include/asm/tables.h new file mode 100644 index 0000000..e69de29 diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index ea00be8..a298e5c 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -14,6 +14,7 @@ #include #include #include +#include #ifdef CONFIG_GENERATE_ACPI_TABLE #include #endif -- cgit v1.1 From 47642428ee16a5bcc0c20f3519c0aaee897ea2fe Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:41 -0700 Subject: efi: Correct call to write_acpi_tables() This must be passed a ulong, not a u64. Fix it to avoid LTO warnings on sandbox. Signed-off-by: Simon Glass --- lib/efi_loader/efi_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c index 83f025e..f2b55b4 100644 --- a/lib/efi_loader/efi_acpi.c +++ b/lib/efi_loader/efi_acpi.c @@ -34,7 +34,7 @@ efi_status_t efi_acpi_register(void) * a 4k-aligned address, so it is safe to assume that * write_acpi_tables() will write the table at that address. */ - write_acpi_tables(acpi); + write_acpi_tables((ulong)acpi); /* And expose them to our EFI payload */ return efi_install_configuration_table(&acpi_guid, -- cgit v1.1 From a9e414dd50c57113f810812af1fd1d1f502b3f57 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:42 -0700 Subject: efi: Correct address handling with ACPI tables The current EFI implementation confuses pointers and addresses. Normally we can get away with this but in the case of sandbox it causes failures. Despite the fact that efi_allocate_pages() returns a u64, it is actually a pointer, not an address. Add special handling to avoid a crash when running 'bootefi hello'. Signed-off-by: Simon Glass --- lib/efi_loader/efi_acpi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_acpi.c b/lib/efi_loader/efi_acpi.c index f2b55b4..2ddc350 100644 --- a/lib/efi_loader/efi_acpi.c +++ b/lib/efi_loader/efi_acpi.c @@ -8,6 +8,7 @@ #include #include #include +#include #include static const efi_guid_t acpi_guid = EFI_ACPI_TABLE_GUID; @@ -22,6 +23,7 @@ efi_status_t efi_acpi_register(void) /* Map within the low 32 bits, to allow for 32bit ACPI tables */ u64 acpi = U32_MAX; efi_status_t ret; + ulong addr; /* Reserve 64kiB page for ACPI */ ret = efi_allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, @@ -34,7 +36,8 @@ efi_status_t efi_acpi_register(void) * a 4k-aligned address, so it is safe to assume that * write_acpi_tables() will write the table at that address. */ - write_acpi_tables((ulong)acpi); + addr = map_to_sysmem((void *)(ulong)acpi); + write_acpi_tables(addr); /* And expose them to our EFI payload */ return efi_install_configuration_table(&acpi_guid, -- cgit v1.1 From 437992d3a936299226b7f0b27d38319b45d4a9e5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:43 -0700 Subject: acpi: Use finer-grained control of ACPI-table generation Rather than keying everything off ACPIGEN, use the main GENERATE_ACPI_TABLE option to determine whether the core ACPI code is included. Make sure these option are not enabled in SPL/TPL since we never generate tables there. Signed-off-by: Simon Glass --- arch/Kconfig | 1 + arch/x86/lib/Makefile | 2 +- lib/Makefile | 2 +- lib/acpi/Makefile | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index c5dd7fc..bea8ead 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -203,6 +203,7 @@ config SANDBOX imply CMD_EXTENSION imply KEYBOARD imply PHYSMEM + imply GENERATE_ACPI_TABLE config SH bool "SuperH architecture" diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index e5235b7..a6f2244 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -41,7 +41,7 @@ obj-y += acpi.o obj-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.o ifndef CONFIG_QEMU obj-y += acpigen.o -obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o +obj-$(CONFIG_$(SPL_TPL_)GENERATE_ACPI_TABLE) += acpi_table.o endif obj-y += tables.o ifndef CONFIG_SPL_BUILD diff --git a/lib/Makefile b/lib/Makefile index 7950e84..bde046c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -58,7 +58,7 @@ obj-$(CONFIG_TPM_V1) += tpm-v1.o obj-$(CONFIG_TPM_V2) += tpm-v2.o endif -obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi/ +obj-$(CONFIG_$(SPL_TPL_)GENERATE_ACPI_TABLE) += acpi/ obj-$(CONFIG_$(SPL_)MD5) += md5.o obj-$(CONFIG_ECDSA) += ecdsa/ obj-$(CONFIG_$(SPL_)RSA) += rsa/ diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 5c2f793..f5d58ab 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += acpigen.o -obj-y += acpi_device.o -obj-y += acpi_dp.o -obj-y += acpi_table.o +obj-$(CONFIG_$(SPL_)ACPIGEN) += acpigen.o +obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_device.o +obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_dp.o +obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_table.o -- cgit v1.1 From a16f48823928b6bfe1e2426b17af1ec325958595 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:44 -0700 Subject: acpi: Allow include files within the board directory Some .asl files include others using the iasl 'include' directive. This needs to be able to find the files referenced. For an out-of-tree build the source directory is not the current directory. Moreover, U-Boot preprocesses the input file and puts the result in the output directory. So iasl does not know where the real source file came from. Add a -I option to produce the correct behaviour. We could add an option to not preprocess the .asl source, but for now that seems unnecessary. Signed-off-by: Simon Glass --- scripts/Makefile.lib | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 77ad282..3de78d9 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -461,7 +461,8 @@ quiet_cmd_acpi_c_asl= ASL $< cmd_acpi_c_asl= \ $(CPP) -x assembler-with-cpp -D__ASSEMBLY__ -D__ACPI__ \ -P $(UBOOTINCLUDE) -o $(ASL_TMP) $< && \ - iasl -p $@ -tc $(ASL_TMP) $(if $(KBUILD_VERBOSE:1=), >/dev/null) && \ + iasl -p $@ -I $(srctree)/board/$(BOARDDIR) -tc $(ASL_TMP) \ + $(if $(KBUILD_VERBOSE:1=), >/dev/null) && \ mv $(patsubst %.c,%.hex,$@) $@ $(obj)/dsdt.c: $(src)/dsdt.asl -- cgit v1.1 From 383bf1bc9ee21c649d04a03a19eedbffa84b7237 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:45 -0700 Subject: acpi: Move acpi_fill_header() to the generic header This function is not x86-specific so move it into the common header file. Signed-off-by: Simon Glass --- arch/x86/include/asm/acpi_table.h | 1 - include/acpi/acpi_table.h | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 55b1a3d..914ca98 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -24,7 +24,6 @@ struct acpi_table_header; /* These can be used by the target port */ -void acpi_fill_header(struct acpi_table_header *header, char *signature); void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, void *dsdt); int acpi_create_madt_lapics(u32 current); diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index dbfea3b..d3fbdc1 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -698,6 +698,14 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start); void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, struct acpi_xsdt *xsdt); +/** + * acpi_fill_header() - Set up a table header + * + * @header: Pointer to header to set up + * @signature: 4-character signature to use (e.g. "FACS") + */ +void acpi_fill_header(struct acpi_table_header *header, char *signature); + #endif /* !__ACPI__*/ #include -- cgit v1.1 From fb746fdec6d58eacd7d9323eda7ccbde9419a41e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:46 -0700 Subject: acpi: Add a table start It is useful to record the start of an ACPI table so that offsets from that point can be easily calculated. Add this to the context and set it before calling the writer method. Signed-off-by: Simon Glass --- drivers/core/acpi.c | 5 ++--- include/dm/acpi.h | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index e988a65..5425e4d 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -266,19 +266,18 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, func = acpi_get_method(parent, method); if (func) { - void *start = ctx->current; - log_debug("- method %d, %s %p\n", method, parent->name, func); ret = device_of_to_plat(parent); if (ret) return log_msg_ret("ofdata", ret); + ctx->tab_start = ctx->current; ret = func(parent, ctx); if (ret) return log_msg_ret("func", ret); /* Add the item to the internal list */ if (type != TYPE_NONE) { - ret = acpi_add_item(ctx, parent, type, start); + ret = acpi_add_item(ctx, parent, type, ctx->tab_start); if (ret) return log_msg_ret("add", ret); } diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 0fa239e..a2da57f 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -43,6 +43,9 @@ enum acpi_dump_option { * * @base: Base address of ACPI tables * @current: Current address for writing + * @tab_start: Address of start of the table being written. This is set up + * before the writer or driver method is called. It must not be changed by the + * method * @rsdp: Pointer to the Root System Description Pointer, typically used when * adding a new table. The RSDP holds pointers to the RSDT and XSDT. * @rsdt: Pointer to the Root System Description Table @@ -56,6 +59,7 @@ enum acpi_dump_option { struct acpi_ctx { void *base; void *current; + void *tab_start; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; -- cgit v1.1 From 6afa63a5a63662fa7e517b29da613f51e9e68429 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:47 -0700 Subject: acpi: Add a linker list for ACPI tables At present we call lots of functions to generate the required ACPI tables. It would be better to standardise these functions and allow them to be automatically collected and used when needed. Add a linker list to handle this. Signed-off-by: Simon Glass --- include/dm/acpi.h | 57 +++++++++++++++++++++++++++++++ lib/acpi/acpi_writer.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 lib/acpi/acpi_writer.c diff --git a/include/dm/acpi.h b/include/dm/acpi.h index a2da57f..2f52950 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -27,6 +27,8 @@ #if !defined(__ACPI__) +#include + struct nhlt; struct udevice; @@ -69,6 +71,48 @@ struct acpi_ctx { }; /** + * enum acpi_writer_flags_t - flags to use for the ACPI writers + */ +enum acpi_writer_flags_t { + ACPIWF_ALIGN64_, +}; + +struct acpi_writer; + +/** + * acpi_writer_func() - Function that can write an ACPI table + * + * @ctx: ACPI context to use for writing + * @entry: Linker-list entry for this writer + * @return 0 if OK, -ve on error + */ +typedef int (*acpi_writer_func)(struct acpi_ctx *ctx, + const struct acpi_writer *entry); + +/** + * struct acpi_writer - an ACPI table that can be written + * + * @name: Name of the writer + * @table: Table name that is generated (e.g. "DSDT") + * @h_write: Writer function + */ +struct acpi_writer { + const char *name; + const char *table; + acpi_writer_func h_write; + int flags; +}; + +/* Declare a new ACPI table writer */ +#define ACPI_WRITER(_name, _table, _write, _flags) \ + ll_entry_declare(struct acpi_writer, _name, acpi_writer) = { \ + .name = #_name, \ + .table = _table, \ + .h_write = _write, \ + .flags = _flags, \ + } + +/** * struct acpi_ops - ACPI operations supported by driver model */ struct acpi_ops { @@ -240,6 +284,19 @@ int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen); */ void acpi_reset_items(void); +/** + * acpi_write_one() - Call a single ACPI writer entry + * + * This handles aligning the context afterwards, if the entry flags indicate + * that. + * + * @ctx: ACPI context to use + * @entry: Entry to call + * @return 0 if OK, -ENOENT if this writer produced an empty entry, other -ve + * value on error + */ +int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry); + #endif /* __ACPI__ */ #endif diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c new file mode 100644 index 0000000..5ddffc8 --- /dev/null +++ b/lib/acpi/acpi_writer.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Handles writing the declared ACPI tables + * + * Copyright 2021 Google LLC + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + int ret; + + log_debug("%s: writing table '%s'\n", entry->name, + entry->table); + ctx->tab_start = ctx->current; + ret = entry->h_write(ctx, entry); + if (ret == -ENOENT) { + log_debug("%s: Omitted due to being empty\n", + entry->name); + ret = 0; + ctx->current = ctx->tab_start; /* drop the table */ + return ret; + } + if (ret) + return log_msg_ret("write", ret); + + acpi_align(ctx); + + return 0; +} + +static int acpi_write_all(struct acpi_ctx *ctx) +{ + const struct acpi_writer *writer = + ll_entry_start(struct acpi_writer, acpi_writer); + const int n_ents = ll_entry_count(struct acpi_writer, acpi_writer); + const struct acpi_writer *entry; + int ret; + + for (entry = writer; entry != writer + n_ents; entry++) { + ret = acpi_write_one(ctx, entry); + if (ret && ret != -ENOENT) + return log_msg_ret("one", ret); + } + + return 0; +} + +/* + * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c + */ +ulong write_acpi_tables(ulong start_addr) +{ + struct acpi_ctx *ctx; + ulong addr; + void *start; + int ret; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return log_msg_ret("mem", -ENOMEM); + gd->acpi_ctx = ctx; + + start = map_sysmem(start_addr, 0); + + log_debug("ACPI: Writing ACPI tables at %lx\n", start_addr); + + acpi_reset_items(); + + ret = acpi_write_all(ctx); + if (ret) { + log_err("Failed to write ACPI tables (err=%d)\n", ret); + return log_msg_ret("write", -ENOMEM); + } + + addr = map_to_sysmem(ctx->current); + log_debug("ACPI current = %lx\n", addr); + + return addr; +} -- cgit v1.1 From cc1f8c39882c5100ec07dfa46e32ff395d792b94 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:48 -0700 Subject: x86: acpi: Split out context creation from base tables At present acpi_setup_base_tables() both sets up the ACPI context and writes out the base tables. We want to use an ACPI writer to write the base tables, so split this function into two, with acpi_setup_ctx() doing the context set, and acpi_setup_base_tables() just doing the base tables. Disable the writer's write_acpi_tables() function for now, to avoid build errors. It is enabled in a following patch. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 6 +++--- include/acpi/acpi_table.h | 10 +++++----- include/dm/acpi.h | 12 ++++++++++++ lib/acpi/Makefile | 1 + lib/acpi/acpi_table.c | 11 ++--------- lib/acpi/acpi_writer.c | 22 ++++++++++++++++------ test/dm/acpi.c | 31 ++++++++++++++++++++++++++----- 7 files changed, 65 insertions(+), 28 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 3f84771..f57323b 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -522,17 +522,17 @@ ulong write_acpi_tables(ulong start_addr) int ret; int i; - ctx = calloc(1, sizeof(*ctx)); + ctx = malloc(sizeof(*ctx)); if (!ctx) return log_msg_ret("mem", -ENOMEM); - gd->acpi_ctx = ctx; start = map_sysmem(start_addr, 0); debug("ACPI: Writing ACPI tables at %lx\n", start_addr); acpi_reset_items(); - acpi_setup_base_tables(ctx, start); + acpi_setup_ctx(ctx, start); + acpi_setup_base_tables(ctx); debug("ACPI: * FACS\n"); facs = ctx->current; diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index d3fbdc1..f34bd63 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -679,14 +679,14 @@ void acpi_inc_align(struct acpi_ctx *ctx, uint amount); int acpi_add_table(struct acpi_ctx *ctx, void *table); /** - * acpi_setup_base_tables() - Set up context along with RSDP, RSDT and XSDT + * acpi_setup_base_tables() - Set up base tables - RSDP, RSDT and XSDT * - * Set up the context with the given start position. Some basic tables are - * always needed, so set them up as well. + * Writes the basic tables to the given context, which must first be set up with + * acpi_setup_ctx(). * - * @ctx: Context to set up + * @ctx: Context to write base tables to */ -void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start); +void acpi_setup_base_tables(struct acpi_ctx *ctx); /** * acpi_write_rsdp() - Write out an RSDP indicating where the ACPI tables are diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 2f52950..f6e5479 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -297,6 +297,18 @@ void acpi_reset_items(void); */ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry); +/** + * acpi_setup_ctx() - Set up a new ACPI context + * + * This zeros the context and sets up the base and current pointers, ensuring + * that they are aligned. Then it writes the acpi_start and acpi_ctx values in + * global_data + * + * @ctx: ACPI context to set up + * @start: Start address for ACPI table + */ +void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start); + #endif /* __ACPI__ */ #endif diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index f5d58ab..1318e83 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_$(SPL_)ACPIGEN) += acpigen.o obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_device.o obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_dp.o obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_table.o +obj-y += acpi_writer.o diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 3a72718..284b5a9 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -253,15 +253,8 @@ static void acpi_write_xsdt(struct acpi_xsdt *xsdt) sizeof(struct acpi_xsdt)); } -void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) +void acpi_setup_base_tables(struct acpi_ctx *ctx) { - ctx->base = start; - ctx->current = start; - - /* Align ACPI tables to 16 byte */ - acpi_align(ctx); - gd_set_acpi_start(map_to_sysmem(ctx->current)); - /* We need at least an RSDP and an RSDT Table */ ctx->rsdp = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); @@ -271,7 +264,7 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start) acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); /* clear all table memory */ - memset((void *)start, '\0', ctx->current - start); + memset(ctx->base, '\0', ctx->current - ctx->base); acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt); acpi_write_rsdt(ctx->rsdt); diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 5ddffc8..7779bf3 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -60,23 +60,20 @@ static int acpi_write_all(struct acpi_ctx *ctx) /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ -ulong write_acpi_tables(ulong start_addr) +ulong new_write_acpi_tables(ulong start_addr) { struct acpi_ctx *ctx; ulong addr; - void *start; int ret; - ctx = calloc(1, sizeof(*ctx)); + ctx = malloc(sizeof(*ctx)); if (!ctx) return log_msg_ret("mem", -ENOMEM); - gd->acpi_ctx = ctx; - - start = map_sysmem(start_addr, 0); log_debug("ACPI: Writing ACPI tables at %lx\n", start_addr); acpi_reset_items(); + acpi_setup_ctx(ctx, start_addr); ret = acpi_write_all(ctx); if (ret) { @@ -89,3 +86,16 @@ ulong write_acpi_tables(ulong start_addr) return addr; } + +void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) +{ + gd->acpi_ctx = ctx; + memset(ctx, '\0', sizeof(*ctx)); + + /* Align ACPI tables to 16-byte boundary */ + start = ALIGN(start, 16); + ctx->base = map_sysmem(start, 0); + ctx->current = ctx->base; + + gd_set_acpi_start(start); +} diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 804124d..a1d70b5 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -45,6 +45,22 @@ struct testacpi_plat { bool no_name; }; +/** + * setup_ctx_and_base_tables() - Set up context along with RSDP, RSDT and XSDT + * + * Set up the context with the given start position. Some basic tables are + * always needed, so set them up as well. + * + * @ctx: Context to set up + */ +static int setup_ctx_and_base_tables(struct acpi_ctx *ctx, ulong start) +{ + acpi_setup_ctx(ctx, start); + acpi_setup_base_tables(ctx); + + return 0; +} + static int testacpi_write_tables(const struct udevice *dev, struct acpi_ctx *ctx) { @@ -240,13 +256,15 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) { struct acpi_dmar *dmar; struct acpi_ctx ctx; + ulong addr; void *buf; int i; buf = malloc(BUF_SIZE); ut_assertnonnull(buf); + addr = map_to_sysmem(buf); - acpi_setup_base_tables(&ctx, buf); + setup_ctx_and_base_tables(&ctx, addr); dmar = ctx.current; ut_assertok(acpi_write_dev_tables(&ctx)); @@ -312,6 +330,7 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) struct acpi_xsdt *xsdt; struct acpi_ctx ctx; void *buf, *end; + ulong addr; /* * Use an unaligned address deliberately, by allocating an aligned @@ -319,7 +338,8 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) */ buf = memalign(64, BUF_SIZE); ut_assertnonnull(buf); - acpi_setup_base_tables(&ctx, buf + 4); + addr = map_to_sysmem(buf); + setup_ctx_and_base_tables(&ctx, addr + 4); ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd_acpi_start()); rsdp = buf + 16; @@ -361,13 +381,13 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) buf = memalign(16, BUF_SIZE); ut_assertnonnull(buf); - acpi_setup_base_tables(&ctx, buf); + addr = map_to_sysmem(buf); + setup_ctx_and_base_tables(&ctx, addr); ut_assertok(acpi_write_dev_tables(&ctx)); console_record_reset(); run_command("acpi list", 0); - addr = (ulong)map_to_sysmem(buf); ut_assert_nextline("ACPI tables start at %lx", addr); ut_assert_nextline("RSDP %08lx %06zx (v02 U-BOOT)", addr, sizeof(struct acpi_rsdp)); @@ -403,7 +423,8 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) buf = memalign(16, BUF_SIZE); ut_assertnonnull(buf); - acpi_setup_base_tables(&ctx, buf); + addr = map_to_sysmem(buf); + setup_ctx_and_base_tables(&ctx, addr); ut_assertok(acpi_write_dev_tables(&ctx)); -- cgit v1.1 From 31c27eb83084e77921b82e7e631ecd6ae8b904da Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:49 -0700 Subject: x86: Use the ACPI table writer Use the new ACPI writer to write the ACPI tables. At present this is all done in one monolithic function. Future work will split this out. Unfortunately the QFW write_acpi_tables() function conflicts with the 'writer' version, so disable that for sandbox. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 21 ++++----------------- drivers/misc/qfw.c | 2 +- lib/acpi/acpi_writer.c | 4 +++- test/dm/acpi.c | 6 +++--- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index f57323b..321faae 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -503,10 +503,10 @@ static int acpi_create_ssdt(struct acpi_ctx *ctx, /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ -ulong write_acpi_tables(ulong start_addr) +static int write_acpi_tables_x86(struct acpi_ctx *ctx, + const struct acpi_writer *entry) { const int thl = sizeof(struct acpi_table_header); - struct acpi_ctx *ctx; struct acpi_facs *facs; struct acpi_table_header *dsdt; struct acpi_fadt *fadt; @@ -516,22 +516,11 @@ ulong write_acpi_tables(ulong start_addr) struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; - void *start; int aml_len; ulong addr; int ret; int i; - ctx = malloc(sizeof(*ctx)); - if (!ctx) - return log_msg_ret("mem", -ENOMEM); - - start = map_sysmem(start_addr, 0); - - debug("ACPI: Writing ACPI tables at %lx\n", start_addr); - - acpi_reset_items(); - acpi_setup_ctx(ctx, start); acpi_setup_base_tables(ctx); debug("ACPI: * FACS\n"); @@ -674,14 +663,12 @@ ulong write_acpi_tables(ulong start_addr) acpi_write_dev_tables(ctx); - addr = map_to_sysmem(ctx->current); - debug("current = %lx\n", addr); - acpi_rsdp_addr = (unsigned long)ctx->rsdp; debug("ACPI: done\n"); - return addr; + return 0; } +ACPI_WRITER(x86, NULL, write_acpi_tables_x86, 0); ulong acpi_get_rsdp_addr(void) { diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index a298e5c..677841a 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -19,7 +19,7 @@ #include #endif -#ifdef CONFIG_GENERATE_ACPI_TABLE +#if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX) /* * This function allocates memory for ACPI tables * diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 7779bf3..53fc753 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -40,6 +40,7 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; } +#ifndef CONFIG_QEMU static int acpi_write_all(struct acpi_ctx *ctx) { const struct acpi_writer *writer = @@ -60,7 +61,7 @@ static int acpi_write_all(struct acpi_ctx *ctx) /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ -ulong new_write_acpi_tables(ulong start_addr) +ulong write_acpi_tables(ulong start_addr) { struct acpi_ctx *ctx; ulong addr; @@ -86,6 +87,7 @@ ulong new_write_acpi_tables(ulong start_addr) return addr; } +#endif /* QEMU */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) { diff --git a/test/dm/acpi.c b/test/dm/acpi.c index a1d70b5..49b71be 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -322,8 +322,8 @@ 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 acpi_setup_base_tables */ -static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) +/* Test setup_ctx_and_base_tables */ +static int dm_test_setup_ctx_and_base_tables(struct unit_test_state *uts) { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; @@ -369,7 +369,7 @@ static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) return 0; } -DM_TEST(dm_test_acpi_setup_base_tables, +DM_TEST(dm_test_setup_ctx_and_base_tables, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); /* Test 'acpi list' command */ -- cgit v1.1 From 94ba15a3f13ff5b510d426d13854014bb9cb4713 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:50 -0700 Subject: x86: Move base tables to a writer function Use the new ACPI writer to write the base tables at the start of the area, moving this code from the x86 implementation. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 2 -- include/acpi/acpi_table.h | 10 ------ include/dm/acpi.h | 23 ++++++++++-- lib/acpi/Makefile | 5 +++ lib/acpi/acpi_table.c | 75 -------------------------------------- lib/acpi/acpi_writer.c | 5 ++- lib/acpi/base.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++ test/dm/acpi.c | 17 +++++---- 8 files changed, 133 insertions(+), 96 deletions(-) create mode 100644 lib/acpi/base.c diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 321faae..e6aa3c5 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -521,8 +521,6 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, int ret; int i; - acpi_setup_base_tables(ctx); - debug("ACPI: * FACS\n"); facs = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_facs)); diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index f34bd63..fcc9caa 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -679,16 +679,6 @@ void acpi_inc_align(struct acpi_ctx *ctx, uint amount); int acpi_add_table(struct acpi_ctx *ctx, void *table); /** - * acpi_setup_base_tables() - Set up base tables - RSDP, RSDT and XSDT - * - * Writes the basic tables to the given context, which must first be set up with - * acpi_setup_ctx(). - * - * @ctx: Context to write base tables to - */ -void acpi_setup_base_tables(struct acpi_ctx *ctx); - -/** * acpi_write_rsdp() - Write out an RSDP indicating where the ACPI tables are * * @rsdp: Address to write RSDP diff --git a/include/dm/acpi.h b/include/dm/acpi.h index f6e5479..2021a69 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -72,9 +72,11 @@ struct acpi_ctx { /** * enum acpi_writer_flags_t - flags to use for the ACPI writers + * + * ACPIWF_ALIGN64 - align to 64 bytes after writing this one (default is 16) */ enum acpi_writer_flags_t { - ACPIWF_ALIGN64_, + ACPIWF_ALIGN64 = 1 << 0, }; struct acpi_writer; @@ -103,7 +105,7 @@ struct acpi_writer { int flags; }; -/* Declare a new ACPI table writer */ +/* Declare a new ACPI-table writer */ #define ACPI_WRITER(_name, _table, _write, _flags) \ ll_entry_declare(struct acpi_writer, _name, acpi_writer) = { \ .name = #_name, \ @@ -112,6 +114,10 @@ struct acpi_writer { .flags = _flags, \ } +/* Get a pointer to a given ACPI-table writer */ +#define ACPI_WRITER_GET(_name) \ + ll_entry_get(struct acpi_writer, _name, acpi_writer) + /** * struct acpi_ops - ACPI operations supported by driver model */ @@ -309,6 +315,19 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry); */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start); +/** + * acpi_write_one() - Call a single ACPI writer entry + * + * This handles aligning the context afterwards, if the entry flags indicate + * that. + * + * @ctx: ACPI context to use + * @entry: Entry to call + * @return 0 if OK, -ENOENT if this writer produced an empty entry, other -ve + * value on error + */ +int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry); + #endif /* __ACPI__ */ #endif diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 1318e83..4674a92 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -6,3 +6,8 @@ obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_device.o obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi_dp.o 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 +obj-y += base.o +endif diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c index 284b5a9..f8642f9 100644 --- a/lib/acpi/acpi_table.c +++ b/lib/acpi/acpi_table.c @@ -201,81 +201,6 @@ int acpi_add_table(struct acpi_ctx *ctx, void *table) return 0; } -void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, - struct acpi_xsdt *xsdt) -{ - memset(rsdp, 0, sizeof(struct acpi_rsdp)); - - memcpy(rsdp->signature, RSDP_SIG, 8); - memcpy(rsdp->oem_id, OEM_ID, 6); - - rsdp->length = sizeof(struct acpi_rsdp); - rsdp->rsdt_address = map_to_sysmem(rsdt); - - rsdp->xsdt_address = map_to_sysmem(xsdt); - rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; - - /* Calculate checksums */ - rsdp->checksum = table_compute_checksum(rsdp, 20); - rsdp->ext_checksum = table_compute_checksum(rsdp, - sizeof(struct acpi_rsdp)); -} - -static void acpi_write_rsdt(struct acpi_rsdt *rsdt) -{ - struct acpi_table_header *header = &rsdt->header; - - /* Fill out header fields */ - acpi_fill_header(header, "RSDT"); - header->length = sizeof(struct acpi_rsdt); - header->revision = 1; - - /* Entries are filled in later, we come with an empty set */ - - /* Fix checksum */ - header->checksum = table_compute_checksum(rsdt, - sizeof(struct acpi_rsdt)); -} - -static void acpi_write_xsdt(struct acpi_xsdt *xsdt) -{ - struct acpi_table_header *header = &xsdt->header; - - /* Fill out header fields */ - acpi_fill_header(header, "XSDT"); - header->length = sizeof(struct acpi_xsdt); - header->revision = 1; - - /* Entries are filled in later, we come with an empty set */ - - /* Fix checksum */ - header->checksum = table_compute_checksum(xsdt, - sizeof(struct acpi_xsdt)); -} - -void acpi_setup_base_tables(struct acpi_ctx *ctx) -{ - /* We need at least an RSDP and an RSDT Table */ - ctx->rsdp = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); - ctx->rsdt = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); - ctx->xsdt = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); - - /* clear all table memory */ - memset(ctx->base, '\0', ctx->current - ctx->base); - - acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt); - acpi_write_rsdt(ctx->rsdt); - acpi_write_xsdt(ctx->xsdt); - /* - * Per ACPI spec, the FACS table address must be aligned to a 64 byte - * boundary (Windows checks this, but Linux does not). - */ - acpi_align64(ctx); -} - void acpi_create_dbg2(struct acpi_dbg2_header *dbg2, int port_type, int port_subtype, struct acpi_gen_regaddr *address, u32 address_size, diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 53fc753..d2505e6 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -35,7 +35,10 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) if (ret) return log_msg_ret("write", ret); - acpi_align(ctx); + if (entry->flags & ACPIWF_ALIGN64) + acpi_align64(ctx); + else + acpi_align(ctx); return 0; } diff --git a/lib/acpi/base.c b/lib/acpi/base.c new file mode 100644 index 0000000..3e8d703 --- /dev/null +++ b/lib/acpi/base.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Write base ACPI tables + * + * Copyright 2021 Google LLC + */ + +#include +#include +#include +#include +#include + +void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, + struct acpi_xsdt *xsdt) +{ + memset(rsdp, 0, sizeof(struct acpi_rsdp)); + + memcpy(rsdp->signature, RSDP_SIG, 8); + memcpy(rsdp->oem_id, OEM_ID, 6); + + rsdp->length = sizeof(struct acpi_rsdp); + rsdp->rsdt_address = map_to_sysmem(rsdt); + + rsdp->xsdt_address = map_to_sysmem(xsdt); + rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; + + /* Calculate checksums */ + rsdp->checksum = table_compute_checksum(rsdp, 20); + rsdp->ext_checksum = table_compute_checksum(rsdp, + sizeof(struct acpi_rsdp)); +} + +static void acpi_write_rsdt(struct acpi_rsdt *rsdt) +{ + struct acpi_table_header *header = &rsdt->header; + + /* Fill out header fields */ + acpi_fill_header(header, "RSDT"); + header->length = sizeof(struct acpi_rsdt); + header->revision = 1; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum(rsdt, + sizeof(struct acpi_rsdt)); +} + +static void acpi_write_xsdt(struct acpi_xsdt *xsdt) +{ + struct acpi_table_header *header = &xsdt->header; + + /* Fill out header fields */ + acpi_fill_header(header, "XSDT"); + header->length = sizeof(struct acpi_xsdt); + header->revision = 1; + + /* Entries are filled in later, we come with an empty set */ + + /* Fix checksum */ + header->checksum = table_compute_checksum(xsdt, + sizeof(struct acpi_xsdt)); +} + +static int acpi_write_base(struct acpi_ctx *ctx, + const struct acpi_writer *entry) +{ + /* We need at least an RSDP and an RSDT Table */ + ctx->rsdp = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdp)); + ctx->rsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_rsdt)); + ctx->xsdt = ctx->current; + acpi_inc_align(ctx, sizeof(struct acpi_xsdt)); + + /* clear all table memory */ + memset(ctx->base, '\0', ctx->current - ctx->base); + + acpi_write_rsdp(ctx->rsdp, ctx->rsdt, ctx->xsdt); + acpi_write_rsdt(ctx->rsdt); + acpi_write_xsdt(ctx->xsdt); + + return 0; +} +/* + * Per ACPI spec, the FACS table address must be aligned to a 64-byte boundary + * (Windows checks this, but Linux does not). + * + * Use the '0' prefix to put this one first + */ +ACPI_WRITER(0base, NULL, acpi_write_base, ACPIWF_ALIGN64); diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 49b71be..da72869 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -53,10 +53,15 @@ struct testacpi_plat { * * @ctx: Context to set up */ -static int setup_ctx_and_base_tables(struct acpi_ctx *ctx, ulong start) +static int setup_ctx_and_base_tables(struct unit_test_state *uts, + struct acpi_ctx *ctx, ulong start) { + struct acpi_writer *entry = ACPI_WRITER_GET(0base); + acpi_setup_ctx(ctx, start); - acpi_setup_base_tables(ctx); + + ctx->tab_start = ctx->current; + ut_assertok(acpi_write_one(ctx, entry)); return 0; } @@ -264,7 +269,7 @@ static int dm_test_acpi_write_tables(struct unit_test_state *uts) ut_assertnonnull(buf); addr = map_to_sysmem(buf); - setup_ctx_and_base_tables(&ctx, addr); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); dmar = ctx.current; ut_assertok(acpi_write_dev_tables(&ctx)); @@ -339,7 +344,7 @@ static int dm_test_setup_ctx_and_base_tables(struct unit_test_state *uts) buf = memalign(64, BUF_SIZE); ut_assertnonnull(buf); addr = map_to_sysmem(buf); - setup_ctx_and_base_tables(&ctx, addr + 4); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr + 4)); ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd_acpi_start()); rsdp = buf + 16; @@ -382,7 +387,7 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) buf = memalign(16, BUF_SIZE); ut_assertnonnull(buf); addr = map_to_sysmem(buf); - setup_ctx_and_base_tables(&ctx, addr); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); ut_assertok(acpi_write_dev_tables(&ctx)); @@ -424,7 +429,7 @@ static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) buf = memalign(16, BUF_SIZE); ut_assertnonnull(buf); addr = map_to_sysmem(buf); - setup_ctx_and_base_tables(&ctx, addr); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); ut_assertok(acpi_write_dev_tables(&ctx)); -- cgit v1.1 From a53d38f80a1b7833a7efad6412fbd0b17cc33a99 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:51 -0700 Subject: x86: Move FACS table to a writer function Move this table over to use a writer function, moving the code from the x86 implementation. Add a pointer to the DSDT in struct acpi_ctx so we can reference it later. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 26 ++------------------------ include/dm/acpi.h | 2 ++ lib/acpi/Makefile | 1 + lib/acpi/facs.c | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 lib/acpi/facs.c diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index e6aa3c5..22f34a4 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -38,21 +38,6 @@ extern const unsigned char AmlCode[]; /* ACPI RSDP address to be used in boot parameters */ static ulong acpi_rsdp_addr; -static void acpi_create_facs(struct acpi_facs *facs) -{ - memset((void *)facs, 0, sizeof(struct acpi_facs)); - - memcpy(facs->signature, "FACS", 4); - facs->length = sizeof(struct acpi_facs); - facs->hardware_signature = 0; - facs->firmware_waking_vector = 0; - facs->global_lock = 0; - facs->flags = 0; - facs->x_firmware_waking_vector_l = 0; - facs->x_firmware_waking_vector_h = 0; - facs->version = 1; -} - static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic, u8 cpu, u8 apic) { @@ -507,7 +492,6 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { const int thl = sizeof(struct acpi_table_header); - struct acpi_facs *facs; struct acpi_table_header *dsdt; struct acpi_fadt *fadt; struct acpi_table_header *ssdt; @@ -521,12 +505,6 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, int ret; int i; - debug("ACPI: * FACS\n"); - facs = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_facs)); - - acpi_create_facs(facs); - debug("ACPI: * DSDT\n"); dsdt = ctx->current; @@ -599,7 +577,7 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, debug("ACPI: * FADT\n"); fadt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_fadt)); - acpi_create_fadt(fadt, facs, dsdt); + acpi_create_fadt(fadt, ctx->facs, dsdt); acpi_add_table(ctx, fadt); debug("ACPI: * SSDT\n"); @@ -666,7 +644,7 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, return 0; } -ACPI_WRITER(x86, NULL, write_acpi_tables_x86, 0); +ACPI_WRITER(2x86, NULL, write_acpi_tables_x86, 0); ulong acpi_get_rsdp_addr(void) { diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 2021a69..c4baeb6 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -52,6 +52,7 @@ enum acpi_dump_option { * adding a new table. The RSDP holds pointers to the RSDT and XSDT. * @rsdt: Pointer to the Root System Description Table * @xsdt: Pointer to the Extended System Description Table + * @facs: Pointer to the Firmware ACPI Control Structure * @nhlt: Intel Non-High-Definition-Audio Link Table (NHLT) pointer, used to * build up information that audio codecs need to provide in the NHLT ACPI * table @@ -65,6 +66,7 @@ struct acpi_ctx { struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; + struct acpi_facs *facs; struct nhlt *nhlt; char *len_stack[ACPIGEN_LENSTACK_SIZE]; int ltop; diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 4674a92..9f70fe6 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -10,4 +10,5 @@ obj-y += acpi_writer.o # With QEMU the ACPI tables come from there, not from U-Boot ifndef CONFIG_QEMU obj-y += base.o +obj-y += facs.o endif diff --git a/lib/acpi/facs.c b/lib/acpi/facs.c new file mode 100644 index 0000000..8a1568f --- /dev/null +++ b/lib/acpi/facs.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Write an ACPI Firmware ACPI Control Structure (FACS) table + * + * Copyright 2021 Google LLC + */ + +#include +#include +#include + +int acpi_write_facs(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_facs *facs = ctx->current; + + memset((void *)facs, '\0', sizeof(struct acpi_facs)); + + memcpy(facs->signature, "FACS", 4); + facs->length = sizeof(struct acpi_facs); + facs->hardware_signature = 0; + facs->firmware_waking_vector = 0; + facs->global_lock = 0; + facs->flags = 0; + facs->x_firmware_waking_vector_l = 0; + facs->x_firmware_waking_vector_h = 0; + facs->version = 1; + + ctx->facs = facs; + acpi_inc(ctx, sizeof(struct acpi_facs)); + + return 0; +} +ACPI_WRITER(1facs, "FACS", acpi_write_facs, 0); -- cgit v1.1 From eacb6d0ba205cae472c46a974fb16fcd783680b1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:52 -0700 Subject: x86: Move DSDT table to a writer function Move this table over to use a writer function, moving the code from the x86 implementation. Add a pointer to the DSDT in struct acpi_ctx so we can reference it later. Disable this table for sandbox since we don't actually compile real ASL code. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 49 ++++++----------------------------------- include/dm/acpi.h | 2 ++ lib/acpi/Makefile | 6 ++++++ lib/acpi/base.c | 2 ++ lib/acpi/dsdt.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ lib/acpi/facs.c | 2 ++ 6 files changed, 74 insertions(+), 42 deletions(-) create mode 100644 lib/acpi/dsdt.c diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 22f34a4..e0c76ab 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -29,12 +29,6 @@ #include #include -/* - * IASL compiles the dsdt entries and writes the hex values - * to a C array AmlCode[] (see dsdt.c). - */ -extern const unsigned char AmlCode[]; - /* ACPI RSDP address to be used in boot parameters */ static ulong acpi_rsdp_addr; @@ -491,8 +485,6 @@ static int acpi_create_ssdt(struct acpi_ctx *ctx, static int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - const int thl = sizeof(struct acpi_table_header); - struct acpi_table_header *dsdt; struct acpi_fadt *fadt; struct acpi_table_header *ssdt; struct acpi_mcfg *mcfg; @@ -500,42 +492,14 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; - int aml_len; ulong addr; int ret; int i; - debug("ACPI: * DSDT\n"); - dsdt = ctx->current; - - /* Put the table header first */ - memcpy(dsdt, &AmlCode, thl); - acpi_inc(ctx, thl); - log_debug("DSDT starts at %p, hdr ends at %p\n", dsdt, ctx->current); - - /* If the table is not empty, allow devices to inject things */ - aml_len = dsdt->length - thl; - if (aml_len) { - void *base = ctx->current; - - acpi_inject_dsdt(ctx); - log_debug("Added %x bytes from inject_dsdt, now at %p\n", - ctx->current - base, ctx->current); - log_debug("Copy AML code size %x to %p\n", aml_len, - ctx->current); - memcpy(ctx->current, AmlCode + thl, aml_len); - acpi_inc(ctx, aml_len); - } - - dsdt->length = ctx->current - (void *)dsdt; - acpi_align(ctx); - log_debug("Updated DSDT length to %x, total %x\n", dsdt->length, - ctx->current - (void *)dsdt); - if (!IS_ENABLED(CONFIG_ACPI_GNVS_EXTERNAL)) { /* Pack GNVS into the ACPI table area */ - for (i = 0; i < dsdt->length; i++) { - u32 *gnvs = (u32 *)((u32)dsdt + i); + for (i = 0; i < ctx->dsdt->length; i++) { + u32 *gnvs = (u32 *)((u32)ctx->dsdt + i); if (*gnvs == ACPI_GNVS_ADDR) { *gnvs = map_to_sysmem(ctx->current); @@ -561,8 +525,9 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, * the GNVS address. Set the checksum to zero since it is part of the * region being checksummed. */ - dsdt->checksum = 0; - dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); + ctx->dsdt->checksum = 0; + ctx->dsdt->checksum = table_compute_checksum((void *)ctx->dsdt, + ctx->dsdt->length); /* * Fill in platform-specific global NVS variables. If this fails we @@ -577,7 +542,7 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, debug("ACPI: * FADT\n"); fadt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_fadt)); - acpi_create_fadt(fadt, ctx->facs, dsdt); + acpi_create_fadt(fadt, ctx->facs, ctx->dsdt); acpi_add_table(ctx, fadt); debug("ACPI: * SSDT\n"); @@ -644,7 +609,7 @@ static int write_acpi_tables_x86(struct acpi_ctx *ctx, return 0; } -ACPI_WRITER(2x86, NULL, write_acpi_tables_x86, 0); +ACPI_WRITER(9x86, NULL, write_acpi_tables_x86, 0); ulong acpi_get_rsdp_addr(void) { diff --git a/include/dm/acpi.h b/include/dm/acpi.h index c4baeb6..815a887 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -53,6 +53,7 @@ enum acpi_dump_option { * @rsdt: Pointer to the Root System Description Table * @xsdt: Pointer to the Extended System Description Table * @facs: Pointer to the Firmware ACPI Control Structure + * @dsdt: Pointer to the Differentiated System Description Table * @nhlt: Intel Non-High-Definition-Audio Link Table (NHLT) pointer, used to * build up information that audio codecs need to provide in the NHLT ACPI * table @@ -67,6 +68,7 @@ struct acpi_ctx { struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; struct acpi_facs *facs; + struct acpi_table_header *dsdt; struct nhlt *nhlt; char *len_stack[ACPIGEN_LENSTACK_SIZE]; int ltop; diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 9f70fe6..ccdf428 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -10,5 +10,11 @@ obj-y += acpi_writer.o # With QEMU the ACPI tables come from there, not from U-Boot ifndef CONFIG_QEMU obj-y += base.o + +# Sandbox does not build a .asl file +ifndef CONFIG_SANDBOX +obj-y += dsdt.o +endif + obj-y += facs.o endif diff --git a/lib/acpi/base.c b/lib/acpi/base.c index 3e8d703..2057bd2 100644 --- a/lib/acpi/base.c +++ b/lib/acpi/base.c @@ -5,6 +5,8 @@ * Copyright 2021 Google LLC */ +#define LOG_CATEGORY LOGC_ACPI + #include #include #include diff --git a/lib/acpi/dsdt.c b/lib/acpi/dsdt.c new file mode 100644 index 0000000..db98cc2 --- /dev/null +++ b/lib/acpi/dsdt.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Write the ACPI Differentiated System Description Table (DSDT) + * + * Copyright 2021 Google LLC + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include + +/* + * IASL compiles the dsdt entries and writes the hex values + * to a C array AmlCode[] (see dsdt.c). + */ +extern const unsigned char AmlCode[]; + +int acpi_write_dsdt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + const int thl = sizeof(struct acpi_table_header); + struct acpi_table_header *dsdt = ctx->current; + int aml_len; + + /* Put the table header first */ + memcpy(dsdt, &AmlCode, thl); + acpi_inc(ctx, thl); + log_debug("DSDT starts at %p, hdr ends at %p\n", dsdt, ctx->current); + + /* If the table is not empty, allow devices to inject things */ + aml_len = dsdt->length - thl; + if (aml_len) { + void *base = ctx->current; + int ret; + + ret = acpi_inject_dsdt(ctx); + if (ret) + return log_msg_ret("inject", ret); + log_debug("Added %lx bytes from inject_dsdt, now at %p\n", + (ulong)(ctx->current - base), ctx->current); + log_debug("Copy AML code size %x to %p\n", aml_len, + ctx->current); + memcpy(ctx->current, AmlCode + thl, aml_len); + acpi_inc(ctx, aml_len); + } + + ctx->dsdt = dsdt; + dsdt->length = ctx->current - (void *)dsdt; + log_debug("Updated DSDT length to %x\n", dsdt->length); + + return 0; +} +ACPI_WRITER(3dsdt, "DSDT", acpi_write_dsdt, 0); diff --git a/lib/acpi/facs.c b/lib/acpi/facs.c index 8a1568f..e89f43c 100644 --- a/lib/acpi/facs.c +++ b/lib/acpi/facs.c @@ -5,6 +5,8 @@ * Copyright 2021 Google LLC */ +#define LOG_CATEGORY LOGC_ACPI + #include #include #include -- cgit v1.1 From 32af3261f73a93d9561ac37bf5529899421e24d6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:53 -0700 Subject: x86: Move GNVS table to a writer function Move this table over to use a writer function, for x86 only. Handle the two cases Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 73 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index e0c76ab..cff47b5 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -479,66 +479,65 @@ static int acpi_create_ssdt(struct acpi_ctx *ctx, return 0; } -/* - * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c - */ -static int write_acpi_tables_x86(struct acpi_ctx *ctx, - const struct acpi_writer *entry) +int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_fadt *fadt; - struct acpi_table_header *ssdt; - struct acpi_mcfg *mcfg; - struct acpi_tcpa *tcpa; - struct acpi_madt *madt; - struct acpi_csrt *csrt; - struct acpi_spcr *spcr; ulong addr; - int ret; - int i; if (!IS_ENABLED(CONFIG_ACPI_GNVS_EXTERNAL)) { + int i; + + /* We need the DSDT to be done */ + if (!ctx->dsdt) + return log_msg_ret("dsdt", -EAGAIN); + /* Pack GNVS into the ACPI table area */ for (i = 0; i < ctx->dsdt->length; i++) { u32 *gnvs = (u32 *)((u32)ctx->dsdt + i); if (*gnvs == ACPI_GNVS_ADDR) { *gnvs = map_to_sysmem(ctx->current); - debug("Fix up global NVS in DSDT to %#08x\n", - *gnvs); + log_debug("Fix up global NVS in DSDT to %#08x\n", + *gnvs); break; } } /* - * Fill in platform-specific global NVS variables. If this fails - * we cannot return the error but this should only happen while - * debugging. + * Recalculate the length and update the DSDT checksum since we + * patched the GNVS address. Set the checksum to zero since it + * is part of the region being checksummed. */ - addr = acpi_create_gnvs(ctx->current); - if (IS_ERR_VALUE(addr)) - printf("Error: Gailed to create GNVS\n"); - acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); + ctx->dsdt->checksum = 0; + ctx->dsdt->checksum = table_compute_checksum((void *)ctx->dsdt, + ctx->dsdt->length); } - /* - * Recalculate the length and update the DSDT checksum since we patched - * the GNVS address. Set the checksum to zero since it is part of the - * region being checksummed. - */ - ctx->dsdt->checksum = 0; - ctx->dsdt->checksum = table_compute_checksum((void *)ctx->dsdt, - ctx->dsdt->length); - - /* - * Fill in platform-specific global NVS variables. If this fails we - * cannot return the error but this should only happen while debugging. - */ + /* Fill in platform-specific global NVS variables */ addr = acpi_create_gnvs(ctx->current); if (IS_ERR_VALUE(addr)) - printf("Error: Failed to create GNVS\n"); + return log_msg_ret("gnvs", (int)addr); acpi_inc_align(ctx, sizeof(struct acpi_global_nvs)); + return 0; +} +ACPI_WRITER(4gnvs, "GNVS", acpi_write_gnvs, 0); + +/* + * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c + */ +int write_acpi_tables_x86(struct acpi_ctx *ctx, + const struct acpi_writer *entry) +{ + struct acpi_fadt *fadt; + struct acpi_table_header *ssdt; + struct acpi_mcfg *mcfg; + struct acpi_tcpa *tcpa; + struct acpi_madt *madt; + struct acpi_csrt *csrt; + struct acpi_spcr *spcr; + int ret; + debug("ACPI: * FADT\n"); fadt = ctx->current; acpi_inc_align(ctx, sizeof(struct acpi_fadt)); -- cgit v1.1 From 138d7ece70e7835ffd120da254af1abdb953e6da Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:54 -0700 Subject: x86: Move FADT table to a writer function Move this table over to use a writer function, for x86 only. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index cff47b5..1f4b7fa 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -523,13 +523,27 @@ int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) } ACPI_WRITER(4gnvs, "GNVS", acpi_write_gnvs, 0); +static int acpi_write_fadt(struct acpi_ctx *ctx, + const struct acpi_writer *entry) +{ + struct acpi_fadt *fadt; + + fadt = ctx->current; + acpi_create_fadt(fadt, ctx->facs, ctx->dsdt); + acpi_add_table(ctx, fadt); + + acpi_inc(ctx, sizeof(struct acpi_fadt)); + + return 0; +} +ACPI_WRITER(5fact, "FADT", acpi_write_fadt, 0); + /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_fadt *fadt; struct acpi_table_header *ssdt; struct acpi_mcfg *mcfg; struct acpi_tcpa *tcpa; @@ -538,12 +552,6 @@ int write_acpi_tables_x86(struct acpi_ctx *ctx, struct acpi_spcr *spcr; int ret; - debug("ACPI: * FADT\n"); - fadt = ctx->current; - acpi_inc_align(ctx, sizeof(struct acpi_fadt)); - acpi_create_fadt(fadt, ctx->facs, ctx->dsdt); - acpi_add_table(ctx, fadt); - debug("ACPI: * SSDT\n"); ssdt = (struct acpi_table_header *)ctx->current; if (!acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID)) -- cgit v1.1 From 379d3c1fd6aa490b1ad5697525cfc89b615cf25a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:55 -0700 Subject: x86: Move FACP table into separate functions Each board has its own way of creating this table. Rather than calling the acpi_create_fadt() function for each one from a common acpi_write_fadt() function, just move the writer into the board-specific code. Signed-off-by: Simon Glass Reviewed-by: Andy Shevchenko --- arch/x86/cpu/apollolake/acpi.c | 17 +++++++++++++---- arch/x86/cpu/baytrail/acpi.c | 27 +++++++++++++++++++-------- arch/x86/cpu/quark/acpi.c | 27 +++++++++++++++++++-------- arch/x86/cpu/tangier/acpi.c | 25 +++++++++++++++++-------- arch/x86/include/asm/acpi_table.h | 2 -- arch/x86/lib/acpi_table.c | 15 --------------- 6 files changed, 68 insertions(+), 45 deletions(-) diff --git a/arch/x86/cpu/apollolake/acpi.c b/arch/x86/cpu/apollolake/acpi.c index fd21c0b..a3dd737 100644 --- a/arch/x86/cpu/apollolake/acpi.c +++ b/arch/x86/cpu/apollolake/acpi.c @@ -146,16 +146,25 @@ void fill_fadt(struct acpi_fadt *fadt) fadt->x_pm_tmr_blk.addrl = IOMAP_ACPI_BASE + PM1_TMR; } -void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt) +static int apl_write_fadt(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_table_header *header = &fadt->header; + struct acpi_table_header *header; + struct acpi_fadt *fadt; - acpi_fadt_common(fadt, facs, dsdt); + fadt = ctx->current; + acpi_fadt_common(fadt, ctx->facs, ctx->dsdt); intel_acpi_fill_fadt(fadt); fill_fadt(fadt); + header = &fadt->header; header->checksum = table_compute_checksum(fadt, header->length); + + acpi_add_table(ctx, fadt); + + acpi_inc(ctx, sizeof(struct acpi_fadt)); + + return 0; } +ACPI_WRITER(5fadt, "FACS", apl_write_fadt, 0); int apl_acpi_fill_dmar(struct acpi_ctx *ctx) { diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c index 07757b8..59db2e2 100644 --- a/arch/x86/cpu/baytrail/acpi.c +++ b/arch/x86/cpu/baytrail/acpi.c @@ -15,20 +15,24 @@ #include #include -void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt) +static int baytrail_write_fadt(struct acpi_ctx *ctx, + const struct acpi_writer *entry) { - struct acpi_table_header *header = &(fadt->header); + struct acpi_table_header *header; + struct acpi_fadt *fadt; + + fadt = ctx->current; + header = &fadt->header; u16 pmbase = ACPI_BASE_ADDRESS; - memset((void *)fadt, 0, sizeof(struct acpi_fadt)); + memset(fadt, '\0', sizeof(struct acpi_fadt)); acpi_fill_header(header, "FACP"); header->length = sizeof(struct acpi_fadt); header->revision = 4; - fadt->firmware_ctrl = (u32)facs; - fadt->dsdt = (u32)dsdt; + 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; @@ -75,9 +79,9 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctl_l = (u32)facs; + fadt->x_firmware_ctl_l = (u32)ctx->facs; fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)dsdt; + fadt->x_dsdt_l = (u32)ctx->dsdt; fadt->x_dsdt_h = 0; fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; @@ -137,7 +141,14 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, fadt->x_gpe1_blk.addrh = 0x0; header->checksum = table_compute_checksum(fadt, header->length); + + acpi_add_table(ctx, fadt); + + acpi_inc(ctx, sizeof(struct acpi_fadt)); + + return 0; } +ACPI_WRITER(5fadt, "FACP", baytrail_write_fadt, 0); int acpi_create_gnvs(struct acpi_global_nvs *gnvs) { diff --git a/arch/x86/cpu/quark/acpi.c b/arch/x86/cpu/quark/acpi.c index 82b776f..9ce9ee3 100644 --- a/arch/x86/cpu/quark/acpi.c +++ b/arch/x86/cpu/quark/acpi.c @@ -10,20 +10,24 @@ #include #include -void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt) +static int quark_write_fadt(struct acpi_ctx *ctx, + const struct acpi_writer *entry) { - struct acpi_table_header *header = &(fadt->header); u16 pmbase = ACPI_PM1_BASE_ADDRESS; + struct acpi_table_header *header; + struct acpi_fadt *fadt; - memset((void *)fadt, 0, sizeof(struct acpi_fadt)); + fadt = ctx->current; + header = &fadt->header; + + memset(fadt, '\0', sizeof(struct acpi_fadt)); acpi_fill_header(header, "FACP"); header->length = sizeof(struct acpi_fadt); header->revision = 4; - fadt->firmware_ctrl = (u32)facs; - fadt->dsdt = (u32)dsdt; + 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; @@ -70,9 +74,9 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, fadt->reset_reg.addrh = 0; fadt->reset_value = SYS_RST | RST_CPU | FULL_RST; - fadt->x_firmware_ctl_l = (u32)facs; + fadt->x_firmware_ctl_l = (u32)ctx->facs; fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)dsdt; + fadt->x_dsdt_l = (u32)ctx->dsdt; fadt->x_dsdt_h = 0; fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; @@ -132,7 +136,14 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, fadt->x_gpe1_blk.addrh = 0x0; header->checksum = table_compute_checksum(fadt, header->length); + + acpi_add_table(ctx, fadt); + + acpi_inc(ctx, sizeof(struct acpi_fadt)); + + return 0; } +ACPI_WRITER(5fadt, "FACP", quark_write_fadt, 0); int acpi_create_gnvs(struct acpi_global_nvs *gnvs) { diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 82f4ce5..1187356 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -16,19 +16,23 @@ #include #include -void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt) +static int tangier_write_fadt(struct acpi_ctx *ctx, + const struct acpi_writer *entry) { - struct acpi_table_header *header = &(fadt->header); + struct acpi_table_header *header; + struct acpi_fadt *fadt; - memset((void *)fadt, 0, sizeof(struct acpi_fadt)); + fadt = ctx->current; + header = &fadt->header; + + memset(fadt, '\0', sizeof(struct acpi_fadt)); acpi_fill_header(header, "FACP"); header->length = sizeof(struct acpi_fadt); header->revision = 6; - fadt->firmware_ctrl = (u32)facs; - fadt->dsdt = (u32)dsdt; + 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 | @@ -41,13 +45,18 @@ void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, fadt->minor_revision = 2; - fadt->x_firmware_ctl_l = (u32)facs; + fadt->x_firmware_ctl_l = (u32)ctx->facs; fadt->x_firmware_ctl_h = 0; - fadt->x_dsdt_l = (u32)dsdt; + fadt->x_dsdt_l = (u32)ctx->dsdt; fadt->x_dsdt_h = 0; header->checksum = table_compute_checksum(fadt, header->length); + + acpi_inc(ctx, sizeof(struct acpi_fadt)); + + return 0; } +ACPI_WRITER(5fadt, "FACP", tangier_write_fadt, 0); u32 acpi_fill_madt(u32 current) { diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index 914ca98..fb60f85 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -24,8 +24,6 @@ struct acpi_table_header; /* These can be used by the target port */ -void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, - void *dsdt); int acpi_create_madt_lapics(u32 current); int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id, u32 addr, u32 gsi_base); diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 1f4b7fa..c1011a4 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -523,21 +523,6 @@ int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) } ACPI_WRITER(4gnvs, "GNVS", acpi_write_gnvs, 0); -static int acpi_write_fadt(struct acpi_ctx *ctx, - const struct acpi_writer *entry) -{ - struct acpi_fadt *fadt; - - fadt = ctx->current; - acpi_create_fadt(fadt, ctx->facs, ctx->dsdt); - acpi_add_table(ctx, fadt); - - acpi_inc(ctx, sizeof(struct acpi_fadt)); - - return 0; -} -ACPI_WRITER(5fact, "FADT", acpi_write_fadt, 0); - /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ -- cgit v1.1 From d953137526cce48002ef1e313592d28b66cc9c48 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:56 -0700 Subject: x86: Move SSDT table to a writer function Move this table over to use a writer function, moving the code from the x86 implementation. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 93 +++++++++++++++-------------------------------- lib/acpi/Makefile | 1 + lib/acpi/ssdt.c | 49 +++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 63 deletions(-) create mode 100644 lib/acpi/ssdt.c diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index c1011a4..18b2847 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -176,26 +176,6 @@ __weak u32 acpi_fill_mcfg(u32 current) return current; } -/* MCFG is defined in the PCI Firmware Specification 3.0 */ -static void acpi_create_mcfg(struct acpi_mcfg *mcfg) -{ - struct acpi_table_header *header = &(mcfg->header); - u32 current = (u32)mcfg + sizeof(struct acpi_mcfg); - - memset((void *)mcfg, 0, sizeof(struct acpi_mcfg)); - - /* Fill out header fields */ - acpi_fill_header(header, "MCFG"); - header->length = sizeof(struct acpi_mcfg); - header->revision = 1; - - current = acpi_fill_mcfg(current); - - /* (Re)calculate length and checksum */ - header->length = current - (u32)mcfg; - header->checksum = table_compute_checksum((void *)mcfg, header->length); -} - /** * acpi_create_tcpa() - Create a TCPA table * @@ -449,36 +429,6 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) header->checksum = table_compute_checksum((void *)spcr, header->length); } -static int acpi_create_ssdt(struct acpi_ctx *ctx, - struct acpi_table_header *ssdt, - const char *oem_table_id) -{ - memset((void *)ssdt, '\0', sizeof(struct acpi_table_header)); - - acpi_fill_header(ssdt, "SSDT"); - ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT); - ssdt->aslc_revision = 1; - ssdt->length = sizeof(struct acpi_table_header); - - acpi_inc(ctx, sizeof(struct acpi_table_header)); - - acpi_fill_ssdt(ctx); - - /* (Re)calculate length and checksum */ - ssdt->length = ctx->current - (void *)ssdt; - ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); - log_debug("SSDT at %p, length %x\n", ssdt, ssdt->length); - - /* Drop the table if it is empty */ - if (ssdt->length == sizeof(struct acpi_table_header)) { - ctx->current = ssdt; - return -ENOENT; - } - acpi_align(ctx); - - return 0; -} - int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) { ulong addr; @@ -523,31 +473,48 @@ int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) } ACPI_WRITER(4gnvs, "GNVS", acpi_write_gnvs, 0); +/* MCFG is defined in the PCI Firmware Specification 3.0 */ +int acpi_write_mcfg(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + struct acpi_mcfg *mcfg; + u32 current; + + mcfg = ctx->current; + header = &mcfg->header; + + current = (u32)mcfg + sizeof(struct acpi_mcfg); + + memset(mcfg, '\0', sizeof(struct acpi_mcfg)); + + /* Fill out header fields */ + acpi_fill_header(header, "MCFG"); + header->length = sizeof(struct acpi_mcfg); + header->revision = 1; + + /* (Re)calculate length and checksum */ + header->length = current - (u32)mcfg; + header->checksum = table_compute_checksum(mcfg, header->length); + + acpi_inc(ctx, mcfg->header.length); + acpi_add_table(ctx, mcfg); + + return 0; +} +ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0); + /* * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c */ int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_table_header *ssdt; - struct acpi_mcfg *mcfg; struct acpi_tcpa *tcpa; struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; int ret; - debug("ACPI: * SSDT\n"); - ssdt = (struct acpi_table_header *)ctx->current; - if (!acpi_create_ssdt(ctx, ssdt, OEM_TABLE_ID)) - acpi_add_table(ctx, ssdt); - - debug("ACPI: * MCFG\n"); - mcfg = ctx->current; - acpi_create_mcfg(mcfg); - acpi_inc_align(ctx, mcfg->header.length); - acpi_add_table(ctx, mcfg); - if (IS_ENABLED(CONFIG_TPM_V2)) { struct acpi_tpm2 *tpm2; diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index ccdf428..552d499 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -17,4 +17,5 @@ obj-y += dsdt.o endif obj-y += facs.o +obj-y += ssdt.o endif diff --git a/lib/acpi/ssdt.c b/lib/acpi/ssdt.c new file mode 100644 index 0000000..659c1aa --- /dev/null +++ b/lib/acpi/ssdt.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Write an ACPI Secondary System Descriptor Table (SSDT) table + * + * Copyright 2021 Google LLC + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include + +int acpi_write_ssdt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *ssdt; + int ret; + + ssdt = ctx->current; + memset((void *)ssdt, '\0', sizeof(struct acpi_table_header)); + + acpi_fill_header(ssdt, "SSDT"); + memcpy(ssdt->oem_table_id, OEM_TABLE_ID, sizeof(ssdt->oem_table_id)); + ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT); + ssdt->aslc_revision = 1; + ssdt->length = sizeof(struct acpi_table_header); + + acpi_inc(ctx, sizeof(struct acpi_table_header)); + + ret = acpi_fill_ssdt(ctx); + if (ret) { + ctx->current = ssdt; + return log_msg_ret("fill", ret); + } + + /* (Re)calculate length and checksum */ + ssdt->length = ctx->current - (void *)ssdt; + ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); + log_debug("SSDT at %p, length %x\n", ssdt, ssdt->length); + + /* Drop the table if it is empty */ + if (ssdt->length == sizeof(struct acpi_table_header)) + return log_msg_ret("fill", -ENOENT); + acpi_add_table(ctx, ssdt); + + return 0; +} +ACPI_WRITER(6ssdt, "SSDT", acpi_write_ssdt, 0); -- cgit v1.1 From 9aacd833897a1c0fb11c33a2b6136ac404f5557d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:57 -0700 Subject: x86: Move TPM2 table to a writer function Move this table over to use a writer function, for x86 only. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 18b2847..7b80d82 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -233,14 +233,21 @@ static int get_tpm2_log(void **ptrp, int *sizep) return 0; } -static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) +static int acpi_write_tpm2(struct acpi_ctx *ctx, + const struct acpi_writer *entry) { - struct acpi_table_header *header = &tpm2->header; + struct acpi_table_header *header; + struct acpi_tpm2 *tpm2; int tpm2_log_len; void *lasa; int ret; - memset((void *)tpm2, 0, sizeof(struct acpi_tpm2)); + if (!IS_ENABLED(CONFIG_TPM_V2)) + return log_msg_ret("none", -ENOENT); + + tpm2 = ctx->current; + header = &tpm2->header; + memset(tpm2, '\0', sizeof(struct acpi_tpm2)); /* * Some payloads like SeaBIOS depend on log area to use TPM2. @@ -248,7 +255,7 @@ static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) */ ret = get_tpm2_log(&lasa, &tpm2_log_len); if (ret) - return ret; + return log_msg_ret("log", ret); /* Fill out header fields. */ acpi_fill_header(header, "TPM2"); @@ -257,7 +264,7 @@ static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) header->length = sizeof(struct acpi_tpm2); header->revision = acpi_get_table_revision(ACPITAB_TPM2); - /* Hard to detect for coreboot. Just set it to 0 */ + /* Hard to detect for U-Boot. Just set it to 0 */ tpm2->platform_class = 0; /* Must be set to 0 for FIFO-interface support */ @@ -267,13 +274,17 @@ static int acpi_create_tpm2(struct acpi_tpm2 *tpm2) /* Fill the log area size and start address fields. */ tpm2->laml = tpm2_log_len; - tpm2->lasa = (uintptr_t)lasa; + tpm2->lasa = map_to_sysmem(lasa); /* Calculate checksum. */ - header->checksum = table_compute_checksum((void *)tpm2, header->length); + header->checksum = table_compute_checksum(tpm2, header->length); + + acpi_inc(ctx, tpm2->header.length); + acpi_add_table(ctx, tpm2); return 0; } +ACPI_WRITER(5tpm2, "TPM2", acpi_write_tpm2, 0); __weak u32 acpi_fill_csrt(u32 current) { @@ -515,20 +526,6 @@ int write_acpi_tables_x86(struct acpi_ctx *ctx, struct acpi_spcr *spcr; int ret; - if (IS_ENABLED(CONFIG_TPM_V2)) { - struct acpi_tpm2 *tpm2; - - debug("ACPI: * TPM2\n"); - tpm2 = (struct acpi_tpm2 *)ctx->current; - ret = acpi_create_tpm2(tpm2); - if (!ret) { - acpi_inc_align(ctx, tpm2->header.length); - acpi_add_table(ctx, tpm2); - } else { - log_warning("TPM2 table creation failed\n"); - } - } - debug("ACPI: * MADT\n"); madt = ctx->current; acpi_create_madt(madt); -- cgit v1.1 From c797f98bcd4c936adca7984f838095583057bfd7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:58 -0700 Subject: x86: Move MADT table to a writer function Move this table over to use a writer function, for x86 only. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 7b80d82..4ae9171 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -131,12 +131,16 @@ __weak u32 acpi_fill_madt(u32 current) return current; } -static void acpi_create_madt(struct acpi_madt *madt) +int acpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_table_header *header = &(madt->header); - u32 current = (u32)madt + sizeof(struct acpi_madt); + struct acpi_table_header *header; + struct acpi_madt *madt; + u32 current; - memset((void *)madt, 0, sizeof(struct acpi_madt)); + madt = ctx->current; + + memset(madt, '\0', sizeof(struct acpi_madt)); + header = &madt->header; /* Fill out header fields */ acpi_fill_header(header, "APIC"); @@ -146,13 +150,19 @@ static void acpi_create_madt(struct acpi_madt *madt) madt->lapic_addr = LAPIC_DEFAULT_BASE; madt->flags = ACPI_MADT_PCAT_COMPAT; + current = (u32)madt + sizeof(struct acpi_madt); current = acpi_fill_madt(current); /* (Re)calculate length and checksum */ header->length = current - (u32)madt; header->checksum = table_compute_checksum((void *)madt, header->length); + acpi_add_table(ctx, madt); + acpi_inc(ctx, madt->header.length); + + return 0; } +ACPI_WRITER(5x86, NULL, acpi_write_madt, 0); int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, u16 seg_nr, u8 start, u8 end) @@ -521,17 +531,10 @@ int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { struct acpi_tcpa *tcpa; - struct acpi_madt *madt; struct acpi_csrt *csrt; struct acpi_spcr *spcr; int ret; - debug("ACPI: * MADT\n"); - madt = ctx->current; - acpi_create_madt(madt); - acpi_inc_align(ctx, madt->header.length); - acpi_add_table(ctx, madt); - if (IS_ENABLED(CONFIG_TPM_V1)) { debug("ACPI: * TCPA\n"); tcpa = (struct acpi_tcpa *)ctx->current; -- cgit v1.1 From ef55f48788100212db7cd19eb9c8a347e630ffe8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:02:59 -0700 Subject: x86: Move TCPA table to a writer function Move this table over to use a writer function, for x86 only. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 4ae9171..f5045cd 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -189,22 +189,26 @@ __weak u32 acpi_fill_mcfg(u32 current) /** * acpi_create_tcpa() - Create a TCPA table * - * @tcpa: Pointer to place to put table - * * Trusted Computing Platform Alliance Capabilities Table * TCPA PC Specific Implementation SpecificationTCPA is defined in the PCI * Firmware Specification 3.0 */ -static int acpi_create_tcpa(struct acpi_tcpa *tcpa) +int acpi_write_tcpa(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_table_header *header = &tcpa->header; - u32 current = (u32)tcpa + sizeof(struct acpi_tcpa); + struct acpi_table_header *header; + struct acpi_tcpa *tcpa; + u32 current; int size = 0x10000; /* Use this as the default size */ void *log; int ret; + if (!IS_ENABLED(CONFIG_TPM_V1)) + return -ENOENT; if (!CONFIG_IS_ENABLED(BLOBLIST)) return -ENXIO; + + tcpa = ctx->current; + header = &tcpa->header; memset(tcpa, '\0', sizeof(struct acpi_tcpa)); /* Fill out header fields */ @@ -218,14 +222,19 @@ static int acpi_create_tcpa(struct acpi_tcpa *tcpa) tcpa->platform_class = 0; tcpa->laml = size; - tcpa->lasa = (ulong)log; + tcpa->lasa = map_to_sysmem(log); /* (Re)calculate length and checksum */ + current = (u32)tcpa + sizeof(struct acpi_tcpa); header->length = current - (u32)tcpa; - header->checksum = table_compute_checksum((void *)tcpa, header->length); + header->checksum = table_compute_checksum(tcpa, header->length); + + acpi_inc(ctx, tcpa->header.length); + acpi_add_table(ctx, tcpa); return 0; } +ACPI_WRITER(5tcpa, "TCPA", acpi_write_tcpa, 0); static int get_tpm2_log(void **ptrp, int *sizep) { @@ -530,23 +539,8 @@ ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0); int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_tcpa *tcpa; struct acpi_csrt *csrt; struct acpi_spcr *spcr; - int ret; - - if (IS_ENABLED(CONFIG_TPM_V1)) { - debug("ACPI: * TCPA\n"); - tcpa = (struct acpi_tcpa *)ctx->current; - ret = acpi_create_tcpa(tcpa); - if (ret) { - log_warning("Failed to create TCPA table (err=%d)\n", - ret); - } else { - acpi_inc_align(ctx, tcpa->header.length); - acpi_add_table(ctx, tcpa); - } - } debug("ACPI: * CSRT\n"); csrt = ctx->current; -- cgit v1.1 From 85b8161b141070e5c7515edb38424ea48f023d04 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:00 -0700 Subject: x86: Move CSRT table to a writer function Move this table over to use a writer function, moving the code from the x86 implementation. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 62 ++++++++++------------------------------------- lib/acpi/Makefile | 1 + lib/acpi/csrt.c | 50 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 lib/acpi/csrt.c diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index f5045cd..b7557f7 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -305,41 +305,12 @@ static int acpi_write_tpm2(struct acpi_ctx *ctx, } ACPI_WRITER(5tpm2, "TPM2", acpi_write_tpm2, 0); -__weak u32 acpi_fill_csrt(u32 current) +int acpi_write_spcr(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - return 0; -} - -static int acpi_create_csrt(struct acpi_csrt *csrt) -{ - struct acpi_table_header *header = &(csrt->header); - u32 current = (u32)csrt + sizeof(struct acpi_csrt); - uint ptr; - - memset((void *)csrt, 0, sizeof(struct acpi_csrt)); - - /* Fill out header fields */ - acpi_fill_header(header, "CSRT"); - header->length = sizeof(struct acpi_csrt); - header->revision = 0; - - ptr = acpi_fill_csrt(current); - if (!ptr) - return -ENOENT; - current = ptr; - - /* (Re)calculate length and checksum */ - header->length = current - (u32)csrt; - header->checksum = table_compute_checksum((void *)csrt, header->length); - - return 0; -} - -static void acpi_create_spcr(struct acpi_spcr *spcr) -{ - struct acpi_table_header *header = &(spcr->header); struct serial_device_info serial_info = {0}; ulong serial_address, serial_offset; + struct acpi_table_header *header; + struct acpi_spcr *spcr; struct udevice *dev; uint serial_config; uint serial_width; @@ -347,7 +318,10 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) int space_id; int ret = -ENODEV; - memset((void *)spcr, 0, sizeof(struct acpi_spcr)); + spcr = ctx->current; + header = &spcr->header; + + memset(spcr, '\0', sizeof(struct acpi_spcr)); /* Fill out header fields */ acpi_fill_header(header, "SPCR"); @@ -457,7 +431,13 @@ static void acpi_create_spcr(struct acpi_spcr *spcr) /* Fix checksum */ header->checksum = table_compute_checksum((void *)spcr, header->length); + + acpi_add_table(ctx, spcr); + acpi_inc(ctx, spcr->header.length); + + return 0; } +ACPI_WRITER(5spcr, "SPCR", acpi_write_spcr, 0); int acpi_write_gnvs(struct acpi_ctx *ctx, const struct acpi_writer *entry) { @@ -539,22 +519,6 @@ ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0); int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - struct acpi_csrt *csrt; - struct acpi_spcr *spcr; - - debug("ACPI: * CSRT\n"); - csrt = ctx->current; - if (!acpi_create_csrt(csrt)) { - acpi_inc_align(ctx, csrt->header.length); - acpi_add_table(ctx, csrt); - } - - debug("ACPI: * SPCR\n"); - spcr = ctx->current; - acpi_create_spcr(spcr); - acpi_inc_align(ctx, spcr->header.length); - acpi_add_table(ctx, spcr); - acpi_write_dev_tables(ctx); acpi_rsdp_addr = (unsigned long)ctx->rsdp; diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile index 552d499..f9b5049 100644 --- a/lib/acpi/Makefile +++ b/lib/acpi/Makefile @@ -10,6 +10,7 @@ obj-y += acpi_writer.o # With QEMU the ACPI tables come from there, not from U-Boot ifndef CONFIG_QEMU obj-y += base.o +obj-y += csrt.o # Sandbox does not build a .asl file ifndef CONFIG_SANDBOX diff --git a/lib/acpi/csrt.c b/lib/acpi/csrt.c new file mode 100644 index 0000000..7606931 --- /dev/null +++ b/lib/acpi/csrt.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Write an ACPI Core System Resource Table (CSRT) + * + * Copyright 2021 Google LLC + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include +#include +#include +#include +#include + +__weak u32 acpi_fill_csrt(u32 current) +{ + return 0; +} + +int acpi_write_csrt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + struct acpi_table_header *header; + struct acpi_csrt *csrt; + uint ptr; + + csrt = ctx->current; + header = &csrt->header; + + memset(csrt, '\0', sizeof(struct acpi_csrt)); + + /* Fill out header fields */ + acpi_fill_header(header, "CSRT"); + header->length = sizeof(struct acpi_csrt); + header->revision = 0; + + ptr = acpi_fill_csrt(map_to_sysmem(csrt)); + if (!ptr) + return log_msg_ret("fill", -ENOENT); + + /* (Re)calculate length and checksum */ + header->length = (ulong)ctx->current - (ulong)csrt; + header->checksum = table_compute_checksum(csrt, header->length); + + acpi_add_table(ctx, csrt); + acpi_inc(ctx, csrt->header.length); + + return 0; +} +ACPI_WRITER(5csrt, "CSRT", acpi_write_csrt, 0); -- cgit v1.1 From 78031ad43168d13de5c9ecf72877e8d5d35f749a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:01 -0700 Subject: x86: acpi: Update acpi_fill_csrt() to use acpi_ctx Update this function to the newer style, so we can avoid passing and returning an address through this function. Also move this function out of the x86 code so it can be used by other archs. Signed-off-by: Simon Glass Reviewed-by: Andy Shevchenko --- arch/x86/cpu/tangier/acpi.c | 9 ++++++--- arch/x86/include/asm/acpi_table.h | 1 - include/acpi/acpi_table.h | 12 ++++++++++++ lib/acpi/csrt.c | 13 ++++++------- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/arch/x86/cpu/tangier/acpi.c b/arch/x86/cpu/tangier/acpi.c index 1187356..12f9289 100644 --- a/arch/x86/cpu/tangier/acpi.c +++ b/arch/x86/cpu/tangier/acpi.c @@ -109,11 +109,14 @@ static u32 acpi_fill_csrt_dma(struct acpi_csrt_group *grp) return grp->length; } -u32 acpi_fill_csrt(u32 current) +int acpi_fill_csrt(struct acpi_ctx *ctx) { - current += acpi_fill_csrt_dma((struct acpi_csrt_group *)current); + int size; - return current; + size = acpi_fill_csrt_dma(ctx->current); + acpi_inc(ctx, size); + + return 0; } int acpi_create_gnvs(struct acpi_global_nvs *gnvs) diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h index fb60f85..0d07f7c 100644 --- a/arch/x86/include/asm/acpi_table.h +++ b/arch/x86/include/asm/acpi_table.h @@ -35,7 +35,6 @@ u32 acpi_fill_madt(u32 current); int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, u16 seg_nr, u8 start, u8 end); u32 acpi_fill_mcfg(u32 current); -u32 acpi_fill_csrt(u32 current); /** * acpi_write_hpet() - Write out a HPET table diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index fcc9caa..e405012 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -696,6 +696,18 @@ void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, */ void acpi_fill_header(struct acpi_table_header *header, char *signature); +/** + * acpi_fill_csrt() - Fill out the body of the CSRT + * + * This should write the contents of the Core System Resource Table (CSRT) + * to the context. The header (struct acpi_table_header) has already been + * written. + * + * @ctx: ACPI context to write to + * @return 0 if OK, -ve on error + */ +int acpi_fill_csrt(struct acpi_ctx *ctx); + #endif /* !__ACPI__*/ #include diff --git a/lib/acpi/csrt.c b/lib/acpi/csrt.c index 7606931..2ba86f2 100644 --- a/lib/acpi/csrt.c +++ b/lib/acpi/csrt.c @@ -13,7 +13,7 @@ #include #include -__weak u32 acpi_fill_csrt(u32 current) +__weak int acpi_fill_csrt(struct acpi_ctx *ctx) { return 0; } @@ -22,7 +22,7 @@ int acpi_write_csrt(struct acpi_ctx *ctx, const struct acpi_writer *entry) { struct acpi_table_header *header; struct acpi_csrt *csrt; - uint ptr; + int ret; csrt = ctx->current; header = &csrt->header; @@ -31,19 +31,18 @@ int acpi_write_csrt(struct acpi_ctx *ctx, const struct acpi_writer *entry) /* Fill out header fields */ acpi_fill_header(header, "CSRT"); - header->length = sizeof(struct acpi_csrt); header->revision = 0; + acpi_inc(ctx, sizeof(*header)); - ptr = acpi_fill_csrt(map_to_sysmem(csrt)); - if (!ptr) - return log_msg_ret("fill", -ENOENT); + ret = acpi_fill_csrt(ctx); + if (ret) + return log_msg_ret("fill", ret); /* (Re)calculate length and checksum */ header->length = (ulong)ctx->current - (ulong)csrt; header->checksum = table_compute_checksum(csrt, header->length); acpi_add_table(ctx, csrt); - acpi_inc(ctx, csrt->header.length); return 0; } -- cgit v1.1 From a7e53b93b1ca4de745ab5fbdd3db2bd7888adb0f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:02 -0700 Subject: x86: Move device-specific ACPI tables to a writer function Move this over to use a writer function, moving the code from the x86 implementation. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 2 -- lib/acpi/acpi_writer.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index b7557f7..8275c36 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -519,8 +519,6 @@ ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0); int write_acpi_tables_x86(struct acpi_ctx *ctx, const struct acpi_writer *entry) { - acpi_write_dev_tables(ctx); - acpi_rsdp_addr = (unsigned long)ctx->rsdp; debug("ACPI: done\n"); diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index d2505e6..59f951c 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -90,6 +90,18 @@ ulong write_acpi_tables(ulong start_addr) return addr; } + +int write_dev_tables(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ + int ret; + + ret = acpi_write_dev_tables(ctx); + if (ret) + return log_msg_ret("write", ret); + + return 0; +} +ACPI_WRITER(8dev, NULL, write_dev_tables, 0); #endif /* QEMU */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) -- cgit v1.1 From 9d2adca8c3e8d195fa4be5acc8c6dfe14933e826 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:03 -0700 Subject: x86: Move acpi_get_rsdp_addr() ACPI tables to the writer Move this over to use a writer file, moving the code from the x86 implementation. There is no need to store a separate variable since we can simply access the ACPI context. With this, the original monolithic x86 function for writing ACPI tables is gone. Note that QEMU has its own implementation. Signed-off-by: Simon Glass --- arch/x86/lib/acpi_table.c | 21 --------------------- lib/acpi/acpi_writer.c | 8 ++++++++ 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c index 8275c36..c053434 100644 --- a/arch/x86/lib/acpi_table.c +++ b/arch/x86/lib/acpi_table.c @@ -29,9 +29,6 @@ #include #include -/* ACPI RSDP address to be used in boot parameters */ -static ulong acpi_rsdp_addr; - static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic, u8 cpu, u8 apic) { @@ -513,24 +510,6 @@ int acpi_write_mcfg(struct acpi_ctx *ctx, const struct acpi_writer *entry) } ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0); -/* - * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c - */ -int write_acpi_tables_x86(struct acpi_ctx *ctx, - const struct acpi_writer *entry) -{ - acpi_rsdp_addr = (unsigned long)ctx->rsdp; - debug("ACPI: done\n"); - - return 0; -} -ACPI_WRITER(9x86, NULL, write_acpi_tables_x86, 0); - -ulong acpi_get_rsdp_addr(void) -{ - return acpi_rsdp_addr; -} - /** * acpi_write_hpet() - Write out a HPET table * diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 59f951c..9b0aa23 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -102,6 +102,14 @@ int write_dev_tables(struct acpi_ctx *ctx, const struct acpi_writer *entry) return 0; } ACPI_WRITER(8dev, NULL, write_dev_tables, 0); + +ulong acpi_get_rsdp_addr(void) +{ + if (!gd->acpi_ctx) + return 0; + + return map_to_sysmem(gd->acpi_ctx->rsdp); +} #endif /* QEMU */ void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start) -- cgit v1.1 From 2d7c7382969ff2a412acb409e76b2959dd715cc3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:04 -0700 Subject: acpi: Collect tables in the acpi_item list At present this list is used to collect items within the DSDT and SSDT tables. It is useful for it to collect the whole tables as well, so there is a list of what was created and which write created each one. Refactor the code accordingly. Signed-off-by: Simon Glass --- drivers/core/acpi.c | 43 ++++++++++++++++++++++++++++++++----------- include/dm/acpi.h | 14 ++++++++++++++ lib/acpi/acpi_writer.c | 5 +++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 5425e4d..1e2f5d5 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -19,11 +19,19 @@ #define MAX_ACPI_ITEMS 100 -/* Type of table that we collected */ +/** + * Type of table that we collected + * + * @TYPE_NONE: Not yet known + * @TYPE_SSDT: Items in the Secondary System Description Table + * @TYPE_DSDT: Items in the Differentiated System Description Table + * @TYPE_OTHER: Other (whole) + */ enum gen_type_t { TYPE_NONE, TYPE_SSDT, TYPE_DSDT, + TYPE_OTHER, }; /* Type of method to call */ @@ -42,11 +50,13 @@ typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx); * * @dev: Device that generated this data * @type: Table type it refers to + * @writer: Writer that wrote this table * @buf: Buffer containing the data * @size: Size of the data in bytes */ struct acpi_item { struct udevice *dev; + const struct acpi_writer *writer; enum gen_type_t type; char *buf; int size; @@ -103,16 +113,18 @@ int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen) } /** - * acpi_add_item() - Add a new item to the list of data collected + * add_item() - Add a new item to the list of data collected * * @ctx: ACPI context - * @dev: Device that generated the data + * @dev: Device that generated the data, if type != TYPE_OTHER + * @writer: Writer entry that generated the data, if type == TYPE_OTHER * @type: Table type it refers to * @start: The start of the data (the end is obtained from ctx->current) * Return: 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory */ -static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, - enum gen_type_t type, void *start) +static int add_item(struct acpi_ctx *ctx, struct udevice *dev, + const struct acpi_writer *writer, enum gen_type_t type, + void *start) { struct acpi_item *item; void *end = ctx->current; @@ -124,14 +136,17 @@ static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, item = &acpi_item[item_count]; item->dev = dev; + item->writer = writer; item->type = type; item->size = end - start; if (!item->size) return 0; - item->buf = malloc(item->size); - if (!item->buf) - return log_msg_ret("mem", -ENOMEM); - memcpy(item->buf, start, item->size); + if (type != TYPE_OTHER) { + item->buf = malloc(item->size); + if (!item->buf) + return log_msg_ret("mem", -ENOMEM); + memcpy(item->buf, start, item->size); + } item_count++; log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start, item->size); @@ -139,6 +154,12 @@ static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev, return 0; } +int acpi_add_other_item(struct acpi_ctx *ctx, const struct acpi_writer *writer, + void *start) +{ + return add_item(ctx, NULL, writer, TYPE_OTHER, start); +} + void acpi_dump_items(enum acpi_dump_option option) { int i; @@ -162,7 +183,7 @@ static struct acpi_item *find_acpi_item(const char *devname) for (i = 0; i < item_count; i++) { struct acpi_item *item = &acpi_item[i]; - if (!strcmp(devname, item->dev->name)) + if (item->dev && !strcmp(devname, item->dev->name)) return item; } @@ -277,7 +298,7 @@ int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent, /* Add the item to the internal list */ if (type != TYPE_NONE) { - ret = acpi_add_item(ctx, parent, type, ctx->tab_start); + ret = add_item(ctx, parent, NULL, type, ctx->tab_start); if (ret) return log_msg_ret("add", ret); } diff --git a/include/dm/acpi.h b/include/dm/acpi.h index 815a887..3adfe21 100644 --- a/include/dm/acpi.h +++ b/include/dm/acpi.h @@ -263,6 +263,20 @@ int acpi_inject_dsdt(struct acpi_ctx *ctx); int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt); /** + * acpi_add_other_item() - Add a new table to the list of ACPI tables + * + * This adds an entry of type ACPIT_TYPE_OTHER + * + * @ctx: ACPI context + * @writer: Writer entry that generated the data + * @type: Table type it refers to + * @start: The start of the data (the end is obtained from ctx->current) + * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory + */ +int acpi_add_other_item(struct acpi_ctx *ctx, const struct acpi_writer *writer, + void *start); + +/** * acpi_dump_items() - Dump out the collected ACPI items * * This lists the ACPI DSDT and SSDT items generated by the various U-Boot diff --git a/lib/acpi/acpi_writer.c b/lib/acpi/acpi_writer.c index 9b0aa23..946f90e 100644 --- a/lib/acpi/acpi_writer.c +++ b/lib/acpi/acpi_writer.c @@ -40,6 +40,11 @@ int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry) else acpi_align(ctx); + /* Add the item to the internal list */ + ret = acpi_add_other_item(ctx, entry, ctx->tab_start); + if (ret) + return log_msg_ret("add", ret); + return 0; } -- cgit v1.1 From a9246416326398b5f0ce72af38bba1c43f1cb1e9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:05 -0700 Subject: acpi: Tidy up the item list At present this is really just a debugging aid, but it is a bit untidy. Add proper columns and display the type name instead of a number. Sample output for coral: => acpi items Seq Type Addr Size Device/Writer --- ----- -------- ---- ------------- 0 other 79925000 240 0base 1 other 79925240 40 1facs 2 dsdt 799252a4 58 board 3 dsdt 799252fc 10 lpc 4 other 79925280 32f0 3dsdt 5 other 79928570 1000 4gnvs 6 other 79929570 100 5fact 7 other 79929670 30 5mcfg 8 other 799296a0 50 5spcr 9 other 799296f0 50 5tpm2 a other 79929740 70 5x86 b ssdt 799297d4 fe maxim-codec c ssdt 799298d2 28 i2c2@16,0 d ssdt 799298fa 270 da-codec e ssdt 79929b6a 28 i2c2@16,1 f ssdt 79929b92 28 i2c2@16,2 10 ssdt 79929bba 83 tpm@50 11 ssdt 79929c3d 28 i2c2@16,3 12 ssdt 79929c65 282 elan-touchscreen@10 13 ssdt 79929ee7 285 raydium-touchscreen@39 14 ssdt 7992a16c 28 i2c2@17,0 15 ssdt 7992a194 d8 elan-touchpad@15 16 ssdt 7992a26c 163 synaptics-touchpad@2c 17 ssdt 7992a3cf 28 i2c2@17,1 18 ssdt 7992a3f7 111 wacom-digitizer@9 19 ssdt 7992a508 8f sdmmc@1b,0 1a ssdt 7992a597 4b wifi 1b ssdt 7992a5e2 1a0 cpu@0 1c ssdt 7992a782 1a0 cpu@1 1d ssdt 7992a922 1a0 cpu@2 1e ssdt 7992aac2 211 cpu@3 1f other 799297b0 1530 6ssdt 20 other 7992ace0 2f10 8dev Signed-off-by: Simon Glass --- drivers/core/acpi.c | 24 ++++++++++++++++++++---- test/dm/acpi.c | 20 ++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 1e2f5d5..0df58db 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,13 @@ enum gen_type_t { TYPE_OTHER, }; +const char *gen_type_str[] = { + "-", + "ssdt", + "dsdt", + "other", +}; + /* Type of method to call */ enum method_t { METHOD_WRITE_TABLES, @@ -51,13 +59,15 @@ typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx); * @dev: Device that generated this data * @type: Table type it refers to * @writer: Writer that wrote this table - * @buf: Buffer containing the data + * @base: Pointer to base of table in its original location + * @buf: Buffer allocated to contain the data (NULL if not allocated) * @size: Size of the data in bytes */ struct acpi_item { struct udevice *dev; const struct acpi_writer *writer; enum gen_type_t type; + const char *base; char *buf; int size; }; @@ -139,6 +149,7 @@ static int add_item(struct acpi_ctx *ctx, struct udevice *dev, item->writer = writer; item->type = type; item->size = end - start; + item->base = start; if (!item->size) return 0; if (type != TYPE_OTHER) { @@ -164,13 +175,18 @@ void acpi_dump_items(enum acpi_dump_option option) { int i; + printf("Seq Type Base Size Device/Writer\n"); + printf("--- ----- -------- ---- -------------\n"); for (i = 0; i < item_count; i++) { struct acpi_item *item = &acpi_item[i]; - printf("dev '%s', type %d, size %x\n", item->dev->name, - item->type, item->size); + printf("%3x %-5s %8lx %5x %s\n", i, + gen_type_str[item->type], + (ulong)map_to_sysmem(item->base), item->size, + item->dev ? item->dev->name : item->writer->name); if (option == ACPI_DUMP_CONTENTS) { - print_buffer(0, item->buf, 1, item->size, 0); + print_buffer(0, item->buf ? item->buf : item->base, 1, + item->size, 0); printf("\n"); } } diff --git a/test/dm/acpi.c b/test/dm/acpi.c index da72869..d648f30 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -566,18 +566,22 @@ DM_TEST(dm_test_acpi_inject_dsdt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); static int dm_test_acpi_cmd_items(struct unit_test_state *uts) { struct acpi_ctx ctx; + ulong addr; void *buf; buf = malloc(BUF_SIZE); ut_assertnonnull(buf); + addr = map_to_sysmem(buf); acpi_reset_items(); ctx.current = buf; ut_assertok(acpi_fill_ssdt(&ctx)); console_record_reset(); run_command("acpi items", 0); - ut_assert_nextline("dev 'acpi-test', type 1, size 2"); - ut_assert_nextline("dev 'acpi-test2', type 1, size 2"); + ut_assert_nextline("Seq Type Base Size Device/Writer"); + ut_assert_nextline("--- ----- -------- ---- -------------"); + ut_assert_nextline(" 0 ssdt %8lx 2 acpi-test", addr); + ut_assert_nextline(" 1 ssdt %8lx 2 acpi-test2", addr + 2); ut_assert_console_end(); acpi_reset_items(); @@ -585,16 +589,20 @@ static int dm_test_acpi_cmd_items(struct unit_test_state *uts) ut_assertok(acpi_inject_dsdt(&ctx)); console_record_reset(); run_command("acpi items", 0); - ut_assert_nextline("dev 'acpi-test', type 2, size 2"); - ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextline(" 0 dsdt %8lx 2 acpi-test", addr); + ut_assert_nextline(" 1 dsdt %8lx 2 acpi-test2", addr + 2); ut_assert_console_end(); console_record_reset(); run_command("acpi items -d", 0); - ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextline(" 0 dsdt %8lx 2 acpi-test", addr); ut_assert_nextlines_are_dump(2); ut_assert_nextline("%s", ""); - ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_nextline(" 1 dsdt %8lx 2 acpi-test2", addr + 2); ut_assert_nextlines_are_dump(2); ut_assert_nextline("%s", ""); ut_assert_console_end(); -- cgit v1.1 From 06f6f3d478e4ab927f404a18b949ee148370f894 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:06 -0700 Subject: acpi: Tidy up the table list At present this is really just a debugging aid, but it is a bit untidy. Add proper columns so it is easier to read. Sample output for coral: => acpi list Name Base Size Detail ---- -------- ----- ------ RSDP 79925000 24 v02 U-BOOT RSDT 79925030 48 v01 U-BOOT U-BOOTBL 20220101 INTL 0 XSDT 799250e0 6c v01 U-BOOT U-BOOTBL 20220101 INTL 0 FACP 79929570 f4 v04 U-BOOT U-BOOTBL 20220101 INTL 1 DSDT 79925280 32ea v02 U-BOOT U-BOOTBL 20110725 INTL 20180105 FACS 79925240 40 MCFG 79929670 2c v01 U-BOOT U-BOOTBL 20220101 INTL 0 SPCR 799296a0 50 v02 U-BOOT U-BOOTBL 20220101 INTL 0 TPM2 799296f0 4c v04 U-BOOT U-BOOTBL 20220101 INTL 0 APIC 79929740 6c v02 U-BOOT U-BOOTBL 20220101 INTL 0 SSDT 799297b0 1523 v02 U-BOOT U-BOOTBL 20220101 INTL 1 NHLT 7992ace0 e60 v05 coral coral 3 INTL 0 DBG2 7992db40 61 v00 U-BOOT U-BOOTBL 20220101 INTL 0 HPET 7992dbb0 38 v01 U-BOOT U-BOOTBL 20220101 INTL 0 Signed-off-by: Simon Glass --- cmd/acpi.c | 9 +++++---- test/dm/acpi.c | 15 ++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cmd/acpi.c b/cmd/acpi.c index 1d30299..c543f1e 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -24,10 +24,10 @@ static void dump_hdr(struct acpi_table_header *hdr) { bool has_hdr = memcmp(hdr->signature, "FACS", ACPI_NAME_LEN); - printf("%.*s %08lx %06x", ACPI_NAME_LEN, hdr->signature, + printf("%.*s %08lx %5x", ACPI_NAME_LEN, hdr->signature, (ulong)map_to_sysmem(hdr), hdr->length); if (has_hdr) { - printf(" (v%02d %.6s %.8s %x %.4s %x)\n", hdr->revision, + printf(" v%02d %.6s %.8s %x %.4s %x\n", hdr->revision, hdr->oem_id, hdr->oem_table_id, hdr->oem_revision, hdr->aslc_id, hdr->aslc_revision); } else { @@ -129,7 +129,7 @@ static int list_rsdp(struct acpi_rsdp *rsdp) struct acpi_rsdt *rsdt; struct acpi_xsdt *xsdt; - printf("RSDP %08lx %06x (v%02d %.6s)\n", (ulong)map_to_sysmem(rsdp), + printf("RSDP %08lx %5x v%02d %.6s\n", (ulong)map_to_sysmem(rsdp), rsdp->length, rsdp->revision, rsdp->oem_id); rsdt = map_sysmem(rsdp->rsdt_address, 0); xsdt = map_sysmem(rsdp->xsdt_address, 0); @@ -148,7 +148,8 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, printf("No ACPI tables present\n"); return 0; } - printf("ACPI tables start at %lx\n", gd_acpi_start()); + printf("Name Base Size Detail\n"); + printf("---- -------- ----- ------\n"); list_rsdp(rsdp); return 0; diff --git a/test/dm/acpi.c b/test/dm/acpi.c index d648f30..edad913 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -393,25 +393,26 @@ static int dm_test_acpi_cmd_list(struct unit_test_state *uts) console_record_reset(); run_command("acpi list", 0); - ut_assert_nextline("ACPI tables start at %lx", addr); - ut_assert_nextline("RSDP %08lx %06zx (v02 U-BOOT)", addr, + ut_assert_nextline("Name Base Size Detail"); + ut_assert_nextline("---- -------- ----- ------"); + ut_assert_nextline("RSDP %08lx %5zx v02 U-BOOT", addr, sizeof(struct acpi_rsdp)); addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16); - ut_assert_nextline("RSDT %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + ut_assert_nextline("RSDT %08lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0", addr, sizeof(struct acpi_table_header) + 3 * sizeof(u32), OEM_REVISION); addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16); - ut_assert_nextline("XSDT %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + ut_assert_nextline("XSDT %08lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0", addr, sizeof(struct acpi_table_header) + 3 * sizeof(u64), OEM_REVISION); addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64); - ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + ut_assert_nextline("DMAR %08lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0", addr, sizeof(struct acpi_dmar), OEM_REVISION); addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); - ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + ut_assert_nextline("DMAR %08lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0", addr, sizeof(struct acpi_dmar), OEM_REVISION); addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); - ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + ut_assert_nextline("DMAR %08lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0", addr, sizeof(struct acpi_dmar), OEM_REVISION); ut_assert_console_end(); -- cgit v1.1 From 02155e0529f05cb868c39d31c7229e1432f3a447 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:07 -0700 Subject: doc: Add usage information for the acpi command Add some documentation for this command. Signed-off-by: Simon Glass --- doc/usage/acpi.rst | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + 2 files changed, 236 insertions(+) create mode 100644 doc/usage/acpi.rst diff --git a/doc/usage/acpi.rst b/doc/usage/acpi.rst new file mode 100644 index 0000000..14bafc8 --- /dev/null +++ b/doc/usage/acpi.rst @@ -0,0 +1,235 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +acpi command +============ + +Synopis +------- + +:: + + acpi list + acpi items [-d] + acpi dump + +Description +----------- + +The *acpi* command is used to dump the ACPI tables generated by U-Boot for passing +to the operating systems. + +ACPI tables can be generated by various output functions and even devices can +output material to include in the Differentiated System Description Table (DSDT) +and SSDT tables (Secondary System Description Table). U-Boot keeps track of +which device or table-writer produced each piece of the ACPI tables. + +The ACPI tables are stored contiguously in memory. + + +acpi list +~~~~~~~~~ + +List the ACPI tables that have been generated. Each table has a 4-character +table name (e.g. SSDT, FACS) and has a format defined by the +`ACPI specification`_. + +U-Boot does not currently support decoding the tables. Unlike devicetree, ACPI +tables have no regular schema and also some include bytecode, so decoding the +tables requires a lot of code. + +The table shows the following information: + +Name + Table name, e.g. `MCFG` + +Base + Base address of table in memory + +Size + Size of table in bytes + +Detail + More information depending on the table type + + Revision + Table revision number (two decimal digits) + + OEM ID + ID for the Original Equipment Manufacturer. Typically this is "U-BOOT". + + OEM Table ID + Table ID for the Original Equipment Manufacturer. Typically this is + "U-BOOTBL" (U-Boot bootloader) + + OEM Revision + Revision string for the Original Equipment Manufacturer. Typically this + is the U-Boot release number, e.g. 20220101 (meaning v2022.01 since the + final 01 is not used). For DSDT, this is set by the source code in + the parameters of DefinitionBlock(). + + ACPI compiler-vendor ID + This is normally `INTL` for Intel + + ACPI compiler revision + This is the compiler revision. It is set to the version string for the + DSDT table but other tables just use the value 0 or 1, since U-Boot does + not actually use the compiler in these cases. It generates the code + itself. + +acpi items +~~~~~~~~~~ + +List the ACPI data that was generated, broken down by item. An item is either +an ACPI table generated by a writer function, or the part of a table that was +generated by a particular device. + +The `-d` flag also shows a binary dump of the table. + +The table shows the following information about each item: + +Seq + Sequence number in hex + +Type + Type of item + + ===== ============================================================ + Type Meaning + ===== ============================================================ + dsdt Fragment of a DSDT table, as generated by a device + ssdt Fragment of a SSDT table, as generated by a device + other A whole table of a particular type. as generated by a writer + ===== ============================================================ + +Base + Base address of table in memory + +Size + Size of table in bytes + +Device / Writer + Name of device (for ssdt/dsdt) that wrong this fragment of the table, or + name of the registered writer function (otherwise) that wrote the table. + +acpi dump +~~~~~~~~~ + +Dump a paticular ACPI table in binary format. This can be used to read the table +if you have the specification handy. + + +Example +------- + +:: + + => acpi list + Name Base Size Detail + ---- -------- ----- ------ + RSDP 79925000 24 v02 U-BOOT + RSDT 79925030 48 v01 U-BOOT U-BOOTBL 20220101 INTL 0 + XSDT 799250e0 6c v01 U-BOOT U-BOOTBL 20220101 INTL 0 + FACP 79929570 f4 v04 U-BOOT U-BOOTBL 20220101 INTL 1 + DSDT 79925280 32ea v02 U-BOOT U-BOOTBL 20110725 INTL 20180105 + FACS 79925240 40 + MCFG 79929670 2c v01 U-BOOT U-BOOTBL 20220101 INTL 0 + SPCR 799296a0 50 v02 U-BOOT U-BOOTBL 20220101 INTL 0 + TPM2 799296f0 4c v04 U-BOOT U-BOOTBL 20220101 INTL 0 + APIC 79929740 6c v02 U-BOOT U-BOOTBL 20220101 INTL 0 + SSDT 799297b0 1523 v02 U-BOOT U-BOOTBL 20220101 INTL 1 + NHLT 7992ace0 e60 v05 coral coral 3 INTL 0 + DBG2 7992db40 61 v00 U-BOOT U-BOOTBL 20220101 INTL 0 + HPET 7992dbb0 38 v01 U-BOOT U-BOOTBL 20220101 INTL 0 + => acpi items + Seq Type Base Size Device/Writer + --- ----- -------- ---- ------------- + 0 other 79925000 240 0base + 1 other 79925240 40 1facs + 2 dsdt 799252a4 58 board + 3 dsdt 799252fc 10 lpc + 4 other 79925280 32f0 3dsdt + 5 other 79928570 1000 4gnvs + 6 other 79929570 100 5fact + 7 other 79929670 30 5mcfg + 8 other 799296a0 50 5spcr + 9 other 799296f0 50 5tpm2 + a other 79929740 70 5x86 + b ssdt 799297d4 fe maxim-codec + c ssdt 799298d2 28 i2c2@16,0 + d ssdt 799298fa 270 da-codec + e ssdt 79929b6a 28 i2c2@16,1 + f ssdt 79929b92 28 i2c2@16,2 + 10 ssdt 79929bba 83 tpm@50 + 11 ssdt 79929c3d 28 i2c2@16,3 + 12 ssdt 79929c65 282 elan-touchscreen@10 + 13 ssdt 79929ee7 285 raydium-touchscreen@39 + 14 ssdt 7992a16c 28 i2c2@17,0 + 15 ssdt 7992a194 d8 elan-touchpad@15 + 16 ssdt 7992a26c 163 synaptics-touchpad@2c + 17 ssdt 7992a3cf 28 i2c2@17,1 + 18 ssdt 7992a3f7 111 wacom-digitizer@9 + 19 ssdt 7992a508 8f sdmmc@1b,0 + 1a ssdt 7992a597 4b wifi + 1b ssdt 7992a5e2 1a0 cpu@0 + 1c ssdt 7992a782 1a0 cpu@1 + 1d ssdt 7992a922 1a0 cpu@2 + 1e ssdt 7992aac2 211 cpu@3 + 1f other 799297b0 1530 6ssdt + 20 other 7992ace0 2f10 8dev + => acpi dump mcfg + MCFG @ 79929670 + 00000000: 4d 43 46 47 2c 00 00 00 01 41 55 2d 42 4f 4f 54 MCFG,....AU-BOOT + 00000010: 55 2d 42 4f 4f 54 42 4c 01 01 22 20 49 4e 54 4c U-BOOTBL.." INTL + 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 ............ + => acpi items -d + Seq Type Base Size Device/Writer + --- ----- -------- ---- ------------- + 0 other 79925000 240 0base + 00000000: 52 53 44 20 50 54 52 20 9e 55 2d 42 4f 4f 54 02 RSD PTR .U-BOOT. + 00000010: 30 50 92 79 24 00 00 00 e0 50 92 79 00 00 00 00 0P.y$....P.y.... + 00000020: a1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000030: 52 53 44 54 48 00 00 00 01 8b 55 2d 42 4f 4f 54 RSDTH.....U-BOOT + 00000040: 55 2d 42 4f 4f 54 42 4c 01 01 22 20 49 4e 54 4c U-BOOTBL.." INTL + 00000050: 00 00 00 00 70 95 92 79 70 96 92 79 a0 96 92 79 ....p..yp..y...y + 00000060: f0 96 92 79 40 97 92 79 b0 97 92 79 e0 ac 92 79 ...y@..y...y...y + 00000070: 40 db 92 79 b0 db 92 79 00 00 00 00 00 00 00 00 @..y...y........ + 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000000a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000000b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000000c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000000d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000000e0: 58 53 44 54 6c 00 00 00 01 61 55 2d 42 4f 4f 54 XSDTl....aU-BOOT + 000000f0: 55 2d 42 4f 4f 54 42 4c 01 01 22 20 49 4e 54 4c U-BOOTBL.." INTL + 00000100: 00 00 00 00 70 95 92 79 00 00 00 00 70 96 92 79 ....p..y....p..y + 00000110: 00 00 00 00 a0 96 92 79 00 00 00 00 f0 96 92 79 .......y.......y + 00000120: 00 00 00 00 40 97 92 79 00 00 00 00 b0 97 92 79 ....@..y.......y + 00000130: 00 00 00 00 e0 ac 92 79 00 00 00 00 40 db 92 79 .......y....@..y + 00000140: 00 00 00 00 b0 db 92 79 00 00 00 00 00 00 00 00 .......y........ + 00000150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + ... + + 1 other 79925240 40 1facs + 00000000: 46 41 43 53 40 00 00 00 00 00 00 00 00 00 00 00 FACS@........... + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000020: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + + 2 dsdt 799252a4 58 board + 00000000: 10 87 05 00 5c 00 08 4f 49 50 47 12 8c 04 00 03 ....\..OIPG..... + 00000010: 12 8b 01 00 04 01 01 0e ff ff ff ff ff ff ff ff ................ + 00000020: 0d 49 4e 54 33 34 35 32 3a 30 31 00 12 85 01 00 .INT3452:01..... + 00000030: 04 0a 03 01 0a 23 0d 49 4e 54 33 34 35 32 3a 30 .....#.INT3452:0 + 00000040: 31 00 12 85 01 00 04 0a 04 01 0a 0a 0d 49 4e 54 1............INT + 00000050: 33 34 35 32 3a 30 30 00 3452:00. + + 3 dsdt 799252fc 10 lpc + 00000000: 10 8f 00 00 5c 00 08 4e 56 53 41 0c 10 50 93 79 ....\..NVSA..P.y + + 4 other 79925280 32f0 3dsdt + 00000000: 44 53 44 54 ea 32 00 00 02 eb 55 2d 42 4f 4f 54 DSDT.2....U-BOOT + 00000010: 55 2d 42 4f 4f 54 42 4c 25 07 11 20 49 4e 54 4c U-BOOTBL%.. INTL + + +.. _`ACPI specification`: https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 33761af..964d761 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -18,6 +18,7 @@ Shell commands .. toctree:: :maxdepth: 1 + acpi addrmap askenv base -- cgit v1.1 From 979a24e48805e71e1c9c5b39d1e04ba64d8a79b8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:08 -0700 Subject: acpi: Add some tables needed by ARM devices Add some tables needed for ARM devices, including more MADT subtables, a CSRT descriptor, GTDT and PPTT. WIP: This needs comments added. Signed-off-by: Simon Glass --- include/acpi/acpi_table.h | 205 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h index e405012..c98c874 100644 --- a/include/acpi/acpi_table.h +++ b/include/acpi/acpi_table.h @@ -162,6 +162,9 @@ enum acpi_pm_profile { #define ACPI_FADT_HW_REDUCED_ACPI BIT(20) #define ACPI_FADT_LOW_PWR_IDLE_S0 BIT(21) +/* ARM boot flags */ +#define ACPI_ARM_PSCI_COMPLIANT BIT(0) + enum acpi_address_space_type { ACPI_ADDRESS_SPACE_MEMORY = 0, /* System memory */ ACPI_ADDRESS_SPACE_IO, /* System I/O */ @@ -237,6 +240,9 @@ struct __packed acpi_fadt { struct acpi_gen_regaddr x_pm_tmr_blk; struct acpi_gen_regaddr x_gpe0_blk; struct acpi_gen_regaddr x_gpe1_blk; + struct acpi_gen_regaddr sleep_control_reg; + struct acpi_gen_regaddr sleep_status_reg; + u64 hyp_vendor_id; }; /* FADT TABLE Revision values - note these do not match the ACPI revision */ @@ -302,6 +308,8 @@ enum acpi_apic_types { ACPI_APIC_PLATFORM_IRQ_SRC, /* Platform interrupt sources */ ACPI_APIC_LX2APIC, /* Processor local x2APIC */ ACPI_APIC_LX2APIC_NMI, /* Local x2APIC NMI */ + ACPI_APIC_GICC, /* Generic Interrupt Ctlr CPU i/f */ + ACPI_APIC_GICD /* Generic Interrupt Ctlr Distributor */ }; /* MADT: Processor Local APIC Structure */ @@ -345,6 +353,57 @@ struct __packed acpi_madt_lapic_nmi { u8 lint; /* Local APIC LINT# */ }; +/* flags for acpi_madr_gicc flags word */ +enum { + ACPI_MADRF_ENABLED = BIT(0), + ACPI_MADRF_PERF = BIT(1), + ACPI_MADRF_VGIC = BIT(2), +}; + +/** + * struct __packed acpi_madr_gicc - GIC CPU interface (type 0xb) + * + * This holds information about the Generic Interrupt Controller (GIC) CPU + * interface. See ACPI Spec v6.3 section 5.2.12.14 + */ +struct __packed acpi_madr_gicc { + u8 type; + u8 length; + u16 reserved; + u32 cpu_if_num; + u32 processor_id; + u32 flags; + u32 parking_proto; + u32 perf_gsiv; + u64 parked_addr; + u64 phys_base; + u64 gicv; + u64 gich; + u32 vgic_maint_irq; + u64 gicr_base; + u64 mpidr; + u8 efficiency; + u8 reserved2; + u16 spi_overflow_irq; +}; + +/** + * struct __packed acpi_madr_gicc - GIC distributor (type 0xc) + * + * This holds information about the Generic Interrupt Controller (GIC) + * Distributor interface. See ACPI Spec v6.3 section 5.2.12.15 + */ +struct __packed acpi_madr_gicd { + u8 type; + u8 length; + u16 reserved; + u32 gic_id; + u64 phys_base; + u32 reserved2; + u8 gic_version; + u8 reserved3[3]; +}; + /* MCFG (PCI Express MMIO config space BAR description table) */ struct acpi_mcfg { struct acpi_table_header header; @@ -371,6 +430,19 @@ struct acpi_csrt { struct acpi_table_header header; }; +/** + * struct acpi_csrt_group - header for a group within the CSRT + * + * The CSRT consists of one or more groups and this is the header for each + * + * See Core System Resources Table (CSRT), March 13, 2017, Microsoft Corporation + * for details + * + * https://uefi.org/sites/default/files/resources/CSRT%20v2.pdf + * + * @shared_info_length indicates the number of shared-info bytes following this + * struct (which may be 0) + */ struct acpi_csrt_group { u32 length; u32 vendor_id; @@ -382,6 +454,25 @@ struct acpi_csrt_group { u32 shared_info_length; }; +/** + * struct acpi_csrt_descriptor - describes the information that follows + * + * See the spec as above for details + */ +struct acpi_csrt_descriptor { + u32 length; + u16 type; + u16 subtype; + u32 uid; +}; + +/** + * struct acpi_csrt_shared_info - shared info for Intel tangier + * + * This provides the shared info for this particular board. Notes that the CSRT + * does not describe the format of data, so this format may not be used by any + * other board. + */ struct acpi_csrt_shared_info { u16 major_version; u16 minor_version; @@ -559,6 +650,120 @@ struct __packed acpi_spcr { u32 reserved2; }; +/** + * struct acpi_gtdt - Generic Timer Description Table (GTDT) + * + * See ACPI Spec v6.3 section 5.2.24 for details + */ +struct __packed acpi_gtdt { + struct acpi_table_header header; + u64 cnt_ctrl_base; + u32 reserved0; + u32 sec_el1_gsiv; + u32 sec_el1_flags; + u32 el1_gsiv; + u32 el1_flags; + u32 virt_el1_gsiv; + u32 virt_el1_flags; + u32 el2_gsiv; + u32 el2_flags; + u64 cnt_read_base; + u32 plat_timer_count; + u32 plat_timer_offset; + u32 virt_el2_gsiv; + u32 virt_el2_flags; +}; + +/** + * struct acpi_bgrt - Boot Graphics Resource Table (BGRT) + * + * Optional table that provides a mechanism to indicate that an image was drawn + * on the screen during boot, and some information about the image. + * + * See ACPI Spec v6.3 section 5.2.22 for details + */ +struct __packed acpi_bgrt { + struct acpi_table_header header; + u16 version; + u8 status; + u8 image_type; + u64 addr; + u32 offset_x; + u32 offset_y; +}; + +/* Types for PPTT */ +#define ACPI_PPTT_TYPE_PROC 0 +#define ACPI_PPTT_TYPE_CACHE 1 + +/* Flags for PPTT */ +#define ACPI_PPTT_PHYSICAL_PACKAGE BIT(0) +#define ACPI_PPTT_PROC_ID_VALID BIT(1) +#define ACPI_PPTT_PROC_IS_THREAD BIT(2) +#define ACPI_PPTT_NODE_IS_LEAF BIT(3) +#define ACPI_PPTT_CHILDREN_IDENTICAL BIT(4) + +/** + * struct acpi_pptt_header - Processor Properties Topology Table (PPTT) header + * + * Describes the topological structure of processors and their shared resources, + * such as caches. + * + * See ACPI Spec v6.3 section 5.2.29 for details + */ +struct __packed acpi_pptt_header { + u8 type; /* ACPI_PPTT_TYPE_... */ + u8 length; + u16 reserved; +}; + +/** + * struct acpi_pptt_proc - a processor as described by PPTT + */ +struct __packed acpi_pptt_proc { + struct acpi_pptt_header hdr; + u32 flags; + u32 parent; + u32 proc_id; + u32 num_resources; +}; + +/* Cache flags for acpi_pptt_cache */ +#define ACPI_PPTT_SIZE_VALID BIT(0) +#define ACPI_PPTT_SETS_VALID BIT(1) +#define ACPI_PPTT_ASSOC_VALID BIT(2) +#define ACPI_PPTT_ALLOC_TYPE_VALID BIT(3) +#define ACPI_PPTT_CACHE_TYPE_VALID BIT(4) +#define ACPI_PPTT_WRITE_POLICY_VALID BIT(5) +#define ACPI_PPTT_LINE_SIZE_VALID BIT(6) + +#define ACPI_PPTT_ALL_VALID 0x7f +#define ACPI_PPTT_ALL_BUT_WRITE_POL 0x5f + +#define ACPI_PPTT_READ_ALLOC BIT(0) +#define ACPI_PPTT_WRITE_ALLOC BIT(1) +#define ACPI_PPTT_CACHE_TYPE_SHIFT 2 +#define ACPI_PPTT_CACHE_TYPE_MASK (3 << ACPI_PPTT_CACHE_TYPE_SHIFT) +#define ACPI_PPTT_CACHE_TYPE_DATA 0 +#define ACPI_PPTT_CACHE_TYPE_INSTR 1 +#define ACPI_PPTT_CACHE_TYPE_UNIFIED 2 +#define ACPI_PPTT_CACHE_TYPE_DATA 0 +#define ACPI_PPTT_WRITE_THROUGH BIT(4) + +/** + * struct acpi_pptt_cache - a cache as described by PPTT + */ +struct __packed acpi_pptt_cache { + struct acpi_pptt_header hdr; + u32 flags; + u32 next_cache_level; + u32 size; + u32 sets; + u8 assoc; + u8 attributes; + u16 line_size; +}; + /* Tables defined/reserved by ACPI and generated by U-Boot */ enum acpi_tables { ACPITAB_BERT, -- cgit v1.1 From c77504df687077284ed4fe1b46007e7d34fa7a53 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Dec 2021 09:03:09 -0700 Subject: acpi: Add myself as maintainer Add myself as maintainer of the generic ACPI code, until someone else steps up to take it on. Signed-off-by: Simon Glass --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9c2d6fe..dcdd99e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -50,6 +50,12 @@ so much easier [Ed] Maintainers List (try to look for most precise areas first) ----------------------------------- +ACPI: +M: Simon Glass +S: Maintained +F: cmd/acpi.c +F: lib/acpi/ + ANDROID AB M: Igor Opaniuk R: Sam Protsenko -- cgit v1.1 From 821ca608d81640f1ed10fc5540eb8df18dfa6679 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 5 Nov 2021 12:52:55 -0400 Subject: usb: Use the first available device for ehci_gadget For whatever reason, usb_setup_ehci_gadget removes and probes USB device 0. However, not all systems have a device 0. Use the first device instead. The device probed should probably have something to do with the controller (as specified by e.g. ums or fastboot ). In fact, I find it odd that we probe the USB device in the first place, because this is just to set up the gadget itself. Presumably, the controller should be probed by usb_gadget_initialize somehow. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- drivers/usb/host/usb-uclass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index fd39c33..27e2fc6 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -396,7 +396,7 @@ int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) int ret; /* Find the old device and remove it */ - ret = uclass_find_device_by_seq(UCLASS_USB, 0, &dev); + ret = uclass_find_first_device(UCLASS_USB, &dev); if (ret) return ret; ret = device_remove(dev, DM_REMOVE_NORMAL); @@ -419,7 +419,7 @@ int usb_remove_ehci_gadget(struct ehci_ctrl **ctlrp) int ret; /* Find the old device and remove it */ - ret = uclass_find_device_by_seq(UCLASS_USB, 0, &dev); + ret = uclass_find_first_device(UCLASS_USB, &dev); if (ret) return ret; ret = device_remove(dev, DM_REMOVE_NORMAL); -- cgit v1.1 From a77f468099be81bdadb872a735f58ae5c2fa5973 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Sun, 21 Nov 2021 14:52:51 +0100 Subject: introduce CONFIG_DEVICE_TREE_INCLUDES The build system already automatically looks for and includes an in-tree *-u-boot.dtsi when building the control .dtb. However, there are some things that are awkward to maintain in such an in-tree file, most notably the metadata associated to public keys used for verified boot. The only "official" API to get that metadata into the .dtb is via mkimage, as a side effect of building an actual signed image. But there are multiple problems with that. First of all, the final U-Boot (be it U-Boot proper or an SPL) image is built based on a binary image, the .dtb, and possibly some other binary artifacts. So modifying the .dtb after the build requires the meta-buildsystem (Yocto, buildroot, whatnot) to know about and repeat some of the steps that are already known to and handled by U-Boot's build system, resulting in needless duplication of code. It's also somewhat annoying and inconsistent to have a .dtb file in the build folder which is not generated by the command listed in the corresponding .cmd file (that of course applies to any generated file). So the contents of the /signature node really needs to be baked into the .dtb file when it is first created, which means providing the relevant data in the form of a .dtsi file. One could in theory put that data into the *-u-boot.dtsi file, but it's more convenient to be able to provide it externally: For example, when developing for a customer, it's common to use a set of dummy keys for development, while the consultants do not (and should not) have access to the actual keys used in production. For such a setup, it's easier if the keys used are chosen via the meta-buildsystem and the path(s) patched in during the configure step. And of course, nothing prevents anybody from having DEVICE_TREE_INCLUDES point at files maintained in git, or for that matter from including the public key metadata in the *-u-boot.dtsi directly and ignore this feature. There are other uses for this, e.g. in combination with ENV_IMPORT_FDT it can be used for providing the contents of the /config/environment node, so I don't want to tie this exclusively to use for verified boot. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes Fix doc formatting error (make htmldocs) Signed-off-by: Simon Glass --- doc/develop/devicetree/control.rst | 18 ++++++++++++++++++ dts/Kconfig | 9 +++++++++ scripts/Makefile.lib | 3 +++ 3 files changed, 30 insertions(+) diff --git a/doc/develop/devicetree/control.rst b/doc/develop/devicetree/control.rst index 0e6f85d..c71570d 100644 --- a/doc/develop/devicetree/control.rst +++ b/doc/develop/devicetree/control.rst @@ -182,6 +182,24 @@ main file, in this order:: Only one of these is selected but of course you can #include another one within that file, to create a hierarchy of shared files. + +External .dtsi fragments +------------------------ + +Apart from describing the hardware present, U-Boot also uses its +control dtb for various configuration purposes. For example, the +public key(s) used for Verified Boot are embedded in a specific format +in a /signature node. + +As mentioned above, the U-Boot build system automatically includes a +`*-u-boot.dtsi` file, if found, containing U-Boot specific +quirks. However, some data, such as the mentioned public keys, are not +appropriate for upstream U-Boot but are better kept and maintained +outside the U-Boot repository. You can use CONFIG_DEVICE_TREE_INCLUDES +to specify a list of .dtsi files that will also be included when +building .dtb files. + + Relocation, SPL and TPL ----------------------- diff --git a/dts/Kconfig b/dts/Kconfig index fb7df53..4de1a70 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -147,6 +147,15 @@ config DEFAULT_DEVICE_TREE It can be overridden from the command line: $ make DEVICE_TREE= +config DEVICE_TREE_INCLUDES + string "Extra .dtsi files to include when building DT control" + depends on OF_CONTROL + help + U-Boot's control .dtb is usually built from an in-tree .dts + file, plus (if available) an in-tree U-Boot-specific .dtsi + file. This option specifies a space-separated list of extra + .dtsi files that will also be used. + config OF_LIST string "List of device tree files to include for DT control" depends on SPL_LOAD_FIT || MULTI_DTB_FIT diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 3de78d9..93cb09a 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -320,8 +320,11 @@ endif quiet_cmd_dtc = DTC $@ # Modified for U-Boot # Bring in any U-Boot-specific include at the end of the file +# And finally any custom .dtsi fragments specified with CONFIG_DEVICE_TREE_INCLUDES cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \ (cat $<; $(if $(u_boot_dtsi),echo '$(pound)include "$(u_boot_dtsi)"')) > $(pre-tmp); \ + $(foreach f,$(subst $(quote),,$(CONFIG_DEVICE_TREE_INCLUDES)), \ + echo '$(pound)include "$(f)"' >> $(pre-tmp);) \ $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $(pre-tmp) ; \ $(DTC) -O dtb -o $@ -b 0 \ -i $(dir $<) $(DTC_FLAGS) \ -- cgit v1.1 From 9876ae7db6da1cf8e106fcb46a36172fbb5781e5 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 4 Jan 2022 08:42:48 +0100 Subject: dm: Fix OF_BAD_ADDR definition When OF_LIVE flag is enabled on a 64 bits platform, there is an issue when dev_read_addr() is called and need to perform an address translation using __of_translate_address(). In case of error, __of_translate_address() return's value is OF_BAD_ADDR (wich is defined in include/dm/of.h to ((u64)-1) = 0xffffffffffffffff). The return value of dev_read_addr() is often compared to FDT_ADDR_T_NONE which is defined as (-1U) = 0xffffffff. In this case the comparison is always false. To fix this issue, define FDT_ADDR_T_NONE to (ulong)(-1) in case of AARCH64. Update accordingly related tests. Signed-off-by: Patrice Chotard Reviewed-by: Simon Glass --- include/fdtdec.h | 5 ++++- test/dm/ofnode.c | 2 +- test/dm/pci.c | 4 ++-- test/dm/test-fdt.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/fdtdec.h b/include/fdtdec.h index 9a7b6a7..4b0b505 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -24,16 +24,19 @@ typedef phys_addr_t fdt_addr_t; typedef phys_size_t fdt_size_t; -#define FDT_ADDR_T_NONE (-1U) #define FDT_SIZE_T_NONE (-1U) #ifdef CONFIG_PHYS_64BIT +#define FDT_ADDR_T_NONE ((ulong)(-1)) + #define fdt_addr_to_cpu(reg) be64_to_cpu(reg) #define fdt_size_to_cpu(reg) be64_to_cpu(reg) #define cpu_to_fdt_addr(reg) cpu_to_be64(reg) #define cpu_to_fdt_size(reg) cpu_to_be64(reg) typedef fdt64_t fdt_val_t; #else +#define FDT_ADDR_T_NONE (-1U) + #define fdt_addr_to_cpu(reg) be32_to_cpu(reg) #define fdt_size_to_cpu(reg) be32_to_cpu(reg) #define cpu_to_fdt_addr(reg) cpu_to_be32(reg) diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 5e7c968..dab0480 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -286,7 +286,7 @@ static int dm_test_ofnode_get_reg(struct unit_test_state *uts) ut_assert(ofnode_valid(node)); addr = ofnode_get_addr(node); size = ofnode_get_size(node); - ut_asserteq(FDT_ADDR_T_NONE, addr); + ut_asserteq_64(FDT_ADDR_T_NONE, addr); ut_asserteq(FDT_SIZE_T_NONE, size); node = ofnode_path("/translation-test@8000/noxlatebus@3,300/dev@42"); diff --git a/test/dm/pci.c b/test/dm/pci.c index fa2e4a8..00e4440 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -331,10 +331,10 @@ static int dm_test_pci_addr_live(struct unit_test_state *uts) struct udevice *swap1f, *swap1; ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f)); - ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f)); + ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f)); ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1)); - ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1)); + ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1)); return 0; } diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 8866d4d..e1de066 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -768,7 +768,7 @@ static int dm_test_fdt_livetree_writing(struct unit_test_state *uts) /* Test setting generic properties */ /* Non-existent in DTB */ - ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr(dev)); + ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr(dev)); /* reg = 0x42, size = 0x100 */ ut_assertok(ofnode_write_prop(node, "reg", 8, "\x00\x00\x00\x42\x00\x00\x01\x00")); -- cgit v1.1 From 8bbd8a35942aee05adca4c8909b133df23b91b3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:36 -0700 Subject: Makefile: Fake external blobs by default with binman This behaviour is necessary with boards where the binman description requires processing external blobs, since these may be missing. Enable it by default, so that CI is happy. Warnings indicate that a valid image is not produced, as with the --allow-missing option. Signed-off-by: Simon Glass --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ebc99d2..10879f1 100644 --- a/Makefile +++ b/Makefile @@ -1320,6 +1320,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ --toolpath $(objtree)/tools \ $(if $(BINMAN_VERBOSE),-v$(BINMAN_VERBOSE)) \ build -u -d u-boot.dtb -O . -m --allow-missing \ + --fake-ext-blobs \ -I . -I $(srctree) -I $(srctree)/board/$(BOARDDIR) \ -I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \ $(foreach f,$(BINMAN_INDIRS),-I $(f)) \ @@ -1331,7 +1332,6 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \ -a spl-dtb=$(CONFIG_SPL_OF_REAL) \ -a tpl-dtb=$(CONFIG_TPL_OF_REAL) \ - $(if $(BINMAN_FAKE_EXT_BLOBS),--fake-ext-blobs) \ $(BINMAN_$(@F)) OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex -- cgit v1.1 From 1e18a69394658ab56ef1c310d5352d7814c0017e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:37 -0700 Subject: binman: Tweak elf tests for a toolchain change Some newer toolchains do not create a symbol for the .ucode section that this test relies on. Update the test to use the symbol that is explicitly created, instead. Signed-off-by: Simon Glass --- tools/binman/elf_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index ac69a95..f727258 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -99,17 +99,17 @@ class TestElf(unittest.TestCase): """Test that we can obtain a symbol from the ELF file""" fname = self.ElfTestFile('u_boot_ucode_ptr') syms = elf.GetSymbols(fname, []) - self.assertIn('.ucode', syms) + self.assertIn('_dt_ucode_base_size', syms) def testRegexSymbols(self): """Test that we can obtain from the ELF file by regular expression""" fname = self.ElfTestFile('u_boot_ucode_ptr') syms = elf.GetSymbols(fname, ['ucode']) - self.assertIn('.ucode', syms) + self.assertIn('_dt_ucode_base_size', syms) syms = elf.GetSymbols(fname, ['missing']) - self.assertNotIn('.ucode', syms) + self.assertNotIn('_dt_ucode_base_size', syms) syms = elf.GetSymbols(fname, ['missing', 'ucode']) - self.assertIn('.ucode', syms) + self.assertIn('_dt_ucode_base_size', syms) def testMissingFile(self): """Test that a missing file is detected""" -- cgit v1.1 From 206117afd137c88f0b0974088a9008bdf123eeb8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:38 -0700 Subject: mkimage: Show the external-offset error This is a debug message at present, which is not very helpful. Print out the error so that action can be taken. Signed-off-by: Simon Glass --- tools/fit_image.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/fit_image.c b/tools/fit_image.c index 8df95c4..9ac5256 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -525,8 +525,9 @@ static int fit_extract_data(struct image_tool_params *params, const char *fname) /* Check if an offset for the external data was set. */ if (params->external_offset > 0) { if (params->external_offset < new_size) { - debug("External offset %x overlaps FIT length %x\n", - params->external_offset, new_size); + fprintf(stderr, + "External offset %x overlaps FIT length %x\n", + params->external_offset, new_size); ret = -EINVAL; goto err; } -- cgit v1.1 From 8bc78b73fb873590238a1788db8783ab8f65e5a1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:39 -0700 Subject: binman: Expand the external FIT test a little At present this does not check that the external data is in the expected place. Use a non-zero offset for the external data and check it. Signed-off-by: Simon Glass --- tools/binman/ftest.py | 17 +++++++++++++++++ tools/binman/test/162_fit_external.dts | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f4ff7b6..6a76473 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -3713,11 +3713,28 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('162_fit_external.dts') fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes + # Size of the external-data region as set up by mkimage + external_data_size = len(U_BOOT_DATA) + 2 + expected_size = (len(U_BOOT_DATA) + 0x400 + + tools.Align(external_data_size, 4) + + len(U_BOOT_NODTB_DATA)) + # The data should be outside the FIT dtb = fdt.Fdt.FromData(fit_data) dtb.Scan() fnode = dtb.GetNode('/images/kernel') self.assertNotIn('data', fnode.props) + self.assertEqual(len(U_BOOT_DATA), + fdt_util.fdt32_to_cpu(fnode.props['data-size'].value)) + fit_pos = 0x400; + self.assertEqual( + fit_pos, + fdt_util.fdt32_to_cpu(fnode.props['data-position'].value)) + + self.assertEquals(expected_size, len(data)) + actual_pos = len(U_BOOT_DATA) + fit_pos + self.assertEqual(U_BOOT_DATA + b'aa', + data[actual_pos:actual_pos + external_data_size]) def testSectionIgnoreHashSignature(self): """Test that sections ignore hash, signature nodes for its data""" diff --git a/tools/binman/test/162_fit_external.dts b/tools/binman/test/162_fit_external.dts index 19518e0..6f2a629 100644 --- a/tools/binman/test/162_fit_external.dts +++ b/tools/binman/test/162_fit_external.dts @@ -10,7 +10,7 @@ u-boot { }; fit { - fit,external-offset = <0>; + fit,external-offset = <0x400>; description = "test-desc"; #address-cells = <1>; -- cgit v1.1 From ade5327655802b0149a4a569dd489318ad59adc3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:40 -0700 Subject: patman: Allow running a tool and returning the full result Add a new function which returns the entire result from running a tool, not just stdout. Update Run() to use this and to return stdout on error, if stderr is empty, since some unfortunate tools write their error output to stdout rather than stderr. Move building of the PATH to a separate function. Make the exception catching more specific, to catch just ValueError, since broad exceptions are a pain to debug. Signed-off-by: Simon Glass --- tools/patman/tools.py | 56 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 86c4f61..7af4a52 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -313,7 +313,22 @@ def GetTargetCompileTool(name, cross_compile=None): target_name = name return target_name, extra_args -def Run(name, *args, **kwargs): +def get_env_with_path(): + """Get an updated environment with the PATH variable set correctly + + If there are any search paths set, these need to come first in the PATH so + that these override any other version of the tools. + + Returns: + dict: New environment with PATH updated, or None if there are not search + paths + """ + if tool_search_paths: + env = dict(os.environ) + env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH'] + return env + +def run_result(name, *args, **kwargs): """Run a tool with some arguments This runs a 'tool', which is a program used by binman to process files and @@ -326,6 +341,7 @@ def Run(name, *args, **kwargs): for_host: True to resolve the command to the version for the host for_target: False to run the command as-is, without resolving it to the version for the compile target + raise_on_error: Raise an error if the command fails (True by default) Returns: CommandResult object @@ -334,10 +350,8 @@ def Run(name, *args, **kwargs): binary = kwargs.get('binary') for_host = kwargs.get('for_host', False) for_target = kwargs.get('for_target', not for_host) - env = None - if tool_search_paths: - env = dict(os.environ) - env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH'] + raise_on_error = kwargs.get('raise_on_error', True) + env = get_env_with_path() if for_target: name, extra_args = GetTargetCompileTool(name) args = tuple(extra_args) + args @@ -349,11 +363,12 @@ def Run(name, *args, **kwargs): result = command.RunPipe([all_args], capture=True, capture_stderr=True, env=env, raise_on_error=False, binary=binary) if result.return_code: - raise ValueError("Error %d running '%s': %s" % - (result.return_code,' '.join(all_args), - result.stderr)) - return result.stdout - except: + if raise_on_error: + raise ValueError("Error %d running '%s': %s" % + (result.return_code,' '.join(all_args), + result.stderr or result.stdout)) + return result + except ValueError: if env and not PathHasFile(env['PATH'], name): msg = "Please install tool '%s'" % name package = packages.get(name) @@ -362,6 +377,27 @@ def Run(name, *args, **kwargs): raise ValueError(msg) raise +def Run(name, *args, **kwargs): + """Run a tool with some arguments + + This runs a 'tool', which is a program used by binman to process files and + perhaps produce some output. Tools can be located on the PATH or in a + search path. + + Args: + name: Command name to run + args: Arguments to the tool + for_host: True to resolve the command to the version for the host + for_target: False to run the command as-is, without resolving it + to the version for the compile target + + Returns: + CommandResult object + """ + result = run_result(name, *args, **kwargs) + if result is not None: + return result.stdout + def Filename(fname): """Resolve a file path to an absolute path. -- cgit v1.1 From 8ea6d23ffb7787c5c42b78c38466e46a43bd55ad Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:41 -0700 Subject: buildman: Move the download function to tools This function is handy for binman as well. Move it into the shared 'tools' module. Signed-off-by: Simon Glass --- tools/buildman/toolchain.py | 46 +-------------------------------------------- tools/patman/tools.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/tools/buildman/toolchain.py b/tools/buildman/toolchain.py index 4e2471f..bcae5ef 100644 --- a/tools/buildman/toolchain.py +++ b/tools/buildman/toolchain.py @@ -515,50 +515,6 @@ class Toolchains: return arch, links return None - def Download(self, url): - """Download a file to a temporary directory - - Args: - url: URL to download - Returns: - Tuple: - Temporary directory name - Full path to the downloaded archive file in that directory, - or None if there was an error while downloading - """ - print('Downloading: %s' % url) - leaf = url.split('/')[-1] - tmpdir = tempfile.mkdtemp('.buildman') - response = urllib.request.urlopen(url) - fname = os.path.join(tmpdir, leaf) - fd = open(fname, 'wb') - meta = response.info() - size = int(meta.get('Content-Length')) - done = 0 - block_size = 1 << 16 - status = '' - - # Read the file in chunks and show progress as we go - while True: - buffer = response.read(block_size) - if not buffer: - print(chr(8) * (len(status) + 1), '\r', end=' ') - break - - done += len(buffer) - fd.write(buffer) - status = r'%10d MiB [%3d%%]' % (done // 1024 // 1024, - done * 100 // size) - status = status + chr(8) * (len(status) + 1) - print(status, end=' ') - sys.stdout.flush() - fd.close() - if done != size: - print('Error, failed to download') - os.remove(fname) - fname = None - return tmpdir, fname - def Unpack(self, fname, dest): """Unpack a tar file @@ -615,7 +571,7 @@ class Toolchains: os.mkdir(dest) # Download the tar file for this toolchain and unpack it - tmpdir, tarfile = self.Download(url) + tmpdir, tarfile = tools.Download(url) if not tarfile: return 1 print(col.Color(col.GREEN, 'Unpacking to: %s' % dest), end=' ') diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 7af4a52..2f817f6 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -10,6 +10,7 @@ import shutil import struct import sys import tempfile +import urllib.request from patman import command from patman import tout @@ -632,3 +633,47 @@ def PrintFullHelp(fname): if not pager: pager = ['more'] command.Run(*pager, fname) + +def Download(url): + """Download a file to a temporary directory + + Args: + url: URL to download + Returns: + Tuple: + Temporary directory name + Full path to the downloaded archive file in that directory, + or None if there was an error while downloading + """ + print('Downloading: %s' % url) + leaf = url.split('/')[-1] + tmpdir = tempfile.mkdtemp('.buildman') + response = urllib.request.urlopen(url) + fname = os.path.join(tmpdir, leaf) + fd = open(fname, 'wb') + meta = response.info() + size = int(meta.get('Content-Length')) + done = 0 + block_size = 1 << 16 + status = '' + + # Read the file in chunks and show progress as we go + while True: + buffer = response.read(block_size) + if not buffer: + print(chr(8) * (len(status) + 1), '\r', end=' ') + break + + done += len(buffer) + fd.write(buffer) + status = r'%10d MiB [%3d%%]' % (done // 1024 // 1024, + done * 100 // size) + status = status + chr(8) * (len(status) + 1) + print(status, end=' ') + sys.stdout.flush() + fd.close() + if done != size: + print('Error, failed to download') + os.remove(fname) + fname = None + return tmpdir, fname -- cgit v1.1 From 5b7968693f9c2aefea5bd50dc1f143ec3a1caa42 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:42 -0700 Subject: patman: Tidy up the download function a little Reverse the order of the return tuple, so that the filename is first. This seems more obvious than putting the temporary directory first. Correct a bug that leaves a space on the final line. Allow the caller to control the name of the temporary directory. Signed-off-by: Simon Glass --- tools/buildman/toolchain.py | 2 +- tools/patman/tools.py | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tools/buildman/toolchain.py b/tools/buildman/toolchain.py index bcae5ef..adc75a7 100644 --- a/tools/buildman/toolchain.py +++ b/tools/buildman/toolchain.py @@ -571,7 +571,7 @@ class Toolchains: os.mkdir(dest) # Download the tar file for this toolchain and unpack it - tmpdir, tarfile = tools.Download(url) + tarfile, tmpdir = tools.Download(url, '.buildman') if not tarfile: return 1 print(col.Color(col.GREEN, 'Unpacking to: %s' % dest), end=' ') diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 2f817f6..24e2bf5 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -634,20 +634,22 @@ def PrintFullHelp(fname): pager = ['more'] command.Run(*pager, fname) -def Download(url): +def Download(url, tmpdir_pattern='.patman'): """Download a file to a temporary directory Args: - url: URL to download + url (str): URL to download + tmpdir_pattern (str): pattern to use for the temporary directory + Returns: Tuple: - Temporary directory name Full path to the downloaded archive file in that directory, or None if there was an error while downloading + Temporary directory name """ - print('Downloading: %s' % url) + print('- downloading: %s' % url) leaf = url.split('/')[-1] - tmpdir = tempfile.mkdtemp('.buildman') + tmpdir = tempfile.mkdtemp(tmpdir_pattern) response = urllib.request.urlopen(url) fname = os.path.join(tmpdir, leaf) fd = open(fname, 'wb') @@ -671,9 +673,11 @@ def Download(url): status = status + chr(8) * (len(status) + 1) print(status, end=' ') sys.stdout.flush() + print('\r', end='') + sys.stdout.flush() fd.close() if done != size: print('Error, failed to download') os.remove(fname) fname = None - return tmpdir, fname + return fname, tmpdir -- cgit v1.1 From 596fd10a79e95197ec9fa1ebe7b9e6f7bf81e217 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:43 -0700 Subject: patman: Add a function to find a tool on the path The Run() function automatically uses the PATH variable to locate a tool when running it. Add a function that does this manually, so we don't have to run a tool to find out if it is present. This is needed by the new Bintool class, which wants to check which tools are present. Signed-off-by: Simon Glass --- tools/patman/tools.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 24e2bf5..a27db05 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -378,6 +378,29 @@ def run_result(name, *args, **kwargs): raise ValueError(msg) raise +def tool_find(name): + """Search the current path for a tool + + This uses both PATH and any value from SetToolPaths() to search for a tool + + Args: + name (str): Name of tool to locate + + Returns: + str: Full path to tool if found, else None + """ + name = os.path.expanduser(name) # Expand paths containing ~ + paths = [] + pathvar = os.environ.get('PATH') + if pathvar: + paths = pathvar.split(':') + if tool_search_paths: + paths += tool_search_paths + for path in paths: + fname = os.path.join(path, name) + if os.path.isfile(fname) and os.access(fname, os.X_OK): + return fname + def Run(name, *args, **kwargs): """Run a tool with some arguments -- cgit v1.1 From 2cc8c1fba6498a3d0a4a8892c58783cb302c0bab Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:45 -0700 Subject: binman: Drop the image name from the fake-blob message This is not really needed and it makes the message different from the missing-blob message. Update it. Signed-off-by: Simon Glass --- tools/binman/control.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/binman/control.py b/tools/binman/control.py index f4c1fd0..e0d2b38 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -578,10 +578,9 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, image.CheckFakedBlobs(faked_list) if faked_list: tout.Warning( - "Image '%s:%s' has faked external blobs and is non-functional: %s" % - (image.name, image.image_name, - ' '.join([os.path.basename(e.GetDefaultFilename()) - for e in faked_list]))) + "Image '%s' has faked external blobs and is non-functional: %s" % + (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) + for e in faked_list]))) return bool(missing_list) or bool(faked_list) -- cgit v1.1 From f4590e02c133e7a971bb425d1e83c7ab0cdbf64a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:46 -0700 Subject: binman: Allow faked blobs in blob-ext-list Since this is a list of blobs, each blob should have the ability to be faked, as with blob-ext. Update the Entry base class to set allow_fake and use the base class in the section code also, so that this propagagtes to blob-ext-list, which is not a section. Signed-off-by: Simon Glass --- tools/binman/entry.py | 2 +- tools/binman/etype/blob_ext_list.py | 1 + tools/binman/etype/section.py | 1 + tools/binman/ftest.py | 8 ++++++++ tools/binman/test/218_blob_ext_list_fake.dts | 14 ++++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tools/binman/test/218_blob_ext_list_fake.dts diff --git a/tools/binman/entry.py b/tools/binman/entry.py index bac90bb..e4a1f2d 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -960,7 +960,7 @@ features to produce new behaviours. Args: allow_fake: True if allowed, False if not allowed """ - pass + self.allow_fake = allow_fake def CheckMissing(self, missing_list): """Check if any entries in this section have missing external blobs diff --git a/tools/binman/etype/blob_ext_list.py b/tools/binman/etype/blob_ext_list.py index 136ae81..29c9092 100644 --- a/tools/binman/etype/blob_ext_list.py +++ b/tools/binman/etype/blob_ext_list.py @@ -37,6 +37,7 @@ class Entry_blob_ext_list(Entry_blob): missing = False pathnames = [] for fname in self._filenames: + fname = self.check_fake_fname(fname) pathname = tools.GetInputFilename( fname, self.external and self.section.GetAllowMissing()) # Allow the file to be missing diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 7a55d03..fdd4cbb 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -805,6 +805,7 @@ class Entry_section(Entry): Args: allow_fake_blob: True if allowed, False if not allowed """ + super().SetAllowFakeBlob(allow_fake) for entry in self._entries.values(): entry.SetAllowFakeBlob(allow_fake) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 6a76473..ac6aabb 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -4982,6 +4982,14 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' has faked external blobs and is non-functional: .*") + def testExtblobListFaked(self): + """Test an extblob with missing external blob that are faked""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('216_blob_ext_list_missing.dts', + allow_fake_blobs=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*faked.*: blob-ext-list") + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/218_blob_ext_list_fake.dts b/tools/binman/test/218_blob_ext_list_fake.dts new file mode 100644 index 0000000..54ee54f --- /dev/null +++ b/tools/binman/test/218_blob_ext_list_fake.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob-ext-list { + filenames = "refcode.bin", "fake-file"; + }; + }; +}; -- cgit v1.1 From 7f29583113b2d9aeba116183df5a8bc8ae2d86e2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:47 -0700 Subject: binman: Correct path for fip_util This should be imported from the binman module. Fix it. Signed-off-by: Simon Glass --- tools/binman/fip_util_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/binman/fip_util_test.py b/tools/binman/fip_util_test.py index 4839b29..06827f5 100755 --- a/tools/binman/fip_util_test.py +++ b/tools/binman/fip_util_test.py @@ -22,7 +22,7 @@ sys.path.insert(2, os.path.join(OUR_PATH, '..')) # pylint: disable=C0413 from patman import test_util from patman import tools -import fip_util +from binman import fip_util HAVE_FIPTOOL = True try: -- cgit v1.1 From 81d6e3f088f799b213b3a4be2b26619c5801e967 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:48 -0700 Subject: binman: Add installation instructions Explain how to install binman, since it is not obvious. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 3e063d1..9dbe582 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -185,14 +185,37 @@ Binman is intended to replace all of this, with ifdtool left to handle only the configuration of the Intel-format descriptor. -Running binman --------------- +Installing binman +----------------- First install prerequisites, e.g:: sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ liblz4-tool +You can run binman directly if you put it on your PATH. But if you want to +install into your `~/.local` Python directory, use:: + + pip install tools/patman tools/dtoc tools/binman + +Note that binman makes use of libraries from patman and dtoc, which is why these +need to be installed. Also you need `libfdt` and `pylibfdt` which can be +installed like this:: + + git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git + cd dtc + pip install . + make NO_PYTHON=1 install + +This installs the `libfdt.so` library into `~/lib` so you can use +`LD_LIBRARY_PATH=~/lib` when running binman. If you want to install it in the +system-library directory, replace the last line with:: + + make NO_PYTHON=1 PREFIX=/ install + +Running binman +-------------- + Type:: binman build -b -- cgit v1.1 From 252de6b1f7189062d984b72149ea4d3123a63d8d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:49 -0700 Subject: binman: Add support for bintools Binman requires various tools to actually work, such as 'lz4' to compress data and 'futility' to sign Chrome OS firmware. At present these are handled in an ad-hoc manner and there is no easy way to find out what tools are needd to build an image, nor where to obtain them. Add an implementation of 'bintool', a base class which implements this functionality. When a bintool is required, it can be requested from this module, then executed. When the tool is missing, it can provide a way to obtain it. Note that this uses Command directly, not the tools.Run() function. This allows proper handling of missing tools and avoids needing to catch and re-raise exceptions. Signed-off-by: Simon Glass --- tools/binman/bintool.py | 421 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 tools/binman/bintool.py diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py new file mode 100644 index 0000000..34102da --- /dev/null +++ b/tools/binman/bintool.py @@ -0,0 +1,421 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Base class for all bintools + +This defines the common functionality for all bintools, including running +the tool, checking its version and fetching it if needed. +""" + +import collections +import glob +import importlib +import multiprocessing +import os +import shutil +import tempfile +import urllib.error + +from patman import command +from patman import terminal +from patman import tools +from patman import tout + +BINMAN_DIR = os.path.dirname(os.path.realpath(__file__)) + +# Format string for listing bintools, see also the header in list_all() +FORMAT = '%-16.16s %-12.12s %-26.26s %s' + +# List of known modules, to avoid importing the module multiple times +modules = {} + +# Possible ways of fetching a tool (FETCH_COUNT is number of ways) +FETCH_ANY, FETCH_BIN, FETCH_BUILD, FETCH_COUNT = range(4) + +FETCH_NAMES = { + FETCH_ANY: 'any method', + FETCH_BIN: 'binary download', + FETCH_BUILD: 'build from source' + } + +# Status of tool fetching +FETCHED, FAIL, PRESENT, STATUS_COUNT = range(4) + +DOWNLOAD_DESTDIR = os.path.join(os.getenv('HOME'), 'bin') + +class Bintool: + """Tool which operates on binaries to help produce entry contents + + This is the base class for all bintools + """ + # List of bintools to regard as missing + missing_list = [] + + def __init__(self, name, desc): + self.name = name + self.desc = desc + + @staticmethod + def find_bintool_class(btype): + """Look up the bintool class for bintool + + Args: + byte: Bintool to use, e.g. 'mkimage' + + Returns: + The bintool class object if found, else a tuple: + module name that could not be found + exception received + """ + # Convert something like 'u-boot' to 'u_boot' since we are only + # interested in the type. + module_name = btype.replace('-', '_') + module = modules.get(module_name) + + # Import the module if we have not already done so + if not module: + try: + module = importlib.import_module('binman.btool.' + module_name) + except ImportError as exc: + return module_name, exc + modules[module_name] = module + + # Look up the expected class name + return getattr(module, 'Bintool%s' % module_name) + + @staticmethod + def create(name): + """Create a new bintool object + + Args: + name (str): Bintool to create, e.g. 'mkimage' + + Returns: + A new object of the correct type (a subclass of Binutil) + """ + cls = Bintool.find_bintool_class(name) + if isinstance(cls, tuple): + raise ValueError("Cannot import bintool module '%s': %s" % cls) + + # Call its constructor to get the object we want. + obj = cls(name) + return obj + + def show(self): + """Show a line of information about a bintool""" + if self.is_present(): + version = self.version() + else: + version = '-' + print(FORMAT % (self.name, version, self.desc, + self.get_path() or '(not found)')) + + @classmethod + def set_missing_list(cls, missing_list): + cls.missing_list = missing_list or [] + + @staticmethod + def get_tool_list(include_testing=False): + """Get a list of the known tools + + Returns: + list of str: names of all tools known to binman + """ + files = glob.glob(os.path.join(BINMAN_DIR, 'btool/*')) + names = [os.path.splitext(os.path.basename(fname))[0] + for fname in files] + names = [name for name in names if name[0] != '_'] + if include_testing: + names.append('_testing') + return sorted(names) + + @staticmethod + def list_all(): + """List all the bintools known to binman""" + names = Bintool.get_tool_list() + print(FORMAT % ('Name', 'Version', 'Description', 'Path')) + print(FORMAT % ('-' * 15,'-' * 11, '-' * 25, '-' * 30)) + for name in names: + btool = Bintool.create(name) + btool.show() + + def is_present(self): + """Check if a bintool is available on the system + + Returns: + bool: True if available, False if not + """ + if self.name in self.missing_list: + return False + return bool(self.get_path()) + + def get_path(self): + """Get the path of a bintool + + Returns: + str: Path to the tool, if available, else None + """ + return tools.tool_find(self.name) + + def fetch_tool(self, method, col, skip_present): + """Fetch a single tool + + Args: + method (FETCH_...): Method to use + col (terminal.Color): Color terminal object + skip_present (boo;): Skip fetching if it is already present + + Returns: + int: Result of fetch either FETCHED, FAIL, PRESENT + """ + def try_fetch(meth): + res = None + try: + res = self.fetch(meth) + except urllib.error.URLError as uerr: + message = uerr.reason + print(col.Color(col.RED, f'- {message}')) + + except ValueError as exc: + print(f'Exception: {exc}') + return res + + if skip_present and self.is_present(): + return PRESENT + print(col.Color(col.YELLOW, 'Fetch: %s' % self.name)) + if method == FETCH_ANY: + for try_method in range(1, FETCH_COUNT): + print(f'- trying method: {FETCH_NAMES[try_method]}') + result = try_fetch(try_method) + if result: + break + else: + result = try_fetch(method) + if not result: + return FAIL + if result is not True: + fname, tmpdir = result + dest = os.path.join(DOWNLOAD_DESTDIR, self.name) + print(f"- writing to '{dest}'") + shutil.move(fname, dest) + if tmpdir: + shutil.rmtree(tmpdir) + return FETCHED + + @staticmethod + def fetch_tools(method, names_to_fetch): + """Fetch bintools from a suitable place + + This fetches or builds the requested bintools so that they can be used + by binman + + Args: + names_to_fetch (list of str): names of bintools to fetch + + Returns: + True on success, False on failure + """ + def show_status(color, prompt, names): + print(col.Color( + color, f'{prompt}:%s{len(names):2}: %s' % + (' ' * (16 - len(prompt)), ' '.join(names)))) + + col = terminal.Color() + skip_present = False + name_list = names_to_fetch + if len(names_to_fetch) == 1 and names_to_fetch[0] in ['all', 'missing']: + name_list = Bintool.get_tool_list() + if names_to_fetch[0] == 'missing': + skip_present = True + print(col.Color(col.YELLOW, + 'Fetching tools: %s' % ' '.join(name_list))) + status = collections.defaultdict(list) + for name in name_list: + btool = Bintool.create(name) + result = btool.fetch_tool(method, col, skip_present) + status[result].append(name) + if result == FAIL: + if method == FETCH_ANY: + print('- failed to fetch with all methods') + else: + print(f"- method '{FETCH_NAMES[method]}' is not supported") + + if len(name_list) > 1: + if skip_present: + show_status(col.GREEN, 'Already present', status[PRESENT]) + show_status(col.GREEN, 'Tools fetched', status[FETCHED]) + if status[FAIL]: + show_status(col.RED, 'Failures', status[FAIL]) + return not status[FAIL] + + def run_cmd_result(self, *args, binary=False, raise_on_error=True): + """Run the bintool using command-line arguments + + Args: + args (list of str): Arguments to provide, in addition to the bintool + name + binary (bool): True to return output as bytes instead of str + raise_on_error (bool): True to raise a ValueError exception if the + tool returns a non-zero return code + + Returns: + CommandResult: Resulting output from the bintool, or None if the + tool is not present + """ + if self.name in self.missing_list: + return None + name = os.path.expanduser(self.name) # Expand paths containing ~ + all_args = (name,) + args + env = tools.get_env_with_path() + tout.Detail(f"bintool: {' '.join(all_args)}") + result = command.RunPipe( + [all_args], capture=True, capture_stderr=True, env=env, + raise_on_error=False, binary=binary) + + if result.return_code: + # Return None if the tool was not found. In this case there is no + # output from the tool and it does not appear on the path. We still + # try to run it (as above) since RunPipe() allows faking the tool's + # output + if not any([result.stdout, result.stderr, tools.tool_find(name)]): + tout.Info(f"bintool '{name}' not found") + return None + if raise_on_error: + tout.Info(f"bintool '{name}' failed") + raise ValueError("Error %d running '%s': %s" % + (result.return_code, ' '.join(all_args), + result.stderr or result.stdout)) + if result.stdout: + tout.Debug(result.stdout) + if result.stderr: + tout.Debug(result.stderr) + return result + + def run_cmd(self, *args, binary=False): + """Run the bintool using command-line arguments + + Args: + args (list of str): Arguments to provide, in addition to the bintool + name + binary (bool): True to return output as bytes instead of str + + Returns: + str or bytes: Resulting stdout from the bintool + """ + result = self.run_cmd_result(*args, binary=binary) + if result: + return result.stdout + + @classmethod + def build_from_git(cls, git_repo, make_target, bintool_path): + """Build a bintool from a git repo + + This clones the repo in a temporary directory, builds it with 'make', + then returns the filename of the resulting executable bintool + + Args: + git_repo (str): URL of git repo + make_target (str): Target to pass to 'make' to build the tool + bintool_path (str): Relative path of the tool in the repo, after + build is complete + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + or None on error + """ + tmpdir = tempfile.mkdtemp(prefix='binmanf.') + print(f"- clone git repo '{git_repo}' to '{tmpdir}'") + tools.Run('git', 'clone', '--depth', '1', git_repo, tmpdir) + print(f"- build target '{make_target}'") + tools.Run('make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}', + make_target) + fname = os.path.join(tmpdir, bintool_path) + if not os.path.exists(fname): + print(f"- File '{fname}' was not produced") + return None + return fname, tmpdir + + @classmethod + def fetch_from_url(cls, url): + """Fetch a bintool from a URL + + Args: + url (str): URL to fetch from + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + """ + fname, tmpdir = tools.Download(url) + tools.Run('chmod', 'a+x', fname) + return fname, tmpdir + + @classmethod + def fetch_from_drive(cls, drive_id): + """Fetch a bintool from Google drive + + Args: + drive_id (str): ID of file to fetch. For a URL of the form + 'https://drive.google.com/file/d/xxx/view?usp=sharing' the value + passed here should be 'xxx' + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + """ + url = f'https://drive.google.com/uc?export=download&id={drive_id}' + return cls.fetch_from_url(url) + + @classmethod + def apt_install(cls, package): + """Install a bintool using the 'aot' tool + + This requires use of servo so may request a password + + Args: + package (str): Name of package to install + + Returns: + True, assuming it completes without error + """ + args = ['sudo', 'apt', 'install', '-y', package] + print('- %s' % ' '.join(args)) + tools.Run(*args) + return True + + # pylint: disable=W0613 + def fetch(self, method): + """Fetch handler for a bintool + + This should be implemented by the base class + + Args: + method (FETCH_...): Method to use + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + or True if the file was fetched and already installed + or None if no fetch() implementation is available + + Raises: + Valuerror: Fetching could not be completed + """ + print(f"No method to fetch bintool '{self.name}'") + return False + + # pylint: disable=R0201 + def version(self): + """Version handler for a bintool + + This should be implemented by the base class + + Returns: + str: Version string for this bintool + """ + return 'unknown' -- cgit v1.1 From 386c63cfad30901055a7d41173c2a99d268b6b0d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:50 -0700 Subject: binman: Plumb in support for bintools Support collecting the available bintools needed by an image, by scanning the entries in the image. Also add a command-line interface to access the basic bintool features, such as listing the bintools and fetching them if needed. Signed-off-by: Simon Glass --- tools/binman/cmdline.py | 7 +++++++ tools/binman/control.py | 17 ++++++++++++++++- tools/binman/entry.py | 22 ++++++++++++++++++++++ tools/binman/etype/section.py | 4 ++++ tools/binman/ftest.py | 1 + tools/binman/image.py | 14 ++++++++++++++ 6 files changed, 64 insertions(+), 1 deletion(-) diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 6c68595..92cc14b 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -167,4 +167,11 @@ controlled by a description in the board device tree.''' test_parser.add_argument('tests', nargs='*', help='Test names to run (omit for all)') + tool_parser = subparsers.add_parser('tool', help='Check bintools') + tool_parser.add_argument('-l', '--list', action='store_true', + help='List all known bintools') + tool_parser.add_argument('-f', '--fetch', action='store_true', + help='fetch a bintool from a known location (or: all/missing)') + tool_parser.add_argument('bintools', type=str, nargs='*') + return parser.parse_args(argv) diff --git a/tools/binman/control.py b/tools/binman/control.py index e0d2b38..5b10f19 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -14,6 +14,7 @@ import re import sys from patman import tools +from binman import bintool from binman import cbfs_util from binman import elf from patman import command @@ -487,6 +488,7 @@ def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): # without changing the device-tree size, thus ensuring that our # entry offsets remain the same. for image in images.values(): + image.CollectBintools() image.ExpandEntries() if update_fdt: image.AddMissingProperties(True) @@ -606,7 +608,7 @@ def Binman(args): from binman.image import Image from binman import state - if args.cmd in ['ls', 'extract', 'replace']: + if args.cmd in ['ls', 'extract', 'replace', 'tool']: try: tout.Init(args.verbosity) tools.PrepareOutputDir(None) @@ -621,6 +623,19 @@ def Binman(args): ReplaceEntries(args.image, args.filename, args.indir, args.paths, do_compress=not args.compressed, allow_resize=not args.fix_size, write_map=args.map) + + if args.cmd == 'tool': + tools.SetToolPaths(args.toolpath) + if args.list: + bintool.Bintool.list_all() + elif args.fetch: + if not args.bintools: + raise ValueError( + "Please specify bintools to fetch or 'all' or 'missing'") + bintool.Bintool.fetch_tools(bintool.FETCH_ANY, + args.bintools) + else: + raise ValueError("Invalid arguments to 'tool' subcommand") except: raise finally: diff --git a/tools/binman/entry.py b/tools/binman/entry.py index e4a1f2d..9cd9006 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -10,6 +10,7 @@ import os import pathlib import sys +from binman import bintool from dtoc import fdt_util from patman import tools from patman.tools import ToHex, ToHexSize @@ -74,6 +75,7 @@ class Entry(object): allow_fake: Allow creating a dummy fake file if the blob file is not available. This is mainly used for testing. external: True if this entry contains an external binary blob + bintools: Bintools used by this entry (only populated for Image) """ def __init__(self, section, etype, node, name_prefix=''): # Put this here to allow entry-docs and help to work without libfdt @@ -105,6 +107,7 @@ class Entry(object): self.external = False self.allow_missing = False self.allow_fake = False + self.bintools = {} @staticmethod def FindEntryClass(etype, expanded): @@ -1065,3 +1068,22 @@ features to produce new behaviours. value: Help text """ pass + + def AddBintools(self, tools): + """Add the bintools used by this entry type + + Args: + tools (dict of Bintool): + """ + pass + + @classmethod + def AddBintool(self, tools, name): + """Add a new bintool to the tools used by this etype + + Args: + name: Name of the tool + """ + btool = bintool.Bintool.create(name) + tools[name] = btool + return btool diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index fdd4cbb..221a64c 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -880,3 +880,7 @@ class Entry_section(Entry): def CheckAltFormats(self, alt_formats): for entry in self._entries.values(): entry.CheckAltFormats(alt_formats) + + def AddBintools(self, tools): + for entry in self._entries.values(): + entry.AddBintools(tools) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index ac6aabb..179326c 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -18,6 +18,7 @@ import sys import tempfile import unittest +from binman import bintool from binman import cbfs_util from binman import cmdline from binman import control diff --git a/tools/binman/image.py b/tools/binman/image.py index f0a7d65..0f0c1d2 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -82,6 +82,7 @@ class Image(section.Entry_section): self.missing_etype = missing_etype self.use_expanded = use_expanded self.test_section_timeout = False + self.bintools = {} if not test: self.ReadNode() @@ -394,3 +395,16 @@ class Image(section.Entry_section): self._CollectEntries(entries, entries_by_name, self) return self.LookupSymbol(sym_name, optional, msg, base_addr, entries_by_name) + + def CollectBintools(self): + """Collect all the bintools used by this image + + Returns: + Dict of bintools: + key: name of tool + value: Bintool object + """ + bintools = {} + super().AddBintools(bintools) + self.bintools = bintools + return bintools -- cgit v1.1 From 3b47dfa50643fca13d4886bc6f049eed6e1aaa3a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:51 -0700 Subject: binman: Add tests for bintool Add tests to cover the bintool functionality. Signed-off-by: Simon Glass --- tools/binman/bintool_test.py | 353 +++++++++++++++++++++++++++++++++++++++++ tools/binman/btool/_testing.py | 36 +++++ 2 files changed, 389 insertions(+) create mode 100644 tools/binman/bintool_test.py create mode 100644 tools/binman/btool/_testing.py diff --git a/tools/binman/bintool_test.py b/tools/binman/bintool_test.py new file mode 100644 index 0000000..3d6bcda --- /dev/null +++ b/tools/binman/bintool_test.py @@ -0,0 +1,353 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# Written by Simon Glass +# + +"""Tests for the Bintool class""" + +import collections +import os +import shutil +import tempfile +import unittest +import unittest.mock +import urllib.error + +from binman import bintool +from binman.bintool import Bintool + +from patman import command +from patman import terminal +from patman import test_util +from patman import tools + +# pylint: disable=R0904 +class TestBintool(unittest.TestCase): + """Tests for the Bintool class""" + def setUp(self): + # Create a temporary directory for test files + self._indir = tempfile.mkdtemp(prefix='bintool.') + self.seq = None + self.count = None + self.fname = None + self.btools = None + + def tearDown(self): + """Remove the temporary input directory and its contents""" + if self._indir: + shutil.rmtree(self._indir) + self._indir = None + + def test_missing_btype(self): + """Test that unknown bintool types are detected""" + with self.assertRaises(ValueError) as exc: + Bintool.create('missing') + self.assertIn("No module named 'binman.btool.missing'", + str(exc.exception)) + + def test_fresh_bintool(self): + """Check that the _testing bintool is not cached""" + btest = Bintool.create('_testing') + btest.present = True + btest2 = Bintool.create('_testing') + self.assertFalse(btest2.present) + + def test_version(self): + """Check handling of a tool being present or absent""" + btest = Bintool.create('_testing') + with test_util.capture_sys_output() as (stdout, _): + btest.show() + self.assertFalse(btest.is_present()) + self.assertIn('-', stdout.getvalue()) + btest.present = True + self.assertTrue(btest.is_present()) + self.assertEqual('123', btest.version()) + with test_util.capture_sys_output() as (stdout, _): + btest.show() + self.assertIn('123', stdout.getvalue()) + + def test_fetch_present(self): + """Test fetching of a tool""" + btest = Bintool.create('_testing') + btest.present = True + col = terminal.Color() + self.assertEqual(bintool.PRESENT, + btest.fetch_tool(bintool.FETCH_ANY, col, True)) + + @classmethod + def check_fetch_url(cls, fake_download, method): + """Check the output from fetching a tool + + Args: + fake_download (function): Function to call instead of + tools.Download() + method (bintool.FETCH_...: Fetch method to use + + Returns: + str: Contents of stdout + """ + btest = Bintool.create('_testing') + col = terminal.Color() + with unittest.mock.patch.object(tools, 'Download', + side_effect=fake_download): + with test_util.capture_sys_output() as (stdout, _): + btest.fetch_tool(method, col, False) + return stdout.getvalue() + + def test_fetch_url_err(self): + """Test an error while fetching a tool from a URL""" + def fail_download(url): + """Take the tools.Download() function by raising an exception""" + raise urllib.error.URLError('my error') + + stdout = self.check_fetch_url(fail_download, bintool.FETCH_ANY) + self.assertIn('my error', stdout) + + def test_fetch_url_exception(self): + """Test an exception while fetching a tool from a URL""" + def cause_exc(url): + raise ValueError('exc error') + + stdout = self.check_fetch_url(cause_exc, bintool.FETCH_ANY) + self.assertIn('exc error', stdout) + + def test_fetch_method(self): + """Test fetching using a particular method""" + def fail_download(url): + """Take the tools.Download() function by raising an exception""" + raise urllib.error.URLError('my error') + + stdout = self.check_fetch_url(fail_download, bintool.FETCH_BIN) + self.assertIn('my error', stdout) + + def test_fetch_pass_fail(self): + """Test fetching multiple tools with some passing and some failing""" + def handle_download(_): + """Take the tools.Download() function by writing a file""" + if self.seq: + raise urllib.error.URLError('not found') + self.seq += 1 + tools.WriteFile(fname, expected) + return fname, dirname + + expected = b'this is a test' + dirname = os.path.join(self._indir, 'download_dir') + os.mkdir(dirname) + fname = os.path.join(dirname, 'downloaded') + destdir = os.path.join(self._indir, 'dest_dir') + os.mkdir(destdir) + dest_fname = os.path.join(destdir, '_testing') + self.seq = 0 + + with unittest.mock.patch.object(bintool, 'DOWNLOAD_DESTDIR', destdir): + with unittest.mock.patch.object(tools, 'Download', + side_effect=handle_download): + with test_util.capture_sys_output() as (stdout, _): + Bintool.fetch_tools(bintool.FETCH_ANY, ['_testing'] * 2) + self.assertTrue(os.path.exists(dest_fname)) + data = tools.ReadFile(dest_fname) + self.assertEqual(expected, data) + + lines = stdout.getvalue().splitlines() + self.assertTrue(len(lines) > 2) + self.assertEqual('Tools fetched: 1: _testing', lines[-2]) + self.assertEqual('Failures: 1: _testing', lines[-1]) + + def test_tool_list(self): + """Test listing available tools""" + self.assertGreater(len(Bintool.get_tool_list()), 3) + + def check_fetch_all(self, method): + """Helper to check the operation of fetching all tools""" + + # pylint: disable=W0613 + def fake_fetch(method, col, skip_present): + """Fakes the Binutils.fetch() function + + Returns FETCHED and FAIL on alternate calls + """ + self.seq += 1 + result = bintool.FETCHED if self.seq & 1 else bintool.FAIL + self.count[result] += 1 + return result + + self.seq = 0 + self.count = collections.defaultdict(int) + with unittest.mock.patch.object(bintool.Bintool, 'fetch_tool', + side_effect=fake_fetch): + with test_util.capture_sys_output() as (stdout, _): + Bintool.fetch_tools(method, ['all']) + lines = stdout.getvalue().splitlines() + self.assertIn(f'{self.count[bintool.FETCHED]}: ', lines[-2]) + self.assertIn(f'{self.count[bintool.FAIL]}: ', lines[-1]) + + def test_fetch_all(self): + """Test fetching all tools""" + self.check_fetch_all(bintool.FETCH_ANY) + + def test_fetch_all_specific(self): + """Test fetching all tools with a specific method""" + self.check_fetch_all(bintool.FETCH_BIN) + + def test_fetch_missing(self): + """Test fetching missing tools""" + # pylint: disable=W0613 + def fake_fetch2(method, col, skip_present): + """Fakes the Binutils.fetch() function + + Returns PRESENT only for the '_testing' bintool + """ + btool = list(self.btools.values())[self.seq] + self.seq += 1 + print('fetch', btool.name) + if btool.name == '_testing': + return bintool.PRESENT + return bintool.FETCHED + + # Preload a list of tools to return when get_tool_list() and create() + # are called + all_tools = Bintool.get_tool_list(True) + self.btools = collections.OrderedDict() + for name in all_tools: + self.btools[name] = Bintool.create(name) + self.seq = 0 + with unittest.mock.patch.object(bintool.Bintool, 'fetch_tool', + side_effect=fake_fetch2): + with unittest.mock.patch.object(bintool.Bintool, + 'get_tool_list', + side_effect=[all_tools]): + with unittest.mock.patch.object(bintool.Bintool, 'create', + side_effect=self.btools.values()): + with test_util.capture_sys_output() as (stdout, _): + Bintool.fetch_tools(bintool.FETCH_ANY, ['missing']) + lines = stdout.getvalue().splitlines() + num_tools = len(self.btools) + fetched = [line for line in lines if 'Tools fetched:' in line].pop() + present = [line for line in lines if 'Already present:' in line].pop() + self.assertIn(f'{num_tools - 1}: ', fetched) + self.assertIn('1: ', present) + + def check_build_method(self, write_file): + """Check the output from fetching using the BUILD method + + Args: + write_file (bool): True to write the output file when 'make' is + called + + Returns: + tuple: + str: Filename of written file (or missing 'make' output) + str: Contents of stdout + """ + def fake_run(*cmd): + if cmd[0] == 'make': + # See Bintool.build_from_git() + tmpdir = cmd[2] + self.fname = os.path.join(tmpdir, 'pathname') + if write_file: + tools.WriteFile(self.fname, b'hello') + + btest = Bintool.create('_testing') + col = terminal.Color() + self.fname = None + with unittest.mock.patch.object(bintool, 'DOWNLOAD_DESTDIR', + self._indir): + with unittest.mock.patch.object(tools, 'Run', side_effect=fake_run): + with test_util.capture_sys_output() as (stdout, _): + btest.fetch_tool(bintool.FETCH_BUILD, col, False) + fname = os.path.join(self._indir, '_testing') + return fname if write_file else self.fname, stdout.getvalue() + + def test_build_method(self): + """Test fetching using the build method""" + fname, stdout = self.check_build_method(write_file=True) + self.assertTrue(os.path.exists(fname)) + self.assertIn(f"writing to '{fname}", stdout) + + def test_build_method_fail(self): + """Test fetching using the build method when no file is produced""" + fname, stdout = self.check_build_method(write_file=False) + self.assertFalse(os.path.exists(fname)) + self.assertIn(f"File '{fname}' was not produced", stdout) + + def test_install(self): + """Test fetching using the install method""" + btest = Bintool.create('_testing') + btest.install = True + col = terminal.Color() + with unittest.mock.patch.object(tools, 'Run', return_value=None): + with test_util.capture_sys_output() as _: + result = btest.fetch_tool(bintool.FETCH_BIN, col, False) + self.assertEqual(bintool.FETCHED, result) + + def test_no_fetch(self): + """Test fetching when there is no method""" + btest = Bintool.create('_testing') + btest.disable = True + col = terminal.Color() + with test_util.capture_sys_output() as _: + result = btest.fetch_tool(bintool.FETCH_BIN, col, False) + self.assertEqual(bintool.FAIL, result) + + def test_all_bintools(self): + """Test that all bintools can handle all available fetch types""" + def handle_download(_): + """Take the tools.Download() function by writing a file""" + tools.WriteFile(fname, expected) + return fname, dirname + + def fake_run(*cmd): + if cmd[0] == 'make': + # See Bintool.build_from_git() + tmpdir = cmd[2] + self.fname = os.path.join(tmpdir, 'pathname') + tools.WriteFile(self.fname, b'hello') + + expected = b'this is a test' + dirname = os.path.join(self._indir, 'download_dir') + os.mkdir(dirname) + fname = os.path.join(dirname, 'downloaded') + + with unittest.mock.patch.object(tools, 'Run', side_effect=fake_run): + with unittest.mock.patch.object(tools, 'Download', + side_effect=handle_download): + with test_util.capture_sys_output() as _: + for name in Bintool.get_tool_list(): + btool = Bintool.create(name) + for method in range(bintool.FETCH_COUNT): + result = btool.fetch(method) + self.assertTrue(result is not False) + if result is not True and result is not None: + result_fname, _ = result + self.assertTrue(os.path.exists(result_fname)) + data = tools.ReadFile(result_fname) + self.assertEqual(expected, data) + os.remove(result_fname) + + def test_all_bintool_versions(self): + """Test handling of bintool version when it cannot be run""" + all_tools = Bintool.get_tool_list() + for name in all_tools: + btool = Bintool.create(name) + with unittest.mock.patch.object( + btool, 'run_cmd_result', return_value=command.CommandResult()): + self.assertEqual('unknown', btool.version()) + + def test_force_missing(self): + btool = Bintool.create('_testing') + btool.present = True + self.assertTrue(btool.is_present()) + + btool.present = None + Bintool.set_missing_list(['_testing']) + self.assertFalse(btool.is_present()) + + def test_failed_command(self): + """Check that running a command that does not exist returns None""" + btool = Bintool.create('_testing') + result = btool.run_cmd_result('fred') + self.assertIsNone(result) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/binman/btool/_testing.py b/tools/binman/btool/_testing.py new file mode 100644 index 0000000..4005e8a --- /dev/null +++ b/tools/binman/btool/_testing.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool used for testing + +This is not a real bintool, just one used for testing""" + +from binman import bintool + +# pylint: disable=C0103 +class Bintool_testing(bintool.Bintool): + """Bintool used for testing""" + def __init__(self, name): + super().__init__(name, 'testing') + self.present = False + self.install = False + self.disable = False + + def is_present(self): + if self.present is None: + return super().is_present() + return self.present + + def version(self): + return '123' + + def fetch(self, method): + if self.disable: + return super().fetch(method) + if method == bintool.FETCH_BIN: + if self.install: + return self.apt_install('package') + return self.fetch_from_drive('junk') + if method == bintool.FETCH_BUILD: + return self.build_from_git('url', 'target', 'pathname') + return None -- cgit v1.1 From d38833373b631ea5aabad2afea0efd4e0e5e71da Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:52 -0700 Subject: binman: Add a bintool implementation for cbfstool Add a Bintool for this, which is used to run CBFS tests. It supports the features needed by the tests as well as fetching a binary from Google Drive. Building it from source is very slow since it is not separately supported by the coreboot build system and it builds an entire gcc toolchain before starting. Signed-off-by: Simon Glass --- tools/binman/btool/cbfstool.py | 219 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 tools/binman/btool/cbfstool.py diff --git a/tools/binman/btool/cbfstool.py b/tools/binman/btool/cbfstool.py new file mode 100644 index 0000000..29be2d8 --- /dev/null +++ b/tools/binman/btool/cbfstool.py @@ -0,0 +1,219 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for cbfstool + +cfstool provides a number of features useful with Coreboot Filesystem binaries. + +Documentation is at https://www.coreboot.org/CBFS + +Source code is at https://github.com/coreboot/coreboot/blob/master/util/cbfstool/cbfstool.c + +Here is the help: + +cbfstool: Management utility for CBFS formatted ROM images + +USAGE: + cbfstool [-h] + cbfstool FILE COMMAND [-v] [PARAMETERS]... + +OPTIONs: + -H header_offset Do not search for header; use this offset* + -T Output top-aligned memory address + -u Accept short data; fill upward/from bottom + -d Accept short data; fill downward/from top + -F Force action + -g Generate position and alignment arguments + -U Unprocessed; don't decompress or make ELF + -v Provide verbose output + -h Display this help message + +COMMANDs: + add [-r image,regions] -f FILE -n NAME -t TYPE [-A hash] \ + [-c compression] [-b base-address | -a alignment] \ + [-p padding size] [-y|--xip if TYPE is FSP] \ + [-j topswap-size] (Intel CPUs only) [--ibb] + Add a component + -j valid size: 0x10000 0x20000 0x40000 0x80000 0x100000 + add-payload [-r image,regions] -f FILE -n NAME [-A hash] \ + [-c compression] [-b base-address] \ + (linux specific: [-C cmdline] [-I initrd]) + Add a payload to the ROM + add-stage [-r image,regions] -f FILE -n NAME [-A hash] \ + [-c compression] [-b base] [-S section-to-ignore] \ + [-a alignment] [-y|--xip] [-P page-size] [--ibb] + Add a stage to the ROM + add-flat-binary [-r image,regions] -f FILE -n NAME \ + [-A hash] -l load-address -e entry-point \ + [-c compression] [-b base] + Add a 32bit flat mode binary + add-int [-r image,regions] -i INTEGER -n NAME [-b base] + Add a raw 64-bit integer value + add-master-header [-r image,regions] \ + [-j topswap-size] (Intel CPUs only) + Add a legacy CBFS master header + remove [-r image,regions] -n NAME + Remove a component + compact -r image,regions + Defragment CBFS image. + copy -r image,regions -R source-region + Create a copy (duplicate) cbfs instance in fmap + create -m ARCH -s size [-b bootblock offset] \ + [-o CBFS offset] [-H header offset] [-B bootblock] + Create a legacy ROM file with CBFS master header* + create -M flashmap [-r list,of,regions,containing,cbfses] + Create a new-style partitioned firmware image + locate [-r image,regions] -f FILE -n NAME [-P page-size] \ + [-a align] [-T] + Find a place for a file of that size + layout [-w] + List mutable (or, with -w, readable) image regions + print [-r image,regions] + Show the contents of the ROM + extract [-r image,regions] [-m ARCH] -n NAME -f FILE [-U] + Extracts a file from ROM + write [-F] -r image,regions -f file [-u | -d] [-i int] + Write file into same-size [or larger] raw region + read [-r fmap-region] -f file + Extract raw region contents into binary file + truncate [-r fmap-region] + Truncate CBFS and print new size on stdout + expand [-r fmap-region] + Expand CBFS to span entire region +OFFSETs: + Numbers accompanying -b, -H, and -o switches* may be provided + in two possible formats: if their value is greater than + 0x80000000, they are interpreted as a top-aligned x86 memory + address; otherwise, they are treated as an offset into flash. +ARCHes: + arm64, arm, mips, ppc64, power8, riscv, x86, unknown +TYPEs: + bootblock, cbfs header, stage, simple elf, fit, optionrom, bootsplash, raw, + vsa, mbi, microcode, fsp, mrc, cmos_default, cmos_layout, spd, + mrc_cache, mma, efi, struct, deleted, null + +* Note that these actions and switches are only valid when + working with legacy images whose structure is described + primarily by a CBFS master header. New-style images, in + contrast, exclusively make use of an FMAP to describe their + layout: this must minimally contain an 'FMAP' section + specifying the location of this FMAP itself and a 'COREBOOT' + section describing the primary CBFS. It should also be noted + that, when working with such images, the -F and -r switches + default to 'COREBOOT' for convenience, and both the -b switch to + CBFS operations and the output of the locate action become + relative to the selected CBFS region's lowest address. + The one exception to this rule is the top-aligned address, + which is always relative to the end of the entire image + rather than relative to the local region; this is true for + for both input (sufficiently large) and output (-T) data. + + +Since binman has a native implementation of CBFS (see cbfs_util.py), we don't +actually need this tool, except for sanity checks in the tests. +""" + +from binman import bintool + +class Bintoolcbfstool(bintool.Bintool): + """Coreboot filesystem (CBFS) tool + + This bintool supports creating new CBFS images and adding files to an + existing image, i.e. the features needed by binman. + + It also supports fetching a binary cbfstool, since building it from source + is fairly slow. + + Documentation about CBFS is at https://www.coreboot.org/CBFS + """ + def __init__(self, name): + super().__init__(name, 'Manipulate CBFS files') + + def create_new(self, cbfs_fname, size, arch='x86'): + """Create a new CBFS + + Args: + cbfs_fname (str): Filename of CBFS to create + size (int): Size of CBFS in bytes + arch (str): Architecture for which this CBFS is intended + + Returns: + str: Tool output + """ + args = [cbfs_fname, 'create', '-s', f'{size:#x}', '-m', arch] + return self.run_cmd(*args) + + # pylint: disable=R0913 + def add_raw(self, cbfs_fname, name, fname, compress=None, base=None): + """Add a raw file to the CBFS + + Args: + cbfs_fname (str): Filename of CBFS to create + name (str): Name to use inside the CBFS + fname (str): Filename of file to add + compress (str): Compression to use (cbfs_util.COMPRESS_NAMES) or + None for None + base (int): Address to place the file, or None for anywhere + + Returns: + str: Tool output + """ + args = [cbfs_fname, + 'add', + '-n', name, + '-t', 'raw', + '-f', fname, + '-c', compress or 'none'] + if base: + args += ['-b', f'{base:#x}'] + return self.run_cmd(*args) + + def add_stage(self, cbfs_fname, name, fname): + """Add a stage file to the CBFS + + Args: + cbfs_fname (str): Filename of CBFS to create + name (str): Name to use inside the CBFS + fname (str): Filename of file to add + + Returns: + str: Tool output + """ + args = [cbfs_fname, + 'add-stage', + '-n', name, + '-f', fname + ] + return self.run_cmd(*args) + + def fail(self): + """Run cbfstool with invalid arguments to check it reports failure + + This is really just a sanity check + + Returns: + CommandResult: Result from running the bad command + """ + args = ['missing-file', 'bad-command'] + return self.run_cmd_result(*args) + + def fetch(self, method): + """Fetch handler for cbfstool + + This installs cbfstool by downloading from Google Drive. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + fname, tmpdir = self.fetch_from_drive( + '1IOnE0Qvy97d-0WOCwF64xBGpKSY2sMtJ') + return fname, tmpdir -- cgit v1.1 From bf87b203a3d688b9a6165e9a2137f08da510cf30 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:53 -0700 Subject: binman: Add a bintool implementation for fiptool Add a Bintool for this, which is used to run FIP tests. It supports the features needed by the tests as well as building a binary from the git tree. Signed-off-by: Simon Glass --- tools/binman/btool/fiptool.py | 123 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tools/binman/btool/fiptool.py diff --git a/tools/binman/btool/fiptool.py b/tools/binman/btool/fiptool.py new file mode 100644 index 0000000..c6d71ce --- /dev/null +++ b/tools/binman/btool/fiptool.py @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for fiptool + +fiptool provides a way to package firmware in an ARM Trusted Firmware Firmware +Image Package (ATF FIP) format. It is used with Trusted Firmware A, for example. + +Documentation is at: +https://trustedfirmware-a.readthedocs.io/en/latest/getting_started/tools-build.html?highlight=fiptool#building-and-using-the-fip-tool + +Source code is at: +https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git + +Here is the help: + +usage: fiptool [--verbose] [] +Global options supported: + --verbose Enable verbose output for all commands. + +Commands supported: + info List images contained in FIP. + create Create a new FIP with the given images. + update Update an existing FIP with the given images. + unpack Unpack images from FIP. + remove Remove images from FIP. + version Show fiptool version. + help Show help for given command. + +""" + +from binman import bintool + +class Bintoolfiptool(bintool.Bintool): + """Image generation for ARM Trusted Firmware + + This bintool supports running `fiptool` with some basic parameters as + neeed by binman. + + It also supports build fiptool from source. + + fiptool provides a way to package firmware in an ARM Trusted Firmware + Firmware Image Package (ATF FIP) format. It is used with Trusted Firmware A, + for example. + + See `TF-A FIP tool documentation`_ for more information. + + .. _`TF-A FIP tool documentation`: + https://trustedfirmware-a.readthedocs.io/en/latest/getting_started/tools-build.html?highlight=fiptool#building-and-using-the-fip-tool + """ + def __init__(self, name): + super().__init__(name, 'Manipulate ATF FIP files') + + def info(self, fname): + """Get info on a FIP image + + Args: + fname (str): Filename to check + + Returns: + str: Tool output + """ + args = ['info', fname] + return self.run_cmd(*args) + + # pylint: disable=R0913 + def create_new(self, fname, align, plat_toc_flags, fwu, tb_fw, blob_uuid, + blob_file): + """Create a new FIP + + Args: + fname (str): Filename to write to + align (int): Alignment to use for entries + plat_toc_flags (int): Flags to use for the TOC header + fwu (str): Filename for the fwu entry + tb_fw (str): Filename for the tb_fw entry + blob_uuid (str): UUID for the blob entry + blob_file (str): Filename for the blob entry + + Returns: + str: Tool output + """ + args = [ + 'create', + '--align', f'{align:x}', + '--plat-toc-flags', f'{plat_toc_flags:#x}', + '--fwu', fwu, + '--tb-fw', tb_fw, + '--blob', f'uuid={blob_uuid},file={blob_file}', + fname] + return self.run_cmd(*args) + + def create_bad(self): + """Run fiptool with invalid arguments""" + args = ['create', '--fred'] + return self.run_cmd_result(*args) + + def fetch(self, method): + """Fetch handler for fiptool + + This builds the tool from source + + Returns: + tuple: + str: Filename of fetched file to copy to a suitable directory + str: Name of temp directory to remove, or None + """ + if method != bintool.FETCH_BUILD: + return None + result = self.build_from_git( + 'https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git', + 'fiptool', + 'tools/fiptool/fiptool') + return result + + def version(self): + """Version handler for fiptool + + Returns: + str: Version number of fiptool + """ + out = self.run_cmd('version').strip() + return out or super().version() -- cgit v1.1 From 9d3a7a2e0b3f6eafaffd95499e33849ceae161db Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:54 -0700 Subject: binman: Add a bintool implementation for futility Add a Bintool for this, which is used to sign Chrome OS images and build the Google Binary Block (GBB). It supports the features needed by binman as well as fetching a binary from Google Drive. Building it from source is possible but is left for another time, as it requires at least one other library. Signed-off-by: Simon Glass --- tools/binman/btool/futility.py | 178 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 tools/binman/btool/futility.py diff --git a/tools/binman/btool/futility.py b/tools/binman/btool/futility.py new file mode 100644 index 0000000..614daaa --- /dev/null +++ b/tools/binman/btool/futility.py @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for futility + +futility (flash utility) is a tool for working with Chromium OS flash images. +This implements just the features used by Binman. + +Documentation is at: + https://chromium.googlesource.com/chromiumos/platform/vboot/+/refs/heads/main/_vboot_reference/README + +Source code: + https://chromium.googlesource.com/chromiumos/platform/vboot/+/refs/heads/master/_vboot_reference/futility + +Here is the help: +Usage: futility [options] COMMAND [args...] + +This is the unified firmware utility, which will eventually replace +most of the distinct verified boot tools formerly produced by the +vboot_reference package. + +When symlinked under the name of one of those previous tools, it should +fully implement the original behavior. It can also be invoked directly +as futility, followed by the original name as the first argument. + +Global options: + + --vb1 Use only vboot v1.0 binary formats + --vb21 Use only vboot v2.1 binary formats + --debug Be noisy about what's going on + +The following commands are built-in: + + bdb Common boot flow utility + create Create a keypair from an RSA .pem file + dump_fmap Display FMAP contents from a firmware image + dump_kernel_config Prints the kernel command line + gbb Manipulate the Google Binary Block (GBB) + gbb_utility Legacy name for `gbb` command + help Show a bit of help (you're looking at it) + load_fmap Replace the contents of specified FMAP areas + pcr Simulate a TPM PCR extension operation + show Display the content of various binary components + sign Sign / resign various binary components + update Update system firmware + validate_rec_mrc Validates content of Recovery MRC cache + vbutil_firmware Verified boot firmware utility + vbutil_kernel Creates, signs, and verifies the kernel partition + vbutil_key Wraps RSA keys with vboot headers + vbutil_keyblock Creates, signs, and verifies a keyblock + verify Verify the signatures of various binary components + version Show the futility source revision and build date +""" + +from binman import bintool + +class Bintoolfutility(bintool.Bintool): + """Handles the 'futility' tool + + futility (flash utility) is a tool for working with Chromium OS flash + images. This Bintool implements just the features used by Binman, related to + GBB creation and firmware signing. + + A binary version of the tool can be fetched. + + See `Chromium OS vboot documentation`_ for more information. + + .. _`Chromium OS vboot documentation`: + https://chromium.googlesource.com/chromiumos/platform/vboot/+/refs/heads/main/_vboot_reference/README + """ + def __init__(self, name): + super().__init__(name, 'Chromium OS firmware utility') + + def gbb_create(self, fname, sizes): + """Create a new Google Binary Block + + Args: + fname (str): Filename to write to + sizes (list of int): Sizes of each regions: + hwid_size, rootkey_size, bmpfv_size, recoverykey_size + + Returns: + str: Tool output + """ + args = [ + 'gbb_utility', + '-c', + ','.join(['%#x' % size for size in sizes]), + fname + ] + return self.run_cmd(*args) + + # pylint: disable=R0913 + def gbb_set(self, fname, hwid, rootkey, recoverykey, flags, bmpfv): + """Set the parameters in a Google Binary Block + + Args: + fname (str): Filename to update + hwid (str): Hardware ID to use + rootkey (str): Filename of root key, e.g. 'root_key.vbpubk' + recoverykey (str): Filename of recovery key, + e.g. 'recovery_key.vbpubk' + flags (int): GBB flags to use + bmpfv (str): Filename of firmware bitmaps (bmpblk file) + + Returns: + str: Tool output + """ + args = ['gbb_utility' + '-s', + f'--hwid={hwid}', + f'--rootkey={rootkey}', + f'--recoverykey={recoverykey}', + f'--flags={flags}', + f'--bmpfv={bmpfv}', + fname + ] + return self.run_cmd(*args) + + def sign_firmware(self, vblock, keyblock, signprivate, version, firmware, + kernelkey, flags): + """Sign firmware to create a vblock file + + Args: + vblock (str): Filename to write the vblock too + keyblock (str): Filename of keyblock file + signprivate (str): Filename of private key + version (int): Version number + firmware (str): Filename of firmware binary to sign + kernelkey (str): Filename of kernel key + flags (int): Preamble flags + + Returns: + str: Tool output + """ + args = [ + 'vbutil_firmware', + '--vblock', vblock, + '--keyblock', keyblock, + '--signprivate', signprivate, + '--version', version, + '--fw', firmware, + '--kernelkey', kernelkey, + '--flags', flags + ] + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for futility + + This installs futility using a binary download. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched, None if a method other than FETCH_BIN + was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + fname, tmpdir = self.fetch_from_drive( + '1hdsInzsE4aJbmBeJ663kYgjOQyW1I-E0') + return fname, tmpdir + + def version(self): + """Version handler for futility + + Returns: + str: Version string for futility + """ + out = self.run_cmd('version').strip() + if not out: + return super().version() + return out -- cgit v1.1 From 6f7eb0c03766f236c519e541ca8a1e10dce395cc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:55 -0700 Subject: binman: Add a bintool implementation for ifwitool Add a Bintool for this, which is used to build Intel IFWI images. It supports the features needed by the tests as well as downloading a binary from Google Drive. Although this is built in the U-Boot tree, it is not currently included with u-boot-tools, so it may be useful to install a binary on the system. Signed-off-by: Simon Glass --- tools/binman/btool/ifwitool.py | 166 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 tools/binman/btool/ifwitool.py diff --git a/tools/binman/btool/ifwitool.py b/tools/binman/btool/ifwitool.py new file mode 100644 index 0000000..96778fc --- /dev/null +++ b/tools/binman/btool/ifwitool.py @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for ifwitool + +ifwitool provides a way to package firmware in an Intel Firmware Image (IFWI) +file on some Intel SoCs, e.g. Apolo Lake. + +Documentation is not really available so far as I can tell + +Source code is at tools/ifwitool.c which is a cleaned-up version of +https://github.com/coreboot/coreboot/blob/master/util/cbfstool/ifwitool.c + +Here is the help: + +ifwitool: Utility for IFWI manipulation + +USAGE: + /tmp/b/sandbox/tools/ifwitool [-h] + /tmp/b/sandbox/tools/ifwitool FILE COMMAND [PARAMETERS] + +COMMANDs: + add -f FILE -n NAME [-d -e ENTRY] + create -f FILE + delete -n NAME + extract -f FILE -n NAME [-d -e ENTRY] + print [-d] + replace -f FILE -n NAME [-d -e ENTRY] +OPTIONs: + -f FILE : File to read/write/create/extract + -d : Perform directory operation + -e ENTRY: Name of directory entry to operate on + -v : Verbose level + -h : Help message + -n NAME : Name of sub-partition to operate on + +NAME should be one of: +SMIP(SMIP) +RBEP(CSE_RBE) +FTPR(CSE_BUP) +UCOD(Microcode) +IBBP(Bootblock) +S_BPDT(S-BPDT) +OBBP(OEM boot block) +NFTP(CSE_MAIN) +ISHP(ISH) +DLMP(CSE_IDLM) +IFP_OVERRIDE(IFP_OVERRIDE) +DEBUG_TOKENS(Debug Tokens) +UFS_PHY(UFS Phy) +UFS_GPP(UFS GPP) +PMCP(PMC firmware) +IUNP(IUNIT) +NVM_CONFIG(NVM Config) +UEP(UEP) +UFS_RATE_B(UFS Rate B Config) +""" + +from binman import bintool + +class Bintoolifwitool(bintool.Bintool): + """Handles the 'ifwitool' tool + + This bintool supports running `ifwitool` with some basic parameters as + neeed by binman. It includes creating a file from a FIT as well as adding, + replacing, deleting and extracting subparts. + + The tool is built as part of U-Boot, but a binary version can be fetched if + required. + + ifwitool provides a way to package firmware in an Intel Firmware Image + (IFWI) file on some Intel SoCs, e.g. Apolo Lake. + """ + def __init__(self, name): + super().__init__(name, 'Manipulate Intel IFWI files') + + def create_ifwi(self, intel_fit, ifwi_file): + """Create a new IFWI file, using an existing Intel FIT binary + + Args: + intel_fit (str): Filename of exist Intel FIT file + ifwi_file (str): Output filename to write the new IFWI too + + Returns: + str: Tool output + """ + args = [intel_fit, 'create', '-f', ifwi_file] + return self.run_cmd(*args) + + def delete_subpart(self, ifwi_file, subpart): + """Delete a subpart within the IFWI file + + Args: + ifwi_file (str): IFWI filename to update + subpart (str): Name of subpart to delete, e.g. 'OBBP' + + Returns: + str: Tool output + """ + args = [ifwi_file, 'delete', '-n', subpart] + return self.run_cmd(*args) + + # pylint: disable=R0913 + def add_subpart(self, ifwi_file, subpart, entry_name, infile, + replace=False): + """Add or replace a subpart within the IFWI file + + Args: + ifwi_file (str): IFWI filename to update + subpart (str): Name of subpart to add/replace + entry_nme (str): Name of entry to add/replace + replace (bool): True to replace the existing entry, False to add a + new one + + Returns: + str: Tool output + """ + args = [ + ifwi_file, + 'replace' if replace else 'add', + '-n', subpart, + '-d', '-e', entry_name, + '-f', infile, + ] + return self.run_cmd(*args) + + def extract(self, ifwi_file, subpart, entry_name, outfile): + """Extract a subpart from the IFWI file + + Args: + ifwi_file (str): IFWI filename to extract from + subpart (str): Name of subpart to extract + entry_nme (str): Name of entry to extract + + Returns: + str: Tool output + """ + args = [ + ifwi_file, + 'extract', + '-n', subpart, + '-d', '-e', entry_name, + '-f', outfile, + ] + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for ifwitool + + This installs ifwitool using a binary download. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched, None if a method other than FETCH_BIN + was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + fname, tmpdir = self.fetch_from_drive( + '18JDghOxlt2Hcc5jv51O1t6uNVHQ0XKJS') + return fname, tmpdir -- cgit v1.1 From e1b7e4ddb6b1a1d0b416facbd0f576dde9c404e6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:56 -0700 Subject: binman: Add a bintool implementation for mkimage Add a Bintool for this, which is used to build images for use by U-Boot. It supports the features needed by binman as well as installing via the u-boot-tools packages. Although this is built in the U-Boot tree, it is still useful to install a binary on the system. Signed-off-by: Simon Glass --- tools/binman/btool/mkimage.py | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 tools/binman/btool/mkimage.py diff --git a/tools/binman/btool/mkimage.py b/tools/binman/btool/mkimage.py new file mode 100644 index 0000000..c85bfe0 --- /dev/null +++ b/tools/binman/btool/mkimage.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for mkimage""" + +import re + +from binman import bintool + +class Bintoolmkimage(bintool.Bintool): + """Image generation for U-Boot + + This bintool supports running `mkimage` with some basic parameters as + neeed by binman. + + Normally binman uses the mkimage built by U-Boot. But when run outside the + U-Boot build system, binman can use the version installed in your system. + Support is provided for fetching this on Debian-like systems, using apt. + """ + def __init__(self, name): + super().__init__(name, 'Generate image for U-Boot') + + # pylint: disable=R0913 + def run(self, reset_timestamp=False, output_fname=None, external=False, + pad=None, version=False): + """Run mkimage + + Args: + reset_timestamp: True to update the timestamp in the FIT + output_fname: Output filename to write to + external: True to create an 'external' FIT, where the binaries are + located outside the main data structure + pad: Bytes to use for padding the FIT devicetree output. This allows + other things to be easily added later, if required, such as + signatures + version: True to get the mkimage version + """ + args = [] + if external: + args.append('-E') + if pad: + args += ['-p', f'{pad:x}'] + if reset_timestamp: + args.append('-t') + if output_fname: + args += ['-F', output_fname] + if version: + args.append('-V') + return self.run_cmd(*args) + + def fetch(self, method): + """Fetch handler for mkimage + + This installs mkimage using the apt utility. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + return self.apt_install('u-boot-tools') + + def version(self): + """Version handler for mkimage + + Returns: + str: Version string for mkimage + """ + out = self.run(version=True).strip() + if not out: + return super().version() + m_version = re.match(r'mkimage version (.*)', out) + return m_version.group(1) if m_version else out -- cgit v1.1 From 56ee85eef11e6162e2626ba644c6de459137dd23 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:57 -0700 Subject: binman: Enable bintool tests including cmdline processing The tests rely on having at least 5 bintool implementions. Now that we have this, enable them. Add tests for the binman 'tool' subcommand. Signed-off-by: Simon Glass --- tools/binman/ftest.py | 32 ++++++++++++++++++++++++++++++++ tools/binman/main.py | 7 ++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 179326c..92bcb74 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -17,6 +17,8 @@ import struct import sys import tempfile import unittest +import unittest.mock +import urllib.error from binman import bintool from binman import cbfs_util @@ -4991,6 +4993,36 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err = stderr.getvalue() self.assertRegex(err, "Image 'main-section'.*faked.*: blob-ext-list") + def testListBintools(self): + args = ['tool', '--list'] + with test_util.capture_sys_output() as (stdout, _): + self._DoBinman(*args) + out = stdout.getvalue().splitlines() + self.assertTrue(len(out) >= 2) + + def testFetchBintools(self): + def fail_download(url): + """Take the tools.Download() function by raising an exception""" + raise urllib.error.URLError('my error') + + args = ['tool'] + with self.assertRaises(ValueError) as e: + self._DoBinman(*args) + self.assertIn("Invalid arguments to 'tool' subcommand", + str(e.exception)) + + args = ['tool', '--fetch'] + with self.assertRaises(ValueError) as e: + self._DoBinman(*args) + self.assertIn('Please specify bintools to fetch', str(e.exception)) + + args = ['tool', '--fetch', '_testing'] + with unittest.mock.patch.object(tools, 'Download', + side_effect=fail_download): + with test_util.capture_sys_output() as (stdout, _): + self._DoBinman(*args) + self.assertIn('failed to fetch with all methods', stdout.getvalue()) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/main.py b/tools/binman/main.py index 35944f3..dcf2029 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -68,6 +68,7 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): name to execute (as in 'binman test testSections', for example) toolpath: List of paths to use for tools """ + from binman import bintool_test from binman import cbfs_util_test from binman import elf_test from binman import entry_test @@ -85,9 +86,9 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): test_util.RunTestSuites( result, debug, verbosity, test_preserve_dirs, processes, test_name, toolpath, - [entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, - elf_test.TestElf, image_test.TestImage, cbfs_util_test.TestCbfs, - fip_util_test.TestFip]) + [bintool_test.TestBintool, entry_test.TestEntry, ftest.TestFunctional, + fdt_test.TestFdt, elf_test.TestElf, image_test.TestImage, + cbfs_util_test.TestCbfs, fip_util_test.TestFip]) return test_util.ReportResult('binman', test_name, result) -- cgit v1.1 From 5417da574e058efc66b471a3286cef5ac3bba1e1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:58 -0700 Subject: binman: Convert to using the CBFS bintool Update the CBFS tests to use this bintool, instead of running cbfstool directly. This simplifies the overall code and provides more consistency, as well as supporting missing bintools. Signed-off-by: Simon Glass --- tools/binman/cbfs_util.py | 24 -------------------- tools/binman/cbfs_util_test.py | 51 ++++++++++++++++-------------------------- 2 files changed, 19 insertions(+), 56 deletions(-) diff --git a/tools/binman/cbfs_util.py b/tools/binman/cbfs_util.py index 3997337..00664bc 100644 --- a/tools/binman/cbfs_util.py +++ b/tools/binman/cbfs_util.py @@ -861,27 +861,3 @@ class CbfsReader(object): val += data[:pos] break return val.decode('utf-8') - - -def cbfstool(fname, *cbfs_args, **kwargs): - """Run cbfstool with provided arguments - - If the tool fails then this function raises an exception and prints out the - output and stderr. - - Args: - fname: Filename of CBFS - *cbfs_args: List of arguments to pass to cbfstool - - Returns: - CommandResult object containing the results - """ - args = ['cbfstool', fname] + list(cbfs_args) - if kwargs.get('base') is not None: - args += ['-b', '%#x' % kwargs['base']] - result = command.RunPipe([args], capture=not VERBOSE, - capture_stderr=not VERBOSE, raise_on_error=False) - if result.return_code: - print(result.stderr, file=sys.stderr) - raise Exception("Failed to run (error %d): '%s'" % - (result.return_code, ' '.join(args))) diff --git a/tools/binman/cbfs_util_test.py b/tools/binman/cbfs_util_test.py index 2c62c8a..70b4279 100755 --- a/tools/binman/cbfs_util_test.py +++ b/tools/binman/cbfs_util_test.py @@ -16,6 +16,7 @@ import struct import tempfile import unittest +from binman import bintool from binman import cbfs_util from binman.cbfs_util import CbfsWriter from binman import elf @@ -45,11 +46,8 @@ class TestCbfs(unittest.TestCase): # compressing files tools.PrepareOutputDir(None) - cls.have_cbfstool = True - try: - tools.Run('which', 'cbfstool') - except: - cls.have_cbfstool = False + cls.cbfstool = bintool.Bintool.create('cbfstool') + cls.have_cbfstool = cls.cbfstool.is_present() cls.have_lz4 = True try: @@ -177,19 +175,19 @@ class TestCbfs(unittest.TestCase): if not self.have_cbfstool or not self.have_lz4: return None cbfs_fname = os.path.join(self._indir, 'test.cbfs') - cbfs_util.cbfstool(cbfs_fname, 'create', '-m', arch, '-s', '%#x' % size) + self.cbfstool.create_new(cbfs_fname, size, arch) if base: base = [(1 << 32) - size + b for b in base] - cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot', '-t', 'raw', - '-c', compress and compress[0] or 'none', - '-f', tools.GetInputFilename( - compress and 'compress' or 'u-boot.bin'), - base=base[0] if base else None) - cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot-dtb', '-t', 'raw', - '-c', compress and compress[1] or 'none', - '-f', tools.GetInputFilename( - compress and 'compress' or 'u-boot.dtb'), - base=base[1] if base else None) + self.cbfstool.add_raw( + cbfs_fname, 'u-boot', + tools.GetInputFilename(compress and 'compress' or 'u-boot.bin'), + compress[0] if compress else None, + base[0] if base else None) + self.cbfstool.add_raw( + cbfs_fname, 'u-boot-dtb', + tools.GetInputFilename(compress and 'compress' or 'u-boot.dtb'), + compress[1] if compress else None, + base[1] if base else None) return cbfs_fname def _compare_expected_cbfs(self, data, cbfstool_fname): @@ -223,18 +221,9 @@ class TestCbfs(unittest.TestCase): """Test failure to run cbfstool""" if not self.have_cbfstool: self.skipTest('No cbfstool available') - try: - # In verbose mode this test fails since stderr is not captured. Fix - # this by turning off verbosity. - old_verbose = cbfs_util.VERBOSE - cbfs_util.VERBOSE = False - with test_util.capture_sys_output() as (_stdout, stderr): - with self.assertRaises(Exception) as e: - cbfs_util.cbfstool('missing-file', 'bad-command') - finally: - cbfs_util.VERBOSE = old_verbose - self.assertIn('Unknown command', stderr.getvalue()) - self.assertIn('Failed to run', str(e.exception)) + with self.assertRaises(ValueError) as exc: + out = self.cbfstool.fail() + self.assertIn('cbfstool missing-file bad-command', str(exc.exception)) def test_cbfs_raw(self): """Test base handling of a Coreboot Filesystem (CBFS)""" @@ -515,10 +504,8 @@ class TestCbfs(unittest.TestCase): # Compare against what cbfstool creates if self.have_cbfstool: cbfs_fname = os.path.join(self._indir, 'test.cbfs') - cbfs_util.cbfstool(cbfs_fname, 'create', '-m', 'x86', '-s', - '%#x' % size) - cbfs_util.cbfstool(cbfs_fname, 'add-stage', '-n', 'u-boot', - '-f', elf_fname) + self.cbfstool.create_new(cbfs_fname, size) + self.cbfstool.add_stage(cbfs_fname, 'u-boot', elf_fname) self._compare_expected_cbfs(data, cbfs_fname) def test_cbfs_raw_compress(self): -- cgit v1.1 From 388f04fb67c8b8c7fbc7e954ab7d96a1f83eea56 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:13:59 -0700 Subject: binman: Convert to using the FIP bintool Update the FIP tests to use this bintool, instead of running fiptool directly. This simplifies the code and provides more consistency as well as supporting missing bintools. Signed-off-by: Simon Glass --- tools/binman/fip_util.py | 26 -------------------------- tools/binman/fip_util_test.py | 23 ++++++++--------------- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/tools/binman/fip_util.py b/tools/binman/fip_util.py index 5f7dbc0..868d0b6 100755 --- a/tools/binman/fip_util.py +++ b/tools/binman/fip_util.py @@ -623,31 +623,5 @@ directory''') return 0 -def fiptool(fname, *fip_args): - """Run fiptool with provided arguments - - If the tool fails then this function raises an exception and prints out the - output and stderr. - - Args: - fname (str): Filename of FIP - *fip_args: List of arguments to pass to fiptool - - Returns: - CommandResult: object containing the results - - Raises: - ValueError: the tool failed to run - """ - args = ['fiptool', fname] + list(fip_args) - result = command.RunPipe([args], capture=not VERBOSE, - capture_stderr=not VERBOSE, raise_on_error=False) - if result.return_code: - print(result.stderr, file=sys.stderr) - raise ValueError("Failed to run (error %d): '%s'" % - (result.return_code, ' '.join(args))) - return result - - if __name__ == "__main__": sys.exit(main(sys.argv[1:], OUR_FILE)) # pragma: no cover diff --git a/tools/binman/fip_util_test.py b/tools/binman/fip_util_test.py index 06827f5..4d2093b 100755 --- a/tools/binman/fip_util_test.py +++ b/tools/binman/fip_util_test.py @@ -22,13 +22,11 @@ sys.path.insert(2, os.path.join(OUR_PATH, '..')) # pylint: disable=C0413 from patman import test_util from patman import tools +from binman import bintool from binman import fip_util -HAVE_FIPTOOL = True -try: - tools.Run('which', 'fiptool') -except ValueError: - HAVE_FIPTOOL = False +FIPTOOL = bintool.Bintool.create('fiptool') +HAVE_FIPTOOL = FIPTOOL.is_present() # pylint: disable=R0902,R0904 class TestFip(unittest.TestCase): @@ -286,13 +284,13 @@ blah blah''', binary=False) data = fip.get_data() fname = tools.GetOutputFilename('data.fip') tools.WriteFile(fname, data) - result = fip_util.fiptool('info', fname) + result = FIPTOOL.info(fname) self.assertEqual( '''Firmware Updater NS_BL2U: offset=0xB0, size=0x7, cmdline="--fwu" Trusted Boot Firmware BL2: offset=0xC0, size=0xE, cmdline="--tb-fw" 00010203-0405-0607-0809-0A0B0C0D0E0F: offset=0xD0, size=0xE, cmdline="--blob" ''', - result.stdout) + result) fwu_data = b'my data' tb_fw_data = b'some more data' @@ -315,11 +313,7 @@ Trusted Boot Firmware BL2: offset=0xC0, size=0xE, cmdline="--tb-fw" fname = tools.GetOutputFilename('data.fip') uuid = 'e3b78d9e-4a64-11ec-b45c-fba2b9b49788' - fip_util.fiptool('create', '--align', '8', '--plat-toc-flags', '0x123', - '--fwu', fwu, - '--tb-fw', tb_fw, - '--blob', f'uuid={uuid},file={other_fw}', - fname) + FIPTOOL.create_new(fname, 8, 0x123, fwu, tb_fw, uuid, other_fw) return fip_util.FipReader(tools.ReadFile(fname)) @@ -396,9 +390,8 @@ Trusted Boot Firmware BL2: offset=0xC0, size=0xE, cmdline="--tb-fw" """Check some error reporting from fiptool""" with self.assertRaises(Exception) as err: with test_util.capture_sys_output(): - fip_util.fiptool('create', '--fred') - self.assertIn("Failed to run (error 1): 'fiptool create --fred'", - str(err.exception)) + FIPTOOL.create_bad() + self.assertIn("unrecognized option '--fred'", str(err.exception)) if __name__ == '__main__': -- cgit v1.1 From a104bb2b485c5991750d7bf16294707e7e377ed8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:00 -0700 Subject: binman: Convert to using the futility bintool Update the GBB and vblock entry types to use this bintool, instead of running futility directly. This simplifies the code and provides more consistency as well as supporting missing bintools. Signed-off-by: Simon Glass --- tools/binman/etype/gbb.py | 37 ++++++++++++++++++++++--------------- tools/binman/etype/vblock.py | 32 +++++++++++++++++++------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/tools/binman/etype/gbb.py b/tools/binman/etype/gbb.py index 41554eb..ca8af1b 100644 --- a/tools/binman/etype/gbb.py +++ b/tools/binman/etype/gbb.py @@ -77,20 +77,27 @@ class Entry_gbb(Entry): bmpfv_size = gbb_size - 0x2180 if bmpfv_size < 0: self.Raise('GBB is too small (minimum 0x2180 bytes)') - sizes = [0x100, 0x1000, bmpfv_size, 0x1000] - sizes = ['%#x' % size for size in sizes] keydir = tools.GetInputFilename(self.keydir) - gbb_set_command = [ - 'gbb_utility', '-s', - '--hwid=%s' % self.hardware_id, - '--rootkey=%s/root_key.vbpubk' % keydir, - '--recoverykey=%s/recovery_key.vbpubk' % keydir, - '--flags=%d' % self.gbb_flags, - '--bmpfv=%s' % tools.GetInputFilename(self.bmpblk), - fname] - - tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname) - tools.Run('futility', *gbb_set_command) - - self.SetContents(tools.ReadFile(fname)) + + stdout = self.futility.gbb_create( + fname, [0x100, 0x1000, bmpfv_size, 0x1000]) + if stdout is not None: + stdout = self.futility.gbb_set( + fname, + hwid=self.hardware_id, + rootkey='%s/root_key.vbpubk' % keydir, + recoverykey='%s/recovery_key.vbpubk' % keydir, + flags=self.gbb_flags, + bmpfv=tools.GetInputFilename(self.bmpblk)) + + if stdout is not None: + self.SetContents(tools.ReadFile(fname)) + else: + # Bintool is missing; just use the required amount of zero data + self.record_missing_bintool(self.futility) + self.SetContents(tools.GetBytes(0, gbb_size)) + return True + + def AddBintools(self, tools): + self.futility = self.AddBintool(tools, 'futility') diff --git a/tools/binman/etype/vblock.py b/tools/binman/etype/vblock.py index c0a6a28..8bbba27 100644 --- a/tools/binman/etype/vblock.py +++ b/tools/binman/etype/vblock.py @@ -38,6 +38,7 @@ class Entry_vblock(Entry_collection): """ def __init__(self, section, etype, node): super().__init__(section, etype, node) + self.futility = None (self.keydir, self.keyblock, self.signprivate, self.version, self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([ EntryArg('keydir', str), @@ -68,19 +69,21 @@ class Entry_vblock(Entry_collection): input_fname = tools.GetOutputFilename('input.%s' % uniq) tools.WriteFile(input_fname, input_data) prefix = self.keydir + '/' - args = [ - 'vbutil_firmware', - '--vblock', output_fname, - '--keyblock', prefix + self.keyblock, - '--signprivate', prefix + self.signprivate, - '--version', '%d' % self.version, - '--fv', input_fname, - '--kernelkey', prefix + self.kernelkey, - '--flags', '%d' % self.preamble_flags, - ] - #out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label)) - stdout = tools.Run('futility', *args) - return tools.ReadFile(output_fname) + stdout = self.futility.sign_firmware( + vblock=output_fname, + keyblock=prefix + self.keyblock, + signprivate=prefix + self.signprivate, + version=f'{self.version,}', + firmware=input_fname, + kernelkey=prefix + self.kernelkey, + flags=f'{self.preamble_flags}') + if stdout is not None: + data = tools.ReadFile(output_fname) + else: + # Bintool is missing; just use 4KB of zero data + self.record_missing_bintool(self.futility) + data = tools.GetBytes(0, 4096) + return data def ObtainContents(self): data = self.GetVblock(False) @@ -93,3 +96,6 @@ class Entry_vblock(Entry_collection): # The blob may have changed due to WriteSymbols() data = self.GetVblock(True) return self.ProcessContentsUpdate(data) + + def AddBintools(self, tools): + self.futility = self.AddBintool(tools, 'futility') -- cgit v1.1 From 532ae7043005fb05a7bfa708fdf3fef5c637dd38 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:01 -0700 Subject: binman: Convert to using the ifwitool bintool Update the ifwi entry type to use this bintool, instead of running ifwitool directly. This simplifies the code and provides more consistency as well as supporting missing bintools. Signed-off-by: Simon Glass --- tools/binman/etype/intel_ifwi.py | 25 +++++++++++++++++++------ tools/binman/ftest.py | 4 ++-- tools/patman/tools.py | 31 ------------------------------- 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/tools/binman/etype/intel_ifwi.py b/tools/binman/etype/intel_ifwi.py index ecbd78d..ed14046 100644 --- a/tools/binman/etype/intel_ifwi.py +++ b/tools/binman/etype/intel_ifwi.py @@ -59,15 +59,23 @@ class Entry_intel_ifwi(Entry_blob_ext): if self._convert_fit: inname = self._pathname outname = tools.GetOutputFilename('ifwi.bin') - tools.RunIfwiTool(inname, tools.CMD_CREATE, outname) + if self.ifwitool.create_ifwi(inname, outname) is None: + # Bintool is missing; just create a zeroed ifwi.bin + self.record_missing_bintool(self.ifwitool) + self.SetContents(tools.GetBytes(0, 1024)) + self._filename = 'ifwi.bin' self._pathname = outname else: # Provide a different code path here to ensure we have test coverage outname = self._pathname - # Delete OBBP if it is there, then add the required new items. - tools.RunIfwiTool(outname, tools.CMD_DELETE, subpart='OBBP') + # Delete OBBP if it is there, then add the required new items + if self.ifwitool.delete_subpart(outname, 'OBBP') is None: + # Bintool is missing; just use zero data + self.record_missing_bintool(self.ifwitool) + self.SetContents(tools.GetBytes(0, 1024)) + return True for entry in self._ifwi_entries.values(): # First get the input data and put it in a file @@ -76,9 +84,11 @@ class Entry_intel_ifwi(Entry_blob_ext): input_fname = tools.GetOutputFilename('input.%s' % uniq) tools.WriteFile(input_fname, data) - tools.RunIfwiTool(outname, - tools.CMD_REPLACE if entry._ifwi_replace else tools.CMD_ADD, - input_fname, entry._ifwi_subpart, entry._ifwi_entry_name) + # At this point we know that ifwitool is present, so we don't need + # to check for None here + self.ifwitool.add_subpart( + outname, entry._ifwi_subpart, entry._ifwi_entry_name, + input_fname, entry._ifwi_replace) self.ReadBlobContents() return True @@ -132,3 +142,6 @@ class Entry_intel_ifwi(Entry_blob_ext): if not self.missing: for entry in self._ifwi_entries.values(): entry.WriteSymbols(self) + + def AddBintools(self, tools): + self.ifwitool = self.AddBintool(tools, 'ifwitool') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 92bcb74..f543d17 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -2314,8 +2314,8 @@ class TestFunctional(unittest.TestCase): # We expect to find the TPL wil in subpart IBBP entry IBBL image_fname = tools.GetOutputFilename('image.bin') tpl_fname = tools.GetOutputFilename('tpl.out') - tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname, - subpart='IBBP', entry_name='IBBL') + ifwitool = bintool.Bintool.create('ifwitool') + ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname) tpl_data = tools.ReadFile(tpl_fname) self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)]) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index a27db05..072b024 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -596,37 +596,6 @@ def Decompress(indata, algo, with_header=True): raise ValueError("Unknown algorithm '%s'" % algo) return data -CMD_CREATE, CMD_DELETE, CMD_ADD, CMD_REPLACE, CMD_EXTRACT = range(5) - -IFWITOOL_CMDS = { - CMD_CREATE: 'create', - CMD_DELETE: 'delete', - CMD_ADD: 'add', - CMD_REPLACE: 'replace', - CMD_EXTRACT: 'extract', - } - -def RunIfwiTool(ifwi_file, cmd, fname=None, subpart=None, entry_name=None): - """Run ifwitool with the given arguments: - - Args: - ifwi_file: IFWI file to operation on - cmd: Command to execute (CMD_...) - fname: Filename of file to add/replace/extract/create (None for - CMD_DELETE) - subpart: Name of sub-partition to operation on (None for CMD_CREATE) - entry_name: Name of directory entry to operate on, or None if none - """ - args = ['ifwitool', ifwi_file] - args.append(IFWITOOL_CMDS[cmd]) - if fname: - args += ['-f', fname] - if subpart: - args += ['-n', subpart] - if entry_name: - args += ['-d', '-e', entry_name] - Run(*args) - def ToHex(val): """Convert an integer value (or None) to a string -- cgit v1.1 From f75db1e9960bca5b287d3471819e451f03cd4bd7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:02 -0700 Subject: binman: Convert to using the mkimage bintool Update the fit and mkimage entry types to use this bintool, instead of running mkimage directly. This simplifies the code and provides more consistency as well as supporting missing bintools. Signed-off-by: Simon Glass --- tools/binman/etype/fit.py | 20 ++++++++++++++++---- tools/binman/etype/mkimage.py | 13 +++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index b41187d..6e5f020 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -134,6 +134,7 @@ class Entry_fit(Entry): self._fdts = fdts.split() self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', str)])[0] + self.mkimage = None def ReadNode(self): self.ReadEntries() @@ -250,13 +251,21 @@ class Entry_fit(Entry): tools.WriteFile(input_fname, data) tools.WriteFile(output_fname, data) - args = [] + args = {} ext_offset = self._fit_props.get('fit,external-offset') if ext_offset is not None: - args += ['-E', '-p', '%x' % fdt_util.fdt32_to_cpu(ext_offset.value)] - tools.Run('mkimage', '-t', '-F', output_fname, *args) + args = { + 'external': True, + 'pad': fdt_util.fdt32_to_cpu(ext_offset.value) + } + if self.mkimage.run(reset_timestamp=True, output_fname=output_fname, + **args) is not None: + self.SetContents(tools.ReadFile(output_fname)) + else: + # Bintool is missing; just use empty data as the output + self.record_missing_bintool(self.mkimage) + self.SetContents(tools.GetBytes(0, 1024)) - self.SetContents(tools.ReadFile(output_fname)) return True def _BuildInput(self, fdt): @@ -295,3 +304,6 @@ class Entry_fit(Entry): def SetAllowMissing(self, allow_missing): for section in self._fit_sections.values(): section.SetAllowMissing(allow_missing) + + def AddBintools(self, tools): + self.mkimage = self.AddBintool(tools, 'mkimage') diff --git a/tools/binman/etype/mkimage.py b/tools/binman/etype/mkimage.py index 1903987..201ee4b 100644 --- a/tools/binman/etype/mkimage.py +++ b/tools/binman/etype/mkimage.py @@ -51,8 +51,14 @@ class Entry_mkimage(Entry): input_fname = tools.GetOutputFilename('mkimage.%s' % uniq) tools.WriteFile(input_fname, data) output_fname = tools.GetOutputFilename('mkimage-out.%s' % uniq) - tools.Run('mkimage', '-d', input_fname, *self._args, output_fname) - self.SetContents(tools.ReadFile(output_fname)) + if self.mkimage.run_cmd('-d', input_fname, *self._args, + output_fname) is not None: + self.SetContents(tools.ReadFile(output_fname)) + else: + # Bintool is missing; just use the input data as the output + self.record_missing_bintool(self.mkimage) + self.SetContents(data) + return True def ReadEntries(self): @@ -81,3 +87,6 @@ class Entry_mkimage(Entry): """ for entry in self._mkimage_entries.values(): entry.CheckFakedBlobs(faked_blobs_list) + + def AddBintools(self, tools): + self.mkimage = self.AddBintool(tools, 'mkimage') -- cgit v1.1 From ad35ce5466187298bc998e851f355f4bb119428b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:03 -0700 Subject: binman: Move compression into binman The compression functions are not actually used by patman, so we don't need then in the tools module. Also we want to change them to use bintools, which patman will not support. Move these into a new comp_util module, within binman. Signed-off-by: Simon Glass --- tools/binman/cbfs_util.py | 9 +++-- tools/binman/comp_util.py | 88 +++++++++++++++++++++++++++++++++++++++++++ tools/binman/entry.py | 3 +- tools/binman/etype/section.py | 3 +- tools/binman/ftest.py | 9 +++-- tools/patman/tools.py | 79 -------------------------------------- 6 files changed, 102 insertions(+), 89 deletions(-) create mode 100644 tools/binman/comp_util.py diff --git a/tools/binman/cbfs_util.py b/tools/binman/cbfs_util.py index 00664bc..2b4178a 100644 --- a/tools/binman/cbfs_util.py +++ b/tools/binman/cbfs_util.py @@ -20,6 +20,7 @@ import io import struct import sys +from binman import comp_util from binman import elf from patman import command from patman import tools @@ -240,9 +241,9 @@ class CbfsFile(object): """Handle decompressing data if necessary""" indata = self.data if self.compress == COMPRESS_LZ4: - data = tools.Decompress(indata, 'lz4', with_header=False) + data = comp_util.Decompress(indata, 'lz4', with_header=False) elif self.compress == COMPRESS_LZMA: - data = tools.Decompress(indata, 'lzma', with_header=False) + data = comp_util.Decompress(indata, 'lzma', with_header=False) else: data = indata self.memlen = len(data) @@ -361,9 +362,9 @@ class CbfsFile(object): elif self.ftype == TYPE_RAW: orig_data = data if self.compress == COMPRESS_LZ4: - data = tools.Compress(orig_data, 'lz4', with_header=False) + data = comp_util.Compress(orig_data, 'lz4', with_header=False) elif self.compress == COMPRESS_LZMA: - data = tools.Compress(orig_data, 'lzma', with_header=False) + data = comp_util.Compress(orig_data, 'lzma', with_header=False) self.memlen = len(orig_data) self.data_len = len(data) attr = struct.pack(ATTR_COMPRESSION_FORMAT, diff --git a/tools/binman/comp_util.py b/tools/binman/comp_util.py new file mode 100644 index 0000000..541e191 --- /dev/null +++ b/tools/binman/comp_util.py @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Utilities to compress and decompress data""" + +import struct +import tempfile + +from patman import tools + +def Compress(indata, algo, with_header=True): + """Compress some data using a given algorithm + + Note that for lzma this uses an old version of the algorithm, not that + provided by xz. + + This requires 'lz4' and 'lzma_alone' tools. It also requires an output + directory to be previously set up, by calling PrepareOutputDir(). + + Care is taken to use unique temporary files so that this function can be + called from multiple threads. + + Args: + indata: Input data to compress + algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') + + Returns: + Compressed data + """ + if algo == 'none': + return indata + fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo, + dir=tools.GetOutputDir()).name + tools.WriteFile(fname, indata) + if algo == 'lz4': + data = tools.Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname, + binary=True) + # cbfstool uses a very old version of lzma + elif algo == 'lzma': + outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo, + dir=tools.GetOutputDir()).name + tools.Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', + '-d8') + data = tools.ReadFile(outfname) + elif algo == 'gzip': + data = tools.Run('gzip', '-c', fname, binary=True) + else: + raise ValueError("Unknown algorithm '%s'" % algo) + if with_header: + hdr = struct.pack(' Date: Sun, 9 Jan 2022 20:14:04 -0700 Subject: binman: Tidy up pylint warnings in comp_util Tweak some naming and comments to resolve these. Use WriteFile() to write the file. Signed-off-by: Simon Glass --- tools/binman/cbfs_util.py | 8 ++++---- tools/binman/comp_util.py | 19 +++++++++---------- tools/binman/entry.py | 2 +- tools/binman/etype/section.py | 2 +- tools/binman/ftest.py | 8 ++++---- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/tools/binman/cbfs_util.py b/tools/binman/cbfs_util.py index 2b4178a..eea7868 100644 --- a/tools/binman/cbfs_util.py +++ b/tools/binman/cbfs_util.py @@ -241,9 +241,9 @@ class CbfsFile(object): """Handle decompressing data if necessary""" indata = self.data if self.compress == COMPRESS_LZ4: - data = comp_util.Decompress(indata, 'lz4', with_header=False) + data = comp_util.decompress(indata, 'lz4', with_header=False) elif self.compress == COMPRESS_LZMA: - data = comp_util.Decompress(indata, 'lzma', with_header=False) + data = comp_util.decompress(indata, 'lzma', with_header=False) else: data = indata self.memlen = len(data) @@ -362,9 +362,9 @@ class CbfsFile(object): elif self.ftype == TYPE_RAW: orig_data = data if self.compress == COMPRESS_LZ4: - data = comp_util.Compress(orig_data, 'lz4', with_header=False) + data = comp_util.compress(orig_data, 'lz4', with_header=False) elif self.compress == COMPRESS_LZMA: - data = comp_util.Compress(orig_data, 'lzma', with_header=False) + data = comp_util.compress(orig_data, 'lzma', with_header=False) self.memlen = len(orig_data) self.data_len = len(data) attr = struct.pack(ATTR_COMPRESSION_FORMAT, diff --git a/tools/binman/comp_util.py b/tools/binman/comp_util.py index 541e191..7e741cb 100644 --- a/tools/binman/comp_util.py +++ b/tools/binman/comp_util.py @@ -8,7 +8,7 @@ import tempfile from patman import tools -def Compress(indata, algo, with_header=True): +def compress(indata, algo, with_header=True): """Compress some data using a given algorithm Note that for lzma this uses an old version of the algorithm, not that @@ -21,11 +21,11 @@ def Compress(indata, algo, with_header=True): called from multiple threads. Args: - indata: Input data to compress - algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') + indata (bytes): Input data to compress + algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') Returns: - Compressed data + bytes: Compressed data """ if algo == 'none': return indata @@ -51,7 +51,7 @@ def Compress(indata, algo, with_header=True): data = hdr + data return data -def Decompress(indata, algo, with_header=True): +def decompress(indata, algo, with_header=True): """Decompress some data using a given algorithm Note that for lzma this uses an old version of the algorithm, not that @@ -61,11 +61,11 @@ def Decompress(indata, algo, with_header=True): directory to be previously set up, by calling PrepareOutputDir(). Args: - indata: Input data to decompress - algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') + indata (bytes): Input data to decompress + algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') Returns: - Compressed data + (bytes) Compressed data """ if algo == 'none': return indata @@ -73,8 +73,7 @@ def Decompress(indata, algo, with_header=True): data_len = struct.unpack(' Date: Sun, 9 Jan 2022 20:14:05 -0700 Subject: binman: Add a bintool implementation for lz4 Add a Bintool for this, which is used to compress and decompress data. It supports the features needed by binman as well as installing via the lz4 package. Signed-off-by: Simon Glass --- tools/binman/btool/lz4.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tools/binman/btool/lz4.py diff --git a/tools/binman/btool/lz4.py b/tools/binman/btool/lz4.py new file mode 100644 index 0000000..d165f52 --- /dev/null +++ b/tools/binman/btool/lz4.py @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for lz4 + +lz4 allows compression and decompression of files. + +Documentation is available via:: + + man lz4 + +Here is the help: + +*** LZ4 command line interface 64-bits v1.9.3, by Yann Collet *** +Usage : + lz4 [arg] [input] [output] + +input : a filename + with no FILE, or when FILE is - or stdin, read standard input +Arguments : + -1 : Fast compression (default) + -9 : High compression + -d : decompression (default for .lz4 extension) + -z : force compression + -D FILE: use FILE as dictionary + -f : overwrite output without prompting + -k : preserve source files(s) (default) +--rm : remove source file(s) after successful de/compression + -h/-H : display help/long help and exit + +Advanced arguments : + -V : display Version number and exit + -v : verbose mode + -q : suppress warnings; specify twice to suppress errors too + -c : force write to standard output, even if it is the console + -t : test compressed file integrity + -m : multiple input files (implies automatic output filenames) + -r : operate recursively on directories (sets also -m) + -l : compress using Legacy format (Linux kernel compression) + -B# : cut file into blocks of size # bytes [32+] + or predefined block size [4-7] (default: 7) + -BI : Block Independence (default) + -BD : Block dependency (improves compression ratio) + -BX : enable block checksum (default:disabled) +--no-frame-crc : disable stream checksum (default:enabled) +--content-size : compressed frame includes original size (default:not present) +--list FILE : lists information about .lz4 files (useful for files compressed + with --content-size flag) +--[no-]sparse : sparse mode (default:enabled on file, disabled on stdout) +--favor-decSpeed: compressed files decompress faster, but are less compressed +--fast[=#]: switch to ultra fast compression level (default: 1) +--best : same as -12 +Benchmark arguments : + -b# : benchmark file(s), using # compression level (default : 1) + -e# : test all compression levels from -bX to # (default : 1) + -i# : minimum evaluation time in seconds (default : 3s) +""" + +import re +import tempfile + +from binman import bintool +from patman import tools + +# pylint: disable=C0103 +class Bintoollz4(bintool.Bintool): + """Compression/decompression using the LZ4 algorithm + + This bintool supports running `lz4` to compress and decompress data, as + used by binman. + + It is also possible to fetch the tool, which uses `apt` to install it. + + Documentation is available via:: + + man lz4 + """ + def __init__(self, name): + super().__init__(name, 'lz4 compression') + + def compress(self, indata): + """Compress data with lz4 + + Args: + indata (bytes): Data to compress + + Returns: + bytes: Compressed data + """ + with tempfile.NamedTemporaryFile(prefix='comp.tmp', + dir=tools.GetOutputDir()) as tmp: + tools.WriteFile(tmp.name, indata) + args = ['--no-frame-crc', '-B4', '-5', '-c', tmp.name] + return self.run_cmd(*args, binary=True) + + def decompress(self, indata): + """Decompress data with lz4 + + Args: + indata (bytes): Data to decompress + + Returns: + bytes: Decompressed data + """ + with tempfile.NamedTemporaryFile(prefix='decomp.tmp', + dir=tools.GetOutputDir()) as inf: + tools.WriteFile(inf.name, indata) + args = ['-cd', inf.name] + return self.run_cmd(*args, binary=True) + + def fetch(self, method): + """Fetch handler for lz4 + + This installs the lz4 package using the apt utility. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + return self.apt_install('lz4') + + def version(self): + """Version handler + + Returns: + str: Version number of lz4 + """ + out = self.run_cmd('-V').strip() + if not out: + return super().version() + m_version = re.match(r'.* (v[0-9.]*),.*', out) + return m_version.group(1) if m_version else out -- cgit v1.1 From 33ce3515ca47965546ae924f15f821f9bd6406ae Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:06 -0700 Subject: binman: Convert to using the lz4 bintool Update the code to use this bintool, instead of running lz4 directly. This simplifies the code and provides more consistency. Signed-off-by: Simon Glass --- tools/binman/cbfs_util_test.py | 8 ++------ tools/binman/comp_util.py | 10 +++++++--- tools/binman/ftest.py | 8 +------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/tools/binman/cbfs_util_test.py b/tools/binman/cbfs_util_test.py index 70b4279..494f614 100755 --- a/tools/binman/cbfs_util_test.py +++ b/tools/binman/cbfs_util_test.py @@ -19,6 +19,7 @@ import unittest from binman import bintool from binman import cbfs_util from binman.cbfs_util import CbfsWriter +from binman import comp_util from binman import elf from patman import test_util from patman import tools @@ -49,12 +50,7 @@ class TestCbfs(unittest.TestCase): cls.cbfstool = bintool.Bintool.create('cbfstool') cls.have_cbfstool = cls.cbfstool.is_present() - cls.have_lz4 = True - try: - tools.Run('lz4', '--no-frame-crc', '-c', - tools.GetInputFilename('u-boot.bin'), binary=True) - except: - cls.have_lz4 = False + cls.have_lz4 = comp_util.HAVE_LZ4 @classmethod def tearDownClass(cls): diff --git a/tools/binman/comp_util.py b/tools/binman/comp_util.py index 7e741cb..baa2979 100644 --- a/tools/binman/comp_util.py +++ b/tools/binman/comp_util.py @@ -6,8 +6,13 @@ import struct import tempfile +from binman import bintool from patman import tools +LZ4 = bintool.Bintool.create('lz4') +HAVE_LZ4 = LZ4.is_present() + + def compress(indata, algo, with_header=True): """Compress some data using a given algorithm @@ -33,8 +38,7 @@ def compress(indata, algo, with_header=True): dir=tools.GetOutputDir()).name tools.WriteFile(fname, indata) if algo == 'lz4': - data = tools.Run('lz4', '--no-frame-crc', '-B4', '-5', '-c', fname, - binary=True) + data = LZ4.compress(indata) # cbfstool uses a very old version of lzma elif algo == 'lzma': outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo, @@ -75,7 +79,7 @@ def decompress(indata, algo, with_header=True): fname = tools.GetOutputFilename('%s.decomp.tmp' % algo) tools.WriteFile(fname, indata) if algo == 'lz4': - data = tools.Run('lz4', '-dc', fname, binary=True) + data = LZ4.decompress(indata) elif algo == 'lzma': outfname = tools.GetOutputFilename('%s.decomp.otmp' % algo) tools.Run('lzma_alone', 'd', fname, outfname) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 779b899..19461c9 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -197,13 +197,7 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('env.txt', ENV_DATA) - # Travis-CI may have an old lz4 - cls.have_lz4 = True - try: - tools.Run('lz4', '--no-frame-crc', '-c', - os.path.join(cls._indir, 'u-boot.bin'), binary=True) - except: - cls.have_lz4 = False + cls.have_lz4 = comp_util.HAVE_LZ4 @classmethod def tearDownClass(cls): -- cgit v1.1 From 4cd4ee04320b421c3e30e4c8b3dd8cdf0f986ec1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:07 -0700 Subject: binman: Add a bintool implementation for lzma_alone Add a Bintool for this, which is used to compress and decompress data. It supports the features needed by binman as well as installing via the lzma-alone package. Signed-off-by: Simon Glass --- tools/binman/btool/lzma_alone.py | 126 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tools/binman/btool/lzma_alone.py diff --git a/tools/binman/btool/lzma_alone.py b/tools/binman/btool/lzma_alone.py new file mode 100644 index 0000000..d7c62df --- /dev/null +++ b/tools/binman/btool/lzma_alone.py @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2022 Google LLC +# +"""Bintool implementation for lzma_alone + +lzma_alone allows compression and decompression of files, using an older version +of lzma. + +Documentation is available via:: + + man lzma_alone + +Here is the help: + +LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18 + +Usage: LZMA inputFile outputFile [...] + e: encode file + d: decode file + b: Benchmark + + -a{N}: set compression mode - [0, 1], default: 1 (max) + -d{N}: set dictionary size - [12, 30], default: 23 (8MB) + -fb{N}: set number of fast bytes - [5, 273], default: 128 + -mc{N}: set number of cycles for match finder + -lc{N}: set number of literal context bits - [0, 8], default: 3 + -lp{N}: set number of literal pos bits - [0, 4], default: 0 + -pb{N}: set number of pos bits - [0, 4], default: 2 + -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4 + -mt{N}: set number of CPU threads + -eos: write End Of Stream marker + -si: read data from stdin + -so: write data to stdout +""" + +import re +import tempfile + +from binman import bintool +from patman import tools + +# pylint: disable=C0103 +class Bintoollzma_alone(bintool.Bintool): + """Compression/decompression using the LZMA algorithm + + This bintool supports running `lzma_alone` to compress and decompress data, + as used by binman. + + It is also possible to fetch the tool, which uses `apt` to install it. + + Documentation is available via:: + + man lzma_alone + """ + def __init__(self, name): + super().__init__(name, 'lzma_alone compression') + + def compress(self, indata): + """Compress data with lzma_alone + + Args: + indata (bytes): Data to compress + + Returns: + bytes: Compressed data + """ + with tempfile.NamedTemporaryFile(prefix='comp.tmp', + dir=tools.GetOutputDir()) as inf: + tools.WriteFile(inf.name, indata) + with tempfile.NamedTemporaryFile(prefix='compo.otmp', + dir=tools.GetOutputDir()) as outf: + args = ['e', inf.name, outf.name, '-lc1', '-lp0', '-pb0', '-d8'] + self.run_cmd(*args, binary=True) + return tools.ReadFile(outf.name) + + def decompress(self, indata): + """Decompress data with lzma_alone + + Args: + indata (bytes): Data to decompress + + Returns: + bytes: Decompressed data + """ + with tempfile.NamedTemporaryFile(prefix='decomp.tmp', + dir=tools.GetOutputDir()) as inf: + tools.WriteFile(inf.name, indata) + with tempfile.NamedTemporaryFile(prefix='compo.otmp', + dir=tools.GetOutputDir()) as outf: + args = ['d', inf.name, outf.name] + self.run_cmd(*args, binary=True) + return tools.ReadFile(outf.name, binary=True) + + def fetch(self, method): + """Fetch handler for lzma_alone + + This installs the lzma-alone package using the apt utility. + + Args: + method (FETCH_...): Method to use + + Returns: + True if the file was fetched and now installed, None if a method + other than FETCH_BIN was requested + + Raises: + Valuerror: Fetching could not be completed + """ + if method != bintool.FETCH_BIN: + return None + return self.apt_install('lzma-alone') + + def version(self): + """Version handler + + Returns: + str: Version number of lzma_alone + """ + out = self.run_cmd_result('', raise_on_error=False).stderr.strip() + lines = out.splitlines() + if not lines: + return super().version() + out = lines[0] + # e.g. LZMA 9.22 beta : Igor Pavlov : Public domain : 2011-04-18 + m_version = re.match(r'LZMA ([^:]*).*', out) + return m_version.group(1).strip() if m_version else out -- cgit v1.1 From 359e431cbc1d956b65b6b6bc86b7f3f88fc15927 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:08 -0700 Subject: binman: Convert to using the lzma_alone bintool Update the code to use this bintool, instead of running lzma_alone directly. This simplifies the code and provides more consistency. Signed-off-by: Simon Glass --- tools/binman/comp_util.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tools/binman/comp_util.py b/tools/binman/comp_util.py index baa2979..2f78bab 100644 --- a/tools/binman/comp_util.py +++ b/tools/binman/comp_util.py @@ -12,6 +12,9 @@ from patman import tools LZ4 = bintool.Bintool.create('lz4') HAVE_LZ4 = LZ4.is_present() +LZMA_ALONE = bintool.Bintool.create('lzma_alone') +HAVE_LZMA_ALONE = LZMA_ALONE.is_present() + def compress(indata, algo, with_header=True): """Compress some data using a given algorithm @@ -41,11 +44,7 @@ def compress(indata, algo, with_header=True): data = LZ4.compress(indata) # cbfstool uses a very old version of lzma elif algo == 'lzma': - outfname = tempfile.NamedTemporaryFile(prefix='%s.comp.otmp' % algo, - dir=tools.GetOutputDir()).name - tools.Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', - '-d8') - data = tools.ReadFile(outfname) + data = LZMA_ALONE.compress(indata) elif algo == 'gzip': data = tools.Run('gzip', '-c', fname, binary=True) else: @@ -81,9 +80,7 @@ def decompress(indata, algo, with_header=True): if algo == 'lz4': data = LZ4.decompress(indata) elif algo == 'lzma': - outfname = tools.GetOutputFilename('%s.decomp.otmp' % algo) - tools.Run('lzma_alone', 'd', fname, outfname) - data = tools.ReadFile(outfname, binary=True) + data = LZMA_ALONE.decompress(indata) elif algo == 'gzip': data = tools.Run('gzip', '-cd', fname, binary=True) else: -- cgit v1.1 From 4f9ee83ba96c6c4e5e647f26eb4a124544ce61d7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:09 -0700 Subject: binman: Plumb in support for missing bintools Bintools can be missing, in which case binman continues operation but reports an invalid image. Plumb in support for this and add tests for entry types which use bintools. Signed-off-by: Simon Glass --- tools/binman/cmdline.py | 2 ++ tools/binman/control.py | 12 ++++++++- tools/binman/entry.py | 20 +++++++++++++++ tools/binman/etype/section.py | 11 ++++++++ tools/binman/ftest.py | 60 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 92cc14b..5ccb238 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -105,6 +105,8 @@ controlled by a description in the board device tree.''' help='Use fake device tree contents (for testing only)') build_parser.add_argument('--fake-ext-blobs', action='store_true', help='Create fake ext blobs with dummy content (for testing only)') + build_parser.add_argument('--force-missing-bintools', type=str, + help='Comma-separated list of bintools to consider missing (for testing)') build_parser.add_argument('-i', '--image', type=str, action='append', help='Image filename to build (if not specified, build all)') build_parser.add_argument('-I', '--indir', action='append', diff --git a/tools/binman/control.py b/tools/binman/control.py index 5b10f19..bbd7773 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -583,7 +583,14 @@ def ProcessImage(image, update_fdt, write_map, get_contents=True, "Image '%s' has faked external blobs and is non-functional: %s" % (image.name, ' '.join([os.path.basename(e.GetDefaultFilename()) for e in faked_list]))) - return bool(missing_list) or bool(faked_list) + missing_bintool_list = [] + image.check_missing_bintools(missing_bintool_list) + if missing_bintool_list: + tout.Warning( + "Image '%s' has missing bintools and is non-functional: %s" % + (image.name, ' '.join([os.path.basename(bintool.name) + for bintool in missing_bintool_list]))) + return any([missing_list, faked_list, missing_bintool_list]) def Binman(args): @@ -688,6 +695,9 @@ def Binman(args): # Set the first image to timeout, used in testThreadTimeout() images[list(images.keys())[0]].test_section_timeout = True invalid = False + bintool.Bintool.set_missing_list( + args.force_missing_bintools.split(',') if + args.force_missing_bintools else None) for image in images.values(): invalid |= ProcessImage(image, args.update_fdt, args.map, allow_missing=args.allow_missing, diff --git a/tools/binman/entry.py b/tools/binman/entry.py index b4323d5..08770ec 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -77,6 +77,7 @@ class Entry(object): available. This is mainly used for testing. external: True if this entry contains an external binary blob bintools: Bintools used by this entry (only populated for Image) + missing_bintools: List of missing bintools for this entry """ def __init__(self, section, etype, node, name_prefix=''): # Put this here to allow entry-docs and help to work without libfdt @@ -109,6 +110,7 @@ class Entry(object): self.allow_missing = False self.allow_fake = False self.bintools = {} + self.missing_bintools = [] @staticmethod def FindEntryClass(etype, expanded): @@ -1015,6 +1017,24 @@ features to produce new behaviours. """ return self.allow_missing + def record_missing_bintool(self, bintool): + """Record a missing bintool that was needed to produce this entry + + Args: + bintool (Bintool): Bintool that was missing + """ + self.missing_bintools.append(bintool) + + def check_missing_bintools(self, missing_list): + """Check if any entries in this section have missing bintools + + If there are missing bintools, these are added to the list + + Args: + missing_list: List of Bintool objects to be added to + """ + missing_list += self.missing_bintools + def GetHelpTags(self): """Get the tags use for missing-blob help diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index f9d3dc3..bb375e9 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -832,6 +832,17 @@ class Entry_section(Entry): for entry in self._entries.values(): entry.CheckFakedBlobs(faked_blobs_list) + def check_missing_bintools(self, missing_list): + """Check if any entries in this section have missing bintools + + If there are missing bintools, these are added to the list + + Args: + missing_list: List of Bintool objects to be added to + """ + for entry in self._entries.values(): + entry.check_missing_bintools(missing_list) + def _CollectEntries(self, entries, entries_by_name, add_entry): """Collect all the entries in an section diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 19461c9..6e1c498 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -310,7 +310,8 @@ class TestFunctional(unittest.TestCase): entry_args=None, images=None, use_real_dtb=False, use_expanded=False, verbosity=None, allow_missing=False, allow_fake_blobs=False, extra_indirs=None, threads=None, - test_section_timeout=False, update_fdt_in_elf=None): + test_section_timeout=False, update_fdt_in_elf=None, + force_missing_bintools=''): """Run binman with a given test file Args: @@ -339,6 +340,8 @@ class TestFunctional(unittest.TestCase): test_section_timeout: True to force the first time to timeout, as used in testThreadTimeout() update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx + force_missing_tools (str): comma-separated list of bintools to + regard as missing Returns: int return code, 0 on success @@ -373,6 +376,8 @@ class TestFunctional(unittest.TestCase): args.append('-M') if allow_fake_blobs: args.append('--fake-ext-blobs') + if force_missing_bintools: + args += ['--force-missing-bintools', force_missing_bintools] if update_fdt_in_elf: args += ['--update-fdt-in-elf', update_fdt_in_elf] if images: @@ -1713,6 +1718,18 @@ class TestFunctional(unittest.TestCase): self.assertIn("Node '/binman/gbb': GBB must have a fixed size", str(e.exception)) + def testGbbMissing(self): + """Test that binman still produces an image if futility is missing""" + entry_args = { + 'keydir': 'devkeys', + } + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('071_gbb.dts', force_missing_bintools='futility', + entry_args=entry_args) + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: futility") + def _HandleVblockCommand(self, pipe_list): """Fake calls to the futility utility @@ -1798,6 +1815,19 @@ class TestFunctional(unittest.TestCase): expected_hashval = m.digest() self.assertEqual(expected_hashval, hashval) + def testVblockMissing(self): + """Test that binman still produces an image if futility is missing""" + entry_args = { + 'keydir': 'devkeys', + } + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('074_vblock.dts', + force_missing_bintools='futility', + entry_args=entry_args) + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: futility") + def testTpl(self): """Test that an image with TPL and its device tree can be created""" # ELF file with a '__bss_size' symbol @@ -2335,6 +2365,16 @@ class TestFunctional(unittest.TestCase): self.assertIn('Could not complete processing of contents', str(e.exception)) + def testIfwiMissing(self): + """Test that binman still produces an image if ifwitool is missing""" + self._SetupIfwi('fitimage.bin') + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('111_x86_rom_ifwi.dts', + force_missing_bintools='ifwitool') + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: ifwitool") + def testCbfsOffset(self): """Test a CBFS with files at particular offsets @@ -3614,6 +3654,15 @@ class TestFunctional(unittest.TestCase): # Just check that the data appears in the file somewhere self.assertIn(U_BOOT_SPL_DATA, data) + def testMkimageMissing(self): + """Test that binman still produces an image if mkimage is missing""" + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('156_mkimage.dts', + force_missing_bintools='mkimage') + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: mkimage") + def testExtblob(self): """Test an image with an external blob""" data = self._DoReadFile('157_blob_ext.dts') @@ -3734,6 +3783,15 @@ class TestFunctional(unittest.TestCase): self.assertEqual(U_BOOT_DATA + b'aa', data[actual_pos:actual_pos + external_data_size]) + def testFitMissing(self): + """Test that binman still produces a FIT image if mkimage is missing""" + with test_util.capture_sys_output() as (_, stderr): + self._DoTestFile('162_fit_external.dts', + force_missing_bintools='mkimage') + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing bintools.*: mkimage") + def testSectionIgnoreHashSignature(self): """Test that sections ignore hash, signature nodes for its data""" data = self._DoReadFile('165_section_ignore_hash_signature.dts') -- cgit v1.1 From a00d9713e4c01de7e7753c9394a95dca27e47e21 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:10 -0700 Subject: binman: Complete test coverage of comp_util Drop the unused gzip code, update comments and add a test for an invalid algorithm. The temporary file is not needed now, so drop that also. Signed-off-by: Simon Glass --- tools/binman/comp_util.py | 16 ++-------------- tools/binman/ftest.py | 9 +++++++++ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/tools/binman/comp_util.py b/tools/binman/comp_util.py index 2f78bab..dc76ada 100644 --- a/tools/binman/comp_util.py +++ b/tools/binman/comp_util.py @@ -25,28 +25,20 @@ def compress(indata, algo, with_header=True): This requires 'lz4' and 'lzma_alone' tools. It also requires an output directory to be previously set up, by calling PrepareOutputDir(). - Care is taken to use unique temporary files so that this function can be - called from multiple threads. - Args: indata (bytes): Input data to compress - algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') + algo (str): Algorithm to use ('none', 'lz4' or 'lzma') Returns: bytes: Compressed data """ if algo == 'none': return indata - fname = tempfile.NamedTemporaryFile(prefix='%s.comp.tmp' % algo, - dir=tools.GetOutputDir()).name - tools.WriteFile(fname, indata) if algo == 'lz4': data = LZ4.compress(indata) # cbfstool uses a very old version of lzma elif algo == 'lzma': data = LZMA_ALONE.compress(indata) - elif algo == 'gzip': - data = tools.Run('gzip', '-c', fname, binary=True) else: raise ValueError("Unknown algorithm '%s'" % algo) if with_header: @@ -65,7 +57,7 @@ def decompress(indata, algo, with_header=True): Args: indata (bytes): Input data to decompress - algo (str): Algorithm to use ('none', 'gzip', 'lz4' or 'lzma') + algo (str): Algorithm to use ('none', 'lz4' or 'lzma') Returns: (bytes) Compressed data @@ -75,14 +67,10 @@ def decompress(indata, algo, with_header=True): if with_header: data_len = struct.unpack(' Date: Sun, 9 Jan 2022 20:14:11 -0700 Subject: binman: Add a command to generate bintool docs Each bintool has some documentation which can be useful for the user. Add a new command that collects this and writes it into a .rst file. Signed-off-by: Simon Glass --- tools/binman/bintool.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ tools/binman/cmdline.py | 3 +++ tools/binman/control.py | 14 +++++++++++++- tools/binman/ftest.py | 15 +++++++++++++++ tools/binman/main.py | 4 ++++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/tools/binman/bintool.py b/tools/binman/bintool.py index 34102da..e2e5660 100644 --- a/tools/binman/bintool.py +++ b/tools/binman/bintool.py @@ -387,6 +387,51 @@ class Bintool: tools.Run(*args) return True + @staticmethod + def WriteDocs(modules, test_missing=None): + """Write out documentation about the various bintools to stdout + + Args: + modules: List of modules to include + test_missing: Used for testing. This is a module to report + as missing + """ + print('''.. SPDX-License-Identifier: GPL-2.0+ + +Binman bintool Documentation +============================ + +This file describes the bintools (binary tools) supported by binman. Bintools +are binman's name for external executables that it runs to generate or process +binaries. It is fairly easy to create new bintools. Just add a new file to the +'btool' directory. You can use existing bintools as examples. + + +''') + modules = sorted(modules) + missing = [] + for name in modules: + module = Bintool.find_bintool_class(name) + docs = getattr(module, '__doc__') + if test_missing == name: + docs = None + if docs: + lines = docs.splitlines() + first_line = lines[0] + rest = [line[4:] for line in lines[1:]] + hdr = 'Bintool: %s: %s' % (name, first_line) + print(hdr) + print('-' * len(hdr)) + print('\n'.join(rest)) + print() + print() + else: + missing.append(name) + + if missing: + raise ValueError('Documentation is missing for modules: %s' % + ', '.join(missing)) + # pylint: disable=W0613 def fetch(self, method): """Fetch handler for a bintool diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 5ccb238..0626b85 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -130,6 +130,9 @@ controlled by a description in the board device tree.''' help='Update an ELF file with the output dtb: infile,outfile,begin_sym,end_sym') subparsers.add_parser( + 'bintool-docs', help='Write out bintool documentation (see bintool.rst)') + + subparsers.add_parser( 'entry-docs', help='Write out entry documentation (see entries.rst)') list_parser = subparsers.add_parser('ls', help='List files in an image') diff --git a/tools/binman/control.py b/tools/binman/control.py index bbd7773..2daad05 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -140,7 +140,7 @@ def WriteEntryDocs(modules, test_missing=None): Args: modules: List of Module objects to get docs for - test_missing: Used for testing only, to force an entry's documeentation + test_missing: Used for testing only, to force an entry's documentation to show as missing even if it is present. Should be set to None in normal use. """ @@ -148,6 +148,18 @@ def WriteEntryDocs(modules, test_missing=None): Entry.WriteDocs(modules, test_missing) +def write_bintool_docs(modules, test_missing=None): + """Write out documentation for all bintools + + Args: + modules: List of Module objects to get docs for + test_missing: Used for testing only, to force an entry's documentation + to show as missing even if it is present. Should be set to None in + normal use. + """ + bintool.Bintool.WriteDocs(modules, test_missing) + + def ListEntries(image_fname, entry_paths): """List the entries in an image diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index a3454dd..ca200ae 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -5085,6 +5085,21 @@ fdt fdtmap Extract the devicetree blob from the fdtmap comp_util.decompress(b'1234', 'invalid') self.assertIn("Unknown algorithm 'invalid'", str(e.exception)) + def testBintoolDocs(self): + """Test for creation of bintool documentation""" + with test_util.capture_sys_output() as (stdout, stderr): + control.write_bintool_docs(control.bintool.Bintool.get_tool_list()) + self.assertTrue(len(stdout.getvalue()) > 0) + + def testBintoolDocsMissing(self): + """Test handling of missing bintool documentation""" + with self.assertRaises(ValueError) as e: + with test_util.capture_sys_output() as (stdout, stderr): + control.write_bintool_docs( + control.bintool.Bintool.get_tool_list(), 'mkimage') + self.assertIn('Documentation is missing for modules: mkimage', + str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/main.py b/tools/binman/main.py index dcf2029..03462e7 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -35,6 +35,7 @@ sys.pycache_prefix = os.path.relpath(our_path, srctree) # in PYTHONPATH) sys.path.insert(2, our1_path) +from binman import bintool from patman import test_util # Bring in the libfdt module @@ -129,6 +130,9 @@ def RunBinman(args): args.test_preserve_dirs, args.tests, args.toolpath) + elif args.cmd == 'bintool-docs': + control.write_bintool_docs(bintool.Bintool.get_tool_list()) + elif args.cmd == 'entry-docs': control.WriteEntryDocs(control.GetEntryModules()) -- cgit v1.1 From 3e7749eaeac8022329df9dd876b7fc5692d0e2d1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 9 Jan 2022 20:14:12 -0700 Subject: binman: Add documentation for bintools Add this documention to explain how bintools are used and which ones are available. Signed-off-by: Simon Glass --- doc/develop/package/bintools.rst | 1 + tools/binman/binman.rst | 71 ++++++++++++++++++++++++ tools/binman/bintools.rst | 115 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 120000 doc/develop/package/bintools.rst create mode 100644 tools/binman/bintools.rst diff --git a/doc/develop/package/bintools.rst b/doc/develop/package/bintools.rst new file mode 120000 index 0000000..7ef3d75 --- /dev/null +++ b/doc/develop/package/bintools.rst @@ -0,0 +1 @@ +../../../tools/binman/bintools.rst \ No newline at end of file diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 9dbe582..db2234b 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1027,6 +1027,77 @@ by increasing the -v/--verbosity from the default of 1: You can use BINMAN_VERBOSE=5 (for example) when building to select this. +Bintools +======== + +`Bintool` is the name binman gives to a binary tool which it uses to create and +manipulate binaries that binman cannot handle itself. Bintools are often +necessary since Binman only supports a subset of the available file formats +natively. + +Many SoC vendors invent ways to load code into their SoC using new file formats, +sometimes changing the format with successive SoC generations. Sometimes the +tool is available as Open Source. Sometimes it is a pre-compiled binary that +must be downloaded from the vendor's website. Sometimes it is available in +source form but difficult or slow to build. + +Even for images that use bintools, binman still assembles the image from its +image description. It may handle parts of the image natively and part with +various bintools. + +Binman relies on these tools so provides various features to manage them: + +- Determining whether the tool is currently installed +- Downloading or building the tool +- Determining the version of the tool that is installed +- Deciding which tools are needed to build an image + +The Bintool class is an interface to the tool, a thin level of abstration, using +Python functions to run the tool for each purpose (e.g. creating a new +structure, adding a file to an existing structure) rather than just lists of +string arguments. + +As with external blobs, bintools (which are like 'external' tools) can be +missing. When building an image requires a bintool and it is not installed, +binman detects this and reports the problem, but continues to build an image. +This is useful in CI systems which want to check that everything is correct but +don't have access to the bintools. + +To make this work, all calls to bintools (e.g. with Bintool.run_cmd()) must cope +with the tool being missing, i.e. when None is returned, by: + +- Calling self.record_missing_bintool() +- Setting up some fake contents so binman can continue + +Of course the image will not work, but binman reports which bintools are needed +and also provide a way to fetch them. + +To see the available bintools, use:: + + binman tool --list + +To fetch tools which are missing, use:: + + binman tool --fetch missing + +You can also use `--fetch all` to fetch all tools or `--fetch ` to fetch +a particular tool. Some tools are built from source code, in which case you will +need to have at least the `build-essential` and `git` packages installed. + +Bintool Documentation +===================== + +To provide details on the various bintools supported by binman, bintools.rst is +generated from the source code using: + + binman bintool-docs >tools/binman/bintools.rst + +.. toctree:: + :maxdepth: 2 + + bintools + + Technical details ================= diff --git a/tools/binman/bintools.rst b/tools/binman/bintools.rst new file mode 100644 index 0000000..edb373a --- /dev/null +++ b/tools/binman/bintools.rst @@ -0,0 +1,115 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Binman bintool Documentation +============================ + +This file describes the bintools (binary tools) supported by binman. Bintools +are binman's name for external executables that it runs to generate or process +binaries. It is fairly easy to create new bintools. Just add a new file to the +'btool' directory. You can use existing bintools as examples. + + + +Bintool: cbfstool: Coreboot filesystem (CBFS) tool +-------------------------------------------------- + +This bintool supports creating new CBFS images and adding files to an +existing image, i.e. the features needed by binman. + +It also supports fetching a binary cbfstool, since building it from source +is fairly slow. + +Documentation about CBFS is at https://www.coreboot.org/CBFS + + + +Bintool: fiptool: Image generation for ARM Trusted Firmware +----------------------------------------------------------- + +This bintool supports running `fiptool` with some basic parameters as +neeed by binman. + +It also supports build fiptool from source. + +fiptool provides a way to package firmware in an ARM Trusted Firmware +Firmware Image Package (ATF FIP) format. It is used with Trusted Firmware A, +for example. + +See `TF-A FIP tool documentation`_ for more information. + +.. _`TF-A FIP tool documentation`: + https://trustedfirmware-a.readthedocs.io/en/latest/getting_started/tools-build.html?highlight=fiptool#building-and-using-the-fip-tool + + + +Bintool: futility: Handles the 'futility' tool +---------------------------------------------- + +futility (flash utility) is a tool for working with Chromium OS flash +images. This Bintool implements just the features used by Binman, related to +GBB creation and firmware signing. + +A binary version of the tool can be fetched. + +See `Chromium OS vboot documentation`_ for more information. + +.. _`Chromium OS vboot documentation`: + https://chromium.googlesource.com/chromiumos/platform/vboot/+/refs/heads/main/_vboot_reference/README + + + +Bintool: ifwitool: Handles the 'ifwitool' tool +---------------------------------------------- + +This bintool supports running `ifwitool` with some basic parameters as +neeed by binman. It includes creating a file from a FIT as well as adding, +replacing, deleting and extracting subparts. + +The tool is built as part of U-Boot, but a binary version can be fetched if +required. + +ifwitool provides a way to package firmware in an Intel Firmware Image +(IFWI) file on some Intel SoCs, e.g. Apolo Lake. + + + +Bintool: lz4: Compression/decompression using the LZ4 algorithm +--------------------------------------------------------------- + +This bintool supports running `lz4` to compress and decompress data, as +used by binman. + +It is also possible to fetch the tool, which uses `apt` to install it. + +Documentation is available via:: + + man lz4 + + + +Bintool: lzma_alone: Compression/decompression using the LZMA algorithm +----------------------------------------------------------------------- + +This bintool supports running `lzma_alone` to compress and decompress data, +as used by binman. + +It is also possible to fetch the tool, which uses `apt` to install it. + +Documentation is available via:: + + man lzma_alone + + + +Bintool: mkimage: Image generation for U-Boot +--------------------------------------------- + +This bintool supports running `mkimage` with some basic parameters as +neeed by binman. + +Normally binman uses the mkimage built by U-Boot. But when run outside the +U-Boot build system, binman can use the version installed in your system. +Support is provided for fetching this on Debian-like systems, using apt. + + + -- cgit v1.1 From ce51884f5181573eaf3d62c4f84e19b80899aba6 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 21 Jan 2022 16:07:30 +0100 Subject: sandbox: sandbox_serial_pending depends on DM_VIDEO When building sandbox_defconfig with CONFIG_DM_VIDEO=n a link time error occurs: in function `sandbox_serial_pending': drivers/serial/sandbox.c:101: undefined reference to `video_sync_all' video_sync_all() is only defined if we have CONFIG_DM_VIDEO=y. Calling this function in a serial driver looks quite hackish but at least let's add the missing build constraint. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- drivers/serial/sandbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index dbbcea5..0b1756f 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -97,7 +97,7 @@ static int sandbox_serial_pending(struct udevice *dev, bool input) return 0; os_usleep(100); - if (!IS_ENABLED(CONFIG_SPL_BUILD)) + if (IS_ENABLED(CONFIG_DM_VIDEO) && !IS_ENABLED(CONFIG_SPL_BUILD)) video_sync_all(); avail = membuff_putraw(&priv->buf, 100, false, &data); if (!avail) -- cgit v1.1 From 8e72374feb08110e407e008b6d4a158158f9fcf1 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 21 Jan 2022 18:01:23 +0100 Subject: sandbox: eth-raw: fix building with musl library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The definition of struct udphdr in include netinet/udp.h in the musl library differs from the definition in the glibc library. To use the same definition with musl the symbol _GNU_SOURCE has to be defined. Reported-by: Milan P. Stanić Signed-off-by: Heinrich Schuchardt Tested-by: Milan P. Stanić --- arch/sandbox/cpu/eth-raw-os.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sandbox/cpu/eth-raw-os.c b/arch/sandbox/cpu/eth-raw-os.c index 6a8d809..e59b96b 100644 --- a/arch/sandbox/cpu/eth-raw-os.c +++ b/arch/sandbox/cpu/eth-raw-os.c @@ -4,6 +4,8 @@ * Copyright (c) 2015-2018 Joe Hershberger */ +#define _GNU_SOURCE + #include #include #include -- cgit v1.1 From 61a631e912dc651f4c6e93b6d1504de68de1ecaf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 23 Jan 2022 12:55:46 -0700 Subject: binman: Document the __bss_size symbol error Add a note about the message so it is clear why it occurs. Signed-off-by: Simon Glass --- tools/binman/binman.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index db2234b..eb2c9d4 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1232,6 +1232,35 @@ To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):: $ sudo apt-get install python-coverage python3-coverage python-pytest +Error messages +-------------- + +This section provides some guidance for some of the less obvious error messages +produced by binman. + + +Expected __bss_size symbol +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Example:: + + binman: Node '/binman/u-boot-spl-ddr/u-boot-spl/u-boot-spl-bss-pad': + Expected __bss_size symbol in spl/u-boot-spl + +This indicates that binman needs the `__bss_size` symbol to be defined in the +SPL binary, where `spl/u-boot-spl` is the ELF file containing the symbols. The +symbol tells binman the size of the BSS region, in bytes. It needs this to be +able to pad the image so that the following entries do not overlap the BSS, +which would cause them to be overwritte by variable access in SPL. + +This symbols is normally defined in the linker script, immediately after +_bss_start and __bss_end are defined, like this:: + + __bss_size = __bss_end - __bss_start; + +You may need to add it to your linker script if you get this error. + + Concurrent tests ---------------- -- cgit v1.1 From 2ce07383a46b8a8d8b888cb1b9f16ebb87663346 Mon Sep 17 00:00:00 2001 From: Heiko Thiery Date: Mon, 24 Jan 2022 08:11:01 +0100 Subject: binman: doc: fix typo for u-boot-tpl Cc: Simon Glass Signed-off-by: Heiko Thiery Reviewed-by: Simon Glass --- tools/binman/binman.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index eb2c9d4..ab5a5e0 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -730,7 +730,7 @@ The above feature ensures that the devicetree is clearly separated from the U-Boot executable and can be updated separately by binman as needed. It can be disabled with the --no-expanded flag if required. -The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion +The same applies for u-boot-spl and u-boot-tpl. In those cases, the expansion includes the BSS padding, so for example:: spl { -- cgit v1.1 From c3675583e93b0529a024bf63020e5f518f988a6a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:02 -0700 Subject: rsa: Add debugging for failure cases Add some more debugging to make it easier to see what is being tried and what fails. Fix a few comment styles while here. Signed-off-by: Simon Glass --- lib/rsa/rsa-verify.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index fbb2d35..32c7507 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -79,14 +79,14 @@ int padding_pkcs_15_verify(struct image_sign_info *info, struct checksum_algo *checksum = info->checksum; int ret, pad_len = msg_len - checksum->checksum_len; - /* Check pkcs1.5 padding bytes. */ + /* Check pkcs1.5 padding bytes */ ret = rsa_verify_padding(msg, pad_len, checksum); if (ret) { debug("In RSAVerify(): Padding check failed!\n"); return -EINVAL; } - /* Check hash. */ + /* Check hash */ if (memcmp((uint8_t *)msg + pad_len, hash, msg_len - pad_len)) { debug("In RSAVerify(): Hash check failed!\n"); return -EACCES; @@ -502,7 +502,8 @@ int rsa_verify_hash(struct image_sign_info *info, if (CONFIG_IS_ENABLED(RSA_VERIFY_WITH_PKEY) && !info->fdt_blob) { /* don't rely on fdt properties */ ret = rsa_verify_with_pkey(info, hash, sig, sig_len); - + if (ret) + debug("%s: rsa_verify_with_pkey() failed\n", __func__); return ret; } @@ -522,6 +523,9 @@ int rsa_verify_hash(struct image_sign_info *info, if (info->required_keynode != -1) { ret = rsa_verify_with_keynode(info, hash, sig, sig_len, info->required_keynode); + if (ret) + debug("%s: Failed to verify required_keynode\n", + __func__); return ret; } @@ -531,6 +535,8 @@ int rsa_verify_hash(struct image_sign_info *info, ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node); if (!ret) return ret; + debug("%s: Could not verify key '%s', trying all\n", __func__, + name); /* No luck, so try each of the keys in turn */ for (ndepth = 0, noffset = fdt_next_node(blob, sig_node, @@ -546,6 +552,7 @@ int rsa_verify_hash(struct image_sign_info *info, } } } + debug("%s: Failed to verify by any means\n", __func__); return ret; } -- cgit v1.1 From 195f7893da9608931cfee63bc2f7a6aaa5169049 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:03 -0700 Subject: fit_check_sign: Update help to mention the key is in a dtb The key is inside a dtb file, so tweak the help to make that clear. Signed-off-by: Simon Glass --- tools/fit_check_sign.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/fit_check_sign.c b/tools/fit_check_sign.c index 5573842..3d1d33f 100644 --- a/tools/fit_check_sign.c +++ b/tools/fit_check_sign.c @@ -27,7 +27,7 @@ void usage(char *cmdname) { fprintf(stderr, "Usage: %s -f fit file -k key file -c config name\n" " -f ==> set fit file which should be checked'\n" - " -k ==> set key file which contains the key'\n" + " -k ==> set key .dtb file which contains the key'\n" " -c ==> set the configuration name'\n", cmdname); exit(EXIT_FAILURE); @@ -89,7 +89,7 @@ int main(int argc, char **argv) fprintf(stderr, "Signature check OK\n"); } else { ret = EXIT_FAILURE; - fprintf(stderr, "Signature check Bad (error %d)\n", ret); + fprintf(stderr, "Signature check bad (error %d)\n", ret); } (void) munmap((void *)fit_blob, fsbuf.st_size); -- cgit v1.1 From e291a5c9a2acff16ba3b976198bba7da9828c9e9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:04 -0700 Subject: tools: Move copyfile() into a common file This function is useful in other places. Move it to a common file. Signed-off-by: Simon Glass --- tools/fit_common.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/fit_common.h | 11 +++++++++++ tools/fit_image.c | 56 ------------------------------------------------------ 3 files changed, 67 insertions(+), 56 deletions(-) diff --git a/tools/fit_common.c b/tools/fit_common.c index 5c8920d..4370de2 100644 --- a/tools/fit_common.c +++ b/tools/fit_common.c @@ -119,3 +119,59 @@ err: return -1; } + +int copyfile(const char *src, const char *dst) +{ + int fd_src = -1, fd_dst = -1; + void *buf = NULL; + ssize_t size; + size_t count; + int ret = -1; + + fd_src = open(src, O_RDONLY); + if (fd_src < 0) { + printf("Can't open file %s (%s)\n", src, strerror(errno)); + goto out; + } + + fd_dst = open(dst, O_WRONLY | O_CREAT, 0666); + if (fd_dst < 0) { + printf("Can't open file %s (%s)\n", dst, strerror(errno)); + goto out; + } + + buf = calloc(1, 512); + if (!buf) { + printf("Can't allocate buffer to copy file\n"); + goto out; + } + + while (1) { + size = read(fd_src, buf, 512); + if (size < 0) { + printf("Can't read file %s\n", src); + goto out; + } + if (!size) + break; + + count = size; + size = write(fd_dst, buf, count); + if (size < 0) { + printf("Can't write file %s\n", dst); + goto out; + } + } + + ret = 0; + + out: + if (fd_src >= 0) + close(fd_src); + if (fd_dst >= 0) + close(fd_dst); + if (buf) + free(buf); + + return ret; +} diff --git a/tools/fit_common.h b/tools/fit_common.h index 0e8ee79..872d8af 100644 --- a/tools/fit_common.h +++ b/tools/fit_common.h @@ -39,4 +39,15 @@ int mmap_fdt(const char *cmdname, const char *fname, size_t size_inc, void **blobp, struct stat *sbuf, bool delete_on_error, bool read_only); +/** + * copyfile() - Copy a file + * + * This uses read()/write() to copy file @src to file @dst + * + * @src: Filename to read from + * @dst: Filename to write to + * @return 0 if OK, -1 on error + */ +int copyfile(const char *src, const char *dst); + #endif /* _FIT_COMMON_H_ */ diff --git a/tools/fit_image.c b/tools/fit_image.c index 9ac5256..0e31f7d 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -656,62 +656,6 @@ err: return ret; } -static int copyfile(const char *src, const char *dst) -{ - int fd_src = -1, fd_dst = -1; - void *buf = NULL; - ssize_t size; - size_t count; - int ret = -1; - - fd_src = open(src, O_RDONLY); - if (fd_src < 0) { - printf("Can't open file %s (%s)\n", src, strerror(errno)); - goto out; - } - - fd_dst = open(dst, O_WRONLY | O_CREAT, 0666); - if (fd_dst < 0) { - printf("Can't open file %s (%s)\n", dst, strerror(errno)); - goto out; - } - - buf = calloc(1, 512); - if (!buf) { - printf("Can't allocate buffer to copy file\n"); - goto out; - } - - while (1) { - size = read(fd_src, buf, 512); - if (size < 0) { - printf("Can't read file %s\n", src); - goto out; - } - if (!size) - break; - - count = size; - size = write(fd_dst, buf, count); - if (size < 0) { - printf("Can't write file %s\n", dst); - goto out; - } - } - - ret = 0; - - out: - if (fd_src >= 0) - close(fd_src); - if (fd_dst >= 0) - close(fd_dst); - if (buf) - free(buf); - - return ret; -} - /** * fit_handle_file - main FIT file processing function * -- cgit v1.1 From 7ae46c35793bcc034f1300d8b79e3fd7e506537c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:05 -0700 Subject: tools: Avoid leaving extra data at the end of copied files The copyfile() implementation has strange behaviour if the destination file already exists. Update it to ensure that any existing data in the destination file is dropped. Signed-off-by: Simon Glass --- tools/fit_common.c | 2 +- tools/fit_common.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/fit_common.c b/tools/fit_common.c index 4370de2..5ea43f5 100644 --- a/tools/fit_common.c +++ b/tools/fit_common.c @@ -134,7 +134,7 @@ int copyfile(const char *src, const char *dst) goto out; } - fd_dst = open(dst, O_WRONLY | O_CREAT, 0666); + fd_dst = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd_dst < 0) { printf("Can't open file %s (%s)\n", dst, strerror(errno)); goto out; diff --git a/tools/fit_common.h b/tools/fit_common.h index 872d8af..c600dc2 100644 --- a/tools/fit_common.h +++ b/tools/fit_common.h @@ -44,6 +44,8 @@ int mmap_fdt(const char *cmdname, const char *fname, size_t size_inc, * * This uses read()/write() to copy file @src to file @dst * + * If @dst exists, it is overwritten and truncated to the correct size. + * * @src: Filename to read from * @dst: Filename to write to * @return 0 if OK, -1 on error -- cgit v1.1 From 70e6bcc43f541fbcb8e878dba8299e8e30943bcd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:06 -0700 Subject: tools: Improve comments in signing functions Add some more comments to explain what is going on in the signing functions. Fix two repeated typos. Signed-off-by: Simon Glass --- include/image.h | 2 +- tools/fdt_host.h | 8 +++++ tools/image-host.c | 98 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 85 insertions(+), 23 deletions(-) diff --git a/include/image.h b/include/image.h index fe13562..15cfb2c 100644 --- a/include/image.h +++ b/include/image.h @@ -1025,7 +1025,7 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit, * fit_add_verification_data() - add verification data to FIT image nodes * * @keydir: Directory containing keys - * @kwydest: FDT blob to write public key information to + * @kwydest: FDT blob to write public key information to (NULL if none) * @fit: Pointer to the FIT format image header * @comment: Comment to add to signature nodes * @require_keys: Mark all keys as 'required' diff --git a/tools/fdt_host.h b/tools/fdt_host.h index 15c07c7..bc42306 100644 --- a/tools/fdt_host.h +++ b/tools/fdt_host.h @@ -27,6 +27,14 @@ */ int fdt_remove_unused_strings(const void *old, void *new); +/** + * fit_check_sign() - Check a signature in a FIT + * + * @fit: FIT to check + * @key: Key FDT blob to check against + * @fit_uname_config: Name of configuration to check (NULL for default) + * @return 0 if OK, -ve if signature failed + */ int fit_check_sign(const void *fit, const void *key, const char *fit_uname_config); diff --git a/tools/image-host.c b/tools/image-host.c index f86e1fb..a5d47a4 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -48,10 +48,10 @@ static int fit_set_hash_value(void *fit, int noffset, uint8_t *value, * fit_image_process_hash - Process a single subnode of the images/ node * * Check each subnode and process accordingly. For hash nodes we generate - * a hash of the supplised data and store it in the node. + * a hash of the supplied data and store it in the node. * * @fit: pointer to the FIT format image header - * @image_name: name of image being processes (used to display errors) + * @image_name: name of image being processed (used to display errors) * @noffset: subnode offset * @data: data to process * @size: size of data in bytes @@ -200,12 +200,12 @@ static int fit_image_setup_sig(struct image_sign_info *info, * fit_image_process_sig- Process a single subnode of the images/ node * * Check each subnode and process accordingly. For signature nodes we - * generate a signed hash of the supplised data and store it in the node. + * generate a signed hash of the supplied data and store it in the node. * * @keydir: Directory containing keys to use for signing - * @keydest: Destination FDT blob to write public keys into + * @keydest: Destination FDT blob to write public keys into (NULL if none) * @fit: pointer to the FIT format image header - * @image_name: name of image being processes (used to display errors) + * @image_name: name of image being processed (used to display errors) * @noffset: subnode offset * @data: data to process * @size: size of data in bytes @@ -689,14 +689,14 @@ static int strlist_add(struct strlist *list, const char *str) return 0; } -static const char *fit_config_get_image_list(void *fit, int noffset, - int *lenp, int *allow_missingp) +static const char *fit_config_get_image_list(const void *fit, int noffset, + int *lenp, int *allow_missingp) { static const char default_list[] = FIT_KERNEL_PROP "\0" FIT_FDT_PROP; const char *prop; - /* If there is an "image" property, use that */ + /* If there is an "sign-image" property, use that */ prop = fdt_getprop(fit, noffset, "sign-images", lenp); if (prop) { *allow_missingp = 0; @@ -710,8 +710,24 @@ static const char *fit_config_get_image_list(void *fit, int noffset, return default_list; } -static int fit_config_add_hash(void *fit, const char *conf_name, const char *sig_name, - struct strlist *node_inc, const char *iname, int image_noffset) +/** + * fit_config_add_hash() - Add a list of nodes to hash for an image + * + * This adds a list of paths to image nodes (as referred to by a particular + * offset) that need to be hashed, to protect a configuration + * + * @fit: Pointer to the FIT format image header + * @image_noffset: Offset of image to process (e.g. /images/kernel-1) + * @node_inc: List of nodes to add to + * @conf_name Configuration-node name, child of /configurations node (only + * used for error messages) + * @sig_name Signature-node name (only used for error messages) + * @iname: Name of image being processed (e.g. "kernel-1" (only used + * for error messages) + */ +static int fit_config_add_hash(const void *fit, int image_noffset, + struct strlist *node_inc, const char *conf_name, + const char *sig_name, const char *iname) { char name[200], path[200]; int noffset; @@ -781,7 +797,21 @@ err_path: return -ENOENT; } -static int fit_config_get_hash_list(void *fit, int conf_noffset, +/** + * fit_config_get_hash_list() - Get the regions to sign + * + * This calculates a list of nodes to hash for this particular configuration, + * returning it as a string list (struct strlist, not a devicetree string list) + * + * @fit: Pointer to the FIT format image header + * @conf_noffset: Offset of configuration node to sign (child of + * /configurations node) + * @sig_offset: Offset of signature node containing info about how to sign it + * (child of 'signatures' node) + * @return 0 if OK, -ENOENT if an image referred to by the configuration cannot + * be found, -ENOMSG if ther were no images in the configuration + */ +static int fit_config_get_hash_list(const void *fit, int conf_noffset, int sig_offset, struct strlist *node_inc) { int allow_missing; @@ -832,9 +862,8 @@ static int fit_config_get_hash_list(void *fit, int conf_noffset, return -ENOENT; } - ret = fit_config_add_hash(fit, conf_name, - sig_name, node_inc, - iname, image_noffset); + ret = fit_config_add_hash(fit, image_noffset, node_inc, + conf_name, sig_name, iname); if (ret < 0) return ret; @@ -856,9 +885,32 @@ err_mem: return -ENOMEM; } -static int fit_config_get_data(void *fit, int conf_noffset, int noffset, - struct image_region **regionp, int *region_countp, - char **region_propp, int *region_proplen) +/** + * fit_config_get_regions() - Get the regions to sign + * + * This calculates a list of node to hash for this particular configuration, + * then finds which regions of the devicetree they correspond to. + * + * @fit: Pointer to the FIT format image header + * @conf_noffset: Offset of configuration node to sign (child of + * /configurations node) + * @sig_offset: Offset of signature node containing info about how to sign it + * (child of 'signatures' node) + * @regionp: Returns list of regions that need to be hashed (allocated; must be + * freed by the caller) + * @region_count: Returns number of regions + * @region_propp: Returns string-list property containing the list of nodes + * that correspond to the regions. Each entry is a full path to the node. + * This is in devicetree format, i.e. a \0 between each string. This is + * allocated and must be freed by the caller. + * @region_proplen: Returns length of *@@region_propp in bytes + * @return 0 if OK, -ENOMEM if out of memory, -EIO if the regions to hash could + * not be found, -EINVAL if no registers were found to hash + */ +static int fit_config_get_regions(const void *fit, int conf_noffset, + int sig_offset, struct image_region **regionp, + int *region_countp, char **region_propp, + int *region_proplen) { char * const exc_prop[] = {"data"}; struct strlist node_inc; @@ -871,11 +923,12 @@ static int fit_config_get_data(void *fit, int conf_noffset, int noffset, int ret, len; conf_name = fit_get_name(fit, conf_noffset, NULL); - sig_name = fit_get_name(fit, noffset, NULL); + sig_name = fit_get_name(fit, sig_offset, NULL); debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name); /* Get a list of nodes we want to hash */ - ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc); + ret = fit_config_get_hash_list(fit, conf_noffset, sig_offset, + &node_inc); if (ret) return ret; @@ -929,7 +982,7 @@ static int fit_config_get_data(void *fit, int conf_noffset, int noffset, } static int fit_config_process_sig(const char *keydir, const char *keyfile, - void *keydest, void *fit, const char *conf_name, + void *keydest, void *fit, const char *conf_name, int conf_noffset, int noffset, const char *comment, int require_keys, const char *engine_id, const char *cmdname, const char *algo_name) @@ -945,8 +998,9 @@ static int fit_config_process_sig(const char *keydir, const char *keyfile, int ret; node_name = fit_get_name(fit, noffset, NULL); - if (fit_config_get_data(fit, conf_noffset, noffset, ®ion, - ®ion_count, ®ion_prop, ®ion_proplen)) + if (fit_config_get_regions(fit, conf_noffset, noffset, ®ion, + ®ion_count, ®ion_prop, + ®ion_proplen)) return -1; if (fit_image_setup_sig(&info, keydir, keyfile, fit, conf_name, noffset, -- cgit v1.1 From 48422343c88304638d62e543060376b0e443904b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:07 -0700 Subject: tools: Drop unused name in image-host The name is created but never used. Drop it. Signed-off-by: Simon Glass --- tools/image-host.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/image-host.c b/tools/image-host.c index a5d47a4..f13a944 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -729,7 +729,7 @@ static int fit_config_add_hash(const void *fit, int image_noffset, struct strlist *node_inc, const char *conf_name, const char *sig_name, const char *iname) { - char name[200], path[200]; + char path[200]; int noffset; int hash_count; int ret; @@ -740,9 +740,6 @@ static int fit_config_add_hash(const void *fit, int image_noffset, if (strlist_add(node_inc, path)) goto err_mem; - snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, - conf_name); - /* Add all this image's hashes */ hash_count = 0; for (noffset = fdt_first_subnode(fit, image_noffset); -- cgit v1.1 From e8aa5580d41dbe556c85a753188de9e43045b220 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:08 -0700 Subject: tools: Avoid confusion between keys and signatures We should be consistent in using the term 'signature' to describe a value added to sign something and 'key' to describe the key that can be used to verify the signature. Tidy up the code to stick to this. Add some comments to fit_config_verify_key() and its callers while we are here. Signed-off-by: Simon Glass --- boot/image-fit-sig.c | 97 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index 495d776..e07bacb 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -135,7 +135,7 @@ int fit_image_check_sig(const void *fit, int noffset, const void *data, static int fit_image_verify_sig(const void *fit, int image_noffset, const char *data, size_t size, - const void *sig_blob, int sig_offset) + const void *key_blob, int key_offset) { int noffset; char *err_msg = ""; @@ -184,34 +184,34 @@ error: int fit_image_verify_required_sigs(const void *fit, int image_noffset, const char *data, size_t size, - const void *sig_blob, int *no_sigsp) + const void *key_blob, int *no_sigsp) { int verify_count = 0; int noffset; - int sig_node; + int key_node; /* Work out what we need to verify */ *no_sigsp = 1; - sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); - if (sig_node < 0) { + key_node = fdt_subnode_offset(key_blob, 0, FIT_SIG_NODENAME); + if (key_node < 0) { debug("%s: No signature node found: %s\n", __func__, - fdt_strerror(sig_node)); + fdt_strerror(key_node)); return 0; } - fdt_for_each_subnode(noffset, sig_blob, sig_node) { + fdt_for_each_subnode(noffset, key_blob, key_node) { const char *required; int ret; - required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + required = fdt_getprop(key_blob, noffset, FIT_KEY_REQUIRED, NULL); if (!required || strcmp(required, "image")) continue; ret = fit_image_verify_sig(fit, image_noffset, data, size, - sig_blob, noffset); + key_blob, noffset); if (ret) { printf("Failed to verify required signature '%s'\n", - fit_get_name(sig_blob, noffset, NULL)); + fit_get_name(key_blob, noffset, NULL)); return ret; } verify_count++; @@ -368,8 +368,35 @@ static int fit_config_check_sig(const void *fit, int noffset, return 0; } -static int fit_config_verify_sig(const void *fit, int conf_noffset, - const void *sig_blob, int sig_offset) +/** + * fit_config_verify_key() - Verify that a configuration is signed with a key + * + * Here we are looking at a particular configuration that needs verification: + * + * configurations { + * default = "conf-1"; + * conf-1 { + * kernel = "kernel-1"; + * fdt = "fdt-1"; + * signature-1 { + * algo = "sha1,rsa2048"; + * value = <...conf 1 signature...>; + * }; + * }; + * + * We must check each of the signature subnodes of conf-1. Hopefully one of them + * will match the key at key_offset. + * + * @fit: FIT to check + * @conf_noffset: Offset of the configuration node to check (e.g. + * /configurations/conf-1) + * @key_blob: Blob containing the keys to check against + * @key_offset: Offset of the key to check within @key_blob + * @return 0 if OK, -EPERM if any signatures did not verify, or the + * configuration node has an invalid name + */ +static int fit_config_verify_key(const void *fit, int conf_noffset, + const void *key_blob, int key_offset) { int noffset; char *err_msg = "No 'signature' subnode found"; @@ -382,7 +409,7 @@ static int fit_config_verify_sig(const void *fit, int conf_noffset, if (!strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { - ret = fit_config_check_sig(fit, noffset, sig_offset, + ret = fit_config_check_sig(fit, noffset, key_offset, conf_noffset, &err_msg); if (ret) { puts("- "); @@ -409,12 +436,25 @@ error: return -EPERM; } -static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, - const void *sig_blob) +/** + * fit_config_verify_required_keys() - verify any required signatures for config + * + * This looks through all the signatures we expect and verifies that at least + * all the required ones are valid signatures for the configuration + * + * @fit: FIT to check + * @conf_noffset: Offset of the configuration node to check (e.g. + * /configurations/conf-1) + * @key_blob: Blob containing the keys to check against + * @return 0 if OK, -EPERM if any signatures did not verify, or the + * configuration node has an invalid name + */ +static int fit_config_verify_required_keys(const void *fit, int conf_noffset, + const void *key_blob) { const char *name = fit_get_name(fit, conf_noffset, NULL); int noffset; - int sig_node; + int key_node; int verified = 0; int reqd_sigs = 0; bool reqd_policy_all = true; @@ -430,38 +470,45 @@ static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, } /* Work out what we need to verify */ - sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); - if (sig_node < 0) { + key_node = fdt_subnode_offset(key_blob, 0, FIT_SIG_NODENAME); + if (key_node < 0) { debug("%s: No signature node found: %s\n", __func__, - fdt_strerror(sig_node)); + fdt_strerror(key_node)); return 0; } /* Get required-mode policy property from DTB */ - reqd_mode = fdt_getprop(sig_blob, sig_node, "required-mode", NULL); + reqd_mode = fdt_getprop(key_blob, key_node, "required-mode", NULL); if (reqd_mode && !strcmp(reqd_mode, "any")) reqd_policy_all = false; debug("%s: required-mode policy set to '%s'\n", __func__, reqd_policy_all ? "all" : "any"); - fdt_for_each_subnode(noffset, sig_blob, sig_node) { + /* + * The algorithm here is a little convoluted due to how we want it to + * work. Here we work through each of the signature nodes in the + * public-key area. These are in the U-Boot control devicetree. Each + * node was created by signing a configuration, so we check if it is + * 'required' and if so, request that it be verified. + */ + fdt_for_each_subnode(noffset, key_blob, key_node) { const char *required; int ret; - required = fdt_getprop(sig_blob, noffset, FIT_KEY_REQUIRED, + required = fdt_getprop(key_blob, noffset, FIT_KEY_REQUIRED, NULL); if (!required || strcmp(required, "conf")) continue; reqd_sigs++; - ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + ret = fit_config_verify_key(fit, conf_noffset, key_blob, noffset); if (ret) { if (reqd_policy_all) { printf("Failed to verify required signature '%s'\n", - fit_get_name(sig_blob, noffset, NULL)); + fit_get_name(key_blob, noffset, NULL)); return ret; } } else { @@ -481,6 +528,6 @@ static int fit_config_verify_required_sigs(const void *fit, int conf_noffset, int fit_config_verify(const void *fit, int conf_noffset) { - return fit_config_verify_required_sigs(fit, conf_noffset, + return fit_config_verify_required_keys(fit, conf_noffset, gd_fdt_blob()); } -- cgit v1.1 From 2ad90b395313a7350cfb0543b4979a24746413b3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:09 -0700 Subject: tools: Tidy up argument order in fit_config_check_sig() Put the parent node first in the parameters as this is more natural. Also add a comment to explain what is going on. Signed-off-by: Simon Glass --- boot/image-fit-sig.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index e07bacb..d6e16c2 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -226,21 +226,34 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, /** * fit_config_check_sig() - Check the signature of a config * + * Here we are looking at a particular signature that needs verification (here + * signature-1): + * + * configurations { + * default = "conf-1"; + * conf-1 { + * kernel = "kernel-1"; + * fdt = "fdt-1"; + * signature-1 { + * algo = "sha1,rsa2048"; + * value = <...conf 1 signature...>; + * }; + * }; + * * @fit: FIT to check - * @noffset: Offset of configuration node (e.g. /configurations/conf-1) - * @required_keynode: Offset in the control FDT of the required key node, + * @noffset: Offset of the signature node being checked (e.g. + * /configurations/conf-1/signature-1) + * @conf_noffset: Offset of configuration node (e.g. /configurations/conf-1) + * @required_keynode: Offset in @key_blob of the required key node, * if any. If this is given, then the configuration wil not * pass verification unless that key is used. If this is * -1 then any signature will do. - * @conf_noffset: Offset of the configuration subnode being checked (e.g. - * /configurations/conf-1/kernel) * @err_msgp: In the event of an error, this will be pointed to a * help error string to display to the user. * Return: 0 if all verified ok, <0 on error */ -static int fit_config_check_sig(const void *fit, int noffset, - int required_keynode, int conf_noffset, - char **err_msgp) +static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset, + int required_keynode, char **err_msgp) { static char * const exc_prop[] = { "data", @@ -409,8 +422,8 @@ static int fit_config_verify_key(const void *fit, int conf_noffset, if (!strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { - ret = fit_config_check_sig(fit, noffset, key_offset, - conf_noffset, &err_msg); + ret = fit_config_check_sig(fit, noffset, conf_noffset, + key_offset, &err_msg); if (ret) { puts("- "); } else { -- cgit v1.1 From 99f844ba3a6b3ddd73742cddf7dee955bbb96c61 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:10 -0700 Subject: tools: Pass the key blob around At present we rely on the key blob being in the global_data fdt_blob pointer. This is true in U-Boot but not with tools. For clarity, pass the parameter around. Signed-off-by: Simon Glass --- boot/image-fit-sig.c | 31 ++++++++++++++++++------------- boot/image-fit.c | 12 +++++++----- common/spl/spl_fit.c | 3 ++- include/image.h | 23 ++++++++++++++++++----- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index d6e16c2..a461d59 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -65,7 +65,8 @@ struct image_region *fit_region_make_list(const void *fit, static int fit_image_setup_verify(struct image_sign_info *info, const void *fit, int noffset, - int required_keynode, char **err_msgp) + const void *key_blob, int required_keynode, + char **err_msgp) { const char *algo_name; const char *padding_name; @@ -91,7 +92,7 @@ static int fit_image_setup_verify(struct image_sign_info *info, info->checksum = image_get_checksum_algo(algo_name); info->crypto = image_get_crypto_algo(algo_name); info->padding = image_get_padding_algo(padding_name); - info->fdt_blob = gd_fdt_blob(); + info->fdt_blob = key_blob; info->required_keynode = required_keynode; printf("%s:%s", algo_name, info->keyname); @@ -104,7 +105,8 @@ static int fit_image_setup_verify(struct image_sign_info *info, } int fit_image_check_sig(const void *fit, int noffset, const void *data, - size_t size, int required_keynode, char **err_msgp) + size_t size, const void *key_blob, int required_keynode, + char **err_msgp) { struct image_sign_info info; struct image_region region; @@ -112,8 +114,8 @@ int fit_image_check_sig(const void *fit, int noffset, const void *data, int fit_value_len; *err_msgp = NULL; - if (fit_image_setup_verify(&info, fit, noffset, required_keynode, - err_msgp)) + if (fit_image_setup_verify(&info, fit, noffset, key_blob, + required_keynode, err_msgp)) return -1; if (fit_image_hash_get_value(fit, noffset, &fit_value, @@ -156,8 +158,8 @@ static int fit_image_verify_sig(const void *fit, int image_noffset, } if (!strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { - ret = fit_image_check_sig(fit, noffset, data, - size, -1, &err_msg); + ret = fit_image_check_sig(fit, noffset, data, size, + key_blob, -1, &err_msg); if (ret) { puts("- "); } else { @@ -244,6 +246,7 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, * @noffset: Offset of the signature node being checked (e.g. * /configurations/conf-1/signature-1) * @conf_noffset: Offset of configuration node (e.g. /configurations/conf-1) + * @key_blob: Blob containing the keys to check against * @required_keynode: Offset in @key_blob of the required key node, * if any. If this is given, then the configuration wil not * pass verification unless that key is used. If this is @@ -253,7 +256,8 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, * Return: 0 if all verified ok, <0 on error */ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset, - int required_keynode, char **err_msgp) + const void *key_blob, int required_keynode, + char **err_msgp) { static char * const exc_prop[] = { "data", @@ -275,12 +279,12 @@ static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset, int count; config_name = fit_get_name(fit, conf_noffset, NULL); - debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, key_blob, fit_get_name(fit, noffset, NULL), - fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + fit_get_name(key_blob, required_keynode, NULL)); *err_msgp = NULL; - if (fit_image_setup_verify(&info, fit, noffset, required_keynode, - err_msgp)) + if (fit_image_setup_verify(&info, fit, noffset, key_blob, + required_keynode, err_msgp)) return -1; if (fit_image_hash_get_value(fit, noffset, &fit_value, @@ -423,7 +427,8 @@ static int fit_config_verify_key(const void *fit, int conf_noffset, if (!strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { ret = fit_config_check_sig(fit, noffset, conf_noffset, - key_offset, &err_msg); + key_blob, key_offset, + &err_msg); if (ret) { puts("- "); } else { diff --git a/boot/image-fit.c b/boot/image-fit.c index 85a6f22..f01cafe 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -1309,7 +1309,8 @@ static int fit_image_check_hash(const void *fit, int noffset, const void *data, } int fit_image_verify_with_data(const void *fit, int image_noffset, - const void *data, size_t size) + const void *key_blob, const void *data, + size_t size) { int noffset = 0; char *err_msg = ""; @@ -1319,7 +1320,7 @@ int fit_image_verify_with_data(const void *fit, int image_noffset, /* Verify all required signatures */ if (FIT_IMAGE_ENABLE_VERIFY && fit_image_verify_required_sigs(fit, image_noffset, data, size, - gd_fdt_blob(), &verify_all)) { + key_blob, &verify_all)) { err_msg = "Unable to verify required signature"; goto error; } @@ -1342,8 +1343,8 @@ int fit_image_verify_with_data(const void *fit, int image_noffset, } else if (FIT_IMAGE_ENABLE_VERIFY && verify_all && !strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { - ret = fit_image_check_sig(fit, noffset, data, - size, -1, &err_msg); + ret = fit_image_check_sig(fit, noffset, data, size, + gd_fdt_blob(), -1, &err_msg); /* * Show an indication on failure, but do not return @@ -1406,7 +1407,8 @@ int fit_image_verify(const void *fit, int image_noffset) goto err; } - return fit_image_verify_with_data(fit, image_noffset, data, size); + return fit_image_verify_with_data(fit, image_noffset, gd_fdt_blob(), + data, size); err: printf("error!\n%s in '%s' image node\n", err_msg, diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 774072b..1bbf824 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -321,7 +321,8 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector, if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) { printf("## Checking hash(es) for Image %s ... ", fit_get_name(fit, node, NULL)); - if (!fit_image_verify_with_data(fit, node, src, length)) + if (!fit_image_verify_with_data(fit, node, gd_fdt_blob(), src, + length)) return -EPERM; puts("OK\n"); } diff --git a/include/image.h b/include/image.h index 15cfb2c..780b624 100644 --- a/include/image.h +++ b/include/image.h @@ -1048,8 +1048,19 @@ int fit_add_verification_data(const char *keydir, const char *keyfile, int require_keys, const char *engine_id, const char *cmdname, const char *algo_name); +/** + * fit_image_verify_with_data() - Verify an image with given data + * + * @fit: Pointer to the FIT format image header + * @image_offset: Offset in @fit of image to verify + * @key_blob: FDT containing public keys + * @data: Image data to verify + * @size: Size of image data + */ int fit_image_verify_with_data(const void *fit, int image_noffset, - const void *data, size_t size); + const void *key_blob, const void *data, + size_t size); + int fit_image_verify(const void *fit, int noffset); int fit_config_verify(const void *fit, int conf_noffset); int fit_all_image_verify(const void *fit); @@ -1297,7 +1308,7 @@ struct padding_algo *image_get_padding_algo(const char *name); * @image_noffset: Offset of image node to check * @data: Image data to check * @size: Size of image data - * @sig_blob: FDT containing public keys + * @key_blob: FDT containing public keys * @no_sigsp: Returns 1 if no signatures were required, and * therefore nothing was checked. The caller may wish * to fall back to other mechanisms, or refuse to @@ -1305,7 +1316,7 @@ struct padding_algo *image_get_padding_algo(const char *name); * Return: 0 if all verified ok, <0 on error */ int fit_image_verify_required_sigs(const void *fit, int image_noffset, - const char *data, size_t size, const void *sig_blob, + const char *data, size_t size, const void *key_blob, int *no_sigsp); /** @@ -1315,7 +1326,8 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, * @noffset: Offset of signature node to check * @data: Image data to check * @size: Size of image data - * @required_keynode: Offset in the control FDT of the required key node, + * @keyblob: Key blob to check (typically the control FDT) + * @required_keynode: Offset in the keyblob of the required key node, * if any. If this is given, then the image wil not * pass verification unless that key is used. If this is * -1 then any signature will do. @@ -1324,7 +1336,8 @@ int fit_image_verify_required_sigs(const void *fit, int image_noffset, * Return: 0 if all verified ok, <0 on error */ int fit_image_check_sig(const void *fit, int noffset, const void *data, - size_t size, int required_keynode, char **err_msgp); + size_t size, const void *key_blob, int required_keynode, + char **err_msgp); int fit_image_decrypt_data(const void *fit, int image_noffset, int cipher_noffset, -- cgit v1.1 From c033dc8c0c4b744e028e124f88be4829309c75d1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:11 -0700 Subject: image: Return destination node for add_verify_data() method It is useful to know where the verification data was written. Update the API to return this. Signed-off-by: Simon Glass --- include/image.h | 3 ++- include/u-boot/ecdsa.h | 5 +++-- include/u-boot/rsa.h | 5 +++-- lib/ecdsa/ecdsa-libcrypto.c | 4 ++-- lib/rsa/rsa-sign.c | 5 ++++- tools/image-host.c | 5 ++--- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/image.h b/include/image.h index 780b624..cf38aec 100644 --- a/include/image.h +++ b/include/image.h @@ -1243,7 +1243,8 @@ struct crypto_algo { * * @info: Specifies key and FIT information * @keydest: Destination FDT blob for public key data - * @return: 0, on success, -ve on error + * @return: node offset within the FDT blob where the data was written, + * or -ve on error */ int (*add_verify_data)(struct image_sign_info *info, void *keydest); diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h index 0ceb0c1..6e0269e 100644 --- a/include/u-boot/ecdsa.h +++ b/include/u-boot/ecdsa.h @@ -44,8 +44,9 @@ int ecdsa_sign(struct image_sign_info *info, const struct image_region region[], * * @info: Specifies key and FIT information * @keydest: Destination FDT blob for public key data - * @return: 0, on success, -ENOSPC if the keydest FDT blob ran out of space, - * other -ve value on error + * @return: node offset within the FDT blob where the data was written on + * success, -ENOSPC if the keydest FDT blob ran out of space, other -ve + * value on other error */ int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest); diff --git a/include/u-boot/rsa.h b/include/u-boot/rsa.h index 2ed2ac7..01b480d 100644 --- a/include/u-boot/rsa.h +++ b/include/u-boot/rsa.h @@ -61,8 +61,9 @@ int rsa_sign(struct image_sign_info *info, * * @info: Specifies key and FIT information * @keydest: Destination FDT blob for public key data - * @return: 0, on success, -ENOSPC if the keydest FDT blob ran out of space, - other -ve value on error + * @return: node offset within the FDT blob where the data was written on + * success, -ENOSPC if the keydest FDT blob ran out of space, other -ve + * value on other error */ int rsa_add_verify_data(struct image_sign_info *info, void *keydest); diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c index ae6dfa0..d5939af 100644 --- a/lib/ecdsa/ecdsa-libcrypto.c +++ b/lib/ecdsa/ecdsa-libcrypto.c @@ -301,7 +301,7 @@ static int do_add(struct signer *ctx, void *fdt, const char *key_node_name) if (ret < 0) return ret; - return 0; + return key_node; } int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt) @@ -313,7 +313,7 @@ int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt) fdt_key_name = info->keyname ? info->keyname : "default-key"; ret = prepare_ctx(&ctx, info); if (ret >= 0) - do_add(&ctx, fdt, fdt_key_name); + ret = do_add(&ctx, fdt, fdt_key_name); free_ctx(&ctx); return ret; diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index a95a3d2..3e7b798 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -703,5 +703,8 @@ err_get_pub_key: if (info->engine_id) rsa_engine_remove(e); - return ret; + if (ret) + return ret; + + return node; } diff --git a/tools/image-host.c b/tools/image-host.c index f13a944..8952091 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -267,7 +267,7 @@ static int fit_image_process_sig(const char *keydir, const char *keyfile, */ if (keydest) { ret = info.crypto->add_verify_data(&info, keydest); - if (ret) { + if (ret < 0) { printf("Failed to add verification data for '%s' signature node in '%s' image node\n", node_name, image_name); return ret; @@ -1037,11 +1037,10 @@ static int fit_config_process_sig(const char *keydir, const char *keyfile, /* Write the public key into the supplied FDT file */ if (keydest) { ret = info.crypto->add_verify_data(&info, keydest); - if (ret) { + if (ret < 0) { printf("Failed to add verification data for '%s' signature node in '%s' configuration node\n", node_name, conf_name); } - return ret; } return 0; -- cgit v1.1 From 9737c2d1ebe0fe61e1f406f7158b97552b6acad2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:12 -0700 Subject: tools: Pass public-key node through to caller Update the two functions that call add_verify_data() so that the caller can see the node that was written to. Signed-off-by: Simon Glass --- tools/image-host.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/image-host.c b/tools/image-host.c index 8952091..030d4eb 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -212,7 +212,8 @@ static int fit_image_setup_sig(struct image_sign_info *info, * @comment: Comment to add to signature nodes * @require_keys: Mark all keys as 'required' * @engine_id: Engine to use for signing - * Return: 0 if ok, -1 on error + * Return: keydest node if @keydest is non-NULL, else 0 if none; -ve error code + * on failure */ static int fit_image_process_sig(const char *keydir, const char *keyfile, void *keydest, void *fit, const char *image_name, @@ -272,6 +273,8 @@ static int fit_image_process_sig(const char *keydir, const char *keyfile, node_name, image_name); return ret; } + /* Return the node that was written to */ + return ret; } return 0; @@ -649,7 +652,7 @@ int fit_image_add_verification_data(const char *keydir, const char *keyfile, comment, require_keys, engine_id, cmdname, algo_name); } - if (ret) + if (ret < 0) return ret; } @@ -978,6 +981,24 @@ static int fit_config_get_regions(const void *fit, int conf_noffset, return 0; } +/** + * fit_config_process_sig - Process a single subnode of the configurations/ node + * + * Generate a signed hash of the supplied data and store it in the node. + * + * @keydir: Directory containing keys to use for signing + * @keydest: Destination FDT blob to write public keys into (NULL if none) + * @fit: pointer to the FIT format image header + * @conf_name name of config being processed (used to display errors) + * @conf_noffset: Offset of configuration node, e.g. '/configurations/conf-1' + * @noffset: subnode offset, e.g. '/configurations/conf-1/sig-1' + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * @engine_id: Engine to use for signing + * @cmdname: Command name used when reporting errors + * @return keydest node if @keydest is non-NULL, else 0 if none; -ve error code + * on failure + */ static int fit_config_process_sig(const char *keydir, const char *keyfile, void *keydest, void *fit, const char *conf_name, int conf_noffset, int noffset, const char *comment, @@ -1041,6 +1062,7 @@ static int fit_config_process_sig(const char *keydir, const char *keyfile, printf("Failed to add verification data for '%s' signature node in '%s' configuration node\n", node_name, conf_name); } + return ret; } return 0; @@ -1070,7 +1092,7 @@ static int fit_config_add_verification_data(const char *keydir, fit, conf_name, conf_noffset, noffset, comment, require_keys, engine_id, cmdname, algo_name); } - if (ret) + if (ret < 0) return ret; } -- cgit v1.1 From 2d2384bbaff0ab84c868b553c74048a5f6acc9e3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 12 Nov 2021 12:28:13 -0700 Subject: tools: mkimage: Show where signatures/keys are written At present mkimage displays the node information but it is not clear what signing action was taken. Add a message that shows it. For now it only supports showing a single signing action, since that is the common case. Sample: Signature written to 'sha1-basic/test.fit', node '/configurations/conf-1/signature' Public key written to 'sha1-basic/sandbox-u-boot.dtb', node '/signature/key-dev' Signed-off-by: Simon Glass --- include/image.h | 23 ++++++++++++++++++++++- tools/fit_common.c | 13 +++++++++++++ tools/fit_common.h | 10 ++++++++++ tools/fit_image.c | 3 ++- tools/image-host.c | 23 ++++++++++++++++++----- tools/imagetool.h | 3 +++ tools/mkimage.c | 4 ++++ 7 files changed, 72 insertions(+), 7 deletions(-) diff --git a/include/image.h b/include/image.h index cf38aec..97e5f2e 100644 --- a/include/image.h +++ b/include/image.h @@ -1021,6 +1021,25 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname); +#define NODE_MAX_NAME_LEN 80 + +/** + * struct image_summary - Provides information about signing info added + * + * @sig_offset: Offset of the node in the blob devicetree where the signature + * was wriiten + * @sig_path: Path to @sig_offset + * @keydest_offset: Offset of the node in the keydest devicetree where the + * public key was written (-1 if none) + * @keydest_path: Path to @keydest_offset + */ +struct image_summary { + int sig_offset; + char sig_path[NODE_MAX_NAME_LEN]; + int keydest_offset; + char keydest_path[NODE_MAX_NAME_LEN]; +}; + /** * fit_add_verification_data() - add verification data to FIT image nodes * @@ -1032,6 +1051,7 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit, * @engine_id: Engine to use for signing * @cmdname: Command name used when reporting errors * @algo_name: Algorithm name, or NULL if to be read from FIT + * @summary: Returns information about what data was written * * Adds hash values for all component images in the FIT blob. * Hashes are calculated for all component images which have hash subnodes @@ -1046,7 +1066,8 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit, int fit_add_verification_data(const char *keydir, const char *keyfile, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, - const char *cmdname, const char *algo_name); + const char *cmdname, const char *algo_name, + struct image_summary *summary); /** * fit_image_verify_with_data() - Verify an image with given data diff --git a/tools/fit_common.c b/tools/fit_common.c index 5ea43f5..0164976 100644 --- a/tools/fit_common.c +++ b/tools/fit_common.c @@ -175,3 +175,16 @@ int copyfile(const char *src, const char *dst) return ret; } + +void summary_show(struct image_summary *summary, const char *imagefile, + const char *keydest) +{ + if (summary->sig_offset) { + printf("Signature written to '%s', node '%s'\n", imagefile, + summary->sig_path); + if (keydest) { + printf("Public key written to '%s', node '%s'\n", + keydest, summary->keydest_path); + } + } +} diff --git a/tools/fit_common.h b/tools/fit_common.h index c600dc2..920a16a 100644 --- a/tools/fit_common.h +++ b/tools/fit_common.h @@ -52,4 +52,14 @@ int mmap_fdt(const char *cmdname, const char *fname, size_t size_inc, */ int copyfile(const char *src, const char *dst); +/** + * summary_show() - Show summary information about the signing process + * + * @summary: Summary info to show + * @imagefile: Filename of the output image + * @keydest: Filename where the key information is written (NULL if none) + */ +void summary_show(struct image_summary *summary, const char *imagefile, + const char *keydest); + #endif /* _FIT_COMMON_H_ */ diff --git a/tools/fit_image.c b/tools/fit_image.c index 0e31f7d..15f7c82 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -74,7 +74,8 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, params->require_keys, params->engine_id, params->cmdname, - params->algo_name); + params->algo_name, + ¶ms->summary); } if (dest_blob) { diff --git a/tools/image-host.c b/tools/image-host.c index 030d4eb..eaeb765 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -1071,7 +1071,8 @@ static int fit_config_process_sig(const char *keydir, const char *keyfile, static int fit_config_add_verification_data(const char *keydir, const char *keyfile, void *keydest, void *fit, int conf_noffset, const char *comment, int require_keys, const char *engine_id, - const char *cmdname, const char *algo_name) + const char *cmdname, const char *algo_name, + struct image_summary *summary) { const char *conf_name; int noffset; @@ -1091,9 +1092,20 @@ static int fit_config_add_verification_data(const char *keydir, ret = fit_config_process_sig(keydir, keyfile, keydest, fit, conf_name, conf_noffset, noffset, comment, require_keys, engine_id, cmdname, algo_name); + if (ret < 0) + return ret; + + summary->sig_offset = noffset; + fdt_get_path(fit, noffset, summary->sig_path, + sizeof(summary->sig_path)); + + if (keydest) { + summary->keydest_offset = ret; + fdt_get_path(keydest, ret, + summary->keydest_path, + sizeof(summary->keydest_path)); + } } - if (ret < 0) - return ret; } return 0; @@ -1137,7 +1149,8 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit, int fit_add_verification_data(const char *keydir, const char *keyfile, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, - const char *cmdname, const char *algo_name) + const char *cmdname, const char *algo_name, + struct image_summary *summary) { int images_noffset, confs_noffset; int noffset; @@ -1186,7 +1199,7 @@ int fit_add_verification_data(const char *keydir, const char *keyfile, fit, noffset, comment, require_keys, engine_id, cmdname, - algo_name); + algo_name, summary); if (ret) return ret; } diff --git a/tools/imagetool.h b/tools/imagetool.h index b7ac3a2..413e97c 100644 --- a/tools/imagetool.h +++ b/tools/imagetool.h @@ -21,6 +21,8 @@ #include #include +#include + #include "fdt_host.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -84,6 +86,7 @@ struct image_tool_params { int bl_len; /* Block length in byte for external data */ const char *engine_id; /* Engine to use for signing */ bool reset_timestamp; /* Reset the timestamp on an existing image */ + struct image_summary summary; /* results of signing process */ }; /* diff --git a/tools/mkimage.c b/tools/mkimage.c index 0ec28da..c8f4ecd 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -10,6 +10,7 @@ #include "imagetool.h" #include "mkimage.h" #include "imximage.h" +#include #include #include #ifdef __linux__ @@ -472,6 +473,9 @@ int main(int argc, char **argv) (void) munmap((void *)ptr, sbuf.st_size); (void) close (ifd); + if (!retval) + summary_show(¶ms.summary, params.imagefile, + params.keydest); exit (retval); } -- cgit v1.1