From e9879c0fbd57f105254c54bacb3e592acdcad35c Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Mon, 10 Nov 2014 13:14:24 -0800 Subject: [PATCH] CBFS: Automate ROM image layout and remove hardcoded offsets Non-x86 boards currently need to hardcode the position of their CBFS master header in a Kconfig. This is very brittle because it is usually put in between the bootblock and the first CBFS entry, without any checks to guarantee that it won't overlap either of those. It is not fun to debug random failures that move and disappear with tiny alignment changes because someone decided to write "ORBC1112" over some part of your data section (in a way that is not visible in the symbolized .elf binaries, only in the final image). This patch seeks to prevent those issues and reduce the need for manual configuration by making the image layout a completely automated part of cbfstool. Since automated placement of the CBFS header means we can no longer hardcode its position into coreboot, this patch takes the existing x86 solution of placing a pointer to the header at the very end of the CBFS-managed section of the ROM and generalizes it to all architectures. This is now even possible with the read-only/read-write split in ChromeOS, since coreboot knows how large that section is from the CBFS_SIZE Kconfig (which is by default equal to ROM_SIZE, but can be changed on systems that place other data next to coreboot/CBFS in ROM). Also adds a feature to cbfstool that makes the -B (bootblock file name) argument on image creation optional, since we have recently found valid use cases for CBFS images that are not the first boot medium of the device (instead opened by an earlier bootloader that can already interpret CBFS) and therefore don't really need a bootblock. BRANCH=None BUG=None TEST=Built and booted on Veyron_Pinky, Nyan_Blaze and Falco. Change-Id: Ifcc755326832755cfbccd6f0a12104cba28a20af Signed-off-by: Julius Werner Reviewed-on: https://chromium-review.googlesource.com/229975 --- payloads/libpayload/include/cbfs_core.h | 7 +- payloads/libpayload/libcbfs/cbfs.c | 13 -- payloads/libpayload/libcbfs/cbfs_core.c | 27 ++-- src/arch/arm/Makefile.inc | 4 +- src/arch/arm64/Makefile.inc | 4 +- src/arch/mips/Makefile.inc | 4 +- src/cpu/mips/Kconfig | 5 - src/include/cbfs_core.h | 7 +- src/lib/cbfs.c | 7 -- src/lib/cbfs_core.c | 33 +++-- .../emulation/foundation-armv8/Kconfig | 12 -- src/mainboard/emulation/qemu-armv7/Kconfig | 12 -- src/soc/imgtec/pistachio/Kconfig | 9 -- src/soc/marvell/bg4cd/Kconfig | 12 -- src/soc/nvidia/tegra124/Kconfig | 12 -- src/soc/nvidia/tegra132/Kconfig | 12 -- src/soc/qualcomm/ipq806x/Kconfig | 12 -- src/soc/rockchip/rk3288/Kconfig | 18 --- src/soc/samsung/exynos5250/Kconfig | 21 ---- src/soc/samsung/exynos5420/Kconfig | 21 ---- util/cbfstool/cbfs_image.c | 54 ++++---- util/cbfstool/cbfstool.c | 118 +++++++++++------- util/cbfstool/common.h | 7 ++ 23 files changed, 151 insertions(+), 280 deletions(-) diff --git a/payloads/libpayload/include/cbfs_core.h b/payloads/libpayload/include/cbfs_core.h index 1ea322abc3..0408ba1f59 100644 --- a/payloads/libpayload/include/cbfs_core.h +++ b/payloads/libpayload/include/cbfs_core.h @@ -85,10 +85,9 @@ #define CBFS_HEADER_INVALID_ADDRESS ((void*)(0xffffffff)) -/** this is the master cbfs header - it needs to be located somewhere available - to bootblock (to load romstage). Where it actually lives is up to coreboot. - On x86, a pointer to this header will live at 0xFFFFFFFC. - For other platforms, you need to define CONFIG_LP_CBFS_HEADER_ROM_OFFSET */ +/* this is the master cbfs header - it must be located somewhere available + * to bootblock (to load romstage). The last 4 bytes in the image contain its + * relative offset from the end of the image (as a 32-bit signed integer). */ struct cbfs_header { uint32_t magic; diff --git a/payloads/libpayload/libcbfs/cbfs.c b/payloads/libpayload/libcbfs/cbfs.c index acb6b3c2d5..806299f299 100644 --- a/payloads/libpayload/libcbfs/cbfs.c +++ b/payloads/libpayload/libcbfs/cbfs.c @@ -62,19 +62,6 @@ # endif #endif -#if defined(CONFIG_LP_CBFS_HEADER_ROM_OFFSET) && (CONFIG_LP_CBFS_HEADER_ROM_OFFSET) -# define CBFS_HEADER_ROM_ADDRESS (CONFIG_LP_CBFS_HEADER_ROM_OFFSET) -#else -/* ugly hack: this assumes that "media" exists - in the scope where the macro is used. */ -static uint32_t fetch_x86_header(struct cbfs_media *media) -{ - uint32_t *header_ptr = media->map(media, 0xfffffffc, 4); - return *header_ptr; -} -# define CBFS_HEADER_ROM_ADDRESS fetch_x86_header(media) -#endif - #include "cbfs_core.c" #ifndef __SMM__ diff --git a/payloads/libpayload/libcbfs/cbfs_core.c b/payloads/libpayload/libcbfs/cbfs_core.c index f3ee45e847..4bb8dca285 100644 --- a/payloads/libpayload/libcbfs/cbfs_core.c +++ b/payloads/libpayload/libcbfs/cbfs_core.c @@ -34,10 +34,6 @@ * CBFS_CORE_WITH_LZMA (must be #define) * if defined, ulzma() must exist for decompression of data streams * - * CBFS_HEADER_ROM_ADDRESS - * ROM address (offset) of CBFS header. Underlying CBFS media may interpret - * it in other way so we call this "address". - * * ERROR(x...) * print an error message x (in printf format) * @@ -56,6 +52,7 @@ * on failure */ const struct cbfs_header *cbfs_get_header(struct cbfs_media *media) { + int32_t rel_offset; const struct cbfs_header *header; struct cbfs_media default_media; @@ -66,22 +63,28 @@ const struct cbfs_header *cbfs_get_header(struct cbfs_media *media) return CBFS_HEADER_INVALID_ADDRESS; } } - media->open(media); - DEBUG("CBFS_HEADER_ROM_ADDRESS: 0x%x/0x%x\n", CBFS_HEADER_ROM_ADDRESS, - CONFIG_LP_ROM_SIZE); - header = media->map(media, CBFS_HEADER_ROM_ADDRESS, sizeof(*header)); + + if (!media->read(media, &rel_offset, (size_t)(0 - sizeof(int32_t)), + sizeof(int32_t))) { + ERROR("Could not read CBFS master header offset!\n"); + return CBFS_HEADER_INVALID_ADDRESS; + } + header = media->map(media, (size_t)rel_offset, sizeof(*header)); + DEBUG("CBFS header at %#zx (-%#zx from end of image).\n", + (size_t)rel_offset, (size_t)-rel_offset); media->close(media); if (header == CBFS_MEDIA_INVALID_MAP_ADDRESS) { - ERROR("Failed to load CBFS header from 0x%x\n", - CBFS_HEADER_ROM_ADDRESS); + ERROR("Failed to load CBFS header from %#zx(-%#zx)\n", + (size_t)rel_offset, (size_t)-rel_offset); return CBFS_HEADER_INVALID_ADDRESS; } if (CBFS_HEADER_MAGIC != ntohl(header->magic)) { - ERROR("Could not find valid CBFS master header at %x: " - "%x vs %x.\n", CBFS_HEADER_ROM_ADDRESS, CBFS_HEADER_MAGIC, + ERROR("Could not find valid CBFS master header at %#zx(-%#zx): " + "magic %#.8x vs %#.8x.\n", (size_t)rel_offset, + (size_t)-rel_offset, CBFS_HEADER_MAGIC, ntohl(header->magic)); if (header->magic == 0xffffffff) { ERROR("Maybe ROM is not mapped properly?\n"); diff --git a/src/arch/arm/Makefile.inc b/src/arch/arm/Makefile.inc index a7d85fe8f6..39f5f56e33 100644 --- a/src/arch/arm/Makefile.inc +++ b/src/arch/arm/Makefile.inc @@ -33,9 +33,7 @@ subdirs-y += armv4/ armv7/ ############################################################################### ifeq ($(CONFIG_ARCH_ROMSTAGE_ARM),y) -CBFSTOOL_PRE1_OPTS = -m arm -b $(CONFIG_BOOTBLOCK_ROM_OFFSET) \ - -H $(CONFIG_CBFS_HEADER_ROM_OFFSET) \ - -o $(CONFIG_CBFS_ROM_OFFSET) -s $(CONFIG_CBFS_SIZE) +CBFSTOOL_PRE1_OPTS = -m arm -s $(CONFIG_CBFS_SIZE) endif ############################################################################### diff --git a/src/arch/arm64/Makefile.inc b/src/arch/arm64/Makefile.inc index e1e8ee71c2..67c7d2668f 100644 --- a/src/arch/arm64/Makefile.inc +++ b/src/arch/arm64/Makefile.inc @@ -34,9 +34,7 @@ subdirs-y += armv8/ ################################################################################ ifeq ($(CONFIG_ARCH_ROMSTAGE_ARM64),y) -CBFSTOOL_PRE1_OPTS = -m arm64 -b $(CONFIG_BOOTBLOCK_ROM_OFFSET) \ - -H $(CONFIG_CBFS_HEADER_ROM_OFFSET) \ - -o $(CONFIG_CBFS_ROM_OFFSET) -s $(CONFIG_CBFS_SIZE) +CBFSTOOL_PRE1_OPTS = -m arm64 -s $(CONFIG_CBFS_SIZE) endif ################################################################################ diff --git a/src/arch/mips/Makefile.inc b/src/arch/mips/Makefile.inc index 39bb05e17a..d0014a2620 100644 --- a/src/arch/mips/Makefile.inc +++ b/src/arch/mips/Makefile.inc @@ -24,9 +24,7 @@ ############################################################################### ifeq ($(CONFIG_ARCH_ROMSTAGE_MIPS),y) -CBFSTOOL_PRE1_OPTS = -m mips -b $(CONFIG_BOOTBLOCK_ROM_OFFSET) \ - -H $(CONFIG_CBFS_HEADER_ROM_OFFSET) \ - -o $(CONFIG_CBFS_ROM_OFFSET) -s $(CONFIG_CBFS_SIZE) +CBFSTOOL_PRE1_OPTS = -m mips -s $(CONFIG_CBFS_SIZE) endif ############################################################################### diff --git a/src/cpu/mips/Kconfig b/src/cpu/mips/Kconfig index 22ac379d16..f3aae85f99 100644 --- a/src/cpu/mips/Kconfig +++ b/src/cpu/mips/Kconfig @@ -24,8 +24,3 @@ config CPU_MIPS select ARCH_BOOTBLOCK_MIPS select ARCH_ROMSTAGE_MIPS select ARCH_RAMSTAGE_MIPS - -config BOOTBLOCK_ROM_OFFSET - hex - depends on CPU_MIPS - default 0x00 diff --git a/src/include/cbfs_core.h b/src/include/cbfs_core.h index 199bce17e0..aaf5c67eb6 100644 --- a/src/include/cbfs_core.h +++ b/src/include/cbfs_core.h @@ -86,10 +86,9 @@ #define CBFS_HEADER_INVALID_ADDRESS ((void*)(0xffffffff)) -/** this is the master cbfs header - it need to be located somewhere available - to bootblock (to load romstage). Where it actually lives is up to coreboot. - On x86, a pointer to this header will live at 0xFFFFFFFC. - For other platforms, you need to define CONFIG_CBFS_HEADER_ROM_OFFSET */ +/* this is the master cbfs header - it must be located somewhere available + * to bootblock (to load romstage). The last 4 bytes in the image contain its + * relative offset from the end of the image (as a 32-bit signed integer). */ struct cbfs_header { uint32_t magic; diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 4e74a09ff7..4c6425f692 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -54,13 +54,6 @@ # endif #endif -#if defined(CONFIG_CBFS_HEADER_ROM_OFFSET) && (CONFIG_CBFS_HEADER_ROM_OFFSET) -# define CBFS_HEADER_ROM_ADDRESS (CONFIG_CBFS_HEADER_ROM_OFFSET) -#else -// Indirect address: only works on 32bit top-aligned systems. -# define CBFS_HEADER_ROM_ADDRESS (*(uint32_t *)0xfffffffc) -#endif - #include "cbfs_core.c" #include diff --git a/src/lib/cbfs_core.c b/src/lib/cbfs_core.c index f2e840baff..c28afda263 100644 --- a/src/lib/cbfs_core.c +++ b/src/lib/cbfs_core.c @@ -34,10 +34,6 @@ * CBFS_CORE_WITH_LZMA (must be #define) * if defined, ulzma() must exist for decompression of data streams * - * CBFS_HEADER_ROM_ADDRESS - * ROM address (offset) of CBFS header. Underlying CBFS media may interpret - * it in other way so we call this "address". - * * ERROR(x...) * print an error message x (in printf format) * @@ -56,6 +52,7 @@ * on failure */ const struct cbfs_header *cbfs_get_header(struct cbfs_media *media) { + size_t offset; const struct cbfs_header *header; struct cbfs_media default_media; @@ -66,22 +63,34 @@ const struct cbfs_header *cbfs_get_header(struct cbfs_media *media) return CBFS_HEADER_INVALID_ADDRESS; } } - media->open(media); - DEBUG("CBFS_HEADER_ROM_ADDRESS: 0x%x/0x%x\n", CBFS_HEADER_ROM_ADDRESS, - CONFIG_ROM_SIZE); - header = media->map(media, CBFS_HEADER_ROM_ADDRESS, sizeof(*header)); + + /* TODO: allow negative offsets from the end of the CBFS image at media + * layer (like libpayload) so we can combine these two cases. */ + if (IS_ENABLED(CONFIG_ARCH_X86)) { + offset = *(int32_t *)(uintptr_t)0xfffffffc; + header = media->map(media, offset, sizeof(*header)); + } else { + int32_t rel_offset; + if (!media->read(media, &rel_offset, CONFIG_CBFS_SIZE - + sizeof(int32_t), sizeof(int32_t))) { + ERROR("Could not read CBFS master header offset!\n"); + return CBFS_HEADER_INVALID_ADDRESS; + } + offset = CONFIG_CBFS_SIZE + rel_offset; + header = media->map(media, offset, sizeof(*header)); + } + DEBUG("CBFS header offset: 0x%zx/0x%x\n", offset, CONFIG_ROM_SIZE); media->close(media); if (header == CBFS_MEDIA_INVALID_MAP_ADDRESS) { - ERROR("Failed to load CBFS header from 0x%x\n", - CBFS_HEADER_ROM_ADDRESS); + ERROR("Failed to load CBFS header from 0x%zx\n", offset); return CBFS_HEADER_INVALID_ADDRESS; } if (CBFS_HEADER_MAGIC != ntohl(header->magic)) { - ERROR("Could not find valid CBFS master header at %x: " - "%x vs %x.\n", CBFS_HEADER_ROM_ADDRESS, CBFS_HEADER_MAGIC, + ERROR("Could not find valid CBFS master header at %#zx: " + "magic %#.8x vs %#.8x.\n", offset, CBFS_HEADER_MAGIC, ntohl(header->magic)); if (header->magic == 0xffffffff) { ERROR("Maybe ROM is not mapped properly?\n"); diff --git a/src/mainboard/emulation/foundation-armv8/Kconfig b/src/mainboard/emulation/foundation-armv8/Kconfig index 55fce38880..ba9657d404 100644 --- a/src/mainboard/emulation/foundation-armv8/Kconfig +++ b/src/mainboard/emulation/foundation-armv8/Kconfig @@ -50,16 +50,4 @@ config DRAM_SIZE_MB int default 1024 -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex - default 0x0100000 - -config CBFS_ROM_OFFSET - hex - default 0x0110000 - endif # BOARD_EMULATION_FOUNDATION_ARMV8 diff --git a/src/mainboard/emulation/qemu-armv7/Kconfig b/src/mainboard/emulation/qemu-armv7/Kconfig index d47fbf94ea..8ddf51fec0 100644 --- a/src/mainboard/emulation/qemu-armv7/Kconfig +++ b/src/mainboard/emulation/qemu-armv7/Kconfig @@ -54,16 +54,4 @@ config DRAM_SIZE_MB int default 1024 -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex - default 0x0100000 - -config CBFS_ROM_OFFSET - hex - default 0x0110000 - endif # BOARD_EMULATION_QEMU_ARMV7 diff --git a/src/soc/imgtec/pistachio/Kconfig b/src/soc/imgtec/pistachio/Kconfig index 2a10e03684..5092ba5386 100644 --- a/src/soc/imgtec/pistachio/Kconfig +++ b/src/soc/imgtec/pistachio/Kconfig @@ -37,13 +37,4 @@ config BOOTBLOCK_CPU_INIT string default "soc/imgtec/pistachio/bootblock.c" -config CBFS_ROM_OFFSET - hex - default 0x8100 - -config CBFS_HEADER_ROM_OFFSET - # Effectively the maximum size of the bootblock - hex - default 0x8000 - endif diff --git a/src/soc/marvell/bg4cd/Kconfig b/src/soc/marvell/bg4cd/Kconfig index 3d4f75cb90..d8e589d165 100644 --- a/src/soc/marvell/bg4cd/Kconfig +++ b/src/soc/marvell/bg4cd/Kconfig @@ -39,16 +39,4 @@ config BOOTBLOCK_CPU_INIT string default "soc/marvell/bg4cd/bootblock.c" -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex - default 0x0008000 - -config CBFS_ROM_OFFSET - hex - default 0x0018000 - endif diff --git a/src/soc/nvidia/tegra124/Kconfig b/src/soc/nvidia/tegra124/Kconfig index 0fa53ee9bc..c7e5ce362a 100644 --- a/src/soc/nvidia/tegra124/Kconfig +++ b/src/soc/nvidia/tegra124/Kconfig @@ -28,18 +28,6 @@ config BOOTBLOCK_CPU_INIT bootblock must load microcode or copy data from ROM before searching for the bootblock. -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex "offset of master CBFS header in ROM" - default 0x18000 - -config CBFS_ROM_OFFSET - hex "offset of CBFS data in ROM" - default 0x18080 - choice CONSOLE_SERIAL_TEGRA124_UART_CHOICES prompt "Serial Console UART" default CONSOLE_SERIAL_UARTA diff --git a/src/soc/nvidia/tegra132/Kconfig b/src/soc/nvidia/tegra132/Kconfig index 2e5a859dd6..f655ff7744 100644 --- a/src/soc/nvidia/tegra132/Kconfig +++ b/src/soc/nvidia/tegra132/Kconfig @@ -31,22 +31,10 @@ config BOOTBLOCK_CPU_INIT bootblock must load microcode or copy data from ROM before searching for the bootblock. -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - config MAX_CPUS int default 2 -config CBFS_HEADER_ROM_OFFSET - hex "offset of master CBFS header in ROM" - default 0x22000 - -config CBFS_ROM_OFFSET - hex "offset of CBFS data in ROM" - default 0x22080 - choice CONSOLE_SERIAL_TEGRA132_UART_CHOICES prompt "Serial Console UART" default CONSOLE_SERIAL_TEGRA132_UARTA diff --git a/src/soc/qualcomm/ipq806x/Kconfig b/src/soc/qualcomm/ipq806x/Kconfig index ebb6ab87b6..f0ae7b85b5 100644 --- a/src/soc/qualcomm/ipq806x/Kconfig +++ b/src/soc/qualcomm/ipq806x/Kconfig @@ -13,18 +13,6 @@ config SOC_QC_IPQ806X if SOC_QC_IPQ806X -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex "offset of master CBFS header in ROM" - default 0x1b4000 - -config CBFS_ROM_OFFSET - hex "offset of CBFS data in ROM" - default 0x1b4080 - config MBN_ENCAPSULATION depends on USE_BLOBS bool "bootblock encapsulation for ipq8064" diff --git a/src/soc/rockchip/rk3288/Kconfig b/src/soc/rockchip/rk3288/Kconfig index a36a861dfc..bb4853106b 100644 --- a/src/soc/rockchip/rk3288/Kconfig +++ b/src/soc/rockchip/rk3288/Kconfig @@ -41,22 +41,4 @@ config BOOTBLOCK_CPU_INIT string default "soc/rockchip/rk3288/bootblock.c" -# ROM image layout. -# -# 0x00000 Combined bootblock and ID Block -# 0x08000 Master CBFS header. -# 0x18000 Free for CBFS data. - -config BOOTBLOCK_ROM_OFFSET - hex - default 0x0 - -config CBFS_HEADER_ROM_OFFSET - hex - default 0x0010000 - -config CBFS_ROM_OFFSET - hex - default 0x0010100 - endif diff --git a/src/soc/samsung/exynos5250/Kconfig b/src/soc/samsung/exynos5250/Kconfig index 1964e3a992..11a451ac2f 100644 --- a/src/soc/samsung/exynos5250/Kconfig +++ b/src/soc/samsung/exynos5250/Kconfig @@ -13,27 +13,6 @@ config CPU_SAMSUNG_EXYNOS5250 if CPU_SAMSUNG_EXYNOS5250 -# ROM image layout. -# -# 0x0000: vendor-provided BL1 (8k). -# 0x2000: bootblock -# 0x9FFC-0xA000: BL2 checksum -# 0xA000-0xA080: reserved for CBFS master header. -# 0xA080: Free for CBFS data. - -config BOOTBLOCK_ROM_OFFSET - hex - default 0 - -config CBFS_HEADER_ROM_OFFSET - hex "offset of master CBFS header in ROM" - default 0x9F80 - -config CBFS_ROM_OFFSET - # Calculated by BOOTBLOCK_ROM_OFFSET + max bootblock size. - hex "offset of CBFS data in ROM" - default 0x0A080 - choice CONSOLE_SERIAL_UART_CHOICES prompt "Serial Console UART" default CONSOLE_SERIAL_UART3 diff --git a/src/soc/samsung/exynos5420/Kconfig b/src/soc/samsung/exynos5420/Kconfig index 0fd4ef2531..4641e4a1dd 100644 --- a/src/soc/samsung/exynos5420/Kconfig +++ b/src/soc/samsung/exynos5420/Kconfig @@ -14,27 +14,6 @@ config CPU_SAMSUNG_EXYNOS5420 if CPU_SAMSUNG_EXYNOS5420 -# ROM image layout. -# -# 0x0000: vendor-provided BL1 (8k). -# 0x2000: variable length bootblock checksum header -# 0x2010: bootblock -# 0x9F80-0xA000: reserved for CBFS master header. -# 0xA000: Free for CBFS data. - -config BOOTBLOCK_ROM_OFFSET - hex - default 0 - -config CBFS_HEADER_ROM_OFFSET - hex "offset of master CBFS header in ROM" - default 0x9F80 - -config CBFS_ROM_OFFSET - # Calculated by BOOTBLOCK_ROM_OFFSET + max bootblock size. - hex "offset of CBFS data in ROM" - default 0x0A000 - choice CONSOLE_SERIAL_UART_CHOICES prompt "Serial Console UART" default CONSOLE_SERIAL_UART3 diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index d3d7cc3228..02285039d6 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -62,13 +62,6 @@ static const struct typedesc_t types_cbfs_compression[] = { {0, NULL}, }; -static uint32_t align_up(uint32_t value, uint32_t align) -{ - if (value % align) - value += align - (value % align); - return value; -} - static const char *lookup_name_by_type(const struct typedesc_t *desc, uint32_t type, const char *default_value) { @@ -161,6 +154,7 @@ int cbfs_image_create(struct cbfs_image *image, { struct cbfs_header header; struct cbfs_file *entry; + int32_t *rel_offset; uint32_t cbfs_len; size_t entry_header_len; @@ -186,9 +180,6 @@ int cbfs_image_create(struct cbfs_image *image, "header=0x%x, entries_offset=0x%x\n", bootblock_offset, header_offset, entries_offset); - if (align == 0) - align = 64; // default align size. - // Prepare bootblock if (bootblock_offset + bootblock->size > size) { ERROR("Bootblock (0x%x+0x%zx) exceed ROM size (0x%zx)\n", @@ -205,7 +196,7 @@ int cbfs_image_create(struct cbfs_image *image, bootblock->size); // Prepare header - if (header_offset + sizeof(header) > size) { + if (header_offset + sizeof(header) > size - sizeof(int32_t)) { ERROR("Header (0x%x+0x%zx) exceed ROM size (0x%zx)\n", header_offset, sizeof(header), size); return -1; @@ -220,6 +211,14 @@ int cbfs_image_create(struct cbfs_image *image, header.architecture = architecture; cbfs_put_header(image->header, &header); + // The last 4 byte of the image contain the relative offset from the end + // of the image to the master header as a 32-bit signed integer. x86 + // relies on this also being its (memory-mapped, top-aligned) absolute + // 32-bit address by virtue of how two's complement numbers work. + assert(size % sizeof(int32_t) == 0); + rel_offset = (int32_t *)(image->buffer.data + size - sizeof(int32_t)); + *rel_offset = header_offset - size; + // Prepare entries if (align_up(entries_offset, align) != entries_offset) { ERROR("Offset (0x%x) must be aligned to 0x%x.\n", @@ -234,8 +233,8 @@ int cbfs_image_create(struct cbfs_image *image, } entry = (struct cbfs_file *)(image->buffer.data + entries_offset); // To calculate available length, find - // e = min(bootblock, header, size) where e > entries_offset. - cbfs_len = size; + // e = min(bootblock, header, rel_offset) where e > entries_offset. + cbfs_len = size - sizeof(int32_t); if (bootblock_offset > entries_offset && bootblock_offset < cbfs_len) cbfs_len = bootblock_offset; if (header_offset > entries_offset && header_offset < cbfs_len) @@ -742,17 +741,21 @@ struct cbfs_header *cbfs_find_header(char *data, size_t size) { size_t offset; int found = 0; - uint32_t x86sig; + int32_t rel_offset; struct cbfs_header *header, *result = NULL; - // Try x86 style (check signature in bottom) header first. - x86sig = *(uint32_t *)(data + size - sizeof(uint32_t)); - offset = (x86sig + (uint32_t)size); - DEBUG("x86sig: 0x%x, offset: 0x%zx\n", x86sig, offset); + // Try finding relative offset of master header at end of file first. + rel_offset = *(int32_t *)(data + size - sizeof(int32_t)); + offset = size + rel_offset; + DEBUG("relative offset: %#zx(-%#zx), offset: %#zx\n", + (size_t)rel_offset, (size_t)-rel_offset, offset); if (offset >= size - sizeof(*header) || ntohl(((struct cbfs_header *)(data + offset))->magic) != - CBFS_HEADER_MAGIC) + CBFS_HEADER_MAGIC) { + // Some use cases append non-CBFS data to the end of the ROM. + DEBUG("relative offset seems wrong, scanning whole image...\n"); offset = 0; + } for (; offset + sizeof(*header) < size; offset++) { header = (struct cbfs_header *)(data + offset); @@ -763,14 +766,15 @@ struct cbfs_header *cbfs_find_header(char *data, size_t size) // Probably not a real CBFS header? continue; } - found++; - result = header; + if (!found++) + result = header; } - if (found > 1) { - ERROR("multiple (%d) CBFS headers found!\n", + if (found > 1) + // Top-aligned images usually have a working relative offset + // field, so this is more likely to happen on bottom-aligned + // ones (where the first header is the "outermost" one) + WARN("Multiple (%d) CBFS headers found, using the first one.\n", found); - result = NULL; - } return result; } diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index d25bb5feb2..973ab0ce7b 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -52,7 +52,8 @@ static struct param { uint32_t size; uint32_t alignment; uint32_t pagesize; - uint32_t offset; + uint32_t cbfsoffset; + uint32_t cbfsoffset_assigned; uint32_t top_aligned; int fit_empty_entries; comp_algo algo; @@ -283,41 +284,59 @@ static int cbfs_create(void) return 1; } - if (!param.bootblock) { - ERROR("You need to specify -B/--bootblock.\n"); - return 1; - } - // TODO Remove arch or pack into param. if (arch == CBFS_ARCHITECTURE_UNKNOWN) { ERROR("You need to specify -m/--machine arch.\n"); return 1; } - if (buffer_from_file(&bootblock, param.bootblock) != 0) { + if (!param.bootblock) { + DEBUG("-B not given, creating image without bootblock.\n"); + buffer_create(&bootblock, 0, "(dummy)"); + } else if (buffer_from_file(&bootblock, param.bootblock)) { return 1; } - // Setup default boot offset and header offset. + if (!param.alignment) + param.alignment = 64; // default CBFS entry alignment + + // Set default offsets. x86, as usual, needs to be a special snowflake. if (!param.baseaddress_assigned) { - // put boot block before end of ROM. - param.baseaddress = param.size - bootblock.size; - DEBUG("bootblock in end of ROM.\n"); + if (arch == CBFS_ARCHITECTURE_X86) { + // Make sure there's at least enough room for rel_offset + param.baseaddress = param.size - ( + bootblock.size > sizeof(int32_t) ? + bootblock.size : sizeof(int32_t)); + DEBUG("x86 -> bootblock lies at end of ROM (%#x).\n", + param.baseaddress); + } else { + param.baseaddress = 0; + DEBUG("bootblock starts at address 0x0.\n"); + } } if (!param.headeroffset_assigned) { - // Put header before bootblock, and make a reference in end of - // bootblock. - param.headeroffset = ( - param.baseaddress - - sizeof(struct cbfs_header)); - if (bootblock.size >= sizeof(uint32_t)) { - // TODO this only works for 32b top-aligned system now... - uint32_t ptr = param.headeroffset - param.size; - uint32_t *sig = (uint32_t *)(bootblock.data + - bootblock.size - - sizeof(ptr)); - *sig = ptr; - DEBUG("CBFS header reference in end of bootblock.\n"); + if (arch == CBFS_ARCHITECTURE_X86) { + param.headeroffset = param.baseaddress - + sizeof(struct cbfs_header); + DEBUG("x86 -> CBFS header before bootblock (%#x).\n", + param.headeroffset); + } else { + param.headeroffset = align_up(param.baseaddress + + bootblock.size, sizeof(uint32_t)); + DEBUG("CBFS header placed behind bootblock (%#x).\n", + param.headeroffset); + } + } + if (!param.cbfsoffset_assigned) { + if (arch == CBFS_ARCHITECTURE_X86) { + param.cbfsoffset = 0; + DEBUG("x86 -> CBFS entries start at address 0x0.\n"); + } else { + param.cbfsoffset = align_up(param.headeroffset + + sizeof(struct cbfs_header), + param.alignment); + DEBUG("CBFS entries start beind master header (%#x).\n", + param.cbfsoffset); } } @@ -328,7 +347,7 @@ static int cbfs_create(void) &bootblock, param.baseaddress, param.headeroffset, - param.offset) != 0) { + param.cbfsoffset) != 0) { ERROR("Failed to create %s.\n", param.cbfs_name); return 1; } @@ -477,27 +496,28 @@ static const struct command commands[] = { }; static struct option long_options[] = { - {"name", required_argument, 0, 'n' }, - {"type", required_argument, 0, 't' }, - {"compression", required_argument, 0, 'c' }, - {"base-address", required_argument, 0, 'b' }, - {"load-address", required_argument, 0, 'l' }, - {"top-aligned", required_argument, 0, 'T' }, - {"entry-point", required_argument, 0, 'e' }, - {"size", required_argument, 0, 's' }, - {"bootblock", required_argument, 0, 'B' }, - {"alignment", required_argument, 0, 'a' }, - {"page-size", required_argument, 0, 'P' }, - {"offset", required_argument, 0, 'o' }, - {"file", required_argument, 0, 'f' }, - {"machine", required_argument, 0, 'm' }, - {"empty-fits", required_argument, 0, 'x' }, - {"initrd", required_argument, 0, 'I' }, - {"cmdline", required_argument, 0, 'C' }, - {"ignore-sec", required_argument, 0, 'S' }, - {"verbose", no_argument, 0, 'v' }, - {"help", no_argument, 0, 'h' }, - {NULL, 0, 0, 0 } + {"name", required_argument, 0, 'n' }, + {"type", required_argument, 0, 't' }, + {"compression", required_argument, 0, 'c' }, + {"base-address", required_argument, 0, 'b' }, + {"load-address", required_argument, 0, 'l' }, + {"top-aligned", required_argument, 0, 'T' }, + {"entry-point", required_argument, 0, 'e' }, + {"size", required_argument, 0, 's' }, + {"bootblock", required_argument, 0, 'B' }, + {"header-offset", required_argument, 0, 'H' }, + {"alignment", required_argument, 0, 'a' }, + {"page-size", required_argument, 0, 'P' }, + {"offset", required_argument, 0, 'o' }, + {"file", required_argument, 0, 'f' }, + {"machine", required_argument, 0, 'm' }, + {"empty-fits", required_argument, 0, 'x' }, + {"initrd", required_argument, 0, 'I' }, + {"cmdline", required_argument, 0, 'C' }, + {"ignore-sec", required_argument, 0, 'S' }, + {"verbose", no_argument, 0, 'v' }, + {"help", no_argument, 0, 'h' }, + {NULL, 0, 0, 0 } }; static void usage(char *name) @@ -523,7 +543,8 @@ static void usage(char *name) "Add a 32bit flat mode binary\n" " remove -n NAME " "Remove a component\n" - " create -s size -B bootblock -m ARCH [-a align] [-o offset] " + " create -s size -m ARCH [-B bootblock] [-b bootblock offset] \\\n" + " [-o CBFS offset] [-H header offset] [-a align] " "Create a ROM file\n" " locate -f FILE -n NAME [-P page-size] [-a align] [-T] " "Find a place for a file of that size\n" @@ -635,7 +656,8 @@ int main(int argc, char **argv) param.pagesize = strtoul(optarg, NULL, 0); break; case 'o': - param.offset = strtoul(optarg, NULL, 0); + param.cbfsoffset = strtoul(optarg, NULL, 0); + param.cbfsoffset_assigned = 1; break; case 'f': param.filename = optarg; diff --git a/util/cbfstool/common.h b/util/cbfstool/common.h index 260d6cd688..bac449ad5a 100644 --- a/util/cbfstool/common.h +++ b/util/cbfstool/common.h @@ -47,6 +47,13 @@ extern int verbose; #define unused __attribute__((unused)) +static inline uint32_t align_up(uint32_t value, uint32_t align) +{ + if (value % align) + value += align - (value % align); + return value; +} + /* Buffer and file I/O */ struct buffer { char *name;