mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	Minor driver-model fixes and tweaks
A few device-tree fixes Binman support for extracting files from an image -----BEGIN PGP SIGNATURE----- iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAl04t50RHHNqZ0BjaHJv bWl1bS5vcmcACgkQfxc6PpAIreYGPwgAlK9Jw/UoLuag8I1rd6nS8U/EYFP4VRrS ND5Vpe+3WxuMZvrTZyeg1oOkC6UwCJZJMJPFDN2j+VD8LzUYHymTfBykJZLq11Ks wAieldpK75ZqKcafHP8TI3L5a2fjTj20Rgg9R3IXjy8pPp9sdtqr/GiupaEY3AJf Y8SQrL7NRZBKxfaRZZAp2MzbzjyUDwmrgntx/xd0Tr/WwZlOf+dojyAdzKP4udfF 9JcEDi4UOyF/9YBzaSfhYO5h38ZdFan+oXpeXDwjWK5w5YV9uheXLChzloF3d9T0 CRPGfFcyl1EowDO1KB3L73HROAURzEJ8gn76IEqHraHm6dqdmU372g== =9x0F -----END PGP SIGNATURE----- Merge tag 'dm-pull-24jul19-take3' of https://gitlab.denx.de/u-boot/custodians/u-boot-dm Minor driver-model fixes and tweaks A few device-tree fixes Binman support for extracting files from an image
This commit is contained in:
		
						commit
						f9b65c76b4
					
				@ -176,7 +176,7 @@ Run binman and dtoc testsuite:
 | 
			
		||||
      ./tools/buildman/buildman -P sandbox_spl && 
 | 
			
		||||
     export PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt";
 | 
			
		||||
     export PATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}";
 | 
			
		||||
     ./tools/binman/binman -t &&
 | 
			
		||||
     ./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test &&
 | 
			
		||||
     ./tools/dtoc/dtoc -t
 | 
			
		||||
 | 
			
		||||
# Test sandbox with test.py
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ addons:
 | 
			
		||||
    - device-tree-compiler
 | 
			
		||||
    - lzop
 | 
			
		||||
    - liblz4-tool
 | 
			
		||||
    - lzma-alone
 | 
			
		||||
    - libisl15
 | 
			
		||||
    - clang-7
 | 
			
		||||
    - srecord
 | 
			
		||||
@ -146,7 +147,7 @@ script:
 | 
			
		||||
   if [[ -n "${TEST_PY_TOOLS}" ]]; then
 | 
			
		||||
     PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt"
 | 
			
		||||
     PATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}"
 | 
			
		||||
     ./tools/binman/binman -t &&
 | 
			
		||||
     ./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test &&
 | 
			
		||||
     ./tools/patman/patman --test &&
 | 
			
		||||
     ./tools/buildman/buildman -t &&
 | 
			
		||||
     PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							@ -1196,9 +1196,9 @@ u-boot.ldr:	u-boot
 | 
			
		||||
# ---------------------------------------------------------------------------
 | 
			
		||||
# Use 'make BINMAN_DEBUG=1' to enable debugging
 | 
			
		||||
quiet_cmd_binman = BINMAN  $@
 | 
			
		||||
cmd_binman = $(srctree)/tools/binman/binman -u -d u-boot.dtb -O . -m \
 | 
			
		||||
cmd_binman = $(srctree)/tools/binman/binman build -u -d u-boot.dtb -O . -m \
 | 
			
		||||
		-I . -I $(srctree) -I $(srctree)/board/$(BOARDDIR) \
 | 
			
		||||
		$(if $(BINMAN_DEBUG),-D) $(BINMAN_$(@F)) $<
 | 
			
		||||
		$(if $(BINMAN_DEBUG),-D) $(BINMAN_$(@F))
 | 
			
		||||
 | 
			
		||||
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -671,30 +671,33 @@ int fdt_pci_dma_ranges(void *blob, int phb_off, struct pci_controller *hose) {
 | 
			
		||||
 | 
			
		||||
		dma_range[0] = 0;
 | 
			
		||||
		if (size >= 0x100000000ull)
 | 
			
		||||
			dma_range[0] |= FDT_PCI_MEM64;
 | 
			
		||||
			dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM64);
 | 
			
		||||
		else
 | 
			
		||||
			dma_range[0] |= FDT_PCI_MEM32;
 | 
			
		||||
			dma_range[0] |= cpu_to_fdt32(FDT_PCI_MEM32);
 | 
			
		||||
		if (hose->regions[r].flags & PCI_REGION_PREFETCH)
 | 
			
		||||
			dma_range[0] |= FDT_PCI_PREFETCH;
 | 
			
		||||
			dma_range[0] |= cpu_to_fdt32(FDT_PCI_PREFETCH);
 | 
			
		||||
#ifdef CONFIG_SYS_PCI_64BIT
 | 
			
		||||
		dma_range[1] = bus_start >> 32;
 | 
			
		||||
		dma_range[1] = cpu_to_fdt32(bus_start >> 32);
 | 
			
		||||
#else
 | 
			
		||||
		dma_range[1] = 0;
 | 
			
		||||
#endif
 | 
			
		||||
		dma_range[2] = bus_start & 0xffffffff;
 | 
			
		||||
		dma_range[2] = cpu_to_fdt32(bus_start & 0xffffffff);
 | 
			
		||||
 | 
			
		||||
		if (addrcell == 2) {
 | 
			
		||||
			dma_range[3] = phys_start >> 32;
 | 
			
		||||
			dma_range[4] = phys_start & 0xffffffff;
 | 
			
		||||
			dma_range[3] = cpu_to_fdt32(phys_start >> 32);
 | 
			
		||||
			dma_range[4] = cpu_to_fdt32(phys_start & 0xffffffff);
 | 
			
		||||
		} else {
 | 
			
		||||
			dma_range[3] = phys_start & 0xffffffff;
 | 
			
		||||
			dma_range[3] = cpu_to_fdt32(phys_start & 0xffffffff);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (sizecell == 2) {
 | 
			
		||||
			dma_range[3 + addrcell + 0] = size >> 32;
 | 
			
		||||
			dma_range[3 + addrcell + 1] = size & 0xffffffff;
 | 
			
		||||
			dma_range[3 + addrcell + 0] =
 | 
			
		||||
				cpu_to_fdt32(size >> 32);
 | 
			
		||||
			dma_range[3 + addrcell + 1] =
 | 
			
		||||
				cpu_to_fdt32(size & 0xffffffff);
 | 
			
		||||
		} else {
 | 
			
		||||
			dma_range[3 + addrcell + 0] = size & 0xffffffff;
 | 
			
		||||
			dma_range[3 + addrcell + 0] =
 | 
			
		||||
				cpu_to_fdt32(size & 0xffffffff);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dma_range += (3 + addrcell + sizecell);
 | 
			
		||||
@ -1552,7 +1555,7 @@ u64 fdt_get_base_address(const void *fdt, int node)
 | 
			
		||||
 | 
			
		||||
	prop = fdt_getprop(fdt, node, "reg", &size);
 | 
			
		||||
 | 
			
		||||
	return prop ? fdt_translate_address(fdt, node, prop) : 0;
 | 
			
		||||
	return prop ? fdt_translate_address(fdt, node, prop) : OF_BAD_ADDR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
@ -51,6 +51,8 @@ static int clk_of_xlate_default(struct clk *clk,
 | 
			
		||||
	else
 | 
			
		||||
		clk->id = 0;
 | 
			
		||||
 | 
			
		||||
	clk->data = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -388,7 +388,8 @@ int device_probe(struct udevice *dev)
 | 
			
		||||
	if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)
 | 
			
		||||
		pinctrl_select_state(dev, "default");
 | 
			
		||||
 | 
			
		||||
	if (dev->parent && device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) {
 | 
			
		||||
	if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent &&
 | 
			
		||||
	    device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) {
 | 
			
		||||
		if (!power_domain_get(dev, &pd))
 | 
			
		||||
			power_domain_on(&pd);
 | 
			
		||||
	}
 | 
			
		||||
@ -409,10 +410,16 @@ int device_probe(struct udevice *dev)
 | 
			
		||||
			goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
 | 
			
		||||
	ret = clk_set_defaults(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto fail;
 | 
			
		||||
	/* Only handle devices that have a valid ofnode */
 | 
			
		||||
	if (dev_of_valid(dev)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Process 'assigned-{clocks/clock-parents/clock-rates}'
 | 
			
		||||
		 * properties
 | 
			
		||||
		 */
 | 
			
		||||
		ret = clk_set_defaults(dev);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (drv->probe) {
 | 
			
		||||
		ret = drv->probe(dev);
 | 
			
		||||
 | 
			
		||||
@ -884,5 +884,5 @@ int ofnode_set_enabled(ofnode node, bool value)
 | 
			
		||||
	if (value)
 | 
			
		||||
		return ofnode_write_string(node, "status", "okay");
 | 
			
		||||
	else
 | 
			
		||||
		return ofnode_write_string(node, "status", "disable");
 | 
			
		||||
		return ofnode_write_string(node, "status", "disabled");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,10 @@ static int timer_pre_probe(struct udevice *dev)
 | 
			
		||||
	int err;
 | 
			
		||||
	ulong ret;
 | 
			
		||||
 | 
			
		||||
	/* It is possible that a timer device has a null ofnode */
 | 
			
		||||
	if (!dev_of_valid(dev))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	err = clk_get_by_index(dev, 0, &timer_clk);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		ret = clk_get_rate(&timer_clk);
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ static void swap_file_header(struct cbfs_fileheader *dest,
 | 
			
		||||
	memcpy(&dest->magic, &src->magic, sizeof(dest->magic));
 | 
			
		||||
	dest->len = be32_to_cpu(src->len);
 | 
			
		||||
	dest->type = be32_to_cpu(src->type);
 | 
			
		||||
	dest->checksum = be32_to_cpu(src->checksum);
 | 
			
		||||
	dest->attributes_offset = be32_to_cpu(src->attributes_offset);
 | 
			
		||||
	dest->offset = be32_to_cpu(src->offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,7 @@ static int file_cbfs_next_file(u8 *start, u32 size, u32 align,
 | 
			
		||||
		newNode->name = (char *)fileHeader +
 | 
			
		||||
				sizeof(struct cbfs_fileheader);
 | 
			
		||||
		newNode->name_length = name_len;
 | 
			
		||||
		newNode->checksum = header.checksum;
 | 
			
		||||
		newNode->attributes_offset = header.attributes_offset;
 | 
			
		||||
 | 
			
		||||
		step = header.len;
 | 
			
		||||
		if (step % align)
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,17 @@ enum cbfs_filetype {
 | 
			
		||||
	CBFS_TYPE_CMOS_LAYOUT = 0x01aa
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	CBFS_HEADER_MAGIC	= 0x4f524243,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct cbfs_header - header at the start of a CBFS region
 | 
			
		||||
 *
 | 
			
		||||
 * All fields use big-endian format.
 | 
			
		||||
 *
 | 
			
		||||
 * @magic: Magic number (CBFS_HEADER_MAGIC)
 | 
			
		||||
 */
 | 
			
		||||
struct cbfs_header {
 | 
			
		||||
	u32 magic;
 | 
			
		||||
	u32 version;
 | 
			
		||||
@ -54,7 +65,8 @@ struct cbfs_fileheader {
 | 
			
		||||
	u8 magic[8];
 | 
			
		||||
	u32 len;
 | 
			
		||||
	u32 type;
 | 
			
		||||
	u32 checksum;
 | 
			
		||||
	/* offset to struct cbfs_file_attribute or 0 */
 | 
			
		||||
	u32 attributes_offset;
 | 
			
		||||
	u32 offset;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
@ -65,7 +77,7 @@ struct cbfs_cachenode {
 | 
			
		||||
	u32 data_length;
 | 
			
		||||
	char *name;
 | 
			
		||||
	u32 name_length;
 | 
			
		||||
	u32 checksum;
 | 
			
		||||
	u32 attributes_offset;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
extern enum cbfs_result file_cbfs_result;
 | 
			
		||||
 | 
			
		||||
@ -227,7 +227,7 @@ fdt_addr_t dev_read_addr_size(struct udevice *dev, const char *propname,
 | 
			
		||||
/**
 | 
			
		||||
 * dev_read_name() - get the name of a device's node
 | 
			
		||||
 *
 | 
			
		||||
 * @node: valid node to look up
 | 
			
		||||
 * @dev: Device to read from
 | 
			
		||||
 * @return name of node
 | 
			
		||||
 */
 | 
			
		||||
const char *dev_read_name(struct udevice *dev);
 | 
			
		||||
 | 
			
		||||
@ -297,7 +297,7 @@ int uclass_first_device_err(enum uclass_id id, struct udevice **devp);
 | 
			
		||||
 *
 | 
			
		||||
 * The device returned is probed if necessary, and ready for use
 | 
			
		||||
 *
 | 
			
		||||
 * This function is useful to start iterating through a list of devices which
 | 
			
		||||
 * This function is useful to iterate through a list of devices which
 | 
			
		||||
 * are functioning correctly and can be probed.
 | 
			
		||||
 *
 | 
			
		||||
 * @devp: On entry, pointer to device to lookup. On exit, returns pointer
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								test/run
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								test/run
									
									
									
									
									
								
							@ -33,12 +33,14 @@ run_test "sandbox_flattree" ./test/py/test.py --bd sandbox_flattree --build \
 | 
			
		||||
	-k test_ut
 | 
			
		||||
 | 
			
		||||
# Set up a path to dtc (device-tree compiler) and libfdt.py, a library it
 | 
			
		||||
# provides and which is built by the sandbox_spl config.
 | 
			
		||||
# provides and which is built by the sandbox_spl config. Also set up the path
 | 
			
		||||
# to tools build by the build.
 | 
			
		||||
DTC_DIR=build-sandbox_spl/scripts/dtc
 | 
			
		||||
export PYTHONPATH=${DTC_DIR}/pylibfdt
 | 
			
		||||
export DTC=${DTC_DIR}/dtc
 | 
			
		||||
TOOLS_DIR=build-sandbox_spl/tools
 | 
			
		||||
 | 
			
		||||
run_test "binman" ./tools/binman/binman -t
 | 
			
		||||
run_test "binman" ./tools/binman/binman --toolpath ${TOOLS_DIR} test
 | 
			
		||||
run_test "patman" ./tools/patman/patman --test
 | 
			
		||||
 | 
			
		||||
[ "$1" == "quick" ] && skip=--skip-net-tests
 | 
			
		||||
@ -49,7 +51,8 @@ run_test "dtoc" ./tools/dtoc/dtoc -t
 | 
			
		||||
# This needs you to set up Python test coverage tools.
 | 
			
		||||
# To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
 | 
			
		||||
#   $ sudo apt-get install python-pytest python-coverage
 | 
			
		||||
run_test "binman code coverage" ./tools/binman/binman -T
 | 
			
		||||
export PATH=$PATH:${TOOLS_DIR}
 | 
			
		||||
run_test "binman code coverage" ./tools/binman/binman test -T
 | 
			
		||||
run_test "dtoc code coverage" ./tools/dtoc/dtoc -T
 | 
			
		||||
run_test "fdt code coverage" ./tools/dtoc/test_fdt -T
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -175,6 +175,9 @@ HOSTCFLAGS_mkexynosspl.o := -pedantic
 | 
			
		||||
ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o
 | 
			
		||||
hostprogs-$(CONFIG_X86) += ifdtool
 | 
			
		||||
 | 
			
		||||
ifwitool-objs := ifwitool.o
 | 
			
		||||
hostprogs-$(CONFIG_X86)$(CONFIG_SANDBOX) += ifwitool
 | 
			
		||||
 | 
			
		||||
hostprogs-$(CONFIG_MX23) += mxsboot
 | 
			
		||||
hostprogs-$(CONFIG_MX28) += mxsboot
 | 
			
		||||
HOSTCFLAGS_mxsboot.o := -pedantic
 | 
			
		||||
 | 
			
		||||
@ -36,10 +36,9 @@ suitable padding and alignment. It provides a way to process binaries before
 | 
			
		||||
they are included, by adding a Python plug-in. The device tree is available
 | 
			
		||||
to U-Boot at run-time so that the images can be interpreted.
 | 
			
		||||
 | 
			
		||||
Binman does not yet update the device tree with the final location of
 | 
			
		||||
everything when it is done. A simple C structure could be generated for
 | 
			
		||||
constrained environments like SPL (using dtoc) but this is also not
 | 
			
		||||
implemented.
 | 
			
		||||
Binman can update the device tree with the final location of everything when it
 | 
			
		||||
is done. Entry positions can be provided to U-Boot SPL as run-time symbols,
 | 
			
		||||
avoiding device-tree code overhead.
 | 
			
		||||
 | 
			
		||||
Binman can also support incorporating filesystems in the image if required.
 | 
			
		||||
For example x86 platforms may use CBFS in some cases.
 | 
			
		||||
@ -181,9 +180,14 @@ the configuration of the Intel-format descriptor.
 | 
			
		||||
Running binman
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
First install prerequisites, e.g.
 | 
			
		||||
 | 
			
		||||
	sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \
 | 
			
		||||
		liblz4-tool
 | 
			
		||||
 | 
			
		||||
Type:
 | 
			
		||||
 | 
			
		||||
	binman -b <board_name>
 | 
			
		||||
	binman build -b <board_name>
 | 
			
		||||
 | 
			
		||||
to build an image for a board. The board name is the same name used when
 | 
			
		||||
configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox').
 | 
			
		||||
@ -191,7 +195,7 @@ Binman assumes that the input files for the build are in ../b/<board_name>.
 | 
			
		||||
 | 
			
		||||
Or you can specify this explicitly:
 | 
			
		||||
 | 
			
		||||
	binman -I <build_path>
 | 
			
		||||
	binman build -I <build_path>
 | 
			
		||||
 | 
			
		||||
where <build_path> is the build directory containing the output of the U-Boot
 | 
			
		||||
build.
 | 
			
		||||
@ -335,6 +339,10 @@ expand-size:
 | 
			
		||||
	limited by the size of the image/section and the position of the next
 | 
			
		||||
	entry.
 | 
			
		||||
 | 
			
		||||
compress:
 | 
			
		||||
	Sets the compression algortihm to use (for blobs only). See the entry
 | 
			
		||||
	documentation for details.
 | 
			
		||||
 | 
			
		||||
The attributes supported for images and sections are described below. Several
 | 
			
		||||
are similar to those for entries.
 | 
			
		||||
 | 
			
		||||
@ -479,7 +487,92 @@ Entry Documentation
 | 
			
		||||
For details on the various entry types supported by binman and how to use them,
 | 
			
		||||
see README.entries. This is generated from the source code using:
 | 
			
		||||
 | 
			
		||||
	binman -E >tools/binman/README.entries
 | 
			
		||||
	binman entry-docs >tools/binman/README.entries
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Listing images
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
It is possible to list the entries in an existing firmware image created by
 | 
			
		||||
binman, provided that there is an 'fdtmap' entry in the image. For example:
 | 
			
		||||
 | 
			
		||||
    $ binman ls -i image.bin
 | 
			
		||||
    Name              Image-pos  Size  Entry-type    Offset  Uncomp-size
 | 
			
		||||
    ----------------------------------------------------------------------
 | 
			
		||||
    main-section                  c00  section            0
 | 
			
		||||
      u-boot                  0     4  u-boot             0
 | 
			
		||||
      section                     5fc  section            4
 | 
			
		||||
        cbfs                100   400  cbfs               0
 | 
			
		||||
          u-boot            138     4  u-boot            38
 | 
			
		||||
          u-boot-dtb        180   108  u-boot-dtb        80          3b5
 | 
			
		||||
        u-boot-dtb          500   1ff  u-boot-dtb       400          3b5
 | 
			
		||||
      fdtmap                6fc   381  fdtmap           6fc
 | 
			
		||||
      image-header          bf8     8  image-header     bf8
 | 
			
		||||
 | 
			
		||||
This shows the hierarchy of the image, the position, size and type of each
 | 
			
		||||
entry, the offset of each entry within its parent and the uncompressed size if
 | 
			
		||||
the entry is compressed.
 | 
			
		||||
 | 
			
		||||
It is also possible to list just some files in an image, e.g.
 | 
			
		||||
 | 
			
		||||
    $ binman ls -i image.bin section/cbfs
 | 
			
		||||
    Name              Image-pos  Size  Entry-type  Offset  Uncomp-size
 | 
			
		||||
    --------------------------------------------------------------------
 | 
			
		||||
        cbfs                100   400  cbfs             0
 | 
			
		||||
          u-boot            138     4  u-boot          38
 | 
			
		||||
          u-boot-dtb        180   108  u-boot-dtb      80          3b5
 | 
			
		||||
 | 
			
		||||
or with wildcards:
 | 
			
		||||
 | 
			
		||||
    $ binman ls -i image.bin "*cb*" "*head*"
 | 
			
		||||
    Name              Image-pos  Size  Entry-type    Offset  Uncomp-size
 | 
			
		||||
    ----------------------------------------------------------------------
 | 
			
		||||
        cbfs                100   400  cbfs               0
 | 
			
		||||
          u-boot            138     4  u-boot            38
 | 
			
		||||
          u-boot-dtb        180   108  u-boot-dtb        80          3b5
 | 
			
		||||
      image-header          bf8     8  image-header     bf8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Extracting files from images
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
You can extract files from an existing firmware image created by binman,
 | 
			
		||||
provided that there is an 'fdtmap' entry in the image. For example:
 | 
			
		||||
 | 
			
		||||
    $ binman extract -i image.bin section/cbfs/u-boot
 | 
			
		||||
 | 
			
		||||
which will write the uncompressed contents of that entry to the file 'u-boot' in
 | 
			
		||||
the current directory. You can also extract to a particular file, in this case
 | 
			
		||||
u-boot.bin:
 | 
			
		||||
 | 
			
		||||
    $ binman extract -i image.bin section/cbfs/u-boot -f u-boot.bin
 | 
			
		||||
 | 
			
		||||
It is possible to extract all files into a destination directory, which will
 | 
			
		||||
put files in subdirectories matching the entry hierarchy:
 | 
			
		||||
 | 
			
		||||
    $ binman extract -i image.bin -O outdir
 | 
			
		||||
 | 
			
		||||
or just a selection:
 | 
			
		||||
 | 
			
		||||
    $ binman extract -i image.bin "*u-boot*" -O outdir
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Logging
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Binman normally operates silently unless there is an error, in which case it
 | 
			
		||||
just displays the error. The -D/--debug option can be used to create a full
 | 
			
		||||
backtrace when errors occur.
 | 
			
		||||
 | 
			
		||||
Internally binman logs some output while it is running. This can be displayed
 | 
			
		||||
by increasing the -v/--verbosity from the default of 1:
 | 
			
		||||
 | 
			
		||||
   0: silent
 | 
			
		||||
   1: warnings only
 | 
			
		||||
   2: notices (important messages)
 | 
			
		||||
   3: info about major operations
 | 
			
		||||
   4: detailed information about each operation
 | 
			
		||||
   5: debug (all output)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Hashing Entries
 | 
			
		||||
@ -558,7 +651,8 @@ tree. This sets the correct 'offset' and 'size' vaues, for example.
 | 
			
		||||
The default implementatoin does nothing. This can be overriden to adjust the
 | 
			
		||||
contents of an entry in some way. For example, it would be possible to create
 | 
			
		||||
an entry containing a hash of the contents of some other entries. At this
 | 
			
		||||
stage the offset and size of entries should not be adjusted.
 | 
			
		||||
stage the offset and size of entries should not be adjusted unless absolutely
 | 
			
		||||
necessary, since it requires a repack (going back to PackEntries()).
 | 
			
		||||
 | 
			
		||||
10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
 | 
			
		||||
See 'Access to binman entry offsets at run time' below for a description of
 | 
			
		||||
@ -634,20 +728,27 @@ the image definition, binman calculates the final values and writes these to
 | 
			
		||||
the device tree. These can be used by U-Boot at run-time to find the location
 | 
			
		||||
of each entry.
 | 
			
		||||
 | 
			
		||||
Alternatively, an FDT map entry can be used to add a special FDT containing
 | 
			
		||||
just the information about the image. This is preceded by a magic string so can
 | 
			
		||||
be located anywhere in the image. An image header (typically at the start or end
 | 
			
		||||
of the image) can be used to point to the FDT map. See fdtmap and image-header
 | 
			
		||||
entries for more information.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Compression
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Binman support compression for 'blob' entries (those of type 'blob' and
 | 
			
		||||
derivatives). To enable this for an entry, add a 'compression' property:
 | 
			
		||||
derivatives). To enable this for an entry, add a 'compress' property:
 | 
			
		||||
 | 
			
		||||
    blob {
 | 
			
		||||
        filename = "datafile";
 | 
			
		||||
        compression = "lz4";
 | 
			
		||||
        compress = "lz4";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
The entry will then contain the compressed data, using the 'lz4' compression
 | 
			
		||||
algorithm. Currently this is the only one that is supported.
 | 
			
		||||
algorithm. Currently this is the only one that is supported. The uncompressed
 | 
			
		||||
size is written to the node in an 'uncomp-size' property, if -u is used.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -691,15 +792,25 @@ Not all properties can be provided this way. Only some entries support it,
 | 
			
		||||
typically for filenames.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
External tools
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Binman can make use of external command-line tools to handle processing of
 | 
			
		||||
entry contents or to generate entry contents. These tools are executed using
 | 
			
		||||
the 'tools' module's Run() method. The tools generally must exist on the PATH,
 | 
			
		||||
but the --toolpath option can be used to specify additional search paths to
 | 
			
		||||
use. This option can be specified multiple times to add more than one path.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Code coverage
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
Binman is a critical tool and is designed to be very testable. Entry
 | 
			
		||||
implementations target 100% test coverage. Run 'binman -T' to check this.
 | 
			
		||||
implementations target 100% test coverage. Run 'binman test -T' to check this.
 | 
			
		||||
 | 
			
		||||
To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
 | 
			
		||||
 | 
			
		||||
   $ sudo apt-get install python-coverage python-pytest
 | 
			
		||||
   $ sudo apt-get install python-coverage python3-coverage python-pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Concurrent tests
 | 
			
		||||
@ -716,6 +827,14 @@ Use '-P 1' to disable this. It is automatically disabled when code coverage is
 | 
			
		||||
being used (-T) since they are incompatible.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Debugging tests
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Sometimes when debugging tests it is useful to keep the input and output
 | 
			
		||||
directories so they can be examined later. Use -X or --test-preserve-dirs for
 | 
			
		||||
this.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Advanced Features / Technical docs
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
@ -788,13 +907,12 @@ Some ideas:
 | 
			
		||||
- Use of-platdata to make the information available to code that is unable
 | 
			
		||||
  to use device tree (such as a very small SPL image)
 | 
			
		||||
- Allow easy building of images by specifying just the board name
 | 
			
		||||
- Produce a full Python binding for libfdt (for upstream). This is nearing
 | 
			
		||||
    completion but some work remains
 | 
			
		||||
- Add an option to decode an image into the constituent binaries
 | 
			
		||||
- Support building an image for a board (-b) more completely, with a
 | 
			
		||||
  configurable build directory
 | 
			
		||||
- Consider making binman work with buildman, although if it is used in the
 | 
			
		||||
  Makefile, this will be automatic
 | 
			
		||||
- Support updating binaries in an image (with no size change / repacking)
 | 
			
		||||
- Support updating binaries in an image (with repacking)
 | 
			
		||||
- Support adding FITs to an image
 | 
			
		||||
- Support for ARM Trusted Firmware (ATF)
 | 
			
		||||
 | 
			
		||||
--
 | 
			
		||||
Simon Glass <sjg@chromium.org>
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,158 @@ See cros_ec_rw for an example of this.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: cbfs: Entry containing a Coreboot Filesystem (CBFS)
 | 
			
		||||
----------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
A CBFS provides a way to group files into a group. It has a simple directory
 | 
			
		||||
structure and allows the position of individual files to be set, since it is
 | 
			
		||||
designed to support execute-in-place in an x86 SPI-flash device. Where XIP
 | 
			
		||||
is not used, it supports compression and storing ELF files.
 | 
			
		||||
 | 
			
		||||
CBFS is used by coreboot as its way of orgnanising SPI-flash contents.
 | 
			
		||||
 | 
			
		||||
The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:
 | 
			
		||||
 | 
			
		||||
    cbfs {
 | 
			
		||||
        size = <0x100000>;
 | 
			
		||||
        u-boot {
 | 
			
		||||
            cbfs-type = "raw";
 | 
			
		||||
        };
 | 
			
		||||
        u-boot-dtb {
 | 
			
		||||
            cbfs-type = "raw";
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb.
 | 
			
		||||
Note that the size is required since binman does not support calculating it.
 | 
			
		||||
The contents of each entry is just what binman would normally provide if it
 | 
			
		||||
were not a CBFS node. A blob type can be used to import arbitrary files as
 | 
			
		||||
with the second subnode below:
 | 
			
		||||
 | 
			
		||||
    cbfs {
 | 
			
		||||
        size = <0x100000>;
 | 
			
		||||
        u-boot {
 | 
			
		||||
            cbfs-name = "BOOT";
 | 
			
		||||
            cbfs-type = "raw";
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        dtb {
 | 
			
		||||
            type = "blob";
 | 
			
		||||
            filename = "u-boot.dtb";
 | 
			
		||||
            cbfs-type = "raw";
 | 
			
		||||
            cbfs-compress = "lz4";
 | 
			
		||||
            cbfs-offset = <0x100000>;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and
 | 
			
		||||
u-boot.dtb (named "dtb") and compressed with the lz4 algorithm.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Properties supported in the top-level CBFS node:
 | 
			
		||||
 | 
			
		||||
cbfs-arch:
 | 
			
		||||
    Defaults to "x86", but you can specify the architecture if needed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Properties supported in the CBFS entry subnodes:
 | 
			
		||||
 | 
			
		||||
cbfs-name:
 | 
			
		||||
    This is the name of the file created in CBFS. It defaults to the entry
 | 
			
		||||
    name (which is the node name), but you can override it with this
 | 
			
		||||
    property.
 | 
			
		||||
 | 
			
		||||
cbfs-type:
 | 
			
		||||
    This is the CBFS file type. The following are supported:
 | 
			
		||||
 | 
			
		||||
    raw:
 | 
			
		||||
        This is a 'raw' file, although compression is supported. It can be
 | 
			
		||||
        used to store any file in CBFS.
 | 
			
		||||
 | 
			
		||||
    stage:
 | 
			
		||||
        This is an ELF file that has been loaded (i.e. mapped to memory), so
 | 
			
		||||
        appears in the CBFS as a flat binary. The input file must be an ELF
 | 
			
		||||
        image, for example this puts "u-boot" (the ELF image) into a 'stage'
 | 
			
		||||
        entry:
 | 
			
		||||
 | 
			
		||||
            cbfs {
 | 
			
		||||
                size = <0x100000>;
 | 
			
		||||
                u-boot-elf {
 | 
			
		||||
                    cbfs-name = "BOOT";
 | 
			
		||||
                    cbfs-type = "stage";
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        You can use your own ELF file with something like:
 | 
			
		||||
 | 
			
		||||
            cbfs {
 | 
			
		||||
                size = <0x100000>;
 | 
			
		||||
                something {
 | 
			
		||||
                    type = "blob";
 | 
			
		||||
                    filename = "cbfs-stage.elf";
 | 
			
		||||
                    cbfs-type = "stage";
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        As mentioned, the file is converted to a flat binary, so it is
 | 
			
		||||
        equivalent to adding "u-boot.bin", for example, but with the load and
 | 
			
		||||
        start addresses specified by the ELF. At present there is no option
 | 
			
		||||
        to add a flat binary with a load/start address, similar to the
 | 
			
		||||
        'add-flat-binary' option in cbfstool.
 | 
			
		||||
 | 
			
		||||
cbfs-offset:
 | 
			
		||||
    This is the offset of the file's data within the CBFS. It is used to
 | 
			
		||||
    specify where the file should be placed in cases where a fixed position
 | 
			
		||||
    is needed. Typical uses are for code which is not relocatable and must
 | 
			
		||||
    execute in-place from a particular address. This works because SPI flash
 | 
			
		||||
    is generally mapped into memory on x86 devices. The file header is
 | 
			
		||||
    placed before this offset so that the data start lines up exactly with
 | 
			
		||||
    the chosen offset. If this property is not provided, then the file is
 | 
			
		||||
    placed in the next available spot.
 | 
			
		||||
 | 
			
		||||
The current implementation supports only a subset of CBFS features. It does
 | 
			
		||||
not support other file types (e.g. payload), adding multiple files (like the
 | 
			
		||||
'files' entry with a pattern supported by binman), putting files at a
 | 
			
		||||
particular offset in the CBFS and a few other things.
 | 
			
		||||
 | 
			
		||||
Of course binman can create images containing multiple CBFSs, simply by
 | 
			
		||||
defining these in the binman config:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    binman {
 | 
			
		||||
        size = <0x800000>;
 | 
			
		||||
        cbfs {
 | 
			
		||||
            offset = <0x100000>;
 | 
			
		||||
            size = <0x100000>;
 | 
			
		||||
            u-boot {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
            u-boot-dtb {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        cbfs2 {
 | 
			
		||||
            offset = <0x700000>;
 | 
			
		||||
            size = <0x100000>;
 | 
			
		||||
            u-boot {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
            u-boot-dtb {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
            image {
 | 
			
		||||
                type = "blob";
 | 
			
		||||
                filename = "image.jpg";
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB,
 | 
			
		||||
both of size 1MB.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
@ -71,6 +223,44 @@ updating the EC on startup via software sync.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: fdtmap: An entry which contains an FDT map
 | 
			
		||||
-------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments:
 | 
			
		||||
    None
 | 
			
		||||
 | 
			
		||||
An FDT map is just a header followed by an FDT containing a list of all the
 | 
			
		||||
entries in the image.
 | 
			
		||||
 | 
			
		||||
The header is the string _FDTMAP_ followed by 8 unused bytes.
 | 
			
		||||
 | 
			
		||||
When used, this entry will be populated with an FDT map which reflects the
 | 
			
		||||
entries in the current image. Hierarchy is preserved, and all offsets and
 | 
			
		||||
sizes are included.
 | 
			
		||||
 | 
			
		||||
Note that the -u option must be provided to ensure that binman updates the
 | 
			
		||||
FDT with the position of each entry.
 | 
			
		||||
 | 
			
		||||
Example output for a simple image with U-Boot and an FDT map:
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
    size = <0x00000112>;
 | 
			
		||||
    image-pos = <0x00000000>;
 | 
			
		||||
    offset = <0x00000000>;
 | 
			
		||||
    u-boot {
 | 
			
		||||
        size = <0x00000004>;
 | 
			
		||||
        image-pos = <0x00000000>;
 | 
			
		||||
        offset = <0x00000000>;
 | 
			
		||||
    };
 | 
			
		||||
    fdtmap {
 | 
			
		||||
        size = <0x0000010e>;
 | 
			
		||||
        image-pos = <0x00000004>;
 | 
			
		||||
        offset = <0x00000004>;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: files: Entry containing a set of files
 | 
			
		||||
---------------------------------------------
 | 
			
		||||
 | 
			
		||||
@ -141,6 +331,25 @@ README.chromium for how to obtain the required keys and tools.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: image-header: An entry which contains a pointer to the FDT map
 | 
			
		||||
---------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments:
 | 
			
		||||
    location: Location of header ("start" or "end" of image). This is
 | 
			
		||||
        optional. If omitted then the entry must have an offset property.
 | 
			
		||||
 | 
			
		||||
This adds an 8-byte entry to the start or end of the image, pointing to the
 | 
			
		||||
location of the FDT map. The format is a magic number followed by an offset
 | 
			
		||||
from the start or end of the image, in twos-compliment format.
 | 
			
		||||
 | 
			
		||||
This entry must be in the top-level part of the image.
 | 
			
		||||
 | 
			
		||||
NOTE: If the location is at the start/end, you will probably need to specify
 | 
			
		||||
sort-by-offset for the image, unless you actually put the image header
 | 
			
		||||
first/last in the entry list.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file
 | 
			
		||||
-------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
@ -192,6 +401,34 @@ See README.x86 for information about x86 binary blobs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: intel-ifwi: Entry containing an Intel Integrated Firmware Image (IFWI) file
 | 
			
		||||
----------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments:
 | 
			
		||||
    - filename: Filename of file to read into entry. This is either the
 | 
			
		||||
        IFWI file itself, or a file that can be converted into one using a
 | 
			
		||||
        tool
 | 
			
		||||
    - convert-fit: If present this indicates that the ifwitool should be
 | 
			
		||||
        used to convert the provided file into a IFWI.
 | 
			
		||||
 | 
			
		||||
This file contains code and data used by the SoC that is required to make
 | 
			
		||||
it work. It includes U-Boot TPL, microcode, things related to the CSE
 | 
			
		||||
(Converged Security Engine, the microcontroller that loads all the firmware)
 | 
			
		||||
and other items beyond the wit of man.
 | 
			
		||||
 | 
			
		||||
A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a
 | 
			
		||||
file that will be converted to an IFWI.
 | 
			
		||||
 | 
			
		||||
The position of this entry is generally set by the intel-descriptor entry.
 | 
			
		||||
 | 
			
		||||
The contents of the IFWI are specified by the subnodes of the IFWI node.
 | 
			
		||||
Each subnode describes an entry which is placed into the IFWFI with a given
 | 
			
		||||
sub-partition (and optional entry name).
 | 
			
		||||
 | 
			
		||||
See README.x86 for information about x86 binary blobs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: intel-me: Entry containing an Intel Management Engine (ME) file
 | 
			
		||||
----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
@ -206,6 +443,8 @@ does not directly execute code in the ME binary.
 | 
			
		||||
 | 
			
		||||
A typical filename is 'me.bin'.
 | 
			
		||||
 | 
			
		||||
The position of this entry is generally set by the intel-descriptor entry.
 | 
			
		||||
 | 
			
		||||
See README.x86 for information about x86 binary blobs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -282,16 +521,21 @@ Entry: section: Entry that contains other entries
 | 
			
		||||
-------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments: (see binman README for more information)
 | 
			
		||||
    - size: Size of section in bytes
 | 
			
		||||
    - align-size: Align size to a particular power of two
 | 
			
		||||
    - pad-before: Add padding before the entry
 | 
			
		||||
    - pad-after: Add padding after the entry
 | 
			
		||||
    - pad-byte: Pad byte to use when padding
 | 
			
		||||
    - sort-by-offset: Reorder the entries by offset
 | 
			
		||||
    - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
 | 
			
		||||
    - name-prefix: Adds a prefix to the name of every entry in the section
 | 
			
		||||
    pad-byte: Pad byte to use when padding
 | 
			
		||||
    sort-by-offset: True if entries should be sorted by offset, False if
 | 
			
		||||
        they must be in-order in the device tree description
 | 
			
		||||
    end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
 | 
			
		||||
    skip-at-start: Number of bytes before the first entry starts. These
 | 
			
		||||
        effectively adjust the starting offset of entries. For example,
 | 
			
		||||
        if this is 16, then the first entry would start at 16. An entry
 | 
			
		||||
        with offset = 20 would in fact be written at offset 4 in the image
 | 
			
		||||
        file, since the first 16 bytes are skipped when writing.
 | 
			
		||||
    name-prefix: Adds a prefix to the name of every entry in the section
 | 
			
		||||
        when writing out the map
 | 
			
		||||
 | 
			
		||||
Since a section is also an entry, it inherits all the properies of entries
 | 
			
		||||
too.
 | 
			
		||||
 | 
			
		||||
A section is an entry which can contain other entries, thus allowing
 | 
			
		||||
hierarchical images to be created. See 'Sections and hierarchical images'
 | 
			
		||||
in the binman README for more information.
 | 
			
		||||
@ -310,6 +554,8 @@ Properties / Entry arguments:
 | 
			
		||||
        that contains the string to place in the entry
 | 
			
		||||
    <xxx> (actual name is the value of text-label): contains the string to
 | 
			
		||||
        place in the entry.
 | 
			
		||||
    <text>: The text to place in the entry (overrides the above mechanism).
 | 
			
		||||
        This is useful when the text is constant.
 | 
			
		||||
 | 
			
		||||
Example node:
 | 
			
		||||
 | 
			
		||||
@ -332,6 +578,13 @@ It is also possible to put the string directly in the node:
 | 
			
		||||
        message = "a message directly in the node"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
or just:
 | 
			
		||||
 | 
			
		||||
    text {
 | 
			
		||||
        size = <8>;
 | 
			
		||||
        text = "some text directly in the node"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
The text is not itself nul-terminated. This can be achieved, if required,
 | 
			
		||||
by setting the size of the entry to something larger than the text.
 | 
			
		||||
 | 
			
		||||
@ -485,7 +738,7 @@ Entry: u-boot-spl-elf: U-Boot SPL ELF image
 | 
			
		||||
-------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments:
 | 
			
		||||
    - filename: Filename of SPL u-boot (default 'spl/u-boot')
 | 
			
		||||
    - filename: Filename of SPL u-boot (default 'spl/u-boot-spl')
 | 
			
		||||
 | 
			
		||||
This is the U-Boot SPL ELF image. It does not include a device tree but can
 | 
			
		||||
be relocated to any address for execution.
 | 
			
		||||
@ -563,6 +816,17 @@ process.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: u-boot-tpl-elf: U-Boot TPL ELF image
 | 
			
		||||
-------------------------------------------
 | 
			
		||||
 | 
			
		||||
Properties / Entry arguments:
 | 
			
		||||
    - filename: Filename of TPL u-boot (default 'tpl/u-boot-tpl')
 | 
			
		||||
 | 
			
		||||
This is the U-Boot TPL ELF image. It does not include a device tree but can
 | 
			
		||||
be relocated to any address for execution.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Entry: u-boot-tpl-with-ucode-ptr: U-Boot TPL with embedded microcode pointer
 | 
			
		||||
----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,23 +11,32 @@
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from distutils.sysconfig import get_python_lib
 | 
			
		||||
import glob
 | 
			
		||||
import multiprocessing
 | 
			
		||||
import os
 | 
			
		||||
import site
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
# Bring in the patman and dtoc libraries
 | 
			
		||||
# Bring in the patman and dtoc libraries (but don't override the first path
 | 
			
		||||
# in PYTHONPATH)
 | 
			
		||||
our_path = os.path.dirname(os.path.realpath(__file__))
 | 
			
		||||
for dirname in ['../patman', '../dtoc', '..', '../concurrencytest']:
 | 
			
		||||
    sys.path.insert(0, os.path.join(our_path, dirname))
 | 
			
		||||
    sys.path.insert(2, os.path.join(our_path, dirname))
 | 
			
		||||
 | 
			
		||||
# Bring in the libfdt module
 | 
			
		||||
sys.path.insert(0, 'scripts/dtc/pylibfdt')
 | 
			
		||||
sys.path.insert(0, os.path.join(our_path,
 | 
			
		||||
sys.path.insert(2, 'scripts/dtc/pylibfdt')
 | 
			
		||||
sys.path.insert(2, os.path.join(our_path,
 | 
			
		||||
                '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
 | 
			
		||||
 | 
			
		||||
# When running under python-coverage on Ubuntu 16.04, the dist-packages
 | 
			
		||||
# directories are dropped from the python path. Add them in so that we can find
 | 
			
		||||
# the elffile module. We could use site.getsitepackages() here but unfortunately
 | 
			
		||||
# that is not available in a virtualenv.
 | 
			
		||||
sys.path.append(get_python_lib())
 | 
			
		||||
 | 
			
		||||
import cmdline
 | 
			
		||||
import command
 | 
			
		||||
use_concurrent = True
 | 
			
		||||
@ -38,15 +47,23 @@ except:
 | 
			
		||||
import control
 | 
			
		||||
import test_util
 | 
			
		||||
 | 
			
		||||
def RunTests(debug, processes, args):
 | 
			
		||||
def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath):
 | 
			
		||||
    """Run the functional tests and any embedded doctests
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        debug: True to enable debugging, which shows a full stack trace on error
 | 
			
		||||
        args: List of positional args provided to binman. This can hold a test
 | 
			
		||||
            name to execute (as in 'binman -t testSections', for example)
 | 
			
		||||
        verbosity: Verbosity level to use
 | 
			
		||||
        test_preserve_dirs: True to preserve the input directory used by tests
 | 
			
		||||
            so that it can be examined afterwards (only useful for debugging
 | 
			
		||||
            tests). If a single test is selected (in args[0]) it also preserves
 | 
			
		||||
            the output directory for this test. Both directories are displayed
 | 
			
		||||
            on the command line.
 | 
			
		||||
        processes: Number of processes to use to run tests (None=same as #CPUs)
 | 
			
		||||
        args: List of positional args provided to binman. This can hold a test
 | 
			
		||||
            name to execute (as in 'binman test testSections', for example)
 | 
			
		||||
        toolpath: List of paths to use for tools
 | 
			
		||||
    """
 | 
			
		||||
    import cbfs_util_test
 | 
			
		||||
    import elf_test
 | 
			
		||||
    import entry_test
 | 
			
		||||
    import fdt_test
 | 
			
		||||
@ -63,8 +80,11 @@ def RunTests(debug, processes, args):
 | 
			
		||||
    sys.argv = [sys.argv[0]]
 | 
			
		||||
    if debug:
 | 
			
		||||
        sys.argv.append('-D')
 | 
			
		||||
    if debug:
 | 
			
		||||
        sys.argv.append('-D')
 | 
			
		||||
    if verbosity:
 | 
			
		||||
        sys.argv.append('-v%d' % verbosity)
 | 
			
		||||
    if toolpath:
 | 
			
		||||
        for path in toolpath:
 | 
			
		||||
            sys.argv += ['--toolpath', path]
 | 
			
		||||
 | 
			
		||||
    # Run the entry tests first ,since these need to be the first to import the
 | 
			
		||||
    # 'entry' module.
 | 
			
		||||
@ -72,7 +92,14 @@ def RunTests(debug, processes, args):
 | 
			
		||||
    suite = unittest.TestSuite()
 | 
			
		||||
    loader = unittest.TestLoader()
 | 
			
		||||
    for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt,
 | 
			
		||||
                   elf_test.TestElf, image_test.TestImage):
 | 
			
		||||
                   elf_test.TestElf, image_test.TestImage,
 | 
			
		||||
                   cbfs_util_test.TestCbfs):
 | 
			
		||||
        # Test the test module about our arguments, if it is interested
 | 
			
		||||
        if hasattr(module, 'setup_test_args'):
 | 
			
		||||
            setup_test_args = getattr(module, 'setup_test_args')
 | 
			
		||||
            setup_test_args(preserve_indir=test_preserve_dirs,
 | 
			
		||||
                preserve_outdirs=test_preserve_dirs and test_name is not None,
 | 
			
		||||
                toolpath=toolpath, verbosity=verbosity)
 | 
			
		||||
        if test_name:
 | 
			
		||||
            try:
 | 
			
		||||
                suite.addTests(loader.loadTestsFromName(test_name, module))
 | 
			
		||||
@ -104,9 +131,14 @@ def RunTests(debug, processes, args):
 | 
			
		||||
        print(test.id(), err)
 | 
			
		||||
    for test, err in result.failures:
 | 
			
		||||
        print(err, result.failures)
 | 
			
		||||
    if result.skipped:
 | 
			
		||||
        print('%d binman test%s SKIPPED:' %
 | 
			
		||||
              (len(result.skipped), 's' if len(result.skipped) > 1 else ''))
 | 
			
		||||
        for skip_info in result.skipped:
 | 
			
		||||
            print('%s: %s' % (skip_info[0], skip_info[1]))
 | 
			
		||||
    if result.errors or result.failures:
 | 
			
		||||
      print('binman tests FAILED')
 | 
			
		||||
      return 1
 | 
			
		||||
        print('binman tests FAILED')
 | 
			
		||||
        return 1
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
def GetEntryModules(include_testing=True):
 | 
			
		||||
@ -127,38 +159,36 @@ def RunTestCoverage():
 | 
			
		||||
                   for item in glob_list if '_testing' not in item])
 | 
			
		||||
    test_util.RunTestCoverage('tools/binman/binman.py', None,
 | 
			
		||||
            ['*test*', '*binman.py', 'tools/patman/*', 'tools/dtoc/*'],
 | 
			
		||||
            options.build_dir, all_set)
 | 
			
		||||
            args.build_dir, all_set)
 | 
			
		||||
 | 
			
		||||
def RunBinman(options, args):
 | 
			
		||||
def RunBinman(args):
 | 
			
		||||
    """Main entry point to binman once arguments are parsed
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        options: Command-line options
 | 
			
		||||
        args: Non-option arguments
 | 
			
		||||
        args: Command line arguments Namespace object
 | 
			
		||||
    """
 | 
			
		||||
    ret_code = 0
 | 
			
		||||
 | 
			
		||||
    # For testing: This enables full exception traces.
 | 
			
		||||
    #options.debug = True
 | 
			
		||||
 | 
			
		||||
    if not options.debug:
 | 
			
		||||
    if not args.debug:
 | 
			
		||||
        sys.tracebacklimit = 0
 | 
			
		||||
 | 
			
		||||
    if options.test:
 | 
			
		||||
        ret_code = RunTests(options.debug, options.processes, args[1:])
 | 
			
		||||
    if args.cmd == 'test':
 | 
			
		||||
        if args.test_coverage:
 | 
			
		||||
            RunTestCoverage()
 | 
			
		||||
        else:
 | 
			
		||||
            ret_code = RunTests(args.debug, args.verbosity, args.processes,
 | 
			
		||||
                                args.test_preserve_dirs, args.tests,
 | 
			
		||||
                                args.toolpath)
 | 
			
		||||
 | 
			
		||||
    elif options.test_coverage:
 | 
			
		||||
        RunTestCoverage()
 | 
			
		||||
 | 
			
		||||
    elif options.entry_docs:
 | 
			
		||||
    elif args.cmd == 'entry-docs':
 | 
			
		||||
        control.WriteEntryDocs(GetEntryModules())
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
        try:
 | 
			
		||||
            ret_code = control.Binman(options, args)
 | 
			
		||||
            ret_code = control.Binman(args)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            print('binman: %s' % e)
 | 
			
		||||
            if options.debug:
 | 
			
		||||
            if args.debug:
 | 
			
		||||
                print()
 | 
			
		||||
                traceback.print_exc()
 | 
			
		||||
            ret_code = 1
 | 
			
		||||
@ -166,6 +196,7 @@ def RunBinman(options, args):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    (options, args) = cmdline.ParseArgs(sys.argv)
 | 
			
		||||
    ret_code = RunBinman(options, args)
 | 
			
		||||
    args = cmdline.ParseArgs(sys.argv[1:])
 | 
			
		||||
 | 
			
		||||
    ret_code = RunBinman(args)
 | 
			
		||||
    sys.exit(ret_code)
 | 
			
		||||
 | 
			
		||||
@ -1,464 +0,0 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright (c) 2018 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
#
 | 
			
		||||
# Base class for sections (collections of entries)
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import fdt_util
 | 
			
		||||
import re
 | 
			
		||||
import state
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
class Section(object):
 | 
			
		||||
    """A section which contains multiple entries
 | 
			
		||||
 | 
			
		||||
    A section represents a collection of entries. There must be one or more
 | 
			
		||||
    sections in an image. Sections are used to group entries together.
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        _node: Node object that contains the section definition in device tree
 | 
			
		||||
        _parent_section: Parent Section object which created this Section
 | 
			
		||||
        _size: Section size in bytes, or None if not known yet
 | 
			
		||||
        _align_size: Section size alignment, or None
 | 
			
		||||
        _pad_before: Number of bytes before the first entry starts. This
 | 
			
		||||
            effectively changes the place where entry offset 0 starts
 | 
			
		||||
        _pad_after: Number of bytes after the last entry ends. The last
 | 
			
		||||
            entry will finish on or before this boundary
 | 
			
		||||
        _pad_byte: Byte to use to pad the section where there is no entry
 | 
			
		||||
        _sort: True if entries should be sorted by offset, False if they
 | 
			
		||||
            must be in-order in the device tree description
 | 
			
		||||
        _skip_at_start: Number of bytes before the first entry starts. These
 | 
			
		||||
            effectively adjust the starting offset of entries. For example,
 | 
			
		||||
            if _pad_before is 16, then the first entry would start at 16.
 | 
			
		||||
            An entry with offset = 20 would in fact be written at offset 4
 | 
			
		||||
            in the image file.
 | 
			
		||||
        _end_4gb: Indicates that the section ends at the 4GB boundary. This is
 | 
			
		||||
            used for x86 images, which want to use offsets such that a memory
 | 
			
		||||
            address (like 0xff800000) is the first entry offset. This causes
 | 
			
		||||
            _skip_at_start to be set to the starting memory address.
 | 
			
		||||
        _name_prefix: Prefix to add to the name of all entries within this
 | 
			
		||||
            section
 | 
			
		||||
        _entries: OrderedDict() of entries
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, name, parent_section, node, image, test=False):
 | 
			
		||||
        global entry
 | 
			
		||||
        global Entry
 | 
			
		||||
        import entry
 | 
			
		||||
        from entry import Entry
 | 
			
		||||
 | 
			
		||||
        self._parent_section = parent_section
 | 
			
		||||
        self._name = name
 | 
			
		||||
        self._node = node
 | 
			
		||||
        self._image = image
 | 
			
		||||
        self._offset = None
 | 
			
		||||
        self._size = None
 | 
			
		||||
        self._align_size = None
 | 
			
		||||
        self._pad_before = 0
 | 
			
		||||
        self._pad_after = 0
 | 
			
		||||
        self._pad_byte = 0
 | 
			
		||||
        self._sort = False
 | 
			
		||||
        self._skip_at_start = None
 | 
			
		||||
        self._end_4gb = False
 | 
			
		||||
        self._name_prefix = ''
 | 
			
		||||
        self._entries = OrderedDict()
 | 
			
		||||
        self._image_pos = None
 | 
			
		||||
        if not test:
 | 
			
		||||
            self._ReadNode()
 | 
			
		||||
            self._ReadEntries()
 | 
			
		||||
 | 
			
		||||
    def _ReadNode(self):
 | 
			
		||||
        """Read properties from the section node"""
 | 
			
		||||
        self._offset = fdt_util.GetInt(self._node, 'offset')
 | 
			
		||||
        self._size = fdt_util.GetInt(self._node, 'size')
 | 
			
		||||
        self._align_size = fdt_util.GetInt(self._node, 'align-size')
 | 
			
		||||
        if tools.NotPowerOfTwo(self._align_size):
 | 
			
		||||
            self._Raise("Alignment size %s must be a power of two" %
 | 
			
		||||
                        self._align_size)
 | 
			
		||||
        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
 | 
			
		||||
        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
 | 
			
		||||
        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
 | 
			
		||||
        self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
 | 
			
		||||
        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
 | 
			
		||||
        self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start')
 | 
			
		||||
        if self._end_4gb:
 | 
			
		||||
            if not self._size:
 | 
			
		||||
                self._Raise("Section size must be provided when using end-at-4gb")
 | 
			
		||||
            if self._skip_at_start is not None:
 | 
			
		||||
                self._Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
 | 
			
		||||
            else:
 | 
			
		||||
                self._skip_at_start = 0x100000000 - self._size
 | 
			
		||||
        else:
 | 
			
		||||
            if self._skip_at_start is None:
 | 
			
		||||
                self._skip_at_start = 0
 | 
			
		||||
        self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
 | 
			
		||||
 | 
			
		||||
    def _ReadEntries(self):
 | 
			
		||||
        for node in self._node.subnodes:
 | 
			
		||||
            if node.name == 'hash':
 | 
			
		||||
                continue
 | 
			
		||||
            entry = Entry.Create(self, node)
 | 
			
		||||
            entry.SetPrefix(self._name_prefix)
 | 
			
		||||
            self._entries[node.name] = entry
 | 
			
		||||
 | 
			
		||||
    def GetFdtSet(self):
 | 
			
		||||
        """Get the set of device tree files used by this image"""
 | 
			
		||||
        fdt_set = set()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            fdt_set.update(entry.GetFdtSet())
 | 
			
		||||
        return fdt_set
 | 
			
		||||
 | 
			
		||||
    def SetOffset(self, offset):
 | 
			
		||||
        self._offset = offset
 | 
			
		||||
 | 
			
		||||
    def ExpandEntries(self):
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.ExpandEntries()
 | 
			
		||||
 | 
			
		||||
    def AddMissingProperties(self):
 | 
			
		||||
        """Add new properties to the device tree as needed for this entry"""
 | 
			
		||||
        for prop in ['offset', 'size', 'image-pos']:
 | 
			
		||||
            if not prop in self._node.props:
 | 
			
		||||
                state.AddZeroProp(self._node, prop)
 | 
			
		||||
        state.CheckAddHashProp(self._node)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.AddMissingProperties()
 | 
			
		||||
 | 
			
		||||
    def SetCalculatedProperties(self):
 | 
			
		||||
        state.SetInt(self._node, 'offset', self._offset or 0)
 | 
			
		||||
        state.SetInt(self._node, 'size', self._size)
 | 
			
		||||
        image_pos = self._image_pos
 | 
			
		||||
        if self._parent_section:
 | 
			
		||||
            image_pos -= self._parent_section.GetRootSkipAtStart()
 | 
			
		||||
        state.SetInt(self._node, 'image-pos', image_pos)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.SetCalculatedProperties()
 | 
			
		||||
 | 
			
		||||
    def ProcessFdt(self, fdt):
 | 
			
		||||
        todo = self._entries.values()
 | 
			
		||||
        for passnum in range(3):
 | 
			
		||||
            next_todo = []
 | 
			
		||||
            for entry in todo:
 | 
			
		||||
                if not entry.ProcessFdt(fdt):
 | 
			
		||||
                    next_todo.append(entry)
 | 
			
		||||
            todo = next_todo
 | 
			
		||||
            if not todo:
 | 
			
		||||
                break
 | 
			
		||||
        if todo:
 | 
			
		||||
            self._Raise('Internal error: Could not complete processing of Fdt: '
 | 
			
		||||
                        'remaining %s' % todo)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def CheckSize(self):
 | 
			
		||||
        """Check that the section contents does not exceed its size, etc."""
 | 
			
		||||
        contents_size = 0
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            contents_size = max(contents_size, entry.offset + entry.size)
 | 
			
		||||
 | 
			
		||||
        contents_size -= self._skip_at_start
 | 
			
		||||
 | 
			
		||||
        size = self._size
 | 
			
		||||
        if not size:
 | 
			
		||||
            size = self._pad_before + contents_size + self._pad_after
 | 
			
		||||
            size = tools.Align(size, self._align_size)
 | 
			
		||||
 | 
			
		||||
        if self._size and contents_size > self._size:
 | 
			
		||||
            self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
 | 
			
		||||
                       (contents_size, contents_size, self._size, self._size))
 | 
			
		||||
        if not self._size:
 | 
			
		||||
            self._size = size
 | 
			
		||||
        if self._size != tools.Align(self._size, self._align_size):
 | 
			
		||||
            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
 | 
			
		||||
                  (self._size, self._size, self._align_size, self._align_size))
 | 
			
		||||
        return size
 | 
			
		||||
 | 
			
		||||
    def _Raise(self, msg):
 | 
			
		||||
        """Raises an error for this section
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            msg: Error message to use in the raise string
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError()
 | 
			
		||||
        """
 | 
			
		||||
        raise ValueError("Section '%s': %s" % (self._node.path, msg))
 | 
			
		||||
 | 
			
		||||
    def GetPath(self):
 | 
			
		||||
        """Get the path of an image (in the FDT)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Full path of the node for this image
 | 
			
		||||
        """
 | 
			
		||||
        return self._node.path
 | 
			
		||||
 | 
			
		||||
    def FindEntryType(self, etype):
 | 
			
		||||
        """Find an entry type in the section
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            etype: Entry type to find
 | 
			
		||||
        Returns:
 | 
			
		||||
            entry matching that type, or None if not found
 | 
			
		||||
        """
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if entry.etype == etype:
 | 
			
		||||
                return entry
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def GetEntryContents(self):
 | 
			
		||||
        """Call ObtainContents() for each entry
 | 
			
		||||
 | 
			
		||||
        This calls each entry's ObtainContents() a few times until they all
 | 
			
		||||
        return True. We stop calling an entry's function once it returns
 | 
			
		||||
        True. This allows the contents of one entry to depend on another.
 | 
			
		||||
 | 
			
		||||
        After 3 rounds we give up since it's likely an error.
 | 
			
		||||
        """
 | 
			
		||||
        todo = self._entries.values()
 | 
			
		||||
        for passnum in range(3):
 | 
			
		||||
            next_todo = []
 | 
			
		||||
            for entry in todo:
 | 
			
		||||
                if not entry.ObtainContents():
 | 
			
		||||
                    next_todo.append(entry)
 | 
			
		||||
            todo = next_todo
 | 
			
		||||
            if not todo:
 | 
			
		||||
                break
 | 
			
		||||
        if todo:
 | 
			
		||||
            self._Raise('Internal error: Could not complete processing of '
 | 
			
		||||
                        'contents: remaining %s' % todo)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _SetEntryOffsetSize(self, name, offset, size):
 | 
			
		||||
        """Set the offset and size of an entry
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name: Entry name to update
 | 
			
		||||
            offset: New offset
 | 
			
		||||
            size: New size
 | 
			
		||||
        """
 | 
			
		||||
        entry = self._entries.get(name)
 | 
			
		||||
        if not entry:
 | 
			
		||||
            self._Raise("Unable to set offset/size for unknown entry '%s'" %
 | 
			
		||||
                        name)
 | 
			
		||||
        entry.SetOffsetSize(self._skip_at_start + offset, size)
 | 
			
		||||
 | 
			
		||||
    def GetEntryOffsets(self):
 | 
			
		||||
        """Handle entries that want to set the offset/size of other entries
 | 
			
		||||
 | 
			
		||||
        This calls each entry's GetOffsets() method. If it returns a list
 | 
			
		||||
        of entries to update, it updates them.
 | 
			
		||||
        """
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            offset_dict = entry.GetOffsets()
 | 
			
		||||
            for name, info in offset_dict.items():
 | 
			
		||||
                self._SetEntryOffsetSize(name, *info)
 | 
			
		||||
 | 
			
		||||
    def PackEntries(self):
 | 
			
		||||
        """Pack all entries into the section"""
 | 
			
		||||
        offset = self._skip_at_start
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            offset = entry.Pack(offset)
 | 
			
		||||
        self._size = self.CheckSize()
 | 
			
		||||
 | 
			
		||||
    def _SortEntries(self):
 | 
			
		||||
        """Sort entries by offset"""
 | 
			
		||||
        entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
 | 
			
		||||
        self._entries.clear()
 | 
			
		||||
        for entry in entries:
 | 
			
		||||
            self._entries[entry._node.name] = entry
 | 
			
		||||
 | 
			
		||||
    def _ExpandEntries(self):
 | 
			
		||||
        """Expand any entries that are permitted to"""
 | 
			
		||||
        exp_entry = None
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if exp_entry:
 | 
			
		||||
                exp_entry.ExpandToLimit(entry.offset)
 | 
			
		||||
                exp_entry = None
 | 
			
		||||
            if entry.expand_size:
 | 
			
		||||
                exp_entry = entry
 | 
			
		||||
        if exp_entry:
 | 
			
		||||
            exp_entry.ExpandToLimit(self._size)
 | 
			
		||||
 | 
			
		||||
    def CheckEntries(self):
 | 
			
		||||
        """Check that entries do not overlap or extend outside the section
 | 
			
		||||
 | 
			
		||||
        This also sorts entries, if needed and expands
 | 
			
		||||
        """
 | 
			
		||||
        if self._sort:
 | 
			
		||||
            self._SortEntries()
 | 
			
		||||
        self._ExpandEntries()
 | 
			
		||||
        offset = 0
 | 
			
		||||
        prev_name = 'None'
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.CheckOffset()
 | 
			
		||||
            if (entry.offset < self._skip_at_start or
 | 
			
		||||
                entry.offset + entry.size > self._skip_at_start + self._size):
 | 
			
		||||
                entry.Raise("Offset %#x (%d) is outside the section starting "
 | 
			
		||||
                            "at %#x (%d)" %
 | 
			
		||||
                            (entry.offset, entry.offset, self._skip_at_start,
 | 
			
		||||
                             self._skip_at_start))
 | 
			
		||||
            if entry.offset < offset:
 | 
			
		||||
                entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
 | 
			
		||||
                            "ending at %#x (%d)" %
 | 
			
		||||
                            (entry.offset, entry.offset, prev_name, offset, offset))
 | 
			
		||||
            offset = entry.offset + entry.size
 | 
			
		||||
            prev_name = entry.GetPath()
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self, image_pos):
 | 
			
		||||
        self._image_pos = image_pos
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.SetImagePos(image_pos)
 | 
			
		||||
 | 
			
		||||
    def ProcessEntryContents(self):
 | 
			
		||||
        """Call the ProcessContents() method for each entry
 | 
			
		||||
 | 
			
		||||
        This is intended to adjust the contents as needed by the entry type.
 | 
			
		||||
        """
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.ProcessContents()
 | 
			
		||||
 | 
			
		||||
    def WriteSymbols(self):
 | 
			
		||||
        """Write symbol values into binary files for access at run time"""
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.WriteSymbols(self)
 | 
			
		||||
 | 
			
		||||
    def BuildSection(self, fd, base_offset):
 | 
			
		||||
        """Write the section to a file"""
 | 
			
		||||
        fd.seek(base_offset)
 | 
			
		||||
        fd.write(self.GetData())
 | 
			
		||||
 | 
			
		||||
    def GetData(self):
 | 
			
		||||
        """Get the contents of the section"""
 | 
			
		||||
        section_data = tools.GetBytes(self._pad_byte, self._size)
 | 
			
		||||
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            data = entry.GetData()
 | 
			
		||||
            base = self._pad_before + entry.offset - self._skip_at_start
 | 
			
		||||
            section_data = (section_data[:base] + data +
 | 
			
		||||
                            section_data[base + len(data):])
 | 
			
		||||
        return section_data
 | 
			
		||||
 | 
			
		||||
    def LookupSymbol(self, sym_name, optional, msg):
 | 
			
		||||
        """Look up a symbol in an ELF file
 | 
			
		||||
 | 
			
		||||
        Looks up a symbol in an ELF file. Only entry types which come from an
 | 
			
		||||
        ELF image can be used by this function.
 | 
			
		||||
 | 
			
		||||
        At present the only entry property supported is offset.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            sym_name: Symbol name in the ELF file to look up in the format
 | 
			
		||||
                _binman_<entry>_prop_<property> where <entry> is the name of
 | 
			
		||||
                the entry and <property> is the property to find (e.g.
 | 
			
		||||
                _binman_u_boot_prop_offset). As a special case, you can append
 | 
			
		||||
                _any to <entry> to have it search for any matching entry. E.g.
 | 
			
		||||
                _binman_u_boot_any_prop_offset will match entries called u-boot,
 | 
			
		||||
                u-boot-img and u-boot-nodtb)
 | 
			
		||||
            optional: True if the symbol is optional. If False this function
 | 
			
		||||
                will raise if the symbol is not found
 | 
			
		||||
            msg: Message to display if an error occurs
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Value that should be assigned to that symbol, or None if it was
 | 
			
		||||
                optional and not found
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if the symbol is invalid or not found, or references a
 | 
			
		||||
                property which is not supported
 | 
			
		||||
        """
 | 
			
		||||
        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
 | 
			
		||||
        if not m:
 | 
			
		||||
            raise ValueError("%s: Symbol '%s' has invalid format" %
 | 
			
		||||
                             (msg, sym_name))
 | 
			
		||||
        entry_name, prop_name = m.groups()
 | 
			
		||||
        entry_name = entry_name.replace('_', '-')
 | 
			
		||||
        entry = self._entries.get(entry_name)
 | 
			
		||||
        if not entry:
 | 
			
		||||
            if entry_name.endswith('-any'):
 | 
			
		||||
                root = entry_name[:-4]
 | 
			
		||||
                for name in self._entries:
 | 
			
		||||
                    if name.startswith(root):
 | 
			
		||||
                        rest = name[len(root):]
 | 
			
		||||
                        if rest in ['', '-img', '-nodtb']:
 | 
			
		||||
                            entry = self._entries[name]
 | 
			
		||||
        if not entry:
 | 
			
		||||
            err = ("%s: Entry '%s' not found in list (%s)" %
 | 
			
		||||
                   (msg, entry_name, ','.join(self._entries.keys())))
 | 
			
		||||
            if optional:
 | 
			
		||||
                print('Warning: %s' % err, file=sys.stderr)
 | 
			
		||||
                return None
 | 
			
		||||
            raise ValueError(err)
 | 
			
		||||
        if prop_name == 'offset':
 | 
			
		||||
            return entry.offset
 | 
			
		||||
        elif prop_name == 'image_pos':
 | 
			
		||||
            return entry.image_pos
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
 | 
			
		||||
 | 
			
		||||
    def GetEntries(self):
 | 
			
		||||
        """Get the number of entries in a section
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Number of entries in a section
 | 
			
		||||
        """
 | 
			
		||||
        return self._entries
 | 
			
		||||
 | 
			
		||||
    def GetSize(self):
 | 
			
		||||
        """Get the size of a section in bytes
 | 
			
		||||
 | 
			
		||||
        This is only meaningful if the section has a pre-defined size, or the
 | 
			
		||||
        entries within it have been packed, so that the size has been
 | 
			
		||||
        calculated.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Entry size in bytes
 | 
			
		||||
        """
 | 
			
		||||
        return self._size
 | 
			
		||||
 | 
			
		||||
    def WriteMap(self, fd, indent):
 | 
			
		||||
        """Write a map of the section to a .map file
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to write the map to
 | 
			
		||||
        """
 | 
			
		||||
        Entry.WriteMapLine(fd, indent, self._name, self._offset or 0,
 | 
			
		||||
                           self._size, self._image_pos)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.WriteMap(fd, indent + 1)
 | 
			
		||||
 | 
			
		||||
    def GetContentsByPhandle(self, phandle, source_entry):
 | 
			
		||||
        """Get the data contents of an entry specified by a phandle
 | 
			
		||||
 | 
			
		||||
        This uses a phandle to look up a node and and find the entry
 | 
			
		||||
        associated with it. Then it returnst he contents of that entry.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            phandle: Phandle to look up (integer)
 | 
			
		||||
            source_entry: Entry containing that phandle (used for error
 | 
			
		||||
                reporting)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            data from associated entry (as a string), or None if not found
 | 
			
		||||
        """
 | 
			
		||||
        node = self._node.GetFdt().LookupPhandle(phandle)
 | 
			
		||||
        if not node:
 | 
			
		||||
            source_entry.Raise("Cannot find node for phandle %d" % phandle)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if entry._node == node:
 | 
			
		||||
                return entry.GetData()
 | 
			
		||||
        source_entry.Raise("Cannot find entry for node '%s'" % node.name)
 | 
			
		||||
 | 
			
		||||
    def ExpandSize(self, size):
 | 
			
		||||
        if size != self._size:
 | 
			
		||||
            self._size = size
 | 
			
		||||
 | 
			
		||||
    def GetRootSkipAtStart(self):
 | 
			
		||||
        if self._parent_section:
 | 
			
		||||
            return self._parent_section.GetRootSkipAtStart()
 | 
			
		||||
        return self._skip_at_start
 | 
			
		||||
 | 
			
		||||
    def GetImageSize(self):
 | 
			
		||||
        return self._image._size
 | 
			
		||||
							
								
								
									
										887
									
								
								tools/binman/cbfs_util.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										887
									
								
								tools/binman/cbfs_util.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,887 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright 2019 Google LLC
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
 | 
			
		||||
"""Support for coreboot's CBFS format
 | 
			
		||||
 | 
			
		||||
CBFS supports a header followed by a number of files, generally targeted at SPI
 | 
			
		||||
flash.
 | 
			
		||||
 | 
			
		||||
The format is somewhat defined by documentation in the coreboot tree although
 | 
			
		||||
it is necessary to rely on the C structures and source code (mostly cbfstool)
 | 
			
		||||
to fully understand it.
 | 
			
		||||
 | 
			
		||||
Currently supported: raw and stage types with compression, padding empty areas
 | 
			
		||||
    with empty files, fixed-offset files
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
import io
 | 
			
		||||
import struct
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
import command
 | 
			
		||||
import elf
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
# Set to True to enable printing output while working
 | 
			
		||||
DEBUG = False
 | 
			
		||||
 | 
			
		||||
# Set to True to enable output from running cbfstool for debugging
 | 
			
		||||
VERBOSE = False
 | 
			
		||||
 | 
			
		||||
# The master header, at the start of the CBFS
 | 
			
		||||
HEADER_FORMAT      = '>IIIIIIII'
 | 
			
		||||
HEADER_LEN         = 0x20
 | 
			
		||||
HEADER_MAGIC       = 0x4f524243
 | 
			
		||||
HEADER_VERSION1    = 0x31313131
 | 
			
		||||
HEADER_VERSION2    = 0x31313132
 | 
			
		||||
 | 
			
		||||
# The file header, at the start of each file in the CBFS
 | 
			
		||||
FILE_HEADER_FORMAT = b'>8sIIII'
 | 
			
		||||
FILE_HEADER_LEN    = 0x18
 | 
			
		||||
FILE_MAGIC         = b'LARCHIVE'
 | 
			
		||||
FILENAME_ALIGN     = 16  # Filename lengths are aligned to this
 | 
			
		||||
 | 
			
		||||
# A stage header containing information about 'stage' files
 | 
			
		||||
# Yes this is correct: this header is in litte-endian format
 | 
			
		||||
STAGE_FORMAT       = '<IQQII'
 | 
			
		||||
STAGE_LEN          = 0x1c
 | 
			
		||||
 | 
			
		||||
# An attribute describring the compression used in a file
 | 
			
		||||
ATTR_COMPRESSION_FORMAT = '>IIII'
 | 
			
		||||
ATTR_COMPRESSION_LEN = 0x10
 | 
			
		||||
 | 
			
		||||
# Attribute tags
 | 
			
		||||
# Depending on how the header was initialised, it may be backed with 0x00 or
 | 
			
		||||
# 0xff. Support both.
 | 
			
		||||
FILE_ATTR_TAG_UNUSED        = 0
 | 
			
		||||
FILE_ATTR_TAG_UNUSED2       = 0xffffffff
 | 
			
		||||
FILE_ATTR_TAG_COMPRESSION   = 0x42435a4c
 | 
			
		||||
FILE_ATTR_TAG_HASH          = 0x68736148
 | 
			
		||||
FILE_ATTR_TAG_POSITION      = 0x42435350  # PSCB
 | 
			
		||||
FILE_ATTR_TAG_ALIGNMENT     = 0x42434c41  # ALCB
 | 
			
		||||
FILE_ATTR_TAG_PADDING       = 0x47444150  # PDNG
 | 
			
		||||
 | 
			
		||||
# This is 'the size of bootblock reserved in firmware image (cbfs.txt)'
 | 
			
		||||
# Not much more info is available, but we set it to 4, due to this comment in
 | 
			
		||||
# cbfstool.c:
 | 
			
		||||
# This causes 4 bytes to be left out at the end of the image, for two reasons:
 | 
			
		||||
# 1. The cbfs master header pointer resides there
 | 
			
		||||
# 2. Ssme cbfs implementations assume that an image that resides below 4GB has
 | 
			
		||||
#    a bootblock and get confused when the end of the image is at 4GB == 0.
 | 
			
		||||
MIN_BOOTBLOCK_SIZE     = 4
 | 
			
		||||
 | 
			
		||||
# Files start aligned to this boundary in the CBFS
 | 
			
		||||
ENTRY_ALIGN    = 0x40
 | 
			
		||||
 | 
			
		||||
# CBFSs must declare an architecture since much of the logic is designed with
 | 
			
		||||
# x86 in mind. The effect of setting this value is not well documented, but in
 | 
			
		||||
# general x86 is used and this makes use of a boot block and an image that ends
 | 
			
		||||
# at the end of 32-bit address space.
 | 
			
		||||
ARCHITECTURE_UNKNOWN  = 0xffffffff
 | 
			
		||||
ARCHITECTURE_X86      = 0x00000001
 | 
			
		||||
ARCHITECTURE_ARM      = 0x00000010
 | 
			
		||||
ARCHITECTURE_AARCH64  = 0x0000aa64
 | 
			
		||||
ARCHITECTURE_MIPS     = 0x00000100
 | 
			
		||||
ARCHITECTURE_RISCV    = 0xc001d0de
 | 
			
		||||
ARCHITECTURE_PPC64    = 0x407570ff
 | 
			
		||||
 | 
			
		||||
ARCH_NAMES = {
 | 
			
		||||
    ARCHITECTURE_UNKNOWN  : 'unknown',
 | 
			
		||||
    ARCHITECTURE_X86      : 'x86',
 | 
			
		||||
    ARCHITECTURE_ARM      : 'arm',
 | 
			
		||||
    ARCHITECTURE_AARCH64  : 'arm64',
 | 
			
		||||
    ARCHITECTURE_MIPS     : 'mips',
 | 
			
		||||
    ARCHITECTURE_RISCV    : 'riscv',
 | 
			
		||||
    ARCHITECTURE_PPC64    : 'ppc64',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
# File types. Only supported ones are included here
 | 
			
		||||
TYPE_CBFSHEADER     = 0x02   # Master header, HEADER_FORMAT
 | 
			
		||||
TYPE_STAGE          = 0x10   # Stage, holding an executable, see STAGE_FORMAT
 | 
			
		||||
TYPE_RAW            = 0x50   # Raw file, possibly compressed
 | 
			
		||||
TYPE_EMPTY          = 0xffffffff     # Empty data
 | 
			
		||||
 | 
			
		||||
# Compression types
 | 
			
		||||
COMPRESS_NONE, COMPRESS_LZMA, COMPRESS_LZ4 = range(3)
 | 
			
		||||
 | 
			
		||||
COMPRESS_NAMES = {
 | 
			
		||||
    COMPRESS_NONE : 'none',
 | 
			
		||||
    COMPRESS_LZMA : 'lzma',
 | 
			
		||||
    COMPRESS_LZ4  : 'lz4',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
def find_arch(find_name):
 | 
			
		||||
    """Look up an architecture name
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        find_name: Architecture name to find
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        ARCHITECTURE_... value or None if not found
 | 
			
		||||
    """
 | 
			
		||||
    for arch, name in ARCH_NAMES.items():
 | 
			
		||||
        if name == find_name:
 | 
			
		||||
            return arch
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def find_compress(find_name):
 | 
			
		||||
    """Look up a compression algorithm name
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        find_name: Compression algorithm name to find
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        COMPRESS_... value or None if not found
 | 
			
		||||
    """
 | 
			
		||||
    for compress, name in COMPRESS_NAMES.items():
 | 
			
		||||
        if name == find_name:
 | 
			
		||||
            return compress
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
def compress_name(compress):
 | 
			
		||||
    """Look up the name of a compression algorithm
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        compress: Compression algorithm number to find (COMPRESS_...)
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Compression algorithm name (string)
 | 
			
		||||
 | 
			
		||||
    Raises:
 | 
			
		||||
        KeyError if the algorithm number is invalid
 | 
			
		||||
    """
 | 
			
		||||
    return COMPRESS_NAMES[compress]
 | 
			
		||||
 | 
			
		||||
def align_int(val, align):
 | 
			
		||||
    """Align a value up to the given alignment
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        val: Integer value to align
 | 
			
		||||
        align: Integer alignment value (e.g. 4 to align to 4-byte boundary)
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        integer value aligned to the required boundary, rounding up if necessary
 | 
			
		||||
    """
 | 
			
		||||
    return int((val + align - 1) / align) * align
 | 
			
		||||
 | 
			
		||||
def align_int_down(val, align):
 | 
			
		||||
    """Align a value down to the given alignment
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        val: Integer value to align
 | 
			
		||||
        align: Integer alignment value (e.g. 4 to align to 4-byte boundary)
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        integer value aligned to the required boundary, rounding down if
 | 
			
		||||
            necessary
 | 
			
		||||
    """
 | 
			
		||||
    return int(val / align) * align
 | 
			
		||||
 | 
			
		||||
def _pack_string(instr):
 | 
			
		||||
    """Pack a string to the required aligned size by adding padding
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        instr: String to process
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        String with required padding (at least one 0x00 byte) at the end
 | 
			
		||||
    """
 | 
			
		||||
    val = tools.ToBytes(instr)
 | 
			
		||||
    pad_len = align_int(len(val) + 1, FILENAME_ALIGN)
 | 
			
		||||
    return val + tools.GetBytes(0, pad_len - len(val))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CbfsFile(object):
 | 
			
		||||
    """Class to represent a single CBFS file
 | 
			
		||||
 | 
			
		||||
    This is used to hold the information about a file, including its contents.
 | 
			
		||||
    Use the get_data_and_offset() method to obtain the raw output for writing to
 | 
			
		||||
    CBFS.
 | 
			
		||||
 | 
			
		||||
    Properties:
 | 
			
		||||
        name: Name of file
 | 
			
		||||
        offset: Offset of file data from start of file header
 | 
			
		||||
        cbfs_offset: Offset of file data in bytes from start of CBFS, or None to
 | 
			
		||||
            place this file anyway
 | 
			
		||||
        data: Contents of file, uncompressed
 | 
			
		||||
        data_len: Length of (possibly compressed) data in bytes
 | 
			
		||||
        ftype: File type (TYPE_...)
 | 
			
		||||
        compression: Compression type (COMPRESS_...)
 | 
			
		||||
        memlen: Length of data in memory, i.e. the uncompressed length, None if
 | 
			
		||||
            no compression algortihm is selected
 | 
			
		||||
        load: Load address in memory if known, else None
 | 
			
		||||
        entry: Entry address in memory if known, else None. This is where
 | 
			
		||||
            execution starts after the file is loaded
 | 
			
		||||
        base_address: Base address to use for 'stage' files
 | 
			
		||||
        erase_byte: Erase byte to use for padding between the file header and
 | 
			
		||||
            contents (used for empty files)
 | 
			
		||||
        size: Size of the file in bytes (used for empty files)
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, name, ftype, data, cbfs_offset, compress=COMPRESS_NONE):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.offset = None
 | 
			
		||||
        self.cbfs_offset = cbfs_offset
 | 
			
		||||
        self.data = data
 | 
			
		||||
        self.ftype = ftype
 | 
			
		||||
        self.compress = compress
 | 
			
		||||
        self.memlen = None
 | 
			
		||||
        self.load = None
 | 
			
		||||
        self.entry = None
 | 
			
		||||
        self.base_address = None
 | 
			
		||||
        self.data_len = len(data)
 | 
			
		||||
        self.erase_byte = None
 | 
			
		||||
        self.size = None
 | 
			
		||||
 | 
			
		||||
    def decompress(self):
 | 
			
		||||
        """Handle decompressing data if necessary"""
 | 
			
		||||
        indata = self.data
 | 
			
		||||
        if self.compress == COMPRESS_LZ4:
 | 
			
		||||
            data = tools.Decompress(indata, 'lz4')
 | 
			
		||||
        elif self.compress == COMPRESS_LZMA:
 | 
			
		||||
            data = tools.Decompress(indata, 'lzma')
 | 
			
		||||
        else:
 | 
			
		||||
            data = indata
 | 
			
		||||
        self.memlen = len(data)
 | 
			
		||||
        self.data = data
 | 
			
		||||
        self.data_len = len(indata)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def stage(cls, base_address, name, data, cbfs_offset):
 | 
			
		||||
        """Create a new stage file
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            base_address: Int base address for memory-mapping of ELF file
 | 
			
		||||
            name: String file name to put in CBFS (does not need to correspond
 | 
			
		||||
                to the name that the file originally came from)
 | 
			
		||||
            data: Contents of file
 | 
			
		||||
            cbfs_offset: Offset of file data in bytes from start of CBFS, or
 | 
			
		||||
                None to place this file anyway
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object containing the file information
 | 
			
		||||
        """
 | 
			
		||||
        cfile = CbfsFile(name, TYPE_STAGE, data, cbfs_offset)
 | 
			
		||||
        cfile.base_address = base_address
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def raw(cls, name, data, cbfs_offset, compress):
 | 
			
		||||
        """Create a new raw file
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name: String file name to put in CBFS (does not need to correspond
 | 
			
		||||
                to the name that the file originally came from)
 | 
			
		||||
            data: Contents of file
 | 
			
		||||
            cbfs_offset: Offset of file data in bytes from start of CBFS, or
 | 
			
		||||
                None to place this file anyway
 | 
			
		||||
            compress: Compression algorithm to use (COMPRESS_...)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object containing the file information
 | 
			
		||||
        """
 | 
			
		||||
        return CbfsFile(name, TYPE_RAW, data, cbfs_offset, compress)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def empty(cls, space_to_use, erase_byte):
 | 
			
		||||
        """Create a new empty file of a given size
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            space_to_use:: Size of available space, which must be at least as
 | 
			
		||||
                large as the alignment size for this CBFS
 | 
			
		||||
            erase_byte: Byte to use for contents of file (repeated through the
 | 
			
		||||
                whole file)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object containing the file information
 | 
			
		||||
        """
 | 
			
		||||
        cfile = CbfsFile('', TYPE_EMPTY, b'', None)
 | 
			
		||||
        cfile.size = space_to_use - FILE_HEADER_LEN - FILENAME_ALIGN
 | 
			
		||||
        cfile.erase_byte = erase_byte
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    def calc_start_offset(self):
 | 
			
		||||
        """Check if this file needs to start at a particular offset in CBFS
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            None if the file can be placed anywhere, or
 | 
			
		||||
            the largest offset where the file could start (integer)
 | 
			
		||||
        """
 | 
			
		||||
        if self.cbfs_offset is None:
 | 
			
		||||
            return None
 | 
			
		||||
        return self.cbfs_offset - self.get_header_len()
 | 
			
		||||
 | 
			
		||||
    def get_header_len(self):
 | 
			
		||||
        """Get the length of headers required for a file
 | 
			
		||||
 | 
			
		||||
        This is the minimum length required before the actual data for this file
 | 
			
		||||
        could start. It might start later if there is padding.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Total length of all non-data fields, in bytes
 | 
			
		||||
        """
 | 
			
		||||
        name = _pack_string(self.name)
 | 
			
		||||
        hdr_len = len(name) + FILE_HEADER_LEN
 | 
			
		||||
        if self.ftype == TYPE_STAGE:
 | 
			
		||||
            pass
 | 
			
		||||
        elif self.ftype == TYPE_RAW:
 | 
			
		||||
            hdr_len += ATTR_COMPRESSION_LEN
 | 
			
		||||
        elif self.ftype == TYPE_EMPTY:
 | 
			
		||||
            pass
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Unknown file type %#x\n' % self.ftype)
 | 
			
		||||
        return hdr_len
 | 
			
		||||
 | 
			
		||||
    def get_data_and_offset(self, offset=None, pad_byte=None):
 | 
			
		||||
        """Obtain the contents of the file, in CBFS format and the offset of
 | 
			
		||||
        the data within the file
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            tuple:
 | 
			
		||||
                bytes representing the contents of this file, packed and aligned
 | 
			
		||||
                    for directly inserting into the final CBFS output
 | 
			
		||||
                offset to the file data from the start of the returned data.
 | 
			
		||||
        """
 | 
			
		||||
        name = _pack_string(self.name)
 | 
			
		||||
        hdr_len = len(name) + FILE_HEADER_LEN
 | 
			
		||||
        attr_pos = 0
 | 
			
		||||
        content = b''
 | 
			
		||||
        attr = b''
 | 
			
		||||
        pad = b''
 | 
			
		||||
        data = self.data
 | 
			
		||||
        if self.ftype == TYPE_STAGE:
 | 
			
		||||
            elf_data = elf.DecodeElf(data, self.base_address)
 | 
			
		||||
            content = struct.pack(STAGE_FORMAT, self.compress,
 | 
			
		||||
                                  elf_data.entry, elf_data.load,
 | 
			
		||||
                                  len(elf_data.data), elf_data.memsize)
 | 
			
		||||
            data = elf_data.data
 | 
			
		||||
        elif self.ftype == TYPE_RAW:
 | 
			
		||||
            orig_data = data
 | 
			
		||||
            if self.compress == COMPRESS_LZ4:
 | 
			
		||||
                data = tools.Compress(orig_data, 'lz4')
 | 
			
		||||
            elif self.compress == COMPRESS_LZMA:
 | 
			
		||||
                data = tools.Compress(orig_data, 'lzma')
 | 
			
		||||
            self.memlen = len(orig_data)
 | 
			
		||||
            self.data_len = len(data)
 | 
			
		||||
            attr = struct.pack(ATTR_COMPRESSION_FORMAT,
 | 
			
		||||
                               FILE_ATTR_TAG_COMPRESSION, ATTR_COMPRESSION_LEN,
 | 
			
		||||
                               self.compress, self.memlen)
 | 
			
		||||
        elif self.ftype == TYPE_EMPTY:
 | 
			
		||||
            data = tools.GetBytes(self.erase_byte, self.size)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Unknown type %#x when writing\n' % self.ftype)
 | 
			
		||||
        if attr:
 | 
			
		||||
            attr_pos = hdr_len
 | 
			
		||||
            hdr_len += len(attr)
 | 
			
		||||
        if self.cbfs_offset is not None:
 | 
			
		||||
            pad_len = self.cbfs_offset - offset - hdr_len
 | 
			
		||||
            if pad_len < 0:  # pragma: no cover
 | 
			
		||||
                # Test coverage of this is not available since this should never
 | 
			
		||||
                # happen. It indicates that get_header_len() provided an
 | 
			
		||||
                # incorrect value (too small) so that we decided that we could
 | 
			
		||||
                # put this file at the requested place, but in fact a previous
 | 
			
		||||
                # file extends far enough into the CBFS that this is not
 | 
			
		||||
                # possible.
 | 
			
		||||
                raise ValueError("Internal error: CBFS file '%s': Requested offset %#x but current output position is %#x" %
 | 
			
		||||
                                 (self.name, self.cbfs_offset, offset))
 | 
			
		||||
            pad = tools.GetBytes(pad_byte, pad_len)
 | 
			
		||||
            hdr_len += pad_len
 | 
			
		||||
 | 
			
		||||
        # This is the offset of the start of the file's data,
 | 
			
		||||
        size = len(content) + len(data)
 | 
			
		||||
        hdr = struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, size,
 | 
			
		||||
                          self.ftype, attr_pos, hdr_len)
 | 
			
		||||
 | 
			
		||||
        # Do a sanity check of the get_header_len() function, to ensure that it
 | 
			
		||||
        # stays in lockstep with this function
 | 
			
		||||
        expected_len = self.get_header_len()
 | 
			
		||||
        actual_len = len(hdr + name + attr)
 | 
			
		||||
        if expected_len != actual_len:  # pragma: no cover
 | 
			
		||||
            # Test coverage of this is not available since this should never
 | 
			
		||||
            # happen. It probably indicates that get_header_len() is broken.
 | 
			
		||||
            raise ValueError("Internal error: CBFS file '%s': Expected headers of %#x bytes, got %#d" %
 | 
			
		||||
                             (self.name, expected_len, actual_len))
 | 
			
		||||
        return hdr + name + attr + pad + content + data, hdr_len
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CbfsWriter(object):
 | 
			
		||||
    """Class to handle writing a Coreboot File System (CBFS)
 | 
			
		||||
 | 
			
		||||
    Usage is something like:
 | 
			
		||||
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', tools.ReadFile('u-boot.bin'))
 | 
			
		||||
        ...
 | 
			
		||||
        data, cbfs_offset = cbw.get_data_and_offset()
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        _master_name: Name of the file containing the master header
 | 
			
		||||
        _size: Size of the filesystem, in bytes
 | 
			
		||||
        _files: Ordered list of files in the CBFS, each a CbfsFile
 | 
			
		||||
        _arch: Architecture of the CBFS (ARCHITECTURE_...)
 | 
			
		||||
        _bootblock_size: Size of the bootblock, typically at the end of the CBFS
 | 
			
		||||
        _erase_byte: Byte to use for empty space in the CBFS
 | 
			
		||||
        _align: Alignment to use for files, typically ENTRY_ALIGN
 | 
			
		||||
        _base_address: Boot block offset in bytes from the start of CBFS.
 | 
			
		||||
            Typically this is located at top of the CBFS. It is 0 when there is
 | 
			
		||||
            no boot block
 | 
			
		||||
        _header_offset: Offset of master header in bytes from start of CBFS
 | 
			
		||||
        _contents_offset: Offset of first file header
 | 
			
		||||
        _hdr_at_start: True if the master header is at the start of the CBFS,
 | 
			
		||||
            instead of the end as normal for x86
 | 
			
		||||
        _add_fileheader: True to add a fileheader around the master header
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, size, arch=ARCHITECTURE_X86):
 | 
			
		||||
        """Set up a new CBFS
 | 
			
		||||
 | 
			
		||||
        This sets up all properties to default values. Files can be added using
 | 
			
		||||
        add_file_raw(), etc.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            size: Size of CBFS in bytes
 | 
			
		||||
            arch: Architecture to declare for CBFS
 | 
			
		||||
        """
 | 
			
		||||
        self._master_name = 'cbfs master header'
 | 
			
		||||
        self._size = size
 | 
			
		||||
        self._files = OrderedDict()
 | 
			
		||||
        self._arch = arch
 | 
			
		||||
        self._bootblock_size = 0
 | 
			
		||||
        self._erase_byte = 0xff
 | 
			
		||||
        self._align = ENTRY_ALIGN
 | 
			
		||||
        self._add_fileheader = False
 | 
			
		||||
        if self._arch == ARCHITECTURE_X86:
 | 
			
		||||
            # Allow 4 bytes for the header pointer. That holds the
 | 
			
		||||
            # twos-compliment negative offset of the master header in bytes
 | 
			
		||||
            # measured from one byte past the end of the CBFS
 | 
			
		||||
            self._base_address = self._size - max(self._bootblock_size,
 | 
			
		||||
                                                  MIN_BOOTBLOCK_SIZE)
 | 
			
		||||
            self._header_offset = self._base_address - HEADER_LEN
 | 
			
		||||
            self._contents_offset = 0
 | 
			
		||||
            self._hdr_at_start = False
 | 
			
		||||
        else:
 | 
			
		||||
            # For non-x86, different rules apply
 | 
			
		||||
            self._base_address = 0
 | 
			
		||||
            self._header_offset = align_int(self._base_address +
 | 
			
		||||
                                            self._bootblock_size, 4)
 | 
			
		||||
            self._contents_offset = align_int(self._header_offset +
 | 
			
		||||
                                              FILE_HEADER_LEN +
 | 
			
		||||
                                              self._bootblock_size, self._align)
 | 
			
		||||
            self._hdr_at_start = True
 | 
			
		||||
 | 
			
		||||
    def _skip_to(self, fd, offset):
 | 
			
		||||
        """Write out pad bytes until a given offset
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File objext to write to
 | 
			
		||||
            offset: Offset to write to
 | 
			
		||||
        """
 | 
			
		||||
        if fd.tell() > offset:
 | 
			
		||||
            raise ValueError('No space for data before offset %#x (current offset %#x)' %
 | 
			
		||||
                             (offset, fd.tell()))
 | 
			
		||||
        fd.write(tools.GetBytes(self._erase_byte, offset - fd.tell()))
 | 
			
		||||
 | 
			
		||||
    def _pad_to(self, fd, offset):
 | 
			
		||||
        """Write out pad bytes and/or an empty file until a given offset
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File objext to write to
 | 
			
		||||
            offset: Offset to write to
 | 
			
		||||
        """
 | 
			
		||||
        self._align_to(fd, self._align)
 | 
			
		||||
        upto = fd.tell()
 | 
			
		||||
        if upto > offset:
 | 
			
		||||
            raise ValueError('No space for data before pad offset %#x (current offset %#x)' %
 | 
			
		||||
                             (offset, upto))
 | 
			
		||||
        todo = align_int_down(offset - upto, self._align)
 | 
			
		||||
        if todo:
 | 
			
		||||
            cbf = CbfsFile.empty(todo, self._erase_byte)
 | 
			
		||||
            fd.write(cbf.get_data_and_offset()[0])
 | 
			
		||||
        self._skip_to(fd, offset)
 | 
			
		||||
 | 
			
		||||
    def _align_to(self, fd, align):
 | 
			
		||||
        """Write out pad bytes until a given alignment is reached
 | 
			
		||||
 | 
			
		||||
        This only aligns if the resulting output would not reach the end of the
 | 
			
		||||
        CBFS, since we want to leave the last 4 bytes for the master-header
 | 
			
		||||
        pointer.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File objext to write to
 | 
			
		||||
            align: Alignment to require (e.g. 4 means pad to next 4-byte
 | 
			
		||||
                boundary)
 | 
			
		||||
        """
 | 
			
		||||
        offset = align_int(fd.tell(), align)
 | 
			
		||||
        if offset < self._size:
 | 
			
		||||
            self._skip_to(fd, offset)
 | 
			
		||||
 | 
			
		||||
    def add_file_stage(self, name, data, cbfs_offset=None):
 | 
			
		||||
        """Add a new stage file to the CBFS
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name: String file name to put in CBFS (does not need to correspond
 | 
			
		||||
                to the name that the file originally came from)
 | 
			
		||||
            data: Contents of file
 | 
			
		||||
            cbfs_offset: Offset of this file's data within the CBFS, in bytes,
 | 
			
		||||
                or None to place this file anywhere
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object created
 | 
			
		||||
        """
 | 
			
		||||
        cfile = CbfsFile.stage(self._base_address, name, data, cbfs_offset)
 | 
			
		||||
        self._files[name] = cfile
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    def add_file_raw(self, name, data, cbfs_offset=None,
 | 
			
		||||
                     compress=COMPRESS_NONE):
 | 
			
		||||
        """Create a new raw file
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name: String file name to put in CBFS (does not need to correspond
 | 
			
		||||
                to the name that the file originally came from)
 | 
			
		||||
            data: Contents of file
 | 
			
		||||
            cbfs_offset: Offset of this file's data within the CBFS, in bytes,
 | 
			
		||||
                or None to place this file anywhere
 | 
			
		||||
            compress: Compression algorithm to use (COMPRESS_...)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object created
 | 
			
		||||
        """
 | 
			
		||||
        cfile = CbfsFile.raw(name, data, cbfs_offset, compress)
 | 
			
		||||
        self._files[name] = cfile
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    def _write_header(self, fd, add_fileheader):
 | 
			
		||||
        """Write out the master header to a CBFS
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File object
 | 
			
		||||
            add_fileheader: True to place the master header in a file header
 | 
			
		||||
                record
 | 
			
		||||
        """
 | 
			
		||||
        if fd.tell() > self._header_offset:
 | 
			
		||||
            raise ValueError('No space for header at offset %#x (current offset %#x)' %
 | 
			
		||||
                             (self._header_offset, fd.tell()))
 | 
			
		||||
        if not add_fileheader:
 | 
			
		||||
            self._pad_to(fd, self._header_offset)
 | 
			
		||||
        hdr = struct.pack(HEADER_FORMAT, HEADER_MAGIC, HEADER_VERSION2,
 | 
			
		||||
                          self._size, self._bootblock_size, self._align,
 | 
			
		||||
                          self._contents_offset, self._arch, 0xffffffff)
 | 
			
		||||
        if add_fileheader:
 | 
			
		||||
            name = _pack_string(self._master_name)
 | 
			
		||||
            fd.write(struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, len(hdr),
 | 
			
		||||
                                 TYPE_CBFSHEADER, 0,
 | 
			
		||||
                                 FILE_HEADER_LEN + len(name)))
 | 
			
		||||
            fd.write(name)
 | 
			
		||||
            self._header_offset = fd.tell()
 | 
			
		||||
            fd.write(hdr)
 | 
			
		||||
            self._align_to(fd, self._align)
 | 
			
		||||
        else:
 | 
			
		||||
            fd.write(hdr)
 | 
			
		||||
 | 
			
		||||
    def get_data(self):
 | 
			
		||||
        """Obtain the full contents of the CBFS
 | 
			
		||||
 | 
			
		||||
        Thhis builds the CBFS with headers and all required files.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            'bytes' type containing the data
 | 
			
		||||
        """
 | 
			
		||||
        fd = io.BytesIO()
 | 
			
		||||
 | 
			
		||||
        # THe header can go at the start in some cases
 | 
			
		||||
        if self._hdr_at_start:
 | 
			
		||||
            self._write_header(fd, add_fileheader=self._add_fileheader)
 | 
			
		||||
        self._skip_to(fd, self._contents_offset)
 | 
			
		||||
 | 
			
		||||
        # Write out each file
 | 
			
		||||
        for cbf in self._files.values():
 | 
			
		||||
            # Place the file at its requested place, if any
 | 
			
		||||
            offset = cbf.calc_start_offset()
 | 
			
		||||
            if offset is not None:
 | 
			
		||||
                self._pad_to(fd, align_int_down(offset, self._align))
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
            data, data_offset = cbf.get_data_and_offset(pos, self._erase_byte)
 | 
			
		||||
            fd.write(data)
 | 
			
		||||
            self._align_to(fd, self._align)
 | 
			
		||||
            cbf.calced_cbfs_offset = pos + data_offset
 | 
			
		||||
        if not self._hdr_at_start:
 | 
			
		||||
            self._write_header(fd, add_fileheader=self._add_fileheader)
 | 
			
		||||
 | 
			
		||||
        # Pad to the end and write a pointer to the CBFS master header
 | 
			
		||||
        self._pad_to(fd, self._base_address or self._size - 4)
 | 
			
		||||
        rel_offset = self._header_offset - self._size
 | 
			
		||||
        fd.write(struct.pack('<I', rel_offset & 0xffffffff))
 | 
			
		||||
 | 
			
		||||
        return fd.getvalue()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CbfsReader(object):
 | 
			
		||||
    """Class to handle reading a Coreboot File System (CBFS)
 | 
			
		||||
 | 
			
		||||
    Usage is something like:
 | 
			
		||||
        cbfs = cbfs_util.CbfsReader(data)
 | 
			
		||||
        cfile = cbfs.files['u-boot']
 | 
			
		||||
        self.WriteFile('u-boot.bin', cfile.data)
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        files: Ordered list of CbfsFile objects
 | 
			
		||||
        align: Alignment to use for files, typically ENTRT_ALIGN
 | 
			
		||||
        stage_base_address: Base address to use when mapping ELF files into the
 | 
			
		||||
            CBFS for TYPE_STAGE files. If this is larger than the code address
 | 
			
		||||
            of the ELF file, then data at the start of the ELF file will not
 | 
			
		||||
            appear in the CBFS. Currently there are no tests for behaviour as
 | 
			
		||||
            documentation is sparse
 | 
			
		||||
        magic: Integer magic number from master header (HEADER_MAGIC)
 | 
			
		||||
        version: Version number of CBFS (HEADER_VERSION2)
 | 
			
		||||
        rom_size: Size of CBFS
 | 
			
		||||
        boot_block_size: Size of boot block
 | 
			
		||||
        cbfs_offset: Offset of the first file in bytes from start of CBFS
 | 
			
		||||
        arch: Architecture of CBFS file (ARCHITECTURE_...)
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, data, read=True):
 | 
			
		||||
        self.align = ENTRY_ALIGN
 | 
			
		||||
        self.arch = None
 | 
			
		||||
        self.boot_block_size = None
 | 
			
		||||
        self.cbfs_offset = None
 | 
			
		||||
        self.files = OrderedDict()
 | 
			
		||||
        self.magic = None
 | 
			
		||||
        self.rom_size = None
 | 
			
		||||
        self.stage_base_address = 0
 | 
			
		||||
        self.version = None
 | 
			
		||||
        self.data = data
 | 
			
		||||
        if read:
 | 
			
		||||
            self.read()
 | 
			
		||||
 | 
			
		||||
    def read(self):
 | 
			
		||||
        """Read all the files in the CBFS and add them to self.files"""
 | 
			
		||||
        with io.BytesIO(self.data) as fd:
 | 
			
		||||
            # First, get the master header
 | 
			
		||||
            if not self._find_and_read_header(fd, len(self.data)):
 | 
			
		||||
                raise ValueError('Cannot find master header')
 | 
			
		||||
            fd.seek(self.cbfs_offset)
 | 
			
		||||
 | 
			
		||||
            # Now read in the files one at a time
 | 
			
		||||
            while True:
 | 
			
		||||
                cfile = self._read_next_file(fd)
 | 
			
		||||
                if cfile:
 | 
			
		||||
                    self.files[cfile.name] = cfile
 | 
			
		||||
                elif cfile is False:
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
    def _find_and_read_header(self, fd, size):
 | 
			
		||||
        """Find and read the master header in the CBFS
 | 
			
		||||
 | 
			
		||||
        This looks at the pointer word at the very end of the CBFS. This is an
 | 
			
		||||
        offset to the header relative to the size of the CBFS, which is assumed
 | 
			
		||||
        to be known. Note that the offset is in *little endian* format.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to read from
 | 
			
		||||
            size: Size of file
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            True if header was found, False if not
 | 
			
		||||
        """
 | 
			
		||||
        orig_pos = fd.tell()
 | 
			
		||||
        fd.seek(size - 4)
 | 
			
		||||
        rel_offset, = struct.unpack('<I', fd.read(4))
 | 
			
		||||
        pos = (size + rel_offset) & 0xffffffff
 | 
			
		||||
        fd.seek(pos)
 | 
			
		||||
        found = self._read_header(fd)
 | 
			
		||||
        if not found:
 | 
			
		||||
            print('Relative offset seems wrong, scanning whole image')
 | 
			
		||||
            for pos in range(0, size - HEADER_LEN, 4):
 | 
			
		||||
                fd.seek(pos)
 | 
			
		||||
                found = self._read_header(fd)
 | 
			
		||||
                if found:
 | 
			
		||||
                    break
 | 
			
		||||
        fd.seek(orig_pos)
 | 
			
		||||
        return found
 | 
			
		||||
 | 
			
		||||
    def _read_next_file(self, fd):
 | 
			
		||||
        """Read the next file from a CBFS
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to read from
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object, if found
 | 
			
		||||
            None if no object found, but data was parsed (e.g. TYPE_CBFSHEADER)
 | 
			
		||||
            False if at end of CBFS and reading should stop
 | 
			
		||||
        """
 | 
			
		||||
        file_pos = fd.tell()
 | 
			
		||||
        data = fd.read(FILE_HEADER_LEN)
 | 
			
		||||
        if len(data) < FILE_HEADER_LEN:
 | 
			
		||||
            print('File header at %x ran out of data' % file_pos)
 | 
			
		||||
            return False
 | 
			
		||||
        magic, size, ftype, attr, offset = struct.unpack(FILE_HEADER_FORMAT,
 | 
			
		||||
                                                         data)
 | 
			
		||||
        if magic != FILE_MAGIC:
 | 
			
		||||
            return False
 | 
			
		||||
        pos = fd.tell()
 | 
			
		||||
        name = self._read_string(fd)
 | 
			
		||||
        if name is None:
 | 
			
		||||
            print('String at %x ran out of data' % pos)
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if DEBUG:
 | 
			
		||||
            print('name', name)
 | 
			
		||||
 | 
			
		||||
        # If there are attribute headers present, read those
 | 
			
		||||
        compress = self._read_attr(fd, file_pos, attr, offset)
 | 
			
		||||
        if compress is None:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        # Create the correct CbfsFile object depending on the type
 | 
			
		||||
        cfile = None
 | 
			
		||||
        cbfs_offset = file_pos + offset
 | 
			
		||||
        fd.seek(cbfs_offset, io.SEEK_SET)
 | 
			
		||||
        if ftype == TYPE_CBFSHEADER:
 | 
			
		||||
            self._read_header(fd)
 | 
			
		||||
        elif ftype == TYPE_STAGE:
 | 
			
		||||
            data = fd.read(STAGE_LEN)
 | 
			
		||||
            cfile = CbfsFile.stage(self.stage_base_address, name, b'',
 | 
			
		||||
                                   cbfs_offset)
 | 
			
		||||
            (cfile.compress, cfile.entry, cfile.load, cfile.data_len,
 | 
			
		||||
             cfile.memlen) = struct.unpack(STAGE_FORMAT, data)
 | 
			
		||||
            cfile.data = fd.read(cfile.data_len)
 | 
			
		||||
        elif ftype == TYPE_RAW:
 | 
			
		||||
            data = fd.read(size)
 | 
			
		||||
            cfile = CbfsFile.raw(name, data, cbfs_offset, compress)
 | 
			
		||||
            cfile.decompress()
 | 
			
		||||
            if DEBUG:
 | 
			
		||||
                print('data', data)
 | 
			
		||||
        elif ftype == TYPE_EMPTY:
 | 
			
		||||
            # Just read the data and discard it, since it is only padding
 | 
			
		||||
            fd.read(size)
 | 
			
		||||
            cfile = CbfsFile('', TYPE_EMPTY, b'', cbfs_offset)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Unknown type %#x when reading\n' % ftype)
 | 
			
		||||
        if cfile:
 | 
			
		||||
            cfile.offset = offset
 | 
			
		||||
 | 
			
		||||
        # Move past the padding to the start of a possible next file. If we are
 | 
			
		||||
        # already at an alignment boundary, then there is no padding.
 | 
			
		||||
        pad = (self.align - fd.tell() % self.align) % self.align
 | 
			
		||||
        fd.seek(pad, io.SEEK_CUR)
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _read_attr(cls, fd, file_pos, attr, offset):
 | 
			
		||||
        """Read attributes from the file
 | 
			
		||||
 | 
			
		||||
        CBFS files can have attributes which are things that cannot fit into the
 | 
			
		||||
        header. The only attributes currently supported are compression and the
 | 
			
		||||
        unused tag.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to read from
 | 
			
		||||
            file_pos: Position of file in fd
 | 
			
		||||
            attr: Offset of attributes, 0 if none
 | 
			
		||||
            offset: Offset of file data (used to indicate the end of the
 | 
			
		||||
                                         attributes)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Compression to use for the file (COMPRESS_...)
 | 
			
		||||
        """
 | 
			
		||||
        compress = COMPRESS_NONE
 | 
			
		||||
        if not attr:
 | 
			
		||||
            return compress
 | 
			
		||||
        attr_size = offset - attr
 | 
			
		||||
        fd.seek(file_pos + attr, io.SEEK_SET)
 | 
			
		||||
        while attr_size:
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
            hdr = fd.read(8)
 | 
			
		||||
            if len(hdr) < 8:
 | 
			
		||||
                print('Attribute tag at %x ran out of data' % pos)
 | 
			
		||||
                return None
 | 
			
		||||
            atag, alen = struct.unpack(">II", hdr)
 | 
			
		||||
            data = hdr + fd.read(alen - 8)
 | 
			
		||||
            if atag == FILE_ATTR_TAG_COMPRESSION:
 | 
			
		||||
                # We don't currently use this information
 | 
			
		||||
                atag, alen, compress, _decomp_size = struct.unpack(
 | 
			
		||||
                    ATTR_COMPRESSION_FORMAT, data)
 | 
			
		||||
            elif atag == FILE_ATTR_TAG_UNUSED2:
 | 
			
		||||
                break
 | 
			
		||||
            else:
 | 
			
		||||
                print('Unknown attribute tag %x' % atag)
 | 
			
		||||
            attr_size -= len(data)
 | 
			
		||||
        return compress
 | 
			
		||||
 | 
			
		||||
    def _read_header(self, fd):
 | 
			
		||||
        """Read the master header
 | 
			
		||||
 | 
			
		||||
        Reads the header and stores the information obtained into the member
 | 
			
		||||
        variables.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to read from
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            True if header was read OK, False if it is truncated or has the
 | 
			
		||||
                wrong magic or version
 | 
			
		||||
        """
 | 
			
		||||
        pos = fd.tell()
 | 
			
		||||
        data = fd.read(HEADER_LEN)
 | 
			
		||||
        if len(data) < HEADER_LEN:
 | 
			
		||||
            print('Header at %x ran out of data' % pos)
 | 
			
		||||
            return False
 | 
			
		||||
        (self.magic, self.version, self.rom_size, self.boot_block_size,
 | 
			
		||||
         self.align, self.cbfs_offset, self.arch, _) = struct.unpack(
 | 
			
		||||
             HEADER_FORMAT, data)
 | 
			
		||||
        return self.magic == HEADER_MAGIC and (
 | 
			
		||||
            self.version == HEADER_VERSION1 or
 | 
			
		||||
            self.version == HEADER_VERSION2)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _read_string(cls, fd):
 | 
			
		||||
        """Read a string from a file
 | 
			
		||||
 | 
			
		||||
        This reads a string and aligns the data to the next alignment boundary
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to read from
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            string read ('str' type) encoded to UTF-8, or None if we ran out of
 | 
			
		||||
                data
 | 
			
		||||
        """
 | 
			
		||||
        val = b''
 | 
			
		||||
        while True:
 | 
			
		||||
            data = fd.read(FILENAME_ALIGN)
 | 
			
		||||
            if len(data) < FILENAME_ALIGN:
 | 
			
		||||
                return None
 | 
			
		||||
            pos = data.find(b'\0')
 | 
			
		||||
            if pos == -1:
 | 
			
		||||
                val += data
 | 
			
		||||
            else:
 | 
			
		||||
                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)))
 | 
			
		||||
							
								
								
									
										625
									
								
								tools/binman/cbfs_util_test.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										625
									
								
								tools/binman/cbfs_util_test.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,625 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright 2019 Google LLC
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
 | 
			
		||||
"""Tests for cbfs_util
 | 
			
		||||
 | 
			
		||||
These create and read various CBFSs and compare the results with expected
 | 
			
		||||
values and with cbfstool
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import io
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import struct
 | 
			
		||||
import tempfile
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import cbfs_util
 | 
			
		||||
from cbfs_util import CbfsWriter
 | 
			
		||||
import elf
 | 
			
		||||
import test_util
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
U_BOOT_DATA           = b'1234'
 | 
			
		||||
U_BOOT_DTB_DATA       = b'udtb'
 | 
			
		||||
COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCbfs(unittest.TestCase):
 | 
			
		||||
    """Test of cbfs_util classes"""
 | 
			
		||||
    #pylint: disable=W0212
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
        # Create a temporary directory for test files
 | 
			
		||||
        cls._indir = tempfile.mkdtemp(prefix='cbfs_util.')
 | 
			
		||||
        tools.SetInputDirs([cls._indir])
 | 
			
		||||
 | 
			
		||||
        # Set up some useful data files
 | 
			
		||||
        TestCbfs._make_input_file('u-boot.bin', U_BOOT_DATA)
 | 
			
		||||
        TestCbfs._make_input_file('u-boot.dtb', U_BOOT_DTB_DATA)
 | 
			
		||||
        TestCbfs._make_input_file('compress', COMPRESS_DATA)
 | 
			
		||||
 | 
			
		||||
        # Set up a temporary output directory, used by the tools library when
 | 
			
		||||
        # compressing files
 | 
			
		||||
        tools.PrepareOutputDir(None)
 | 
			
		||||
 | 
			
		||||
        cls.have_cbfstool = True
 | 
			
		||||
        try:
 | 
			
		||||
            tools.Run('which', 'cbfstool')
 | 
			
		||||
        except:
 | 
			
		||||
            cls.have_cbfstool = False
 | 
			
		||||
 | 
			
		||||
        cls.have_lz4 = True
 | 
			
		||||
        try:
 | 
			
		||||
            tools.Run('lz4', '--no-frame-crc', '-c',
 | 
			
		||||
                      tools.GetInputFilename('u-boot.bin'))
 | 
			
		||||
        except:
 | 
			
		||||
            cls.have_lz4 = False
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def tearDownClass(cls):
 | 
			
		||||
        """Remove the temporary input directory and its contents"""
 | 
			
		||||
        if cls._indir:
 | 
			
		||||
            shutil.rmtree(cls._indir)
 | 
			
		||||
        cls._indir = None
 | 
			
		||||
        tools.FinaliseOutputDir()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _make_input_file(cls, fname, contents):
 | 
			
		||||
        """Create a new test input file, creating directories as needed
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            fname: Filename to create
 | 
			
		||||
            contents: File contents to write in to the file
 | 
			
		||||
        Returns:
 | 
			
		||||
            Full pathname of file created
 | 
			
		||||
        """
 | 
			
		||||
        pathname = os.path.join(cls._indir, fname)
 | 
			
		||||
        tools.WriteFile(pathname, contents)
 | 
			
		||||
        return pathname
 | 
			
		||||
 | 
			
		||||
    def _check_hdr(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86):
 | 
			
		||||
        """Check that the CBFS has the expected header
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: Data to check
 | 
			
		||||
            size: Expected ROM size
 | 
			
		||||
            offset: Expected offset to first CBFS file
 | 
			
		||||
            arch: Expected architecture
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsReader object containing the CBFS
 | 
			
		||||
        """
 | 
			
		||||
        cbfs = cbfs_util.CbfsReader(data)
 | 
			
		||||
        self.assertEqual(cbfs_util.HEADER_MAGIC, cbfs.magic)
 | 
			
		||||
        self.assertEqual(cbfs_util.HEADER_VERSION2, cbfs.version)
 | 
			
		||||
        self.assertEqual(size, cbfs.rom_size)
 | 
			
		||||
        self.assertEqual(0, cbfs.boot_block_size)
 | 
			
		||||
        self.assertEqual(cbfs_util.ENTRY_ALIGN, cbfs.align)
 | 
			
		||||
        self.assertEqual(offset, cbfs.cbfs_offset)
 | 
			
		||||
        self.assertEqual(arch, cbfs.arch)
 | 
			
		||||
        return cbfs
 | 
			
		||||
 | 
			
		||||
    def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38,
 | 
			
		||||
                     data=U_BOOT_DATA, cbfs_offset=None):
 | 
			
		||||
        """Check that the U-Boot file is as expected
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            cbfs: CbfsReader object to check
 | 
			
		||||
            ftype: Expected file type
 | 
			
		||||
            offset: Expected offset of file
 | 
			
		||||
            data: Expected data in file
 | 
			
		||||
            cbfs_offset: Expected CBFS offset for file's data
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            CbfsFile object containing the file
 | 
			
		||||
        """
 | 
			
		||||
        self.assertIn('u-boot', cbfs.files)
 | 
			
		||||
        cfile = cbfs.files['u-boot']
 | 
			
		||||
        self.assertEqual('u-boot', cfile.name)
 | 
			
		||||
        self.assertEqual(offset, cfile.offset)
 | 
			
		||||
        if cbfs_offset is not None:
 | 
			
		||||
            self.assertEqual(cbfs_offset, cfile.cbfs_offset)
 | 
			
		||||
        self.assertEqual(data, cfile.data)
 | 
			
		||||
        self.assertEqual(ftype, cfile.ftype)
 | 
			
		||||
        self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress)
 | 
			
		||||
        self.assertEqual(len(data), cfile.memlen)
 | 
			
		||||
        return cfile
 | 
			
		||||
 | 
			
		||||
    def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA,
 | 
			
		||||
                   cbfs_offset=None):
 | 
			
		||||
        """Check that the U-Boot dtb file is as expected
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            cbfs: CbfsReader object to check
 | 
			
		||||
            offset: Expected offset of file
 | 
			
		||||
            data: Expected data in file
 | 
			
		||||
            cbfs_offset: Expected CBFS offset for file's data
 | 
			
		||||
        """
 | 
			
		||||
        self.assertIn('u-boot-dtb', cbfs.files)
 | 
			
		||||
        cfile = cbfs.files['u-boot-dtb']
 | 
			
		||||
        self.assertEqual('u-boot-dtb', cfile.name)
 | 
			
		||||
        self.assertEqual(offset, cfile.offset)
 | 
			
		||||
        if cbfs_offset is not None:
 | 
			
		||||
            self.assertEqual(cbfs_offset, cfile.cbfs_offset)
 | 
			
		||||
        self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
 | 
			
		||||
        self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype)
 | 
			
		||||
        self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress)
 | 
			
		||||
        self.assertEqual(len(U_BOOT_DTB_DATA), cfile.memlen)
 | 
			
		||||
 | 
			
		||||
    def _check_raw(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86):
 | 
			
		||||
        """Check that two raw files are added as expected
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: Data to check
 | 
			
		||||
            size: Expected ROM size
 | 
			
		||||
            offset: Expected offset to first CBFS file
 | 
			
		||||
            arch: Expected architecture
 | 
			
		||||
        """
 | 
			
		||||
        cbfs = self._check_hdr(data, size, offset=offset, arch=arch)
 | 
			
		||||
        self._check_uboot(cbfs)
 | 
			
		||||
        self._check_dtb(cbfs)
 | 
			
		||||
 | 
			
		||||
    def _get_expected_cbfs(self, size, arch='x86', compress=None, base=None):
 | 
			
		||||
        """Get the file created by cbfstool for a particular scenario
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            size: Size of the CBFS in bytes
 | 
			
		||||
            arch: Architecture of the CBFS, as a string
 | 
			
		||||
            compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA
 | 
			
		||||
            base: Base address of file, or None to put it anywhere
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Resulting CBFS file, or None if cbfstool is not available
 | 
			
		||||
        """
 | 
			
		||||
        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)
 | 
			
		||||
        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)
 | 
			
		||||
        return cbfs_fname
 | 
			
		||||
 | 
			
		||||
    def _compare_expected_cbfs(self, data, cbfstool_fname):
 | 
			
		||||
        """Compare against what cbfstool creates
 | 
			
		||||
 | 
			
		||||
        This compares what binman creates with what cbfstool creates for what
 | 
			
		||||
        is proportedly the same thing.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: CBFS created by binman
 | 
			
		||||
            cbfstool_fname: CBFS created by cbfstool
 | 
			
		||||
        """
 | 
			
		||||
        if not self.have_cbfstool or not self.have_lz4:
 | 
			
		||||
            return
 | 
			
		||||
        expect = tools.ReadFile(cbfstool_fname)
 | 
			
		||||
        if expect != data:
 | 
			
		||||
            tools.WriteFile('/tmp/expect', expect)
 | 
			
		||||
            tools.WriteFile('/tmp/actual', data)
 | 
			
		||||
            print('diff -y <(xxd -g1 /tmp/expect) <(xxd -g1 /tmp/actual) | colordiff')
 | 
			
		||||
            self.fail('cbfstool produced a different result')
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_functions(self):
 | 
			
		||||
        """Test global functions of cbfs_util"""
 | 
			
		||||
        self.assertEqual(cbfs_util.ARCHITECTURE_X86, cbfs_util.find_arch('x86'))
 | 
			
		||||
        self.assertIsNone(cbfs_util.find_arch('bad-arch'))
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(cbfs_util.COMPRESS_LZMA, cbfs_util.find_compress('lzma'))
 | 
			
		||||
        self.assertIsNone(cbfs_util.find_compress('bad-comp'))
 | 
			
		||||
 | 
			
		||||
    def test_cbfstool_failure(self):
 | 
			
		||||
        """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))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_raw(self):
 | 
			
		||||
        """Test base handling of a Coreboot Filesystem (CBFS)"""
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
        self._check_raw(data, size)
 | 
			
		||||
        cbfs_fname = self._get_expected_cbfs(size=size)
 | 
			
		||||
        self._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_invalid_file_type(self):
 | 
			
		||||
        """Check handling of an invalid file type when outputiing a CBFS"""
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
 | 
			
		||||
        # Change the type manually before generating the CBFS, and make sure
 | 
			
		||||
        # that the generator complains
 | 
			
		||||
        cfile.ftype = 0xff
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('Unknown type 0xff when writing', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_invalid_file_type_on_read(self):
 | 
			
		||||
        """Check handling of an invalid file type when reading the CBFS"""
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Read in the first file header
 | 
			
		||||
        cbr = cbfs_util.CbfsReader(data, read=False)
 | 
			
		||||
        with io.BytesIO(data) as fd:
 | 
			
		||||
            self.assertTrue(cbr._find_and_read_header(fd, len(data)))
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
            hdr_data = fd.read(cbfs_util.FILE_HEADER_LEN)
 | 
			
		||||
            magic, size, ftype, attr, offset = struct.unpack(
 | 
			
		||||
                cbfs_util.FILE_HEADER_FORMAT, hdr_data)
 | 
			
		||||
 | 
			
		||||
        # Create a new CBFS with a change to the file type
 | 
			
		||||
        ftype = 0xff
 | 
			
		||||
        newdata = data[:pos]
 | 
			
		||||
        newdata += struct.pack(cbfs_util.FILE_HEADER_FORMAT, magic, size, ftype,
 | 
			
		||||
                               attr, offset)
 | 
			
		||||
        newdata += data[pos + cbfs_util.FILE_HEADER_LEN:]
 | 
			
		||||
 | 
			
		||||
        # Read in this CBFS and make sure that the reader complains
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbfs_util.CbfsReader(newdata)
 | 
			
		||||
        self.assertIn('Unknown type 0xff when reading', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_no_space(self):
 | 
			
		||||
        """Check handling of running out of space in the CBFS"""
 | 
			
		||||
        size = 0x60
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('No space for header', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_no_space_skip(self):
 | 
			
		||||
        """Check handling of running out of space in CBFS with file header"""
 | 
			
		||||
        size = 0x5c
 | 
			
		||||
        cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64)
 | 
			
		||||
        cbw._add_fileheader = True
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('No space for data before offset', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_no_space_pad(self):
 | 
			
		||||
        """Check handling of running out of space in CBFS with file header"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw._add_fileheader = True
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('No space for data before pad offset', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_bad_header_ptr(self):
 | 
			
		||||
        """Check handling of a bad master-header pointer"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Add one to the pointer to make it invalid
 | 
			
		||||
        newdata = data[:-4] + struct.pack('<I', cbw._header_offset + 1)
 | 
			
		||||
 | 
			
		||||
        # We should still be able to find the master header by searching
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            cbfs = cbfs_util.CbfsReader(newdata)
 | 
			
		||||
        self.assertIn('Relative offset seems wrong', stdout.getvalue())
 | 
			
		||||
        self.assertIn('u-boot', cbfs.files)
 | 
			
		||||
        self.assertEqual(size, cbfs.rom_size)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_bad_header(self):
 | 
			
		||||
        """Check handling of a bad master header"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Drop most of the header and try reading the modified CBFS
 | 
			
		||||
        newdata = data[:cbw._header_offset + 4]
 | 
			
		||||
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            with self.assertRaises(ValueError) as e:
 | 
			
		||||
                cbfs_util.CbfsReader(newdata)
 | 
			
		||||
        self.assertIn('Relative offset seems wrong', stdout.getvalue())
 | 
			
		||||
        self.assertIn('Cannot find master header', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_bad_file_header(self):
 | 
			
		||||
        """Check handling of a bad file header"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Read in the CBFS master header (only), then stop
 | 
			
		||||
        cbr = cbfs_util.CbfsReader(data, read=False)
 | 
			
		||||
        with io.BytesIO(data) as fd:
 | 
			
		||||
            self.assertTrue(cbr._find_and_read_header(fd, len(data)))
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
 | 
			
		||||
        # Remove all but 4 bytes of the file headerm and try to read the file
 | 
			
		||||
        newdata = data[:pos + 4]
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            with io.BytesIO(newdata) as fd:
 | 
			
		||||
                fd.seek(pos)
 | 
			
		||||
                self.assertEqual(False, cbr._read_next_file(fd))
 | 
			
		||||
        self.assertIn('File header at 0 ran out of data', stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_bad_file_string(self):
 | 
			
		||||
        """Check handling of an incomplete filename string"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('16-characters xx', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Read in the CBFS master header (only), then stop
 | 
			
		||||
        cbr = cbfs_util.CbfsReader(data, read=False)
 | 
			
		||||
        with io.BytesIO(data) as fd:
 | 
			
		||||
            self.assertTrue(cbr._find_and_read_header(fd, len(data)))
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
 | 
			
		||||
        # Create a new CBFS with only the first 16 bytes of the file name, then
 | 
			
		||||
        # try to read the file
 | 
			
		||||
        newdata = data[:pos + cbfs_util.FILE_HEADER_LEN + 16]
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            with io.BytesIO(newdata) as fd:
 | 
			
		||||
                fd.seek(pos)
 | 
			
		||||
                self.assertEqual(False, cbr._read_next_file(fd))
 | 
			
		||||
        self.assertIn('String at %x ran out of data' %
 | 
			
		||||
                      cbfs_util.FILE_HEADER_LEN, stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_debug(self):
 | 
			
		||||
        """Check debug output"""
 | 
			
		||||
        size = 0x70
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            cbfs_util.DEBUG = True
 | 
			
		||||
            with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
                cbfs_util.CbfsReader(data)
 | 
			
		||||
            self.assertEqual('name u-boot\ndata %s\n' % U_BOOT_DATA,
 | 
			
		||||
                             stdout.getvalue())
 | 
			
		||||
        finally:
 | 
			
		||||
            cbfs_util.DEBUG = False
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_bad_attribute(self):
 | 
			
		||||
        """Check handling of bad attribute tag"""
 | 
			
		||||
        if not self.have_lz4:
 | 
			
		||||
            self.skipTest('lz4 --no-frame-crc not available')
 | 
			
		||||
        size = 0x140
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
 | 
			
		||||
                         compress=cbfs_util.COMPRESS_LZ4)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Search the CBFS for the expected compression tag
 | 
			
		||||
        with io.BytesIO(data) as fd:
 | 
			
		||||
            while True:
 | 
			
		||||
                pos = fd.tell()
 | 
			
		||||
                tag, = struct.unpack('>I', fd.read(4))
 | 
			
		||||
                if tag == cbfs_util.FILE_ATTR_TAG_COMPRESSION:
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
        # Create a new CBFS with the tag changed to something invalid
 | 
			
		||||
        newdata = data[:pos] + struct.pack('>I', 0x123) + data[pos + 4:]
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            cbfs_util.CbfsReader(newdata)
 | 
			
		||||
        self.assertEqual('Unknown attribute tag 123\n', stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_missing_attribute(self):
 | 
			
		||||
        """Check handling of an incomplete attribute tag"""
 | 
			
		||||
        if not self.have_lz4:
 | 
			
		||||
            self.skipTest('lz4 --no-frame-crc not available')
 | 
			
		||||
        size = 0x140
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
 | 
			
		||||
                         compress=cbfs_util.COMPRESS_LZ4)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        # Read in the CBFS master header (only), then stop
 | 
			
		||||
        cbr = cbfs_util.CbfsReader(data, read=False)
 | 
			
		||||
        with io.BytesIO(data) as fd:
 | 
			
		||||
            self.assertTrue(cbr._find_and_read_header(fd, len(data)))
 | 
			
		||||
            pos = fd.tell()
 | 
			
		||||
 | 
			
		||||
        # Create a new CBFS with only the first 4 bytes of the compression tag,
 | 
			
		||||
        # then try to read the file
 | 
			
		||||
        tag_pos = pos + cbfs_util.FILE_HEADER_LEN + cbfs_util.FILENAME_ALIGN
 | 
			
		||||
        newdata = data[:tag_pos + 4]
 | 
			
		||||
        with test_util.capture_sys_output() as (stdout, _stderr):
 | 
			
		||||
            with io.BytesIO(newdata) as fd:
 | 
			
		||||
                fd.seek(pos)
 | 
			
		||||
                self.assertEqual(False, cbr._read_next_file(fd))
 | 
			
		||||
        self.assertIn('Attribute tag at %x ran out of data' % tag_pos,
 | 
			
		||||
                      stdout.getvalue())
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_file_master_header(self):
 | 
			
		||||
        """Check handling of a file containing a master header"""
 | 
			
		||||
        size = 0x100
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw._add_fileheader = True
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        cbr = cbfs_util.CbfsReader(data)
 | 
			
		||||
        self.assertIn('u-boot', cbr.files)
 | 
			
		||||
        self.assertEqual(size, cbr.rom_size)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_arch(self):
 | 
			
		||||
        """Test on non-x86 architecture"""
 | 
			
		||||
        size = 0x100
 | 
			
		||||
        cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
        self._check_raw(data, size, offset=0x40,
 | 
			
		||||
                        arch=cbfs_util.ARCHITECTURE_PPC64)
 | 
			
		||||
 | 
			
		||||
        # Compare against what cbfstool creates
 | 
			
		||||
        cbfs_fname = self._get_expected_cbfs(size=size, arch='ppc64')
 | 
			
		||||
        self._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_stage(self):
 | 
			
		||||
        """Tests handling of a Coreboot Filesystem (CBFS)"""
 | 
			
		||||
        if not elf.ELF_TOOLS:
 | 
			
		||||
            self.skipTest('Python elftools not available')
 | 
			
		||||
        elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
 | 
			
		||||
        elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
 | 
			
		||||
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_stage('u-boot', tools.ReadFile(elf_fname))
 | 
			
		||||
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
        cbfs = self._check_hdr(data, size)
 | 
			
		||||
        load = 0xfef20000
 | 
			
		||||
        entry = load + 2
 | 
			
		||||
 | 
			
		||||
        cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x28,
 | 
			
		||||
                                  data=U_BOOT_DATA + U_BOOT_DTB_DATA)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(entry, cfile.entry)
 | 
			
		||||
        self.assertEqual(load, cfile.load)
 | 
			
		||||
        self.assertEqual(len(U_BOOT_DATA) + len(U_BOOT_DTB_DATA),
 | 
			
		||||
                         cfile.data_len)
 | 
			
		||||
 | 
			
		||||
        # 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._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_raw_compress(self):
 | 
			
		||||
        """Test base handling of compressing raw files"""
 | 
			
		||||
        if not self.have_lz4:
 | 
			
		||||
            self.skipTest('lz4 --no-frame-crc not available')
 | 
			
		||||
        size = 0x140
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', COMPRESS_DATA, None,
 | 
			
		||||
                         compress=cbfs_util.COMPRESS_LZ4)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, None,
 | 
			
		||||
                         compress=cbfs_util.COMPRESS_LZMA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        cbfs = self._check_hdr(data, size)
 | 
			
		||||
        self.assertIn('u-boot', cbfs.files)
 | 
			
		||||
        cfile = cbfs.files['u-boot']
 | 
			
		||||
        self.assertEqual(cfile.name, 'u-boot')
 | 
			
		||||
        self.assertEqual(cfile.offset, 56)
 | 
			
		||||
        self.assertEqual(cfile.data, COMPRESS_DATA)
 | 
			
		||||
        self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
 | 
			
		||||
        self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZ4)
 | 
			
		||||
        self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
 | 
			
		||||
 | 
			
		||||
        self.assertIn('u-boot-dtb', cbfs.files)
 | 
			
		||||
        cfile = cbfs.files['u-boot-dtb']
 | 
			
		||||
        self.assertEqual(cfile.name, 'u-boot-dtb')
 | 
			
		||||
        self.assertEqual(cfile.offset, 56)
 | 
			
		||||
        self.assertEqual(cfile.data, COMPRESS_DATA)
 | 
			
		||||
        self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW)
 | 
			
		||||
        self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZMA)
 | 
			
		||||
        self.assertEqual(cfile.memlen, len(COMPRESS_DATA))
 | 
			
		||||
 | 
			
		||||
        cbfs_fname = self._get_expected_cbfs(size=size, compress=['lz4', 'lzma'])
 | 
			
		||||
        self._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_raw_space(self):
 | 
			
		||||
        """Test files with unused space in the CBFS"""
 | 
			
		||||
        size = 0xf0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
        self._check_raw(data, size)
 | 
			
		||||
        cbfs_fname = self._get_expected_cbfs(size=size)
 | 
			
		||||
        self._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_offset(self):
 | 
			
		||||
        """Test a CBFS with files at particular offsets"""
 | 
			
		||||
        size = 0x200
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x140)
 | 
			
		||||
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
        cbfs = self._check_hdr(data, size)
 | 
			
		||||
        self._check_uboot(cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x40,
 | 
			
		||||
                          cbfs_offset=0x40)
 | 
			
		||||
        self._check_dtb(cbfs, offset=0x40, cbfs_offset=0x140)
 | 
			
		||||
 | 
			
		||||
        cbfs_fname = self._get_expected_cbfs(size=size, base=(0x40, 0x140))
 | 
			
		||||
        self._compare_expected_cbfs(data, cbfs_fname)
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_invalid_file_type_header(self):
 | 
			
		||||
        """Check handling of an invalid file type when outputting a header"""
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA, 0)
 | 
			
		||||
 | 
			
		||||
        # Change the type manually before generating the CBFS, and make sure
 | 
			
		||||
        # that the generator complains
 | 
			
		||||
        cfile.ftype = 0xff
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('Unknown file type 0xff', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_offset_conflict(self):
 | 
			
		||||
        """Test a CBFS with files that want to overlap"""
 | 
			
		||||
        size = 0x200
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x80)
 | 
			
		||||
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            cbw.get_data()
 | 
			
		||||
        self.assertIn('No space for data before pad offset', str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def test_cbfs_check_offset(self):
 | 
			
		||||
        """Test that we can discover the offset of a file after writing it"""
 | 
			
		||||
        size = 0xb0
 | 
			
		||||
        cbw = CbfsWriter(size)
 | 
			
		||||
        cbw.add_file_raw('u-boot', U_BOOT_DATA)
 | 
			
		||||
        cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA)
 | 
			
		||||
        data = cbw.get_data()
 | 
			
		||||
 | 
			
		||||
        cbfs = cbfs_util.CbfsReader(data)
 | 
			
		||||
        self.assertEqual(0x38, cbfs.files['u-boot'].cbfs_offset)
 | 
			
		||||
        self.assertEqual(0x78, cbfs.files['u-boot-dtb'].cbfs_offset)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
# Command-line parser for binman
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from optparse import OptionParser
 | 
			
		||||
from argparse import ArgumentParser
 | 
			
		||||
 | 
			
		||||
def ParseArgs(argv):
 | 
			
		||||
    """Parse the binman command-line arguments
 | 
			
		||||
@ -17,50 +17,83 @@ def ParseArgs(argv):
 | 
			
		||||
            options provides access to the options (e.g. option.debug)
 | 
			
		||||
            args is a list of string arguments
 | 
			
		||||
    """
 | 
			
		||||
    parser = OptionParser()
 | 
			
		||||
    parser.add_option('-a', '--entry-arg', type='string', action='append',
 | 
			
		||||
            help='Set argument value arg=value')
 | 
			
		||||
    parser.add_option('-b', '--board', type='string',
 | 
			
		||||
            help='Board name to build')
 | 
			
		||||
    parser.add_option('-B', '--build-dir', type='string', default='b',
 | 
			
		||||
            help='Directory containing the build output')
 | 
			
		||||
    parser.add_option('-d', '--dt', type='string',
 | 
			
		||||
            help='Configuration file (.dtb) to use')
 | 
			
		||||
    parser.add_option('-D', '--debug', action='store_true',
 | 
			
		||||
            help='Enabling debugging (provides a full traceback on error)')
 | 
			
		||||
    parser.add_option('-E', '--entry-docs', action='store_true',
 | 
			
		||||
            help='Write out entry documentation (see README.entries)')
 | 
			
		||||
    parser.add_option('--fake-dtb', action='store_true',
 | 
			
		||||
            help='Use fake device tree contents (for testing only)')
 | 
			
		||||
    parser.add_option('-i', '--image', type='string', action='append',
 | 
			
		||||
            help='Image filename to build (if not specified, build all)')
 | 
			
		||||
    parser.add_option('-I', '--indir', action='append',
 | 
			
		||||
            help='Add a path to a directory to use for input files')
 | 
			
		||||
    parser.add_option('-H', '--full-help', action='store_true',
 | 
			
		||||
    if '-H' in argv:
 | 
			
		||||
        argv.append('build')
 | 
			
		||||
 | 
			
		||||
    epilog = '''Binman creates and manipulate images for a board from a set of binaries. Binman is
 | 
			
		||||
controlled by a description in the board device tree.'''
 | 
			
		||||
 | 
			
		||||
    parser = ArgumentParser(epilog=epilog)
 | 
			
		||||
    parser.add_argument('-B', '--build-dir', type=str, default='b',
 | 
			
		||||
        help='Directory containing the build output')
 | 
			
		||||
    parser.add_argument('-D', '--debug', action='store_true',
 | 
			
		||||
        help='Enabling debugging (provides a full traceback on error)')
 | 
			
		||||
    parser.add_argument('-H', '--full-help', action='store_true',
 | 
			
		||||
        default=False, help='Display the README file')
 | 
			
		||||
    parser.add_option('-m', '--map', action='store_true',
 | 
			
		||||
    parser.add_argument('--toolpath', type=str, action='append',
 | 
			
		||||
        help='Add a path to the directories containing tools')
 | 
			
		||||
    parser.add_argument('-v', '--verbosity', default=1,
 | 
			
		||||
        type=int, help='Control verbosity: 0=silent, 1=warnings, 2=notices, '
 | 
			
		||||
        '3=info, 4=detail, 5=debug')
 | 
			
		||||
 | 
			
		||||
    subparsers = parser.add_subparsers(dest='cmd')
 | 
			
		||||
 | 
			
		||||
    build_parser = subparsers.add_parser('build', help='Build firmware image')
 | 
			
		||||
    build_parser.add_argument('-a', '--entry-arg', type=str, action='append',
 | 
			
		||||
            help='Set argument value arg=value')
 | 
			
		||||
    build_parser.add_argument('-b', '--board', type=str,
 | 
			
		||||
            help='Board name to build')
 | 
			
		||||
    build_parser.add_argument('-d', '--dt', type=str,
 | 
			
		||||
            help='Configuration file (.dtb) to use')
 | 
			
		||||
    build_parser.add_argument('--fake-dtb', action='store_true',
 | 
			
		||||
            help='Use fake device tree contents (for testing only)')
 | 
			
		||||
    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',
 | 
			
		||||
            help='Add a path to the list of directories to use for input files')
 | 
			
		||||
    build_parser.add_argument('-m', '--map', action='store_true',
 | 
			
		||||
        default=False, help='Output a map file for each image')
 | 
			
		||||
    parser.add_option('-O', '--outdir', type='string',
 | 
			
		||||
    build_parser.add_argument('-O', '--outdir', type=str,
 | 
			
		||||
        action='store', help='Path to directory to use for intermediate and '
 | 
			
		||||
        'output files')
 | 
			
		||||
    parser.add_option('-p', '--preserve', action='store_true',\
 | 
			
		||||
    build_parser.add_argument('-p', '--preserve', action='store_true',\
 | 
			
		||||
        help='Preserve temporary output directory even if option -O is not '
 | 
			
		||||
             'given')
 | 
			
		||||
    parser.add_option('-P', '--processes', type=int,
 | 
			
		||||
                      help='set number of processes to use for running tests')
 | 
			
		||||
    parser.add_option('-t', '--test', action='store_true',
 | 
			
		||||
                    default=False, help='run tests')
 | 
			
		||||
    parser.add_option('-T', '--test-coverage', action='store_true',
 | 
			
		||||
                    default=False, help='run tests and check for 100% coverage')
 | 
			
		||||
    parser.add_option('-u', '--update-fdt', action='store_true',
 | 
			
		||||
    build_parser.add_argument('-u', '--update-fdt', action='store_true',
 | 
			
		||||
        default=False, help='Update the binman node with offset/size info')
 | 
			
		||||
    parser.add_option('-v', '--verbosity', default=1,
 | 
			
		||||
        type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
 | 
			
		||||
        '4=debug')
 | 
			
		||||
 | 
			
		||||
    parser.usage += """
 | 
			
		||||
    entry_parser = subparsers.add_parser('entry-docs',
 | 
			
		||||
        help='Write out entry documentation (see README.entries)')
 | 
			
		||||
 | 
			
		||||
Create images for a board from a set of binaries. It is controlled by a
 | 
			
		||||
description in the board device tree."""
 | 
			
		||||
    list_parser = subparsers.add_parser('ls', help='List files in an image')
 | 
			
		||||
    list_parser.add_argument('-i', '--image', type=str, required=True,
 | 
			
		||||
                             help='Image filename to list')
 | 
			
		||||
    list_parser.add_argument('paths', type=str, nargs='*',
 | 
			
		||||
                             help='Paths within file to list (wildcard)')
 | 
			
		||||
 | 
			
		||||
    extract_parser = subparsers.add_parser('extract',
 | 
			
		||||
                                           help='Extract files from an image')
 | 
			
		||||
    extract_parser.add_argument('-i', '--image', type=str, required=True,
 | 
			
		||||
                                help='Image filename to extract')
 | 
			
		||||
    extract_parser.add_argument('-f', '--filename', type=str,
 | 
			
		||||
                                help='Output filename to write to')
 | 
			
		||||
    extract_parser.add_argument('-O', '--outdir', type=str, default='',
 | 
			
		||||
        help='Path to directory to use for output files')
 | 
			
		||||
    extract_parser.add_argument('paths', type=str, nargs='*',
 | 
			
		||||
                                help='Paths within file to extract (wildcard)')
 | 
			
		||||
    extract_parser.add_argument('-U', '--uncompressed', action='store_true',
 | 
			
		||||
        help='Output raw uncompressed data for compressed entries')
 | 
			
		||||
 | 
			
		||||
    test_parser = subparsers.add_parser('test', help='Run tests')
 | 
			
		||||
    test_parser.add_argument('-P', '--processes', type=int,
 | 
			
		||||
        help='set number of processes to use for running tests')
 | 
			
		||||
    test_parser.add_argument('-T', '--test-coverage', action='store_true',
 | 
			
		||||
        default=False, help='run tests and check for 100%% coverage')
 | 
			
		||||
    test_parser.add_argument('-X', '--test-preserve-dirs', action='store_true',
 | 
			
		||||
        help='Preserve and display test-created input directories; also '
 | 
			
		||||
             'preserve the output directory if a single test is run (pass test '
 | 
			
		||||
             'name at the end of the command line')
 | 
			
		||||
    test_parser.add_argument('tests', nargs='*',
 | 
			
		||||
                             help='Test names to run (omit for all)')
 | 
			
		||||
 | 
			
		||||
    return parser.parse_args(argv)
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ import os
 | 
			
		||||
import sys
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
import cbfs_util
 | 
			
		||||
import command
 | 
			
		||||
import elf
 | 
			
		||||
from image import Image
 | 
			
		||||
@ -66,19 +67,120 @@ def WriteEntryDocs(modules, test_missing=None):
 | 
			
		||||
    from entry import Entry
 | 
			
		||||
    Entry.WriteDocs(modules, test_missing)
 | 
			
		||||
 | 
			
		||||
def Binman(options, args):
 | 
			
		||||
 | 
			
		||||
def ListEntries(image_fname, entry_paths):
 | 
			
		||||
    """List the entries in an image
 | 
			
		||||
 | 
			
		||||
    This decodes the supplied image and displays a table of entries from that
 | 
			
		||||
    image, preceded by a header.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        image_fname: Image filename to process
 | 
			
		||||
        entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
 | 
			
		||||
                                                     'section/u-boot'])
 | 
			
		||||
    """
 | 
			
		||||
    image = Image.FromFile(image_fname)
 | 
			
		||||
 | 
			
		||||
    entries, lines, widths = image.GetListEntries(entry_paths)
 | 
			
		||||
 | 
			
		||||
    num_columns = len(widths)
 | 
			
		||||
    for linenum, line in enumerate(lines):
 | 
			
		||||
        if linenum == 1:
 | 
			
		||||
            # Print header line
 | 
			
		||||
            print('-' * (sum(widths) + num_columns * 2))
 | 
			
		||||
        out = ''
 | 
			
		||||
        for i, item in enumerate(line):
 | 
			
		||||
            width = -widths[i]
 | 
			
		||||
            if item.startswith('>'):
 | 
			
		||||
                width = -width
 | 
			
		||||
                item = item[1:]
 | 
			
		||||
            txt = '%*s  ' % (width, item)
 | 
			
		||||
            out += txt
 | 
			
		||||
        print(out.rstrip())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ReadEntry(image_fname, entry_path, decomp=True):
 | 
			
		||||
    """Extract an entry from an image
 | 
			
		||||
 | 
			
		||||
    This extracts the data from a particular entry in an image
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        image_fname: Image filename to process
 | 
			
		||||
        entry_path: Path to entry to extract
 | 
			
		||||
        decomp: True to return uncompressed data, if the data is compress
 | 
			
		||||
            False to return the raw data
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        data extracted from the entry
 | 
			
		||||
    """
 | 
			
		||||
    image = Image.FromFile(image_fname)
 | 
			
		||||
    entry = image.FindEntryPath(entry_path)
 | 
			
		||||
    return entry.ReadData(decomp)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ExtractEntries(image_fname, output_fname, outdir, entry_paths,
 | 
			
		||||
                   decomp=True):
 | 
			
		||||
    """Extract the data from one or more entries and write it to files
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        image_fname: Image filename to process
 | 
			
		||||
        output_fname: Single output filename to use if extracting one file, None
 | 
			
		||||
            otherwise
 | 
			
		||||
        outdir: Output directory to use (for any number of files), else None
 | 
			
		||||
        entry_paths: List of entry paths to extract
 | 
			
		||||
        decomp: True to compress the entry data
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        List of EntryInfo records that were written
 | 
			
		||||
    """
 | 
			
		||||
    image = Image.FromFile(image_fname)
 | 
			
		||||
 | 
			
		||||
    # Output an entry to a single file, as a special case
 | 
			
		||||
    if output_fname:
 | 
			
		||||
        if not entry_paths:
 | 
			
		||||
            raise ValueError('Must specify an entry path to write with -o')
 | 
			
		||||
        if len(entry_paths) != 1:
 | 
			
		||||
            raise ValueError('Must specify exactly one entry path to write with -o')
 | 
			
		||||
        entry = image.FindEntryPath(entry_paths[0])
 | 
			
		||||
        data = entry.ReadData(decomp)
 | 
			
		||||
        tools.WriteFile(output_fname, data)
 | 
			
		||||
        tout.Notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname))
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # Otherwise we will output to a path given by the entry path of each entry.
 | 
			
		||||
    # This means that entries will appear in subdirectories if they are part of
 | 
			
		||||
    # a sub-section.
 | 
			
		||||
    einfos = image.GetListEntries(entry_paths)[0]
 | 
			
		||||
    tout.Notice('%d entries match and will be written' % len(einfos))
 | 
			
		||||
    for einfo in einfos:
 | 
			
		||||
        entry = einfo.entry
 | 
			
		||||
        data = entry.ReadData(decomp)
 | 
			
		||||
        path = entry.GetPath()[1:]
 | 
			
		||||
        fname = os.path.join(outdir, path)
 | 
			
		||||
 | 
			
		||||
        # If this entry has children, create a directory for it and put its
 | 
			
		||||
        # data in a file called 'root' in that directory
 | 
			
		||||
        if entry.GetEntries():
 | 
			
		||||
            if not os.path.exists(fname):
 | 
			
		||||
                os.makedirs(fname)
 | 
			
		||||
            fname = os.path.join(fname, 'root')
 | 
			
		||||
        tout.Notice("Write entry '%s' to '%s'" % (entry.GetPath(), fname))
 | 
			
		||||
        tools.WriteFile(fname, data)
 | 
			
		||||
    return einfos
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def Binman(args):
 | 
			
		||||
    """The main control code for binman
 | 
			
		||||
 | 
			
		||||
    This assumes that help and test options have already been dealt with. It
 | 
			
		||||
    deals with the core task of building images.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        options: Command line options object
 | 
			
		||||
        args: Command line arguments (list of strings)
 | 
			
		||||
        args: Command line arguments Namespace object
 | 
			
		||||
    """
 | 
			
		||||
    global images
 | 
			
		||||
 | 
			
		||||
    if options.full_help:
 | 
			
		||||
    if args.full_help:
 | 
			
		||||
        pager = os.getenv('PAGER')
 | 
			
		||||
        if not pager:
 | 
			
		||||
            pager = 'more'
 | 
			
		||||
@ -87,18 +189,31 @@ def Binman(options, args):
 | 
			
		||||
        command.Run(pager, fname)
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    if args.cmd == 'ls':
 | 
			
		||||
        ListEntries(args.image, args.paths)
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    if args.cmd == 'extract':
 | 
			
		||||
        try:
 | 
			
		||||
            tools.PrepareOutputDir(None)
 | 
			
		||||
            ExtractEntries(args.image, args.filename, args.outdir, args.paths,
 | 
			
		||||
                           not args.uncompressed)
 | 
			
		||||
        finally:
 | 
			
		||||
            tools.FinaliseOutputDir()
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    # Try to figure out which device tree contains our image description
 | 
			
		||||
    if options.dt:
 | 
			
		||||
        dtb_fname = options.dt
 | 
			
		||||
    if args.dt:
 | 
			
		||||
        dtb_fname = args.dt
 | 
			
		||||
    else:
 | 
			
		||||
        board = options.board
 | 
			
		||||
        board = args.board
 | 
			
		||||
        if not board:
 | 
			
		||||
            raise ValueError('Must provide a board to process (use -b <board>)')
 | 
			
		||||
        board_pathname = os.path.join(options.build_dir, board)
 | 
			
		||||
        board_pathname = os.path.join(args.build_dir, board)
 | 
			
		||||
        dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
 | 
			
		||||
        if not options.indir:
 | 
			
		||||
            options.indir = ['.']
 | 
			
		||||
        options.indir.append(board_pathname)
 | 
			
		||||
        if not args.indir:
 | 
			
		||||
            args.indir = ['.']
 | 
			
		||||
        args.indir.append(board_pathname)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        # Import these here in case libfdt.py is not available, in which case
 | 
			
		||||
@ -106,13 +221,15 @@ def Binman(options, args):
 | 
			
		||||
        import fdt
 | 
			
		||||
        import fdt_util
 | 
			
		||||
 | 
			
		||||
        tout.Init(options.verbosity)
 | 
			
		||||
        elf.debug = options.debug
 | 
			
		||||
        state.use_fake_dtb = options.fake_dtb
 | 
			
		||||
        tout.Init(args.verbosity)
 | 
			
		||||
        elf.debug = args.debug
 | 
			
		||||
        cbfs_util.VERBOSE = args.verbosity > 2
 | 
			
		||||
        state.use_fake_dtb = args.fake_dtb
 | 
			
		||||
        try:
 | 
			
		||||
            tools.SetInputDirs(options.indir)
 | 
			
		||||
            tools.PrepareOutputDir(options.outdir, options.preserve)
 | 
			
		||||
            state.SetEntryArgs(options.entry_arg)
 | 
			
		||||
            tools.SetInputDirs(args.indir)
 | 
			
		||||
            tools.PrepareOutputDir(args.outdir, args.preserve)
 | 
			
		||||
            tools.SetToolPaths(args.toolpath)
 | 
			
		||||
            state.SetEntryArgs(args.entry_arg)
 | 
			
		||||
 | 
			
		||||
            # Get the device tree ready by compiling it and copying the compiled
 | 
			
		||||
            # output into a file in our output directly. Then scan it for use
 | 
			
		||||
@ -129,16 +246,16 @@ def Binman(options, args):
 | 
			
		||||
 | 
			
		||||
            images = _ReadImageDesc(node)
 | 
			
		||||
 | 
			
		||||
            if options.image:
 | 
			
		||||
            if args.image:
 | 
			
		||||
                skip = []
 | 
			
		||||
                new_images = OrderedDict()
 | 
			
		||||
                for name, image in images.items():
 | 
			
		||||
                    if name in options.image:
 | 
			
		||||
                    if name in args.image:
 | 
			
		||||
                        new_images[name] = image
 | 
			
		||||
                    else:
 | 
			
		||||
                        skip.append(name)
 | 
			
		||||
                images = new_images
 | 
			
		||||
                if skip and options.verbosity >= 2:
 | 
			
		||||
                if skip and args.verbosity >= 2:
 | 
			
		||||
                    print('Skipping images: %s' % ', '.join(skip))
 | 
			
		||||
 | 
			
		||||
            state.Prepare(images, dtb)
 | 
			
		||||
@ -152,7 +269,7 @@ def Binman(options, args):
 | 
			
		||||
            # entry offsets remain the same.
 | 
			
		||||
            for image in images.values():
 | 
			
		||||
                image.ExpandEntries()
 | 
			
		||||
                if options.update_fdt:
 | 
			
		||||
                if args.update_fdt:
 | 
			
		||||
                    image.AddMissingProperties()
 | 
			
		||||
                image.ProcessFdt(dtb)
 | 
			
		||||
 | 
			
		||||
@ -168,24 +285,45 @@ def Binman(options, args):
 | 
			
		||||
                # completed and written, but that does not seem important.
 | 
			
		||||
                image.GetEntryContents()
 | 
			
		||||
                image.GetEntryOffsets()
 | 
			
		||||
                try:
 | 
			
		||||
                    image.PackEntries()
 | 
			
		||||
                    image.CheckSize()
 | 
			
		||||
                    image.CheckEntries()
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    if options.map:
 | 
			
		||||
                        fname = image.WriteMap()
 | 
			
		||||
                        print("Wrote map file '%s' to show errors"  % fname)
 | 
			
		||||
                    raise
 | 
			
		||||
                image.SetImagePos()
 | 
			
		||||
                if options.update_fdt:
 | 
			
		||||
                    image.SetCalculatedProperties()
 | 
			
		||||
                    for dtb_item in state.GetFdts():
 | 
			
		||||
                        dtb_item.Sync()
 | 
			
		||||
                image.ProcessEntryContents()
 | 
			
		||||
 | 
			
		||||
                # We need to pack the entries to figure out where everything
 | 
			
		||||
                # should be placed. This sets the offset/size of each entry.
 | 
			
		||||
                # However, after packing we call ProcessEntryContents() which
 | 
			
		||||
                # may result in an entry changing size. In that case we need to
 | 
			
		||||
                # do another pass. Since the device tree often contains the
 | 
			
		||||
                # final offset/size information we try to make space for this in
 | 
			
		||||
                # AddMissingProperties() above. However, if the device is
 | 
			
		||||
                # compressed we cannot know this compressed size in advance,
 | 
			
		||||
                # since changing an offset from 0x100 to 0x104 (for example) can
 | 
			
		||||
                # alter the compressed size of the device tree. So we need a
 | 
			
		||||
                # third pass for this.
 | 
			
		||||
                passes = 3
 | 
			
		||||
                for pack_pass in range(passes):
 | 
			
		||||
                    try:
 | 
			
		||||
                        image.PackEntries()
 | 
			
		||||
                        image.CheckSize()
 | 
			
		||||
                        image.CheckEntries()
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        if args.map:
 | 
			
		||||
                            fname = image.WriteMap()
 | 
			
		||||
                            print("Wrote map file '%s' to show errors"  % fname)
 | 
			
		||||
                        raise
 | 
			
		||||
                    image.SetImagePos()
 | 
			
		||||
                    if args.update_fdt:
 | 
			
		||||
                        image.SetCalculatedProperties()
 | 
			
		||||
                        for dtb_item in state.GetFdts():
 | 
			
		||||
                            dtb_item.Sync()
 | 
			
		||||
                    sizes_ok = image.ProcessEntryContents()
 | 
			
		||||
                    if sizes_ok:
 | 
			
		||||
                        break
 | 
			
		||||
                    image.ResetForPack()
 | 
			
		||||
                if not sizes_ok:
 | 
			
		||||
                    image.Raise('Entries expanded after packing (tried %s passes)' %
 | 
			
		||||
                                passes)
 | 
			
		||||
 | 
			
		||||
                image.WriteSymbols()
 | 
			
		||||
                image.BuildImage()
 | 
			
		||||
                if options.map:
 | 
			
		||||
                if args.map:
 | 
			
		||||
                    image.WriteMap()
 | 
			
		||||
 | 
			
		||||
            # Write the updated FDTs to our output files
 | 
			
		||||
 | 
			
		||||
@ -5,19 +5,39 @@
 | 
			
		||||
# Handle various things related to ELF images
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from collections import namedtuple, OrderedDict
 | 
			
		||||
import command
 | 
			
		||||
import io
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import shutil
 | 
			
		||||
import struct
 | 
			
		||||
import tempfile
 | 
			
		||||
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
ELF_TOOLS = True
 | 
			
		||||
try:
 | 
			
		||||
    from elftools.elf.elffile import ELFFile
 | 
			
		||||
    from elftools.elf.sections import SymbolTableSection
 | 
			
		||||
except:  # pragma: no cover
 | 
			
		||||
    ELF_TOOLS = False
 | 
			
		||||
 | 
			
		||||
# This is enabled from control.py
 | 
			
		||||
debug = False
 | 
			
		||||
 | 
			
		||||
Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
 | 
			
		||||
 | 
			
		||||
# Information about an ELF file:
 | 
			
		||||
#    data: Extracted program contents of ELF file (this would be loaded by an
 | 
			
		||||
#           ELF loader when reading this file
 | 
			
		||||
#    load: Load address of code
 | 
			
		||||
#    entry: Entry address of code
 | 
			
		||||
#    memsize: Number of bytes in memory occupied by loading this ELF file
 | 
			
		||||
ElfInfo = namedtuple('ElfInfo', ['data', 'load', 'entry', 'memsize'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def GetSymbols(fname, patterns):
 | 
			
		||||
    """Get the symbols from an ELF file
 | 
			
		||||
@ -128,3 +148,157 @@ def LookupAndWriteSymbols(elf_fname, entry, section):
 | 
			
		||||
                      (msg, name, offset, value, len(value_bytes)))
 | 
			
		||||
            entry.data = (entry.data[:offset] + value_bytes +
 | 
			
		||||
                        entry.data[offset + sym.size:])
 | 
			
		||||
 | 
			
		||||
def MakeElf(elf_fname, text, data):
 | 
			
		||||
    """Make an elf file with the given data in a single section
 | 
			
		||||
 | 
			
		||||
    The output file has a several section including '.text' and '.data',
 | 
			
		||||
    containing the info provided in arguments.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        elf_fname: Output filename
 | 
			
		||||
        text: Text (code) to put in the file's .text section
 | 
			
		||||
        data: Data to put in the file's .data section
 | 
			
		||||
    """
 | 
			
		||||
    outdir = tempfile.mkdtemp(prefix='binman.elf.')
 | 
			
		||||
    s_file = os.path.join(outdir, 'elf.S')
 | 
			
		||||
 | 
			
		||||
    # Spilt the text into two parts so that we can make the entry point two
 | 
			
		||||
    # bytes after the start of the text section
 | 
			
		||||
    text_bytes1 = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in text[:2]]
 | 
			
		||||
    text_bytes2 = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in text[2:]]
 | 
			
		||||
    data_bytes = ['\t.byte\t%#x' % tools.ToByte(byte) for byte in data]
 | 
			
		||||
    with open(s_file, 'w') as fd:
 | 
			
		||||
        print('''/* Auto-generated C program to produce an ELF file for testing */
 | 
			
		||||
 | 
			
		||||
.section .text
 | 
			
		||||
.code32
 | 
			
		||||
.globl _start
 | 
			
		||||
.type _start, @function
 | 
			
		||||
%s
 | 
			
		||||
_start:
 | 
			
		||||
%s
 | 
			
		||||
.ident "comment"
 | 
			
		||||
 | 
			
		||||
.comm fred,8,4
 | 
			
		||||
 | 
			
		||||
.section .empty
 | 
			
		||||
.globl _empty
 | 
			
		||||
_empty:
 | 
			
		||||
.byte 1
 | 
			
		||||
 | 
			
		||||
.globl ernie
 | 
			
		||||
.data
 | 
			
		||||
.type ernie, @object
 | 
			
		||||
.size ernie, 4
 | 
			
		||||
ernie:
 | 
			
		||||
%s
 | 
			
		||||
''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)),
 | 
			
		||||
        file=fd)
 | 
			
		||||
    lds_file = os.path.join(outdir, 'elf.lds')
 | 
			
		||||
 | 
			
		||||
    # Use a linker script to set the alignment and text address.
 | 
			
		||||
    with open(lds_file, 'w') as fd:
 | 
			
		||||
        print('''/* Auto-generated linker script to produce an ELF file for testing */
 | 
			
		||||
 | 
			
		||||
PHDRS
 | 
			
		||||
{
 | 
			
		||||
    text PT_LOAD ;
 | 
			
		||||
    data PT_LOAD ;
 | 
			
		||||
    empty PT_LOAD FLAGS ( 6 ) ;
 | 
			
		||||
    note PT_NOTE ;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
    . = 0xfef20000;
 | 
			
		||||
    ENTRY(_start)
 | 
			
		||||
    .text . : SUBALIGN(0)
 | 
			
		||||
    {
 | 
			
		||||
        *(.text)
 | 
			
		||||
    } :text
 | 
			
		||||
    .data : {
 | 
			
		||||
        *(.data)
 | 
			
		||||
    } :data
 | 
			
		||||
    _bss_start = .;
 | 
			
		||||
    .empty : {
 | 
			
		||||
        *(.empty)
 | 
			
		||||
    } :empty
 | 
			
		||||
    .note : {
 | 
			
		||||
        *(.comment)
 | 
			
		||||
    } :note
 | 
			
		||||
    .bss _bss_start  (OVERLAY) : {
 | 
			
		||||
        *(.bss)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
''', file=fd)
 | 
			
		||||
    # -static: Avoid requiring any shared libraries
 | 
			
		||||
    # -nostdlib: Don't link with C library
 | 
			
		||||
    # -Wl,--build-id=none: Don't generate a build ID, so that we just get the
 | 
			
		||||
    #   text section at the start
 | 
			
		||||
    # -m32: Build for 32-bit x86
 | 
			
		||||
    # -T...: Specifies the link script, which sets the start address
 | 
			
		||||
    stdout = command.Output('cc', '-static', '-nostdlib', '-Wl,--build-id=none',
 | 
			
		||||
                            '-m32','-T', lds_file, '-o', elf_fname, s_file)
 | 
			
		||||
    shutil.rmtree(outdir)
 | 
			
		||||
 | 
			
		||||
def DecodeElf(data, location):
 | 
			
		||||
    """Decode an ELF file and return information about it
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        data: Data from ELF file
 | 
			
		||||
        location: Start address of data to return
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        ElfInfo object containing information about the decoded ELF file
 | 
			
		||||
    """
 | 
			
		||||
    file_size = len(data)
 | 
			
		||||
    with io.BytesIO(data) as fd:
 | 
			
		||||
        elf = ELFFile(fd)
 | 
			
		||||
        data_start = 0xffffffff;
 | 
			
		||||
        data_end = 0;
 | 
			
		||||
        mem_end = 0;
 | 
			
		||||
        virt_to_phys = 0;
 | 
			
		||||
 | 
			
		||||
        for i in range(elf.num_segments()):
 | 
			
		||||
            segment = elf.get_segment(i)
 | 
			
		||||
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
 | 
			
		||||
                skipped = 1  # To make code-coverage see this line
 | 
			
		||||
                continue
 | 
			
		||||
            start = segment['p_paddr']
 | 
			
		||||
            mend = start + segment['p_memsz']
 | 
			
		||||
            rend = start + segment['p_filesz']
 | 
			
		||||
            data_start = min(data_start, start)
 | 
			
		||||
            data_end = max(data_end, rend)
 | 
			
		||||
            mem_end = max(mem_end, mend)
 | 
			
		||||
            if not virt_to_phys:
 | 
			
		||||
                virt_to_phys = segment['p_paddr'] - segment['p_vaddr']
 | 
			
		||||
 | 
			
		||||
        output = bytearray(data_end - data_start)
 | 
			
		||||
        for i in range(elf.num_segments()):
 | 
			
		||||
            segment = elf.get_segment(i)
 | 
			
		||||
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
 | 
			
		||||
                skipped = 1  # To make code-coverage see this line
 | 
			
		||||
                continue
 | 
			
		||||
            start = segment['p_paddr']
 | 
			
		||||
            offset = 0
 | 
			
		||||
            if start < location:
 | 
			
		||||
                offset = location - start
 | 
			
		||||
                start = location
 | 
			
		||||
            # A legal ELF file can have a program header with non-zero length
 | 
			
		||||
            # but zero-length file size and a non-zero offset which, added
 | 
			
		||||
            # together, are greater than input->size (i.e. the total file size).
 | 
			
		||||
            #  So we need to not even test in the case that p_filesz is zero.
 | 
			
		||||
            # Note: All of this code is commented out since we don't have a test
 | 
			
		||||
            # case for it.
 | 
			
		||||
            size = segment['p_filesz']
 | 
			
		||||
            #if not size:
 | 
			
		||||
                #continue
 | 
			
		||||
            #end = segment['p_offset'] + segment['p_filesz']
 | 
			
		||||
            #if end > file_size:
 | 
			
		||||
                #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n',
 | 
			
		||||
                                 #file_size, end)
 | 
			
		||||
            output[start - data_start:start - data_start + size] = (
 | 
			
		||||
                segment.data()[offset:])
 | 
			
		||||
    return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys,
 | 
			
		||||
                   mem_end - data_start)
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,12 @@
 | 
			
		||||
# Test for the elf module
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import command
 | 
			
		||||
import elf
 | 
			
		||||
import test_util
 | 
			
		||||
import tools
 | 
			
		||||
@ -136,6 +139,44 @@ class TestElf(unittest.TestCase):
 | 
			
		||||
        elf.debug = False
 | 
			
		||||
        self.assertTrue(len(stdout.getvalue()) > 0)
 | 
			
		||||
 | 
			
		||||
    def testMakeElf(self):
 | 
			
		||||
        """Test for the MakeElf function"""
 | 
			
		||||
        outdir = tempfile.mkdtemp(prefix='elf.')
 | 
			
		||||
        expected_text = b'1234'
 | 
			
		||||
        expected_data = b'wxyz'
 | 
			
		||||
        elf_fname = os.path.join(outdir, 'elf')
 | 
			
		||||
        bin_fname = os.path.join(outdir, 'elf')
 | 
			
		||||
 | 
			
		||||
        # Make an Elf file and then convert it to a fkat binary file. This
 | 
			
		||||
        # should produce the original data.
 | 
			
		||||
        elf.MakeElf(elf_fname, expected_text, expected_data)
 | 
			
		||||
        stdout = command.Output('objcopy', '-O', 'binary', elf_fname, bin_fname)
 | 
			
		||||
        with open(bin_fname, 'rb') as fd:
 | 
			
		||||
            data = fd.read()
 | 
			
		||||
        self.assertEqual(expected_text + expected_data, data)
 | 
			
		||||
        shutil.rmtree(outdir)
 | 
			
		||||
 | 
			
		||||
    def testDecodeElf(self):
 | 
			
		||||
        """Test for the MakeElf function"""
 | 
			
		||||
        if not elf.ELF_TOOLS:
 | 
			
		||||
            self.skipTest('Python elftools not available')
 | 
			
		||||
        outdir = tempfile.mkdtemp(prefix='elf.')
 | 
			
		||||
        expected_text = b'1234'
 | 
			
		||||
        expected_data = b'wxyz'
 | 
			
		||||
        elf_fname = os.path.join(outdir, 'elf')
 | 
			
		||||
        elf.MakeElf(elf_fname, expected_text, expected_data)
 | 
			
		||||
        data = tools.ReadFile(elf_fname)
 | 
			
		||||
 | 
			
		||||
        load = 0xfef20000
 | 
			
		||||
        entry = load + 2
 | 
			
		||||
        expected = expected_text + expected_data
 | 
			
		||||
        self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)),
 | 
			
		||||
                         elf.DecodeElf(data, 0))
 | 
			
		||||
        self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:],
 | 
			
		||||
                                     load, entry, len(expected)),
 | 
			
		||||
                         elf.DecodeElf(data, load + 2))
 | 
			
		||||
        #shutil.rmtree(outdir)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import sys
 | 
			
		||||
import fdt_util
 | 
			
		||||
import state
 | 
			
		||||
import tools
 | 
			
		||||
import tout
 | 
			
		||||
 | 
			
		||||
modules = {}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +34,10 @@ our_path = os.path.dirname(os.path.realpath(__file__))
 | 
			
		||||
# device-tree properties.
 | 
			
		||||
EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
 | 
			
		||||
 | 
			
		||||
# Information about an entry for use when displaying summaries
 | 
			
		||||
EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size',
 | 
			
		||||
                                     'image_pos', 'uncomp_size', 'offset',
 | 
			
		||||
                                     'entry'])
 | 
			
		||||
 | 
			
		||||
class Entry(object):
 | 
			
		||||
    """An Entry in the section
 | 
			
		||||
@ -51,6 +56,8 @@ class Entry(object):
 | 
			
		||||
        offset: Offset of entry within the section, None if not known yet (in
 | 
			
		||||
            which case it will be calculated by Pack())
 | 
			
		||||
        size: Entry size in bytes, None if not known
 | 
			
		||||
        uncomp_size: Size of uncompressed data in bytes, if the entry is
 | 
			
		||||
            compressed, else None
 | 
			
		||||
        contents_size: Size of contents in bytes, 0 by default
 | 
			
		||||
        align: Entry start offset alignment, or None
 | 
			
		||||
        align_size: Entry size alignment, or None
 | 
			
		||||
@ -58,6 +65,9 @@ class Entry(object):
 | 
			
		||||
        pad_before: Number of pad bytes before the contents, 0 if none
 | 
			
		||||
        pad_after: Number of pad bytes after the contents, 0 if none
 | 
			
		||||
        data: Contents of entry (string of bytes)
 | 
			
		||||
        compress: Compression algoithm used (e.g. 'lz4'), 'none' if none
 | 
			
		||||
        orig_offset: Original offset value read from node
 | 
			
		||||
        orig_size: Original size value read from node
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node, read_node=True, name_prefix=''):
 | 
			
		||||
        self.section = section
 | 
			
		||||
@ -66,6 +76,7 @@ class Entry(object):
 | 
			
		||||
        self.name = node and (name_prefix + node.name) or 'none'
 | 
			
		||||
        self.offset = None
 | 
			
		||||
        self.size = None
 | 
			
		||||
        self.uncomp_size = None
 | 
			
		||||
        self.data = None
 | 
			
		||||
        self.contents_size = 0
 | 
			
		||||
        self.align = None
 | 
			
		||||
@ -76,15 +87,15 @@ class Entry(object):
 | 
			
		||||
        self.offset_unset = False
 | 
			
		||||
        self.image_pos = None
 | 
			
		||||
        self._expand_size = False
 | 
			
		||||
        self.compress = 'none'
 | 
			
		||||
        if read_node:
 | 
			
		||||
            self.ReadNode()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def Lookup(section, node_path, etype):
 | 
			
		||||
    def Lookup(node_path, etype):
 | 
			
		||||
        """Look up the entry class for a node.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            section:   Section object containing this node
 | 
			
		||||
            node_node: Path name of Node object containing information about
 | 
			
		||||
                       the entry to create (used for errors)
 | 
			
		||||
            etype:   Entry type to use
 | 
			
		||||
@ -135,7 +146,7 @@ class Entry(object):
 | 
			
		||||
        """
 | 
			
		||||
        if not etype:
 | 
			
		||||
            etype = fdt_util.GetString(node, 'type', node.name)
 | 
			
		||||
        obj = Entry.Lookup(section, node.path, etype)
 | 
			
		||||
        obj = Entry.Lookup(node.path, etype)
 | 
			
		||||
 | 
			
		||||
        # Call its constructor to get the object we want.
 | 
			
		||||
        return obj(section, etype, node)
 | 
			
		||||
@ -149,6 +160,14 @@ class Entry(object):
 | 
			
		||||
            self.Raise("Please use 'offset' instead of 'pos'")
 | 
			
		||||
        self.offset = fdt_util.GetInt(self._node, 'offset')
 | 
			
		||||
        self.size = fdt_util.GetInt(self._node, 'size')
 | 
			
		||||
        self.orig_offset = self.offset
 | 
			
		||||
        self.orig_size = self.size
 | 
			
		||||
 | 
			
		||||
        # These should not be set in input files, but are set in an FDT map,
 | 
			
		||||
        # which is also read by this code.
 | 
			
		||||
        self.image_pos = fdt_util.GetInt(self._node, 'image-pos')
 | 
			
		||||
        self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size')
 | 
			
		||||
 | 
			
		||||
        self.align = fdt_util.GetInt(self._node, 'align')
 | 
			
		||||
        if tools.NotPowerOfTwo(self.align):
 | 
			
		||||
            raise ValueError("Node '%s': Alignment %s must be a power of two" %
 | 
			
		||||
@ -157,8 +176,8 @@ class Entry(object):
 | 
			
		||||
        self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
 | 
			
		||||
        self.align_size = fdt_util.GetInt(self._node, 'align-size')
 | 
			
		||||
        if tools.NotPowerOfTwo(self.align_size):
 | 
			
		||||
            raise ValueError("Node '%s': Alignment size %s must be a power "
 | 
			
		||||
                             "of two" % (self._node.path, self.align_size))
 | 
			
		||||
            self.Raise("Alignment size %s must be a power of two" %
 | 
			
		||||
                       self.align_size)
 | 
			
		||||
        self.align_end = fdt_util.GetInt(self._node, 'align-end')
 | 
			
		||||
        self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
 | 
			
		||||
        self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
 | 
			
		||||
@ -188,6 +207,8 @@ class Entry(object):
 | 
			
		||||
        for prop in ['offset', 'size', 'image-pos']:
 | 
			
		||||
            if not prop in self._node.props:
 | 
			
		||||
                state.AddZeroProp(self._node, prop)
 | 
			
		||||
        if self.compress != 'none':
 | 
			
		||||
            state.AddZeroProp(self._node, 'uncomp-size')
 | 
			
		||||
        err = state.CheckAddHashProp(self._node)
 | 
			
		||||
        if err:
 | 
			
		||||
            self.Raise(err)
 | 
			
		||||
@ -196,8 +217,10 @@ class Entry(object):
 | 
			
		||||
        """Set the value of device-tree properties calculated by binman"""
 | 
			
		||||
        state.SetInt(self._node, 'offset', self.offset)
 | 
			
		||||
        state.SetInt(self._node, 'size', self.size)
 | 
			
		||||
        state.SetInt(self._node, 'image-pos',
 | 
			
		||||
                       self.image_pos - self.section.GetRootSkipAtStart())
 | 
			
		||||
        base = self.section.GetRootSkipAtStart() if self.section else 0
 | 
			
		||||
        state.SetInt(self._node, 'image-pos', self.image_pos - base)
 | 
			
		||||
        if self.uncomp_size is not None:
 | 
			
		||||
            state.SetInt(self._node, 'uncomp-size', self.uncomp_size)
 | 
			
		||||
        state.CheckSetHashValue(self._node, self.GetData)
 | 
			
		||||
 | 
			
		||||
    def ProcessFdt(self, fdt):
 | 
			
		||||
@ -229,26 +252,36 @@ class Entry(object):
 | 
			
		||||
        This sets both the data and content_size properties
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: Data to set to the contents (string)
 | 
			
		||||
            data: Data to set to the contents (bytes)
 | 
			
		||||
        """
 | 
			
		||||
        self.data = data
 | 
			
		||||
        self.contents_size = len(self.data)
 | 
			
		||||
 | 
			
		||||
    def ProcessContentsUpdate(self, data):
 | 
			
		||||
        """Update the contens of an entry, after the size is fixed
 | 
			
		||||
        """Update the contents of an entry, after the size is fixed
 | 
			
		||||
 | 
			
		||||
        This checks that the new data is the same size as the old.
 | 
			
		||||
        This checks that the new data is the same size as the old. If the size
 | 
			
		||||
        has changed, this triggers a re-run of the packing algorithm.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: Data to set to the contents (string)
 | 
			
		||||
            data: Data to set to the contents (bytes)
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if the new data size is not the same as the old
 | 
			
		||||
        """
 | 
			
		||||
        if len(data) != self.contents_size:
 | 
			
		||||
        size_ok = True
 | 
			
		||||
        new_size = len(data)
 | 
			
		||||
        if state.AllowEntryExpansion():
 | 
			
		||||
            if new_size > self.contents_size:
 | 
			
		||||
                tout.Debug("Entry '%s' size change from %#x to %#x" % (
 | 
			
		||||
                    self._node.path, self.contents_size, new_size))
 | 
			
		||||
                # self.data will indicate the new size needed
 | 
			
		||||
                size_ok = False
 | 
			
		||||
        elif new_size != self.contents_size:
 | 
			
		||||
            self.Raise('Cannot update entry size from %d to %d' %
 | 
			
		||||
                       (len(data), self.contents_size))
 | 
			
		||||
                       (self.contents_size, new_size))
 | 
			
		||||
        self.SetContents(data)
 | 
			
		||||
        return size_ok
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        """Figure out the contents of an entry.
 | 
			
		||||
@ -260,6 +293,11 @@ class Entry(object):
 | 
			
		||||
        # No contents by default: subclasses can implement this
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ResetForPack(self):
 | 
			
		||||
        """Reset offset/size fields so that packing can be done again"""
 | 
			
		||||
        self.offset = self.orig_offset
 | 
			
		||||
        self.size = self.orig_size
 | 
			
		||||
 | 
			
		||||
    def Pack(self, offset):
 | 
			
		||||
        """Figure out how to pack the entry into the section
 | 
			
		||||
 | 
			
		||||
@ -355,11 +393,34 @@ class Entry(object):
 | 
			
		||||
        return self.data
 | 
			
		||||
 | 
			
		||||
    def GetOffsets(self):
 | 
			
		||||
        """Get the offsets for siblings
 | 
			
		||||
 | 
			
		||||
        Some entry types can contain information about the position or size of
 | 
			
		||||
        other entries. An example of this is the Intel Flash Descriptor, which
 | 
			
		||||
        knows where the Intel Management Engine section should go.
 | 
			
		||||
 | 
			
		||||
        If this entry knows about the position of other entries, it can specify
 | 
			
		||||
        this by returning values here
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Dict:
 | 
			
		||||
                key: Entry type
 | 
			
		||||
                value: List containing position and size of the given entry
 | 
			
		||||
                    type. Either can be None if not known
 | 
			
		||||
        """
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    def SetOffsetSize(self, pos, size):
 | 
			
		||||
        self.offset = pos
 | 
			
		||||
        self.size = size
 | 
			
		||||
    def SetOffsetSize(self, offset, size):
 | 
			
		||||
        """Set the offset and/or size of an entry
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            offset: New offset, or None to leave alone
 | 
			
		||||
            size: New size, or None to leave alone
 | 
			
		||||
        """
 | 
			
		||||
        if offset is not None:
 | 
			
		||||
            self.offset = offset
 | 
			
		||||
        if size is not None:
 | 
			
		||||
            self.size = size
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self, image_pos):
 | 
			
		||||
        """Set the position in the image
 | 
			
		||||
@ -370,7 +431,22 @@ class Entry(object):
 | 
			
		||||
        self.image_pos = image_pos + self.offset
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        pass
 | 
			
		||||
        """Do any post-packing updates of entry contents
 | 
			
		||||
 | 
			
		||||
        This function should call ProcessContentsUpdate() to update the entry
 | 
			
		||||
        contents, if necessary, returning its return value here.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            data: Data to set to the contents (bytes)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            True if the new data size is OK, False if expansion is needed
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if the new data size is not the same as the old and
 | 
			
		||||
                state.AllowEntryExpansion() is False
 | 
			
		||||
        """
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def WriteSymbols(self, section):
 | 
			
		||||
        """Write symbol values into binary files for access at run time
 | 
			
		||||
@ -482,7 +558,9 @@ features to produce new behaviours.
 | 
			
		||||
            modules.remove('_testing')
 | 
			
		||||
        missing = []
 | 
			
		||||
        for name in modules:
 | 
			
		||||
            module = Entry.Lookup(name, name, name)
 | 
			
		||||
            if name.startswith('__'):
 | 
			
		||||
                continue
 | 
			
		||||
            module = Entry.Lookup(name, name)
 | 
			
		||||
            docs = getattr(module, '__doc__')
 | 
			
		||||
            if test_missing == name:
 | 
			
		||||
                docs = None
 | 
			
		||||
@ -529,3 +607,76 @@ features to produce new behaviours.
 | 
			
		||||
            # the data grows. This should not fail, but check it to be sure.
 | 
			
		||||
            if not self.ObtainContents():
 | 
			
		||||
                self.Raise('Cannot obtain contents when expanding entry')
 | 
			
		||||
 | 
			
		||||
    def HasSibling(self, name):
 | 
			
		||||
        """Check if there is a sibling of a given name
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            True if there is an entry with this name in the the same section,
 | 
			
		||||
                else False
 | 
			
		||||
        """
 | 
			
		||||
        return name in self.section.GetEntries()
 | 
			
		||||
 | 
			
		||||
    def GetSiblingImagePos(self, name):
 | 
			
		||||
        """Return the image position of the given sibling
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Image position of sibling, or None if the sibling has no position,
 | 
			
		||||
                or False if there is no such sibling
 | 
			
		||||
        """
 | 
			
		||||
        if not self.HasSibling(name):
 | 
			
		||||
            return False
 | 
			
		||||
        return self.section.GetEntries()[name].image_pos
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def AddEntryInfo(entries, indent, name, etype, size, image_pos,
 | 
			
		||||
                     uncomp_size, offset, entry):
 | 
			
		||||
        """Add a new entry to the entries list
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entries: List (of EntryInfo objects) to add to
 | 
			
		||||
            indent: Current indent level to add to list
 | 
			
		||||
            name: Entry name (string)
 | 
			
		||||
            etype: Entry type (string)
 | 
			
		||||
            size: Entry size in bytes (int)
 | 
			
		||||
            image_pos: Position within image in bytes (int)
 | 
			
		||||
            uncomp_size: Uncompressed size if the entry uses compression, else
 | 
			
		||||
                None
 | 
			
		||||
            offset: Entry offset within parent in bytes (int)
 | 
			
		||||
            entry: Entry object
 | 
			
		||||
        """
 | 
			
		||||
        entries.append(EntryInfo(indent, name, etype, size, image_pos,
 | 
			
		||||
                                 uncomp_size, offset, entry))
 | 
			
		||||
 | 
			
		||||
    def ListEntries(self, entries, indent):
 | 
			
		||||
        """Add files in this entry to the list of entries
 | 
			
		||||
 | 
			
		||||
        This can be overridden by subclasses which need different behaviour.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entries: List (of EntryInfo objects) to add to
 | 
			
		||||
            indent: Current indent level to add to list
 | 
			
		||||
        """
 | 
			
		||||
        self.AddEntryInfo(entries, indent, self.name, self.etype, self.size,
 | 
			
		||||
                          self.image_pos, self.uncomp_size, self.offset, self)
 | 
			
		||||
 | 
			
		||||
    def ReadData(self, decomp=True):
 | 
			
		||||
        """Read the data for an entry from the image
 | 
			
		||||
 | 
			
		||||
        This is used when the image has been read in and we want to extract the
 | 
			
		||||
        data for a particular entry from that image.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            decomp: True to decompress any compressed data before returning it;
 | 
			
		||||
                False to return the raw, uncompressed data
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Entry data (bytes)
 | 
			
		||||
        """
 | 
			
		||||
        # Use True here so that we get an uncompressed section to work from,
 | 
			
		||||
        # although compressed sections are currently not supported
 | 
			
		||||
        data = self.section.ReadData(True)
 | 
			
		||||
        tout.Info('%s: Reading data from offset %#x-%#x, size %#x (avail %#x)' %
 | 
			
		||||
                  (self.GetPath(), self.offset, self.offset + self.size,
 | 
			
		||||
                   self.size, len(data)))
 | 
			
		||||
        return data[self.offset:self.offset + self.size]
 | 
			
		||||
 | 
			
		||||
@ -9,12 +9,11 @@ import os
 | 
			
		||||
import sys
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import entry
 | 
			
		||||
import fdt
 | 
			
		||||
import fdt_util
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
entry = None
 | 
			
		||||
 | 
			
		||||
class TestEntry(unittest.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        tools.PrepareOutputDir(None)
 | 
			
		||||
@ -29,16 +28,7 @@ class TestEntry(unittest.TestCase):
 | 
			
		||||
        dtb = fdt.FdtScan(fname)
 | 
			
		||||
        return dtb.GetNode('/binman/u-boot')
 | 
			
		||||
 | 
			
		||||
    def test1EntryNoImportLib(self):
 | 
			
		||||
        """Test that we can import Entry subclassess successfully"""
 | 
			
		||||
 | 
			
		||||
        sys.modules['importlib'] = None
 | 
			
		||||
        global entry
 | 
			
		||||
        import entry
 | 
			
		||||
        entry.Entry.Create(None, self.GetNode(), 'u-boot')
 | 
			
		||||
 | 
			
		||||
    def test2EntryImportLib(self):
 | 
			
		||||
        del sys.modules['importlib']
 | 
			
		||||
    def _ReloadEntry(self):
 | 
			
		||||
        global entry
 | 
			
		||||
        if entry:
 | 
			
		||||
            if sys.version_info[0] >= 3:
 | 
			
		||||
@ -48,8 +38,21 @@ class TestEntry(unittest.TestCase):
 | 
			
		||||
                reload(entry)
 | 
			
		||||
        else:
 | 
			
		||||
            import entry
 | 
			
		||||
 | 
			
		||||
    def test1EntryNoImportLib(self):
 | 
			
		||||
        """Test that we can import Entry subclassess successfully"""
 | 
			
		||||
        sys.modules['importlib'] = None
 | 
			
		||||
        global entry
 | 
			
		||||
        self._ReloadEntry()
 | 
			
		||||
        entry.Entry.Create(None, self.GetNode(), 'u-boot')
 | 
			
		||||
        self.assertFalse(entry.have_importlib)
 | 
			
		||||
 | 
			
		||||
    def test2EntryImportLib(self):
 | 
			
		||||
        del sys.modules['importlib']
 | 
			
		||||
        global entry
 | 
			
		||||
        self._ReloadEntry()
 | 
			
		||||
        entry.Entry.Create(None, self.GetNode(), 'u-boot-spl')
 | 
			
		||||
        del entry
 | 
			
		||||
        self.assertTrue(entry.have_importlib)
 | 
			
		||||
 | 
			
		||||
    def testEntryContents(self):
 | 
			
		||||
        """Test the Entry bass class"""
 | 
			
		||||
@ -59,7 +62,6 @@ class TestEntry(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def testUnknownEntry(self):
 | 
			
		||||
        """Test that unknown entry types are detected"""
 | 
			
		||||
        import entry
 | 
			
		||||
        Node = collections.namedtuple('Node', ['name', 'path'])
 | 
			
		||||
        node = Node('invalid-name', 'invalid-path')
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
@ -69,7 +71,6 @@ class TestEntry(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def testUniqueName(self):
 | 
			
		||||
        """Test Entry.GetUniqueName"""
 | 
			
		||||
        import entry
 | 
			
		||||
        Node = collections.namedtuple('Node', ['name', 'parent'])
 | 
			
		||||
        base_node = Node('root', None)
 | 
			
		||||
        base_entry = entry.Entry(None, None, base_node, read_node=False)
 | 
			
		||||
@ -80,7 +81,6 @@ class TestEntry(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def testGetDefaultFilename(self):
 | 
			
		||||
        """Trivial test for this base class function"""
 | 
			
		||||
        import entry
 | 
			
		||||
        base_entry = entry.Entry(None, None, None, read_node=False)
 | 
			
		||||
        self.assertIsNone(base_entry.GetDefaultFilename())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								tools/binman/etype/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tools/binman/etype/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -50,6 +50,8 @@ class Entry__testing(Entry):
 | 
			
		||||
                                                    'bad-update-contents')
 | 
			
		||||
        self.return_contents_once = fdt_util.GetBool(self._node,
 | 
			
		||||
                                                     'return-contents-once')
 | 
			
		||||
        self.bad_update_contents_twice = fdt_util.GetBool(self._node,
 | 
			
		||||
                                                    'bad-update-contents-twice')
 | 
			
		||||
 | 
			
		||||
        # Set to True when the entry is ready to process the FDT.
 | 
			
		||||
        self.process_fdt_ready = False
 | 
			
		||||
@ -71,11 +73,12 @@ class Entry__testing(Entry):
 | 
			
		||||
        if self.force_bad_datatype:
 | 
			
		||||
            self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)])
 | 
			
		||||
        self.return_contents = True
 | 
			
		||||
        self.contents = b'a'
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        if self.return_unknown_contents or not self.return_contents:
 | 
			
		||||
            return False
 | 
			
		||||
        self.data = b'a'
 | 
			
		||||
        self.data = self.contents
 | 
			
		||||
        self.contents_size = len(self.data)
 | 
			
		||||
        if self.return_contents_once:
 | 
			
		||||
            self.return_contents = False
 | 
			
		||||
@ -88,9 +91,14 @@ class Entry__testing(Entry):
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        if self.bad_update_contents:
 | 
			
		||||
            # Request to update the conents with something larger, to cause a
 | 
			
		||||
            # Request to update the contents with something larger, to cause a
 | 
			
		||||
            # failure.
 | 
			
		||||
            self.ProcessContentsUpdate('aa')
 | 
			
		||||
            if self.bad_update_contents_twice:
 | 
			
		||||
                self.contents += b'a'
 | 
			
		||||
            else:
 | 
			
		||||
                self.contents = b'aa'
 | 
			
		||||
            return self.ProcessContentsUpdate(self.contents)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ProcessFdt(self, fdt):
 | 
			
		||||
        """Force reprocessing the first time"""
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ from entry import Entry
 | 
			
		||||
import fdt_util
 | 
			
		||||
import state
 | 
			
		||||
import tools
 | 
			
		||||
import tout
 | 
			
		||||
 | 
			
		||||
class Entry_blob(Entry):
 | 
			
		||||
    """Entry containing an arbitrary binary blob
 | 
			
		||||
@ -33,8 +34,7 @@ class Entry_blob(Entry):
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
        self._filename = fdt_util.GetString(self._node, 'filename', self.etype)
 | 
			
		||||
        self._compress = fdt_util.GetString(self._node, 'compress', 'none')
 | 
			
		||||
        self._uncompressed_size = None
 | 
			
		||||
        self.compress = fdt_util.GetString(self._node, 'compress', 'none')
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        self._filename = self.GetDefaultFilename()
 | 
			
		||||
@ -42,37 +42,40 @@ class Entry_blob(Entry):
 | 
			
		||||
        self.ReadBlobContents()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ReadBlobContents(self):
 | 
			
		||||
        # We assume the data is small enough to fit into memory. If this
 | 
			
		||||
        # is used for large filesystem image that might not be true.
 | 
			
		||||
        # In that case, Image.BuildImage() could be adjusted to use a
 | 
			
		||||
        # new Entry method which can read in chunks. Then we could copy
 | 
			
		||||
        # the data in chunks and avoid reading it all at once. For now
 | 
			
		||||
        # this seems like an unnecessary complication.
 | 
			
		||||
        data = tools.ReadFile(self._pathname)
 | 
			
		||||
        if self._compress == 'lz4':
 | 
			
		||||
            self._uncompressed_size = len(data)
 | 
			
		||||
            '''
 | 
			
		||||
            import lz4  # Import this only if needed (python-lz4 dependency)
 | 
			
		||||
    def CompressData(self, indata):
 | 
			
		||||
        if self.compress != 'none':
 | 
			
		||||
            self.uncomp_size = len(indata)
 | 
			
		||||
        data = tools.Compress(indata, self.compress)
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                data = lz4.frame.compress(data)
 | 
			
		||||
            except AttributeError:
 | 
			
		||||
                data = lz4.compress(data)
 | 
			
		||||
            '''
 | 
			
		||||
            data = tools.Run('lz4', '-c', self._pathname, binary=True)
 | 
			
		||||
    def ReadBlobContents(self):
 | 
			
		||||
        """Read blob contents into memory
 | 
			
		||||
 | 
			
		||||
        This function compresses the data before storing if needed.
 | 
			
		||||
 | 
			
		||||
        We assume the data is small enough to fit into memory. If this
 | 
			
		||||
        is used for large filesystem image that might not be true.
 | 
			
		||||
        In that case, Image.BuildImage() could be adjusted to use a
 | 
			
		||||
        new Entry method which can read in chunks. Then we could copy
 | 
			
		||||
        the data in chunks and avoid reading it all at once. For now
 | 
			
		||||
        this seems like an unnecessary complication.
 | 
			
		||||
        """
 | 
			
		||||
        indata = tools.ReadFile(self._pathname)
 | 
			
		||||
        data = self.CompressData(indata)
 | 
			
		||||
        self.SetContents(data)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def GetDefaultFilename(self):
 | 
			
		||||
        return self._filename
 | 
			
		||||
 | 
			
		||||
    def AddMissingProperties(self):
 | 
			
		||||
        Entry.AddMissingProperties(self)
 | 
			
		||||
        if self._compress != 'none':
 | 
			
		||||
            state.AddZeroProp(self._node, 'uncomp-size')
 | 
			
		||||
 | 
			
		||||
    def SetCalculatedProperties(self):
 | 
			
		||||
        Entry.SetCalculatedProperties(self)
 | 
			
		||||
        if self._uncompressed_size is not None:
 | 
			
		||||
            state.SetInt(self._node, 'uncomp-size', self._uncompressed_size)
 | 
			
		||||
    def ReadData(self, decomp=True):
 | 
			
		||||
        indata = Entry.ReadData(self, decomp)
 | 
			
		||||
        if decomp:
 | 
			
		||||
            data = tools.Decompress(indata, self.compress)
 | 
			
		||||
            if self.uncomp_size:
 | 
			
		||||
                tout.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" %
 | 
			
		||||
                          (self.GetPath(), len(indata), self.compress,
 | 
			
		||||
                           len(data)))
 | 
			
		||||
        else:
 | 
			
		||||
            data = indata
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
@ -23,11 +23,11 @@ class Entry_blob_dtb(Entry_blob):
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        """Get the device-tree from the list held by the 'state' module"""
 | 
			
		||||
        self._filename = self.GetDefaultFilename()
 | 
			
		||||
        self._pathname, data = state.GetFdtContents(self._filename)
 | 
			
		||||
        self.SetContents(data)
 | 
			
		||||
        return True
 | 
			
		||||
        self._pathname, _ = state.GetFdtContents(self._filename)
 | 
			
		||||
        return Entry_blob.ReadBlobContents(self)
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        """Re-read the DTB contents so that we get any calculated properties"""
 | 
			
		||||
        _, data = state.GetFdtContents(self._filename)
 | 
			
		||||
        self.SetContents(data)
 | 
			
		||||
        _, indata = state.GetFdtContents(self._filename)
 | 
			
		||||
        data = self.CompressData(indata)
 | 
			
		||||
        return self.ProcessContentsUpdate(data)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										263
									
								
								tools/binman/etype/cbfs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								tools/binman/etype/cbfs.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,263 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright 2019 Google LLC
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
#
 | 
			
		||||
# Entry-type module for a Coreboot Filesystem (CBFS)
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
import cbfs_util
 | 
			
		||||
from cbfs_util import CbfsWriter
 | 
			
		||||
from entry import Entry
 | 
			
		||||
import fdt_util
 | 
			
		||||
import state
 | 
			
		||||
 | 
			
		||||
class Entry_cbfs(Entry):
 | 
			
		||||
    """Entry containing a Coreboot Filesystem (CBFS)
 | 
			
		||||
 | 
			
		||||
    A CBFS provides a way to group files into a group. It has a simple directory
 | 
			
		||||
    structure and allows the position of individual files to be set, since it is
 | 
			
		||||
    designed to support execute-in-place in an x86 SPI-flash device. Where XIP
 | 
			
		||||
    is not used, it supports compression and storing ELF files.
 | 
			
		||||
 | 
			
		||||
    CBFS is used by coreboot as its way of orgnanising SPI-flash contents.
 | 
			
		||||
 | 
			
		||||
    The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:
 | 
			
		||||
 | 
			
		||||
        cbfs {
 | 
			
		||||
            size = <0x100000>;
 | 
			
		||||
            u-boot {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
            u-boot-dtb {
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb.
 | 
			
		||||
    Note that the size is required since binman does not support calculating it.
 | 
			
		||||
    The contents of each entry is just what binman would normally provide if it
 | 
			
		||||
    were not a CBFS node. A blob type can be used to import arbitrary files as
 | 
			
		||||
    with the second subnode below:
 | 
			
		||||
 | 
			
		||||
        cbfs {
 | 
			
		||||
            size = <0x100000>;
 | 
			
		||||
            u-boot {
 | 
			
		||||
                cbfs-name = "BOOT";
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            dtb {
 | 
			
		||||
                type = "blob";
 | 
			
		||||
                filename = "u-boot.dtb";
 | 
			
		||||
                cbfs-type = "raw";
 | 
			
		||||
                cbfs-compress = "lz4";
 | 
			
		||||
                cbfs-offset = <0x100000>;
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and
 | 
			
		||||
    u-boot.dtb (named "dtb") and compressed with the lz4 algorithm.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Properties supported in the top-level CBFS node:
 | 
			
		||||
 | 
			
		||||
    cbfs-arch:
 | 
			
		||||
        Defaults to "x86", but you can specify the architecture if needed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Properties supported in the CBFS entry subnodes:
 | 
			
		||||
 | 
			
		||||
    cbfs-name:
 | 
			
		||||
        This is the name of the file created in CBFS. It defaults to the entry
 | 
			
		||||
        name (which is the node name), but you can override it with this
 | 
			
		||||
        property.
 | 
			
		||||
 | 
			
		||||
    cbfs-type:
 | 
			
		||||
        This is the CBFS file type. The following are supported:
 | 
			
		||||
 | 
			
		||||
        raw:
 | 
			
		||||
            This is a 'raw' file, although compression is supported. It can be
 | 
			
		||||
            used to store any file in CBFS.
 | 
			
		||||
 | 
			
		||||
        stage:
 | 
			
		||||
            This is an ELF file that has been loaded (i.e. mapped to memory), so
 | 
			
		||||
            appears in the CBFS as a flat binary. The input file must be an ELF
 | 
			
		||||
            image, for example this puts "u-boot" (the ELF image) into a 'stage'
 | 
			
		||||
            entry:
 | 
			
		||||
 | 
			
		||||
                cbfs {
 | 
			
		||||
                    size = <0x100000>;
 | 
			
		||||
                    u-boot-elf {
 | 
			
		||||
                        cbfs-name = "BOOT";
 | 
			
		||||
                        cbfs-type = "stage";
 | 
			
		||||
                    };
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            You can use your own ELF file with something like:
 | 
			
		||||
 | 
			
		||||
                cbfs {
 | 
			
		||||
                    size = <0x100000>;
 | 
			
		||||
                    something {
 | 
			
		||||
                        type = "blob";
 | 
			
		||||
                        filename = "cbfs-stage.elf";
 | 
			
		||||
                        cbfs-type = "stage";
 | 
			
		||||
                    };
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
            As mentioned, the file is converted to a flat binary, so it is
 | 
			
		||||
            equivalent to adding "u-boot.bin", for example, but with the load and
 | 
			
		||||
            start addresses specified by the ELF. At present there is no option
 | 
			
		||||
            to add a flat binary with a load/start address, similar to the
 | 
			
		||||
            'add-flat-binary' option in cbfstool.
 | 
			
		||||
 | 
			
		||||
    cbfs-offset:
 | 
			
		||||
        This is the offset of the file's data within the CBFS. It is used to
 | 
			
		||||
        specify where the file should be placed in cases where a fixed position
 | 
			
		||||
        is needed. Typical uses are for code which is not relocatable and must
 | 
			
		||||
        execute in-place from a particular address. This works because SPI flash
 | 
			
		||||
        is generally mapped into memory on x86 devices. The file header is
 | 
			
		||||
        placed before this offset so that the data start lines up exactly with
 | 
			
		||||
        the chosen offset. If this property is not provided, then the file is
 | 
			
		||||
        placed in the next available spot.
 | 
			
		||||
 | 
			
		||||
    The current implementation supports only a subset of CBFS features. It does
 | 
			
		||||
    not support other file types (e.g. payload), adding multiple files (like the
 | 
			
		||||
    'files' entry with a pattern supported by binman), putting files at a
 | 
			
		||||
    particular offset in the CBFS and a few other things.
 | 
			
		||||
 | 
			
		||||
    Of course binman can create images containing multiple CBFSs, simply by
 | 
			
		||||
    defining these in the binman config:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        binman {
 | 
			
		||||
            size = <0x800000>;
 | 
			
		||||
            cbfs {
 | 
			
		||||
                offset = <0x100000>;
 | 
			
		||||
                size = <0x100000>;
 | 
			
		||||
                u-boot {
 | 
			
		||||
                    cbfs-type = "raw";
 | 
			
		||||
                };
 | 
			
		||||
                u-boot-dtb {
 | 
			
		||||
                    cbfs-type = "raw";
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            cbfs2 {
 | 
			
		||||
                offset = <0x700000>;
 | 
			
		||||
                size = <0x100000>;
 | 
			
		||||
                u-boot {
 | 
			
		||||
                    cbfs-type = "raw";
 | 
			
		||||
                };
 | 
			
		||||
                u-boot-dtb {
 | 
			
		||||
                    cbfs-type = "raw";
 | 
			
		||||
                };
 | 
			
		||||
                image {
 | 
			
		||||
                    type = "blob";
 | 
			
		||||
                    filename = "image.jpg";
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB,
 | 
			
		||||
    both of size 1MB.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
        self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86')
 | 
			
		||||
        self._cbfs_entries = OrderedDict()
 | 
			
		||||
        self._ReadSubnodes()
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        arch = cbfs_util.find_arch(self._cbfs_arg)
 | 
			
		||||
        if arch is None:
 | 
			
		||||
            self.Raise("Invalid architecture '%s'" % self._cbfs_arg)
 | 
			
		||||
        if self.size is None:
 | 
			
		||||
            self.Raise("'cbfs' entry must have a size property")
 | 
			
		||||
        cbfs = CbfsWriter(self.size, arch)
 | 
			
		||||
        for entry in self._cbfs_entries.values():
 | 
			
		||||
            # First get the input data and put it in a file. If not available,
 | 
			
		||||
            # try later.
 | 
			
		||||
            if not entry.ObtainContents():
 | 
			
		||||
                return False
 | 
			
		||||
            data = entry.GetData()
 | 
			
		||||
            cfile = None
 | 
			
		||||
            if entry._type == 'raw':
 | 
			
		||||
                cfile = cbfs.add_file_raw(entry._cbfs_name, data,
 | 
			
		||||
                                          entry._cbfs_offset,
 | 
			
		||||
                                          entry._cbfs_compress)
 | 
			
		||||
            elif entry._type == 'stage':
 | 
			
		||||
                cfile = cbfs.add_file_stage(entry._cbfs_name, data,
 | 
			
		||||
                                            entry._cbfs_offset)
 | 
			
		||||
            else:
 | 
			
		||||
                entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" %
 | 
			
		||||
                            entry._type)
 | 
			
		||||
            if cfile:
 | 
			
		||||
                entry._cbfs_file = cfile
 | 
			
		||||
        data = cbfs.get_data()
 | 
			
		||||
        self.SetContents(data)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _ReadSubnodes(self):
 | 
			
		||||
        """Read the subnodes to find out what should go in this IFWI"""
 | 
			
		||||
        for node in self._node.subnodes:
 | 
			
		||||
            entry = Entry.Create(self.section, node)
 | 
			
		||||
            entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name)
 | 
			
		||||
            entry._type = fdt_util.GetString(node, 'cbfs-type')
 | 
			
		||||
            compress = fdt_util.GetString(node, 'cbfs-compress', 'none')
 | 
			
		||||
            entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset')
 | 
			
		||||
            entry._cbfs_compress = cbfs_util.find_compress(compress)
 | 
			
		||||
            if entry._cbfs_compress is None:
 | 
			
		||||
                self.Raise("Invalid compression in '%s': '%s'" %
 | 
			
		||||
                           (node.name, compress))
 | 
			
		||||
            self._cbfs_entries[entry._cbfs_name] = entry
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self, image_pos):
 | 
			
		||||
        """Override this function to set all the entry properties from CBFS
 | 
			
		||||
 | 
			
		||||
        We can only do this once image_pos is known
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            image_pos: Position of this entry in the image
 | 
			
		||||
        """
 | 
			
		||||
        Entry.SetImagePos(self, image_pos)
 | 
			
		||||
 | 
			
		||||
        # Now update the entries with info from the CBFS entries
 | 
			
		||||
        for entry in self._cbfs_entries.values():
 | 
			
		||||
            cfile = entry._cbfs_file
 | 
			
		||||
            entry.size = cfile.data_len
 | 
			
		||||
            entry.offset = cfile.calced_cbfs_offset
 | 
			
		||||
            entry.image_pos = self.image_pos + entry.offset
 | 
			
		||||
            if entry._cbfs_compress:
 | 
			
		||||
                entry.uncomp_size = cfile.memlen
 | 
			
		||||
 | 
			
		||||
    def AddMissingProperties(self):
 | 
			
		||||
        Entry.AddMissingProperties(self)
 | 
			
		||||
        for entry in self._cbfs_entries.values():
 | 
			
		||||
            entry.AddMissingProperties()
 | 
			
		||||
            if entry._cbfs_compress:
 | 
			
		||||
                state.AddZeroProp(entry._node, 'uncomp-size')
 | 
			
		||||
                # Store the 'compress' property, since we don't look at
 | 
			
		||||
                # 'cbfs-compress' in Entry.ReadData()
 | 
			
		||||
                state.AddString(entry._node, 'compress',
 | 
			
		||||
                                cbfs_util.compress_name(entry._cbfs_compress))
 | 
			
		||||
 | 
			
		||||
    def SetCalculatedProperties(self):
 | 
			
		||||
        """Set the value of device-tree properties calculated by binman"""
 | 
			
		||||
        Entry.SetCalculatedProperties(self)
 | 
			
		||||
        for entry in self._cbfs_entries.values():
 | 
			
		||||
            state.SetInt(entry._node, 'offset', entry.offset)
 | 
			
		||||
            state.SetInt(entry._node, 'size', entry.size)
 | 
			
		||||
            state.SetInt(entry._node, 'image-pos', entry.image_pos)
 | 
			
		||||
            if entry.uncomp_size is not None:
 | 
			
		||||
                state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size)
 | 
			
		||||
 | 
			
		||||
    def ListEntries(self, entries, indent):
 | 
			
		||||
        """Override this method to list all files in the section"""
 | 
			
		||||
        Entry.ListEntries(self, entries, indent)
 | 
			
		||||
        for entry in self._cbfs_entries.values():
 | 
			
		||||
            entry.ListEntries(entries, indent + 1)
 | 
			
		||||
 | 
			
		||||
    def GetEntries(self):
 | 
			
		||||
        return self._cbfs_entries
 | 
			
		||||
							
								
								
									
										130
									
								
								tools/binman/etype/fdtmap.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								tools/binman/etype/fdtmap.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,130 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright (c) 2018 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
 | 
			
		||||
"""# Entry-type module for a full map of the firmware image
 | 
			
		||||
 | 
			
		||||
This handles putting an FDT into the image with just the information about the
 | 
			
		||||
image.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import libfdt
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
from fdt import Fdt
 | 
			
		||||
import state
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
FDTMAP_MAGIC   = b'_FDTMAP_'
 | 
			
		||||
FDTMAP_HDR_LEN = 16
 | 
			
		||||
 | 
			
		||||
def LocateFdtmap(data):
 | 
			
		||||
    """Search an image for an fdt map
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        data: Data to search
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Position of fdt map in data, or None if not found. Note that the
 | 
			
		||||
            position returned is of the FDT header, i.e. before the FDT data
 | 
			
		||||
    """
 | 
			
		||||
    hdr_pos = data.find(FDTMAP_MAGIC)
 | 
			
		||||
    size = len(data)
 | 
			
		||||
    if hdr_pos != -1:
 | 
			
		||||
        hdr = data[hdr_pos:hdr_pos + FDTMAP_HDR_LEN]
 | 
			
		||||
        if len(hdr) == FDTMAP_HDR_LEN:
 | 
			
		||||
            return hdr_pos
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
class Entry_fdtmap(Entry):
 | 
			
		||||
    """An entry which contains an FDT map
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments:
 | 
			
		||||
        None
 | 
			
		||||
 | 
			
		||||
    An FDT map is just a header followed by an FDT containing a list of all the
 | 
			
		||||
    entries in the image. The root node corresponds to the image node in the
 | 
			
		||||
    original FDT, and an image-name property indicates the image name in that
 | 
			
		||||
    original tree.
 | 
			
		||||
 | 
			
		||||
    The header is the string _FDTMAP_ followed by 8 unused bytes.
 | 
			
		||||
 | 
			
		||||
    When used, this entry will be populated with an FDT map which reflects the
 | 
			
		||||
    entries in the current image. Hierarchy is preserved, and all offsets and
 | 
			
		||||
    sizes are included.
 | 
			
		||||
 | 
			
		||||
    Note that the -u option must be provided to ensure that binman updates the
 | 
			
		||||
    FDT with the position of each entry.
 | 
			
		||||
 | 
			
		||||
    Example output for a simple image with U-Boot and an FDT map:
 | 
			
		||||
 | 
			
		||||
    / {
 | 
			
		||||
        size = <0x00000112>;
 | 
			
		||||
        image-pos = <0x00000000>;
 | 
			
		||||
        offset = <0x00000000>;
 | 
			
		||||
        u-boot {
 | 
			
		||||
            size = <0x00000004>;
 | 
			
		||||
            image-pos = <0x00000000>;
 | 
			
		||||
            offset = <0x00000000>;
 | 
			
		||||
        };
 | 
			
		||||
        fdtmap {
 | 
			
		||||
            size = <0x0000010e>;
 | 
			
		||||
            image-pos = <0x00000004>;
 | 
			
		||||
            offset = <0x00000004>;
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
 | 
			
		||||
    def _GetFdtmap(self):
 | 
			
		||||
        """Build an FDT map from the entries in the current image
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            FDT map binary data
 | 
			
		||||
        """
 | 
			
		||||
        def _AddNode(node):
 | 
			
		||||
            """Add a node to the FDT map"""
 | 
			
		||||
            for pname, prop in node.props.items():
 | 
			
		||||
                fsw.property(pname, prop.bytes)
 | 
			
		||||
            for subnode in node.subnodes:
 | 
			
		||||
                with fsw.add_node(subnode.name):
 | 
			
		||||
                    _AddNode(subnode)
 | 
			
		||||
 | 
			
		||||
        # Get the FDT data into an Fdt object
 | 
			
		||||
        data = state.GetFdtContents()[1]
 | 
			
		||||
        infdt = Fdt.FromData(data)
 | 
			
		||||
        infdt.Scan()
 | 
			
		||||
 | 
			
		||||
        # Find the node for the image containing the Fdt-map entry
 | 
			
		||||
        path = self.section.GetPath()
 | 
			
		||||
        node = infdt.GetNode(path)
 | 
			
		||||
        if not node:
 | 
			
		||||
            self.Raise("Internal error: Cannot locate node for path '%s'" %
 | 
			
		||||
                       path)
 | 
			
		||||
 | 
			
		||||
        # Build a new tree with all nodes and properties starting from that node
 | 
			
		||||
        fsw = libfdt.FdtSw()
 | 
			
		||||
        fsw.finish_reservemap()
 | 
			
		||||
        with fsw.add_node(''):
 | 
			
		||||
            _AddNode(node)
 | 
			
		||||
        fdt = fsw.as_fdt()
 | 
			
		||||
 | 
			
		||||
        # Pack this new FDT and return its contents
 | 
			
		||||
        fdt.pack()
 | 
			
		||||
        outfdt = Fdt.FromData(fdt.as_bytearray())
 | 
			
		||||
        data = FDTMAP_MAGIC + tools.GetBytes(0, 8) + outfdt.GetContents()
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        """Obtain a placeholder for the fdt-map contents"""
 | 
			
		||||
        self.SetContents(self._GetFdtmap())
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        """Write an updated version of the FDT map to this entry
 | 
			
		||||
 | 
			
		||||
        This is necessary since new data may have been written back to it during
 | 
			
		||||
        processing, e.g. the image-pos properties.
 | 
			
		||||
        """
 | 
			
		||||
        return self.ProcessContentsUpdate(self._GetFdtmap())
 | 
			
		||||
@ -14,7 +14,6 @@ import fdt_util
 | 
			
		||||
import state
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
import bsection
 | 
			
		||||
 | 
			
		||||
class Entry_files(Entry_section):
 | 
			
		||||
    """Entry containing a set of files
 | 
			
		||||
@ -54,4 +53,4 @@ class Entry_files(Entry_section):
 | 
			
		||||
            state.AddString(subnode, 'compress', self._compress)
 | 
			
		||||
 | 
			
		||||
        # Read entries again, now that we have some
 | 
			
		||||
        self._section._ReadEntries()
 | 
			
		||||
        self._ReadEntries()
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ class Entry_fmap(Entry):
 | 
			
		||||
                areas.append(fmap_util.FmapArea(pos or 0, entry.size or 0,
 | 
			
		||||
                                            tools.FromUnicode(entry.name), 0))
 | 
			
		||||
 | 
			
		||||
        entries = self.section._image.GetEntries()
 | 
			
		||||
        entries = self.section.image.GetEntries()
 | 
			
		||||
        areas = []
 | 
			
		||||
        for entry in entries.values():
 | 
			
		||||
            _AddEntries(areas, entry)
 | 
			
		||||
@ -62,4 +62,4 @@ class Entry_fmap(Entry):
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        self.SetContents(self._GetFmap())
 | 
			
		||||
        return self.ProcessContentsUpdate(self._GetFmap())
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										99
									
								
								tools/binman/etype/image_header.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								tools/binman/etype/image_header.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright (c) 2018 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
 | 
			
		||||
"""Entry-type module for an image header which points to the FDT map
 | 
			
		||||
 | 
			
		||||
This creates an 8-byte entry with a magic number and the offset of the FDT map
 | 
			
		||||
(which is another entry in the image), relative to the start or end of the
 | 
			
		||||
image.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import struct
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
import fdt_util
 | 
			
		||||
 | 
			
		||||
IMAGE_HEADER_MAGIC = b'BinM'
 | 
			
		||||
IMAGE_HEADER_LEN   = 8
 | 
			
		||||
 | 
			
		||||
def LocateHeaderOffset(data):
 | 
			
		||||
    """Search an image for an image header
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        data: Data to search
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Offset of image header in the image, or None if not found
 | 
			
		||||
    """
 | 
			
		||||
    hdr_pos = data.find(IMAGE_HEADER_MAGIC)
 | 
			
		||||
    if hdr_pos != -1:
 | 
			
		||||
        size = len(data)
 | 
			
		||||
        hdr = data[hdr_pos:hdr_pos + IMAGE_HEADER_LEN]
 | 
			
		||||
        if len(hdr) == IMAGE_HEADER_LEN:
 | 
			
		||||
            offset = struct.unpack('<I', hdr[4:])[0]
 | 
			
		||||
            if hdr_pos == len(data) - IMAGE_HEADER_LEN:
 | 
			
		||||
                pos = size + offset - (1 << 32)
 | 
			
		||||
            else:
 | 
			
		||||
                pos = offset
 | 
			
		||||
            return pos
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
class Entry_image_header(Entry):
 | 
			
		||||
    """An entry which contains a pointer to the FDT map
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments:
 | 
			
		||||
        location: Location of header ("start" or "end" of image). This is
 | 
			
		||||
            optional. If omitted then the entry must have an offset property.
 | 
			
		||||
 | 
			
		||||
    This adds an 8-byte entry to the start or end of the image, pointing to the
 | 
			
		||||
    location of the FDT map. The format is a magic number followed by an offset
 | 
			
		||||
    from the start or end of the image, in twos-compliment format.
 | 
			
		||||
 | 
			
		||||
    This entry must be in the top-level part of the image.
 | 
			
		||||
 | 
			
		||||
    NOTE: If the location is at the start/end, you will probably need to specify
 | 
			
		||||
    sort-by-offset for the image, unless you actually put the image header
 | 
			
		||||
    first/last in the entry list.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
        self.location = fdt_util.GetString(self._node, 'location')
 | 
			
		||||
 | 
			
		||||
    def _GetHeader(self):
 | 
			
		||||
        image_pos = self.GetSiblingImagePos('fdtmap')
 | 
			
		||||
        if image_pos == False:
 | 
			
		||||
            self.Raise("'image_header' section must have an 'fdtmap' sibling")
 | 
			
		||||
        elif image_pos is None:
 | 
			
		||||
            # This will be available when called from ProcessContents(), but not
 | 
			
		||||
            # when called from ObtainContents()
 | 
			
		||||
            offset = 0xffffffff
 | 
			
		||||
        else:
 | 
			
		||||
            image_size = self.section.GetImageSize() or 0
 | 
			
		||||
            base = (0 if self.location != 'end' else image_size)
 | 
			
		||||
            offset = (image_pos - base) & 0xffffffff
 | 
			
		||||
        data = IMAGE_HEADER_MAGIC + struct.pack('<I', offset)
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        """Obtain a placeholder for the header contents"""
 | 
			
		||||
        self.SetContents(self._GetHeader())
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def Pack(self, offset):
 | 
			
		||||
        """Special pack method to set the offset to start/end of image"""
 | 
			
		||||
        if not self.offset:
 | 
			
		||||
            if self.location not in ['start', 'end']:
 | 
			
		||||
                self.Raise("Invalid location '%s', expected 'start' or 'end'" %
 | 
			
		||||
                           self.location)
 | 
			
		||||
            image_size = self.section.GetImageSize() or 0
 | 
			
		||||
            self.offset = (0 if self.location != 'end' else image_size - 8)
 | 
			
		||||
        return Entry.Pack(self, offset)
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        """Write an updated version of the FDT map to this entry
 | 
			
		||||
 | 
			
		||||
        This is necessary since image_pos is not available when ObtainContents()
 | 
			
		||||
        is called, since by then the entries have not been packed in the image.
 | 
			
		||||
        """
 | 
			
		||||
        return self.ProcessContentsUpdate(self._GetHeader())
 | 
			
		||||
@ -47,17 +47,25 @@ class Entry_intel_descriptor(Entry_blob):
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry_blob.__init__(self, section, etype, node)
 | 
			
		||||
        self._regions = []
 | 
			
		||||
        if self.offset is None:
 | 
			
		||||
            self.offset = self.section.GetStartOffset()
 | 
			
		||||
 | 
			
		||||
    def GetOffsets(self):
 | 
			
		||||
        offset = self.data.find(FD_SIGNATURE)
 | 
			
		||||
        if offset == -1:
 | 
			
		||||
            self.Raise('Cannot find FD signature')
 | 
			
		||||
            self.Raise('Cannot find Intel Flash Descriptor (FD) signature')
 | 
			
		||||
        flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL',
 | 
			
		||||
                                                self.data[offset:offset + 16])
 | 
			
		||||
        frba = ((flmap0 >> 16) & 0xff) << 4
 | 
			
		||||
        for i in range(MAX_REGIONS):
 | 
			
		||||
            self._regions.append(Region(self.data, frba, i))
 | 
			
		||||
 | 
			
		||||
        # Set the offset for ME only, for now, since the others are not used
 | 
			
		||||
        return {'intel-me': [self._regions[REGION_ME].base,
 | 
			
		||||
                             self._regions[REGION_ME].size]}
 | 
			
		||||
        # Set the offset for ME (Management Engine) and IFWI (Integrated
 | 
			
		||||
        # Firmware Image), for now, since the others are not used.
 | 
			
		||||
        info = {}
 | 
			
		||||
        if self.HasSibling('intel-me'):
 | 
			
		||||
            info['intel-me'] = [self._regions[REGION_ME].base,
 | 
			
		||||
                                self._regions[REGION_ME].size]
 | 
			
		||||
        if self.HasSibling('intel-ifwi'):
 | 
			
		||||
            info['intel-ifwi'] = [self._regions[REGION_BIOS].base, None]
 | 
			
		||||
        return info
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										100
									
								
								tools/binman/etype/intel_ifwi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								tools/binman/etype/intel_ifwi.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright (c) 2016 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
#
 | 
			
		||||
# Entry-type module for Intel Management Engine binary blob
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
from blob import Entry_blob
 | 
			
		||||
import fdt_util
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
class Entry_intel_ifwi(Entry_blob):
 | 
			
		||||
    """Entry containing an Intel Integrated Firmware Image (IFWI) file
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments:
 | 
			
		||||
        - filename: Filename of file to read into entry. This is either the
 | 
			
		||||
            IFWI file itself, or a file that can be converted into one using a
 | 
			
		||||
            tool
 | 
			
		||||
        - convert-fit: If present this indicates that the ifwitool should be
 | 
			
		||||
            used to convert the provided file into a IFWI.
 | 
			
		||||
 | 
			
		||||
    This file contains code and data used by the SoC that is required to make
 | 
			
		||||
    it work. It includes U-Boot TPL, microcode, things related to the CSE
 | 
			
		||||
    (Converged Security Engine, the microcontroller that loads all the firmware)
 | 
			
		||||
    and other items beyond the wit of man.
 | 
			
		||||
 | 
			
		||||
    A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a
 | 
			
		||||
    file that will be converted to an IFWI.
 | 
			
		||||
 | 
			
		||||
    The position of this entry is generally set by the intel-descriptor entry.
 | 
			
		||||
 | 
			
		||||
    The contents of the IFWI are specified by the subnodes of the IFWI node.
 | 
			
		||||
    Each subnode describes an entry which is placed into the IFWFI with a given
 | 
			
		||||
    sub-partition (and optional entry name).
 | 
			
		||||
 | 
			
		||||
    See README.x86 for information about x86 binary blobs.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry_blob.__init__(self, section, etype, node)
 | 
			
		||||
        self._convert_fit = fdt_util.GetBool(self._node, 'convert-fit')
 | 
			
		||||
        self._ifwi_entries = OrderedDict()
 | 
			
		||||
        self._ReadSubnodes()
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        """Get the contects for the IFWI
 | 
			
		||||
 | 
			
		||||
        Unfortunately we cannot create anything from scratch here, as Intel has
 | 
			
		||||
        tools which create precursor binaries with lots of data and settings,
 | 
			
		||||
        and these are not incorporated into binman.
 | 
			
		||||
 | 
			
		||||
        The first step is to get a file in the IFWI format. This is either
 | 
			
		||||
        supplied directly or is extracted from a fitimage using the 'create'
 | 
			
		||||
        subcommand.
 | 
			
		||||
 | 
			
		||||
        After that we delete the OBBP sub-partition and add each of the files
 | 
			
		||||
        that we want in the IFWI file, one for each sub-entry of the IWFI node.
 | 
			
		||||
        """
 | 
			
		||||
        self._pathname = tools.GetInputFilename(self._filename)
 | 
			
		||||
 | 
			
		||||
        # Create the IFWI file if needed
 | 
			
		||||
        if self._convert_fit:
 | 
			
		||||
            inname = self._pathname
 | 
			
		||||
            outname = tools.GetOutputFilename('ifwi.bin')
 | 
			
		||||
            tools.RunIfwiTool(inname, tools.CMD_CREATE, outname)
 | 
			
		||||
            self._filename = 'ifwi.bin'
 | 
			
		||||
            self._pathname = outname
 | 
			
		||||
        else:
 | 
			
		||||
            # Provide a different code path here to ensure we have test coverage
 | 
			
		||||
            inname = self._pathname
 | 
			
		||||
 | 
			
		||||
        # Delete OBBP if it is there, then add the required new items.
 | 
			
		||||
        tools.RunIfwiTool(inname, tools.CMD_DELETE, subpart='OBBP')
 | 
			
		||||
 | 
			
		||||
        for entry in self._ifwi_entries.values():
 | 
			
		||||
            # First get the input data and put it in a file
 | 
			
		||||
            if not entry.ObtainContents():
 | 
			
		||||
                return False
 | 
			
		||||
            data = entry.GetData()
 | 
			
		||||
            uniq = self.GetUniqueName()
 | 
			
		||||
            input_fname = tools.GetOutputFilename('input.%s' % uniq)
 | 
			
		||||
            tools.WriteFile(input_fname, data)
 | 
			
		||||
 | 
			
		||||
            tools.RunIfwiTool(inname,
 | 
			
		||||
                tools.CMD_REPLACE if entry._ifwi_replace else tools.CMD_ADD,
 | 
			
		||||
                input_fname, entry._ifwi_subpart, entry._ifwi_entry_name)
 | 
			
		||||
 | 
			
		||||
        self.ReadBlobContents()
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _ReadSubnodes(self):
 | 
			
		||||
        """Read the subnodes to find out what should go in this IFWI"""
 | 
			
		||||
        for node in self._node.subnodes:
 | 
			
		||||
            entry = Entry.Create(self.section, node)
 | 
			
		||||
            entry._ifwi_replace = fdt_util.GetBool(node, 'replace')
 | 
			
		||||
            entry._ifwi_subpart = fdt_util.GetString(node, 'ifwi-subpart')
 | 
			
		||||
            entry._ifwi_entry_name = fdt_util.GetString(node, 'ifwi-entry')
 | 
			
		||||
            self._ifwi_entries[entry._ifwi_subpart] = entry
 | 
			
		||||
@ -22,6 +22,8 @@ class Entry_intel_me(Entry_blob):
 | 
			
		||||
 | 
			
		||||
    A typical filename is 'me.bin'.
 | 
			
		||||
 | 
			
		||||
    The position of this entry is generally set by the intel-descriptor entry.
 | 
			
		||||
 | 
			
		||||
    See README.x86 for information about x86 binary blobs.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
 | 
			
		||||
@ -1,59 +1,155 @@
 | 
			
		||||
# SPDX-License-Identifier:      GPL-2.0+
 | 
			
		||||
# Copyright (c) 2018 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
#
 | 
			
		||||
# Entry-type module for sections, which are entries which can contain other
 | 
			
		||||
# entries.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
"""Entry-type module for sections (groups of entries)
 | 
			
		||||
 | 
			
		||||
Sections are entries which can contain other entries. This allows hierarchical
 | 
			
		||||
images to be created.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
import fdt_util
 | 
			
		||||
import tools
 | 
			
		||||
 | 
			
		||||
import bsection
 | 
			
		||||
 | 
			
		||||
class Entry_section(Entry):
 | 
			
		||||
    """Entry that contains other entries
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments: (see binman README for more information)
 | 
			
		||||
        - size: Size of section in bytes
 | 
			
		||||
        - align-size: Align size to a particular power of two
 | 
			
		||||
        - pad-before: Add padding before the entry
 | 
			
		||||
        - pad-after: Add padding after the entry
 | 
			
		||||
        - pad-byte: Pad byte to use when padding
 | 
			
		||||
        - sort-by-offset: Reorder the entries by offset
 | 
			
		||||
        - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
 | 
			
		||||
        - name-prefix: Adds a prefix to the name of every entry in the section
 | 
			
		||||
        pad-byte: Pad byte to use when padding
 | 
			
		||||
        sort-by-offset: True if entries should be sorted by offset, False if
 | 
			
		||||
            they must be in-order in the device tree description
 | 
			
		||||
        end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
 | 
			
		||||
        skip-at-start: Number of bytes before the first entry starts. These
 | 
			
		||||
            effectively adjust the starting offset of entries. For example,
 | 
			
		||||
            if this is 16, then the first entry would start at 16. An entry
 | 
			
		||||
            with offset = 20 would in fact be written at offset 4 in the image
 | 
			
		||||
            file, since the first 16 bytes are skipped when writing.
 | 
			
		||||
        name-prefix: Adds a prefix to the name of every entry in the section
 | 
			
		||||
            when writing out the map
 | 
			
		||||
 | 
			
		||||
    Since a section is also an entry, it inherits all the properies of entries
 | 
			
		||||
    too.
 | 
			
		||||
 | 
			
		||||
    A section is an entry which can contain other entries, thus allowing
 | 
			
		||||
    hierarchical images to be created. See 'Sections and hierarchical images'
 | 
			
		||||
    in the binman README for more information.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
        self._section = bsection.Section(node.name, section, node,
 | 
			
		||||
                                         section._image)
 | 
			
		||||
    def __init__(self, section, etype, node, test=False):
 | 
			
		||||
        if not test:
 | 
			
		||||
            Entry.__init__(self, section, etype, node)
 | 
			
		||||
        if section:
 | 
			
		||||
            self.image = section.image
 | 
			
		||||
        self._entries = OrderedDict()
 | 
			
		||||
        self._pad_byte = 0
 | 
			
		||||
        self._sort = False
 | 
			
		||||
        self._skip_at_start = None
 | 
			
		||||
        self._end_4gb = False
 | 
			
		||||
        if not test:
 | 
			
		||||
            self._ReadNode()
 | 
			
		||||
            self._ReadEntries()
 | 
			
		||||
 | 
			
		||||
    def _Raise(self, msg):
 | 
			
		||||
        """Raises an error for this section
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            msg: Error message to use in the raise string
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError()
 | 
			
		||||
        """
 | 
			
		||||
        raise ValueError("Section '%s': %s" % (self._node.path, msg))
 | 
			
		||||
 | 
			
		||||
    def _ReadNode(self):
 | 
			
		||||
        """Read properties from the image node"""
 | 
			
		||||
        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
 | 
			
		||||
        self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
 | 
			
		||||
        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
 | 
			
		||||
        self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start')
 | 
			
		||||
        if self._end_4gb:
 | 
			
		||||
            if not self.size:
 | 
			
		||||
                self.Raise("Section size must be provided when using end-at-4gb")
 | 
			
		||||
            if self._skip_at_start is not None:
 | 
			
		||||
                self.Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
 | 
			
		||||
            else:
 | 
			
		||||
                self._skip_at_start = 0x100000000 - self.size
 | 
			
		||||
        else:
 | 
			
		||||
            if self._skip_at_start is None:
 | 
			
		||||
                self._skip_at_start = 0
 | 
			
		||||
        self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
 | 
			
		||||
        filename = fdt_util.GetString(self._node, 'filename')
 | 
			
		||||
        if filename:
 | 
			
		||||
            self._filename = filename
 | 
			
		||||
 | 
			
		||||
    def _ReadEntries(self):
 | 
			
		||||
        for node in self._node.subnodes:
 | 
			
		||||
            if node.name == 'hash':
 | 
			
		||||
                continue
 | 
			
		||||
            entry = Entry.Create(self, node)
 | 
			
		||||
            entry.SetPrefix(self._name_prefix)
 | 
			
		||||
            self._entries[node.name] = entry
 | 
			
		||||
 | 
			
		||||
    def GetFdtSet(self):
 | 
			
		||||
        return self._section.GetFdtSet()
 | 
			
		||||
        fdt_set = set()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            fdt_set.update(entry.GetFdtSet())
 | 
			
		||||
        return fdt_set
 | 
			
		||||
 | 
			
		||||
    def ProcessFdt(self, fdt):
 | 
			
		||||
        return self._section.ProcessFdt(fdt)
 | 
			
		||||
        """Allow entries to adjust the device tree
 | 
			
		||||
 | 
			
		||||
        Some entries need to adjust the device tree for their purposes. This
 | 
			
		||||
        may involve adding or deleting properties.
 | 
			
		||||
        """
 | 
			
		||||
        todo = self._entries.values()
 | 
			
		||||
        for passnum in range(3):
 | 
			
		||||
            next_todo = []
 | 
			
		||||
            for entry in todo:
 | 
			
		||||
                if not entry.ProcessFdt(fdt):
 | 
			
		||||
                    next_todo.append(entry)
 | 
			
		||||
            todo = next_todo
 | 
			
		||||
            if not todo:
 | 
			
		||||
                break
 | 
			
		||||
        if todo:
 | 
			
		||||
            self.Raise('Internal error: Could not complete processing of Fdt: remaining %s' %
 | 
			
		||||
                       todo)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def ExpandEntries(self):
 | 
			
		||||
        """Expand out any entries which have calculated sub-entries
 | 
			
		||||
 | 
			
		||||
        Some entries are expanded out at runtime, e.g. 'files', which produces
 | 
			
		||||
        a section containing a list of files. Process these entries so that
 | 
			
		||||
        this information is added to the device tree.
 | 
			
		||||
        """
 | 
			
		||||
        Entry.ExpandEntries(self)
 | 
			
		||||
        self._section.ExpandEntries()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.ExpandEntries()
 | 
			
		||||
 | 
			
		||||
    def AddMissingProperties(self):
 | 
			
		||||
        """Add new properties to the device tree as needed for this entry"""
 | 
			
		||||
        Entry.AddMissingProperties(self)
 | 
			
		||||
        self._section.AddMissingProperties()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.AddMissingProperties()
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
        return self._section.GetEntryContents()
 | 
			
		||||
        return self.GetEntryContents()
 | 
			
		||||
 | 
			
		||||
    def GetData(self):
 | 
			
		||||
        return self._section.GetData()
 | 
			
		||||
        section_data = tools.GetBytes(self._pad_byte, self.size)
 | 
			
		||||
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            data = entry.GetData()
 | 
			
		||||
            base = self.pad_before + entry.offset - self._skip_at_start
 | 
			
		||||
            section_data = (section_data[:base] + data +
 | 
			
		||||
                            section_data[base + len(data):])
 | 
			
		||||
        return section_data
 | 
			
		||||
 | 
			
		||||
    def GetOffsets(self):
 | 
			
		||||
        """Handle entries that want to set the offset/size of other entries
 | 
			
		||||
@ -61,35 +157,94 @@ class Entry_section(Entry):
 | 
			
		||||
        This calls each entry's GetOffsets() method. If it returns a list
 | 
			
		||||
        of entries to update, it updates them.
 | 
			
		||||
        """
 | 
			
		||||
        self._section.GetEntryOffsets()
 | 
			
		||||
        self.GetEntryOffsets()
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    def ResetForPack(self):
 | 
			
		||||
        """Reset offset/size fields so that packing can be done again"""
 | 
			
		||||
        Entry.ResetForPack(self)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.ResetForPack()
 | 
			
		||||
 | 
			
		||||
    def Pack(self, offset):
 | 
			
		||||
        """Pack all entries into the section"""
 | 
			
		||||
        self._section.PackEntries()
 | 
			
		||||
        if self._section._offset is None:
 | 
			
		||||
            self._section.SetOffset(offset)
 | 
			
		||||
        self.size = self._section.GetSize()
 | 
			
		||||
        return super(Entry_section, self).Pack(offset)
 | 
			
		||||
        self._PackEntries()
 | 
			
		||||
        return Entry.Pack(self, offset)
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self, image_pos):
 | 
			
		||||
        Entry.SetImagePos(self, image_pos)
 | 
			
		||||
        self._section.SetImagePos(image_pos + self.offset)
 | 
			
		||||
    def _PackEntries(self):
 | 
			
		||||
        """Pack all entries into the image"""
 | 
			
		||||
        offset = self._skip_at_start
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            offset = entry.Pack(offset)
 | 
			
		||||
        self.size = self.CheckSize()
 | 
			
		||||
 | 
			
		||||
    def _ExpandEntries(self):
 | 
			
		||||
        """Expand any entries that are permitted to"""
 | 
			
		||||
        exp_entry = None
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if exp_entry:
 | 
			
		||||
                exp_entry.ExpandToLimit(entry.offset)
 | 
			
		||||
                exp_entry = None
 | 
			
		||||
            if entry.expand_size:
 | 
			
		||||
                exp_entry = entry
 | 
			
		||||
        if exp_entry:
 | 
			
		||||
            exp_entry.ExpandToLimit(self.size)
 | 
			
		||||
 | 
			
		||||
    def _SortEntries(self):
 | 
			
		||||
        """Sort entries by offset"""
 | 
			
		||||
        entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
 | 
			
		||||
        self._entries.clear()
 | 
			
		||||
        for entry in entries:
 | 
			
		||||
            self._entries[entry._node.name] = entry
 | 
			
		||||
 | 
			
		||||
    def CheckEntries(self):
 | 
			
		||||
        """Check that entries do not overlap or extend outside the image"""
 | 
			
		||||
        if self._sort:
 | 
			
		||||
            self._SortEntries()
 | 
			
		||||
        self._ExpandEntries()
 | 
			
		||||
        offset = 0
 | 
			
		||||
        prev_name = 'None'
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.CheckOffset()
 | 
			
		||||
            if (entry.offset < self._skip_at_start or
 | 
			
		||||
                    entry.offset + entry.size > self._skip_at_start +
 | 
			
		||||
                    self.size):
 | 
			
		||||
                entry.Raise("Offset %#x (%d) is outside the section starting "
 | 
			
		||||
                            "at %#x (%d)" %
 | 
			
		||||
                            (entry.offset, entry.offset, self._skip_at_start,
 | 
			
		||||
                             self._skip_at_start))
 | 
			
		||||
            if entry.offset < offset:
 | 
			
		||||
                entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
 | 
			
		||||
                            "ending at %#x (%d)" %
 | 
			
		||||
                            (entry.offset, entry.offset, prev_name, offset, offset))
 | 
			
		||||
            offset = entry.offset + entry.size
 | 
			
		||||
            prev_name = entry.GetPath()
 | 
			
		||||
 | 
			
		||||
    def WriteSymbols(self, section):
 | 
			
		||||
        """Write symbol values into binary files for access at run time"""
 | 
			
		||||
        self._section.WriteSymbols()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.WriteSymbols(self)
 | 
			
		||||
 | 
			
		||||
    def SetCalculatedProperties(self):
 | 
			
		||||
        Entry.SetCalculatedProperties(self)
 | 
			
		||||
        self._section.SetCalculatedProperties()
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.SetCalculatedProperties()
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self, image_pos):
 | 
			
		||||
        Entry.SetImagePos(self, image_pos)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.SetImagePos(image_pos + self.offset)
 | 
			
		||||
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        self._section.ProcessEntryContents()
 | 
			
		||||
        super(Entry_section, self).ProcessContents()
 | 
			
		||||
        sizes_ok_base = super(Entry_section, self).ProcessContents()
 | 
			
		||||
        sizes_ok = True
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if not entry.ProcessContents():
 | 
			
		||||
                sizes_ok = False
 | 
			
		||||
        return sizes_ok and sizes_ok_base
 | 
			
		||||
 | 
			
		||||
    def CheckOffset(self):
 | 
			
		||||
        self._section.CheckEntries()
 | 
			
		||||
        self.CheckEntries()
 | 
			
		||||
 | 
			
		||||
    def WriteMap(self, fd, indent):
 | 
			
		||||
        """Write a map of the section to a .map file
 | 
			
		||||
@ -97,11 +252,211 @@ class Entry_section(Entry):
 | 
			
		||||
        Args:
 | 
			
		||||
            fd: File to write the map to
 | 
			
		||||
        """
 | 
			
		||||
        self._section.WriteMap(fd, indent)
 | 
			
		||||
        Entry.WriteMapLine(fd, indent, self.name, self.offset or 0,
 | 
			
		||||
                           self.size, self.image_pos)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.WriteMap(fd, indent + 1)
 | 
			
		||||
 | 
			
		||||
    def GetEntries(self):
 | 
			
		||||
        return self._section.GetEntries()
 | 
			
		||||
        return self._entries
 | 
			
		||||
 | 
			
		||||
    def ExpandToLimit(self, limit):
 | 
			
		||||
        super(Entry_section, self).ExpandToLimit(limit)
 | 
			
		||||
        self._section.ExpandSize(self.size)
 | 
			
		||||
    def GetContentsByPhandle(self, phandle, source_entry):
 | 
			
		||||
        """Get the data contents of an entry specified by a phandle
 | 
			
		||||
 | 
			
		||||
        This uses a phandle to look up a node and and find the entry
 | 
			
		||||
        associated with it. Then it returnst he contents of that entry.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            phandle: Phandle to look up (integer)
 | 
			
		||||
            source_entry: Entry containing that phandle (used for error
 | 
			
		||||
                reporting)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            data from associated entry (as a string), or None if not found
 | 
			
		||||
        """
 | 
			
		||||
        node = self._node.GetFdt().LookupPhandle(phandle)
 | 
			
		||||
        if not node:
 | 
			
		||||
            source_entry.Raise("Cannot find node for phandle %d" % phandle)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if entry._node == node:
 | 
			
		||||
                return entry.GetData()
 | 
			
		||||
        source_entry.Raise("Cannot find entry for node '%s'" % node.name)
 | 
			
		||||
 | 
			
		||||
    def LookupSymbol(self, sym_name, optional, msg):
 | 
			
		||||
        """Look up a symbol in an ELF file
 | 
			
		||||
 | 
			
		||||
        Looks up a symbol in an ELF file. Only entry types which come from an
 | 
			
		||||
        ELF image can be used by this function.
 | 
			
		||||
 | 
			
		||||
        At present the only entry property supported is offset.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            sym_name: Symbol name in the ELF file to look up in the format
 | 
			
		||||
                _binman_<entry>_prop_<property> where <entry> is the name of
 | 
			
		||||
                the entry and <property> is the property to find (e.g.
 | 
			
		||||
                _binman_u_boot_prop_offset). As a special case, you can append
 | 
			
		||||
                _any to <entry> to have it search for any matching entry. E.g.
 | 
			
		||||
                _binman_u_boot_any_prop_offset will match entries called u-boot,
 | 
			
		||||
                u-boot-img and u-boot-nodtb)
 | 
			
		||||
            optional: True if the symbol is optional. If False this function
 | 
			
		||||
                will raise if the symbol is not found
 | 
			
		||||
            msg: Message to display if an error occurs
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Value that should be assigned to that symbol, or None if it was
 | 
			
		||||
                optional and not found
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if the symbol is invalid or not found, or references a
 | 
			
		||||
                property which is not supported
 | 
			
		||||
        """
 | 
			
		||||
        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
 | 
			
		||||
        if not m:
 | 
			
		||||
            raise ValueError("%s: Symbol '%s' has invalid format" %
 | 
			
		||||
                             (msg, sym_name))
 | 
			
		||||
        entry_name, prop_name = m.groups()
 | 
			
		||||
        entry_name = entry_name.replace('_', '-')
 | 
			
		||||
        entry = self._entries.get(entry_name)
 | 
			
		||||
        if not entry:
 | 
			
		||||
            if entry_name.endswith('-any'):
 | 
			
		||||
                root = entry_name[:-4]
 | 
			
		||||
                for name in self._entries:
 | 
			
		||||
                    if name.startswith(root):
 | 
			
		||||
                        rest = name[len(root):]
 | 
			
		||||
                        if rest in ['', '-img', '-nodtb']:
 | 
			
		||||
                            entry = self._entries[name]
 | 
			
		||||
        if not entry:
 | 
			
		||||
            err = ("%s: Entry '%s' not found in list (%s)" %
 | 
			
		||||
                   (msg, entry_name, ','.join(self._entries.keys())))
 | 
			
		||||
            if optional:
 | 
			
		||||
                print('Warning: %s' % err, file=sys.stderr)
 | 
			
		||||
                return None
 | 
			
		||||
            raise ValueError(err)
 | 
			
		||||
        if prop_name == 'offset':
 | 
			
		||||
            return entry.offset
 | 
			
		||||
        elif prop_name == 'image_pos':
 | 
			
		||||
            return entry.image_pos
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
 | 
			
		||||
 | 
			
		||||
    def GetRootSkipAtStart(self):
 | 
			
		||||
        """Get the skip-at-start value for the top-level section
 | 
			
		||||
 | 
			
		||||
        This is used to find out the starting offset for root section that
 | 
			
		||||
        contains this section. If this is a top-level section then it returns
 | 
			
		||||
        the skip-at-start offset for this section.
 | 
			
		||||
 | 
			
		||||
        This is used to get the absolute position of section within the image.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Integer skip-at-start value for the root section containing this
 | 
			
		||||
                section
 | 
			
		||||
        """
 | 
			
		||||
        if self.section:
 | 
			
		||||
            return self.section.GetRootSkipAtStart()
 | 
			
		||||
        return self._skip_at_start
 | 
			
		||||
 | 
			
		||||
    def GetStartOffset(self):
 | 
			
		||||
        """Get the start offset for this section
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            The first available offset in this section (typically 0)
 | 
			
		||||
        """
 | 
			
		||||
        return self._skip_at_start
 | 
			
		||||
 | 
			
		||||
    def GetImageSize(self):
 | 
			
		||||
        """Get the size of the image containing this section
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Image size as an integer number of bytes, which may be None if the
 | 
			
		||||
                image size is dynamic and its sections have not yet been packed
 | 
			
		||||
        """
 | 
			
		||||
        return self.image.size
 | 
			
		||||
 | 
			
		||||
    def FindEntryType(self, etype):
 | 
			
		||||
        """Find an entry type in the section
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            etype: Entry type to find
 | 
			
		||||
        Returns:
 | 
			
		||||
            entry matching that type, or None if not found
 | 
			
		||||
        """
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if entry.etype == etype:
 | 
			
		||||
                return entry
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def GetEntryContents(self):
 | 
			
		||||
        """Call ObtainContents() for the section
 | 
			
		||||
        """
 | 
			
		||||
        todo = self._entries.values()
 | 
			
		||||
        for passnum in range(3):
 | 
			
		||||
            next_todo = []
 | 
			
		||||
            for entry in todo:
 | 
			
		||||
                if not entry.ObtainContents():
 | 
			
		||||
                    next_todo.append(entry)
 | 
			
		||||
            todo = next_todo
 | 
			
		||||
            if not todo:
 | 
			
		||||
                break
 | 
			
		||||
        if todo:
 | 
			
		||||
            self.Raise('Internal error: Could not complete processing of contents: remaining %s' %
 | 
			
		||||
                       todo)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _SetEntryOffsetSize(self, name, offset, size):
 | 
			
		||||
        """Set the offset and size of an entry
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            name: Entry name to update
 | 
			
		||||
            offset: New offset, or None to leave alone
 | 
			
		||||
            size: New size, or None to leave alone
 | 
			
		||||
        """
 | 
			
		||||
        entry = self._entries.get(name)
 | 
			
		||||
        if not entry:
 | 
			
		||||
            self._Raise("Unable to set offset/size for unknown entry '%s'" %
 | 
			
		||||
                        name)
 | 
			
		||||
        entry.SetOffsetSize(self._skip_at_start + offset if offset else None,
 | 
			
		||||
                            size)
 | 
			
		||||
 | 
			
		||||
    def GetEntryOffsets(self):
 | 
			
		||||
        """Handle entries that want to set the offset/size of other entries
 | 
			
		||||
 | 
			
		||||
        This calls each entry's GetOffsets() method. If it returns a list
 | 
			
		||||
        of entries to update, it updates them.
 | 
			
		||||
        """
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            offset_dict = entry.GetOffsets()
 | 
			
		||||
            for name, info in offset_dict.items():
 | 
			
		||||
                self._SetEntryOffsetSize(name, *info)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def CheckSize(self):
 | 
			
		||||
        """Check that the image contents does not exceed its size, etc."""
 | 
			
		||||
        contents_size = 0
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            contents_size = max(contents_size, entry.offset + entry.size)
 | 
			
		||||
 | 
			
		||||
        contents_size -= self._skip_at_start
 | 
			
		||||
 | 
			
		||||
        size = self.size
 | 
			
		||||
        if not size:
 | 
			
		||||
            size = self.pad_before + contents_size + self.pad_after
 | 
			
		||||
            size = tools.Align(size, self.align_size)
 | 
			
		||||
 | 
			
		||||
        if self.size and contents_size > self.size:
 | 
			
		||||
            self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
 | 
			
		||||
                        (contents_size, contents_size, self.size, self.size))
 | 
			
		||||
        if not self.size:
 | 
			
		||||
            self.size = size
 | 
			
		||||
        if self.size != tools.Align(self.size, self.align_size):
 | 
			
		||||
            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
 | 
			
		||||
                        (self.size, self.size, self.align_size,
 | 
			
		||||
                         self.align_size))
 | 
			
		||||
        return size
 | 
			
		||||
 | 
			
		||||
    def ListEntries(self, entries, indent):
 | 
			
		||||
        """List the files in the section"""
 | 
			
		||||
        Entry.AddEntryInfo(entries, indent, self.name, 'section', self.size,
 | 
			
		||||
                           self.image_pos, None, self.offset, self)
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            entry.ListEntries(entries, indent + 1)
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,8 @@ class Entry_text(Entry):
 | 
			
		||||
            that contains the string to place in the entry
 | 
			
		||||
        <xxx> (actual name is the value of text-label): contains the string to
 | 
			
		||||
            place in the entry.
 | 
			
		||||
        <text>: The text to place in the entry (overrides the above mechanism).
 | 
			
		||||
            This is useful when the text is constant.
 | 
			
		||||
 | 
			
		||||
    Example node:
 | 
			
		||||
 | 
			
		||||
@ -44,15 +46,28 @@ class Entry_text(Entry):
 | 
			
		||||
            message = "a message directly in the node"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    or just:
 | 
			
		||||
 | 
			
		||||
        text {
 | 
			
		||||
            size = <8>;
 | 
			
		||||
            text = "some text directly in the node"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    The text is not itself nul-terminated. This can be achieved, if required,
 | 
			
		||||
    by setting the size of the entry to something larger than the text.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry.__init__(self, section, etype, node)
 | 
			
		||||
        label, = self.GetEntryArgsOrProps([EntryArg('text-label', str)])
 | 
			
		||||
        self.text_label = tools.ToStr(label) if type(label) != str else label
 | 
			
		||||
        value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, str)])
 | 
			
		||||
        value = tools.ToBytes(value) if value is not None else value
 | 
			
		||||
        value = fdt_util.GetString(self._node, 'text')
 | 
			
		||||
        if value:
 | 
			
		||||
            value = tools.ToBytes(value)
 | 
			
		||||
        else:
 | 
			
		||||
            label, = self.GetEntryArgsOrProps([EntryArg('text-label', str)])
 | 
			
		||||
            self.text_label = label
 | 
			
		||||
            if self.text_label:
 | 
			
		||||
                value, = self.GetEntryArgsOrProps([EntryArg(self.text_label,
 | 
			
		||||
                                                            str)])
 | 
			
		||||
                value = tools.ToBytes(value) if value is not None else value
 | 
			
		||||
        self.value = value
 | 
			
		||||
 | 
			
		||||
    def ObtainContents(self):
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ class Entry_u_boot_spl_elf(Entry_blob):
 | 
			
		||||
    """U-Boot SPL ELF image
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments:
 | 
			
		||||
        - filename: Filename of SPL u-boot (default 'spl/u-boot')
 | 
			
		||||
        - filename: Filename of SPL u-boot (default 'spl/u-boot-spl')
 | 
			
		||||
 | 
			
		||||
    This is the U-Boot SPL ELF image. It does not include a device tree but can
 | 
			
		||||
    be relocated to any address for execution.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								tools/binman/etype/u_boot_tpl_elf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tools/binman/etype/u_boot_tpl_elf.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
# Copyright (c) 2018 Google, Inc
 | 
			
		||||
# Written by Simon Glass <sjg@chromium.org>
 | 
			
		||||
#
 | 
			
		||||
# Entry-type module for U-Boot TPL ELF image
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
from blob import Entry_blob
 | 
			
		||||
 | 
			
		||||
class Entry_u_boot_tpl_elf(Entry_blob):
 | 
			
		||||
    """U-Boot TPL ELF image
 | 
			
		||||
 | 
			
		||||
    Properties / Entry arguments:
 | 
			
		||||
        - filename: Filename of TPL u-boot (default 'tpl/u-boot-tpl')
 | 
			
		||||
 | 
			
		||||
    This is the U-Boot TPL ELF image. It does not include a device tree but can
 | 
			
		||||
    be relocated to any address for execution.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, section, etype, node):
 | 
			
		||||
        Entry_blob.__init__(self, section, etype, node)
 | 
			
		||||
 | 
			
		||||
    def GetDefaultFilename(self):
 | 
			
		||||
        return 'tpl/u-boot-tpl'
 | 
			
		||||
@ -49,7 +49,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
 | 
			
		||||
    def ProcessContents(self):
 | 
			
		||||
        # If the image does not need microcode, there is nothing to do
 | 
			
		||||
        if not self.target_offset:
 | 
			
		||||
            return
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        # Get the offset of the microcode
 | 
			
		||||
        ucode_entry = self.section.FindEntryType('u-boot-ucode')
 | 
			
		||||
@ -91,6 +91,6 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
 | 
			
		||||
        # Write the microcode offset and size into the entry
 | 
			
		||||
        offset_and_size = struct.pack('<2L', offset, size)
 | 
			
		||||
        self.target_offset -= self.image_pos
 | 
			
		||||
        self.ProcessContentsUpdate(self.data[:self.target_offset] +
 | 
			
		||||
                                   offset_and_size +
 | 
			
		||||
                                   self.data[self.target_offset + 8:])
 | 
			
		||||
        return self.ProcessContentsUpdate(self.data[:self.target_offset] +
 | 
			
		||||
                                          offset_and_size +
 | 
			
		||||
                                          self.data[self.target_offset + 8:])
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -8,15 +8,21 @@
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
import fnmatch
 | 
			
		||||
from operator import attrgetter
 | 
			
		||||
import re
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from entry import Entry
 | 
			
		||||
from etype import fdtmap
 | 
			
		||||
from etype import image_header
 | 
			
		||||
from etype import section
 | 
			
		||||
import fdt
 | 
			
		||||
import fdt_util
 | 
			
		||||
import bsection
 | 
			
		||||
import tools
 | 
			
		||||
import tout
 | 
			
		||||
 | 
			
		||||
class Image:
 | 
			
		||||
class Image(section.Entry_section):
 | 
			
		||||
    """A Image, representing an output from binman
 | 
			
		||||
 | 
			
		||||
    An image is comprised of a collection of entries each containing binary
 | 
			
		||||
@ -24,12 +30,8 @@ class Image:
 | 
			
		||||
 | 
			
		||||
    This class implements the various operations needed for images.
 | 
			
		||||
 | 
			
		||||
    Atrtributes:
 | 
			
		||||
        _node: Node object that contains the image definition in device tree
 | 
			
		||||
        _name: Image name
 | 
			
		||||
        _size: Image size in bytes, or None if not known yet
 | 
			
		||||
        _filename: Output filename for image
 | 
			
		||||
        _sections: Sections present in this image (may be one or more)
 | 
			
		||||
    Attributes:
 | 
			
		||||
        filename: Output filename for image
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        test: True if this is being called from a test of Images. This this case
 | 
			
		||||
@ -37,106 +39,94 @@ class Image:
 | 
			
		||||
            we create a section manually.
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, name, node, test=False):
 | 
			
		||||
        self._node = node
 | 
			
		||||
        self._name = name
 | 
			
		||||
        self._size = None
 | 
			
		||||
        self._filename = '%s.bin' % self._name
 | 
			
		||||
        if test:
 | 
			
		||||
            self._section = bsection.Section('main-section', None, self._node,
 | 
			
		||||
                                             self, True)
 | 
			
		||||
        else:
 | 
			
		||||
            self._ReadNode()
 | 
			
		||||
        self.image = self
 | 
			
		||||
        section.Entry_section.__init__(self, None, 'section', node, test)
 | 
			
		||||
        self.name = 'main-section'
 | 
			
		||||
        self.image_name = name
 | 
			
		||||
        self._filename = '%s.bin' % self.image_name
 | 
			
		||||
        if not test:
 | 
			
		||||
            filename = fdt_util.GetString(self._node, 'filename')
 | 
			
		||||
            if filename:
 | 
			
		||||
                self._filename = filename
 | 
			
		||||
 | 
			
		||||
    def _ReadNode(self):
 | 
			
		||||
        """Read properties from the image node"""
 | 
			
		||||
        self._size = fdt_util.GetInt(self._node, 'size')
 | 
			
		||||
        filename = fdt_util.GetString(self._node, 'filename')
 | 
			
		||||
        if filename:
 | 
			
		||||
            self._filename = filename
 | 
			
		||||
        self._section = bsection.Section('main-section', None, self._node, self)
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def FromFile(cls, fname):
 | 
			
		||||
        """Convert an image file into an Image for use in binman
 | 
			
		||||
 | 
			
		||||
    def GetFdtSet(self):
 | 
			
		||||
        """Get the set of device tree files used by this image"""
 | 
			
		||||
        return self._section.GetFdtSet()
 | 
			
		||||
        Args:
 | 
			
		||||
            fname: Filename of image file to read
 | 
			
		||||
 | 
			
		||||
    def ExpandEntries(self):
 | 
			
		||||
        """Expand out any entries which have calculated sub-entries
 | 
			
		||||
        Returns:
 | 
			
		||||
            Image object on success
 | 
			
		||||
 | 
			
		||||
        Some entries are expanded out at runtime, e.g. 'files', which produces
 | 
			
		||||
        a section containing a list of files. Process these entries so that
 | 
			
		||||
        this information is added to the device tree.
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if something goes wrong
 | 
			
		||||
        """
 | 
			
		||||
        self._section.ExpandEntries()
 | 
			
		||||
        data = tools.ReadFile(fname)
 | 
			
		||||
        size = len(data)
 | 
			
		||||
 | 
			
		||||
    def AddMissingProperties(self):
 | 
			
		||||
        """Add properties that are not present in the device tree
 | 
			
		||||
        # First look for an image header
 | 
			
		||||
        pos = image_header.LocateHeaderOffset(data)
 | 
			
		||||
        if pos is None:
 | 
			
		||||
            # Look for the FDT map
 | 
			
		||||
            pos = fdtmap.LocateFdtmap(data)
 | 
			
		||||
        if pos is None:
 | 
			
		||||
            raise ValueError('Cannot find FDT map in image')
 | 
			
		||||
 | 
			
		||||
        When binman has completed packing the entries the offset and size of
 | 
			
		||||
        each entry are known. But before this the device tree may not specify
 | 
			
		||||
        these. Add any missing properties, with a dummy value, so that the
 | 
			
		||||
        size of the entry is correct. That way we can insert the correct values
 | 
			
		||||
        later.
 | 
			
		||||
        """
 | 
			
		||||
        self._section.AddMissingProperties()
 | 
			
		||||
        # We don't know the FDT size, so check its header first
 | 
			
		||||
        probe_dtb = fdt.Fdt.FromData(
 | 
			
		||||
            data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256])
 | 
			
		||||
        dtb_size = probe_dtb.GetFdtObj().totalsize()
 | 
			
		||||
        fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN]
 | 
			
		||||
        dtb = fdt.Fdt.FromData(fdtmap_data[fdtmap.FDTMAP_HDR_LEN:])
 | 
			
		||||
        dtb.Scan()
 | 
			
		||||
 | 
			
		||||
    def ProcessFdt(self, fdt):
 | 
			
		||||
        """Allow entries to adjust the device tree
 | 
			
		||||
        # Return an Image with the associated nodes
 | 
			
		||||
        image = Image('image', dtb.GetRoot())
 | 
			
		||||
        image._data = data
 | 
			
		||||
        return image
 | 
			
		||||
 | 
			
		||||
        Some entries need to adjust the device tree for their purposes. This
 | 
			
		||||
        may involve adding or deleting properties.
 | 
			
		||||
        """
 | 
			
		||||
        return self._section.ProcessFdt(fdt)
 | 
			
		||||
 | 
			
		||||
    def GetEntryContents(self):
 | 
			
		||||
        """Call ObtainContents() for the section
 | 
			
		||||
        """
 | 
			
		||||
        self._section.GetEntryContents()
 | 
			
		||||
 | 
			
		||||
    def GetEntryOffsets(self):
 | 
			
		||||
        """Handle entries that want to set the offset/size of other entries
 | 
			
		||||
 | 
			
		||||
        This calls each entry's GetOffsets() method. If it returns a list
 | 
			
		||||
        of entries to update, it updates them.
 | 
			
		||||
        """
 | 
			
		||||
        self._section.GetEntryOffsets()
 | 
			
		||||
    def Raise(self, msg):
 | 
			
		||||
        """Convenience function to raise an error referencing an image"""
 | 
			
		||||
        raise ValueError("Image '%s': %s" % (self._node.path, msg))
 | 
			
		||||
 | 
			
		||||
    def PackEntries(self):
 | 
			
		||||
        """Pack all entries into the image"""
 | 
			
		||||
        self._section.PackEntries()
 | 
			
		||||
 | 
			
		||||
    def CheckSize(self):
 | 
			
		||||
        """Check that the image contents does not exceed its size, etc."""
 | 
			
		||||
        self._size = self._section.CheckSize()
 | 
			
		||||
 | 
			
		||||
    def CheckEntries(self):
 | 
			
		||||
        """Check that entries do not overlap or extend outside the image"""
 | 
			
		||||
        self._section.CheckEntries()
 | 
			
		||||
 | 
			
		||||
    def SetCalculatedProperties(self):
 | 
			
		||||
        self._section.SetCalculatedProperties()
 | 
			
		||||
        section.Entry_section.Pack(self, 0)
 | 
			
		||||
 | 
			
		||||
    def SetImagePos(self):
 | 
			
		||||
        self._section.SetImagePos(0)
 | 
			
		||||
        # This first section in the image so it starts at 0
 | 
			
		||||
        section.Entry_section.SetImagePos(self, 0)
 | 
			
		||||
 | 
			
		||||
    def ProcessEntryContents(self):
 | 
			
		||||
        """Call the ProcessContents() method for each entry
 | 
			
		||||
 | 
			
		||||
        This is intended to adjust the contents as needed by the entry type.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            True if the new data size is OK, False if expansion is needed
 | 
			
		||||
        """
 | 
			
		||||
        self._section.ProcessEntryContents()
 | 
			
		||||
        sizes_ok = True
 | 
			
		||||
        for entry in self._entries.values():
 | 
			
		||||
            if not entry.ProcessContents():
 | 
			
		||||
                sizes_ok = False
 | 
			
		||||
                tout.Debug("Entry '%s' size change" % self._node.path)
 | 
			
		||||
        return sizes_ok
 | 
			
		||||
 | 
			
		||||
    def WriteSymbols(self):
 | 
			
		||||
        """Write symbol values into binary files for access at run time"""
 | 
			
		||||
        self._section.WriteSymbols()
 | 
			
		||||
        section.Entry_section.WriteSymbols(self, self)
 | 
			
		||||
 | 
			
		||||
    def BuildSection(self, fd, base_offset):
 | 
			
		||||
        """Write the section to a file"""
 | 
			
		||||
        fd.seek(base_offset)
 | 
			
		||||
        fd.write(self.GetData())
 | 
			
		||||
 | 
			
		||||
    def BuildImage(self):
 | 
			
		||||
        """Write the image to a file"""
 | 
			
		||||
        fname = tools.GetOutputFilename(self._filename)
 | 
			
		||||
        with open(fname, 'wb') as fd:
 | 
			
		||||
            self._section.BuildSection(fd, 0)
 | 
			
		||||
 | 
			
		||||
    def GetEntries(self):
 | 
			
		||||
        return self._section.GetEntries()
 | 
			
		||||
            self.BuildSection(fd, 0)
 | 
			
		||||
 | 
			
		||||
    def WriteMap(self):
 | 
			
		||||
        """Write a map of the image to a .map file
 | 
			
		||||
@ -144,10 +134,169 @@ class Image:
 | 
			
		||||
        Returns:
 | 
			
		||||
            Filename of map file written
 | 
			
		||||
        """
 | 
			
		||||
        filename = '%s.map' % self._name
 | 
			
		||||
        filename = '%s.map' % self.image_name
 | 
			
		||||
        fname = tools.GetOutputFilename(filename)
 | 
			
		||||
        with open(fname, 'w') as fd:
 | 
			
		||||
            print('%8s  %8s  %8s  %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
 | 
			
		||||
                  file=fd)
 | 
			
		||||
            self._section.WriteMap(fd, 0)
 | 
			
		||||
            section.Entry_section.WriteMap(self, fd, 0)
 | 
			
		||||
        return fname
 | 
			
		||||
 | 
			
		||||
    def BuildEntryList(self):
 | 
			
		||||
        """List the files in an image
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            List of entry.EntryInfo objects describing all entries in the image
 | 
			
		||||
        """
 | 
			
		||||
        entries = []
 | 
			
		||||
        self.ListEntries(entries, 0)
 | 
			
		||||
        return entries
 | 
			
		||||
 | 
			
		||||
    def FindEntryPath(self, entry_path):
 | 
			
		||||
        """Find an entry at a given path in the image
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entry_path: Path to entry (e.g. /ro-section/u-boot')
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Entry object corresponding to that past
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            ValueError if no entry found
 | 
			
		||||
        """
 | 
			
		||||
        parts = entry_path.split('/')
 | 
			
		||||
        entries = self.GetEntries()
 | 
			
		||||
        parent = '/'
 | 
			
		||||
        for part in parts:
 | 
			
		||||
            entry = entries.get(part)
 | 
			
		||||
            if not entry:
 | 
			
		||||
                raise ValueError("Entry '%s' not found in '%s'" %
 | 
			
		||||
                                 (part, parent))
 | 
			
		||||
            parent = entry.GetPath()
 | 
			
		||||
            entries = entry.GetEntries()
 | 
			
		||||
        return entry
 | 
			
		||||
 | 
			
		||||
    def ReadData(self, decomp=True):
 | 
			
		||||
        return self._data
 | 
			
		||||
 | 
			
		||||
    def GetListEntries(self, entry_paths):
 | 
			
		||||
        """List the entries in an image
 | 
			
		||||
 | 
			
		||||
        This decodes the supplied image and returns a list of entries from that
 | 
			
		||||
        image, preceded by a header.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            entry_paths: List of paths to match (each can have wildcards). Only
 | 
			
		||||
                entries whose names match one of these paths will be printed
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            String error message if something went wrong, otherwise
 | 
			
		||||
            3-Tuple:
 | 
			
		||||
                List of EntryInfo objects
 | 
			
		||||
                List of lines, each
 | 
			
		||||
                    List of text columns, each a string
 | 
			
		||||
                List of widths of each column
 | 
			
		||||
        """
 | 
			
		||||
        def _EntryToStrings(entry):
 | 
			
		||||
            """Convert an entry to a list of strings, one for each column
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                entry: EntryInfo object containing information to output
 | 
			
		||||
 | 
			
		||||
            Returns:
 | 
			
		||||
                List of strings, one for each field in entry
 | 
			
		||||
            """
 | 
			
		||||
            def _AppendHex(val):
 | 
			
		||||
                """Append a hex value, or an empty string if val is None
 | 
			
		||||
 | 
			
		||||
                Args:
 | 
			
		||||
                    val: Integer value, or None if none
 | 
			
		||||
                """
 | 
			
		||||
                args.append('' if val is None else '>%x' % val)
 | 
			
		||||
 | 
			
		||||
            args = ['  ' * entry.indent + entry.name]
 | 
			
		||||
            _AppendHex(entry.image_pos)
 | 
			
		||||
            _AppendHex(entry.size)
 | 
			
		||||
            args.append(entry.etype)
 | 
			
		||||
            _AppendHex(entry.offset)
 | 
			
		||||
            _AppendHex(entry.uncomp_size)
 | 
			
		||||
            return args
 | 
			
		||||
 | 
			
		||||
        def _DoLine(lines, line):
 | 
			
		||||
            """Add a line to the output list
 | 
			
		||||
 | 
			
		||||
            This adds a line (a list of columns) to the output list. It also updates
 | 
			
		||||
            the widths[] array with the maximum width of each column
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                lines: List of lines to add to
 | 
			
		||||
                line: List of strings, one for each column
 | 
			
		||||
            """
 | 
			
		||||
            for i, item in enumerate(line):
 | 
			
		||||
                widths[i] = max(widths[i], len(item))
 | 
			
		||||
            lines.append(line)
 | 
			
		||||
 | 
			
		||||
        def _NameInPaths(fname, entry_paths):
 | 
			
		||||
            """Check if a filename is in a list of wildcarded paths
 | 
			
		||||
 | 
			
		||||
            Args:
 | 
			
		||||
                fname: Filename to check
 | 
			
		||||
                entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*',
 | 
			
		||||
                                                             'section/u-boot'])
 | 
			
		||||
 | 
			
		||||
            Returns:
 | 
			
		||||
                True if any wildcard matches the filename (using Unix filename
 | 
			
		||||
                    pattern matching, not regular expressions)
 | 
			
		||||
                False if not
 | 
			
		||||
            """
 | 
			
		||||
            for path in entry_paths:
 | 
			
		||||
                if fnmatch.fnmatch(fname, path):
 | 
			
		||||
                    return True
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        entries = self.BuildEntryList()
 | 
			
		||||
 | 
			
		||||
        # This is our list of lines. Each item in the list is a list of strings, one
 | 
			
		||||
        # for each column
 | 
			
		||||
        lines = []
 | 
			
		||||
        HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset',
 | 
			
		||||
                  'Uncomp-size']
 | 
			
		||||
        num_columns = len(HEADER)
 | 
			
		||||
 | 
			
		||||
        # This records the width of each column, calculated as the maximum width of
 | 
			
		||||
        # all the strings in that column
 | 
			
		||||
        widths = [0] * num_columns
 | 
			
		||||
        _DoLine(lines, HEADER)
 | 
			
		||||
 | 
			
		||||
        # We won't print anything unless it has at least this indent. So at the
 | 
			
		||||
        # start we will print nothing, unless a path matches (or there are no
 | 
			
		||||
        # entry paths)
 | 
			
		||||
        MAX_INDENT = 100
 | 
			
		||||
        min_indent = MAX_INDENT
 | 
			
		||||
        path_stack = []
 | 
			
		||||
        path = ''
 | 
			
		||||
        indent = 0
 | 
			
		||||
        selected_entries = []
 | 
			
		||||
        for entry in entries:
 | 
			
		||||
            if entry.indent > indent:
 | 
			
		||||
                path_stack.append(path)
 | 
			
		||||
            elif entry.indent < indent:
 | 
			
		||||
                path_stack.pop()
 | 
			
		||||
            if path_stack:
 | 
			
		||||
                path = path_stack[-1] + '/' + entry.name
 | 
			
		||||
            indent = entry.indent
 | 
			
		||||
 | 
			
		||||
            # If there are entry paths to match and we are not looking at a
 | 
			
		||||
            # sub-entry of a previously matched entry, we need to check the path
 | 
			
		||||
            if entry_paths and indent <= min_indent:
 | 
			
		||||
                if _NameInPaths(path[1:], entry_paths):
 | 
			
		||||
                    # Print this entry and all sub-entries (=higher indent)
 | 
			
		||||
                    min_indent = indent
 | 
			
		||||
                else:
 | 
			
		||||
                    # Don't print this entry, nor any following entries until we get
 | 
			
		||||
                    # a path match
 | 
			
		||||
                    min_indent = MAX_INDENT
 | 
			
		||||
                    continue
 | 
			
		||||
            _DoLine(lines, _EntryToStrings(entry))
 | 
			
		||||
            selected_entries.append(entry)
 | 
			
		||||
        return selected_entries, lines, widths
 | 
			
		||||
 | 
			
		||||
@ -12,28 +12,25 @@ from test_util import capture_sys_output
 | 
			
		||||
class TestImage(unittest.TestCase):
 | 
			
		||||
    def testInvalidFormat(self):
 | 
			
		||||
        image = Image('name', 'node', test=True)
 | 
			
		||||
        section = image._section
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            section.LookupSymbol('_binman_something_prop_', False, 'msg')
 | 
			
		||||
            image.LookupSymbol('_binman_something_prop_', False, 'msg')
 | 
			
		||||
        self.assertIn(
 | 
			
		||||
            "msg: Symbol '_binman_something_prop_' has invalid format",
 | 
			
		||||
            str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def testMissingSymbol(self):
 | 
			
		||||
        image = Image('name', 'node', test=True)
 | 
			
		||||
        section = image._section
 | 
			
		||||
        section._entries = {}
 | 
			
		||||
        image._entries = {}
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            section.LookupSymbol('_binman_type_prop_pname', False, 'msg')
 | 
			
		||||
            image.LookupSymbol('_binman_type_prop_pname', False, 'msg')
 | 
			
		||||
        self.assertIn("msg: Entry 'type' not found in list ()",
 | 
			
		||||
                      str(e.exception))
 | 
			
		||||
 | 
			
		||||
    def testMissingSymbolOptional(self):
 | 
			
		||||
        image = Image('name', 'node', test=True)
 | 
			
		||||
        section = image._section
 | 
			
		||||
        section._entries = {}
 | 
			
		||||
        image._entries = {}
 | 
			
		||||
        with capture_sys_output() as (stdout, stderr):
 | 
			
		||||
            val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg')
 | 
			
		||||
            val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg')
 | 
			
		||||
        self.assertEqual(val, None)
 | 
			
		||||
        self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n",
 | 
			
		||||
                         stderr.getvalue())
 | 
			
		||||
@ -41,8 +38,7 @@ class TestImage(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def testBadProperty(self):
 | 
			
		||||
        image = Image('name', 'node', test=True)
 | 
			
		||||
        section = image._section
 | 
			
		||||
        section._entries = {'u-boot': 1}
 | 
			
		||||
        image._entries = {'u-boot': 1}
 | 
			
		||||
        with self.assertRaises(ValueError) as e:
 | 
			
		||||
            section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
 | 
			
		||||
            image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
 | 
			
		||||
        self.assertIn("msg: No such property 'bad", str(e.exception))
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,11 @@ fdt_subset = set()
 | 
			
		||||
# The DTB which contains the full image information
 | 
			
		||||
main_dtb = None
 | 
			
		||||
 | 
			
		||||
# Allow entries to expand after they have been packed. This is detected and
 | 
			
		||||
# forces a re-pack. If not allowed, any attempted expansion causes an error in
 | 
			
		||||
# Entry.ProcessContentsUpdate()
 | 
			
		||||
allow_entry_expansion = True
 | 
			
		||||
 | 
			
		||||
def GetFdt(fname):
 | 
			
		||||
    """Get the Fdt object for a particular device-tree filename
 | 
			
		||||
 | 
			
		||||
@ -59,7 +64,7 @@ def GetFdtPath(fname):
 | 
			
		||||
    """
 | 
			
		||||
    return fdt_files[fname]._fname
 | 
			
		||||
 | 
			
		||||
def GetFdtContents(fname):
 | 
			
		||||
def GetFdtContents(fname='u-boot.dtb'):
 | 
			
		||||
    """Looks up the FDT pathname and contents
 | 
			
		||||
 | 
			
		||||
    This is used to obtain the Fdt pathname and contents when needed by an
 | 
			
		||||
@ -250,3 +255,22 @@ def CheckSetHashValue(node, get_data_func):
 | 
			
		||||
            data = m.digest()
 | 
			
		||||
        for n in GetUpdateNodes(hash_node):
 | 
			
		||||
            n.SetData('value', data)
 | 
			
		||||
 | 
			
		||||
def SetAllowEntryExpansion(allow):
 | 
			
		||||
    """Set whether post-pack expansion of entries is allowed
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
       allow: True to allow expansion, False to raise an exception
 | 
			
		||||
    """
 | 
			
		||||
    global allow_entry_expansion
 | 
			
		||||
 | 
			
		||||
    allow_entry_expansion = allow
 | 
			
		||||
 | 
			
		||||
def AllowEntryExpansion():
 | 
			
		||||
    """Check whether post-pack expansion of entries is allowed
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        True if expansion should be allowed, False if an exception should be
 | 
			
		||||
            raised
 | 
			
		||||
    """
 | 
			
		||||
    return allow_entry_expansion
 | 
			
		||||
 | 
			
		||||
@ -24,5 +24,10 @@
 | 
			
		||||
			text-label = "test-id4";
 | 
			
		||||
			test-id4 = "some text";
 | 
			
		||||
		};
 | 
			
		||||
		/* Put text directly in the node */
 | 
			
		||||
		text5 {
 | 
			
		||||
			type = "text";
 | 
			
		||||
			text = "more text";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -10,5 +10,7 @@
 | 
			
		||||
		};
 | 
			
		||||
		u-boot-spl-elf {
 | 
			
		||||
		};
 | 
			
		||||
		u-boot-tpl-elf {
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								tools/binman/test/102_cbfs_raw.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tools/binman/test/102_cbfs_raw.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0xb0>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								tools/binman/test/103_cbfs_raw_ppc.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/binman/test/103_cbfs_raw_ppc.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			cbfs-arch = "ppc64";
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										19
									
								
								tools/binman/test/104_cbfs_stage.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tools/binman/test/104_cbfs_stage.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0xb0>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				type = "blob";
 | 
			
		||||
				filename = "cbfs-stage.elf";
 | 
			
		||||
				cbfs-type = "stage";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										26
									
								
								tools/binman/test/105_cbfs_raw_compress.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tools/binman/test/105_cbfs_raw_compress.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x140>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				type = "text";
 | 
			
		||||
				text = "compress xxxxxxxxxxxxxxxxxxxxxx data";
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
				cbfs-compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				type = "text";
 | 
			
		||||
				text = "compress xxxxxxxxxxxxxxxxxxxxxx data";
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
				cbfs-compress = "lzma";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										15
									
								
								tools/binman/test/106_cbfs_bad_arch.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tools/binman/test/106_cbfs_bad_arch.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			cbfs-arch = "bad-arch";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										13
									
								
								tools/binman/test/107_cbfs_no_size.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tools/binman/test/107_cbfs_no_size.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								tools/binman/test/108_cbfs_no_contents.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tools/binman/test/108_cbfs_no_contents.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			_testing {
 | 
			
		||||
				return-unknown-contents;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										18
									
								
								tools/binman/test/109_cbfs_bad_compress.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tools/binman/test/109_cbfs_bad_compress.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0xb0>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
				cbfs-compress = "invalid-algo";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								tools/binman/test/110_cbfs_name.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tools/binman/test/110_cbfs_name.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-name = "FRED";
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			hello {
 | 
			
		||||
				type = "blob";
 | 
			
		||||
				filename = "u-boot.dtb";
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										29
									
								
								tools/binman/test/111_x86-rom-ifwi.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tools/binman/test/111_x86-rom-ifwi.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		end-at-4gb;
 | 
			
		||||
		size = <0x800000>;
 | 
			
		||||
		intel-descriptor {
 | 
			
		||||
			filename = "descriptor.bin";
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		intel-ifwi {
 | 
			
		||||
			offset-unset;
 | 
			
		||||
			filename = "fitimage.bin";
 | 
			
		||||
			convert-fit;
 | 
			
		||||
 | 
			
		||||
			u-boot-tpl {
 | 
			
		||||
				replace;
 | 
			
		||||
				ifwi-subpart = "IBBP";
 | 
			
		||||
				ifwi-entry = "IBBL";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										28
									
								
								tools/binman/test/112_x86-rom-ifwi-nodesc.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tools/binman/test/112_x86-rom-ifwi-nodesc.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		end-at-4gb;
 | 
			
		||||
		size = <0x800000>;
 | 
			
		||||
		intel-descriptor {
 | 
			
		||||
			filename = "descriptor.bin";
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		intel-ifwi {
 | 
			
		||||
			offset-unset;
 | 
			
		||||
			filename = "ifwi.bin";
 | 
			
		||||
 | 
			
		||||
			u-boot-tpl {
 | 
			
		||||
				replace;
 | 
			
		||||
				ifwi-subpart = "IBBP";
 | 
			
		||||
				ifwi-entry = "IBBL";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										29
									
								
								tools/binman/test/113_x86-rom-ifwi-nodata.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tools/binman/test/113_x86-rom-ifwi-nodata.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		end-at-4gb;
 | 
			
		||||
		size = <0x800000>;
 | 
			
		||||
		intel-descriptor {
 | 
			
		||||
			filename = "descriptor.bin";
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		intel-ifwi {
 | 
			
		||||
			offset-unset;
 | 
			
		||||
			filename = "ifwi.bin";
 | 
			
		||||
 | 
			
		||||
			_testing {
 | 
			
		||||
				return-unknown-contents;
 | 
			
		||||
				replace;
 | 
			
		||||
				ifwi-subpart = "IBBP";
 | 
			
		||||
				ifwi-entry = "IBBL";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										26
									
								
								tools/binman/test/114_cbfs_offset.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								tools/binman/test/114_cbfs_offset.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		end-at-4gb;
 | 
			
		||||
		size = <0x200>;
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x200>;
 | 
			
		||||
			offset = <0xfffffe00>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-offset = <0x40>;
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				cbfs-offset = <0x140>;
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										13
									
								
								tools/binman/test/115_fdtmap.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tools/binman/test/115_fdtmap.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								tools/binman/test/116_fdtmap_hdr.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tools/binman/test/116_fdtmap_hdr.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0x400>;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			location = "end";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										19
									
								
								tools/binman/test/117_fdtmap_hdr_start.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tools/binman/test/117_fdtmap_hdr_start.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0x400>;
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		u-boot {
 | 
			
		||||
			offset = <0x100>;
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			location = "start";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										19
									
								
								tools/binman/test/118_fdtmap_hdr_pos.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tools/binman/test/118_fdtmap_hdr_pos.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0x400>;
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		u-boot {
 | 
			
		||||
			offset = <0x100>;
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			offset = <0x80>;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										16
									
								
								tools/binman/test/119_fdtmap_hdr_missing.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tools/binman/test/119_fdtmap_hdr_missing.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			offset = <0x80>;
 | 
			
		||||
			location = "start";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										16
									
								
								tools/binman/test/120_hdr_no_location.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tools/binman/test/120_hdr_no_location.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		sort-by-offset;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										20
									
								
								tools/binman/test/121_entry_expand.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tools/binman/test/121_entry_expand.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		_testing {
 | 
			
		||||
			bad-update-contents;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		_testing2 {
 | 
			
		||||
			type = "_testing";
 | 
			
		||||
			bad-update-contents;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								tools/binman/test/122_entry_expand_twice.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/binman/test/122_entry_expand_twice.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		_testing {
 | 
			
		||||
			bad-update-contents;
 | 
			
		||||
			bad-update-contents-twice;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		_testing2 {
 | 
			
		||||
			type = "_testing";
 | 
			
		||||
			bad-update-contents;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										22
									
								
								tools/binman/test/123_entry_expand_section.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tools/binman/test/123_entry_expand_section.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		_testing {
 | 
			
		||||
			bad-update-contents;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		section {
 | 
			
		||||
			_testing2 {
 | 
			
		||||
				type = "_testing";
 | 
			
		||||
				bad-update-contents;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										14
									
								
								tools/binman/test/124_compress_dtb.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tools/binman/test/124_compress_dtb.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		u-boot-dtb {
 | 
			
		||||
			compress = "lz4";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								tools/binman/test/125_cbfs_update.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tools/binman/test/125_cbfs_update.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
				cbfs-compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				cbfs-type = "raw";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								tools/binman/test/126_cbfs_bad_type.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tools/binman/test/126_cbfs_bad_type.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		cbfs {
 | 
			
		||||
			size = <0x100>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
				cbfs-type = "badtype";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										33
									
								
								tools/binman/test/127_list.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tools/binman/test/127_list.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		section {
 | 
			
		||||
			align = <0x100>;
 | 
			
		||||
			cbfs {
 | 
			
		||||
				size = <0x400>;
 | 
			
		||||
				u-boot {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
					cbfs-offset = <0x38>;
 | 
			
		||||
				};
 | 
			
		||||
				u-boot-dtb {
 | 
			
		||||
					type = "text";
 | 
			
		||||
					text = "compress xxxxxxxxxxxxxxxxxxxxxx data";
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
					cbfs-compress = "lzma";
 | 
			
		||||
					cbfs-offset = <0x78>;
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										36
									
								
								tools/binman/test/128_decode_image.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tools/binman/test/128_decode_image.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0xc00>;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		section {
 | 
			
		||||
			align = <0x100>;
 | 
			
		||||
			cbfs {
 | 
			
		||||
				size = <0x400>;
 | 
			
		||||
				u-boot {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
				};
 | 
			
		||||
				u-boot-dtb {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
					cbfs-compress = "lzma";
 | 
			
		||||
					cbfs-offset = <0x80>;
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			location = "end";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										33
									
								
								tools/binman/test/129_decode_image_nohdr.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tools/binman/test/129_decode_image_nohdr.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0xc00>;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		section {
 | 
			
		||||
			align = <0x100>;
 | 
			
		||||
			cbfs {
 | 
			
		||||
				size = <0x400>;
 | 
			
		||||
				u-boot {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
				};
 | 
			
		||||
				u-boot-dtb {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
					cbfs-compress = "lzma";
 | 
			
		||||
					cbfs-offset = <0x80>;
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										36
									
								
								tools/binman/test/130_list_fdtmap.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								tools/binman/test/130_list_fdtmap.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0+
 | 
			
		||||
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		size = <0xc00>;
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		section {
 | 
			
		||||
			align = <0x100>;
 | 
			
		||||
			cbfs {
 | 
			
		||||
				size = <0x400>;
 | 
			
		||||
				u-boot {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
				};
 | 
			
		||||
				u-boot-dtb {
 | 
			
		||||
					cbfs-type = "raw";
 | 
			
		||||
					cbfs-compress = "lzma";
 | 
			
		||||
					cbfs-offset = <0x80>;
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			u-boot-dtb {
 | 
			
		||||
				compress = "lz4";
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		fdtmap {
 | 
			
		||||
		};
 | 
			
		||||
		image-header {
 | 
			
		||||
			location = "end";
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										28
									
								
								tools/binman/test/131_pack_align_section.dts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tools/binman/test/131_pack_align_section.dts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
/dts-v1/;
 | 
			
		||||
 | 
			
		||||
/ {
 | 
			
		||||
	#address-cells = <1>;
 | 
			
		||||
	#size-cells = <1>;
 | 
			
		||||
 | 
			
		||||
	binman {
 | 
			
		||||
		u-boot {
 | 
			
		||||
		};
 | 
			
		||||
		section0 {
 | 
			
		||||
			type = "section";
 | 
			
		||||
			align = <0x10>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		section1 {
 | 
			
		||||
			type = "section";
 | 
			
		||||
			align-size = <0x20>;
 | 
			
		||||
			u-boot {
 | 
			
		||||
			};
 | 
			
		||||
			section2 {
 | 
			
		||||
				type = "section";
 | 
			
		||||
				u-boot {
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tools/binman/test/fitimage.bin.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tools/binman/test/fitimage.bin.gz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								tools/binman/test/ifwi.bin.gz
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tools/binman/test/ifwi.bin.gz
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -137,7 +137,7 @@ the '&' operator to limit the selection:
 | 
			
		||||
 | 
			
		||||
You can also use -x to specifically exclude some boards. For example:
 | 
			
		||||
 | 
			
		||||
 buildmand arm -x nvidia,freescale,.*ball$
 | 
			
		||||
  buildman arm -x nvidia,freescale,.*ball$
 | 
			
		||||
 | 
			
		||||
means to build all arm boards except nvidia, freescale and anything ending
 | 
			
		||||
with 'ball'.
 | 
			
		||||
@ -146,7 +146,7 @@ For building specific boards you can use the --boards option, which takes a
 | 
			
		||||
comma-separated list of board target names and be used multiple times on
 | 
			
		||||
the command line:
 | 
			
		||||
 | 
			
		||||
   buidman --boards sandbox,snow --boards
 | 
			
		||||
  buildman --boards sandbox,snow --boards
 | 
			
		||||
 | 
			
		||||
It is convenient to use the -n option to see what will be built based on
 | 
			
		||||
the subset given. Use -v as well to get an actual list of boards.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2304
									
								
								tools/ifwitool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2304
									
								
								tools/ifwitool.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -108,8 +108,8 @@ def RunPipe(pipe_list, infile=None, outfile=None,
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
def Output(*cmd, **kwargs):
 | 
			
		||||
    raise_on_error = kwargs.get('raise_on_error', True)
 | 
			
		||||
    return RunPipe([cmd], capture=True, raise_on_error=raise_on_error).stdout
 | 
			
		||||
    kwargs['raise_on_error'] = kwargs.get('raise_on_error', True)
 | 
			
		||||
    return RunPipe([cmd], capture=True, **kwargs).stdout
 | 
			
		||||
 | 
			
		||||
def OutputOneLine(*cmd, **kwargs):
 | 
			
		||||
    raise_on_error = kwargs.pop('raise_on_error', True)
 | 
			
		||||
 | 
			
		||||
@ -46,9 +46,10 @@ def RunTestCoverage(prog, filter_fname, exclude_list, build_dir, required=None):
 | 
			
		||||
        glob_list = []
 | 
			
		||||
    glob_list += exclude_list
 | 
			
		||||
    glob_list += ['*libfdt.py', '*site-packages*', '*dist-packages*']
 | 
			
		||||
    test_cmd = 'test' if 'binman.py' in prog else '-t'
 | 
			
		||||
    cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools %s-coverage run '
 | 
			
		||||
           '--omit "%s" %s -P1 -t' % (build_dir, PYTHON, ','.join(glob_list),
 | 
			
		||||
                                      prog))
 | 
			
		||||
           '--omit "%s" %s %s -P1' % (build_dir, PYTHON, ','.join(glob_list),
 | 
			
		||||
                                      prog, test_cmd))
 | 
			
		||||
    os.system(cmd)
 | 
			
		||||
    stdout = command.Output('%s-coverage' % PYTHON, 'report')
 | 
			
		||||
    lines = stdout.splitlines()
 | 
			
		||||
@ -57,6 +58,7 @@ def RunTestCoverage(prog, filter_fname, exclude_list, build_dir, required=None):
 | 
			
		||||
        test_set = set([os.path.splitext(os.path.basename(line.split()[0]))[0]
 | 
			
		||||
                        for line in lines if '/etype/' in line])
 | 
			
		||||
        missing_list = required
 | 
			
		||||
        missing_list.discard('__init__')
 | 
			
		||||
        missing_list.difference_update(test_set)
 | 
			
		||||
        if missing_list:
 | 
			
		||||
            print('Missing tests for %s' % (', '.join(missing_list)))
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,8 @@
 | 
			
		||||
# Copyright (c) 2016 Google, Inc
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import command
 | 
			
		||||
import glob
 | 
			
		||||
import os
 | 
			
		||||
@ -24,6 +26,8 @@ chroot_path = None
 | 
			
		||||
# Search paths to use for Filename(), used to find files
 | 
			
		||||
search_paths = []
 | 
			
		||||
 | 
			
		||||
tool_search_paths = []
 | 
			
		||||
 | 
			
		||||
# Tools and the packages that contain them, on debian
 | 
			
		||||
packages = {
 | 
			
		||||
    'lz4': 'liblz4-tool',
 | 
			
		||||
@ -154,26 +158,56 @@ def Align(pos, align):
 | 
			
		||||
def NotPowerOfTwo(num):
 | 
			
		||||
    return num and (num & (num - 1))
 | 
			
		||||
 | 
			
		||||
def PathHasFile(fname):
 | 
			
		||||
def SetToolPaths(toolpaths):
 | 
			
		||||
    """Set the path to search for tools
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        toolpaths: List of paths to search for tools executed by Run()
 | 
			
		||||
    """
 | 
			
		||||
    global tool_search_paths
 | 
			
		||||
 | 
			
		||||
    tool_search_paths = toolpaths
 | 
			
		||||
 | 
			
		||||
def PathHasFile(path_spec, fname):
 | 
			
		||||
    """Check if a given filename is in the PATH
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        path_spec: Value of PATH variable to check
 | 
			
		||||
        fname: Filename to check
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        True if found, False if not
 | 
			
		||||
    """
 | 
			
		||||
    for dir in os.environ['PATH'].split(':'):
 | 
			
		||||
    for dir in path_spec.split(':'):
 | 
			
		||||
        if os.path.exists(os.path.join(dir, fname)):
 | 
			
		||||
            return True
 | 
			
		||||
    return False
 | 
			
		||||
 | 
			
		||||
def Run(name, *args, **kwargs):
 | 
			
		||||
    """Run a tool with some arguments
 | 
			
		||||
 | 
			
		||||
    This runs a 'tool', which is a program used by binman to process files and
 | 
			
		||||
    perhaps produce some output. Tools can be located on the PATH or in a
 | 
			
		||||
    search path.
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        name: Command name to run
 | 
			
		||||
        args: Arguments to the tool
 | 
			
		||||
        kwargs: Options to pass to command.run()
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        CommandResult object
 | 
			
		||||
    """
 | 
			
		||||
    try:
 | 
			
		||||
        return command.Run(name, *args, cwd=outdir, capture=True, **kwargs)
 | 
			
		||||
        env = None
 | 
			
		||||
        if tool_search_paths:
 | 
			
		||||
            env = dict(os.environ)
 | 
			
		||||
            env['PATH'] = ':'.join(tool_search_paths) + ':' + env['PATH']
 | 
			
		||||
        return command.Run(name, *args, capture=True,
 | 
			
		||||
                           capture_stderr=True, env=env, **kwargs)
 | 
			
		||||
    except:
 | 
			
		||||
        if not PathHasFile(name):
 | 
			
		||||
            msg = "Plesae install tool '%s'" % name
 | 
			
		||||
        if env and not PathHasFile(env['PATH'], name):
 | 
			
		||||
            msg = "Please install tool '%s'" % name
 | 
			
		||||
            package = packages.get(name)
 | 
			
		||||
            if package:
 | 
			
		||||
                 msg += " (e.g. from package '%s')" % package
 | 
			
		||||
@ -342,3 +376,100 @@ def ToBytes(string):
 | 
			
		||||
    if sys.version_info[0] >= 3:
 | 
			
		||||
        return string.encode('utf-8')
 | 
			
		||||
    return string
 | 
			
		||||
 | 
			
		||||
def Compress(indata, algo):
 | 
			
		||||
    """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: Input data to compress
 | 
			
		||||
        algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Compressed data
 | 
			
		||||
    """
 | 
			
		||||
    if algo == 'none':
 | 
			
		||||
        return indata
 | 
			
		||||
    fname = GetOutputFilename('%s.comp.tmp' % algo)
 | 
			
		||||
    WriteFile(fname, indata)
 | 
			
		||||
    if algo == 'lz4':
 | 
			
		||||
        data = Run('lz4', '--no-frame-crc', '-c', fname, binary=True)
 | 
			
		||||
    # cbfstool uses a very old version of lzma
 | 
			
		||||
    elif algo == 'lzma':
 | 
			
		||||
        outfname = GetOutputFilename('%s.comp.otmp' % algo)
 | 
			
		||||
        Run('lzma_alone', 'e', fname, outfname, '-lc1', '-lp0', '-pb0', '-d8')
 | 
			
		||||
        data = ReadFile(outfname)
 | 
			
		||||
    elif algo == 'gzip':
 | 
			
		||||
        data = Run('gzip', '-c', fname, binary=True)
 | 
			
		||||
    else:
 | 
			
		||||
        raise ValueError("Unknown algorithm '%s'" % algo)
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
def Decompress(indata, algo):
 | 
			
		||||
    """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: Input data to decompress
 | 
			
		||||
        algo: Algorithm to use ('none', 'gzip', 'lz4' or 'lzma')
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        Compressed data
 | 
			
		||||
    """
 | 
			
		||||
    if algo == 'none':
 | 
			
		||||
        return indata
 | 
			
		||||
    fname = GetOutputFilename('%s.decomp.tmp' % algo)
 | 
			
		||||
    with open(fname, 'wb') as fd:
 | 
			
		||||
        fd.write(indata)
 | 
			
		||||
    if algo == 'lz4':
 | 
			
		||||
        data = Run('lz4', '-dc', fname, binary=True)
 | 
			
		||||
    elif algo == 'lzma':
 | 
			
		||||
        outfname = GetOutputFilename('%s.decomp.otmp' % algo)
 | 
			
		||||
        Run('lzma_alone', 'd', fname, outfname)
 | 
			
		||||
        data = ReadFile(outfname)
 | 
			
		||||
    elif algo == 'gzip':
 | 
			
		||||
        data = Run('gzip', '-cd', fname, binary=True)
 | 
			
		||||
    else:
 | 
			
		||||
        raise ValueError("Unknown algorithm '%s'" % algo)
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
CMD_CREATE, CMD_DELETE, CMD_ADD, CMD_REPLACE, CMD_EXTRACT = range(5)
 | 
			
		||||
 | 
			
		||||
IFWITOOL_CMDS = {
 | 
			
		||||
    CMD_CREATE: 'create',
 | 
			
		||||
    CMD_DELETE: 'delete',
 | 
			
		||||
    CMD_ADD: 'add',
 | 
			
		||||
    CMD_REPLACE: 'replace',
 | 
			
		||||
    CMD_EXTRACT: 'extract',
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
def RunIfwiTool(ifwi_file, cmd, fname=None, subpart=None, entry_name=None):
 | 
			
		||||
    """Run ifwitool with the given arguments:
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        ifwi_file: IFWI file to operation on
 | 
			
		||||
        cmd: Command to execute (CMD_...)
 | 
			
		||||
        fname: Filename of file to add/replace/extract/create (None for
 | 
			
		||||
            CMD_DELETE)
 | 
			
		||||
        subpart: Name of sub-partition to operation on (None for CMD_CREATE)
 | 
			
		||||
        entry_name: Name of directory entry to operate on, or None if none
 | 
			
		||||
    """
 | 
			
		||||
    args = ['ifwitool', ifwi_file]
 | 
			
		||||
    args.append(IFWITOOL_CMDS[cmd])
 | 
			
		||||
    if fname:
 | 
			
		||||
        args += ['-f', fname]
 | 
			
		||||
    if subpart:
 | 
			
		||||
        args += ['-n', subpart]
 | 
			
		||||
    if entry_name:
 | 
			
		||||
        args += ['-d', '-e', entry_name]
 | 
			
		||||
    Run(*args)
 | 
			
		||||
 | 
			
		||||
@ -131,13 +131,21 @@ def Info(msg):
 | 
			
		||||
    """
 | 
			
		||||
    _Output(3, msg)
 | 
			
		||||
 | 
			
		||||
def Detail(msg):
 | 
			
		||||
    """Display a detailed message
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        msg; Message to display.
 | 
			
		||||
    """
 | 
			
		||||
    _Output(4, msg)
 | 
			
		||||
 | 
			
		||||
def Debug(msg):
 | 
			
		||||
    """Display a debug message
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        msg; Message to display.
 | 
			
		||||
    """
 | 
			
		||||
    _Output(4, msg)
 | 
			
		||||
    _Output(5, msg)
 | 
			
		||||
 | 
			
		||||
def UserOutput(msg):
 | 
			
		||||
    """Display a message regardless of the current output level.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user