acpi refactoring to allow non-x86 use

binman support for bintools (binary tools)
 minor tools improvements in preparation for FDT signing
 various minor fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmHyEeYRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreYYlgf8C7tQVklXz9ZHhxVrFaPHWwgmBzEZJSrc
 7SWhDX9yBrAYBUk8Q4OMnJz6nXW8Rz5Td9GfBNWcJag1HjJqno4BxkqwGk2tshgj
 GIY0D1cGfKJzB4E6/rK/ETwmX4KVUyEf7S2IdJ0oydkn9t7OwDkUX5WtGXo70E7q
 ZJGYlX5NQCVBRAF9NYDuyVFljrPzAiyT58VCtXY3q63C7PmRYvag8r56ug+8lkk3
 9EbOTcwETqq9SbT1vucyBxwbUmoKhb2BF32jEcIhozowIM3GNsSpkdzQCUnylqfa
 LYI/p0oVTKLfFRBRvQjLgdB/OQP8MLkby8A++Db2OW49mBXhn/5JWA==
 =PdpE
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-26jan22' of https://source.denx.de/u-boot/custodians/u-boot-dm

acpi refactoring to allow non-x86 use
binman support for bintools (binary tools)
minor tools improvements in preparation for FDT signing
various minor fixes and improvements
This commit is contained in:
Tom Rini 2022-01-27 14:14:47 -05:00
commit 761a1786e1
109 changed files with 4591 additions and 1013 deletions

View file

@ -50,6 +50,12 @@ so much easier [Ed]
Maintainers List (try to look for most precise areas first)
-----------------------------------
ACPI:
M: Simon Glass <sjg@chromium.org>
S: Maintained
F: cmd/acpi.c
F: lib/acpi/
ANDROID AB
M: Igor Opaniuk <igor.opaniuk@gmail.com>
R: Sam Protsenko <joe.skb7@gmail.com>

View file

@ -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

View file

@ -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
@ -152,6 +153,7 @@ config SANDBOX
select SYS_CACHE_SHIFT_4
select IRQ
select SUPPORT_EXTENSION_SCAN
select SUPPORT_ACPI
imply BITREVERSE
select BLOBLIST
imply LTO
@ -201,6 +203,7 @@ config SANDBOX
imply CMD_EXTENSION
imply KEYBOARD
imply PHYSMEM
imply GENERATE_ACPI_TABLE
config SH
bool "SuperH architecture"
@ -217,6 +220,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
@ -254,7 +258,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

View file

@ -4,6 +4,8 @@
* Copyright (c) 2015-2018 Joe Hershberger <joe.hershberger@ni.com>
*/
#define _GNU_SOURCE
#include <asm/eth-raw-os.h>
#include <errno.h>
#include <fcntl.h>

View file

@ -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__ */

View file

@ -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 <asm-generic/global_data.h>

View file

View file

@ -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

View file

@ -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)
{

View file

@ -15,20 +15,24 @@
#include <asm/arch/iomap.h>
#include <dm/uclass-internal.h>
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)
{

View file

@ -10,20 +10,24 @@
#include <asm/arch/global_nvs.h>
#include <asm/arch/iomap.h>
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)
{

View file

@ -16,19 +16,23 @@
#include <asm/arch/iomap.h>
#include <dm/uclass-internal.h>
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)
{
@ -100,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)

View file

@ -24,9 +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);
int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id,
u32 addr, u32 gsi_base);
@ -38,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

View file

@ -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 */
};

View file

@ -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

View file

@ -29,30 +29,6 @@
#include <dm/acpi.h>
#include <linux/err.h>
/*
* 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;
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)
{
@ -152,12 +128,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");
@ -167,13 +147,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)
@ -197,45 +183,29 @@ __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
*
* @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 */
@ -249,14 +219,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)
{
@ -274,14 +249,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.
@ -289,7 +271,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");
@ -298,7 +280,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 */
@ -308,49 +290,24 @@ 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)
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;
@ -358,7 +315,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");
@ -468,225 +428,87 @@ static void acpi_create_spcr(struct acpi_spcr *spcr)
/* Fix checksum */
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);
acpi_add_table(ctx, spcr);
acpi_inc(ctx, spcr->header.length);
return 0;
}
ACPI_WRITER(5spcr, "SPCR", acpi_write_spcr, 0);
/*
* QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
*/
ulong write_acpi_tables(ulong start_addr)
int acpi_write_gnvs(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;
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;
void *start;
int aml_len;
ulong addr;
int ret;
int i;
ctx = calloc(1, 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);
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;
/* 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)) {
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 < 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);
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.
*/
dsdt->checksum = 0;
dsdt->checksum = table_compute_checksum((void *)dsdt, 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));
debug("ACPI: * FADT\n");
fadt = ctx->current;
acpi_inc_align(ctx, sizeof(struct acpi_fadt));
acpi_create_fadt(fadt, facs, dsdt);
acpi_add_table(ctx, fadt);
return 0;
}
ACPI_WRITER(4gnvs, "GNVS", acpi_write_gnvs, 0);
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);
/* 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;
debug("ACPI: * MCFG\n");
mcfg = ctx->current;
acpi_create_mcfg(mcfg);
acpi_inc_align(ctx, mcfg->header.length);
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);
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);
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;
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;
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);
addr = map_to_sysmem(ctx->current);
debug("current = %lx\n", addr);
acpi_rsdp_addr = (unsigned long)ctx->rsdp;
debug("ACPI: done\n");
return addr;
}
ulong acpi_get_rsdp_addr(void)
{
return acpi_rsdp_addr;
return 0;
}
ACPI_WRITER(5mcfg, "MCFG", acpi_write_mcfg, 0);
/**
* acpi_write_hpet() - Write out a HPET table

View file

@ -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,
@ -135,7 +137,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 = "";
@ -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 {
@ -184,34 +186,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++;
@ -226,20 +228,35 @@ 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)
* @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
* -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,
static int fit_config_check_sig(const void *fit, int noffset, int conf_noffset,
const void *key_blob, int required_keynode,
char **err_msgp)
{
static char * const exc_prop[] = {
@ -262,12 +279,12 @@ static int fit_config_check_sig(const void *fit, int 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,
@ -368,8 +385,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,8 +426,9 @@ 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,
conf_noffset, &err_msg);
ret = fit_config_check_sig(fit, noffset, conf_noffset,
key_blob, key_offset,
&err_msg);
if (ret) {
puts("- ");
} else {
@ -409,12 +454,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 +488,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 +546,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());
}

View file

@ -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,

View file

@ -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 {
@ -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);
@ -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);
@ -143,12 +143,13 @@ 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("Name Base Size Detail\n");
printf("---- -------- ----- ------\n");
list_rsdp(rsdp);
return 0;

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
-----------------------

View file

@ -0,0 +1 @@
../../../tools/binman/bintools.rst

235
doc/usage/acpi.rst Normal file
View file

@ -0,0 +1,235 @@
.. SPDX-License-Identifier: GPL-2.0+:
acpi command
============
Synopis
-------
::
acpi list
acpi items [-d]
acpi dump <name>
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

View file

@ -18,6 +18,7 @@ Shell commands
.. toctree::
:maxdepth: 1
acpi
addrmap
askenv
base

View file

@ -12,6 +12,7 @@
#include <dm.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <acpi/acpi_device.h>
#include <dm/acpi.h>
#include <dm/device-internal.h>
@ -19,11 +20,26 @@
#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,
};
const char *gen_type_str[] = {
"-",
"ssdt",
"dsdt",
"other",
};
/* Type of method to call */
@ -42,12 +58,16 @@ typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
*
* @dev: Device that generated this data
* @type: Table type it refers to
* @buf: Buffer containing the data
* @writer: Writer that wrote this table
* @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;
};
@ -103,16 +123,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 +146,18 @@ 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;
item->base = 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,17 +165,28 @@ 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;
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");
}
}
@ -162,7 +199,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;
}
@ -266,19 +303,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 = add_item(ctx, parent, NULL, type, ctx->tab_start);
if (ret)
return log_msg_ret("add", ret);
}

View file

@ -14,11 +14,12 @@
#include <qfw.h>
#include <dm.h>
#include <misc.h>
#include <tables_csum.h>
#ifdef CONFIG_GENERATE_ACPI_TABLE
#include <asm/tables.h>
#endif
#ifdef CONFIG_GENERATE_ACPI_TABLE
#if defined(CONFIG_GENERATE_ACPI_TABLE) && !defined(CONFIG_SANDBOX)
/*
* This function allocates memory for ACPI tables
*

View file

@ -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)

View file

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

View file

@ -147,6 +147,15 @@ config DEFAULT_DEVICE_TREE
It can be overridden from the command line:
$ make DEVICE_TREE=<device-tree-name>
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

View file

@ -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,
@ -678,16 +883,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 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
*/
void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start);
/**
* acpi_write_rsdp() - Write out an RSDP indicating where the ACPI tables are
*
@ -698,6 +893,26 @@ 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);
/**
* 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 <asm/acpi_table.h>

View file

@ -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)

View file

@ -27,6 +27,8 @@
#if !defined(__ACPI__)
#include <linker_lists.h>
struct nhlt;
struct udevice;
@ -43,10 +45,15 @@ 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
* @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
@ -56,14 +63,65 @@ 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;
struct acpi_facs *facs;
struct acpi_table_header *dsdt;
struct nhlt *nhlt;
char *len_stack[ACPIGEN_LENSTACK_SIZE];
int ltop;
};
/**
* 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 = 1 << 0,
};
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, \
}
/* 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
*/
@ -204,6 +262,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
*
@ -236,6 +308,44 @@ 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);
/**
* 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);
/**
* 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

View file

@ -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)

View file

@ -1021,17 +1021,37 @@ 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
*
* @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'
* @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,10 +1066,22 @@ 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
*
* @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);
@ -1232,7 +1264,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);
@ -1297,7 +1330,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 +1338,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 +1348,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 +1358,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,

View file

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

View file

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

View file

@ -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

View file

@ -64,7 +64,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/

View file

@ -1,7 +1,22 @@
# 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
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
obj-y += dsdt.o
endif
obj-y += facs.o
obj-y += ssdt.o
endif

View file

@ -201,88 +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, void *start)
{
ctx->base = start;
ctx->current = start;
/* Align ACPI tables to 16 byte */
acpi_align(ctx);
gd->arch.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));
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((void *)start, '\0', ctx->current - start);
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,

131
lib/acpi/acpi_writer.c Normal file
View file

@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Handles writing the declared ACPI tables
*
* Copyright 2021 Google LLC
*/
#define LOG_CATEGORY LOGC_ACPI
#include <common.h>
#include <log.h>
#include <malloc.h>
#include <mapmem.h>
#include <acpi/acpi_table.h>
#include <asm/global_data.h>
#include <dm/acpi.h>
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);
if (entry->flags & ACPIWF_ALIGN64)
acpi_align64(ctx);
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;
}
#ifndef CONFIG_QEMU
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;
int ret;
ctx = malloc(sizeof(*ctx));
if (!ctx)
return log_msg_ret("mem", -ENOMEM);
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) {
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;
}
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);
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)
{
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);
}

94
lib/acpi/base.c Normal file
View file

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Write base ACPI tables
*
* Copyright 2021 Google LLC
*/
#define LOG_CATEGORY LOGC_ACPI
#include <common.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
#include <mapmem.h>
#include <tables_csum.h>
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);

49
lib/acpi/csrt.c Normal file
View file

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Write an ACPI Core System Resource Table (CSRT)
*
* Copyright 2021 Google LLC
*/
#define LOG_CATEGORY LOGC_ACPI
#include <common.h>
#include <mapmem.h>
#include <tables_csum.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
__weak int acpi_fill_csrt(struct acpi_ctx *ctx)
{
return 0;
}
int acpi_write_csrt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
{
struct acpi_table_header *header;
struct acpi_csrt *csrt;
int ret;
csrt = ctx->current;
header = &csrt->header;
memset(csrt, '\0', sizeof(struct acpi_csrt));
/* Fill out header fields */
acpi_fill_header(header, "CSRT");
header->revision = 0;
acpi_inc(ctx, sizeof(*header));
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);
return 0;
}
ACPI_WRITER(5csrt, "CSRT", acpi_write_csrt, 0);

55
lib/acpi/dsdt.c Normal file
View file

@ -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 <common.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
#include <tables_csum.h>
/*
* 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);

35
lib/acpi/facs.c Normal file
View file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Write an ACPI Firmware ACPI Control Structure (FACS) table
*
* Copyright 2021 Google LLC
*/
#define LOG_CATEGORY LOGC_ACPI
#include <common.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
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);

49
lib/acpi/ssdt.c Normal file
View file

@ -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 <common.h>
#include <acpi/acpi_table.h>
#include <dm/acpi.h>
#include <tables_csum.h>
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);

View file

@ -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;

View file

@ -8,6 +8,7 @@
#include <common.h>
#include <efi_loader.h>
#include <log.h>
#include <mapmem.h>
#include <acpi/acpi_table.h>
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(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,

View file

@ -703,5 +703,8 @@ err_get_pub_key:
if (info->engine_id)
rsa_engine_remove(e);
return ret;
if (ret)
return ret;
return node;
}

View file

@ -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;
}

View file

@ -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) \
@ -461,7 +464,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

View file

@ -45,6 +45,27 @@ 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 unit_test_state *uts,
struct acpi_ctx *ctx, ulong start)
{
struct acpi_writer *entry = ACPI_WRITER_GET(0base);
acpi_setup_ctx(ctx, start);
ctx->tab_start = ctx->current;
ut_assertok(acpi_write_one(ctx, entry));
return 0;
}
static int testacpi_write_tables(const struct udevice *dev,
struct acpi_ctx *ctx)
{
@ -240,13 +261,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);
ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
dmar = ctx.current;
ut_assertok(acpi_write_dev_tables(&ctx));
@ -304,14 +327,15 @@ 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;
struct acpi_xsdt *xsdt;
struct acpi_ctx ctx;
void *buf, *end;
ulong addr;
/*
* Use an unaligned address deliberately, by allocating an aligned
@ -319,8 +343,9 @@ 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);
addr = map_to_sysmem(buf);
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;
ut_asserteq_ptr(rsdp, ctx.rsdp);
@ -349,7 +374,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 */
@ -361,32 +386,33 @@ 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);
ut_assertok(setup_ctx_and_base_tables(uts, &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,
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();
@ -403,7 +429,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);
ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
ut_assertok(acpi_write_dev_tables(&ctx));
@ -540,18 +567,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();
@ -559,16 +590,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();

View file

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

View file

@ -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;
}

View file

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

View file

@ -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 <board_name>
@ -707,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 {
@ -1004,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 <tool>` 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
=================
@ -1138,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
----------------

466
tools/binman/bintool.py Normal file
View file

@ -0,0 +1,466 @@
# 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
@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
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'

View file

@ -0,0 +1,353 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2022 Google LLC
# Written by Simon Glass <sjg@chromium.org>
#
"""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()

115
tools/binman/bintools.rst Normal file
View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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] <command> [<args>]
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()

View file

@ -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

View file

@ -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

140
tools/binman/btool/lz4.py Normal file
View file

@ -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

View file

@ -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 <e|d> inputFile outputFile [<switches>...]
e: encode file
d: decode file
b: Benchmark
<Switches>
-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

View file

@ -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

View file

@ -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,
@ -861,27 +862,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)))

View file

@ -16,8 +16,10 @@ import struct
import tempfile
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
@ -45,18 +47,10 @@ 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:
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):
@ -177,19 +171,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 +217,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 +500,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):

View file

@ -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',
@ -127,6 +129,9 @@ controlled by a description in the board device tree.'''
build_parser.add_argument('--update-fdt-in-elf', type=str,
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)')
@ -167,4 +172,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)

76
tools/binman/comp_util.py Normal file
View file

@ -0,0 +1,76 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2022 Google LLC
#
"""Utilities to compress and decompress data"""
import struct
import tempfile
from binman import bintool
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
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().
Args:
indata (bytes): Input data to compress
algo (str): Algorithm to use ('none', 'lz4' or 'lzma')
Returns:
bytes: Compressed data
"""
if algo == 'none':
return indata
if algo == 'lz4':
data = LZ4.compress(indata)
# cbfstool uses a very old version of lzma
elif algo == 'lzma':
data = LZMA_ALONE.compress(indata)
else:
raise ValueError("Unknown algorithm '%s'" % algo)
if with_header:
hdr = struct.pack('<I', len(data))
data = hdr + data
return data
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
provided by xz.
This requires 'lz4' and 'lzma_alone' tools. It also requires an output
directory to be previously set up, by calling PrepareOutputDir().
Args:
indata (bytes): Input data to decompress
algo (str): Algorithm to use ('none', 'lz4' or 'lzma')
Returns:
(bytes) Compressed data
"""
if algo == 'none':
return indata
if with_header:
data_len = struct.unpack('<I', indata[:4])[0]
indata = indata[4:4 + data_len]
if algo == 'lz4':
data = LZ4.decompress(indata)
elif algo == 'lzma':
data = LZMA_ALONE.decompress(indata)
else:
raise ValueError("Unknown algorithm '%s'" % algo)
return data

View file

@ -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
@ -139,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.
"""
@ -147,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
@ -487,6 +500,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)
@ -578,11 +592,17 @@ 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])))
return bool(missing_list) or bool(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])))
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):
@ -607,7 +627,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)
@ -622,6 +642,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:
@ -674,6 +707,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,

View file

@ -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"""

View file

@ -10,6 +10,8 @@ import os
import pathlib
import sys
from binman import bintool
from binman import comp_util
from dtoc import fdt_util
from patman import tools
from patman.tools import ToHex, ToHexSize
@ -74,6 +76,8 @@ 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)
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
@ -105,6 +109,8 @@ class Entry(object):
self.external = False
self.allow_missing = False
self.allow_fake = False
self.bintools = {}
self.missing_bintools = []
@staticmethod
def FindEntryClass(etype, expanded):
@ -960,7 +966,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
@ -1011,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
@ -1031,7 +1055,7 @@ features to produce new behaviours.
self.uncomp_data = indata
if self.compress != 'none':
self.uncomp_size = len(indata)
data = tools.Compress(indata, self.compress)
data = comp_util.compress(indata, self.compress)
return data
@classmethod
@ -1065,3 +1089,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

View file

@ -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

View file

@ -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')

View file

@ -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)
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))
self.SetContents(tools.ReadFile(fname))
return True
def AddBintools(self, tools):
self.futility = self.AddBintool(tools, 'futility')

View file

@ -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')

View file

@ -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')

View file

@ -13,6 +13,7 @@ import concurrent.futures
import re
import sys
from binman import comp_util
from binman.entry import Entry
from binman import state
from dtoc import fdt_util
@ -775,7 +776,7 @@ class Entry_section(Entry):
data = parent_data[offset:offset + child.size]
if decomp:
indata = data
data = tools.Decompress(indata, child.compress)
data = comp_util.decompress(indata, child.compress)
if child.uncomp_size:
tout.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" %
(child.GetPath(), len(indata), child.compress,
@ -805,6 +806,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)
@ -830,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
@ -879,3 +892,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)

View file

@ -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')

View file

@ -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

View file

@ -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
import fip_util
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__':

View file

@ -17,9 +17,13 @@ import struct
import sys
import tempfile
import unittest
import unittest.mock
import urllib.error
from binman import bintool
from binman import cbfs_util
from binman import cmdline
from binman import comp_util
from binman import control
from binman import elf
from binman import elf_test
@ -193,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):
@ -312,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:
@ -341,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
@ -375,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:
@ -1715,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
@ -1800,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
@ -1923,7 +1951,7 @@ class TestFunctional(unittest.TestCase):
self._ResetDtbs()
def _decompress(self, data):
return tools.Decompress(data, 'lz4')
return comp_util.decompress(data, 'lz4')
def testCompress(self):
"""Test compression of blobs"""
@ -2311,8 +2339,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)])
@ -2337,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
@ -2802,7 +2840,7 @@ class TestFunctional(unittest.TestCase):
def testExtractCbfsRaw(self):
"""Test extracting CBFS compressed data without decompressing it"""
data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
dtb = tools.Decompress(data, 'lzma', with_header=False)
dtb = comp_util.decompress(data, 'lzma', with_header=False)
self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
def testExtractBadEntry(self):
@ -3616,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')
@ -3713,11 +3760,37 @@ 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 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"""
@ -4212,13 +4285,13 @@ class TestFunctional(unittest.TestCase):
# Check compressed data
section1 = self._decompress(rest)
expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
expect1 = comp_util.compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
self.assertEquals(expect1, rest[:len(expect1)])
self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
rest1 = rest[len(expect1):]
section2 = self._decompress(rest1)
expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
expect2 = comp_util.compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
self.assertEquals(expect2, rest1[:len(expect2)])
self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
rest2 = rest1[len(expect2):]
@ -4965,6 +5038,68 @@ 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")
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())
def testInvalidCompress(self):
with self.assertRaises(ValueError) as e:
comp_util.compress(b'', 'invalid')
self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
with self.assertRaises(ValueError) as e:
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()

View file

@ -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

View file

@ -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
@ -68,6 +69,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 +87,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)
@ -128,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())

View file

@ -10,7 +10,7 @@
u-boot {
};
fit {
fit,external-offset = <0>;
fit,external-offset = <0x400>;
description = "test-desc";
#address-cells = <1>;

View file

@ -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";
};
};
};

View file

@ -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)
tarfile, tmpdir = tools.Download(url, '.buildman')
if not tarfile:
return 1
print(col.Color(col.GREEN, 'Unpacking to: %s' % dest), end=' ')

Some files were not shown because too many files have changed in this diff Show more