]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
Merge git://git.denx.de/u-boot-dm
authorTom Rini <trini@konsulko.com>
Tue, 20 Dec 2016 13:42:04 +0000 (08:42 -0500)
committerTom Rini <trini@konsulko.com>
Tue, 20 Dec 2016 13:42:04 +0000 (08:42 -0500)
91 files changed:
Makefile
arch/arm/dts/sunxi-u-boot.dtsi [new file with mode: 0644]
arch/arm/dts/tegra124-nyan-big-u-boot.dtsi [new file with mode: 0644]
arch/arm/dts/tegra124-nyan-big.dts
arch/arm/dts/tegra20-u-boot.dtsi [new file with mode: 0644]
arch/arm/dts/tegra20.dtsi
arch/x86/dts/emulation-u-boot.dtsi [new file with mode: 0644]
arch/x86/dts/u-boot.dtsi [new file with mode: 0644]
scripts/Makefile.lib
tools/binman/.gitignore [new file with mode: 0644]
tools/binman/README [new file with mode: 0644]
tools/binman/binman [new symlink]
tools/binman/binman.py [new file with mode: 0755]
tools/binman/cmdline.py [new file with mode: 0644]
tools/binman/control.py [new file with mode: 0644]
tools/binman/entry_test.py [new file with mode: 0644]
tools/binman/etype/_testing.py [new file with mode: 0644]
tools/binman/etype/blob.py [new file with mode: 0644]
tools/binman/etype/entry.py [new file with mode: 0644]
tools/binman/etype/intel_cmc.py [new file with mode: 0644]
tools/binman/etype/intel_descriptor.py [new file with mode: 0644]
tools/binman/etype/intel_fsp.py [new file with mode: 0644]
tools/binman/etype/intel_me.py [new file with mode: 0644]
tools/binman/etype/intel_mrc.py [new file with mode: 0644]
tools/binman/etype/intel_vga.py [new file with mode: 0644]
tools/binman/etype/u_boot.py [new file with mode: 0644]
tools/binman/etype/u_boot_dtb.py [new file with mode: 0644]
tools/binman/etype/u_boot_dtb_with_ucode.py [new file with mode: 0644]
tools/binman/etype/u_boot_img.py [new file with mode: 0644]
tools/binman/etype/u_boot_nodtb.py [new file with mode: 0644]
tools/binman/etype/u_boot_spl.py [new file with mode: 0644]
tools/binman/etype/u_boot_spl_bss_pad.py [new file with mode: 0644]
tools/binman/etype/u_boot_spl_with_ucode_ptr.py [new file with mode: 0644]
tools/binman/etype/u_boot_ucode.py [new file with mode: 0644]
tools/binman/etype/u_boot_with_ucode_ptr.py [new file with mode: 0644]
tools/binman/etype/x86_start16.py [new file with mode: 0644]
tools/binman/etype/x86_start16_spl.py [new file with mode: 0644]
tools/binman/fdt_test.py [new file with mode: 0644]
tools/binman/func_test.py [new file with mode: 0644]
tools/binman/image.py [new file with mode: 0644]
tools/binman/test/01_invalid.dts [new file with mode: 0644]
tools/binman/test/02_missing_node.dts [new file with mode: 0644]
tools/binman/test/03_empty.dts [new file with mode: 0644]
tools/binman/test/04_invalid_entry.dts [new file with mode: 0644]
tools/binman/test/05_simple.dts [new file with mode: 0644]
tools/binman/test/06_dual_image.dts [new file with mode: 0644]
tools/binman/test/07_bad_align.dts [new file with mode: 0644]
tools/binman/test/08_pack.dts [new file with mode: 0644]
tools/binman/test/09_pack_extra.dts [new file with mode: 0644]
tools/binman/test/10_pack_align_power2.dts [new file with mode: 0644]
tools/binman/test/11_pack_align_size_power2.dts [new file with mode: 0644]
tools/binman/test/12_pack_inv_align.dts [new file with mode: 0644]
tools/binman/test/13_pack_inv_size_align.dts [new file with mode: 0644]
tools/binman/test/14_pack_overlap.dts [new file with mode: 0644]
tools/binman/test/15_pack_overflow.dts [new file with mode: 0644]
tools/binman/test/16_pack_image_overflow.dts [new file with mode: 0644]
tools/binman/test/17_pack_image_size.dts [new file with mode: 0644]
tools/binman/test/18_pack_image_align.dts [new file with mode: 0644]
tools/binman/test/19_pack_inv_image_align.dts [new file with mode: 0644]
tools/binman/test/20_pack_inv_image_align_power2.dts [new file with mode: 0644]
tools/binman/test/21_image_pad.dts [new file with mode: 0644]
tools/binman/test/22_image_name.dts [new file with mode: 0644]
tools/binman/test/23_blob.dts [new file with mode: 0644]
tools/binman/test/24_sorted.dts [new file with mode: 0644]
tools/binman/test/25_pack_zero_size.dts [new file with mode: 0644]
tools/binman/test/26_pack_u_boot_dtb.dts [new file with mode: 0644]
tools/binman/test/27_pack_4gb_no_size.dts [new file with mode: 0644]
tools/binman/test/28_pack_4gb_outside.dts [new file with mode: 0644]
tools/binman/test/29_x86-rom.dts [new file with mode: 0644]
tools/binman/test/30_x86-rom-me-no-desc.dts [new file with mode: 0644]
tools/binman/test/31_x86-rom-me.dts [new file with mode: 0644]
tools/binman/test/32_intel-vga.dts [new file with mode: 0644]
tools/binman/test/33_x86-start16.dts [new file with mode: 0644]
tools/binman/test/34_x86_ucode.dts [new file with mode: 0644]
tools/binman/test/35_x86_single_ucode.dts [new file with mode: 0644]
tools/binman/test/36_u_boot_img.dts [new file with mode: 0644]
tools/binman/test/37_x86_no_ucode.dts [new file with mode: 0644]
tools/binman/test/38_x86_ucode_missing_node.dts [new file with mode: 0644]
tools/binman/test/39_x86_ucode_missing_node2.dts [new file with mode: 0644]
tools/binman/test/40_x86_ucode_not_in_image.dts [new file with mode: 0644]
tools/binman/test/41_unknown_pos_size.dts [new file with mode: 0644]
tools/binman/test/42_intel-fsp.dts [new file with mode: 0644]
tools/binman/test/43_intel-cmc.dts [new file with mode: 0644]
tools/binman/test/44_x86_optional_ucode.dts [new file with mode: 0644]
tools/binman/test/descriptor.bin [new file with mode: 0644]
tools/binman/test/u_boot_no_ucode_ptr [new file with mode: 0755]
tools/binman/test/u_boot_no_ucode_ptr.c [new file with mode: 0644]
tools/binman/test/u_boot_ucode_ptr [new file with mode: 0755]
tools/binman/test/u_boot_ucode_ptr.c [new file with mode: 0644]
tools/binman/test/u_boot_ucode_ptr.lds [new file with mode: 0644]
tools/ifdtool.c

index 6a94504d5865b886c7087da6d586dc6987e54221..08749644f43ee41bca0e9a8119718c7afd42738b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -903,6 +903,12 @@ u-boot.ldr:        u-boot
                $(LDR) -T $(CONFIG_CPU) -c $@ $< $(LDR_FLAGS)
                $(BOARD_SIZE_CHECK)
 
+# binman
+# ---------------------------------------------------------------------------
+quiet_cmd_binman = BINMAN  $@
+cmd_binman = $(srctree)/tools/binman/binman -d u-boot.dtb -O . \
+               -I . -I $(srctree)/board/$(BOARDDIR) $<
+
 OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex
 
 OBJCOPYFLAGS_u-boot.ldr.srec := -I binary -O srec
@@ -1047,50 +1053,11 @@ endif
 
 # x86 uses a large ROM. We fill it with 0xff, put the 16-bit stuff (including
 # reset vector) at the top, Intel ME descriptor at the bottom, and U-Boot in
-# the middle.
+# the middle. This is handled by binman based on an image description in the
+# board's device tree.
 ifneq ($(CONFIG_X86_RESET_VECTOR),)
 rom: u-boot.rom FORCE
 
-IFDTOOL=$(objtree)/tools/ifdtool
-IFDTOOL_FLAGS  = -f 0:$(objtree)/u-boot.dtb
-IFDTOOL_FLAGS += -m 0x$(shell $(NM) u-boot |grep _dt_ucode_base_size |cut -d' ' -f1)
-IFDTOOL_FLAGS += -U $(CONFIG_SYS_TEXT_BASE):$(objtree)/u-boot-nodtb.bin
-IFDTOOL_FLAGS += -w $(CONFIG_SYS_X86_START16):$(objtree)/u-boot-x86-16bit.bin
-IFDTOOL_FLAGS += -C
-
-ifneq ($(CONFIG_HAVE_INTEL_ME),)
-IFDTOOL_ME_FLAGS  = -D $(srctree)/board/$(BOARDDIR)/descriptor.bin
-IFDTOOL_ME_FLAGS += -i ME:$(srctree)/board/$(BOARDDIR)/me.bin
-endif
-
-ifneq ($(CONFIG_HAVE_MRC),)
-IFDTOOL_FLAGS += -w $(CONFIG_X86_MRC_ADDR):$(srctree)/board/$(BOARDDIR)/mrc.bin
-endif
-
-ifneq ($(CONFIG_HAVE_FSP),)
-IFDTOOL_FLAGS += -w $(CONFIG_FSP_ADDR):$(srctree)/board/$(BOARDDIR)/$(CONFIG_FSP_FILE)
-endif
-
-ifneq ($(CONFIG_HAVE_CMC),)
-IFDTOOL_FLAGS += -w $(CONFIG_CMC_ADDR):$(srctree)/board/$(BOARDDIR)/$(CONFIG_CMC_FILE)
-endif
-
-ifneq ($(CONFIG_HAVE_VGA_BIOS),)
-IFDTOOL_FLAGS += -w $(CONFIG_VGA_BIOS_ADDR):$(srctree)/board/$(BOARDDIR)/$(CONFIG_VGA_BIOS_FILE)
-endif
-
-ifneq ($(CONFIG_HAVE_REFCODE),)
-IFDTOOL_FLAGS += -w $(CONFIG_X86_REFCODE_ADDR):refcode.bin
-endif
-
-quiet_cmd_ifdtool = IFDTOOL $@
-cmd_ifdtool  = $(IFDTOOL) -c -r $(CONFIG_ROM_SIZE) u-boot.tmp;
-ifneq ($(CONFIG_HAVE_INTEL_ME),)
-cmd_ifdtool += $(IFDTOOL) $(IFDTOOL_ME_FLAGS) u-boot.tmp;
-endif
-cmd_ifdtool += $(IFDTOOL) $(IFDTOOL_FLAGS) u-boot.tmp;
-cmd_ifdtool += mv u-boot.tmp $@
-
 refcode.bin: $(srctree)/board/$(BOARDDIR)/refcode.bin FORCE
        $(call if_changed,copy)
 
@@ -1100,7 +1067,7 @@ cmd_ldr = $(LD) $(LDFLAGS_$(@F)) \
 
 u-boot.rom: u-boot-x86-16bit.bin u-boot.bin FORCE \
                $(if $(CONFIG_HAVE_REFCODE),refcode.bin)
-       $(call if_changed,ifdtool)
+       $(call if_changed,binman)
 
 OBJCOPYFLAGS_u-boot-x86-16bit.bin := -O binary -j .start16 -j .resetvec
 u-boot-x86-16bit.bin: u-boot FORCE
@@ -1108,10 +1075,8 @@ u-boot-x86-16bit.bin: u-boot FORCE
 endif
 
 ifneq ($(CONFIG_ARCH_SUNXI),)
-OBJCOPYFLAGS_u-boot-sunxi-with-spl.bin = -I binary -O binary \
-                                  --pad-to=$(CONFIG_SPL_PAD_TO) --gap-fill=0xff
-u-boot-sunxi-with-spl.bin: spl/sunxi-spl.bin u-boot.img FORCE
-       $(call if_changed,pad_cat)
+u-boot-sunxi-with-spl.bin: spl/sunxi-spl.bin u-boot.img u-boot.dtb FORCE
+       $(call if_changed,binman)
 endif
 
 ifneq ($(CONFIG_TEGRA),)
diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi
new file mode 100644 (file)
index 0000000..5adfd9b
--- /dev/null
@@ -0,0 +1,14 @@
+#include <config.h>
+
+/ {
+       binman {
+               filename = "u-boot-sunxi-with-spl.bin";
+               pad-byte = <0xff>;
+               blob {
+                       filename = "spl/sunxi-spl.bin";
+               };
+               u-boot-img {
+                       pos = <CONFIG_SPL_PAD_TO>;
+               };
+       };
+};
diff --git a/arch/arm/dts/tegra124-nyan-big-u-boot.dtsi b/arch/arm/dts/tegra124-nyan-big-u-boot.dtsi
new file mode 100644 (file)
index 0000000..fff1d78
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+/ {
+       host1x@50000000 {
+               u-boot,dm-pre-reloc;
+               dc@54200000 {
+                       u-boot,dm-pre-reloc;
+               };
+       };
+};
index 3758395c6f7633c3bc0ad0820ad5000218aefb62..62f89d0f1a866ce6890ae8d3646e843008099c38 100644 (file)
@@ -27,9 +27,7 @@
        };
 
        host1x@50000000 {
-               u-boot,dm-pre-reloc;
                dc@54200000 {
-                       u-boot,dm-pre-reloc;
                        display-timings {
                                timing@0 {
                                        clock-frequency = <69500000>;
diff --git a/arch/arm/dts/tegra20-u-boot.dtsi b/arch/arm/dts/tegra20-u-boot.dtsi
new file mode 100644 (file)
index 0000000..9b9835d
--- /dev/null
@@ -0,0 +1,8 @@
+/ {
+       host1x@50000000 {
+               u-boot,dm-pre-reloc;
+               dc@54200000 {
+                       u-boot,dm-pre-reloc;
+               };
+       };
+};
index 84bb1b0215c8cf83ebc88b033027c055251379c5..e21ee258b3788cfdc77a0b22eb711706428bc127 100644 (file)
@@ -10,7 +10,6 @@
        interrupt-parent = <&lic>;
 
        host1x@50000000 {
-               u-boot,dm-pre-reloc;
                compatible = "nvidia,tegra20-host1x", "simple-bus";
                reg = <0x50000000 0x00024000>;
                interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>, /* syncpt */
@@ -78,7 +77,6 @@
                };
 
                dc@54200000 {
-                       u-boot,dm-pre-reloc;
                        compatible = "nvidia,tegra20-dc";
                        reg = <0x54200000 0x00040000>;
                        interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
diff --git a/arch/x86/dts/emulation-u-boot.dtsi b/arch/x86/dts/emulation-u-boot.dtsi
new file mode 100644 (file)
index 0000000..56d34af
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <u-boot.dtsi>
+
+#ifdef CONFIG_ROM_SIZE
+/ {
+       binman {
+               u-boot-with-ucode-ptr {
+                       optional-ucode;
+               };
+       };
+};
+#endif
diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi
new file mode 100644 (file)
index 0000000..724913f
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <config.h>
+
+#ifdef CONFIG_ROM_SIZE
+/ {
+       binman {
+               filename = "u-boot.rom";
+               end-at-4gb;
+               sort-by-pos;
+               pad-byte = <0xff>;
+               size = <CONFIG_ROM_SIZE>;
+#ifdef CONFIG_HAVE_INTEL_ME
+               intel-descriptor {
+               };
+               intel-me {
+               };
+#endif
+               u-boot-with-ucode-ptr {
+                       pos = <CONFIG_SYS_TEXT_BASE>;
+               };
+               u-boot-dtb-with-ucode {
+               };
+               u-boot-ucode {
+                       align = <16>;
+               };
+#ifdef CONFIG_HAVE_MRC
+               intel-mrc {
+                       pos = <CONFIG_X86_MRC_ADDR>;
+               };
+#endif
+#ifdef CONFIG_HAVE_FSP
+               intel-fsp {
+                       pos = <CONFIG_FSP_ADDR>;
+               };
+#endif
+#ifdef CONFIG_HAVE_CMC
+               intel-cmc {
+                       pos = <CONFIG_CMC_ADDR>;
+               };
+#endif
+#ifdef CONFIG_HAVE_VGA_BIOS
+               intel-vga {
+                       pos = <CONFIG_VGA_BIOS_ADDR>;
+               };
+#endif
+#ifdef CONFIG_HAVE_REFCODE
+               intel-refcode {
+                       pos = <CONFIG_X86_REFCODE_ADDR>;
+               };
+#endif
+               x86-start16 {
+                       pos = <CONFIG_SYS_X86_START16>;
+               };
+       };
+};
+#endif
index 956a8a9b04471fceabdc8ca659a577da6360af78..348de2dbf81ebb9928af4da99bd164e37699e4ac 100644 (file)
@@ -164,10 +164,30 @@ cpp_flags      = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(UBOOTINCLUDE)     \
 
 ld_flags       = $(LDFLAGS) $(ldflags-y)
 
+dts_dir = $(srctree)/arch/$(ARCH)/dts
+
+# Try these files in order to find the U-Boot-specific .dtsi include file
+u_boot_dtsi_options = $(wildcard $(dts_dir)/$(basename $(notdir $<))-u-boot.dtsi) \
+       $(wildcard $(dts_dir)/$(subst $\",,$(CONFIG_SYS_SOC))-u-boot.dtsi) \
+       $(wildcard $(dts_dir)/$(subst $\",,$(CONFIG_SYS_CPU))-u-boot.dtsi) \
+       $(wildcard $(dts_dir)/$(subst $\",,$(CONFIG_SYS_VENDOR))-u-boot.dtsi) \
+       $(wildcard $(dts_dir)/u-boot.dtsi)
+
+# Uncomment for debugging
+# $(warning u_boot_dtsi_options: $(u_boot_dtsi_options))
+
+# We use the first match
+u_boot_dtsi = $(firstword $(u_boot_dtsi_options))
+
 # Modified for U-Boot
 dtc_cpp_flags  = -Wp,-MD,$(depfile).pre.tmp -nostdinc                    \
                 -I$(srctree)/arch/$(ARCH)/dts                           \
                 -I$(srctree)/arch/$(ARCH)/dts/include                   \
+                -Iinclude                                               \
+                -I$(srctree)/include                                    \
+                -I$(srctree)/arch/$(ARCH)/include                       \
+                -include $(srctree)/include/linux/kconfig.h             \
+                -D__ASSEMBLY__                                          \
                 -undef -D__DTS__
 
 # Finds the multi-part object the current object will be linked into
@@ -288,8 +308,11 @@ $(obj)/%.dtb.S: $(obj)/%.dtb
 
 quiet_cmd_dtc = DTC     $@
 # Modified for U-Boot
+# Bring in any U-Boot-specific include after the '/dts-v1/;' header
 cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
-       $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
+       cat $< $(if $(u_boot_dtsi),\
+               | sed 's%^/ {$$%\#include \"$(u_boot_dtsi)\"\n&%')  | \
+               $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) - ; \
        $(DTC) -O dtb -o $@ -b 0 \
                -i $(dir $<) $(DTC_FLAGS) \
                -d $(depfile).dtc.tmp $(dtc-tmp) ; \
diff --git a/tools/binman/.gitignore b/tools/binman/.gitignore
new file mode 100644 (file)
index 0000000..0d20b64
--- /dev/null
@@ -0,0 +1 @@
+*.pyc
diff --git a/tools/binman/README b/tools/binman/README
new file mode 100644 (file)
index 0000000..cb47e73
--- /dev/null
@@ -0,0 +1,541 @@
+# Copyright (c) 2016 Google, Inc
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+Introduction
+------------
+
+Firmware often consists of several components which must be packaged together.
+For example, we may have SPL, U-Boot, a device tree and an environment area
+grouped together and placed in MMC flash. When the system starts, it must be
+able to find these pieces.
+
+So far U-Boot has not provided a way to handle creating such images in a
+general way. Each SoC does what it needs to build an image, often packing or
+concatenating images in the U-Boot build system.
+
+Binman aims to provide a mechanism for building images, from simple
+SPL + U-Boot combinations, to more complex arrangements with many parts.
+
+
+What it does
+------------
+
+Binman reads your board's device tree and finds a node which describes the
+required image layout. It uses this to work out what to place where. The
+output file normally contains the device tree, so it is in principle possible
+to read an image and extract its constituent parts.
+
+
+Features
+--------
+
+So far binman is pretty simple. It supports binary blobs, such as 'u-boot',
+'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can
+place entries at a fixed location in the image, or fit them together with
+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 also support incorporating filesystems in the image if required.
+For example x86 platforms may use CBFS in some cases.
+
+Binman is intended for use with U-Boot but is designed to be general enough
+to be useful in other image-packaging situations.
+
+
+Motivation
+----------
+
+Packaging of firmware is quite a different task from building the various
+parts. In many cases the various binaries which go into the image come from
+separate build systems. For example, ARM Trusted Firmware is used on ARMv8
+devices but is not built in the U-Boot tree. If a Linux kernel is included
+in the firmware image, it is built elsewhere.
+
+It is of course possible to add more and more build rules to the U-Boot
+build system to cover these cases. It can shell out to other Makefiles and
+build scripts. But it seems better to create a clear divide between building
+software and packaging it.
+
+At present this is handled by manual instructions, different for each board,
+on how to create images that will boot. By turning these instructions into a
+standard format, we can support making valid images for any board without
+manual effort, lots of READMEs, etc.
+
+Benefits:
+- Each binary can have its own build system and tool chain without creating
+any dependencies between them
+- Avoids the need for a single-shot build: individual parts can be updated
+and brought in as needed
+- Provides for a standard image description available in the build and at
+run-time
+- SoC-specific image-signing tools can be accomodated
+- Avoids cluttering the U-Boot build system with image-building code
+- The image description is automatically available at run-time in U-Boot,
+SPL. It can be made available to other software also
+- The image description is easily readable (it's a text file in device-tree
+format) and permits flexible packing of binaries
+
+
+Terminology
+-----------
+
+Binman uses the following terms:
+
+- image - an output file containing a firmware image
+- binary - an input binary that goes into the image
+
+
+Relationship to FIT
+-------------------
+
+FIT is U-Boot's official image format. It supports multiple binaries with
+load / execution addresses, compression. It also supports verification
+through hashing and RSA signatures.
+
+FIT was originally designed to support booting a Linux kernel (with an
+optional ramdisk) and device tree chosen from various options in the FIT.
+Now that U-Boot supports configuration via device tree, it is possible to
+load U-Boot from a FIT, with the device tree chosen by SPL.
+
+Binman considers FIT to be one of the binaries it can place in the image.
+
+Where possible it is best to put as much as possible in the FIT, with binman
+used to deal with cases not covered by FIT. Examples include initial
+execution (since FIT itself does not have an executable header) and dealing
+with device boundaries, such as the read-only/read-write separation in SPI
+flash.
+
+For U-Boot, binman should not be used to create ad-hoc images in place of
+FIT.
+
+
+Relationship to mkimage
+-----------------------
+
+The mkimage tool provides a means to create a FIT. Traditionally it has
+needed an image description file: a device tree, like binman, but in a
+different format. More recently it has started to support a '-f auto' mode
+which can generate that automatically.
+
+More relevant to binman, mkimage also permits creation of many SoC-specific
+image types. These can be listed by running 'mkimage -T list'. Examples
+include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often
+called from the U-Boot build system for this reason.
+
+Binman considers the output files created by mkimage to be binary blobs
+which it can place in an image. Binman does not replace the mkimage tool or
+this purpose. It would be possible in some situtions to create a new entry
+type for the images in mkimage, but this would not add functionality. It
+seems better to use the mkiamge tool to generate binaries and avoid blurring
+the boundaries between building input files (mkimage) and packaging then
+into a final image (binman).
+
+
+Example use of binman in U-Boot
+-------------------------------
+
+Binman aims to replace some of the ad-hoc image creation in the U-Boot
+build system.
+
+Consider sunxi. It has the following steps:
+
+1. It uses a custom mksunxiboot tool to build an SPL image called
+sunxi-spl.bin. This should probably move into mkimage.
+
+2. It uses mkimage to package U-Boot into a legacy image file (so that it can
+hold the load and execution address) called u-boot.img.
+
+3. It builds a final output image called u-boot-sunxi-with-spl.bin which
+consists of sunxi-spl.bin, some padding and u-boot.img.
+
+Binman is intended to replace the last step. The U-Boot build system builds
+u-boot.bin and sunxi-spl.bin. Binman can then take over creation of
+sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any
+case, it would then create the image from the component parts.
+
+This simplifies the U-Boot Makefile somewhat, since various pieces of logic
+can be replaced by a call to binman.
+
+
+Example use of binman for x86
+-----------------------------
+
+In most cases x86 images have a lot of binary blobs, 'black-box' code
+provided by Intel which must be run for the platform to work. Typically
+these blobs are not relocatable and must be placed at fixed areas in the
+firmare image.
+
+Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA
+BIOS, reference code and Intel ME binaries into a u-boot.rom file.
+
+Binman is intended to replace all of this, with ifdtool left to handle only
+the configuration of the Intel-format descriptor.
+
+
+Running binman
+--------------
+
+Type:
+
+       binman -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').
+Binman assumes that the input files for the build are in ../b/<board_name>.
+
+Or you can specify this explicitly:
+
+       binman -I <build_path>
+
+where <build_path> is the build directory containing the output of the U-Boot
+build.
+
+(Future work will make this more configurable)
+
+In either case, binman picks up the device tree file (u-boot.dtb) and looks
+for its instructions in the 'binman' node.
+
+Binman has a few other options which you can see by running 'binman -h'.
+
+
+Image description format
+------------------------
+
+The binman node is called 'binman'. An example image description is shown
+below:
+
+       binman {
+               filename = "u-boot-sunxi-with-spl.bin";
+               pad-byte = <0xff>;
+               blob {
+                       filename = "spl/sunxi-spl.bin";
+               };
+               u-boot {
+                       pos = <CONFIG_SPL_PAD_TO>;
+               };
+       };
+
+
+This requests binman to create an image file called u-boot-sunxi-with-spl.bin
+consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the
+normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The
+padding comes from the fact that the second binary is placed at
+CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would
+immediately follow the SPL binary.
+
+The binman node describes an image. The sub-nodes describe entries in the
+image. Each entry represents a region within the overall image. The name of
+the entry (blob, u-boot) tells binman what to put there. For 'blob' we must
+provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'.
+
+Entries are normally placed into the image sequentially, one after the other.
+The image size is the total size of all entries. As you can see, you can
+specify the start position of an entry using the 'pos' property.
+
+Note that due to a device tree requirement, all entries must have a unique
+name. If you want to put the same binary in the image multiple times, you can
+use any unique name, with the 'type' property providing the type.
+
+The attributes supported for entries are described below.
+
+pos:
+       This sets the position of an entry within the image. The first byte
+       of the image is normally at position 0. If 'pos' is not provided,
+       binman sets it to the end of the previous region, or the start of
+       the image's entry area (normally 0) if there is no previous region.
+
+align:
+       This sets the alignment of the entry. The entry position is adjusted
+       so that the entry starts on an aligned boundary within the image. For
+       example 'align = <16>' means that the entry will start on a 16-byte
+       boundary. Alignment shold be a power of 2. If 'align' is not
+       provided, no alignment is performed.
+
+size:
+       This sets the size of the entry. The contents will be padded out to
+       this size. If this is not provided, it will be set to the size of the
+       contents.
+
+pad-before:
+       Padding before the contents of the entry. Normally this is 0, meaning
+       that the contents start at the beginning of the entry. This can be
+       offset the entry contents a little. Defaults to 0.
+
+pad-after:
+       Padding after the contents of the entry. Normally this is 0, meaning
+       that the entry ends at the last byte of content (unless adjusted by
+       other properties). This allows room to be created in the image for
+       this entry to expand later. Defaults to 0.
+
+align-size:
+       This sets the alignment of the entry size. For example, to ensure
+       that the size of an entry is a multiple of 64 bytes, set this to 64.
+       If 'align-size' is not provided, no alignment is performed.
+
+align-end:
+       This sets the alignment of the end of an entry. Some entries require
+       that they end on an alignment boundary, regardless of where they
+       start. If 'align-end' is not provided, no alignment is performed.
+
+       Note: This is not yet implemented in binman.
+
+filename:
+       For 'blob' types this provides the filename containing the binary to
+       put into the entry. If binman knows about the entry type (like
+       u-boot-bin), then there is no need to specify this.
+
+type:
+       Sets the type of an entry. This defaults to the entry name, but it is
+       possible to use any name, and then add (for example) 'type = "u-boot"'
+       to specify the type.
+
+
+The attributes supported for images are described below. Several are similar
+to those for entries.
+
+size:
+       Sets the image size in bytes, for example 'size = <0x100000>' for a
+       1MB image.
+
+align-size:
+       This sets the alignment of the image size. For example, to ensure
+       that the image ends on a 512-byte boundary, use 'align-size = <512>'.
+       If 'align-size' is not provided, no alignment is performed.
+
+pad-before:
+       This sets the padding before the image entries. The first entry will
+       be positionad after the padding. This defaults to 0.
+
+pad-after:
+       This sets the padding after the image entries. The padding will be
+       placed after the last entry. This defaults to 0.
+
+pad-byte:
+       This specifies the pad byte to use when padding in the image. It
+       defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'.
+
+filename:
+       This specifies the image filename. It defaults to 'image.bin'.
+
+sort-by-pos:
+       This causes binman to reorder the entries as needed to make sure they
+       are in increasing positional order. This can be used when your entry
+       order may not match the positional order. A common situation is where
+       the 'pos' properties are set by CONFIG options, so their ordering is
+       not known a priori.
+
+       This is a boolean property so needs no value. To enable it, add a
+       line 'sort-by-pos;' to your description.
+
+multiple-images:
+       Normally only a single image is generated. To create more than one
+       image, put this property in the binman node. For example, this will
+       create image1.bin containing u-boot.bin, and image2.bin containing
+       both spl/u-boot-spl.bin and u-boot.bin:
+
+       binman {
+               multiple-images;
+               image1 {
+                       u-boot {
+                       };
+               };
+
+               image2 {
+                       spl {
+                       };
+                       u-boot {
+                       };
+               };
+       };
+
+end-at-4gb:
+       For x86 machines the ROM positions start just before 4GB and extend
+       up so that the image finished at the 4GB boundary. This boolean
+       option can be enabled to support this. The image size must be
+       provided so that binman knows when the image should start. For an
+       8MB ROM, the position of the first entry would be 0xfff80000 with
+       this option, instead of 0 without this option.
+
+
+Examples of the above options can be found in the tests. See the
+tools/binman/test directory.
+
+
+Special properties
+------------------
+
+Some entries support special properties, documented here:
+
+u-boot-with-ucode-ptr:
+       optional-ucode: boolean property to make microcode optional. If the
+               u-boot.bin image does not include microcode, no error will
+               be generated.
+
+
+Order of image creation
+-----------------------
+
+Image creation proceeds in the following order, for each entry in the image.
+
+1. GetEntryContents() - the contents of each entry are obtained, normally by
+reading from a file. This calls the Entry.ObtainContents() to read the
+contents. The default version of Entry.ObtainContents() calls
+Entry.GetDefaultFilename() and then reads that file. So a common mechanism
+to select a file to read is to override that function in the subclass. The
+functions must return True when they have read the contents. Binman will
+retry calling the functions a few times if False is returned, allowing
+dependencies between the contents of different entries.
+
+2. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can
+return a dict containing entries that need updating. The key should be the
+entry name and the value is a tuple (pos, size). This allows an entry to
+provide the position and size for other entries. The default implementation
+of GetEntryPositions() returns {}.
+
+3. PackEntries() - calls Entry.Pack() which figures out the position and
+size of an entry. The 'current' image position is passed in, and the function
+returns the position immediately after the entry being packed. The default
+implementation of Pack() is usually sufficient.
+
+4. CheckSize() - checks that the contents of all the entries fits within
+the image size. If the image does not have a defined size, the size is set
+large enough to hold all the entries.
+
+5. CheckEntries() - checks that the entries do not overlap, nor extend
+outside the image.
+
+6. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
+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 position and size of entries should not be adjusted.
+
+7. BuildImage() - builds the image and writes it to a file. This is the final
+step.
+
+
+Automatic .dtsi inclusion
+-------------------------
+
+It is sometimes inconvenient to add a 'binman' node to the .dts file for each
+board. This can be done by using #include to bring in a common file. Another
+approach supported by the U-Boot build system is to automatically include
+a common header. You can then put the binman node (and anything else that is
+specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header
+file.
+
+Binman will search for the following files in arch/<arch>/dts:
+
+   <dts>-u-boot.dtsi where <dts> is the base name of the .dts file
+   <CONFIG_SYS_SOC>-u-boot.dtsi
+   <CONFIG_SYS_CPU>-u-boot.dtsi
+   <CONFIG_SYS_VENDOR>-u-boot.dtsi
+   u-boot.dtsi
+
+U-Boot will only use the first one that it finds. If you need to include a
+more general file you can do that from the more specific file using #include.
+If you are having trouble figuring out what is going on, you can uncomment
+the 'warning' line in scripts/Makefile.lib to see what it has found:
+
+   # Uncomment for debugging
+   # $(warning binman_dtsi_options: $(binman_dtsi_options))
+
+
+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.
+
+To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
+
+   $ sudo apt-get install python-pip python-pytest
+   $ sudo pip install coverage
+
+
+Advanced Features / Technical docs
+----------------------------------
+
+The behaviour of entries is defined by the Entry class. All other entries are
+a subclass of this. An important subclass is Entry_blob which takes binary
+data from a file and places it in the entry. In fact most entry types are
+subclasses of Entry_blob.
+
+Each entry type is a separate file in the tools/binman/etype directory. Each
+file contains a class called Entry_<type> where <type> is the entry type.
+New entry types can be supported by adding new files in that directory.
+These will automatically be detected by binman when needed.
+
+Entry properties are documented in entry.py. The entry subclasses are free
+to change the values of properties to support special behaviour. For example,
+when Entry_blob loads a file, it sets content_size to the size of the file.
+Entry classes can adjust other entries. For example, an entry that knows
+where other entries should be positioned can set up those entries' positions
+so they don't need to be set in the binman decription. It can also adjust
+entry contents.
+
+Most of the time such essoteric behaviour is not needed, but it can be
+essential for complex images.
+
+
+History / Credits
+-----------------
+
+Binman takes a lot of inspiration from a Chrome OS tool called
+'cros_bundle_firmware', which I wrote some years ago. That tool was based on
+a reasonably simple and sound design but has expanded greatly over the
+years. In particular its handling of x86 images is convoluted.
+
+Quite a few lessons have been learned which are hopefully be applied here.
+
+
+Design notes
+------------
+
+On the face of it, a tool to create firmware images should be fairly simple:
+just find all the input binaries and place them at the right place in the
+image. The difficulty comes from the wide variety of input types (simple
+flat binaries containing code, packaged data with various headers), packing
+requirments (alignment, spacing, device boundaries) and other required
+features such as hierarchical images.
+
+The design challenge is to make it easy to create simple images, while
+allowing the more complex cases to be supported. For example, for most
+images we don't much care exactly where each binary ends up, so we should
+not have to specify that unnecessarily.
+
+New entry types should aim to provide simple usage where possible. If new
+core features are needed, they can be added in the Entry base class.
+
+
+To do
+-----
+
+Some ideas:
+- Fill out the device tree to include the final position and size of each
+  entry (since the input file may not always specify these)
+- Use of-platdata to make the information available to code that is unable
+  to use device tree (such as a very small SPL image)
+- Write an image map to a text file
+- Allow easy building of images by specifying just the board name
+- Produce a full Python binding for libfdt (for upstream)
+- Add an option to decode an image into the constituent binaries
+- Suppoort hierarchical images (packing of binaries into another binary
+  which is then placed in the image)
+- 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
+- Implement align-end
+
+--
+Simon Glass <sjg@chromium.org>
+7/7/2016
diff --git a/tools/binman/binman b/tools/binman/binman
new file mode 120000 (symlink)
index 0000000..979b7e4
--- /dev/null
@@ -0,0 +1 @@
+binman.py
\ No newline at end of file
diff --git a/tools/binman/binman.py b/tools/binman/binman.py
new file mode 100755 (executable)
index 0000000..7fb67cb
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+# Creates binary images from input files controlled by a description
+#
+
+"""See README for more information"""
+
+import os
+import sys
+import traceback
+import unittest
+
+# Bring in the patman and dtoc libraries
+our_path = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(our_path, '../patman'))
+sys.path.append(os.path.join(our_path, '../dtoc'))
+
+# Also allow entry-type modules to be brought in from the etype directory.
+sys.path.append(os.path.join(our_path, 'etype'))
+
+import cmdline
+import command
+import control
+
+def RunTests():
+    """Run the functional tests and any embedded doctests"""
+    import entry_test
+    import fdt_test
+    import func_test
+    import test
+    import doctest
+
+    result = unittest.TestResult()
+    for module in []:
+        suite = doctest.DocTestSuite(module)
+        suite.run(result)
+
+    sys.argv = [sys.argv[0]]
+    for module in (func_test.TestFunctional, fdt_test.TestFdt,
+                   entry_test.TestEntry):
+        suite = unittest.TestLoader().loadTestsFromTestCase(module)
+        suite.run(result)
+
+    print result
+    for test, err in result.errors:
+        print test.id(), err
+    for test, err in result.failures:
+        print err
+
+def RunTestCoverage():
+    """Run the tests and check that we get 100% coverage"""
+    # This uses the build output from sandbox_spl to get _libfdt.so
+    cmd = ('PYTHONPATH=%s/sandbox_spl/tools coverage run '
+            '--include "tools/binman/*.py" --omit "*test*,*binman.py" '
+            'tools/binman/binman.py -t' % options.build_dir)
+    os.system(cmd)
+    stdout = command.Output('coverage', 'report')
+    coverage = stdout.splitlines()[-1].split(' ')[-1]
+    if coverage != '100%':
+        print stdout
+        print "Type 'coverage html' to get a report in htmlcov/index.html"
+        raise ValueError('Coverage error: %s, but should be 100%%' % coverage)
+
+
+def RunBinman(options, args):
+    """Main entry point to binman once arguments are parsed
+
+    Args:
+        options: Command-line options
+        args: Non-option arguments
+    """
+    ret_code = 0
+
+    # For testing: This enables full exception traces.
+    #options.debug = True
+
+    if not options.debug:
+        sys.tracebacklimit = 0
+
+    if options.test:
+        RunTests()
+
+    elif options.test_coverage:
+        RunTestCoverage()
+
+    elif options.full_help:
+        pager = os.getenv('PAGER')
+        if not pager:
+            pager = 'more'
+        fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
+                            'README')
+        command.Run(pager, fname)
+
+    else:
+        try:
+            ret_code = control.Binman(options, args)
+        except Exception as e:
+            print 'binman: %s' % e
+            if options.debug:
+                print
+                traceback.print_exc()
+            ret_code = 1
+    return ret_code
+
+
+if __name__ == "__main__":
+    (options, args) = cmdline.ParseArgs(sys.argv)
+    ret_code = RunBinman(options, args)
+    sys.exit(ret_code)
diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py
new file mode 100644 (file)
index 0000000..233d5e1
--- /dev/null
@@ -0,0 +1,53 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Command-line parser for binman
+#
+
+from optparse import OptionParser
+
+def ParseArgs(argv):
+    """Parse the binman command-line arguments
+
+    Args:
+        argv: List of string arguments
+    Returns:
+        Tuple (options, args) with the command-line options and arugments.
+            options provides access to the options (e.g. option.debug)
+            args is a list of string arguments
+    """
+    parser = OptionParser()
+    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('-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',
+        default=False, help='Display the README file')
+    parser.add_option('-O', '--outdir', type='string',
+        action='store', help='Path to directory to use for intermediate and '
+        'output files')
+    parser.add_option('-p', '--preserve', action='store_true',\
+        help='Preserve temporary output directory even if option -O is not '
+             'given')
+    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('-v', '--verbosity', default=1,
+        type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
+        '4=debug')
+
+    parser.usage += """
+
+Create images for a board from a set of binaries. It is controlled by a
+description in the board device tree."""
+
+    return parser.parse_args(argv)
diff --git a/tools/binman/control.py b/tools/binman/control.py
new file mode 100644 (file)
index 0000000..e909678
--- /dev/null
@@ -0,0 +1,118 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Creates binary images from input files controlled by a description
+#
+
+from collections import OrderedDict
+import os
+import sys
+import tools
+
+import command
+import fdt_select
+import fdt_util
+from image import Image
+import tout
+
+# List of images we plan to create
+# Make this global so that it can be referenced from tests
+images = OrderedDict()
+
+def _ReadImageDesc(binman_node):
+    """Read the image descriptions from the /binman node
+
+    This normally produces a single Image object called 'image'. But if
+    multiple images are present, they will all be returned.
+
+    Args:
+        binman_node: Node object of the /binman node
+    Returns:
+        OrderedDict of Image objects, each of which describes an image
+    """
+    images = OrderedDict()
+    if 'multiple-images' in binman_node.props:
+        for node in binman_node.subnodes:
+            images[node.name] = Image(node.name, node)
+    else:
+        images['image'] = Image('image', binman_node)
+    return images
+
+def _FindBinmanNode(fdt):
+    """Find the 'binman' node in the device tree
+
+    Args:
+        fdt: Fdt object to scan
+    Returns:
+        Node object of /binman node, or None if not found
+    """
+    for node in fdt.GetRoot().subnodes:
+        if node.name == 'binman':
+            return node
+    return None
+
+def Binman(options, 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)
+    """
+    global images
+
+    if options.full_help:
+        pager = os.getenv('PAGER')
+        if not pager:
+            pager = 'more'
+        fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
+                            'README')
+        command.Run(pager, fname)
+        return 0
+
+    # Try to figure out which device tree contains our image description
+    if options.dt:
+        dtb_fname = options.dt
+    else:
+        board = options.board
+        if not board:
+            raise ValueError('Must provide a board to process (use -b <board>)')
+        board_pathname = os.path.join(options.build_dir, board)
+        dtb_fname = os.path.join(board_pathname, 'u-boot.dtb')
+        if not options.indir:
+            options.indir = ['.']
+        options.indir.append(board_pathname)
+
+    try:
+        tout.Init(options.verbosity)
+        try:
+            tools.SetInputDirs(options.indir)
+            tools.PrepareOutputDir(options.outdir, options.preserve)
+            fdt = fdt_select.FdtScan(dtb_fname)
+            node = _FindBinmanNode(fdt)
+            if not node:
+                raise ValueError("Device tree '%s' does not have a 'binman' "
+                                 "node" % dtb_fname)
+            images = _ReadImageDesc(node)
+            for image in images.values():
+                # Perform all steps for this image, including checking and
+                # writing it. This means that errors found with a later
+                # image will be reported after earlier images are already
+                # completed and written, but that does not seem important.
+                image.GetEntryContents()
+                image.GetEntryPositions()
+                image.PackEntries()
+                image.CheckSize()
+                image.CheckEntries()
+                image.ProcessEntryContents()
+                image.BuildImage()
+        finally:
+            tools.FinaliseOutputDir()
+    finally:
+        tout.Uninit()
+
+    return 0
diff --git a/tools/binman/entry_test.py b/tools/binman/entry_test.py
new file mode 100644 (file)
index 0000000..8a9ae01
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Test for the Entry class
+
+import collections
+import unittest
+
+import entry
+
+class TestEntry(unittest.TestCase):
+    def testEntryContents(self):
+        """Test the Entry bass class"""
+        base_entry = entry.Entry(None, None, None, read_node=False)
+        self.assertEqual(True, base_entry.ObtainContents())
+
+    def testUnknownEntry(self):
+        """Test that unknown entry types are detected"""
+        Node = collections.namedtuple('Node', ['name', 'path'])
+        node = Node('invalid-name', 'invalid-path')
+        with self.assertRaises(ValueError) as e:
+            entry.Entry.Create(None, node, node.name)
+        self.assertIn("Unknown entry type 'invalid-name' in node "
+                      "'invalid-path'", str(e.exception))
diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py
new file mode 100644 (file)
index 0000000..1783098
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for testing purposes. Not used in real images.
+#
+
+from entry import Entry
+import fdt_util
+import tools
+
+class Entry__testing(Entry):
+    def __init__(self, image, etype, node):
+        Entry.__init__(self, image, etype, node)
+
+    def ObtainContents(self):
+        self.data = 'a'
+        self.contents_size = len(self.data)
+        return True
+
+    def ReadContents(self):
+        return True
+
+    def GetPositions(self):
+        return {'invalid-entry': [1, 2]}
diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py
new file mode 100644 (file)
index 0000000..def2164
--- /dev/null
@@ -0,0 +1,37 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for blobs, which are binary objects read from files
+#
+
+from entry import Entry
+import fdt_util
+import tools
+
+class Entry_blob(Entry):
+    def __init__(self, image, etype, node):
+        Entry.__init__(self, image, etype, node)
+        self._filename = fdt_util.GetString(self._node, "filename", self.etype)
+
+    def ObtainContents(self):
+        self._filename = self.GetDefaultFilename()
+        self._pathname = tools.GetInputFilename(self._filename)
+        self.ReadContents()
+        return True
+
+    def ReadContents(self):
+        with open(self._pathname) as fd:
+            # 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.
+            self.data = fd.read()
+            self.contents_size = len(self.data)
+        return True
+
+    def GetDefaultFilename(self):
+        return self._filename
diff --git a/tools/binman/etype/entry.py b/tools/binman/etype/entry.py
new file mode 100644 (file)
index 0000000..67c5734
--- /dev/null
@@ -0,0 +1,200 @@
+# Copyright (c) 2016 Google, Inc
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Base class for all entries
+#
+
+# importlib was introduced in Python 2.7 but there was a report of it not
+# working in 2.7.12, so we work around this:
+# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
+try:
+    import importlib
+    have_importlib = True
+except:
+    have_importlib = False
+
+import fdt_util
+import tools
+
+modules = {}
+
+class Entry(object):
+    """An Entry in the image
+
+    An entry corresponds to a single node in the device-tree description
+    of the image. Each entry ends up being a part of the final image.
+    Entries can be placed either right next to each other, or with padding
+    between them. The type of the entry determines the data that is in it.
+
+    This class is not used by itself. All entry objects are subclasses of
+    Entry.
+
+    Attributes:
+        image: The image containing this entry
+        node: The node that created this entry
+        pos: Absolute position of entry within the image, None if not known
+        size: Entry size in bytes, None if not known
+        contents_size: Size of contents in bytes, 0 by default
+        align: Entry start position alignment, or None
+        align_size: Entry size alignment, or None
+        align_end: Entry end position alignment, or None
+        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)
+    """
+    def __init__(self, image, etype, node, read_node=True):
+        self.image = image
+        self.etype = etype
+        self._node = node
+        self.pos = None
+        self.size = None
+        self.contents_size = 0
+        self.align = None
+        self.align_size = None
+        self.align_end = None
+        self.pad_before = 0
+        self.pad_after = 0
+        self.pos_unset = False
+        if read_node:
+            self.ReadNode()
+
+    @staticmethod
+    def Create(image, node, etype=None):
+        """Create a new entry for a node.
+
+        Args:
+            image:  Image object containing this node
+            node:   Node object containing information about the entry to create
+            etype:  Entry type to use, or None to work it out (used for tests)
+
+        Returns:
+            A new Entry object of the correct type (a subclass of Entry)
+        """
+        if not etype:
+            etype = fdt_util.GetString(node, 'type', node.name)
+        module_name = etype.replace('-', '_')
+        module = modules.get(module_name)
+
+        # Import the module if we have not already done so.
+        if not module:
+            try:
+                if have_importlib:
+                    module = importlib.import_module(module_name)
+                else:
+                    module = __import__(module_name)
+            except ImportError:
+                raise ValueError("Unknown entry type '%s' in node '%s'" %
+                        (etype, node.path))
+            modules[module_name] = module
+
+        # Call its constructor to get the object we want.
+        obj = getattr(module, 'Entry_%s' % module_name)
+        return obj(image, etype, node)
+
+    def ReadNode(self):
+        """Read entry information from the node
+
+        This reads all the fields we recognise from the node, ready for use.
+        """
+        self.pos = fdt_util.GetInt(self._node, 'pos')
+        self.size = fdt_util.GetInt(self._node, '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" %
+                             (self._node.path, self.align))
+        self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
+        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.align_end = fdt_util.GetInt(self._node, 'align-end')
+        self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
+
+    def ObtainContents(self):
+        """Figure out the contents of an entry.
+
+        Returns:
+            True if the contents were found, False if another call is needed
+            after the other entries are processed.
+        """
+        # No contents by default: subclasses can implement this
+        return True
+
+    def Pack(self, pos):
+        """Figure out how to pack the entry into the image
+
+        Most of the time the entries are not fully specified. There may be
+        an alignment but no size. In that case we take the size from the
+        contents of the entry.
+
+        If an entry has no hard-coded position, it will be placed at @pos.
+
+        Once this function is complete, both the position and size of the
+        entry will be know.
+
+        Args:
+            Current image position pointer
+
+        Returns:
+            New image position pointer (after this entry)
+        """
+        if self.pos is None:
+            if self.pos_unset:
+                self.Raise('No position set with pos-unset: should another '
+                           'entry provide this correct position?')
+            self.pos = tools.Align(pos, self.align)
+        needed = self.pad_before + self.contents_size + self.pad_after
+        needed = tools.Align(needed, self.align_size)
+        size = self.size
+        if not size:
+            size = needed
+        new_pos = self.pos + size
+        aligned_pos = tools.Align(new_pos, self.align_end)
+        if aligned_pos != new_pos:
+            size = aligned_pos - self.pos
+            new_pos = aligned_pos
+
+        if not self.size:
+            self.size = size
+
+        if self.size < needed:
+            self.Raise("Entry contents size is %#x (%d) but entry size is "
+                       "%#x (%d)" % (needed, needed, self.size, self.size))
+        # Check that the alignment is correct. It could be wrong if the
+        # and pos or size values were provided (i.e. not calculated), but
+        # conflict with the provided alignment values
+        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))
+        if self.pos != tools.Align(self.pos, self.align):
+            self.Raise("Position %#x (%d) does not match align %#x (%d)" %
+                  (self.pos, self.pos, self.align, self.align))
+
+        return new_pos
+
+    def Raise(self, msg):
+        """Convenience function to raise an error referencing a node"""
+        raise ValueError("Node '%s': %s" % (self._node.path, msg))
+
+    def GetPath(self):
+        """Get the path of a node
+
+        Returns:
+            Full path of the node for this entry
+        """
+        return self._node.path
+
+    def GetData(self):
+        return self.data
+
+    def GetPositions(self):
+        return {}
+
+    def SetPositionSize(self, pos, size):
+        self.pos = pos
+        self.size = size
+
+    def ProcessContents(self):
+        pass
diff --git a/tools/binman/etype/intel_cmc.py b/tools/binman/etype/intel_cmc.py
new file mode 100644 (file)
index 0000000..9bce8ae
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Chip Microcode binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_cmc(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'cmc.bin'
diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py
new file mode 100644 (file)
index 0000000..7f4ea0b
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for 'u-boot'
+#
+
+import struct
+
+from entry import Entry
+from blob import Entry_blob
+
+FD_SIGNATURE   = struct.pack('<L', 0x0ff0a55a)
+MAX_REGIONS    = 5
+
+(REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE,
+        REGION_PDATA) = range(5)
+
+class Region:
+    def __init__(self, data, frba, region_num):
+        pos = frba + region_num * 4
+        val = struct.unpack('<L', data[pos:pos + 4])[0]
+        self.base = (val & 0xfff) << 12
+        self.limit = ((val & 0x0fff0000) >> 4) | 0xfff
+        self.size = self.limit - self.base + 1
+
+class Entry_intel_descriptor(Entry_blob):
+    """Intel flash descriptor block (4KB)
+
+    This is placed at the start of flash and provides information about
+    the SPI flash regions. In particular it provides the base address and
+    size of the ME region, allowing us to place the ME binary in the right
+    place.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self._regions = []
+
+    def GetDefaultFilename(self):
+        return 'descriptor.bin'
+
+    def GetPositions(self):
+        pos = self.data.find(FD_SIGNATURE)
+        if pos == -1:
+            self.Raise('Cannot find FD signature')
+        flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL',
+                                                    self.data[pos:pos + 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]}
diff --git a/tools/binman/etype/intel_fsp.py b/tools/binman/etype/intel_fsp.py
new file mode 100644 (file)
index 0000000..d75be5b
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Firmware Support Package binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_fsp(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'fsp.bin'
diff --git a/tools/binman/etype/intel_me.py b/tools/binman/etype/intel_me.py
new file mode 100644 (file)
index 0000000..45ab50c
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Management Engine binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_me(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'me.bin'
diff --git a/tools/binman/etype/intel_mrc.py b/tools/binman/etype/intel_mrc.py
new file mode 100644 (file)
index 0000000..f6cedb7
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for Intel Memory Reference Code binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_mrc(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'mrc.bin'
diff --git a/tools/binman/etype/intel_vga.py b/tools/binman/etype/intel_vga.py
new file mode 100644 (file)
index 0000000..d8f270b
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for x86 VGA ROM binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_vga(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'vga.bin'
diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py
new file mode 100644 (file)
index 0000000..1fcff73
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for U-Boot binary
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot.bin'
diff --git a/tools/binman/etype/u_boot_dtb.py b/tools/binman/etype/u_boot_dtb.py
new file mode 100644 (file)
index 0000000..1122c95
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for U-Boot device tree
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_dtb(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot.dtb'
diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py
new file mode 100644 (file)
index 0000000..fc02c67
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright (c) 2016 Google, Inc
+## Written by Simon Glass <sjg@chromium.org>
+
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for U-Boot device tree with the microcode removed
+#
+
+import fdt_select
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_dtb_with_ucode(Entry_blob):
+    """A U-Boot device tree file, with the microcode removed
+
+    See Entry_u_boot_ucode for full details of the 3 entries involved in this
+    process.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self.ucode_data = ''
+        self.collate = False
+        self.ucode_offset = None
+        self.ucode_size = None
+
+    def GetDefaultFilename(self):
+        return 'u-boot.dtb'
+
+    def ObtainContents(self):
+        Entry_blob.ObtainContents(self)
+
+        # If the image does not need microcode, there is nothing to do
+        ucode_dest_entry = self.image.FindEntryType('u-boot-spl-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_pos:
+            ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_pos:
+            return True
+
+        # Create a new file to hold the copied device tree
+        dtb_name = 'u-boot-dtb-with-ucode.dtb'
+        fname = tools.GetOutputFilename(dtb_name)
+        with open(fname, 'wb') as fd:
+            fd.write(self.data)
+
+        # Remove the microcode
+        fdt = fdt_select.FdtScan(fname)
+        fdt.Scan()
+        ucode = fdt.GetNode('/microcode')
+        if not ucode:
+            raise self.Raise("No /microcode node found in '%s'" % fname)
+
+        # There's no need to collate it (move all microcode into one place)
+        # if we only have one chunk of microcode.
+        self.collate = len(ucode.subnodes) > 1
+        for node in ucode.subnodes:
+            data_prop = node.props.get('data')
+            if data_prop:
+                self.ucode_data += ''.join(data_prop.bytes)
+                if not self.collate:
+                    poffset = data_prop.GetOffset()
+                    if poffset is None:
+                        # We cannot obtain a property offset. Collate instead.
+                        self.collate = True
+                    else:
+                        # Find the offset in the device tree of the ucode data
+                        self.ucode_offset = poffset + 12
+                        self.ucode_size = len(data_prop.bytes)
+                if self.collate:
+                    prop = node.DeleteProp('data')
+        if self.collate:
+            fdt.Pack()
+            fdt.Flush()
+
+            # Make this file the contents of this entry
+            self._pathname = fname
+            self.ReadContents()
+        return True
diff --git a/tools/binman/etype/u_boot_img.py b/tools/binman/etype/u_boot_img.py
new file mode 100644 (file)
index 0000000..744f1b4
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for U-Boot binary
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_img(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot.img'
diff --git a/tools/binman/etype/u_boot_nodtb.py b/tools/binman/etype/u_boot_nodtb.py
new file mode 100644 (file)
index 0000000..3721c3b
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for 'u-boot-nodtb.bin'
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_nodtb(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot-nodtb.bin'
diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py
new file mode 100644 (file)
index 0000000..68b0148
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for spl/u-boot-spl.bin
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_spl(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl.bin'
diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py
new file mode 100644 (file)
index 0000000..c005f28
--- /dev/null
@@ -0,0 +1,26 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for BSS padding for spl/u-boot-spl.bin. This padding
+# can be added after the SPL binary to ensure that anything concatenated
+# to it will appear to SPL to be at the end of BSS rather than the start.
+#
+
+import command
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_spl_bss_pad(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def ObtainContents(self):
+        fname = tools.GetInputFilename('spl/u-boot-spl')
+        args = [['nm', fname], ['grep', '__bss_size']]
+        out = command.RunPipe(args, capture=True).stdout.splitlines()
+        bss_size = int(out[0].split()[0], 16)
+        self.data = chr(0) * bss_size
+        self.contents_size = bss_size
diff --git a/tools/binman/etype/u_boot_spl_with_ucode_ptr.py b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py
new file mode 100644 (file)
index 0000000..764c282
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for an SPL binary with an embedded microcode pointer
+#
+
+import struct
+
+import command
+from entry import Entry
+from blob import Entry_blob
+from u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr
+import tools
+
+class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr):
+    """U-Boot SPL with embedded microcode pointer
+
+    See Entry_u_boot_ucode for full details of the entries involved in this
+    process.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self.elf_fname = 'spl/u-boot-spl'
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl.bin'
diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py
new file mode 100644 (file)
index 0000000..8fe27ac
--- /dev/null
@@ -0,0 +1,84 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for a U-Boot binary with an embedded microcode pointer
+#
+
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_ucode(Entry_blob):
+    """U-Boot microcode block
+
+    U-Boot on x86 needs a single block of microcode. This is collected from
+    the various microcode update nodes in the device tree. It is also unable
+    to read the microcode from the device tree on platforms that use FSP
+    (Firmware Support Package) binaries, because the API requires that the
+    microcode is supplied before there is any SRAM available to use (i.e.
+    the FSP sets up the SRAM / cache-as-RAM but does so in the call that
+    requires the microcode!). To keep things simple, all x86 platforms handle
+    microcode the same way in U-Boot (even non-FSP platforms). This is that
+    a table is placed at _dt_ucode_base_size containing the base address and
+    size of the microcode. This is either passed to the FSP (for FSP
+    platforms), or used to set up the microcode (for non-FSP platforms).
+    This all happens in the build system since it is the only way to get
+    the microcode into a single blob and accessible without SRAM.
+
+    There are two cases to handle. If there is only one microcode blob in
+    the device tree, then the ucode pointer it set to point to that. This
+    entry (u-boot-ucode) is empty. If there is more than one update, then
+    this entry holds the concatenation of all updates, and the device tree
+    entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This
+    last step ensures that that the microcode appears in one contiguous
+    block in the image and is not unnecessarily duplicated in the device
+    tree. It is referred to as 'collation' here.
+
+    Entry types that have a part to play in handling microcode:
+
+        Entry_u_boot_with_ucode_ptr:
+            Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree).
+            It updates it with the address and size of the microcode so that
+            U-Boot can find it early on start-up.
+        Entry_u_boot_dtb_with_ucode:
+            Contains u-boot.dtb. It stores the microcode in a
+            'self.ucode_data' property, which is then read by this class to
+            obtain the microcode if needed. If collation is performed, it
+            removes the microcode from the device tree.
+        Entry_u_boot_ucode:
+            This class. If collation is enabled it reads the microcode from
+            the Entry_u_boot_dtb_with_ucode entry, and uses it as the
+            contents of this entry.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def ObtainContents(self):
+        # If the image does not need microcode, there is nothing to do
+        ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr')
+        if ucode_dest_entry and not ucode_dest_entry.target_pos:
+            self.data = ''
+            return True
+
+        # Get the microcode from the device tree entry
+        fdt_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+        if not fdt_entry or not fdt_entry.ucode_data:
+            return False
+
+        if not fdt_entry.collate:
+            # This section can be empty
+            self.data = ''
+            return True
+
+        # Write it out to a file
+        dtb_name = 'u-boot-ucode.bin'
+        fname = tools.GetOutputFilename(dtb_name)
+        with open(fname, 'wb') as fd:
+            fd.write(fdt_entry.ucode_data)
+
+        self._pathname = fname
+        self.ReadContents()
+
+        return True
diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py
new file mode 100644 (file)
index 0000000..6f01adb
--- /dev/null
@@ -0,0 +1,87 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for a U-Boot binary with an embedded microcode pointer
+#
+
+import struct
+
+import command
+from entry import Entry
+from blob import Entry_blob
+import fdt_util
+import tools
+
+class Entry_u_boot_with_ucode_ptr(Entry_blob):
+    """U-Boot with embedded microcode pointer
+
+    See Entry_u_boot_ucode for full details of the 3 entries involved in this
+    process.
+    """
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+        self.elf_fname = 'u-boot'
+        self.target_pos = None
+
+    def GetDefaultFilename(self):
+        return 'u-boot-nodtb.bin'
+
+    def ObtainContents(self):
+        # Figure out where to put the microcode pointer
+        fname = tools.GetInputFilename(self.elf_fname)
+        args = [['nm', fname], ['grep', '-w', '_dt_ucode_base_size']]
+        out = (command.RunPipe(args, capture=True, raise_on_error=False).
+               stdout.splitlines())
+        if len(out) == 1:
+            self.target_pos = int(out[0].split()[0], 16)
+        elif not fdt_util.GetBool(self._node, 'optional-ucode'):
+            self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
+
+        return Entry_blob.ObtainContents(self)
+
+    def ProcessContents(self):
+        # If the image does not need microcode, there is nothing to do
+        if not self.target_pos:
+            return
+
+        # Get the position of the microcode
+        ucode_entry = self.image.FindEntryType('u-boot-ucode')
+        if not ucode_entry:
+            self.Raise('Cannot find microcode region u-boot-ucode')
+
+        # Check the target pos is in the image. If it is not, then U-Boot is
+        # being linked incorrectly, or is being placed at the wrong position
+        # in the image.
+        #
+        # The image must be set up so that U-Boot is placed at the
+        # flash address to which it is linked. For example, if
+        # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then
+        # the U-Boot region must start at position 7MB in the image. In this
+        # case the ROM starts at 0xff800000, so the position of the first
+        # entry in the image corresponds to that.
+        if (self.target_pos < self.pos or
+                self.target_pos >= self.pos + self.size):
+            self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
+                'outside the image ranging from %08x to %08x' %
+                (self.target_pos, self.pos, self.pos + self.size))
+
+        # Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode.
+        # If we have left the microcode in the device tree, then it will be
+        # in the former. If we extracted the microcode from the device tree
+        # and collated it in one place, it will be in the latter.
+        if ucode_entry.size:
+            pos, size = ucode_entry.pos, ucode_entry.size
+        else:
+            dtb_entry = self.image.FindEntryType('u-boot-dtb-with-ucode')
+            if not dtb_entry:
+                self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
+            pos = dtb_entry.pos + dtb_entry.ucode_offset
+            size = dtb_entry.ucode_size
+
+        # Write the microcode position and size into the entry
+        pos_and_size = struct.pack('<2L', pos, size)
+        self.target_pos -= self.pos
+        self.data = (self.data[:self.target_pos] + pos_and_size +
+                     self.data[self.target_pos + 8:])
diff --git a/tools/binman/etype/x86_start16.py b/tools/binman/etype/x86_start16.py
new file mode 100644 (file)
index 0000000..a44ea68
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for the 16-bit x86 start-up code for U-Boot
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_x86_start16(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot-x86-16bit.bin'
diff --git a/tools/binman/etype/x86_start16_spl.py b/tools/binman/etype/x86_start16_spl.py
new file mode 100644 (file)
index 0000000..3679a43
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Entry-type module for the 16-bit x86 start-up code for U-Boot SPL
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_x86_start16_spl(Entry_blob):
+    def __init__(self, image, etype, node):
+        Entry_blob.__init__(self, image, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-x86-16bit-spl.bin'
diff --git a/tools/binman/fdt_test.py b/tools/binman/fdt_test.py
new file mode 100644 (file)
index 0000000..1d9494e
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Test for the fdt modules
+
+import os
+import sys
+import tempfile
+import unittest
+
+from fdt_select import FdtScan
+import fdt_util
+import tools
+
+class TestFdt(unittest.TestCase):
+    @classmethod
+    def setUpClass(self):
+        self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+        self._indir = tempfile.mkdtemp(prefix='binmant.')
+        tools.PrepareOutputDir(self._indir, True)
+
+    def TestFile(self, fname):
+        return os.path.join(self._binman_dir, 'test', fname)
+
+    def GetCompiled(self, fname):
+        return fdt_util.EnsureCompiled(self.TestFile(fname))
+
+    def _DeleteProp(self, fdt):
+        node = fdt.GetNode('/microcode/update@0')
+        node.DeleteProp('data')
+
+    def testFdtNormal(self):
+        fname = self.GetCompiled('34_x86_ucode.dts')
+        fdt = FdtScan(fname)
+        self._DeleteProp(fdt)
+
+    def testFdtFallback(self):
+        fname = self.GetCompiled('34_x86_ucode.dts')
+        fdt = FdtScan(fname, True)
+        fdt.GetProp('/microcode/update@0', 'data')
+        self.assertEqual('fred',
+            fdt.GetProp('/microcode/update@0', 'none', default='fred'))
+        self.assertEqual('12345678 12345679',
+            fdt.GetProp('/microcode/update@0', 'data', typespec='x'))
+        self._DeleteProp(fdt)
diff --git a/tools/binman/func_test.py b/tools/binman/func_test.py
new file mode 100644 (file)
index 0000000..740fa9e
--- /dev/null
@@ -0,0 +1,822 @@
+#
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# To run a single test, change to this directory, and:
+#
+#    python -m unittest func_test.TestFunctional.testHelp
+
+from optparse import OptionParser
+import os
+import shutil
+import struct
+import sys
+import tempfile
+import unittest
+
+import binman
+import cmdline
+import command
+import control
+import entry
+import fdt_select
+import fdt_util
+import tools
+import tout
+
+# Contents of test files, corresponding to different entry types
+U_BOOT_DATA         = '1234'
+U_BOOT_IMG_DATA     = 'img'
+U_BOOT_SPL_DATA     = '567'
+BLOB_DATA           = '89'
+ME_DATA             = '0abcd'
+VGA_DATA            = 'vga'
+U_BOOT_DTB_DATA     = 'udtb'
+X86_START16_DATA    = 'start16'
+U_BOOT_NODTB_DATA   = 'nodtb with microcode pointer somewhere in here'
+FSP_DATA            = 'fsp'
+CMC_DATA            = 'cmc'
+
+class TestFunctional(unittest.TestCase):
+    """Functional tests for binman
+
+    Most of these use a sample .dts file to build an image and then check
+    that it looks correct. The sample files are in the test/ subdirectory
+    and are numbered.
+
+    For each entry type a very small test file is created using fixed
+    string contents. This makes it easy to test that things look right, and
+    debug problems.
+
+    In some cases a 'real' file must be used - these are also supplied in
+    the test/ diurectory.
+    """
+    @classmethod
+    def setUpClass(self):
+        # Handle the case where argv[0] is 'python'
+        self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+        self._binman_pathname = os.path.join(self._binman_dir, 'binman')
+
+        # Create a temporary directory for input files
+        self._indir = tempfile.mkdtemp(prefix='binmant.')
+
+        # Create some test files
+        TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
+        TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
+        TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
+        TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
+        TestFunctional._MakeInputFile('me.bin', ME_DATA)
+        TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
+        TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
+        TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
+        TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
+        TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
+        TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
+        self._output_setup = False
+
+        # ELF file with a '_dt_ucode_base_size' symbol
+        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
+            TestFunctional._MakeInputFile('u-boot', fd.read())
+
+        # Intel flash descriptor file
+        with open(self.TestFile('descriptor.bin')) as fd:
+            TestFunctional._MakeInputFile('descriptor.bin', fd.read())
+
+    @classmethod
+    def tearDownClass(self):
+        """Remove the temporary input directory and its contents"""
+        if self._indir:
+            shutil.rmtree(self._indir)
+        self._indir = None
+
+    def setUp(self):
+        # Enable this to turn on debugging output
+        # tout.Init(tout.DEBUG)
+        command.test_result = None
+
+    def tearDown(self):
+        """Remove the temporary output directory"""
+        tools._FinaliseForTest()
+
+    def _RunBinman(self, *args, **kwargs):
+        """Run binman using the command line
+
+        Args:
+            Arguments to pass, as a list of strings
+            kwargs: Arguments to pass to Command.RunPipe()
+        """
+        result = command.RunPipe([[self._binman_pathname] + list(args)],
+                capture=True, capture_stderr=True, raise_on_error=False)
+        if result.return_code and kwargs.get('raise_on_error', True):
+            raise Exception("Error running '%s': %s" % (' '.join(args),
+                            result.stdout + result.stderr))
+        return result
+
+    def _DoBinman(self, *args):
+        """Run binman using directly (in the same process)
+
+        Args:
+            Arguments to pass, as a list of strings
+        Returns:
+            Return value (0 for success)
+        """
+        (options, args) = cmdline.ParseArgs(list(args))
+        options.pager = 'binman-invalid-pager'
+        options.build_dir = self._indir
+
+        # For testing, you can force an increase in verbosity here
+        # options.verbosity = tout.DEBUG
+        return control.Binman(options, args)
+
+    def _DoTestFile(self, fname):
+        """Run binman with a given test file
+
+        Args:
+            fname: Device tree source filename to use (e.g. 05_simple.dts)
+        """
+        return self._DoBinman('-p', '-I', self._indir,
+                              '-d', self.TestFile(fname))
+
+    def _SetupDtb(self, fname, outfile='u-boot.dtb'):
+        """Set up a new test device-tree file
+
+        The given file is compiled and set up as the device tree to be used
+        for ths test.
+
+        Args:
+            fname: Filename of .dts file to read
+            outfile: Output filename for compiled device tree binary
+
+        Returns:
+            Contents of device tree binary
+        """
+        if not self._output_setup:
+            tools.PrepareOutputDir(self._indir, True)
+            self._output_setup = True
+        dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
+        with open(dtb) as fd:
+            data = fd.read()
+            TestFunctional._MakeInputFile(outfile, data)
+            return data
+
+    def _DoReadFileDtb(self, fname, use_real_dtb=False):
+        """Run binman and return the resulting image
+
+        This runs binman with a given test file and then reads the resulting
+        output file. It is a shortcut function since most tests need to do
+        these steps.
+
+        Raises an assertion failure if binman returns a non-zero exit code.
+
+        Args:
+            fname: Device tree source filename to use (e.g. 05_simple.dts)
+            use_real_dtb: True to use the test file as the contents of
+                the u-boot-dtb entry. Normally this is not needed and the
+                test contents (the U_BOOT_DTB_DATA string) can be used.
+                But in some test we need the real contents.
+
+        Returns:
+            Tuple:
+                Resulting image contents
+                Device tree contents
+        """
+        dtb_data = None
+        # Use the compiled test file as the u-boot-dtb input
+        if use_real_dtb:
+            dtb_data = self._SetupDtb(fname)
+
+        try:
+            retcode = self._DoTestFile(fname)
+            self.assertEqual(0, retcode)
+
+            # Find the (only) image, read it and return its contents
+            image = control.images['image']
+            fname = tools.GetOutputFilename('image.bin')
+            self.assertTrue(os.path.exists(fname))
+            with open(fname) as fd:
+                return fd.read(), dtb_data
+        finally:
+            # Put the test file back
+            if use_real_dtb:
+                TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
+
+    def _DoReadFile(self, fname, use_real_dtb=False):
+        """Helper function which discards the device-tree binary"""
+        return self._DoReadFileDtb(fname, use_real_dtb)[0]
+
+    @classmethod
+    def _MakeInputFile(self, fname, contents):
+        """Create a new test input file, creating directories as needed
+
+        Args:
+            fname: Filenaem to create
+            contents: File contents to write in to the file
+        Returns:
+            Full pathname of file created
+        """
+        pathname = os.path.join(self._indir, fname)
+        dirname = os.path.dirname(pathname)
+        if dirname and not os.path.exists(dirname):
+            os.makedirs(dirname)
+        with open(pathname, 'wb') as fd:
+            fd.write(contents)
+        return pathname
+
+    @classmethod
+    def TestFile(self, fname):
+        return os.path.join(self._binman_dir, 'test', fname)
+
+    def AssertInList(self, grep_list, target):
+        """Assert that at least one of a list of things is in a target
+
+        Args:
+            grep_list: List of strings to check
+            target: Target string
+        """
+        for grep in grep_list:
+            if grep in target:
+                return
+        self.fail("Error: '%' not found in '%s'" % (grep_list, target))
+
+    def CheckNoGaps(self, entries):
+        """Check that all entries fit together without gaps
+
+        Args:
+            entries: List of entries to check
+        """
+        pos = 0
+        for entry in entries.values():
+            self.assertEqual(pos, entry.pos)
+            pos += entry.size
+
+    def GetFdtLen(self, dtb):
+        """Get the totalsize field from a device tree binary
+
+        Args:
+            dtb: Device tree binary contents
+
+        Returns:
+            Total size of device tree binary, from the header
+        """
+        return struct.unpack('>L', dtb[4:8])[0]
+
+    def testRun(self):
+        """Test a basic run with valid args"""
+        result = self._RunBinman('-h')
+
+    def testFullHelp(self):
+        """Test that the full help is displayed with -H"""
+        result = self._RunBinman('-H')
+        help_file = os.path.join(self._binman_dir, 'README')
+        self.assertEqual(len(result.stdout), os.path.getsize(help_file))
+        self.assertEqual(0, len(result.stderr))
+        self.assertEqual(0, result.return_code)
+
+    def testFullHelpInternal(self):
+        """Test that the full help is displayed with -H"""
+        try:
+            command.test_result = command.CommandResult()
+            result = self._DoBinman('-H')
+            help_file = os.path.join(self._binman_dir, 'README')
+        finally:
+            command.test_result = None
+
+    def testHelp(self):
+        """Test that the basic help is displayed with -h"""
+        result = self._RunBinman('-h')
+        self.assertTrue(len(result.stdout) > 200)
+        self.assertEqual(0, len(result.stderr))
+        self.assertEqual(0, result.return_code)
+
+    # Not yet available.
+    def testBoard(self):
+        """Test that we can run it with a specific board"""
+        self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
+        TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
+        result = self._DoBinman('-b', 'sandbox')
+        self.assertEqual(0, result)
+
+    def testNeedBoard(self):
+        """Test that we get an error when no board ius supplied"""
+        with self.assertRaises(ValueError) as e:
+            result = self._DoBinman()
+        self.assertIn("Must provide a board to process (use -b <board>)",
+                str(e.exception))
+
+    def testMissingDt(self):
+        """Test that an invalid device tree file generates an error"""
+        with self.assertRaises(Exception) as e:
+            self._RunBinman('-d', 'missing_file')
+        # We get one error from libfdt, and a different one from fdtget.
+        self.AssertInList(["Couldn't open blob from 'missing_file'",
+                           'No such file or directory'], str(e.exception))
+
+    def testBrokenDt(self):
+        """Test that an invalid device tree source file generates an error
+
+        Since this is a source file it should be compiled and the error
+        will come from the device-tree compiler (dtc).
+        """
+        with self.assertRaises(Exception) as e:
+            self._RunBinman('-d', self.TestFile('01_invalid.dts'))
+        self.assertIn("FATAL ERROR: Unable to parse input tree",
+                str(e.exception))
+
+    def testMissingNode(self):
+        """Test that a device tree without a 'binman' node generates an error"""
+        with self.assertRaises(Exception) as e:
+            self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
+        self.assertIn("does not have a 'binman' node", str(e.exception))
+
+    def testEmpty(self):
+        """Test that an empty binman node works OK (i.e. does nothing)"""
+        result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
+        self.assertEqual(0, len(result.stderr))
+        self.assertEqual(0, result.return_code)
+
+    def testInvalidEntry(self):
+        """Test that an invalid entry is flagged"""
+        with self.assertRaises(Exception) as e:
+            result = self._RunBinman('-d',
+                                     self.TestFile('04_invalid_entry.dts'))
+        #print e.exception
+        self.assertIn("Unknown entry type 'not-a-valid-type' in node "
+                "'/binman/not-a-valid-type'", str(e.exception))
+
+    def testSimple(self):
+        """Test a simple binman with a single file"""
+        data = self._DoReadFile('05_simple.dts')
+        self.assertEqual(U_BOOT_DATA, data)
+
+    def testDual(self):
+        """Test that we can handle creating two images
+
+        This also tests image padding.
+        """
+        retcode = self._DoTestFile('06_dual_image.dts')
+        self.assertEqual(0, retcode)
+
+        image = control.images['image1']
+        self.assertEqual(len(U_BOOT_DATA), image._size)
+        fname = tools.GetOutputFilename('image1.bin')
+        self.assertTrue(os.path.exists(fname))
+        with open(fname) as fd:
+            data = fd.read()
+            self.assertEqual(U_BOOT_DATA, data)
+
+        image = control.images['image2']
+        self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
+        fname = tools.GetOutputFilename('image2.bin')
+        self.assertTrue(os.path.exists(fname))
+        with open(fname) as fd:
+            data = fd.read()
+            self.assertEqual(U_BOOT_DATA, data[3:7])
+            self.assertEqual(chr(0) * 3, data[:3])
+            self.assertEqual(chr(0) * 5, data[7:])
+
+    def testBadAlign(self):
+        """Test that an invalid alignment value is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('07_bad_align.dts')
+        self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
+                      "of two", str(e.exception))
+
+    def testPackSimple(self):
+        """Test that packing works as expected"""
+        retcode = self._DoTestFile('08_pack.dts')
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        entries = image._entries
+        self.assertEqual(5, len(entries))
+
+        # First u-boot
+        self.assertIn('u-boot', entries)
+        entry = entries['u-boot']
+        self.assertEqual(0, entry.pos)
+        self.assertEqual(len(U_BOOT_DATA), entry.size)
+
+        # Second u-boot, aligned to 16-byte boundary
+        self.assertIn('u-boot-align', entries)
+        entry = entries['u-boot-align']
+        self.assertEqual(16, entry.pos)
+        self.assertEqual(len(U_BOOT_DATA), entry.size)
+
+        # Third u-boot, size 23 bytes
+        self.assertIn('u-boot-size', entries)
+        entry = entries['u-boot-size']
+        self.assertEqual(20, entry.pos)
+        self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
+        self.assertEqual(23, entry.size)
+
+        # Fourth u-boot, placed immediate after the above
+        self.assertIn('u-boot-next', entries)
+        entry = entries['u-boot-next']
+        self.assertEqual(43, entry.pos)
+        self.assertEqual(len(U_BOOT_DATA), entry.size)
+
+        # Fifth u-boot, placed at a fixed position
+        self.assertIn('u-boot-fixed', entries)
+        entry = entries['u-boot-fixed']
+        self.assertEqual(61, entry.pos)
+        self.assertEqual(len(U_BOOT_DATA), entry.size)
+
+        self.assertEqual(65, image._size)
+
+    def testPackExtra(self):
+        """Test that extra packing feature works as expected"""
+        retcode = self._DoTestFile('09_pack_extra.dts')
+
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        entries = image._entries
+        self.assertEqual(5, len(entries))
+
+        # First u-boot with padding before and after
+        self.assertIn('u-boot', entries)
+        entry = entries['u-boot']
+        self.assertEqual(0, entry.pos)
+        self.assertEqual(3, entry.pad_before)
+        self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
+
+        # Second u-boot has an aligned size, but it has no effect
+        self.assertIn('u-boot-align-size-nop', entries)
+        entry = entries['u-boot-align-size-nop']
+        self.assertEqual(12, entry.pos)
+        self.assertEqual(4, entry.size)
+
+        # Third u-boot has an aligned size too
+        self.assertIn('u-boot-align-size', entries)
+        entry = entries['u-boot-align-size']
+        self.assertEqual(16, entry.pos)
+        self.assertEqual(32, entry.size)
+
+        # Fourth u-boot has an aligned end
+        self.assertIn('u-boot-align-end', entries)
+        entry = entries['u-boot-align-end']
+        self.assertEqual(48, entry.pos)
+        self.assertEqual(16, entry.size)
+
+        # Fifth u-boot immediately afterwards
+        self.assertIn('u-boot-align-both', entries)
+        entry = entries['u-boot-align-both']
+        self.assertEqual(64, entry.pos)
+        self.assertEqual(64, entry.size)
+
+        self.CheckNoGaps(entries)
+        self.assertEqual(128, image._size)
+
+    def testPackAlignPowerOf2(self):
+        """Test that invalid entry alignment is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('10_pack_align_power2.dts')
+        self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
+                      "of two", str(e.exception))
+
+    def testPackAlignSizePowerOf2(self):
+        """Test that invalid entry size alignment is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('11_pack_align_size_power2.dts')
+        self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
+                      "power of two", str(e.exception))
+
+    def testPackInvalidAlign(self):
+        """Test detection of an position that does not match its alignment"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('12_pack_inv_align.dts')
+        self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match "
+                      "align 0x4 (4)", str(e.exception))
+
+    def testPackInvalidSizeAlign(self):
+        """Test that invalid entry size alignment is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('13_pack_inv_size_align.dts')
+        self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
+                      "align-size 0x4 (4)", str(e.exception))
+
+    def testPackOverlap(self):
+        """Test that overlapping regions are detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('14_pack_overlap.dts')
+        self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps "
+                      "with previous entry '/binman/u-boot' ending at 0x4 (4)",
+                      str(e.exception))
+
+    def testPackEntryOverflow(self):
+        """Test that entries that overflow their size are detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('15_pack_overflow.dts')
+        self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
+                      "but entry size is 0x3 (3)", str(e.exception))
+
+    def testPackImageOverflow(self):
+        """Test that entries which overflow the image size are detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('16_pack_image_overflow.dts')
+        self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image "
+                      "size 0x3 (3)", str(e.exception))
+
+    def testPackImageSize(self):
+        """Test that the image size can be set"""
+        retcode = self._DoTestFile('17_pack_image_size.dts')
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        self.assertEqual(7, image._size)
+
+    def testPackImageSizeAlign(self):
+        """Test that image size alignemnt works as expected"""
+        retcode = self._DoTestFile('18_pack_image_align.dts')
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        self.assertEqual(16, image._size)
+
+    def testPackInvalidImageAlign(self):
+        """Test that invalid image alignment is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('19_pack_inv_image_align.dts')
+        self.assertIn("Image '/binman': Size 0x7 (7) does not match "
+                      "align-size 0x8 (8)", str(e.exception))
+
+    def testPackAlignPowerOf2(self):
+        """Test that invalid image alignment is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('20_pack_inv_image_align_power2.dts')
+        self.assertIn("Image '/binman': Alignment size 131 must be a power of "
+                      "two", str(e.exception))
+
+    def testImagePadByte(self):
+        """Test that the image pad byte can be specified"""
+        data = self._DoReadFile('21_image_pad.dts')
+        self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 9) + U_BOOT_DATA, data)
+
+    def testImageName(self):
+        """Test that image files can be named"""
+        retcode = self._DoTestFile('22_image_name.dts')
+        self.assertEqual(0, retcode)
+        image = control.images['image1']
+        fname = tools.GetOutputFilename('test-name')
+        self.assertTrue(os.path.exists(fname))
+
+        image = control.images['image2']
+        fname = tools.GetOutputFilename('test-name.xx')
+        self.assertTrue(os.path.exists(fname))
+
+    def testBlobFilename(self):
+        """Test that generic blobs can be provided by filename"""
+        data = self._DoReadFile('23_blob.dts')
+        self.assertEqual(BLOB_DATA, data)
+
+    def testPackSorted(self):
+        """Test that entries can be sorted"""
+        data = self._DoReadFile('24_sorted.dts')
+        self.assertEqual(chr(0) * 5 + U_BOOT_SPL_DATA + chr(0) * 2 +
+                         U_BOOT_DATA, data)
+
+    def testPackZeroPosition(self):
+        """Test that an entry at position 0 is not given a new position"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('25_pack_zero_size.dts')
+        self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps "
+                      "with previous entry '/binman/u-boot' ending at 0x4 (4)",
+                      str(e.exception))
+
+    def testPackUbootDtb(self):
+        """Test that a device tree can be added to U-Boot"""
+        data = self._DoReadFile('26_pack_u_boot_dtb.dts')
+        self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
+
+    def testPackX86RomNoSize(self):
+        """Test that the end-at-4gb property requires a size property"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('27_pack_4gb_no_size.dts')
+        self.assertIn("Image '/binman': Image size must be provided when "
+                      "using end-at-4gb", str(e.exception))
+
+    def testPackX86RomOutside(self):
+        """Test that the end-at-4gb property checks for position boundaries"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('28_pack_4gb_outside.dts')
+        self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
+                      "the image starting at 0xfffffff0 (4294967280)",
+                      str(e.exception))
+
+    def testPackX86Rom(self):
+        """Test that a basic x86 ROM can be created"""
+        data = self._DoReadFile('29_x86-rom.dts')
+        self.assertEqual(U_BOOT_DATA + chr(0) * 3 + U_BOOT_SPL_DATA +
+                         chr(0) * 6, data)
+
+    def testPackX86RomMeNoDesc(self):
+        """Test that an invalid Intel descriptor entry is detected"""
+        TestFunctional._MakeInputFile('descriptor.bin', '')
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('31_x86-rom-me.dts')
+        self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
+                      "signature", str(e.exception))
+
+    def testPackX86RomBadDesc(self):
+        """Test that the Intel requires a descriptor entry"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('30_x86-rom-me-no-desc.dts')
+        self.assertIn("Node '/binman/intel-me': No position set with "
+                      "pos-unset: should another entry provide this correct "
+                      "position?", str(e.exception))
+
+    def testPackX86RomMe(self):
+        """Test that an x86 ROM with an ME region can be created"""
+        data = self._DoReadFile('31_x86-rom-me.dts')
+        self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
+
+    def testPackVga(self):
+        """Test that an image with a VGA binary can be created"""
+        data = self._DoReadFile('32_intel-vga.dts')
+        self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
+
+    def testPackStart16(self):
+        """Test that an image with an x86 start16 region can be created"""
+        data = self._DoReadFile('33_x86-start16.dts')
+        self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
+
+    def testPackUbootMicrocode(self):
+        """Test that x86 microcode can be handled correctly
+
+        We expect to see the following in the image, in order:
+            u-boot-nodtb.bin with a microcode pointer inserted at the correct
+                place
+            u-boot.dtb with the microcode removed
+            the microcode
+        """
+        data = self._DoReadFile('34_x86_ucode.dts', True)
+
+        # Now check the device tree has no microcode
+        second = data[len(U_BOOT_NODTB_DATA):]
+        fname = tools.GetOutputFilename('test.dtb')
+        with open(fname, 'wb') as fd:
+            fd.write(second)
+        fdt = fdt_select.FdtScan(fname)
+        ucode = fdt.GetNode('/microcode')
+        self.assertTrue(ucode)
+        for node in ucode.subnodes:
+            self.assertFalse(node.props.get('data'))
+
+        fdt_len = self.GetFdtLen(second)
+        third = second[fdt_len:]
+
+        # Check that the microcode appears immediately after the Fdt
+        # This matches the concatenation of the data properties in
+        # the /microcode/update@xxx nodes in x86_ucode.dts.
+        ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
+                                 0x78235609)
+        self.assertEqual(ucode_data, third[:len(ucode_data)])
+        ucode_pos = len(U_BOOT_NODTB_DATA) + fdt_len
+
+        # Check that the microcode pointer was inserted. It should match the
+        # expected position and size
+        pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
+                                   len(ucode_data))
+        first = data[:len(U_BOOT_NODTB_DATA)]
+        self.assertEqual('nodtb with microcode' + pos_and_size +
+                         ' somewhere in here', first)
+
+    def _RunPackUbootSingleMicrocode(self, collate):
+        """Test that x86 microcode can be handled correctly
+
+        We expect to see the following in the image, in order:
+            u-boot-nodtb.bin with a microcode pointer inserted at the correct
+                place
+            u-boot.dtb with the microcode
+            an empty microcode region
+        """
+        # We need the libfdt library to run this test since only that allows
+        # finding the offset of a property. This is required by
+        # Entry_u_boot_dtb_with_ucode.ObtainContents().
+        if not fdt_select.have_libfdt:
+            return
+        data = self._DoReadFile('35_x86_single_ucode.dts', True)
+
+        second = data[len(U_BOOT_NODTB_DATA):]
+
+        fdt_len = self.GetFdtLen(second)
+        third = second[fdt_len:]
+        second = second[:fdt_len]
+
+        if not collate:
+            ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
+            self.assertIn(ucode_data, second)
+            ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
+
+            # Check that the microcode pointer was inserted. It should match the
+            # expected position and size
+            pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
+                                    len(ucode_data))
+            first = data[:len(U_BOOT_NODTB_DATA)]
+            self.assertEqual('nodtb with microcode' + pos_and_size +
+                            ' somewhere in here', first)
+
+    def testPackUbootSingleMicrocode(self):
+        """Test that x86 microcode can be handled correctly with fdt_normal.
+        """
+        self._RunPackUbootSingleMicrocode(False)
+
+    def testPackUbootSingleMicrocodeFallback(self):
+        """Test that x86 microcode can be handled correctly with fdt_fallback.
+
+        This only supports collating the microcode.
+        """
+        try:
+            old_val = fdt_select.UseFallback(True)
+            self._RunPackUbootSingleMicrocode(True)
+        finally:
+            fdt_select.UseFallback(old_val)
+
+    def testUBootImg(self):
+        """Test that u-boot.img can be put in a file"""
+        data = self._DoReadFile('36_u_boot_img.dts')
+        self.assertEqual(U_BOOT_IMG_DATA, data)
+
+    def testNoMicrocode(self):
+        """Test that a missing microcode region is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('37_x86_no_ucode.dts', True)
+        self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
+                      "node found in ", str(e.exception))
+
+    def testMicrocodeWithoutNode(self):
+        """Test that a missing u-boot-dtb-with-ucode node is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('38_x86_ucode_missing_node.dts', True)
+        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
+                "microcode region u-boot-dtb-with-ucode", str(e.exception))
+
+    def testMicrocodeWithoutNode2(self):
+        """Test that a missing u-boot-ucode node is detected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
+        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
+            "microcode region u-boot-ucode", str(e.exception))
+
+    def testMicrocodeWithoutPtrInElf(self):
+        """Test that a U-Boot binary without the microcode symbol is detected"""
+        # ELF file without a '_dt_ucode_base_size' symbol
+        if not fdt_select.have_libfdt:
+            return
+        try:
+            with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
+                TestFunctional._MakeInputFile('u-boot', fd.read())
+
+            with self.assertRaises(ValueError) as e:
+                self._RunPackUbootSingleMicrocode(False)
+            self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
+                    "_dt_ucode_base_size symbol in u-boot", str(e.exception))
+
+        finally:
+            # Put the original file back
+            with open(self.TestFile('u_boot_ucode_ptr')) as fd:
+                TestFunctional._MakeInputFile('u-boot', fd.read())
+
+    def testMicrocodeNotInImage(self):
+        """Test that microcode must be placed within the image"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
+        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
+                "pointer _dt_ucode_base_size at fffffe14 is outside the "
+                "image ranging from 00000000 to 0000002e", str(e.exception))
+
+    def testWithoutMicrocode(self):
+        """Test that we can cope with an image without microcode (e.g. qemu)"""
+        with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
+            TestFunctional._MakeInputFile('u-boot', fd.read())
+        data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
+
+        # Now check the device tree has no microcode
+        self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
+        second = data[len(U_BOOT_NODTB_DATA):]
+
+        fdt_len = self.GetFdtLen(second)
+        self.assertEqual(dtb, second[:fdt_len])
+
+        used_len = len(U_BOOT_NODTB_DATA) + fdt_len
+        third = data[used_len:]
+        self.assertEqual(chr(0) * (0x200 - used_len), third)
+
+    def testUnknownPosSize(self):
+        """Test that microcode must be placed within the image"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('41_unknown_pos_size.dts', True)
+        self.assertIn("Image '/binman': Unable to set pos/size for unknown "
+                "entry 'invalid-entry'", str(e.exception))
+
+    def testPackFsp(self):
+        """Test that an image with a FSP binary can be created"""
+        data = self._DoReadFile('42_intel-fsp.dts')
+        self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
+
+    def testPackCmc(self):
+        """Test that an image with a FSP binary can be created"""
+        data = self._DoReadFile('43_intel-cmc.dts')
+        self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
diff --git a/tools/binman/image.py b/tools/binman/image.py
new file mode 100644 (file)
index 0000000..07fc930
--- /dev/null
@@ -0,0 +1,229 @@
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+# Class for an image, the output of binman
+#
+
+from collections import OrderedDict
+from operator import attrgetter
+
+import entry
+from entry import Entry
+import fdt_util
+import tools
+
+class Image:
+    """A Image, representing an output from binman
+
+    An image is comprised of a collection of entries each containing binary
+    data. The image size must be large enough to hold all of this data.
+
+    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
+        _align_size: Image size alignment, or None
+        _pad_before: Number of bytes before the first entry starts. This
+            effectively changes the place where entry position 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 image where there is no entry
+        _filename: Output filename for image
+        _sort: True if entries should be sorted by position, False if they
+            must be in-order in the device tree description
+        _skip_at_start: Number of bytes before the first entry starts. These
+            effecively adjust the starting position of entries. For example,
+            if _pad_before is 16, then the first entry would start at 16.
+            An entry with pos = 20 would in fact be written at position 4
+            in the image file.
+        _end_4gb: Indicates that the image ends at the 4GB boundary. This is
+            used for x86 images, which want to use positions such that a
+             memory address (like 0xff800000) is the first entry position.
+             This causes _skip_at_start to be set to the starting memory
+             address.
+        _entries: OrderedDict() of entries
+    """
+    def __init__(self, name, node):
+        self._node = node
+        self._name = name
+        self._size = None
+        self._align_size = None
+        self._pad_before = 0
+        self._pad_after = 0
+        self._pad_byte = 0
+        self._filename = '%s.bin' % self._name
+        self._sort = False
+        self._skip_at_start = 0
+        self._end_4gb = False
+        self._entries = OrderedDict()
+
+        self._ReadNode()
+        self._ReadEntries()
+
+    def _ReadNode(self):
+        """Read properties from the image node"""
+        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)
+        filename = fdt_util.GetString(self._node, 'filename')
+        if filename:
+            self._filename = filename
+        self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
+        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
+        if self._end_4gb and not self._size:
+            self._Raise("Image size must be provided when using end-at-4gb")
+        if self._end_4gb:
+            self._skip_at_start = 0x100000000 - self._size
+
+    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.pos + 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 image 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))
+
+    def _Raise(self, msg):
+        """Raises an error for this image
+
+        Args:
+            msg: Error message to use in the raise string
+        Raises:
+            ValueError()
+        """
+        raise ValueError("Image '%s': %s" % (self._node.path, msg))
+
+    def _ReadEntries(self):
+        for node in self._node.subnodes:
+            self._entries[node.name] = Entry.Create(self, node)
+
+    def FindEntryType(self, etype):
+        """Find an entry type in the image
+
+        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
+
+    def _SetEntryPosSize(self, name, pos, size):
+        """Set the position and size of an entry
+
+        Args:
+            name: Entry name to update
+            pos: New position
+            size: New size
+        """
+        entry = self._entries.get(name)
+        if not entry:
+            self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
+        entry.SetPositionSize(self._skip_at_start + pos, size)
+
+    def GetEntryPositions(self):
+        """Handle entries that want to set the position/size of other entries
+
+        This calls each entry's GetPositions() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        for entry in self._entries.values():
+            pos_dict = entry.GetPositions()
+            for name, info in pos_dict.iteritems():
+                self._SetEntryPosSize(name, *info)
+
+    def PackEntries(self):
+        """Pack all entries into the image"""
+        pos = self._skip_at_start
+        for entry in self._entries.values():
+            pos = entry.Pack(pos)
+
+    def _SortEntries(self):
+        """Sort entries by position"""
+        entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
+        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()
+        pos = 0
+        prev_name = 'None'
+        for entry in self._entries.values():
+            if (entry.pos < self._skip_at_start or
+                entry.pos >= self._skip_at_start + self._size):
+                entry.Raise("Position %#x (%d) is outside the image starting "
+                            "at %#x (%d)" %
+                            (entry.pos, entry.pos, self._skip_at_start,
+                             self._skip_at_start))
+            if entry.pos < pos:
+                entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
+                            "ending at %#x (%d)" %
+                            (entry.pos, entry.pos, prev_name, pos, pos))
+            pos = entry.pos + entry.size
+            prev_name = entry.GetPath()
+
+    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 BuildImage(self):
+        """Write the image to a file"""
+        fname = tools.GetOutputFilename(self._filename)
+        with open(fname, 'wb') as fd:
+            fd.write(chr(self._pad_byte) * self._size)
+
+            for entry in self._entries.values():
+                data = entry.GetData()
+                fd.seek(self._pad_before + entry.pos - self._skip_at_start)
+                fd.write(data)
diff --git a/tools/binman/test/01_invalid.dts b/tools/binman/test/01_invalid.dts
new file mode 100644 (file)
index 0000000..7d00455
--- /dev/null
@@ -0,0 +1,5 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
diff --git a/tools/binman/test/02_missing_node.dts b/tools/binman/test/02_missing_node.dts
new file mode 100644 (file)
index 0000000..3a51ec2
--- /dev/null
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+};
diff --git a/tools/binman/test/03_empty.dts b/tools/binman/test/03_empty.dts
new file mode 100644 (file)
index 0000000..493c9a0
--- /dev/null
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+       };
+};
diff --git a/tools/binman/test/04_invalid_entry.dts b/tools/binman/test/04_invalid_entry.dts
new file mode 100644 (file)
index 0000000..b043455
--- /dev/null
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               not-a-valid-type {
+               };
+       };
+};
diff --git a/tools/binman/test/05_simple.dts b/tools/binman/test/05_simple.dts
new file mode 100644 (file)
index 0000000..3771aa2
--- /dev/null
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/06_dual_image.dts b/tools/binman/test/06_dual_image.dts
new file mode 100644 (file)
index 0000000..78be16f
--- /dev/null
@@ -0,0 +1,22 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               multiple-images;
+               image1 {
+                       u-boot {
+                       };
+               };
+
+               image2 {
+                       pad-before = <3>;
+                       pad-after = <5>;
+
+                       u-boot {
+                       };
+               };
+       };
+};
diff --git a/tools/binman/test/07_bad_align.dts b/tools/binman/test/07_bad_align.dts
new file mode 100644 (file)
index 0000000..123bb13
--- /dev/null
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       align = <23>;
+               };
+       };
+};
diff --git a/tools/binman/test/08_pack.dts b/tools/binman/test/08_pack.dts
new file mode 100644 (file)
index 0000000..dc63d99
--- /dev/null
@@ -0,0 +1,30 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+               };
+
+               u-boot-align {
+                       type = "u-boot";
+                       align = <16>;
+               };
+
+               u-boot-size {
+                       type = "u-boot";
+                       size = <23>;
+               };
+
+               u-boot-next {
+                       type = "u-boot";
+               };
+
+               u-boot-fixed {
+                       type = "u-boot";
+                       pos = <61>;
+               };
+       };
+};
diff --git a/tools/binman/test/09_pack_extra.dts b/tools/binman/test/09_pack_extra.dts
new file mode 100644 (file)
index 0000000..0765707
--- /dev/null
@@ -0,0 +1,35 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       pad-before = <3>;
+                       pad-after = <5>;
+               };
+
+               u-boot-align-size-nop {
+                       type = "u-boot";
+                       align-size = <4>;
+               };
+
+               u-boot-align-size {
+                       type = "u-boot";
+                       align = <16>;
+                       align-size = <32>;
+               };
+
+               u-boot-align-end {
+                       type = "u-boot";
+                       align-end = <64>;
+               };
+
+               u-boot-align-both {
+                       type = "u-boot";
+                       align= <64>;
+                       align-end = <128>;
+               };
+       };
+};
diff --git a/tools/binman/test/10_pack_align_power2.dts b/tools/binman/test/10_pack_align_power2.dts
new file mode 100644 (file)
index 0000000..8f6253a
--- /dev/null
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       align = <5>;
+               };
+       };
+};
diff --git a/tools/binman/test/11_pack_align_size_power2.dts b/tools/binman/test/11_pack_align_size_power2.dts
new file mode 100644 (file)
index 0000000..04f7672
--- /dev/null
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       align-size = <55>;
+               };
+       };
+};
diff --git a/tools/binman/test/12_pack_inv_align.dts b/tools/binman/test/12_pack_inv_align.dts
new file mode 100644 (file)
index 0000000..1d9d80a
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       pos = <5>;
+                       align = <4>;
+               };
+       };
+};
diff --git a/tools/binman/test/13_pack_inv_size_align.dts b/tools/binman/test/13_pack_inv_size_align.dts
new file mode 100644 (file)
index 0000000..dfafa13
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       size = <5>;
+                       align-size = <4>;
+               };
+       };
+};
diff --git a/tools/binman/test/14_pack_overlap.dts b/tools/binman/test/14_pack_overlap.dts
new file mode 100644 (file)
index 0000000..611cfd9
--- /dev/null
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+               };
+
+               u-boot-align {
+                       type = "u-boot";
+                       pos = <3>;
+               };
+       };
+};
diff --git a/tools/binman/test/15_pack_overflow.dts b/tools/binman/test/15_pack_overflow.dts
new file mode 100644 (file)
index 0000000..6f65433
--- /dev/null
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+                       size = <3>;
+               };
+       };
+};
diff --git a/tools/binman/test/16_pack_image_overflow.dts b/tools/binman/test/16_pack_image_overflow.dts
new file mode 100644 (file)
index 0000000..6ae66f3
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <3>;
+
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/17_pack_image_size.dts b/tools/binman/test/17_pack_image_size.dts
new file mode 100644 (file)
index 0000000..2360eb5
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <7>;
+
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/18_pack_image_align.dts b/tools/binman/test/18_pack_image_align.dts
new file mode 100644 (file)
index 0000000..16cd2a4
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               align-size = <16>;
+
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/19_pack_inv_image_align.dts b/tools/binman/test/19_pack_inv_image_align.dts
new file mode 100644 (file)
index 0000000..e5ee87b
--- /dev/null
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <7>;
+               align-size = <8>;
+
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/20_pack_inv_image_align_power2.dts b/tools/binman/test/20_pack_inv_image_align_power2.dts
new file mode 100644 (file)
index 0000000..a428c4b
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               align-size = <131>;
+
+               u-boot {
+               };
+       };
+};
diff --git a/tools/binman/test/21_image_pad.dts b/tools/binman/test/21_image_pad.dts
new file mode 100644 (file)
index 0000000..daf8385
--- /dev/null
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               pad-byte = <0xff>;
+               u-boot-spl {
+               };
+
+               u-boot {
+                       pos = <12>;
+               };
+       };
+};
diff --git a/tools/binman/test/22_image_name.dts b/tools/binman/test/22_image_name.dts
new file mode 100644 (file)
index 0000000..94fc069
--- /dev/null
@@ -0,0 +1,21 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               multiple-images;
+               image1 {
+                       filename = "test-name";
+                       u-boot {
+                       };
+               };
+
+               image2 {
+                       filename = "test-name.xx";
+                       u-boot {
+                       };
+               };
+       };
+};
diff --git a/tools/binman/test/23_blob.dts b/tools/binman/test/23_blob.dts
new file mode 100644 (file)
index 0000000..7dcff69
--- /dev/null
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               blob {
+                       filename = "blobfile";
+               };
+       };
+};
diff --git a/tools/binman/test/24_sorted.dts b/tools/binman/test/24_sorted.dts
new file mode 100644 (file)
index 0000000..9f4151c
--- /dev/null
@@ -0,0 +1,17 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               u-boot {
+                       pos = <10>;
+               };
+
+               u-boot-spl {
+                       pos = <5>;
+               };
+       };
+};
diff --git a/tools/binman/test/25_pack_zero_size.dts b/tools/binman/test/25_pack_zero_size.dts
new file mode 100644 (file)
index 0000000..7d2baad
--- /dev/null
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot {
+               };
+
+               u-boot-spl {
+                       pos = <0>;
+               };
+       };
+};
diff --git a/tools/binman/test/26_pack_u_boot_dtb.dts b/tools/binman/test/26_pack_u_boot_dtb.dts
new file mode 100644 (file)
index 0000000..2707a73
--- /dev/null
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot-nodtb {
+               };
+
+               u-boot-dtb {
+               };
+       };
+};
diff --git a/tools/binman/test/27_pack_4gb_no_size.dts b/tools/binman/test/27_pack_4gb_no_size.dts
new file mode 100644 (file)
index 0000000..e0b6519
--- /dev/null
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               u-boot {
+                       pos = <0xfffffff0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/28_pack_4gb_outside.dts b/tools/binman/test/28_pack_4gb_outside.dts
new file mode 100644 (file)
index 0000000..ff468c7
--- /dev/null
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               u-boot {
+                       pos = <0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/29_x86-rom.dts b/tools/binman/test/29_x86-rom.dts
new file mode 100644 (file)
index 0000000..075ede3
--- /dev/null
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               u-boot {
+                       pos = <0xfffffff0>;
+               };
+
+               u-boot-spl {
+                       pos = <0xfffffff7>;
+               };
+       };
+};
diff --git a/tools/binman/test/30_x86-rom-me-no-desc.dts b/tools/binman/test/30_x86-rom-me-no-desc.dts
new file mode 100644 (file)
index 0000000..4578f66
--- /dev/null
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <16>;
+               intel-me {
+                       pos-unset;
+               };
+       };
+};
diff --git a/tools/binman/test/31_x86-rom-me.dts b/tools/binman/test/31_x86-rom-me.dts
new file mode 100644 (file)
index 0000000..b484ab3
--- /dev/null
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x800000>;
+               intel-descriptor {
+               };
+
+               intel-me {
+                       pos-unset;
+               };
+       };
+};
diff --git a/tools/binman/test/32_intel-vga.dts b/tools/binman/test/32_intel-vga.dts
new file mode 100644 (file)
index 0000000..1790833
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               intel-vga {
+               };
+       };
+};
diff --git a/tools/binman/test/33_x86-start16.dts b/tools/binman/test/33_x86-start16.dts
new file mode 100644 (file)
index 0000000..2e279de
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               x86-start16 {
+               };
+       };
+};
diff --git a/tools/binman/test/34_x86_ucode.dts b/tools/binman/test/34_x86_ucode.dts
new file mode 100644 (file)
index 0000000..64a6c2c
--- /dev/null
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/35_x86_single_ucode.dts b/tools/binman/test/35_x86_single_ucode.dts
new file mode 100644 (file)
index 0000000..973e97f
--- /dev/null
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+       };
+};
diff --git a/tools/binman/test/36_u_boot_img.dts b/tools/binman/test/36_u_boot_img.dts
new file mode 100644 (file)
index 0000000..aa5a3fe
--- /dev/null
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               u-boot-img {
+               };
+       };
+};
diff --git a/tools/binman/test/37_x86_no_ucode.dts b/tools/binman/test/37_x86_no_ucode.dts
new file mode 100644 (file)
index 0000000..9e12156
--- /dev/null
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+};
diff --git a/tools/binman/test/38_x86_ucode_missing_node.dts b/tools/binman/test/38_x86_ucode_missing_node.dts
new file mode 100644 (file)
index 0000000..d6cf0d8
--- /dev/null
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/39_x86_ucode_missing_node2.dts b/tools/binman/test/39_x86_ucode_missing_node2.dts
new file mode 100644 (file)
index 0000000..b7e26c5
--- /dev/null
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/40_x86_ucode_not_in_image.dts b/tools/binman/test/40_x86_ucode_not_in_image.dts
new file mode 100644 (file)
index 0000000..67d17d3
--- /dev/null
@@ -0,0 +1,28 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/41_unknown_pos_size.dts b/tools/binman/test/41_unknown_pos_size.dts
new file mode 100644 (file)
index 0000000..a8e7d8a
--- /dev/null
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               _testing {
+               };
+        };
+};
diff --git a/tools/binman/test/42_intel-fsp.dts b/tools/binman/test/42_intel-fsp.dts
new file mode 100644 (file)
index 0000000..e0a1e76
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               intel-fsp {
+               };
+       };
+};
diff --git a/tools/binman/test/43_intel-cmc.dts b/tools/binman/test/43_intel-cmc.dts
new file mode 100644 (file)
index 0000000..26c456d
--- /dev/null
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               size = <16>;
+
+               intel-cmc {
+               };
+       };
+};
diff --git a/tools/binman/test/44_x86_optional_ucode.dts b/tools/binman/test/44_x86_optional_ucode.dts
new file mode 100644 (file)
index 0000000..abe1322
--- /dev/null
@@ -0,0 +1,30 @@
+/dts-v1/;
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       binman {
+               sort-by-pos;
+               end-at-4gb;
+               size = <0x200>;
+               u-boot-with-ucode-ptr {
+                       optional-ucode;
+               };
+
+               u-boot-dtb-with-ucode {
+               };
+
+               u-boot-ucode {
+               };
+       };
+
+       microcode {
+               update@0 {
+                       data = <0x12345678 0x12345679>;
+               };
+               update@1 {
+                       data = <0xabcd0000 0x78235609>;
+               };
+       };
+};
diff --git a/tools/binman/test/descriptor.bin b/tools/binman/test/descriptor.bin
new file mode 100644 (file)
index 0000000..3d54943
Binary files /dev/null and b/tools/binman/test/descriptor.bin differ
diff --git a/tools/binman/test/u_boot_no_ucode_ptr b/tools/binman/test/u_boot_no_ucode_ptr
new file mode 100755 (executable)
index 0000000..f72462f
Binary files /dev/null and b/tools/binman/test/u_boot_no_ucode_ptr differ
diff --git a/tools/binman/test/u_boot_no_ucode_ptr.c b/tools/binman/test/u_boot_no_ucode_ptr.c
new file mode 100644 (file)
index 0000000..a17bb4c
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Simple program to create a bad _dt_ucode_base_size symbol to create an
+ * error when it is used. This is used by binman tests.
+ *
+ * Build with:
+ * cc -march=i386 -m32 -o u_boot_no_ucode_ptr -T u_boot_ucode_ptr.lds \
+       -nostdlib u_boot_no_ucode_ptr.c
+ */
+
+static unsigned long not__dt_ucode_base_size[2]
+               __attribute__((section(".ucode"))) = {1, 2};
diff --git a/tools/binman/test/u_boot_ucode_ptr b/tools/binman/test/u_boot_ucode_ptr
new file mode 100755 (executable)
index 0000000..dbfb184
Binary files /dev/null and b/tools/binman/test/u_boot_ucode_ptr differ
diff --git a/tools/binman/test/u_boot_ucode_ptr.c b/tools/binman/test/u_boot_ucode_ptr.c
new file mode 100644 (file)
index 0000000..434c9f4
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Simple program to create a _dt_ucode_base_size symbol which can be read
+ * by 'nm'. This is used by binman tests.
+ *
+ * Build with:
+ * cc -march=i386 -m32 -o u_boot_ucode_ptr -T u_boot_ucode_ptr.lds -nostdlib \
+       u_boot_ucode_ptr.c
+ */
+
+static unsigned long _dt_ucode_base_size[2]
+               __attribute__((section(".ucode"))) = {1, 2};
diff --git a/tools/binman/test/u_boot_ucode_ptr.lds b/tools/binman/test/u_boot_ucode_ptr.lds
new file mode 100644 (file)
index 0000000..167debf
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * SPDX-License-Identifier:      GPL-2.0+
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+       . = 0xfffffdf0;
+       _start = .;
+       .ucode : {
+               *(.ucode)
+       }
+}
index 48059c02b550ee8db3cbfc7c9702b152936a0d4e..195b1533ab69cf6e7747ce03d7d280d6dad2de3c 100644 (file)
 #define FLREG_BASE(reg)                ((reg & 0x00000fff) << 12);
 #define FLREG_LIMIT(reg)       (((reg & 0x0fff0000) >> 4) | 0xfff);
 
-enum input_file_type_t {
-       IF_normal,
-       IF_fdt,
-       IF_uboot,
-};
-
 struct input_file {
        char *fname;
        unsigned int addr;
-       enum input_file_type_t type;
 };
 
 /**
@@ -760,219 +753,6 @@ static int write_data(char *image, int size, unsigned int addr,
        return write_size;
 }
 
-static int scan_ucode(const void *blob, char *ucode_base, int *countp,
-                     const char **datap, int *data_sizep)
-{
-       const char *data = NULL;
-       int node, count;
-       int data_size;
-       char *ucode;
-
-       for (node = 0, count = 0, ucode = ucode_base; node >= 0; count++) {
-               node = fdt_node_offset_by_compatible(blob, node,
-                                                    "intel,microcode");
-               if (node < 0)
-                       break;
-
-               data = fdt_getprop(blob, node, "data", &data_size);
-               if (!data) {
-                       debug("Missing microcode data in FDT '%s': %s\n",
-                             fdt_get_name(blob, node, NULL),
-                             fdt_strerror(data_size));
-                       return -ENOENT;
-               }
-
-               if (ucode_base)
-                       memcpy(ucode, data, data_size);
-               ucode += data_size;
-       }
-
-       if (countp)
-               *countp = count;
-       if (datap)
-               *datap = data;
-       if (data_sizep)
-               *data_sizep = data_size;
-
-       return ucode - ucode_base;
-}
-
-static int remove_ucode(char *blob)
-{
-       int node, count;
-       int ret;
-
-       /* Keep going until we find no more microcode to remove */
-       do {
-               for (node = 0, count = 0; node >= 0;) {
-                       int ret;
-
-                       node = fdt_node_offset_by_compatible(blob, node,
-                                                            "intel,microcode");
-                       if (node < 0)
-                               break;
-
-                       ret = fdt_delprop(blob, node, "data");
-
-                       /*
-                        * -FDT_ERR_NOTFOUND means we already removed the
-                        * data for this one, so we just continue.
-                        * 0 means we did remove it, so offsets may have
-                        * changed and we need to restart our scan.
-                        * Anything else indicates an error we should report.
-                        */
-                       if (ret == -FDT_ERR_NOTFOUND)
-                               continue;
-                       else if (!ret)
-                               node = 0;
-                       else
-                               return ret;
-               }
-       } while (count);
-
-       /* Pack down to remove excees space */
-       ret = fdt_pack(blob);
-       if (ret)
-               return ret;
-
-       return fdt_totalsize(blob);
-}
-
-static int write_ucode(char *image, int size, struct input_file *fdt,
-                      int fdt_size, unsigned int ucode_ptr,
-                      int collate_ucode)
-{
-       const char *data = NULL;
-       char *ucode_buf;
-       const void *blob;
-       char *ucode_base;
-       uint32_t *ptr;
-       int ucode_size;
-       int data_size;
-       int offset;
-       int count;
-       int ret;
-
-       blob = (void *)image + (uint32_t)(fdt->addr + size);
-
-       debug("DTB at %lx\n", (char *)blob - image);
-
-       /* Find out about the micrcode we have */
-       ucode_size = scan_ucode(blob, NULL, &count, &data, &data_size);
-       if (ucode_size < 0)
-               return ucode_size;
-       if (!count) {
-               debug("No microcode found in FDT\n");
-               return -ENOENT;
-       }
-
-       if (count > 1 && !collate_ucode) {
-               fprintf(stderr,
-                       "Cannot handle multiple microcode blocks - please use -C flag to collate them\n");
-               return -EMLINK;
-       }
-
-       /*
-        * Collect the microcode into a buffer, remove it from the device
-        * tree and place it immediately above the (now smaller) device tree.
-        */
-       if (collate_ucode && count > 1) {
-               ucode_buf = malloc(ucode_size);
-               if (!ucode_buf) {
-                       fprintf(stderr,
-                               "Out of memory for microcode (%d bytes)\n",
-                               ucode_size);
-                       return -ENOMEM;
-               }
-               ret = scan_ucode(blob, ucode_buf, NULL, NULL, NULL);
-               if (ret < 0)
-                       return ret;
-
-               /* Remove the microcode from the device tree */
-               ret = remove_ucode((char *)blob);
-               if (ret < 0) {
-                       debug("Could not remove FDT microcode: %s\n",
-                             fdt_strerror(ret));
-                       return -EINVAL;
-               }
-               debug("Collated %d microcode block(s)\n", count);
-               debug("Device tree reduced from %x to %x bytes\n",
-                     fdt_size, ret);
-               fdt_size = ret;
-
-               /*
-                * Place microcode area immediately above the FDT, aligned
-                * to a 16-byte boundary.
-                */
-               ucode_base = (char *)(((unsigned long)blob + fdt_size + 15) &
-                               ~15);
-
-               data = ucode_base;
-               data_size = ucode_size;
-               memcpy(ucode_base, ucode_buf, ucode_size);
-               free(ucode_buf);
-       }
-
-       offset = (uint32_t)(ucode_ptr + size);
-       ptr = (void *)image + offset;
-
-       ptr[0] = (data - image) - size;
-       ptr[1] = data_size;
-       debug("Wrote microcode pointer at %x: addr=%x, size=%x\n", ucode_ptr,
-             ptr[0], ptr[1]);
-
-       return (collate_ucode ? data + data_size : (char *)blob + fdt_size) -
-                       image;
-}
-
-/**
- * write_uboot() - Write U-Boot, device tree and microcode pointer
- *
- * This writes U-Boot into a place in the flash, followed by its device tree.
- * The microcode pointer is written so that U-Boot can find the microcode in
- * the device tree very early in boot.
- *
- * @image:     Pointer to image
- * @size:      Size of image in bytes
- * @uboot:     Input file information for u-boot.bin
- * @fdt:       Input file information for u-boot.dtb
- * @ucode_ptr: Address in U-Boot where the microcode pointer should be placed
- * @return 0 if OK, -ve on error
- */
-static int write_uboot(char *image, int size, struct input_file *uboot,
-                      struct input_file *fdt, unsigned int ucode_ptr,
-                      int collate_ucode, int *offset_uboot_top,
-                      int *offset_uboot_start)
-{
-       int uboot_size, fdt_size;
-       int uboot_top;
-
-       uboot_size = write_data(image, size, uboot->addr, uboot->fname, 0, 0);
-       if (uboot_size < 0)
-               return uboot_size;
-       fdt->addr = uboot->addr + uboot_size;
-       debug("U-Boot size %#x, FDT at %#x\n", uboot_size, fdt->addr);
-       fdt_size = write_data(image, size, fdt->addr, fdt->fname, 0, 0);
-       if (fdt_size < 0)
-               return fdt_size;
-
-       uboot_top = (uint32_t)(fdt->addr + size) + fdt_size;
-
-       if (ucode_ptr) {
-               uboot_top = write_ucode(image, size, fdt, fdt_size, ucode_ptr,
-                                       collate_ucode);
-               if (uboot_top < 0)
-                       return uboot_top;
-       }
-
-       if (offset_uboot_top && offset_uboot_start) {
-               *offset_uboot_top = uboot_top;
-               *offset_uboot_start = (uint32_t)(uboot->addr + size);
-       }
-
-       return 0;
-}
-
 static void print_version(void)
 {
        printf("ifdtool v%s -- ", IFDTOOL_VERSION);
@@ -1034,7 +814,7 @@ int main(int argc, char *argv[])
        int mode_dump = 0, mode_extract = 0, mode_inject = 0;
        int mode_spifreq = 0, mode_em100 = 0, mode_locked = 0;
        int mode_unlocked = 0, mode_write = 0, mode_write_descriptor = 0;
-       int create = 0, collate_ucode = 0;
+       int create = 0;
        char *region_type_string = NULL, *inject_fname = NULL;
        char *desc_fname = NULL, *addr_str = NULL;
        int region_type = -1, inputfreq = 0;
@@ -1047,14 +827,12 @@ int main(int argc, char *argv[])
        char *outfile = NULL;
        struct stat buf;
        int size = 0;
-       unsigned int ucode_ptr = 0;
        bool have_uboot = false;
        int bios_fd;
        char *image;
        int ret;
        static struct option long_options[] = {
                {"create", 0, NULL, 'c'},
-               {"collate-microcode", 0, NULL, 'C'},
                {"dump", 0, NULL, 'd'},
                {"descriptor", 1, NULL, 'D'},
                {"em100", 0, NULL, 'e'},
@@ -1062,7 +840,6 @@ int main(int argc, char *argv[])
                {"fdt", 1, NULL, 'f'},
                {"inject", 1, NULL, 'i'},
                {"lock", 0, NULL, 'l'},
-               {"microcode", 1, NULL, 'm'},
                {"romsize", 1, NULL, 'r'},
                {"spifreq", 1, NULL, 's'},
                {"unlock", 0, NULL, 'u'},
@@ -1073,15 +850,12 @@ int main(int argc, char *argv[])
                {0, 0, 0, 0}
        };
 
-       while ((opt = getopt_long(argc, argv, "cCdD:ef:hi:lm:r:s:uU:vw:x?",
+       while ((opt = getopt_long(argc, argv, "cdD:ef:hi:lr:s:uU:vw:x?",
                                  long_options, &option_index)) != EOF) {
                switch (opt) {
                case 'c':
                        create = 1;
                        break;
-               case 'C':
-                       collate_ucode = 1;
-                       break;
                case 'd':
                        mode_dump = 1;
                        break;
@@ -1119,9 +893,6 @@ int main(int argc, char *argv[])
                case 'l':
                        mode_locked = 1;
                        break;
-               case 'm':
-                       ucode_ptr = strtoul(optarg, NULL, 0);
-                       break;
                case 'r':
                        rom_size = strtol(optarg, NULL, 0);
                        debug("ROM size %d\n", rom_size);
@@ -1166,12 +937,6 @@ int main(int argc, char *argv[])
                                        exit(EXIT_FAILURE);
                                }
                                ifile->addr = strtoll(optarg, NULL, 0);
-                               ifile->type = opt == 'f' ? IF_fdt :
-                                       opt == 'U' ? IF_uboot : IF_normal;
-                               if (ifile->type == IF_fdt)
-                                       fdt = ifile;
-                               else if (ifile->type == IF_uboot)
-                                       have_uboot = true;
                                wr_num++;
                        } else {
                                fprintf(stderr,
@@ -1302,18 +1067,9 @@ int main(int argc, char *argv[])
 
                for (wr_idx = 0; wr_idx < wr_num; wr_idx++) {
                        ifile = &input_file[wr_idx];
-                       if (ifile->type == IF_fdt) {
-                               continue;
-                       } else if (ifile->type == IF_uboot) {
-                               ret = write_uboot(image, size, ifile, fdt,
-                                                 ucode_ptr, collate_ucode,
-                                                 &offset_uboot_top,
-                                                 &offset_uboot_start);
-                       } else {
-                               ret = write_data(image, size, ifile->addr,
-                                                ifile->fname, offset_uboot_top,
-                                                offset_uboot_start);
-                       }
+                       ret = write_data(image, size, ifile->addr,
+                                        ifile->fname, offset_uboot_top,
+                                        offset_uboot_start);
                        if (ret < 0)
                                break;
                }