Project import generated by Copybara.

GitOrigin-RevId: 81172f277d66bac9440e6cac9fdc30ff2a29d5c8
diff --git a/tools/binman/.gitignore b/tools/binman/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/tools/binman/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/tools/binman/README b/tools/binman/README
new file mode 100644
index 0000000..04ed2b7
--- /dev/null
+++ b/tools/binman/README
@@ -0,0 +1,780 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+
+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 situations to create a new entry
+type for the images in mkimage, but this would not add functionality. It
+seems better to use the mkimage 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
+firmware 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'.
+
+
+Enabling binman for a board
+---------------------------
+
+At present binman is invoked from a rule in the main Makefile. Typically you
+will have a rule like:
+
+ifneq ($(CONFIG_ARCH_<something>),)
+u-boot-<your_suffix>.bin: <input_file_1> <input_file_2> checkbinman FORCE
+	$(call if_changed,binman)
+endif
+
+This assumes that u-boot-<your_suffix>.bin is a target, and is the final file
+that you need to produce. You can make it a target by adding it to ALL-y
+either in the main Makefile or in a config.mk file in your arch subdirectory.
+
+Once binman is executed it will pick up its instructions from a device-tree
+file, typically <soc>-u-boot.dtsi, where <soc> is your CONFIG_SYS_SOC value.
+You can use other, more specific CONFIG options - see 'Automatic .dtsi
+inclusion' below.
+
+
+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 {
+			offset = <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 offset of an entry using the 'offset' 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.
+
+offset:
+	This sets the offset of an entry within the image or section containing
+	it. The first byte of the image is normally at offset 0. If 'offset' 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 offset 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. This does not move the start of the entry, so the contents of
+	the entry will still start at the beginning. But there may be padding
+	at the end. If 'align-end' is not provided, no alignment is performed.
+
+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.
+
+offset-unset:
+	Indicates that the offset of this entry should not be set by placing
+	it immediately after the entry before. Instead, is set by another
+	entry which knows where this entry should go. When this boolean
+	property is present, binman will give an error if another entry does
+	not set the offset (with the GetOffsets() method).
+
+image-pos:
+	This cannot be set on entry (or at least it is ignored if it is), but
+	with the -u option, binman will set it to the absolute image position
+	for each entry. This makes it easy to find out exactly where the entry
+	ended up in the image, regardless of parent sections, etc.
+
+expand-size:
+	Expand the size of this entry to fit available space. This space is only
+	limited by the size of the image/section and the position of the next
+	entry.
+
+The attributes supported for images and sections 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 positioned 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-offset:
+	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 'offset' 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-offset;' 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 offsets 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 offset of the first entry would be 0xfff80000 with
+	this option, instead of 0 without this option.
+
+skip-at-start:
+	This property specifies the entry offset of the first entry.
+
+	For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry
+	offset of the first entry. It can be 0xeff40000 or 0xfff40000 for
+	nor flash boot, 0x201000 for sd boot etc.
+
+	'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE +
+	Image size != 4gb.
+
+Examples of the above options can be found in the tests. See the
+tools/binman/test directory.
+
+It is possible to have the same binary appear multiple times in the image,
+either by using a unit number suffix (u-boot@0, u-boot@1) or by using a
+different name for each and specifying the type with the 'type' attribute.
+
+
+Sections and hierachical images
+-------------------------------
+
+Sometimes it is convenient to split an image into several pieces, each of which
+contains its own set of binaries. An example is a flash device where part of
+the image is read-only and part is read-write. We can set up sections for each
+of these, and place binaries in them independently. The image is still produced
+as a single output file.
+
+This feature provides a way of creating hierarchical images. For example here
+is an example image with two copies of U-Boot. One is read-only (ro), intended
+to be written only in the factory. Another is read-write (rw), so that it can be
+upgraded in the field. The sizes are fixed so that the ro/rw boundary is known
+and can be programmed:
+
+	binman {
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x100000>;
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x100000>;
+			u-boot {
+			};
+		};
+	};
+
+This image could be placed into a SPI flash chip, with the protection boundary
+set at 1MB.
+
+A few special properties are provided for sections:
+
+read-only:
+	Indicates that this section is read-only. This has no impact on binman's
+	operation, but his property can be read at run time.
+
+name-prefix:
+	This string is prepended to all the names of the binaries in the
+	section. In the example above, the 'u-boot' binaries which actually be
+	renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to
+	distinguish binaries with otherwise identical names.
+
+
+Entry Documentation
+-------------------
+
+For details on the various entry types supported by binman and how to use them,
+see README.entries. This is generated from the source code using:
+
+	binman -E >tools/binman/README.entries
+
+
+Hashing Entries
+---------------
+
+It is possible to ask binman to hash the contents of an entry and write that
+value back to the device-tree node. For example:
+
+	binman {
+		u-boot {
+			hash {
+				algo = "sha256";
+			};
+		};
+	};
+
+Here, a new 'value' property will be written to the 'hash' node containing
+the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole
+sections can be hased if desired, by adding the 'hash' node to the section.
+
+The has value can be chcked at runtime by hashing the data actually read and
+comparing this has to the value in the device tree.
+
+
+Order of image creation
+-----------------------
+
+Image creation proceeds in the following order, for each entry in the image.
+
+1. AddMissingProperties() - binman can add calculated values to the device
+tree as part of its processing, for example the offset and size of each
+entry. This method adds any properties associated with this, expanding the
+device tree as needed. These properties can have placeholder values which are
+set later by SetCalculatedProperties(). By that stage the size of sections
+cannot be changed (since it would cause the images to need to be repacked),
+but the correct values can be inserted.
+
+2. ProcessFdt() - process the device tree information as required by the
+particular entry. This may involve adding or deleting properties. If the
+processing is complete, this method should return True. If the processing
+cannot complete because it needs the ProcessFdt() method of another entry to
+run first, this method should return False, in which case it will be called
+again later.
+
+3. 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.
+
+4. GetEntryOffsets() - calls Entry.GetOffsets() 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 (offset, size). This allows an entry to
+provide the offset and size for other entries. The default implementation
+of GetEntryOffsets() returns {}.
+
+5. PackEntries() - calls Entry.Pack() which figures out the offset and
+size of an entry. The 'current' image offset is passed in, and the function
+returns the offset immediately after the entry being packed. The default
+implementation of Pack() is usually sufficient.
+
+6. 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.
+
+7. CheckEntries() - checks that the entries do not overlap, nor extend
+outside the image.
+
+8. SetCalculatedProperties() - update any calculated properties in the device
+tree. This sets the correct 'offset' and 'size' vaues, for example.
+
+9. 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 offset and size of entries should not be adjusted.
+
+10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
+See 'Access to binman entry offsets at run time' below for a description of
+what happens in this stage.
+
+11. 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
+   # This shows all the files that were considered and the one that we chose.
+   # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw)
+
+
+Access to binman entry offsets at run time (symbols)
+----------------------------------------------------
+
+Binman assembles images and determines where each entry is placed in the image.
+This information may be useful to U-Boot at run time. For example, in SPL it
+is useful to be able to find the location of U-Boot so that it can be executed
+when SPL is finished.
+
+Binman allows you to declare symbols in the SPL image which are filled in
+with their correct values during the build. For example:
+
+    binman_sym_declare(ulong, u_boot_any, offset);
+
+declares a ulong value which will be assigned to the offset of any U-Boot
+image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image.
+You can access this value with something like:
+
+    ulong u_boot_offset = binman_sym(ulong, u_boot_any, offset);
+
+Thus u_boot_offset will be set to the offset of U-Boot in memory, assuming that
+the whole image has been loaded, or is available in flash. You can then jump to
+that address to start U-Boot.
+
+At present this feature is only supported in SPL. In principle it is possible
+to fill in such symbols in U-Boot proper, as well.
+
+
+Access to binman entry offsets at run time (fdt)
+------------------------------------------------
+
+Binman can update the U-Boot FDT to include the final position and size of
+each entry in the images it processes. The option to enable this is -u and it
+causes binman to make sure that the 'offset', 'image-pos' and 'size' properties
+are set correctly for every entry. Since it is not necessary to specify these in
+the image definition, binman calculates the final values and writes these to
+the device tree. These can be used by U-Boot at run-time to find the location
+of each entry.
+
+
+Compression
+-----------
+
+Binman support compression for 'blob' entries (those of type 'blob' and
+derivatives). To enable this for an entry, add a 'compression' property:
+
+    blob {
+        filename = "datafile";
+        compression = "lz4";
+    };
+
+The entry will then contain the compressed data, using the 'lz4' compression
+algorithm. Currently this is the only one that is supported.
+
+
+
+Map files
+---------
+
+The -m option causes binman to output a .map file for each image that it
+generates. This shows the offset and size of each entry. For example:
+
+      Offset      Size  Name
+    00000000  00000028  main-section
+     00000000  00000010  section@0
+      00000000  00000004  u-boot
+     00000010  00000010  section@1
+      00000000  00000004  u-boot
+
+This shows a hierarchical image with two sections, each with a single entry. The
+offsets of the sections are absolute hex byte offsets within the image. The
+offsets of the entries are relative to their respective sections. The size of
+each entry is also shown, in bytes (hex). The indentation shows the entries
+nested inside their sections.
+
+
+Passing command-line arguments to entries
+-----------------------------------------
+
+Sometimes it is useful to pass binman the value of an entry property from the
+command line. For example some entries need access to files and it is not
+always convenient to put these filenames in the image definition (device tree).
+
+The-a option supports this:
+
+    -a<prop>=<value>
+
+where
+
+    <prop> is the property to set
+    <value> is the value to set it to
+
+Not all properties can be provided this way. Only some entries support it,
+typically for filenames.
+
+
+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-coverage python-pytest
+
+
+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' offsets
+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.
+
+If you need to specify a particular device-tree compiler to use, you can define
+the DTC environment variable. This can be useful when the system dtc is too
+old.
+
+To enable a full backtrace and other debugging features in binman, pass
+BINMAN_DEBUG=1 to your build:
+
+   make sandbox_defconfig
+   make BINMAN_DEBUG=1
+
+
+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 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:
+- Use of-platdata to make the information available to code that is unable
+  to use device tree (such as a very small SPL image)
+- Allow easy building of images by specifying just the board name
+- Produce a full Python binding for libfdt (for upstream). This is nearing
+    completion but some work remains
+- Add an option to decode an image into the constituent binaries
+- Support building an image for a board (-b) more completely, with a
+  configurable build directory
+- Consider making binman work with buildman, although if it is used in the
+  Makefile, this will be automatic
+
+--
+Simon Glass <sjg@chromium.org>
+7/7/2016
diff --git a/tools/binman/README.entries b/tools/binman/README.entries
new file mode 100644
index 0000000..9fc2f83
--- /dev/null
+++ b/tools/binman/README.entries
@@ -0,0 +1,698 @@
+Binman Entry Documentation
+===========================
+
+This file describes the entry types supported by binman. These entry types can
+be placed in an image one by one to build up a final firmware image. It is
+fairly easy to create new entry types. Just add a new file to the 'etype'
+directory. You can use the existing entries as examples.
+
+Note that some entries are subclasses of others, using and extending their
+features to produce new behaviours.
+
+
+
+Entry: blob: Entry containing an arbitrary binary blob
+------------------------------------------------------
+
+Note: This should not be used by itself. It is normally used as a parent
+class by other entry types.
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+    - compress: Compression algorithm to use:
+        none: No compression
+        lz4: Use lz4 compression (via 'lz4' command-line utility)
+
+This entry reads data from a file and places it in the entry. The
+default filename is often specified specified by the subclass. See for
+example the 'u_boot' entry which provides the filename 'u-boot.bin'.
+
+If compression is enabled, an extra 'uncomp-size' property is written to
+the node (if enabled with -u) which provides the uncompressed size of the
+data.
+
+
+
+Entry: blob-dtb: A blob that holds a device tree
+------------------------------------------------
+
+This is a blob containing a device tree. The contents of the blob are
+obtained from the list of available device-tree files, managed by the
+'state' module.
+
+
+
+Entry: blob-named-by-arg: A blob entry which gets its filename property from its subclass
+-----------------------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - <xxx>-path: Filename containing the contents of this entry (optional,
+        defaults to 0)
+
+where <xxx> is the blob_fname argument to the constructor.
+
+This entry cannot be used directly. Instead, it is used as a parent class
+for another entry, which defined blob_fname. This parameter is used to
+set the entry-arg or property containing the filename. The entry-arg or
+property is in turn used to set the actual filename.
+
+See cros_ec_rw for an example of this.
+
+
+
+Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image
+--------------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - cros-ec-rw-path: Filename containing the EC image
+
+This entry holds a Chromium OS EC (embedded controller) image, for use in
+updating the EC on startup via software sync.
+
+
+
+Entry: files: Entry containing a set of files
+---------------------------------------------
+
+Properties / Entry arguments:
+    - pattern: Filename pattern to match the files to include
+    - compress: Compression algorithm to use:
+        none: No compression
+        lz4: Use lz4 compression (via 'lz4' command-line utility)
+
+This entry reads a number of files and places each in a separate sub-entry
+within this entry. To access these you need to enable device-tree updates
+at run-time so you can obtain the file positions.
+
+
+
+Entry: fill: An entry which is filled to a particular byte value
+----------------------------------------------------------------
+
+Properties / Entry arguments:
+    - fill-byte: Byte to use to fill the entry
+
+Note that the size property must be set since otherwise this entry does not
+know how large it should be.
+
+You can often achieve the same effect using the pad-byte property of the
+overall image, in that the space between entries will then be padded with
+that byte. But this entry is sometimes useful for explicitly setting the
+byte value of a region.
+
+
+
+Entry: fmap: An entry which contains an Fmap section
+----------------------------------------------------
+
+Properties / Entry arguments:
+    None
+
+FMAP is a simple format used by flashrom, an open-source utility for
+reading and writing the SPI flash, typically on x86 CPUs. The format
+provides flashrom with a list of areas, so it knows what it in the flash.
+It can then read or write just a single area, instead of the whole flash.
+
+The format is defined by the flashrom project, in the file lib/fmap.h -
+see www.flashrom.org/Flashrom for more information.
+
+When used, this entry will be populated with an FMAP which reflects the
+entries in the current image. Note that any hierarchy is squashed, since
+FMAP does not support this.
+
+
+
+Entry: gbb: An entry which contains a Chromium OS Google Binary Block
+---------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - hardware-id: Hardware ID to use for this build (a string)
+    - keydir: Directory containing the public keys to use
+    - bmpblk: Filename containing images used by recovery
+
+Chromium OS uses a GBB to store various pieces of information, in particular
+the root and recovery keys that are used to verify the boot process. Some
+more details are here:
+
+    https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
+
+but note that the page dates from 2013 so is quite out of date. See
+README.chromium for how to obtain the required keys and tools.
+
+
+
+Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file
+-------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains microcode for some devices in a special format. An
+example filename is 'Microcode/C0_22211.BIN'.
+
+See README.x86 for information about x86 binary blobs.
+
+
+
+Entry: intel-descriptor: Intel flash descriptor block (4KB)
+-----------------------------------------------------------
+
+Properties / Entry arguments:
+    filename: Filename of file containing the descriptor. This is typically
+        a 4KB binary file, sometimes called 'descriptor.bin'
+
+This entry 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 (Management Engine) region, allowing us to place the ME
+binary in the right place.
+
+With this entry in your image, the position of the 'intel-me' entry will be
+fixed in the image, which avoids you needed to specify an offset for that
+region. This is useful, because it is not possible to change the position
+of the ME region without updating the descriptor.
+
+See README.x86 for information about x86 binary blobs.
+
+
+
+Entry: intel-fsp: Entry containing an Intel Firmware Support Package (FSP) file
+-------------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains binary blobs which are used on some devices to make the
+platform work. U-Boot executes this code since it is not possible to set up
+the hardware using U-Boot open-source code. Documentation is typically not
+available in sufficient detail to allow this.
+
+An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd'
+
+See README.x86 for information about x86 binary blobs.
+
+
+
+Entry: intel-me: Entry containing an Intel Management Engine (ME) file
+----------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains code used by the SoC that is required to make it work.
+The Management Engine is like a background task that runs things that are
+not clearly documented, but may include keyboard, deplay and network
+access. For platform that use ME it is not possible to disable it. U-Boot
+does not directly execute code in the ME binary.
+
+A typical filename is 'me.bin'.
+
+See README.x86 for information about x86 binary blobs.
+
+
+
+Entry: intel-mrc: Entry containing an Intel Memory Reference Code (MRC) file
+----------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains code for setting up the SDRAM on some Intel systems. This
+is executed by U-Boot when needed early during startup. A typical filename
+is 'mrc.bin'.
+
+See README.x86 for information about x86 binary blobs.
+
+
+
+Entry: intel-vbt: Entry containing an Intel Video BIOS Table (VBT) file
+-----------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains code that sets up the integrated graphics subsystem on
+some Intel SoCs. U-Boot executes this when the display is started up.
+
+See README.x86 for information about Intel binary blobs.
+
+
+
+Entry: intel-vga: Entry containing an Intel Video Graphics Adaptor (VGA) file
+-----------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of file to read into entry
+
+This file contains code that sets up the integrated graphics subsystem on
+some Intel SoCs. U-Boot executes this when the display is started up.
+
+This is similar to the VBT file but in a different format.
+
+See README.x86 for information about Intel binary blobs.
+
+
+
+Entry: powerpc-mpc85xx-bootpg-resetvec: PowerPC mpc85xx bootpg + resetvec code for U-Boot
+-----------------------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot-br.bin (default 'u-boot-br.bin')
+
+This enrty is valid for PowerPC mpc85xx cpus. This entry holds
+'bootpg + resetvec' code for PowerPC mpc85xx CPUs which needs to be
+placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'.
+
+
+
+Entry: section: Entry that contains other entries
+-------------------------------------------------
+
+Properties / Entry arguments: (see binman README for more information)
+    - size: Size of section in bytes
+    - align-size: Align size to a particular power of two
+    - pad-before: Add padding before the entry
+    - pad-after: Add padding after the entry
+    - pad-byte: Pad byte to use when padding
+    - sort-by-offset: Reorder the entries by offset
+    - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
+    - name-prefix: Adds a prefix to the name of every entry in the section
+        when writing out the map
+
+A section is an entry which can contain other entries, thus allowing
+hierarchical images to be created. See 'Sections and hierarchical images'
+in the binman README for more information.
+
+
+
+Entry: text: An entry which contains text
+-----------------------------------------
+
+The text can be provided either in the node itself or by a command-line
+argument. There is a level of indirection to allow multiple text strings
+and sharing of text.
+
+Properties / Entry arguments:
+    text-label: The value of this string indicates the property / entry-arg
+        that contains the string to place in the entry
+    <xxx> (actual name is the value of text-label): contains the string to
+        place in the entry.
+
+Example node:
+
+    text {
+        size = <50>;
+        text-label = "message";
+    };
+
+You can then use:
+
+    binman -amessage="this is my message"
+
+and binman will insert that string into the entry.
+
+It is also possible to put the string directly in the node:
+
+    text {
+        size = <8>;
+        text-label = "message";
+        message = "a message directly in the node"
+    };
+
+The text is not itself nul-terminated. This can be achieved, if required,
+by setting the size of the entry to something larger than the text.
+
+
+
+Entry: u-boot: U-Boot flat binary
+---------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.bin (default 'u-boot.bin')
+
+This is the U-Boot binary, containing relocation information to allow it
+to relocate itself at runtime. The binary typically includes a device tree
+blob at the end of it. Use u_boot_nodtb if you want to package the device
+tree separately.
+
+U-Boot can access binman symbols at runtime. See:
+
+    'Access to binman entry offsets at run time (fdt)'
+
+in the binman README for more information.
+
+
+
+Entry: u-boot-dtb: U-Boot device tree
+-------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.dtb (default 'u-boot.dtb')
+
+This is the U-Boot device tree, containing configuration information for
+U-Boot. U-Boot needs this to know what devices are present and which drivers
+to activate.
+
+Note: This is mostly an internal entry type, used by others. This allows
+binman to know which entries contain a device tree.
+
+
+
+Entry: u-boot-dtb-with-ucode: A U-Boot device tree file, with the microcode removed
+-----------------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.dtb (default 'u-boot.dtb')
+
+See Entry_u_boot_ucode for full details of the three entries involved in
+this process. This entry provides the U-Boot device-tree file, which
+contains the microcode. If the microcode is not being collated into one
+place then the offset and size of the microcode is recorded by this entry,
+for use by u_boot_with_ucode_ptr. If it is being collated, then this
+entry deletes the microcode from the device tree (to save space) and makes
+it available to u_boot_ucode.
+
+
+
+Entry: u-boot-elf: U-Boot ELF image
+-----------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot (default 'u-boot')
+
+This is the U-Boot ELF image. It does not include a device tree but can be
+relocated to any address for execution.
+
+
+
+Entry: u-boot-img: U-Boot legacy image
+--------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.img (default 'u-boot.img')
+
+This is the U-Boot binary as a packaged image, in legacy format. It has a
+header which allows it to be loaded at the correct address for execution.
+
+You should use FIT (Flat Image Tree) instead of the legacy image for new
+applications.
+
+
+
+Entry: u-boot-nodtb: U-Boot flat binary without device tree appended
+--------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin')
+
+This is the U-Boot binary, containing relocation information to allow it
+to relocate itself at runtime. It does not include a device tree blob at
+the end of it so normally cannot work without it. You can add a u_boot_dtb
+entry after this one, or use a u_boot entry instead (which contains both
+U-Boot and the device tree).
+
+
+
+Entry: u-boot-spl: U-Boot SPL binary
+------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin')
+
+This is the U-Boot SPL (Secondary Program Loader) binary. This is a small
+binary which loads before U-Boot proper, typically into on-chip SRAM. It is
+responsible for locating, loading and jumping to U-Boot. Note that SPL is
+not relocatable so must be loaded to the correct address in SRAM, or written
+to run from the correct address if direct flash execution is possible (e.g.
+on x86 devices).
+
+SPL can access binman symbols at runtime. See:
+
+    'Access to binman entry offsets at run time (symbols)'
+
+in the binman README for more information.
+
+The ELF file 'spl/u-boot-spl' must also be available for this to work, since
+binman uses that to look up symbols to write into the SPL binary.
+
+
+
+Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region
+---------------------------------------------------------------------
+
+Properties / Entry arguments:
+    None
+
+This is similar to u_boot_spl except that padding is added after the SPL
+binary to cover the BSS (Block Started by Symbol) region. This region holds
+the various used by SPL. It is set to 0 by SPL when it starts up. If you
+want to append data to the SPL image (such as a device tree file), you must
+pad out the BSS region to avoid the data overlapping with U-Boot variables.
+This entry is useful in that case. It automatically pads out the entry size
+to cover both the code, data and BSS.
+
+The ELF file 'spl/u-boot-spl' must also be available for this to work, since
+binman uses that to look up the BSS address.
+
+
+
+Entry: u-boot-spl-dtb: U-Boot SPL device tree
+---------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb')
+
+This is the SPL device tree, containing configuration information for
+SPL. SPL needs this to know what devices are present and which drivers
+to activate.
+
+
+
+Entry: u-boot-spl-elf: U-Boot SPL ELF image
+-------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of SPL u-boot (default 'spl/u-boot')
+
+This is the U-Boot SPL ELF image. It does not include a device tree but can
+be relocated to any address for execution.
+
+
+
+Entry: u-boot-spl-nodtb: SPL binary without device tree appended
+----------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of spl/u-boot-spl-nodtb.bin (default
+        'spl/u-boot-spl-nodtb.bin')
+
+This is the U-Boot SPL binary, It does not include a device tree blob at
+the end of it so may not be able to work without it, assuming SPL needs
+a device tree to operation on your platform. You can add a u_boot_spl_dtb
+entry after this one, or use a u_boot_spl entry instead (which contains
+both SPL and the device tree).
+
+
+
+Entry: u-boot-spl-with-ucode-ptr: U-Boot SPL with embedded microcode pointer
+----------------------------------------------------------------------------
+
+This is used when SPL must set up the microcode for U-Boot.
+
+See Entry_u_boot_ucode for full details of the entries involved in this
+process.
+
+
+
+Entry: u-boot-tpl: U-Boot TPL binary
+------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin')
+
+This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small
+binary which loads before SPL, typically into on-chip SRAM. It is
+responsible for locating, loading and jumping to SPL, the next-stage
+loader. Note that SPL is not relocatable so must be loaded to the correct
+address in SRAM, or written to run from the correct address if direct
+flash execution is possible (e.g. on x86 devices).
+
+SPL can access binman symbols at runtime. See:
+
+    'Access to binman entry offsets at run time (symbols)'
+
+in the binman README for more information.
+
+The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
+binman uses that to look up symbols to write into the TPL binary.
+
+
+
+Entry: u-boot-tpl-dtb: U-Boot TPL device tree
+---------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb')
+
+This is the TPL device tree, containing configuration information for
+TPL. TPL needs this to know what devices are present and which drivers
+to activate.
+
+
+
+Entry: u-boot-tpl-dtb-with-ucode: U-Boot TPL with embedded microcode pointer
+----------------------------------------------------------------------------
+
+This is used when TPL must set up the microcode for U-Boot.
+
+See Entry_u_boot_ucode for full details of the entries involved in this
+process.
+
+
+
+Entry: u-boot-tpl-with-ucode-ptr: U-Boot TPL with embedded microcode pointer
+----------------------------------------------------------------------------
+
+See Entry_u_boot_ucode for full details of the entries involved in this
+process.
+
+
+
+Entry: u-boot-ucode: U-Boot microcode block
+-------------------------------------------
+
+Properties / Entry arguments:
+    None
+
+The contents of this entry are filled in automatically by other entries
+which must also be in the image.
+
+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.
+
+
+
+Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer
+--------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
+    - optional-ucode: boolean property to make microcode optional. If the
+        u-boot.bin image does not include microcode, no error will
+        be generated.
+
+See Entry_u_boot_ucode for full details of the three entries involved in
+this process. This entry updates U-Boot with the offset and size of the
+microcode, to allow early x86 boot code to find it without doing anything
+complicated. Otherwise it is the same as the u_boot entry.
+
+
+
+Entry: vblock: An entry which contains a Chromium OS verified boot block
+------------------------------------------------------------------------
+
+Properties / Entry arguments:
+    - keydir: Directory containing the public keys to use
+    - keyblock: Name of the key file to use (inside keydir)
+    - signprivate: Name of provide key file to use (inside keydir)
+    - version: Version number of the vblock (typically 1)
+    - kernelkey: Name of the kernel key to use (inside keydir)
+    - preamble-flags: Value of the vboot preamble flags (typically 0)
+
+Output files:
+    - input.<unique_name> - input file passed to futility
+    - vblock.<unique_name> - output file generated by futility (which is
+        used as the entry contents)
+
+Chromium OS signs the read-write firmware and kernel, writing the signature
+in this block. This allows U-Boot to verify that the next firmware stage
+and kernel are genuine.
+
+
+
+Entry: x86-start16: x86 16-bit start-up code for U-Boot
+-------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of u-boot-x86-16bit.bin (default
+        'u-boot-x86-16bit.bin')
+
+x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code
+must be placed at a particular address. This entry holds that code. It is
+typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+for changing to 32-bit mode and jumping to U-Boot's entry point, which
+requires 32-bit mode (for 32-bit U-Boot).
+
+For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead.
+
+
+
+Entry: x86-start16-spl: x86 16-bit start-up code for SPL
+--------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of spl/u-boot-x86-16bit-spl.bin (default
+        'spl/u-boot-x86-16bit-spl.bin')
+
+x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
+must be placed at a particular address. This entry holds that code. It is
+typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+for changing to 32-bit mode and starting SPL, which in turn changes to
+64-bit mode and jumps to U-Boot (for 64-bit U-Boot).
+
+For 32-bit U-Boot, the 'x86_start16' entry type is used instead.
+
+
+
+Entry: x86-start16-tpl: x86 16-bit start-up code for TPL
+--------------------------------------------------------
+
+Properties / Entry arguments:
+    - filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default
+        'tpl/u-boot-x86-16bit-tpl.bin')
+
+x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
+must be placed at a particular address. This entry holds that code. It is
+typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+for changing to 32-bit mode and starting TPL, which in turn jumps to SPL.
+
+If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types
+may be used instead.
+
+
+
diff --git a/tools/binman/binman b/tools/binman/binman
new file mode 120000
index 0000000..979b7e4
--- /dev/null
+++ b/tools/binman/binman
@@ -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
index 0000000..439908e
--- /dev/null
+++ b/tools/binman/binman.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python2
+# SPDX-License-Identifier: GPL-2.0+
+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Creates binary images from input files controlled by a description
+#
+
+"""See README for more information"""
+
+import glob
+import multiprocessing
+import os
+import sys
+import traceback
+import unittest
+
+# Bring in the patman and dtoc libraries
+our_path = os.path.dirname(os.path.realpath(__file__))
+for dirname in ['../patman', '../dtoc', '..', '../concurrencytest']:
+    sys.path.insert(0, os.path.join(our_path, dirname))
+
+# Bring in the libfdt module
+sys.path.insert(0, 'scripts/dtc/pylibfdt')
+sys.path.insert(0, os.path.join(our_path,
+                '../../build-sandbox_spl/scripts/dtc/pylibfdt'))
+
+import cmdline
+import command
+use_concurrent = True
+try:
+    from concurrencytest import ConcurrentTestSuite, fork_for_tests
+except:
+    use_concurrent = False
+import control
+import test_util
+
+def RunTests(debug, processes, args):
+    """Run the functional tests and any embedded doctests
+
+    Args:
+        debug: True to enable debugging, which shows a full stack trace on error
+        args: List of positional args provided to binman. This can hold a test
+            name to execute (as in 'binman -t testSections', for example)
+        processes: Number of processes to use to run tests (None=same as #CPUs)
+    """
+    import elf_test
+    import entry_test
+    import fdt_test
+    import ftest
+    import image_test
+    import test
+    import doctest
+
+    result = unittest.TestResult()
+    for module in []:
+        suite = doctest.DocTestSuite(module)
+        suite.run(result)
+
+    sys.argv = [sys.argv[0]]
+    if debug:
+        sys.argv.append('-D')
+    if debug:
+        sys.argv.append('-D')
+
+    # Run the entry tests first ,since these need to be the first to import the
+    # 'entry' module.
+    test_name = args and args[0] or None
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt,
+                   elf_test.TestElf, image_test.TestImage):
+        if test_name:
+            try:
+                suite.addTests(loader.loadTestsFromName(test_name, module))
+            except AttributeError:
+                continue
+        else:
+            suite.addTests(loader.loadTestsFromTestCase(module))
+    if use_concurrent and processes != 1:
+        concurrent_suite = ConcurrentTestSuite(suite,
+                fork_for_tests(processes or multiprocessing.cpu_count()))
+        concurrent_suite.run(result)
+    else:
+        suite.run(result)
+
+    print result
+    for test, err in result.errors:
+        print test.id(), err
+    for test, err in result.failures:
+        print err, result.failures
+    if result.errors or result.failures:
+      print 'binman tests FAILED'
+      return 1
+    return 0
+
+def GetEntryModules(include_testing=True):
+    """Get a set of entry class implementations
+
+    Returns:
+        Set of paths to entry class filenames
+    """
+    glob_list = glob.glob(os.path.join(our_path, 'etype/*.py'))
+    return set([os.path.splitext(os.path.basename(item))[0]
+                for item in glob_list
+                if include_testing or '_testing' not in item])
+
+def RunTestCoverage():
+    """Run the tests and check that we get 100% coverage"""
+    glob_list = GetEntryModules(False)
+    all_set = set([os.path.splitext(os.path.basename(item))[0]
+                   for item in glob_list if '_testing' not in item])
+    test_util.RunTestCoverage('tools/binman/binman.py', None,
+            ['*test*', '*binman.py', 'tools/patman/*', 'tools/dtoc/*'],
+            options.build_dir, all_set)
+
+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:
+        ret_code = RunTests(options.debug, options.processes, args[1:])
+
+    elif options.test_coverage:
+        RunTestCoverage()
+
+    elif options.entry_docs:
+        control.WriteEntryDocs(GetEntryModules())
+
+    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/bsection.py b/tools/binman/bsection.py
new file mode 100644
index 0000000..ccf2920
--- /dev/null
+++ b/tools/binman/bsection.py
@@ -0,0 +1,464 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Base class for sections (collections of entries)
+#
+
+from __future__ import print_function
+
+from collections import OrderedDict
+from sets import Set
+import sys
+
+import fdt_util
+import re
+import state
+import tools
+
+class Section(object):
+    """A section which contains multiple entries
+
+    A section represents a collection of entries. There must be one or more
+    sections in an image. Sections are used to group entries together.
+
+    Attributes:
+        _node: Node object that contains the section definition in device tree
+        _parent_section: Parent Section object which created this Section
+        _size: Section size in bytes, or None if not known yet
+        _align_size: Section size alignment, or None
+        _pad_before: Number of bytes before the first entry starts. This
+            effectively changes the place where entry offset 0 starts
+        _pad_after: Number of bytes after the last entry ends. The last
+            entry will finish on or before this boundary
+        _pad_byte: Byte to use to pad the section where there is no entry
+        _sort: True if entries should be sorted by offset, False if they
+            must be in-order in the device tree description
+        _skip_at_start: Number of bytes before the first entry starts. These
+            effectively adjust the starting offset of entries. For example,
+            if _pad_before is 16, then the first entry would start at 16.
+            An entry with offset = 20 would in fact be written at offset 4
+            in the image file.
+        _end_4gb: Indicates that the section ends at the 4GB boundary. This is
+            used for x86 images, which want to use offsets such that a memory
+            address (like 0xff800000) is the first entry offset. This causes
+            _skip_at_start to be set to the starting memory address.
+        _name_prefix: Prefix to add to the name of all entries within this
+            section
+        _entries: OrderedDict() of entries
+    """
+    def __init__(self, name, parent_section, node, image, test=False):
+        global entry
+        global Entry
+        import entry
+        from entry import Entry
+
+        self._parent_section = parent_section
+        self._name = name
+        self._node = node
+        self._image = image
+        self._offset = 0
+        self._size = None
+        self._align_size = None
+        self._pad_before = 0
+        self._pad_after = 0
+        self._pad_byte = 0
+        self._sort = False
+        self._skip_at_start = None
+        self._end_4gb = False
+        self._name_prefix = ''
+        self._entries = OrderedDict()
+        self._image_pos = None
+        if not test:
+            self._ReadNode()
+            self._ReadEntries()
+
+    def _ReadNode(self):
+        """Read properties from the section node"""
+        self._size = fdt_util.GetInt(self._node, 'size')
+        self._align_size = fdt_util.GetInt(self._node, 'align-size')
+        if tools.NotPowerOfTwo(self._align_size):
+            self._Raise("Alignment size %s must be a power of two" %
+                        self._align_size)
+        self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
+        self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
+        self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
+        self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
+        self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
+        self._skip_at_start = fdt_util.GetInt(self._node, 'skip-at-start')
+        if self._end_4gb:
+            if not self._size:
+                self._Raise("Section size must be provided when using end-at-4gb")
+            if self._skip_at_start is not None:
+                self._Raise("Provide either 'end-at-4gb' or 'skip-at-start'")
+            else:
+                self._skip_at_start = 0x100000000 - self._size
+        else:
+            if self._skip_at_start is None:
+                self._skip_at_start = 0
+        self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
+
+    def _ReadEntries(self):
+        for node in self._node.subnodes:
+            if node.name == 'hash':
+                continue
+            entry = Entry.Create(self, node)
+            entry.SetPrefix(self._name_prefix)
+            self._entries[node.name] = entry
+
+    def GetFdtSet(self):
+        """Get the set of device tree files used by this image"""
+        fdt_set = Set()
+        for entry in self._entries.values():
+            fdt_set.update(entry.GetFdtSet())
+        return fdt_set
+
+    def SetOffset(self, offset):
+        self._offset = offset
+
+    def ExpandEntries(self):
+        for entry in self._entries.values():
+            entry.ExpandEntries()
+
+    def AddMissingProperties(self):
+        """Add new properties to the device tree as needed for this entry"""
+        for prop in ['offset', 'size', 'image-pos']:
+            if not prop in self._node.props:
+                state.AddZeroProp(self._node, prop)
+        state.CheckAddHashProp(self._node)
+        for entry in self._entries.values():
+            entry.AddMissingProperties()
+
+    def SetCalculatedProperties(self):
+        state.SetInt(self._node, 'offset', self._offset)
+        state.SetInt(self._node, 'size', self._size)
+        image_pos = self._image_pos
+        if self._parent_section:
+            image_pos -= self._parent_section.GetRootSkipAtStart()
+        state.SetInt(self._node, 'image-pos', image_pos)
+        for entry in self._entries.values():
+            entry.SetCalculatedProperties()
+
+    def ProcessFdt(self, fdt):
+        todo = self._entries.values()
+        for passnum in range(3):
+            next_todo = []
+            for entry in todo:
+                if not entry.ProcessFdt(fdt):
+                    next_todo.append(entry)
+            todo = next_todo
+            if not todo:
+                break
+        if todo:
+            self._Raise('Internal error: Could not complete processing of Fdt: '
+                        'remaining %s' % todo)
+        return True
+
+    def CheckSize(self):
+        """Check that the section contents does not exceed its size, etc."""
+        contents_size = 0
+        for entry in self._entries.values():
+            contents_size = max(contents_size, entry.offset + entry.size)
+
+        contents_size -= self._skip_at_start
+
+        size = self._size
+        if not size:
+            size = self._pad_before + contents_size + self._pad_after
+            size = tools.Align(size, self._align_size)
+
+        if self._size and contents_size > self._size:
+            self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
+                       (contents_size, contents_size, self._size, self._size))
+        if not self._size:
+            self._size = size
+        if self._size != tools.Align(self._size, self._align_size):
+            self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
+                  (self._size, self._size, self._align_size, self._align_size))
+        return size
+
+    def _Raise(self, msg):
+        """Raises an error for this section
+
+        Args:
+            msg: Error message to use in the raise string
+        Raises:
+            ValueError()
+        """
+        raise ValueError("Section '%s': %s" % (self._node.path, msg))
+
+    def GetPath(self):
+        """Get the path of an image (in the FDT)
+
+        Returns:
+            Full path of the node for this image
+        """
+        return self._node.path
+
+    def FindEntryType(self, etype):
+        """Find an entry type in the section
+
+        Args:
+            etype: Entry type to find
+        Returns:
+            entry matching that type, or None if not found
+        """
+        for entry in self._entries.values():
+            if entry.etype == etype:
+                return entry
+        return None
+
+    def GetEntryContents(self):
+        """Call ObtainContents() for each entry
+
+        This calls each entry's ObtainContents() a few times until they all
+        return True. We stop calling an entry's function once it returns
+        True. This allows the contents of one entry to depend on another.
+
+        After 3 rounds we give up since it's likely an error.
+        """
+        todo = self._entries.values()
+        for passnum in range(3):
+            next_todo = []
+            for entry in todo:
+                if not entry.ObtainContents():
+                    next_todo.append(entry)
+            todo = next_todo
+            if not todo:
+                break
+        if todo:
+            self._Raise('Internal error: Could not complete processing of '
+                        'contents: remaining %s' % todo)
+        return True
+
+    def _SetEntryOffsetSize(self, name, offset, size):
+        """Set the offset and size of an entry
+
+        Args:
+            name: Entry name to update
+            offset: New offset
+            size: New size
+        """
+        entry = self._entries.get(name)
+        if not entry:
+            self._Raise("Unable to set offset/size for unknown entry '%s'" %
+                        name)
+        entry.SetOffsetSize(self._skip_at_start + offset, size)
+
+    def GetEntryOffsets(self):
+        """Handle entries that want to set the offset/size of other entries
+
+        This calls each entry's GetOffsets() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        for entry in self._entries.values():
+            offset_dict = entry.GetOffsets()
+            for name, info in offset_dict.iteritems():
+                self._SetEntryOffsetSize(name, *info)
+
+    def PackEntries(self):
+        """Pack all entries into the section"""
+        offset = self._skip_at_start
+        for entry in self._entries.values():
+            offset = entry.Pack(offset)
+        self._size = self.CheckSize()
+
+    def _SortEntries(self):
+        """Sort entries by offset"""
+        entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
+        self._entries.clear()
+        for entry in entries:
+            self._entries[entry._node.name] = entry
+
+    def _ExpandEntries(self):
+        """Expand any entries that are permitted to"""
+        exp_entry = None
+        for entry in self._entries.values():
+            if exp_entry:
+                exp_entry.ExpandToLimit(entry.offset)
+                exp_entry = None
+            if entry.expand_size:
+                exp_entry = entry
+        if exp_entry:
+            exp_entry.ExpandToLimit(self._size)
+
+    def CheckEntries(self):
+        """Check that entries do not overlap or extend outside the section
+
+        This also sorts entries, if needed and expands
+        """
+        if self._sort:
+            self._SortEntries()
+        self._ExpandEntries()
+        offset = 0
+        prev_name = 'None'
+        for entry in self._entries.values():
+            entry.CheckOffset()
+            if (entry.offset < self._skip_at_start or
+                entry.offset + entry.size > self._skip_at_start + self._size):
+                entry.Raise("Offset %#x (%d) is outside the section starting "
+                            "at %#x (%d)" %
+                            (entry.offset, entry.offset, self._skip_at_start,
+                             self._skip_at_start))
+            if entry.offset < offset:
+                entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
+                            "ending at %#x (%d)" %
+                            (entry.offset, entry.offset, prev_name, offset, offset))
+            offset = entry.offset + entry.size
+            prev_name = entry.GetPath()
+
+    def SetImagePos(self, image_pos):
+        self._image_pos = image_pos
+        for entry in self._entries.values():
+            entry.SetImagePos(image_pos)
+
+    def ProcessEntryContents(self):
+        """Call the ProcessContents() method for each entry
+
+        This is intended to adjust the contents as needed by the entry type.
+        """
+        for entry in self._entries.values():
+            entry.ProcessContents()
+
+    def WriteSymbols(self):
+        """Write symbol values into binary files for access at run time"""
+        for entry in self._entries.values():
+            entry.WriteSymbols(self)
+
+    def BuildSection(self, fd, base_offset):
+        """Write the section to a file"""
+        fd.seek(base_offset)
+        fd.write(self.GetData())
+
+    def GetData(self):
+        """Get the contents of the section"""
+        section_data = chr(self._pad_byte) * self._size
+
+        for entry in self._entries.values():
+            data = entry.GetData()
+            base = self._pad_before + entry.offset - self._skip_at_start
+            section_data = (section_data[:base] + data +
+                            section_data[base + len(data):])
+        return section_data
+
+    def LookupSymbol(self, sym_name, optional, msg):
+        """Look up a symbol in an ELF file
+
+        Looks up a symbol in an ELF file. Only entry types which come from an
+        ELF image can be used by this function.
+
+        At present the only entry property supported is offset.
+
+        Args:
+            sym_name: Symbol name in the ELF file to look up in the format
+                _binman_<entry>_prop_<property> where <entry> is the name of
+                the entry and <property> is the property to find (e.g.
+                _binman_u_boot_prop_offset). As a special case, you can append
+                _any to <entry> to have it search for any matching entry. E.g.
+                _binman_u_boot_any_prop_offset will match entries called u-boot,
+                u-boot-img and u-boot-nodtb)
+            optional: True if the symbol is optional. If False this function
+                will raise if the symbol is not found
+            msg: Message to display if an error occurs
+
+        Returns:
+            Value that should be assigned to that symbol, or None if it was
+                optional and not found
+
+        Raises:
+            ValueError if the symbol is invalid or not found, or references a
+                property which is not supported
+        """
+        m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name)
+        if not m:
+            raise ValueError("%s: Symbol '%s' has invalid format" %
+                             (msg, sym_name))
+        entry_name, prop_name = m.groups()
+        entry_name = entry_name.replace('_', '-')
+        entry = self._entries.get(entry_name)
+        if not entry:
+            if entry_name.endswith('-any'):
+                root = entry_name[:-4]
+                for name in self._entries:
+                    if name.startswith(root):
+                        rest = name[len(root):]
+                        if rest in ['', '-img', '-nodtb']:
+                            entry = self._entries[name]
+        if not entry:
+            err = ("%s: Entry '%s' not found in list (%s)" %
+                   (msg, entry_name, ','.join(self._entries.keys())))
+            if optional:
+                print('Warning: %s' % err, file=sys.stderr)
+                return None
+            raise ValueError(err)
+        if prop_name == 'offset':
+            return entry.offset
+        elif prop_name == 'image_pos':
+            return entry.image_pos
+        else:
+            raise ValueError("%s: No such property '%s'" % (msg, prop_name))
+
+    def GetEntries(self):
+        """Get the number of entries in a section
+
+        Returns:
+            Number of entries in a section
+        """
+        return self._entries
+
+    def GetSize(self):
+        """Get the size of a section in bytes
+
+        This is only meaningful if the section has a pre-defined size, or the
+        entries within it have been packed, so that the size has been
+        calculated.
+
+        Returns:
+            Entry size in bytes
+        """
+        return self._size
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the section to a .map file
+
+        Args:
+            fd: File to write the map to
+        """
+        Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size,
+                           self._image_pos)
+        for entry in self._entries.values():
+            entry.WriteMap(fd, indent + 1)
+
+    def GetContentsByPhandle(self, phandle, source_entry):
+        """Get the data contents of an entry specified by a phandle
+
+        This uses a phandle to look up a node and and find the entry
+        associated with it. Then it returnst he contents of that entry.
+
+        Args:
+            phandle: Phandle to look up (integer)
+            source_entry: Entry containing that phandle (used for error
+                reporting)
+
+        Returns:
+            data from associated entry (as a string), or None if not found
+        """
+        node = self._node.GetFdt().LookupPhandle(phandle)
+        if not node:
+            source_entry.Raise("Cannot find node for phandle %d" % phandle)
+        for entry in self._entries.values():
+            if entry._node == node:
+                return entry.GetData()
+        source_entry.Raise("Cannot find entry for node '%s'" % node.name)
+
+    def ExpandSize(self, size):
+        if size != self._size:
+            self._size = size
+
+    def GetRootSkipAtStart(self):
+        if self._parent_section:
+            return self._parent_section.GetRootSkipAtStart()
+        return self._skip_at_start
+
+    def GetImageSize(self):
+        return self._image._size
diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py
new file mode 100644
index 0000000..3886d52
--- /dev/null
+++ b/tools/binman/cmdline.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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('-a', '--entry-arg', type='string', action='append',
+            help='Set argument value arg=value')
+    parser.add_option('-b', '--board', type='string',
+            help='Board name to build')
+    parser.add_option('-B', '--build-dir', type='string', default='b',
+            help='Directory containing the build output')
+    parser.add_option('-d', '--dt', type='string',
+            help='Configuration file (.dtb) to use')
+    parser.add_option('-D', '--debug', action='store_true',
+            help='Enabling debugging (provides a full traceback on error)')
+    parser.add_option('-E', '--entry-docs', action='store_true',
+            help='Write out entry documentation (see README.entries)')
+    parser.add_option('--fake-dtb', action='store_true',
+            help='Use fake device tree contents (for testing only)')
+    parser.add_option('-i', '--image', type='string', action='append',
+            help='Image filename to build (if not specified, build all)')
+    parser.add_option('-I', '--indir', action='append',
+            help='Add a path to a directory to use for input files')
+    parser.add_option('-H', '--full-help', action='store_true',
+        default=False, help='Display the README file')
+    parser.add_option('-m', '--map', action='store_true',
+        default=False, help='Output a map file for each image')
+    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('-P', '--processes', type=int,
+                      help='set number of processes to use for running tests')
+    parser.add_option('-t', '--test', action='store_true',
+                    default=False, help='run tests')
+    parser.add_option('-T', '--test-coverage', action='store_true',
+                    default=False, help='run tests and check for 100% coverage')
+    parser.add_option('-u', '--update-fdt', action='store_true',
+        default=False, help='Update the binman node with offset/size info')
+    parser.add_option('-v', '--verbosity', default=1,
+        type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
+        '4=debug')
+
+    parser.usage += """
+
+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
index 0000000..3446e2e
--- /dev/null
+++ b/tools/binman/control.py
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Creates binary images from input files controlled by a description
+#
+
+from collections import OrderedDict
+import os
+import sys
+import tools
+
+import command
+import elf
+from image import Image
+import state
+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(dtb):
+    """Find the 'binman' node in the device tree
+
+    Args:
+        dtb: Fdt object to scan
+    Returns:
+        Node object of /binman node, or None if not found
+    """
+    for node in dtb.GetRoot().subnodes:
+        if node.name == 'binman':
+            return node
+    return None
+
+def WriteEntryDocs(modules, test_missing=None):
+    """Write out documentation for all entries
+
+    Args:
+        modules: List of Module objects to get docs for
+        test_missing: Used for testing only, to force an entry's documeentation
+            to show as missing even if it is present. Should be set to None in
+            normal use.
+    """
+    from entry import Entry
+    Entry.WriteDocs(modules, test_missing)
+
+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:
+        # Import these here in case libfdt.py is not available, in which case
+        # the above help option still works.
+        import fdt
+        import fdt_util
+
+        tout.Init(options.verbosity)
+        elf.debug = options.debug
+        state.use_fake_dtb = options.fake_dtb
+        try:
+            tools.SetInputDirs(options.indir)
+            tools.PrepareOutputDir(options.outdir, options.preserve)
+            state.SetEntryArgs(options.entry_arg)
+
+            # Get the device tree ready by compiling it and copying the compiled
+            # output into a file in our output directly. Then scan it for use
+            # in binman.
+            dtb_fname = fdt_util.EnsureCompiled(dtb_fname)
+            fname = tools.GetOutputFilename('u-boot.dtb.out')
+            tools.WriteFile(fname, tools.ReadFile(dtb_fname))
+            dtb = fdt.FdtScan(fname)
+
+            node = _FindBinmanNode(dtb)
+            if not node:
+                raise ValueError("Device tree '%s' does not have a 'binman' "
+                                 "node" % dtb_fname)
+
+            images = _ReadImageDesc(node)
+
+            if options.image:
+                skip = []
+                for name, image in images.iteritems():
+                    if name not in options.image:
+                        del images[name]
+                        skip.append(name)
+                if skip:
+                    print 'Skipping images: %s\n' % ', '.join(skip)
+
+            state.Prepare(images, dtb)
+
+            # Prepare the device tree by making sure that any missing
+            # properties are added (e.g. 'pos' and 'size'). The values of these
+            # may not be correct yet, but we add placeholders so that the
+            # size of the device tree is correct. Later, in
+            # SetCalculatedProperties() we will insert the correct values
+            # without changing the device-tree size, thus ensuring that our
+            # entry offsets remain the same.
+            for image in images.values():
+                image.ExpandEntries()
+                if options.update_fdt:
+                    image.AddMissingProperties()
+                image.ProcessFdt(dtb)
+
+            for dtb_item in state.GetFdts():
+                dtb_item.Sync(auto_resize=True)
+                dtb_item.Pack()
+                dtb_item.Flush()
+
+            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.GetEntryOffsets()
+                try:
+                    image.PackEntries()
+                    image.CheckSize()
+                    image.CheckEntries()
+                except Exception as e:
+                    if options.map:
+                        fname = image.WriteMap()
+                        print "Wrote map file '%s' to show errors"  % fname
+                    raise
+                image.SetImagePos()
+                if options.update_fdt:
+                    image.SetCalculatedProperties()
+                    for dtb_item in state.GetFdts():
+                        dtb_item.Sync()
+                image.ProcessEntryContents()
+                image.WriteSymbols()
+                image.BuildImage()
+                if options.map:
+                    image.WriteMap()
+
+            # Write the updated FDTs to our output files
+            for dtb_item in state.GetFdts():
+                tools.WriteFile(dtb_item._fname, dtb_item.GetContents())
+
+        finally:
+            tools.FinaliseOutputDir()
+    finally:
+        tout.Uninit()
+
+    return 0
diff --git a/tools/binman/elf.py b/tools/binman/elf.py
new file mode 100644
index 0000000..97df8e3
--- /dev/null
+++ b/tools/binman/elf.py
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Handle various things related to ELF images
+#
+
+from collections import namedtuple, OrderedDict
+import command
+import os
+import re
+import struct
+
+import tools
+
+# This is enabled from control.py
+debug = False
+
+Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak'])
+
+
+def GetSymbols(fname, patterns):
+    """Get the symbols from an ELF file
+
+    Args:
+        fname: Filename of the ELF file to read
+        patterns: List of regex patterns to search for, each a string
+
+    Returns:
+        None, if the file does not exist, or Dict:
+          key: Name of symbol
+          value: Hex value of symbol
+    """
+    stdout = command.Output('objdump', '-t', fname, raise_on_error=False)
+    lines = stdout.splitlines()
+    if patterns:
+        re_syms = re.compile('|'.join(patterns))
+    else:
+        re_syms = None
+    syms = {}
+    syms_started = False
+    for line in lines:
+        if not line or not syms_started:
+            if 'SYMBOL TABLE' in line:
+                syms_started = True
+            line = None  # Otherwise code coverage complains about 'continue'
+            continue
+        if re_syms and not re_syms.search(line):
+            continue
+
+        space_pos = line.find(' ')
+        value, rest = line[:space_pos], line[space_pos + 1:]
+        flags = rest[:7]
+        parts = rest[7:].split()
+        section, size =  parts[:2]
+        if len(parts) > 2:
+            name = parts[2]
+            syms[name] = Symbol(section, int(value, 16), int(size,16),
+                                flags[1] == 'w')
+
+    # Sort dict by address
+    return OrderedDict(sorted(syms.iteritems(), key=lambda x: x[1].address))
+
+def GetSymbolAddress(fname, sym_name):
+    """Get a value of a symbol from an ELF file
+
+    Args:
+        fname: Filename of the ELF file to read
+        patterns: List of regex patterns to search for, each a string
+
+    Returns:
+        Symbol value (as an integer) or None if not found
+    """
+    syms = GetSymbols(fname, [sym_name])
+    sym = syms.get(sym_name)
+    if not sym:
+        return None
+    return sym.address
+
+def LookupAndWriteSymbols(elf_fname, entry, section):
+    """Replace all symbols in an entry with their correct values
+
+    The entry contents is updated so that values for referenced symbols will be
+    visible at run time. This is done by finding out the symbols offsets in the
+    entry (using the ELF file) and replacing them with values from binman's data
+    structures.
+
+    Args:
+        elf_fname: Filename of ELF image containing the symbol information for
+            entry
+        entry: Entry to process
+        section: Section which can be used to lookup symbol values
+    """
+    fname = tools.GetInputFilename(elf_fname)
+    syms = GetSymbols(fname, ['image', 'binman'])
+    if not syms:
+        return
+    base = syms.get('__image_copy_start')
+    if not base:
+        return
+    for name, sym in syms.iteritems():
+        if name.startswith('_binman'):
+            msg = ("Section '%s': Symbol '%s'\n   in entry '%s'" %
+                   (section.GetPath(), name, entry.GetPath()))
+            offset = sym.address - base.address
+            if offset < 0 or offset + sym.size > entry.contents_size:
+                raise ValueError('%s has offset %x (size %x) but the contents '
+                                 'size is %x' % (entry.GetPath(), offset,
+                                                 sym.size, entry.contents_size))
+            if sym.size == 4:
+                pack_string = '<I'
+            elif sym.size == 8:
+                pack_string = '<Q'
+            else:
+                raise ValueError('%s has size %d: only 4 and 8 are supported' %
+                                 (msg, sym.size))
+
+            # Look up the symbol in our entry tables.
+            value = section.LookupSymbol(name, sym.weak, msg)
+            if value is not None:
+                value += base.address
+            else:
+                value = -1
+                pack_string = pack_string.lower()
+            value_bytes = struct.pack(pack_string, value)
+            if debug:
+                print('%s:\n   insert %s, offset %x, value %x, length %d' %
+                      (msg, name, offset, value, len(value_bytes)))
+            entry.data = (entry.data[:offset] + value_bytes +
+                        entry.data[offset + sym.size:])
diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py
new file mode 100644
index 0000000..b68530c
--- /dev/null
+++ b/tools/binman/elf_test.py
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2017 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Test for the elf module
+
+import os
+import sys
+import unittest
+
+import elf
+import test_util
+import tools
+
+binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+
+
+class FakeEntry:
+    """A fake Entry object, usedfor testing
+
+    This supports an entry with a given size.
+    """
+    def __init__(self, contents_size):
+        self.contents_size = contents_size
+        self.data = 'a' * contents_size
+
+    def GetPath(self):
+        return 'entry_path'
+
+
+class FakeSection:
+    """A fake Section object, used for testing
+
+    This has the minimum feature set needed to support testing elf functions.
+    A LookupSymbol() function is provided which returns a fake value for amu
+    symbol requested.
+    """
+    def __init__(self, sym_value=1):
+        self.sym_value = sym_value
+
+    def GetPath(self):
+        return 'section_path'
+
+    def LookupSymbol(self, name, weak, msg):
+        """Fake implementation which returns the same value for all symbols"""
+        return self.sym_value
+
+
+class TestElf(unittest.TestCase):
+    @classmethod
+    def setUpClass(self):
+        tools.SetInputDirs(['.'])
+
+    def testAllSymbols(self):
+        """Test that we can obtain a symbol from the ELF file"""
+        fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr')
+        syms = elf.GetSymbols(fname, [])
+        self.assertIn('.ucode', syms)
+
+    def testRegexSymbols(self):
+        """Test that we can obtain from the ELF file by regular expression"""
+        fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr')
+        syms = elf.GetSymbols(fname, ['ucode'])
+        self.assertIn('.ucode', syms)
+        syms = elf.GetSymbols(fname, ['missing'])
+        self.assertNotIn('.ucode', syms)
+        syms = elf.GetSymbols(fname, ['missing', 'ucode'])
+        self.assertIn('.ucode', syms)
+
+    def testMissingFile(self):
+        """Test that a missing file is detected"""
+        entry = FakeEntry(10)
+        section = FakeSection()
+        with self.assertRaises(ValueError) as e:
+            syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
+        self.assertIn("Filename 'missing-file' not found in input path",
+                      str(e.exception))
+
+    def testOutsideFile(self):
+        """Test a symbol which extends outside the entry area is detected"""
+        entry = FakeEntry(10)
+        section = FakeSection()
+        elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
+        with self.assertRaises(ValueError) as e:
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
+        self.assertIn('entry_path has offset 4 (size 8) but the contents size '
+                      'is a', str(e.exception))
+
+    def testMissingImageStart(self):
+        """Test that we detect a missing __image_copy_start symbol
+
+        This is needed to mark the start of the image. Without it we cannot
+        locate the offset of a binman symbol within the image.
+        """
+        entry = FakeEntry(10)
+        section = FakeSection()
+        elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
+        self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
+                         None)
+
+    def testBadSymbolSize(self):
+        """Test that an attempt to use an 8-bit symbol are detected
+
+        Only 32 and 64 bits are supported, since we need to store an offset
+        into the image.
+        """
+        entry = FakeEntry(10)
+        section = FakeSection()
+        elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size')
+        with self.assertRaises(ValueError) as e:
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
+        self.assertIn('has size 1: only 4 and 8 are supported',
+                      str(e.exception))
+
+    def testNoValue(self):
+        """Test the case where we have no value for the symbol
+
+        This should produce -1 values for all thress symbols, taking up the
+        first 16 bytes of the image.
+        """
+        entry = FakeEntry(20)
+        section = FakeSection(sym_value=None)
+        elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
+        syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
+        self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data)
+
+    def testDebug(self):
+        """Check that enabling debug in the elf module produced debug output"""
+        elf.debug = True
+        entry = FakeEntry(20)
+        section = FakeSection()
+        elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
+        with test_util.capture_sys_output() as (stdout, stderr):
+            syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
+        elf.debug = False
+        self.assertTrue(len(stdout.getvalue()) > 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tools/binman/entry.py b/tools/binman/entry.py
new file mode 100644
index 0000000..648cfd2
--- /dev/null
+++ b/tools/binman/entry.py
@@ -0,0 +1,532 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+#
+# Base class for all entries
+#
+
+from __future__ import print_function
+
+from collections import namedtuple
+
+# 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 os
+from sets import Set
+import sys
+
+import fdt_util
+import state
+import tools
+
+modules = {}
+
+our_path = os.path.dirname(os.path.realpath(__file__))
+
+
+# An argument which can be passed to entries on the command line, in lieu of
+# device-tree properties.
+EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
+
+
+class Entry(object):
+    """An Entry in the section
+
+    An entry corresponds to a single node in the device-tree description
+    of the section. Each entry ends up being a part of the final section.
+    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:
+        section: Section object containing this entry
+        node: The node that created this entry
+        offset: Offset of entry within the section, None if not known yet (in
+            which case it will be calculated by Pack())
+        size: Entry size in bytes, None if not known
+        contents_size: Size of contents in bytes, 0 by default
+        align: Entry start offset alignment, or None
+        align_size: Entry size alignment, or None
+        align_end: Entry end offset 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, section, etype, node, read_node=True, name_prefix=''):
+        self.section = section
+        self.etype = etype
+        self._node = node
+        self.name = node and (name_prefix + node.name) or 'none'
+        self.offset = None
+        self.size = None
+        self.data = 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.offset_unset = False
+        self.image_pos = None
+        self._expand_size = False
+        if read_node:
+            self.ReadNode()
+
+    @staticmethod
+    def Lookup(section, node_path, etype):
+        """Look up the entry class for a node.
+
+        Args:
+            section:   Section object containing this node
+            node_node: Path name of Node object containing information about
+                       the entry to create (used for errors)
+            etype:   Entry type to use
+
+        Returns:
+            The entry class object if found, else None
+        """
+        # Convert something like 'u-boot@0' to 'u_boot' since we are only
+        # interested in the type.
+        module_name = etype.replace('-', '_')
+        if '@' in module_name:
+            module_name = module_name.split('@')[0]
+        module = modules.get(module_name)
+
+        # Also allow entry-type modules to be brought in from the etype directory.
+
+        # Import the module if we have not already done so.
+        if not module:
+            old_path = sys.path
+            sys.path.insert(0, os.path.join(our_path, 'etype'))
+            try:
+                if have_importlib:
+                    module = importlib.import_module(module_name)
+                else:
+                    module = __import__(module_name)
+            except ImportError as e:
+                raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
+                                 (etype, node_path, module_name, e))
+            finally:
+                sys.path = old_path
+            modules[module_name] = module
+
+        # Look up the expected class name
+        return getattr(module, 'Entry_%s' % module_name)
+
+    @staticmethod
+    def Create(section, node, etype=None):
+        """Create a new entry for a node.
+
+        Args:
+            section: Section 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)
+        obj = Entry.Lookup(section, node.path, etype)
+
+        # Call its constructor to get the object we want.
+        return obj(section, etype, node)
+
+    def ReadNode(self):
+        """Read entry information from the node
+
+        This reads all the fields we recognise from the node, ready for use.
+        """
+        if 'pos' in self._node.props:
+            self.Raise("Please use 'offset' instead of 'pos'")
+        self.offset = fdt_util.GetInt(self._node, 'offset')
+        self.size = fdt_util.GetInt(self._node, 'size')
+        self.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.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
+        self.expand_size = fdt_util.GetBool(self._node, 'expand-size')
+
+    def GetDefaultFilename(self):
+        return None
+
+    def GetFdtSet(self):
+        """Get the set of device trees used by this entry
+
+        Returns:
+            Set containing the filename from this entry, if it is a .dtb, else
+            an empty set
+        """
+        fname = self.GetDefaultFilename()
+        # It would be better to use isinstance(self, Entry_blob_dtb) here but
+        # we cannot access Entry_blob_dtb
+        if fname and fname.endswith('.dtb'):
+            return Set([fname])
+        return Set()
+
+    def ExpandEntries(self):
+        pass
+
+    def AddMissingProperties(self):
+        """Add new properties to the device tree as needed for this entry"""
+        for prop in ['offset', 'size', 'image-pos']:
+            if not prop in self._node.props:
+                state.AddZeroProp(self._node, prop)
+        err = state.CheckAddHashProp(self._node)
+        if err:
+            self.Raise(err)
+
+    def SetCalculatedProperties(self):
+        """Set the value of device-tree properties calculated by binman"""
+        state.SetInt(self._node, 'offset', self.offset)
+        state.SetInt(self._node, 'size', self.size)
+        state.SetInt(self._node, 'image-pos',
+                       self.image_pos - self.section.GetRootSkipAtStart())
+        state.CheckSetHashValue(self._node, self.GetData)
+
+    def ProcessFdt(self, fdt):
+        """Allow entries to adjust the device tree
+
+        Some entries need to adjust the device tree for their purposes. This
+        may involve adding or deleting properties.
+
+        Returns:
+            True if processing is complete
+            False if processing could not be completed due to a dependency.
+                This will cause the entry to be retried after others have been
+                called
+        """
+        return True
+
+    def SetPrefix(self, prefix):
+        """Set the name prefix for a node
+
+        Args:
+            prefix: Prefix to set, or '' to not use a prefix
+        """
+        if prefix:
+            self.name = prefix + self.name
+
+    def SetContents(self, data):
+        """Set the contents of an entry
+
+        This sets both the data and content_size properties
+
+        Args:
+            data: Data to set to the contents (string)
+        """
+        self.data = data
+        self.contents_size = len(self.data)
+
+    def ProcessContentsUpdate(self, data):
+        """Update the contens of an entry, after the size is fixed
+
+        This checks that the new data is the same size as the old.
+
+        Args:
+            data: Data to set to the contents (string)
+
+        Raises:
+            ValueError if the new data size is not the same as the old
+        """
+        if len(data) != self.contents_size:
+            self.Raise('Cannot update entry size from %d to %d' %
+                       (len(data), self.contents_size))
+        self.SetContents(data)
+
+    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, offset):
+        """Figure out how to pack the entry into the section
+
+        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 offset, it will be placed at @offset.
+
+        Once this function is complete, both the offset and size of the
+        entry will be know.
+
+        Args:
+            Current section offset pointer
+
+        Returns:
+            New section offset pointer (after this entry)
+        """
+        if self.offset is None:
+            if self.offset_unset:
+                self.Raise('No offset set with offset-unset: should another '
+                           'entry provide this correct offset?')
+            self.offset = tools.Align(offset, 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_offset = self.offset + size
+        aligned_offset = tools.Align(new_offset, self.align_end)
+        if aligned_offset != new_offset:
+            size = aligned_offset - self.offset
+            new_offset = aligned_offset
+
+        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 offset 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.offset != tools.Align(self.offset, self.align):
+            self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
+                  (self.offset, self.offset, self.align, self.align))
+
+        return new_offset
+
+    def Raise(self, msg):
+        """Convenience function to raise an error referencing a node"""
+        raise ValueError("Node '%s': %s" % (self._node.path, msg))
+
+    def GetEntryArgsOrProps(self, props, required=False):
+        """Return the values of a set of properties
+
+        Args:
+            props: List of EntryArg objects
+
+        Raises:
+            ValueError if a property is not found
+        """
+        values = []
+        missing = []
+        for prop in props:
+            python_prop = prop.name.replace('-', '_')
+            if hasattr(self, python_prop):
+                value = getattr(self, python_prop)
+            else:
+                value = None
+            if value is None:
+                value = self.GetArg(prop.name, prop.datatype)
+            if value is None and required:
+                missing.append(prop.name)
+            values.append(value)
+        if missing:
+            self.Raise('Missing required properties/entry args: %s' %
+                       (', '.join(missing)))
+        return values
+
+    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 GetOffsets(self):
+        return {}
+
+    def SetOffsetSize(self, pos, size):
+        self.offset = pos
+        self.size = size
+
+    def SetImagePos(self, image_pos):
+        """Set the position in the image
+
+        Args:
+            image_pos: Position of this entry in the image
+        """
+        self.image_pos = image_pos + self.offset
+
+    def ProcessContents(self):
+        pass
+
+    def WriteSymbols(self, section):
+        """Write symbol values into binary files for access at run time
+
+        Args:
+          section: Section containing the entry
+        """
+        pass
+
+    def CheckOffset(self):
+        """Check that the entry offsets are correct
+
+        This is used for entries which have extra offset requirements (other
+        than having to be fully inside their section). Sub-classes can implement
+        this function and raise if there is a problem.
+        """
+        pass
+
+    @staticmethod
+    def GetStr(value):
+        if value is None:
+            return '<none>  '
+        return '%08x' % value
+
+    @staticmethod
+    def WriteMapLine(fd, indent, name, offset, size, image_pos):
+        print('%s  %s%s  %s  %s' % (Entry.GetStr(image_pos), ' ' * indent,
+                                    Entry.GetStr(offset), Entry.GetStr(size),
+                                    name), file=fd)
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the entry to a .map file
+
+        Args:
+            fd: File to write the map to
+            indent: Curent indent level of map (0=none, 1=one level, etc.)
+        """
+        self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
+                          self.image_pos)
+
+    def GetEntries(self):
+        """Return a list of entries contained by this entry
+
+        Returns:
+            List of entries, or None if none. A normal entry has no entries
+                within it so will return None
+        """
+        return None
+
+    def GetArg(self, name, datatype=str):
+        """Get the value of an entry argument or device-tree-node property
+
+        Some node properties can be provided as arguments to binman. First check
+        the entry arguments, and fall back to the device tree if not found
+
+        Args:
+            name: Argument name
+            datatype: Data type (str or int)
+
+        Returns:
+            Value of argument as a string or int, or None if no value
+
+        Raises:
+            ValueError if the argument cannot be converted to in
+        """
+        value = state.GetEntryArg(name)
+        if value is not None:
+            if datatype == int:
+                try:
+                    value = int(value)
+                except ValueError:
+                    self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
+                               (name, value))
+            elif datatype == str:
+                pass
+            else:
+                raise ValueError("GetArg() internal error: Unknown data type '%s'" %
+                                 datatype)
+        else:
+            value = fdt_util.GetDatatype(self._node, name, datatype)
+        return value
+
+    @staticmethod
+    def WriteDocs(modules, test_missing=None):
+        """Write out documentation about the various entry types to stdout
+
+        Args:
+            modules: List of modules to include
+            test_missing: Used for testing. This is a module to report
+                as missing
+        """
+        print('''Binman Entry Documentation
+===========================
+
+This file describes the entry types supported by binman. These entry types can
+be placed in an image one by one to build up a final firmware image. It is
+fairly easy to create new entry types. Just add a new file to the 'etype'
+directory. You can use the existing entries as examples.
+
+Note that some entries are subclasses of others, using and extending their
+features to produce new behaviours.
+
+
+''')
+        modules = sorted(modules)
+
+        # Don't show the test entry
+        if '_testing' in modules:
+            modules.remove('_testing')
+        missing = []
+        for name in modules:
+            module = Entry.Lookup(name, name, name)
+            docs = getattr(module, '__doc__')
+            if test_missing == name:
+                docs = None
+            if docs:
+                lines = docs.splitlines()
+                first_line = lines[0]
+                rest = [line[4:] for line in lines[1:]]
+                hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
+                print(hdr)
+                print('-' * len(hdr))
+                print('\n'.join(rest))
+                print()
+                print()
+            else:
+                missing.append(name)
+
+        if missing:
+            raise ValueError('Documentation is missing for modules: %s' %
+                             ', '.join(missing))
+
+    def GetUniqueName(self):
+        """Get a unique name for a node
+
+        Returns:
+            String containing a unique name for a node, consisting of the name
+            of all ancestors (starting from within the 'binman' node) separated
+            by a dot ('.'). This can be useful for generating unique filesnames
+            in the output directory.
+        """
+        name = self.name
+        node = self._node
+        while node.parent:
+            node = node.parent
+            if node.name == 'binman':
+                break
+            name = '%s.%s' % (node.name, name)
+        return name
+
+    def ExpandToLimit(self, limit):
+        """Expand an entry so that it ends at the given offset limit"""
+        if self.offset + self.size < limit:
+            self.size = limit - self.offset
+            # Request the contents again, since changing the size requires that
+            # the data grows. This should not fail, but check it to be sure.
+            if not self.ObtainContents():
+                self.Raise('Cannot obtain contents when expanding entry')
diff --git a/tools/binman/entry_test.py b/tools/binman/entry_test.py
new file mode 100644
index 0000000..1f7ff5b
--- /dev/null
+++ b/tools/binman/entry_test.py
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Test for the Entry class
+
+import collections
+import os
+import sys
+import unittest
+
+import fdt
+import fdt_util
+import tools
+
+entry = None
+
+class TestEntry(unittest.TestCase):
+    def setUp(self):
+        tools.PrepareOutputDir(None)
+
+    def tearDown(self):
+        tools.FinaliseOutputDir()
+
+    def GetNode(self):
+        binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
+        fname = fdt_util.EnsureCompiled(
+            os.path.join(binman_dir,('test/005_simple.dts')))
+        dtb = fdt.FdtScan(fname)
+        return dtb.GetNode('/binman/u-boot')
+
+    def test1EntryNoImportLib(self):
+        """Test that we can import Entry subclassess successfully"""
+
+        sys.modules['importlib'] = None
+        global entry
+        import entry
+        entry.Entry.Create(None, self.GetNode(), 'u-boot')
+
+    def test2EntryImportLib(self):
+        del sys.modules['importlib']
+        global entry
+        if entry:
+            reload(entry)
+        else:
+            import entry
+        entry.Entry.Create(None, self.GetNode(), 'u-boot-spl')
+        del entry
+
+    def testEntryContents(self):
+        """Test the Entry bass class"""
+        import entry
+        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"""
+        import entry
+        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))
+
+    def testUniqueName(self):
+        """Test Entry.GetUniqueName"""
+        import entry
+        Node = collections.namedtuple('Node', ['name', 'parent'])
+        base_node = Node('root', None)
+        base_entry = entry.Entry(None, None, base_node, read_node=False)
+        self.assertEqual('root', base_entry.GetUniqueName())
+        sub_node = Node('subnode', base_node)
+        sub_entry = entry.Entry(None, None, sub_node, read_node=False)
+        self.assertEqual('root.subnode', sub_entry.GetUniqueName())
+
+    def testGetDefaultFilename(self):
+        """Trivial test for this base class function"""
+        import entry
+        base_entry = entry.Entry(None, None, None, read_node=False)
+        self.assertIsNone(base_entry.GetDefaultFilename())
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py
new file mode 100644
index 0000000..3e345bd
--- /dev/null
+++ b/tools/binman/etype/_testing.py
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for testing purposes. Not used in real images.
+#
+
+from collections import OrderedDict
+
+from entry import Entry, EntryArg
+import fdt_util
+import tools
+
+
+class Entry__testing(Entry):
+    """A fake entry used for testing
+
+    This entry should not be used in normal images. It is a special entry with
+    strange features used for testing.
+
+    Properties / Entry arguments
+        test-str-fdt: Test string, normally in the node
+        test-int-fdt: Test integer, normally in the node
+        test-str-arg: Test string, normally in the entry arguments
+        test-int-arg: Test integer, normally in the entry arguments
+
+    The entry has a single 'a' byte as its contents. Operation is controlled by
+    a number of properties in the node, as follows:
+
+    Properties:
+        return-invalid-entry: Return an invalid entry from GetOffsets()
+        return-unknown-contents: Refuse to provide any contents (to cause a
+            failure)
+        bad-update-contents: Implement ProcessContents() incorrectly so as to
+            cause a failure
+        never-complete-process-fdt: Refund to process the FDT (to cause a
+            failure)
+        require-args: Require that all used args are present (generating an
+            error if not)
+        force-bad-datatype: Force a call to GetEntryArgsOrProps() with a bad
+            data type (generating an error)
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self.return_invalid_entry = fdt_util.GetBool(self._node,
+                                                     'return-invalid-entry')
+        self.return_unknown_contents = fdt_util.GetBool(self._node,
+                                                     'return-unknown-contents')
+        self.bad_update_contents = fdt_util.GetBool(self._node,
+                                                    'bad-update-contents')
+        self.return_contents_once = fdt_util.GetBool(self._node,
+                                                     'return-contents-once')
+
+        # Set to True when the entry is ready to process the FDT.
+        self.process_fdt_ready = False
+        self.never_complete_process_fdt = fdt_util.GetBool(self._node,
+                                                'never-complete-process-fdt')
+        self.require_args = fdt_util.GetBool(self._node, 'require-args')
+
+        # This should be picked up by GetEntryArgsOrProps()
+        self.test_existing_prop = 'existing'
+        self.force_bad_datatype = fdt_util.GetBool(self._node,
+                                                   'force-bad-datatype')
+        (self.test_str_fdt, self.test_str_arg, self.test_int_fdt,
+         self.test_int_arg, existing) = self.GetEntryArgsOrProps([
+            EntryArg('test-str-fdt', str),
+            EntryArg('test-str-arg', str),
+            EntryArg('test-int-fdt', int),
+            EntryArg('test-int-arg', int),
+            EntryArg('test-existing-prop', str)], self.require_args)
+        if self.force_bad_datatype:
+            self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)])
+        self.return_contents = True
+
+    def ObtainContents(self):
+        if self.return_unknown_contents or not self.return_contents:
+            return False
+        self.data = 'a'
+        self.contents_size = len(self.data)
+        if self.return_contents_once:
+            self.return_contents = False
+        return True
+
+    def GetOffsets(self):
+        if self.return_invalid_entry :
+            return {'invalid-entry': [1, 2]}
+        return {}
+
+    def ProcessContents(self):
+        if self.bad_update_contents:
+            # Request to update the conents with something larger, to cause a
+            # failure.
+            self.ProcessContentsUpdate('aa')
+
+    def ProcessFdt(self, fdt):
+        """Force reprocessing the first time"""
+        ready = self.process_fdt_ready
+        if not self.never_complete_process_fdt:
+            self.process_fdt_ready = True
+        return ready
diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py
new file mode 100644
index 0000000..ae80bbe
--- /dev/null
+++ b/tools/binman/etype/blob.py
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for blobs, which are binary objects read from files
+#
+
+from entry import Entry
+import fdt_util
+import state
+import tools
+
+class Entry_blob(Entry):
+    """Entry containing an arbitrary binary blob
+
+    Note: This should not be used by itself. It is normally used as a parent
+    class by other entry types.
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+        - compress: Compression algorithm to use:
+            none: No compression
+            lz4: Use lz4 compression (via 'lz4' command-line utility)
+
+    This entry reads data from a file and places it in the entry. The
+    default filename is often specified specified by the subclass. See for
+    example the 'u_boot' entry which provides the filename 'u-boot.bin'.
+
+    If compression is enabled, an extra 'uncomp-size' property is written to
+    the node (if enabled with -u) which provides the uncompressed size of the
+    data.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self._filename = fdt_util.GetString(self._node, 'filename', self.etype)
+        self._compress = fdt_util.GetString(self._node, 'compress', 'none')
+        self._uncompressed_size = None
+
+    def ObtainContents(self):
+        self._filename = self.GetDefaultFilename()
+        self._pathname = tools.GetInputFilename(self._filename)
+        self.ReadBlobContents()
+        return True
+
+    def ReadBlobContents(self):
+        # We assume the data is small enough to fit into memory. If this
+        # is used for large filesystem image that might not be true.
+        # In that case, Image.BuildImage() could be adjusted to use a
+        # new Entry method which can read in chunks. Then we could copy
+        # the data in chunks and avoid reading it all at once. For now
+        # this seems like an unnecessary complication.
+        data = tools.ReadFile(self._pathname)
+        if self._compress == 'lz4':
+            self._uncompressed_size = len(data)
+            '''
+            import lz4  # Import this only if needed (python-lz4 dependency)
+
+            try:
+                data = lz4.frame.compress(data)
+            except AttributeError:
+                data = lz4.compress(data)
+            '''
+            data = tools.Run('lz4', '-c', self._pathname)
+        self.SetContents(data)
+        return True
+
+    def GetDefaultFilename(self):
+        return self._filename
+
+    def AddMissingProperties(self):
+        Entry.AddMissingProperties(self)
+        if self._compress != 'none':
+            state.AddZeroProp(self._node, 'uncomp-size')
+
+    def SetCalculatedProperties(self):
+        Entry.SetCalculatedProperties(self)
+        if self._uncompressed_size is not None:
+            state.SetInt(self._node, 'uncomp-size', self._uncompressed_size)
diff --git a/tools/binman/etype/blob_dtb.py b/tools/binman/etype/blob_dtb.py
new file mode 100644
index 0000000..cc5b4a3
--- /dev/null
+++ b/tools/binman/etype/blob_dtb.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree files
+#
+
+import state
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_blob_dtb(Entry_blob):
+    """A blob that holds a device tree
+
+    This is a blob containing a device tree. The contents of the blob are
+    obtained from the list of available device-tree files, managed by the
+    'state' module.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def ObtainContents(self):
+        """Get the device-tree from the list held by the 'state' module"""
+        self._filename = self.GetDefaultFilename()
+        self._pathname, data = state.GetFdtContents(self._filename)
+        self.SetContents(data)
+        return True
+
+    def ProcessContents(self):
+        """Re-read the DTB contents so that we get any calculated properties"""
+        _, data = state.GetFdtContents(self._filename)
+        self.SetContents(data)
diff --git a/tools/binman/etype/blob_named_by_arg.py b/tools/binman/etype/blob_named_by_arg.py
new file mode 100644
index 0000000..344112b
--- /dev/null
+++ b/tools/binman/etype/blob_named_by_arg.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for a blob where the filename comes from a property in the
+# node or an entry argument. The property is called '<blob_fname>-path' where
+# <blob_fname> is provided by the subclass using this entry type.
+
+from collections import OrderedDict
+
+from blob import Entry_blob
+from entry import EntryArg
+
+
+class Entry_blob_named_by_arg(Entry_blob):
+    """A blob entry which gets its filename property from its subclass
+
+    Properties / Entry arguments:
+        - <xxx>-path: Filename containing the contents of this entry (optional,
+            defaults to 0)
+
+    where <xxx> is the blob_fname argument to the constructor.
+
+    This entry cannot be used directly. Instead, it is used as a parent class
+    for another entry, which defined blob_fname. This parameter is used to
+    set the entry-arg or property containing the filename. The entry-arg or
+    property is in turn used to set the actual filename.
+
+    See cros_ec_rw for an example of this.
+    """
+    def __init__(self, section, etype, node, blob_fname):
+        Entry_blob.__init__(self, section, etype, node)
+        self._filename, = self.GetEntryArgsOrProps(
+            [EntryArg('%s-path' % blob_fname, str)])
diff --git a/tools/binman/etype/cros_ec_rw.py b/tools/binman/etype/cros_ec_rw.py
new file mode 100644
index 0000000..261f865
--- /dev/null
+++ b/tools/binman/etype/cros_ec_rw.py
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for a Chromium OS EC image (read-write section)
+#
+
+from blob_named_by_arg import Entry_blob_named_by_arg
+
+
+class Entry_cros_ec_rw(Entry_blob_named_by_arg):
+    """A blob entry which contains a Chromium OS read-write EC image
+
+    Properties / Entry arguments:
+        - cros-ec-rw-path: Filename containing the EC image
+
+    This entry holds a Chromium OS EC (embedded controller) image, for use in
+    updating the EC on startup via software sync.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob_named_by_arg.__init__(self, section, etype, node,
+                                         'cros-ec-rw')
diff --git a/tools/binman/etype/files.py b/tools/binman/etype/files.py
new file mode 100644
index 0000000..99f2f2f
--- /dev/null
+++ b/tools/binman/etype/files.py
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for a set of files which are placed in individual
+# sub-entries
+#
+
+import glob
+import os
+
+from section import Entry_section
+import fdt_util
+import state
+import tools
+
+import bsection
+
+class Entry_files(Entry_section):
+    """Entry containing a set of files
+
+    Properties / Entry arguments:
+        - pattern: Filename pattern to match the files to include
+        - compress: Compression algorithm to use:
+            none: No compression
+            lz4: Use lz4 compression (via 'lz4' command-line utility)
+
+    This entry reads a number of files and places each in a separate sub-entry
+    within this entry. To access these you need to enable device-tree updates
+    at run-time so you can obtain the file positions.
+    """
+    def __init__(self, section, etype, node):
+        Entry_section.__init__(self, section, etype, node)
+        self._pattern = fdt_util.GetString(self._node, 'pattern')
+        if not self._pattern:
+            self.Raise("Missing 'pattern' property")
+        self._compress = fdt_util.GetString(self._node, 'compress', 'none')
+        self._require_matches = fdt_util.GetBool(self._node,
+                                                'require-matches')
+
+    def ExpandEntries(self):
+        files = tools.GetInputFilenameGlob(self._pattern)
+        if self._require_matches and not files:
+            self.Raise("Pattern '%s' matched no files" % self._pattern)
+        for fname in files:
+            if not os.path.isfile(fname):
+                continue
+            name = os.path.basename(fname)
+            subnode = self._node.FindNode(name)
+            if not subnode:
+                subnode = state.AddSubnode(self._node, name)
+            state.AddString(subnode, 'type', 'blob')
+            state.AddString(subnode, 'filename', fname)
+            state.AddString(subnode, 'compress', self._compress)
+
+        # Read entries again, now that we have some
+        self._section._ReadEntries()
diff --git a/tools/binman/etype/fill.py b/tools/binman/etype/fill.py
new file mode 100644
index 0000000..dcfe978
--- /dev/null
+++ b/tools/binman/etype/fill.py
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+from entry import Entry
+import fdt_util
+
+
+class Entry_fill(Entry):
+    """An entry which is filled to a particular byte value
+
+    Properties / Entry arguments:
+        - fill-byte: Byte to use to fill the entry
+
+    Note that the size property must be set since otherwise this entry does not
+    know how large it should be.
+
+    You can often achieve the same effect using the pad-byte property of the
+    overall image, in that the space between entries will then be padded with
+    that byte. But this entry is sometimes useful for explicitly setting the
+    byte value of a region.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        if self.size is None:
+            self.Raise("'fill' entry must have a size property")
+        self.fill_value = fdt_util.GetByte(self._node, 'fill-byte', 0)
+
+    def ObtainContents(self):
+        self.SetContents(chr(self.fill_value) * self.size)
+        return True
diff --git a/tools/binman/etype/fmap.py b/tools/binman/etype/fmap.py
new file mode 100644
index 0000000..bf35a5b
--- /dev/null
+++ b/tools/binman/etype/fmap.py
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for a Flash map, as used by the flashrom SPI flash tool
+#
+
+from entry import Entry
+import fmap_util
+
+
+class Entry_fmap(Entry):
+    """An entry which contains an Fmap section
+
+    Properties / Entry arguments:
+        None
+
+    FMAP is a simple format used by flashrom, an open-source utility for
+    reading and writing the SPI flash, typically on x86 CPUs. The format
+    provides flashrom with a list of areas, so it knows what it in the flash.
+    It can then read or write just a single area, instead of the whole flash.
+
+    The format is defined by the flashrom project, in the file lib/fmap.h -
+    see www.flashrom.org/Flashrom for more information.
+
+    When used, this entry will be populated with an FMAP which reflects the
+    entries in the current image. Note that any hierarchy is squashed, since
+    FMAP does not support this.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+
+    def _GetFmap(self):
+        """Build an FMAP from the entries in the current image
+
+        Returns:
+            FMAP binary data
+        """
+        def _AddEntries(areas, entry):
+            entries = entry.GetEntries()
+            if entries:
+                for subentry in entries.values():
+                    _AddEntries(areas, subentry)
+            else:
+                pos = entry.image_pos
+                if pos is not None:
+                    pos -= entry.section.GetRootSkipAtStart()
+                areas.append(fmap_util.FmapArea(pos or 0, entry.size or 0,
+                                                entry.name, 0))
+
+        entries = self.section._image.GetEntries()
+        areas = []
+        for entry in entries.values():
+            _AddEntries(areas, entry)
+        return fmap_util.EncodeFmap(self.section.GetImageSize() or 0, self.name,
+                                    areas)
+
+    def ObtainContents(self):
+        """Obtain a placeholder for the fmap contents"""
+        self.SetContents(self._GetFmap())
+        return True
+
+    def ProcessContents(self):
+        self.SetContents(self._GetFmap())
diff --git a/tools/binman/etype/gbb.py b/tools/binman/etype/gbb.py
new file mode 100644
index 0000000..8fe10f4
--- /dev/null
+++ b/tools/binman/etype/gbb.py
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+# Support for a Chromium OS Google Binary Block, used to record read-only
+# information mostly used by firmware.
+
+from collections import OrderedDict
+
+import command
+from entry import Entry, EntryArg
+
+import fdt_util
+import tools
+
+# Build GBB flags.
+# (src/platform/vboot_reference/firmware/include/gbb_header.h)
+gbb_flag_properties = {
+  'dev-screen-short-delay': 0x1,
+  'load-option-roms': 0x2,
+  'enable-alternate-os': 0x4,
+  'force-dev-switch-on': 0x8,
+  'force-dev-boot-usb': 0x10,
+  'disable-fw-rollback-check': 0x20,
+  'enter-triggers-tonorm': 0x40,
+  'force-dev-boot-legacy': 0x80,
+  'faft-key-override': 0x100,
+  'disable-ec-software-sync': 0x200,
+  'default-dev-boot-legacy': 0x400,
+  'disable-pd-software-sync': 0x800,
+  'disable-lid-shutdown': 0x1000,
+  'force-dev-boot-fastboot-full-cap': 0x2000,
+  'enable-serial': 0x4000,
+  'disable-dwmp': 0x8000,
+}
+
+
+class Entry_gbb(Entry):
+    """An entry which contains a Chromium OS Google Binary Block
+
+    Properties / Entry arguments:
+        - hardware-id: Hardware ID to use for this build (a string)
+        - keydir: Directory containing the public keys to use
+        - bmpblk: Filename containing images used by recovery
+
+    Chromium OS uses a GBB to store various pieces of information, in particular
+    the root and recovery keys that are used to verify the boot process. Some
+    more details are here:
+
+        https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
+
+    but note that the page dates from 2013 so is quite out of date. See
+    README.chromium for how to obtain the required keys and tools.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self.hardware_id, self.keydir, self.bmpblk = self.GetEntryArgsOrProps(
+            [EntryArg('hardware-id', str),
+             EntryArg('keydir', str),
+             EntryArg('bmpblk', str)])
+
+        # Read in the GBB flags from the config
+        self.gbb_flags = 0
+        flags_node = node.FindNode('flags')
+        if flags_node:
+            for flag, value in gbb_flag_properties.iteritems():
+                if fdt_util.GetBool(flags_node, flag):
+                    self.gbb_flags |= value
+
+    def ObtainContents(self):
+        gbb = 'gbb.bin'
+        fname = tools.GetOutputFilename(gbb)
+        if not self.size:
+            self.Raise('GBB must have a fixed size')
+        gbb_size = self.size
+        bmpfv_size = gbb_size - 0x2180
+        if bmpfv_size < 0:
+            self.Raise('GBB is too small (minimum 0x2180 bytes)')
+        sizes = [0x100, 0x1000, bmpfv_size, 0x1000]
+        sizes = ['%#x' % size for size in sizes]
+        keydir = tools.GetInputFilename(self.keydir)
+        gbb_set_command = [
+            'gbb_utility', '-s',
+            '--hwid=%s' % self.hardware_id,
+            '--rootkey=%s/root_key.vbpubk' % keydir,
+            '--recoverykey=%s/recovery_key.vbpubk' % keydir,
+            '--flags=%d' % self.gbb_flags,
+            '--bmpfv=%s' % tools.GetInputFilename(self.bmpblk),
+            fname]
+
+        tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname)
+        tools.Run('futility', *gbb_set_command)
+
+        self.SetContents(tools.ReadFile(fname))
+        return True
diff --git a/tools/binman/etype/intel_cmc.py b/tools/binman/etype/intel_cmc.py
new file mode 100644
index 0000000..fa6f779
--- /dev/null
+++ b/tools/binman/etype/intel_cmc.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for Intel Chip Microcode binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_cmc(Entry_blob):
+    """Entry containing an Intel Chipset Micro Code (CMC) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains microcode for some devices in a special format. An
+    example filename is 'Microcode/C0_22211.BIN'.
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py
new file mode 100644
index 0000000..6acbbd8
--- /dev/null
+++ b/tools/binman/etype/intel_descriptor.py
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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 numbers supported by the Intel firmware format
+(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)
+
+    Properties / Entry arguments:
+        filename: Filename of file containing the descriptor. This is typically
+            a 4KB binary file, sometimes called 'descriptor.bin'
+
+    This entry 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 (Management Engine) region, allowing us to place the ME
+    binary in the right place.
+
+    With this entry in your image, the position of the 'intel-me' entry will be
+    fixed in the image, which avoids you needed to specify an offset for that
+    region. This is useful, because it is not possible to change the position
+    of the ME region without updating the descriptor.
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self._regions = []
+
+    def GetOffsets(self):
+        offset = self.data.find(FD_SIGNATURE)
+        if offset == -1:
+            self.Raise('Cannot find FD signature')
+        flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL',
+                                                self.data[offset:offset + 16])
+        frba = ((flmap0 >> 16) & 0xff) << 4
+        for i in range(MAX_REGIONS):
+            self._regions.append(Region(self.data, frba, i))
+
+        # Set the offset for ME only, for now, since the others are not used
+        return {'intel-me': [self._regions[REGION_ME].base,
+                             self._regions[REGION_ME].size]}
diff --git a/tools/binman/etype/intel_fsp.py b/tools/binman/etype/intel_fsp.py
new file mode 100644
index 0000000..00a78e7
--- /dev/null
+++ b/tools/binman/etype/intel_fsp.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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):
+    """Entry containing an Intel Firmware Support Package (FSP) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains binary blobs which are used on some devices to make the
+    platform work. U-Boot executes this code since it is not possible to set up
+    the hardware using U-Boot open-source code. Documentation is typically not
+    available in sufficient detail to allow this.
+
+    An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd'
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_me.py b/tools/binman/etype/intel_me.py
new file mode 100644
index 0000000..247c5b3
--- /dev/null
+++ b/tools/binman/etype/intel_me.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for Intel Management Engine binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_me(Entry_blob):
+    """Entry containing an Intel Management Engine (ME) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains code used by the SoC that is required to make it work.
+    The Management Engine is like a background task that runs things that are
+    not clearly documented, but may include keyboard, deplay and network
+    access. For platform that use ME it is not possible to disable it. U-Boot
+    does not directly execute code in the ME binary.
+
+    A typical filename is 'me.bin'.
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_mrc.py b/tools/binman/etype/intel_mrc.py
new file mode 100644
index 0000000..4dbc99a
--- /dev/null
+++ b/tools/binman/etype/intel_mrc.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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):
+    """Entry containing an Intel Memory Reference Code (MRC) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains code for setting up the SDRAM on some Intel systems. This
+    is executed by U-Boot when needed early during startup. A typical filename
+    is 'mrc.bin'.
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'mrc.bin'
diff --git a/tools/binman/etype/intel_refcode.py b/tools/binman/etype/intel_refcode.py
new file mode 100644
index 0000000..045db58
--- /dev/null
+++ b/tools/binman/etype/intel_refcode.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for Intel Memory Reference Code binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_refcode(Entry_blob):
+    """Entry containing an Intel Reference Code file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains code for setting up the platform on some Intel systems.
+    This is executed by U-Boot when needed early during startup. A typical
+    filename is 'refcode.bin'.
+
+    See README.x86 for information about x86 binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'refcode.bin'
diff --git a/tools/binman/etype/intel_vbt.py b/tools/binman/etype/intel_vbt.py
new file mode 100644
index 0000000..d93dd19
--- /dev/null
+++ b/tools/binman/etype/intel_vbt.py
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+#
+# Entry-type module for Intel Video BIOS Table binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_vbt(Entry_blob):
+    """Entry containing an Intel Video BIOS Table (VBT) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains code that sets up the integrated graphics subsystem on
+    some Intel SoCs. U-Boot executes this when the display is started up.
+
+    See README.x86 for information about Intel binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/intel_vga.py b/tools/binman/etype/intel_vga.py
new file mode 100644
index 0000000..40982c8
--- /dev/null
+++ b/tools/binman/etype/intel_vga.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for x86 VGA ROM binary blob
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_intel_vga(Entry_blob):
+    """Entry containing an Intel Video Graphics Adaptor (VGA) file
+
+    Properties / Entry arguments:
+        - filename: Filename of file to read into entry
+
+    This file contains code that sets up the integrated graphics subsystem on
+    some Intel SoCs. U-Boot executes this when the display is started up.
+
+    This is similar to the VBT file but in a different format.
+
+    See README.x86 for information about Intel binary blobs.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
diff --git a/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py b/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py
new file mode 100644
index 0000000..59fedd2
--- /dev/null
+++ b/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2018 NXP
+#
+# Entry-type module for the PowerPC mpc85xx bootpg and resetvec code for U-Boot
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_powerpc_mpc85xx_bootpg_resetvec(Entry_blob):
+    """PowerPC mpc85xx bootpg + resetvec code for U-Boot
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot-br.bin (default 'u-boot-br.bin')
+
+    This enrty is valid for PowerPC mpc85xx cpus. This entry holds
+    'bootpg + resetvec' code for PowerPC mpc85xx CPUs which needs to be
+    placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'.
+    """
+
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'u-boot-br.bin'
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
new file mode 100644
index 0000000..7f1b413
--- /dev/null
+++ b/tools/binman/etype/section.py
@@ -0,0 +1,106 @@
+# SPDX-License-Identifier:      GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for sections, which are entries which can contain other
+# entries.
+#
+
+from entry import Entry
+import fdt_util
+import tools
+
+import bsection
+
+class Entry_section(Entry):
+    """Entry that contains other entries
+
+    Properties / Entry arguments: (see binman README for more information)
+        - size: Size of section in bytes
+        - align-size: Align size to a particular power of two
+        - pad-before: Add padding before the entry
+        - pad-after: Add padding after the entry
+        - pad-byte: Pad byte to use when padding
+        - sort-by-offset: Reorder the entries by offset
+        - end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
+        - name-prefix: Adds a prefix to the name of every entry in the section
+            when writing out the map
+
+    A section is an entry which can contain other entries, thus allowing
+    hierarchical images to be created. See 'Sections and hierarchical images'
+    in the binman README for more information.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self._section = bsection.Section(node.name, section, node,
+                                         section._image)
+
+    def GetFdtSet(self):
+        return self._section.GetFdtSet()
+
+    def ProcessFdt(self, fdt):
+        return self._section.ProcessFdt(fdt)
+
+    def ExpandEntries(self):
+        Entry.ExpandEntries(self)
+        self._section.ExpandEntries()
+
+    def AddMissingProperties(self):
+        Entry.AddMissingProperties(self)
+        self._section.AddMissingProperties()
+
+    def ObtainContents(self):
+        return self._section.GetEntryContents()
+
+    def GetData(self):
+        return self._section.GetData()
+
+    def GetOffsets(self):
+        """Handle entries that want to set the offset/size of other entries
+
+        This calls each entry's GetOffsets() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        self._section.GetEntryOffsets()
+        return {}
+
+    def Pack(self, offset):
+        """Pack all entries into the section"""
+        self._section.PackEntries()
+        self._section.SetOffset(offset)
+        self.size = self._section.GetSize()
+        return super(Entry_section, self).Pack(offset)
+
+    def SetImagePos(self, image_pos):
+        Entry.SetImagePos(self, image_pos)
+        self._section.SetImagePos(image_pos + self.offset)
+
+    def WriteSymbols(self, section):
+        """Write symbol values into binary files for access at run time"""
+        self._section.WriteSymbols()
+
+    def SetCalculatedProperties(self):
+        Entry.SetCalculatedProperties(self)
+        self._section.SetCalculatedProperties()
+
+    def ProcessContents(self):
+        self._section.ProcessEntryContents()
+        super(Entry_section, self).ProcessContents()
+
+    def CheckOffset(self):
+        self._section.CheckEntries()
+
+    def WriteMap(self, fd, indent):
+        """Write a map of the section to a .map file
+
+        Args:
+            fd: File to write the map to
+        """
+        self._section.WriteMap(fd, indent)
+
+    def GetEntries(self):
+        return self._section.GetEntries()
+
+    def ExpandToLimit(self, limit):
+        super(Entry_section, self).ExpandToLimit(limit)
+        self._section.ExpandSize(self.size)
diff --git a/tools/binman/etype/text.py b/tools/binman/etype/text.py
new file mode 100644
index 0000000..6e99819
--- /dev/null
+++ b/tools/binman/etype/text.py
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+from collections import OrderedDict
+
+from entry import Entry, EntryArg
+import fdt_util
+
+
+class Entry_text(Entry):
+    """An entry which contains text
+
+    The text can be provided either in the node itself or by a command-line
+    argument. There is a level of indirection to allow multiple text strings
+    and sharing of text.
+
+    Properties / Entry arguments:
+        text-label: The value of this string indicates the property / entry-arg
+            that contains the string to place in the entry
+        <xxx> (actual name is the value of text-label): contains the string to
+            place in the entry.
+
+    Example node:
+
+        text {
+            size = <50>;
+            text-label = "message";
+        };
+
+    You can then use:
+
+        binman -amessage="this is my message"
+
+    and binman will insert that string into the entry.
+
+    It is also possible to put the string directly in the node:
+
+        text {
+            size = <8>;
+            text-label = "message";
+            message = "a message directly in the node"
+        };
+
+    The text is not itself nul-terminated. This can be achieved, if required,
+    by setting the size of the entry to something larger than the text.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self.text_label, = self.GetEntryArgsOrProps(
+            [EntryArg('text-label', str)])
+        self.value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, str)])
+        if not self.value:
+            self.Raise("No value provided for text label '%s'" %
+                       self.text_label)
+
+    def ObtainContents(self):
+        self.SetContents(self.value)
+        return True
diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py
new file mode 100644
index 0000000..23dd12c
--- /dev/null
+++ b/tools/binman/etype/u_boot.py
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot binary
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot(Entry_blob):
+    """U-Boot flat binary
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.bin (default 'u-boot.bin')
+
+    This is the U-Boot binary, containing relocation information to allow it
+    to relocate itself at runtime. The binary typically includes a device tree
+    blob at the end of it. Use u_boot_nodtb if you want to package the device
+    tree separately.
+
+    U-Boot can access binman symbols at runtime. See:
+
+        'Access to binman entry offsets at run time (fdt)'
+
+    in the binman README for more information.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, 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
index 0000000..6263c4e
--- /dev/null
+++ b/tools/binman/etype/u_boot_dtb.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree
+#
+
+from entry import Entry
+from blob_dtb import Entry_blob_dtb
+
+class Entry_u_boot_dtb(Entry_blob_dtb):
+    """U-Boot device tree
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.dtb (default 'u-boot.dtb')
+
+    This is the U-Boot device tree, containing configuration information for
+    U-Boot. U-Boot needs this to know what devices are present and which drivers
+    to activate.
+
+    Note: This is mostly an internal entry type, used by others. This allows
+    binman to know which entries contain a device tree.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob_dtb.__init__(self, section, 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
index 0000000..444c51b
--- /dev/null
+++ b/tools/binman/etype/u_boot_dtb_with_ucode.py
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree with the microcode removed
+#
+
+from entry import Entry
+from blob_dtb import Entry_blob_dtb
+import state
+import tools
+
+class Entry_u_boot_dtb_with_ucode(Entry_blob_dtb):
+    """A U-Boot device tree file, with the microcode removed
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.dtb (default 'u-boot.dtb')
+
+    See Entry_u_boot_ucode for full details of the three entries involved in
+    this process. This entry provides the U-Boot device-tree file, which
+    contains the microcode. If the microcode is not being collated into one
+    place then the offset and size of the microcode is recorded by this entry,
+    for use by u_boot_with_ucode_ptr. If it is being collated, then this
+    entry deletes the microcode from the device tree (to save space) and makes
+    it available to u_boot_ucode.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob_dtb.__init__(self, section, etype, node)
+        self.ucode_data = ''
+        self.collate = False
+        self.ucode_offset = None
+        self.ucode_size = None
+        self.ucode = None
+        self.ready = False
+
+    def GetDefaultFilename(self):
+        return 'u-boot.dtb'
+
+    def ProcessFdt(self, fdt):
+        # So the module can be loaded without it
+        import fdt
+
+        # If the section does not need microcode, there is nothing to do
+        ucode_dest_entry = self.section.FindEntryType(
+            'u-boot-spl-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_offset:
+            ucode_dest_entry = self.section.FindEntryType(
+                'u-boot-tpl-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_offset:
+            ucode_dest_entry = self.section.FindEntryType(
+                'u-boot-with-ucode-ptr')
+        if not ucode_dest_entry or not ucode_dest_entry.target_offset:
+            return True
+
+        # Remove the microcode
+        fname = self.GetDefaultFilename()
+        fdt = state.GetFdt(fname)
+        self.ucode = fdt.GetNode('/microcode')
+        if not self.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(self.ucode.subnodes) > 1
+        for node in self.ucode.subnodes:
+            data_prop = node.props.get('data')
+            if data_prop:
+                self.ucode_data += ''.join(data_prop.bytes)
+                if self.collate:
+                    node.DeleteProp('data')
+        return True
+
+    def ObtainContents(self):
+        # Call the base class just in case it does something important.
+        Entry_blob_dtb.ObtainContents(self)
+        if self.ucode and not self.collate:
+            for node in self.ucode.subnodes:
+                data_prop = node.props.get('data')
+                if data_prop:
+                    # Find the offset in the device tree of the ucode data
+                    self.ucode_offset = data_prop.GetOffset() + 12
+                    self.ucode_size = len(data_prop.bytes)
+                    self.ready = True
+        else:
+            self.ready = True
+        return self.ready
diff --git a/tools/binman/etype/u_boot_elf.py b/tools/binman/etype/u_boot_elf.py
new file mode 100644
index 0000000..f83860d
--- /dev/null
+++ b/tools/binman/etype/u_boot_elf.py
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot ELF image
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+import fdt_util
+import tools
+
+class Entry_u_boot_elf(Entry_blob):
+    """U-Boot ELF image
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot (default 'u-boot')
+
+    This is the U-Boot ELF image. It does not include a device tree but can be
+    relocated to any address for execution.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self._strip = fdt_util.GetBool(self._node, 'strip')
+
+    def ReadBlobContents(self):
+        if self._strip:
+            uniq = self.GetUniqueName()
+            out_fname = tools.GetOutputFilename('%s.stripped' % uniq)
+            tools.WriteFile(out_fname, tools.ReadFile(self._pathname))
+            tools.Run('strip', out_fname)
+            self._pathname = out_fname
+        Entry_blob.ReadBlobContents(self)
+        return True
+
+    def GetDefaultFilename(self):
+        return 'u-boot'
diff --git a/tools/binman/etype/u_boot_img.py b/tools/binman/etype/u_boot_img.py
new file mode 100644
index 0000000..1ec0757
--- /dev/null
+++ b/tools/binman/etype/u_boot_img.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot binary
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_img(Entry_blob):
+    """U-Boot legacy image
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.img (default 'u-boot.img')
+
+    This is the U-Boot binary as a packaged image, in legacy format. It has a
+    header which allows it to be loaded at the correct address for execution.
+
+    You should use FIT (Flat Image Tree) instead of the legacy image for new
+    applications.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, 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
index 0000000..a4b95a4
--- /dev/null
+++ b/tools/binman/etype/u_boot_nodtb.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for 'u-boot-nodtb.bin'
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_nodtb(Entry_blob):
+    """U-Boot flat binary without device tree appended
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin')
+
+    This is the U-Boot binary, containing relocation information to allow it
+    to relocate itself at runtime. It does not include a device tree blob at
+    the end of it so normally cannot work without it. You can add a u_boot_dtb
+    entry after this one, or use a u_boot entry instead (which contains both
+    U-Boot and the device tree).
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, 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
index 0000000..ab78714
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for spl/u-boot-spl.bin
+#
+
+import elf
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_spl(Entry_blob):
+    """U-Boot SPL binary
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin')
+
+    This is the U-Boot SPL (Secondary Program Loader) binary. This is a small
+    binary which loads before U-Boot proper, typically into on-chip SRAM. It is
+    responsible for locating, loading and jumping to U-Boot. Note that SPL is
+    not relocatable so must be loaded to the correct address in SRAM, or written
+    to run from the correct address if direct flash execution is possible (e.g.
+    on x86 devices).
+
+    SPL can access binman symbols at runtime. See:
+
+        'Access to binman entry offsets at run time (symbols)'
+
+    in the binman README for more information.
+
+    The ELF file 'spl/u-boot-spl' must also be available for this to work, since
+    binman uses that to look up symbols to write into the SPL binary.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self.elf_fname = 'spl/u-boot-spl'
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl.bin'
+
+    def WriteSymbols(self, section):
+        elf.LookupAndWriteSymbols(self.elf_fname, self, section)
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
index 0000000..00b7ac5
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl_bss_pad.py
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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
+import elf
+from entry import Entry
+from blob import Entry_blob
+import tools
+
+class Entry_u_boot_spl_bss_pad(Entry_blob):
+    """U-Boot SPL binary padded with a BSS region
+
+    Properties / Entry arguments:
+        None
+
+    This is similar to u_boot_spl except that padding is added after the SPL
+    binary to cover the BSS (Block Started by Symbol) region. This region holds
+    the various used by SPL. It is set to 0 by SPL when it starts up. If you
+    want to append data to the SPL image (such as a device tree file), you must
+    pad out the BSS region to avoid the data overlapping with U-Boot variables.
+    This entry is useful in that case. It automatically pads out the entry size
+    to cover both the code, data and BSS.
+
+    The ELF file 'spl/u-boot-spl' must also be available for this to work, since
+    binman uses that to look up the BSS address.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def ObtainContents(self):
+        fname = tools.GetInputFilename('spl/u-boot-spl')
+        bss_size = elf.GetSymbolAddress(fname, '__bss_size')
+        if not bss_size:
+            self.Raise('Expected __bss_size symbol in spl/u-boot-spl')
+        self.SetContents(chr(0) * bss_size)
+        return True
diff --git a/tools/binman/etype/u_boot_spl_dtb.py b/tools/binman/etype/u_boot_spl_dtb.py
new file mode 100644
index 0000000..e735464
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl_dtb.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree in SPL (Secondary Program Loader)
+#
+
+from entry import Entry
+from blob_dtb import Entry_blob_dtb
+
+class Entry_u_boot_spl_dtb(Entry_blob_dtb):
+    """U-Boot SPL device tree
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb')
+
+    This is the SPL device tree, containing configuration information for
+    SPL. SPL needs this to know what devices are present and which drivers
+    to activate.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob_dtb.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl.dtb'
diff --git a/tools/binman/etype/u_boot_spl_elf.py b/tools/binman/etype/u_boot_spl_elf.py
new file mode 100644
index 0000000..da328ae
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl_elf.py
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot SPL ELF image
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_spl_elf(Entry_blob):
+    """U-Boot SPL ELF image
+
+    Properties / Entry arguments:
+        - filename: Filename of SPL u-boot (default 'spl/u-boot')
+
+    This is the U-Boot SPL ELF image. It does not include a device tree but can
+    be relocated to any address for execution.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl'
diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py
new file mode 100644
index 0000000..41c1736
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl_nodtb.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for 'u-boot-nodtb.bin'
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_spl_nodtb(Entry_blob):
+    """SPL binary without device tree appended
+
+    Properties / Entry arguments:
+        - filename: Filename of spl/u-boot-spl-nodtb.bin (default
+            'spl/u-boot-spl-nodtb.bin')
+
+    This is the U-Boot SPL binary, It does not include a device tree blob at
+    the end of it so may not be able to work without it, assuming SPL needs
+    a device tree to operation on your platform. You can add a u_boot_spl_dtb
+    entry after this one, or use a u_boot_spl entry instead (which contains
+    both SPL and the device tree).
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl-nodtb.bin'
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
index 0000000..b650cf0
--- /dev/null
+++ b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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
+
+    This is used when SPL must set up the microcode for U-Boot.
+
+    See Entry_u_boot_ucode for full details of the entries involved in this
+    process.
+    """
+    def __init__(self, section, etype, node):
+        Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node)
+        self.elf_fname = 'spl/u-boot-spl'
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-spl-nodtb.bin'
diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py
new file mode 100644
index 0000000..4d4bb92
--- /dev/null
+++ b/tools/binman/etype/u_boot_tpl.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for tpl/u-boot-tpl.bin
+#
+
+import elf
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_u_boot_tpl(Entry_blob):
+    """U-Boot TPL binary
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin')
+
+    This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small
+    binary which loads before SPL, typically into on-chip SRAM. It is
+    responsible for locating, loading and jumping to SPL, the next-stage
+    loader. Note that SPL is not relocatable so must be loaded to the correct
+    address in SRAM, or written to run from the correct address if direct
+    flash execution is possible (e.g. on x86 devices).
+
+    SPL can access binman symbols at runtime. See:
+
+        'Access to binman entry offsets at run time (symbols)'
+
+    in the binman README for more information.
+
+    The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
+    binman uses that to look up symbols to write into the TPL binary.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self.elf_fname = 'tpl/u-boot-tpl'
+
+    def GetDefaultFilename(self):
+        return 'tpl/u-boot-tpl.bin'
+
+    def WriteSymbols(self, section):
+        elf.LookupAndWriteSymbols(self.elf_fname, self, section)
diff --git a/tools/binman/etype/u_boot_tpl_dtb.py b/tools/binman/etype/u_boot_tpl_dtb.py
new file mode 100644
index 0000000..bdeb0f7
--- /dev/null
+++ b/tools/binman/etype/u_boot_tpl_dtb.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree in TPL (Tertiary Program Loader)
+#
+
+from entry import Entry
+from blob_dtb import Entry_blob_dtb
+
+class Entry_u_boot_tpl_dtb(Entry_blob_dtb):
+    """U-Boot TPL device tree
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb')
+
+    This is the TPL device tree, containing configuration information for
+    TPL. TPL needs this to know what devices are present and which drivers
+    to activate.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob_dtb.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'tpl/u-boot-tpl.dtb'
diff --git a/tools/binman/etype/u_boot_tpl_dtb_with_ucode.py b/tools/binman/etype/u_boot_tpl_dtb_with_ucode.py
new file mode 100644
index 0000000..71e04fc
--- /dev/null
+++ b/tools/binman/etype/u_boot_tpl_dtb_with_ucode.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for U-Boot device tree with the microcode removed
+#
+
+import control
+from entry import Entry
+from u_boot_dtb_with_ucode import Entry_u_boot_dtb_with_ucode
+import tools
+
+class Entry_u_boot_tpl_dtb_with_ucode(Entry_u_boot_dtb_with_ucode):
+    """U-Boot TPL with embedded microcode pointer
+
+    This is used when TPL must set up the microcode for U-Boot.
+
+    See Entry_u_boot_ucode for full details of the entries involved in this
+    process.
+    """
+    def __init__(self, section, etype, node):
+        Entry_u_boot_dtb_with_ucode.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'tpl/u-boot-tpl.dtb'
diff --git a/tools/binman/etype/u_boot_tpl_with_ucode_ptr.py b/tools/binman/etype/u_boot_tpl_with_ucode_ptr.py
new file mode 100644
index 0000000..8d94dde
--- /dev/null
+++ b/tools/binman/etype/u_boot_tpl_with_ucode_ptr.py
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for an TPL 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_tpl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr):
+    """U-Boot TPL with embedded microcode pointer
+
+    See Entry_u_boot_ucode for full details of the entries involved in this
+    process.
+    """
+    def __init__(self, section, etype, node):
+        Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node)
+        self.elf_fname = 'tpl/u-boot-tpl'
+
+    def GetDefaultFilename(self):
+        return 'tpl/u-boot-tpl-nodtb.bin'
diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py
new file mode 100644
index 0000000..a00e530
--- /dev/null
+++ b/tools/binman/etype/u_boot_ucode.py
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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
+
+    Properties / Entry arguments:
+        None
+
+    The contents of this entry are filled in automatically by other entries
+    which must also be in the image.
+
+    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, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def ObtainContents(self):
+        # If the section does not need microcode, there is nothing to do
+        found = False
+        for suffix in ['', '-spl', '-tpl']:
+            name = 'u-boot%s-with-ucode-ptr' % suffix
+            entry = self.section.FindEntryType(name)
+            if entry and entry.target_offset:
+                found = True
+        if not found:
+            self.data = ''
+            return True
+        # Get the microcode from the device tree entry. If it is not available
+        # yet, return False so we will be called later. If the section simply
+        # doesn't exist, then we may as well return True, since we are going to
+        # get an error anyway.
+        for suffix in ['', '-spl', '-tpl']:
+            name = 'u-boot%s-dtb-with-ucode' % suffix
+            fdt_entry = self.section.FindEntryType(name)
+            if fdt_entry:
+                break
+        if not fdt_entry:
+            return True
+        if not fdt_entry.ready:
+            return False
+
+        if not fdt_entry.collate:
+            # This binary can be empty
+            self.data = ''
+            return True
+
+        # Write it out to a file
+        self._pathname = tools.GetOutputFilename('u-boot-ucode.bin')
+        tools.WriteFile(self._pathname, fdt_entry.ucode_data)
+
+        self.ReadBlobContents()
+
+        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
index 0000000..da0e124
--- /dev/null
+++ b/tools/binman/etype/u_boot_with_ucode_ptr.py
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for a U-Boot binary with an embedded microcode pointer
+#
+
+import struct
+
+import command
+import elf
+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
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
+        - optional-ucode: boolean property to make microcode optional. If the
+            u-boot.bin image does not include microcode, no error will
+            be generated.
+
+    See Entry_u_boot_ucode for full details of the three entries involved in
+    this process. This entry updates U-Boot with the offset and size of the
+    microcode, to allow early x86 boot code to find it without doing anything
+    complicated. Otherwise it is the same as the u_boot entry.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+        self.elf_fname = 'u-boot'
+        self.target_offset = None
+
+    def GetDefaultFilename(self):
+        return 'u-boot-nodtb.bin'
+
+    def ProcessFdt(self, fdt):
+        # Figure out where to put the microcode pointer
+        fname = tools.GetInputFilename(self.elf_fname)
+        sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
+        if sym:
+           self.target_offset = sym
+        elif not fdt_util.GetBool(self._node, 'optional-ucode'):
+            self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
+        return True
+
+    def ProcessContents(self):
+        # If the image does not need microcode, there is nothing to do
+        if not self.target_offset:
+            return
+
+        # Get the offset of the microcode
+        ucode_entry = self.section.FindEntryType('u-boot-ucode')
+        if not ucode_entry:
+            self.Raise('Cannot find microcode region u-boot-ucode')
+
+        # Check the target pos is in the section. If it is not, then U-Boot is
+        # being linked incorrectly, or is being placed at the wrong offset
+        # in the section.
+        #
+        # The section 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 offset 7MB in the section. In this
+        # case the ROM starts at 0xff800000, so the offset of the first
+        # entry in the section corresponds to that.
+        if (self.target_offset < self.image_pos or
+                self.target_offset >= self.image_pos + self.size):
+            self.Raise('Microcode pointer _dt_ucode_base_size at %08x is outside the section ranging from %08x to %08x' %
+                (self.target_offset, self.image_pos,
+                 self.image_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 latter. If we extracted the microcode from the device tree
+        # and collated it in one place, it will be in the former.
+        if ucode_entry.size:
+            offset, size = ucode_entry.offset, ucode_entry.size
+        else:
+            dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
+            if not dtb_entry:
+                dtb_entry = self.section.FindEntryType(
+                        'u-boot-tpl-dtb-with-ucode')
+            if not dtb_entry:
+                self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
+            offset = dtb_entry.offset + dtb_entry.ucode_offset
+            size = dtb_entry.ucode_size
+
+        # Write the microcode offset and size into the entry
+        offset_and_size = struct.pack('<2L', offset, size)
+        self.target_offset -= self.image_pos
+        self.ProcessContentsUpdate(self.data[:self.target_offset] +
+                                   offset_and_size +
+                                   self.data[self.target_offset + 8:])
diff --git a/tools/binman/etype/vblock.py b/tools/binman/etype/vblock.py
new file mode 100644
index 0000000..c4d970e
--- /dev/null
+++ b/tools/binman/etype/vblock.py
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+# Support for a Chromium OS verified boot block, used to sign a read-write
+# section of the image.
+
+from collections import OrderedDict
+import os
+
+from entry import Entry, EntryArg
+
+import fdt_util
+import tools
+
+class Entry_vblock(Entry):
+    """An entry which contains a Chromium OS verified boot block
+
+    Properties / Entry arguments:
+        - keydir: Directory containing the public keys to use
+        - keyblock: Name of the key file to use (inside keydir)
+        - signprivate: Name of provide key file to use (inside keydir)
+        - version: Version number of the vblock (typically 1)
+        - kernelkey: Name of the kernel key to use (inside keydir)
+        - preamble-flags: Value of the vboot preamble flags (typically 0)
+
+    Output files:
+        - input.<unique_name> - input file passed to futility
+        - vblock.<unique_name> - output file generated by futility (which is
+            used as the entry contents)
+
+    Chromium OS signs the read-write firmware and kernel, writing the signature
+    in this block. This allows U-Boot to verify that the next firmware stage
+    and kernel are genuine.
+    """
+    def __init__(self, section, etype, node):
+        Entry.__init__(self, section, etype, node)
+        self.content = fdt_util.GetPhandleList(self._node, 'content')
+        if not self.content:
+            self.Raise("Vblock must have a 'content' property")
+        (self.keydir, self.keyblock, self.signprivate, self.version,
+         self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([
+            EntryArg('keydir', str),
+            EntryArg('keyblock', str),
+            EntryArg('signprivate', str),
+            EntryArg('version', int),
+            EntryArg('kernelkey', str),
+            EntryArg('preamble-flags', int)])
+
+    def ObtainContents(self):
+        # Join up the data files to be signed
+        input_data = ''
+        for entry_phandle in self.content:
+            data = self.section.GetContentsByPhandle(entry_phandle, self)
+            if data is None:
+                # Data not available yet
+                return False
+            input_data += data
+
+        uniq = self.GetUniqueName()
+        output_fname = tools.GetOutputFilename('vblock.%s' % uniq)
+        input_fname = tools.GetOutputFilename('input.%s' % uniq)
+        tools.WriteFile(input_fname, input_data)
+        prefix = self.keydir + '/'
+        args = [
+            'vbutil_firmware',
+            '--vblock', output_fname,
+            '--keyblock', prefix + self.keyblock,
+            '--signprivate', prefix + self.signprivate,
+            '--version', '%d' % self.version,
+            '--fv', input_fname,
+            '--kernelkey', prefix + self.kernelkey,
+            '--flags', '%d' % self.preamble_flags,
+        ]
+        #out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label))
+        stdout = tools.Run('futility', *args)
+        self.SetContents(tools.ReadFile(output_fname))
+        return True
diff --git a/tools/binman/etype/x86_start16.py b/tools/binman/etype/x86_start16.py
new file mode 100644
index 0000000..7d32ecd
--- /dev/null
+++ b/tools/binman/etype/x86_start16.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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):
+    """x86 16-bit start-up code for U-Boot
+
+    Properties / Entry arguments:
+        - filename: Filename of u-boot-x86-16bit.bin (default
+            'u-boot-x86-16bit.bin')
+
+    x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code
+    must be placed at a particular address. This entry holds that code. It is
+    typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+    for changing to 32-bit mode and jumping to U-Boot's entry point, which
+    requires 32-bit mode (for 32-bit U-Boot).
+
+    For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, 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
index 0000000..d85909e
--- /dev/null
+++ b/tools/binman/etype/x86_start16_spl.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# 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):
+    """x86 16-bit start-up code for SPL
+
+    Properties / Entry arguments:
+        - filename: Filename of spl/u-boot-x86-16bit-spl.bin (default
+            'spl/u-boot-x86-16bit-spl.bin')
+
+    x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
+    must be placed at a particular address. This entry holds that code. It is
+    typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+    for changing to 32-bit mode and starting SPL, which in turn changes to
+    64-bit mode and jumps to U-Boot (for 64-bit U-Boot).
+
+    For 32-bit U-Boot, the 'x86_start16' entry type is used instead.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'spl/u-boot-x86-16bit-spl.bin'
diff --git a/tools/binman/etype/x86_start16_tpl.py b/tools/binman/etype/x86_start16_tpl.py
new file mode 100644
index 0000000..46ce169
--- /dev/null
+++ b/tools/binman/etype/x86_start16_tpl.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Entry-type module for the 16-bit x86 start-up code for U-Boot TPL
+#
+
+from entry import Entry
+from blob import Entry_blob
+
+class Entry_x86_start16_tpl(Entry_blob):
+    """x86 16-bit start-up code for TPL
+
+    Properties / Entry arguments:
+        - filename: Filename of tpl/u-boot-x86-16bit-tpl.bin (default
+            'tpl/u-boot-x86-16bit-tpl.bin')
+
+    x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
+    must be placed at a particular address. This entry holds that code. It is
+    typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
+    for changing to 32-bit mode and starting TPL, which in turn jumps to SPL.
+
+    If TPL is not being used, the 'x86_start16_spl or 'x86_start16' entry types
+    may be used instead.
+    """
+    def __init__(self, section, etype, node):
+        Entry_blob.__init__(self, section, etype, node)
+
+    def GetDefaultFilename(self):
+        return 'tpl/u-boot-x86-16bit-tpl.bin'
diff --git a/tools/binman/fdt_test.py b/tools/binman/fdt_test.py
new file mode 100644
index 0000000..ac6f910
--- /dev/null
+++ b/tools/binman/fdt_test.py
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Test for the fdt modules
+
+import os
+import sys
+import tempfile
+import unittest
+
+import fdt
+from fdt 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)
+
+    @classmethod
+    def tearDownClass(self):
+        tools._FinaliseForTest()
+
+    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, dt):
+        node = dt.GetNode('/microcode/update@0')
+        node.DeleteProp('data')
+
+    def testFdtNormal(self):
+        fname = self.GetCompiled('034_x86_ucode.dts')
+        dt = FdtScan(fname)
+        self._DeleteProp(dt)
+
+    def testFdtNormalProp(self):
+        fname = self.GetCompiled('045_prop_test.dts')
+        dt = FdtScan(fname)
+        node = dt.GetNode('/binman/intel-me')
+        self.assertEquals('intel-me', node.name)
+        val = fdt_util.GetString(node, 'filename')
+        self.assertEquals(str, type(val))
+        self.assertEquals('me.bin', val)
+
+        prop = node.props['intval']
+        self.assertEquals(fdt.TYPE_INT, prop.type)
+        self.assertEquals(3, fdt_util.GetInt(node, 'intval'))
+
+        prop = node.props['intarray']
+        self.assertEquals(fdt.TYPE_INT, prop.type)
+        self.assertEquals(list, type(prop.value))
+        self.assertEquals(2, len(prop.value))
+        self.assertEquals([5, 6],
+                          [fdt_util.fdt32_to_cpu(val) for val in prop.value])
+
+        prop = node.props['byteval']
+        self.assertEquals(fdt.TYPE_BYTE, prop.type)
+        self.assertEquals(chr(8), prop.value)
+
+        prop = node.props['bytearray']
+        self.assertEquals(fdt.TYPE_BYTE, prop.type)
+        self.assertEquals(list, type(prop.value))
+        self.assertEquals(str, type(prop.value[0]))
+        self.assertEquals(3, len(prop.value))
+        self.assertEquals([chr(1), '#', '4'], prop.value)
+
+        prop = node.props['longbytearray']
+        self.assertEquals(fdt.TYPE_INT, prop.type)
+        self.assertEquals(0x090a0b0c, fdt_util.GetInt(node, 'longbytearray'))
+
+        prop = node.props['stringval']
+        self.assertEquals(fdt.TYPE_STRING, prop.type)
+        self.assertEquals('message2', fdt_util.GetString(node, 'stringval'))
+
+        prop = node.props['stringarray']
+        self.assertEquals(fdt.TYPE_STRING, prop.type)
+        self.assertEquals(list, type(prop.value))
+        self.assertEquals(3, len(prop.value))
+        self.assertEquals(['another', 'multi-word', 'message'], prop.value)
diff --git a/tools/binman/fmap_util.py b/tools/binman/fmap_util.py
new file mode 100644
index 0000000..be3cbee
--- /dev/null
+++ b/tools/binman/fmap_util.py
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Support for flashrom's FMAP format. This supports a header followed by a
+# number of 'areas', describing regions of a firmware storage device,
+# generally SPI flash.
+
+import collections
+import struct
+
+# constants imported from lib/fmap.h
+FMAP_SIGNATURE = '__FMAP__'
+FMAP_VER_MAJOR = 1
+FMAP_VER_MINOR = 0
+FMAP_STRLEN = 32
+
+FMAP_AREA_STATIC = 1 << 0
+FMAP_AREA_COMPRESSED = 1 << 1
+FMAP_AREA_RO = 1 << 2
+
+FMAP_HEADER_LEN = 56
+FMAP_AREA_LEN = 42
+
+FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
+FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
+
+FMAP_HEADER_NAMES = (
+    'signature',
+    'ver_major',
+    'ver_minor',
+    'base',
+    'image_size',
+    'name',
+    'nareas',
+)
+
+FMAP_AREA_NAMES = (
+    'offset',
+    'size',
+    'name',
+    'flags',
+)
+
+# These are the two data structures supported by flashrom, a header (which
+# appears once at the start) and an area (which is repeated until the end of
+# the list of areas)
+FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
+FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
+
+
+def NameToFmap(name):
+    return name.replace('\0', '').replace('-', '_').upper()
+
+def ConvertName(field_names, fields):
+    """Convert a name to something flashrom likes
+
+    Flashrom requires upper case, underscores instead of hyphens. We remove any
+    null characters as well. This updates the 'name' value in fields.
+
+    Args:
+        field_names: List of field names for this struct
+        fields: Dict:
+            key: Field name
+            value: value of that field (string for the ones we support)
+    """
+    name_index = field_names.index('name')
+    fields[name_index] = NameToFmap(fields[name_index])
+
+def DecodeFmap(data):
+    """Decode a flashmap into a header and list of areas
+
+    Args:
+        data: Data block containing the FMAP
+
+    Returns:
+        Tuple:
+            header: FmapHeader object
+            List of FmapArea objects
+    """
+    fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
+    ConvertName(FMAP_HEADER_NAMES, fields)
+    header = FmapHeader(*fields)
+    areas = []
+    data = data[FMAP_HEADER_LEN:]
+    for area in range(header.nareas):
+        fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
+        ConvertName(FMAP_AREA_NAMES, fields)
+        areas.append(FmapArea(*fields))
+        data = data[FMAP_AREA_LEN:]
+    return header, areas
+
+def EncodeFmap(image_size, name, areas):
+    """Create a new FMAP from a list of areas
+
+    Args:
+        image_size: Size of image, to put in the header
+        name: Name of image, to put in the header
+        areas: List of FmapArea objects
+
+    Returns:
+        String containing the FMAP created
+    """
+    def _FormatBlob(fmt, names, obj):
+        params = [getattr(obj, name) for name in names]
+        ConvertName(names, params)
+        return struct.pack(fmt, *params)
+
+    values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))
+    blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
+    for area in areas:
+        blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
+    return blob
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
new file mode 100644
index 0000000..e77fce5
--- /dev/null
+++ b/tools/binman/ftest.py
@@ -0,0 +1,1776 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# To run a single test, change to this directory, and:
+#
+#    python -m unittest func_test.TestFunctional.testHelp
+
+import hashlib
+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 elf
+import fdt
+import fdt_util
+import fmap_util
+import test_util
+import state
+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       = '56780123456789abcde'
+U_BOOT_TPL_DATA       = 'tpl'
+BLOB_DATA             = '89'
+ME_DATA               = '0abcd'
+VGA_DATA              = 'vga'
+U_BOOT_DTB_DATA       = 'udtb'
+U_BOOT_SPL_DTB_DATA   = 'spldtb'
+U_BOOT_TPL_DTB_DATA   = 'tpldtb'
+X86_START16_DATA      = 'start16'
+X86_START16_SPL_DATA  = 'start16spl'
+X86_START16_TPL_DATA  = 'start16tpl'
+PPC_MPC85XX_BR_DATA   = 'ppcmpc85xxbr'
+U_BOOT_NODTB_DATA     = 'nodtb with microcode pointer somewhere in here'
+U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
+U_BOOT_TPL_NODTB_DATA = 'tplnodtb with microcode pointer somewhere in here'
+FSP_DATA              = 'fsp'
+CMC_DATA              = 'cmc'
+VBT_DATA              = 'vbt'
+MRC_DATA              = 'mrc'
+TEXT_DATA             = 'text'
+TEXT_DATA2            = 'text2'
+TEXT_DATA3            = 'text3'
+CROS_EC_RW_DATA       = 'ecrw'
+GBB_DATA              = 'gbbd'
+BMPBLK_DATA           = 'bmp'
+VBLOCK_DATA           = 'vblk'
+FILES_DATA            = ("sorry I'm late\nOh, don't bother apologising, I'm " +
+                         "sorry you're alive\n")
+COMPRESS_DATA         = 'data to compress'
+REFCODE_DATA          = 'refcode'
+
+
+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):
+        global entry
+        import entry
+
+        # 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('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
+        TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
+        TestFunctional._MakeInputFile('me.bin', ME_DATA)
+        TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
+        self._ResetDtbs()
+        TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
+        TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
+        TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
+                                      X86_START16_SPL_DATA)
+        TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin',
+                                      X86_START16_TPL_DATA)
+        TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
+        TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
+                                      U_BOOT_SPL_NODTB_DATA)
+        TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
+                                      U_BOOT_TPL_NODTB_DATA)
+        TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
+        TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
+        TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
+        TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
+        TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
+        TestFunctional._MakeInputDir('devkeys')
+        TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
+        TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
+
+        # 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())
+
+        shutil.copytree(self.TestFile('files'),
+                        os.path.join(self._indir, 'files'))
+
+        TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
+
+    @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()
+
+    @classmethod
+    def _ResetDtbs(self):
+        TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
+        TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
+        TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
+
+    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)
+        """
+        args = list(args)
+        if '-D' in sys.argv:
+            args = args + ['-D']
+        (options, args) = cmdline.ParseArgs(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, debug=False, map=False, update_dtb=False,
+                    entry_args=None, images=None, use_real_dtb=False):
+        """Run binman with a given test file
+
+        Args:
+            fname: Device-tree source filename to use (e.g. 005_simple.dts)
+            debug: True to enable debugging output
+            map: True to output map files for the images
+            update_dtb: Update the offset and size of each entry in the device
+                tree before packing it into the image
+            entry_args: Dict of entry args to supply to binman
+                key: arg name
+                value: value of that arg
+            images: List of image names to build
+        """
+        args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
+        if debug:
+            args.append('-D')
+        if map:
+            args.append('-m')
+        if update_dtb:
+            args.append('-up')
+        if not use_real_dtb:
+            args.append('--fake-dtb')
+        if entry_args:
+            for arg, value in entry_args.iteritems():
+                args.append('-a%s=%s' % (arg, value))
+        if images:
+            for image in images:
+                args += ['-i', image]
+        return self._DoBinman(*args)
+
+    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
+        """
+        tools.PrepareOutputDir(None)
+        dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
+        with open(dtb) as fd:
+            data = fd.read()
+            TestFunctional._MakeInputFile(outfile, data)
+        tools.FinaliseOutputDir()
+        return data
+
+    def _GetDtbContentsForSplTpl(self, dtb_data, name):
+        """Create a version of the main DTB for SPL or SPL
+
+        For testing we don't actually have different versions of the DTB. With
+        U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
+        we don't normally have any unwanted nodes.
+
+        We still want the DTBs for SPL and TPL to be different though, since
+        otherwise it is confusing to know which one we are looking at. So add
+        an 'spl' or 'tpl' property to the top-level node.
+        """
+        dtb = fdt.Fdt.FromData(dtb_data)
+        dtb.Scan()
+        dtb.GetNode('/binman').AddZeroProp(name)
+        dtb.Sync(auto_resize=True)
+        dtb.Pack()
+        return dtb.GetContents()
+
+    def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
+                       update_dtb=False, entry_args=None, reset_dtbs=True):
+        """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. 005_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.
+            map: True to output map files for the images
+            update_dtb: Update the offset and size of each entry in the device
+                tree before packing it into the image
+
+        Returns:
+            Tuple:
+                Resulting image contents
+                Device tree contents
+                Map data showing contents of image (or None if none)
+                Output device tree binary filename ('u-boot.dtb' path)
+        """
+        dtb_data = None
+        # Use the compiled test file as the u-boot-dtb input
+        if use_real_dtb:
+            dtb_data = self._SetupDtb(fname)
+            infile = os.path.join(self._indir, 'u-boot.dtb')
+
+            # For testing purposes, make a copy of the DT for SPL and TPL. Add
+            # a node indicating which it is, so aid verification.
+            for name in ['spl', 'tpl']:
+                dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
+                outfile = os.path.join(self._indir, dtb_fname)
+                TestFunctional._MakeInputFile(dtb_fname,
+                        self._GetDtbContentsForSplTpl(dtb_data, name))
+
+        try:
+            retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
+                    entry_args=entry_args, use_real_dtb=use_real_dtb)
+            self.assertEqual(0, retcode)
+            out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
+
+            # Find the (only) image, read it and return its contents
+            image = control.images['image']
+            image_fname = tools.GetOutputFilename('image.bin')
+            self.assertTrue(os.path.exists(image_fname))
+            if map:
+                map_fname = tools.GetOutputFilename('image.map')
+                with open(map_fname) as fd:
+                    map_data = fd.read()
+            else:
+                map_data = None
+            with open(image_fname) as fd:
+                return fd.read(), dtb_data, map_data, out_dtb_fname
+        finally:
+            # Put the test file back
+            if reset_dtbs and use_real_dtb:
+                self._ResetDtbs()
+
+    def _DoReadFile(self, fname, use_real_dtb=False):
+        """Helper function which discards the device-tree binary
+
+        Args:
+            fname: Device-tree source filename to use (e.g. 005_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:
+            Resulting image contents
+        """
+        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: Filename 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 _MakeInputDir(self, dirname):
+        """Create a new test input directory, creating directories as needed
+
+        Args:
+            dirname: Directory name to create
+
+        Returns:
+            Full pathname of directory created
+        """
+        pathname = os.path.join(self._indir, dirname)
+        if not os.path.exists(pathname):
+            os.makedirs(pathname)
+        return pathname
+
+    @classmethod
+    def _SetupSplElf(self, src_fname='bss_data'):
+        """Set up an ELF file with a '_dt_ucode_base_size' symbol
+
+        Args:
+            Filename of ELF file to use as SPL
+        """
+        with open(self.TestFile(src_fname)) as fd:
+            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
+
+    @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
+        """
+        offset = 0
+        for entry in entries.values():
+            self.assertEqual(offset, entry.offset)
+            offset += 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 _GetPropTree(self, dtb, prop_names):
+        def AddNode(node, path):
+            if node.name != '/':
+                path += '/' + node.name
+            for subnode in node.subnodes:
+                for prop in subnode.props.values():
+                    if prop.name in prop_names:
+                        prop_path = path + '/' + subnode.name + ':' + prop.name
+                        tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
+                            prop.value)
+                AddNode(subnode, path)
+
+        tree = {}
+        AddNode(dtb.GetRoot(), '')
+        return tree
+
+    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')
+        # Remove possible extraneous strings
+        extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
+        gothelp = result.stdout.replace(extra, '')
+        self.assertEqual(len(gothelp), 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)
+
+    def testBoard(self):
+        """Test that we can run it with a specific board"""
+        self._SetupDtb('005_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('001_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('002_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('003_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('004_invalid_entry.dts'))
+        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('005_simple.dts')
+        self.assertEqual(U_BOOT_DATA, data)
+
+    def testSimpleDebug(self):
+        """Test a simple binman run with debugging enabled"""
+        data = self._DoTestFile('005_simple.dts', debug=True)
+
+    def testDual(self):
+        """Test that we can handle creating two images
+
+        This also tests image padding.
+        """
+        retcode = self._DoTestFile('006_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('007_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('008_pack.dts')
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        entries = image.GetEntries()
+        self.assertEqual(5, len(entries))
+
+        # First u-boot
+        self.assertIn('u-boot', entries)
+        entry = entries['u-boot']
+        self.assertEqual(0, entry.offset)
+        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.offset)
+        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.offset)
+        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.offset)
+        self.assertEqual(len(U_BOOT_DATA), entry.size)
+
+        # Fifth u-boot, placed at a fixed offset
+        self.assertIn('u-boot-fixed', entries)
+        entry = entries['u-boot-fixed']
+        self.assertEqual(61, entry.offset)
+        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('009_pack_extra.dts')
+
+        self.assertEqual(0, retcode)
+        self.assertIn('image', control.images)
+        image = control.images['image']
+        entries = image.GetEntries()
+        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.offset)
+        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.offset)
+        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.offset)
+        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.offset)
+        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.offset)
+        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('010_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('011_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 offset that does not match its alignment"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('012_pack_inv_align.dts')
+        self.assertIn("Node '/binman/u-boot': Offset 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('013_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('014_pack_overlap.dts')
+        self.assertIn("Node '/binman/u-boot-align': Offset 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('015_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('016_pack_image_overflow.dts')
+        self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
+                      "size 0x3 (3)", str(e.exception))
+
+    def testPackImageSize(self):
+        """Test that the image size can be set"""
+        retcode = self._DoTestFile('017_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('018_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('019_pack_inv_image_align.dts')
+        self.assertIn("Section '/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('020_pack_inv_image_align_power2.dts')
+        self.assertIn("Section '/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"""
+        self._SetupSplElf()
+        data = self._DoReadFile('021_image_pad.dts')
+        self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
+
+    def testImageName(self):
+        """Test that image files can be named"""
+        retcode = self._DoTestFile('022_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('023_blob.dts')
+        self.assertEqual(BLOB_DATA, data)
+
+    def testPackSorted(self):
+        """Test that entries can be sorted"""
+        self._SetupSplElf()
+        data = self._DoReadFile('024_sorted.dts')
+        self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
+                         U_BOOT_DATA, data)
+
+    def testPackZeroOffset(self):
+        """Test that an entry at offset 0 is not given a new offset"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('025_pack_zero_size.dts')
+        self.assertIn("Node '/binman/u-boot-spl': Offset 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('026_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('027_pack_4gb_no_size.dts')
+        self.assertIn("Section '/binman': Section size must be provided when "
+                      "using end-at-4gb", str(e.exception))
+
+    def test4gbAndSkipAtStartTogether(self):
+        """Test that the end-at-4gb and skip-at-size property can't be used
+        together"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('80_4gb_and_skip_at_start_together.dts')
+        self.assertIn("Section '/binman': Provide either 'end-at-4gb' or "
+                      "'skip-at-start'", str(e.exception))
+
+    def testPackX86RomOutside(self):
+        """Test that the end-at-4gb property checks for offset boundaries"""
+        with self.assertRaises(ValueError) as e:
+            self._DoTestFile('028_pack_4gb_outside.dts')
+        self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
+                      "the section starting at 0xffffffe0 (4294967264)",
+                      str(e.exception))
+
+    def testPackX86Rom(self):
+        """Test that a basic x86 ROM can be created"""
+        self._SetupSplElf()
+        data = self._DoReadFile('029_x86-rom.dts')
+        self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
+                         chr(0) * 2, 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('031_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('030_x86-rom-me-no-desc.dts')
+        self.assertIn("Node '/binman/intel-me': No offset set with "
+                      "offset-unset: should another entry provide this correct "
+                      "offset?", str(e.exception))
+
+    def testPackX86RomMe(self):
+        """Test that an x86 ROM with an ME region can be created"""
+        data = self._DoReadFile('031_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('032_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('033_x86-start16.dts')
+        self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
+
+    def testPackPowerpcMpc85xxBootpgResetvec(self):
+        """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
+        created"""
+        data = self._DoReadFile('81_powerpc_mpc85xx_bootpg_resetvec.dts')
+        self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
+
+    def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
+        """Handle running a test for insertion of microcode
+
+        Args:
+            dts_fname: Name of test .dts file
+            nodtb_data: Data that we expect in the first section
+            ucode_second: True if the microsecond entry is second instead of
+                third
+
+        Returns:
+            Tuple:
+                Contents of first region (U-Boot or SPL)
+                Offset and size components of microcode pointer, as inserted
+                    in the above (two 4-byte words)
+        """
+        data = self._DoReadFile(dts_fname, True)
+
+        # Now check the device tree has no microcode
+        if ucode_second:
+            ucode_content = data[len(nodtb_data):]
+            ucode_pos = len(nodtb_data)
+            dtb_with_ucode = ucode_content[16:]
+            fdt_len = self.GetFdtLen(dtb_with_ucode)
+        else:
+            dtb_with_ucode = data[len(nodtb_data):]
+            fdt_len = self.GetFdtLen(dtb_with_ucode)
+            ucode_content = dtb_with_ucode[fdt_len:]
+            ucode_pos = len(nodtb_data) + fdt_len
+        fname = tools.GetOutputFilename('test.dtb')
+        with open(fname, 'wb') as fd:
+            fd.write(dtb_with_ucode)
+        dtb = fdt.FdtScan(fname)
+        ucode = dtb.GetNode('/microcode')
+        self.assertTrue(ucode)
+        for node in ucode.subnodes:
+            self.assertFalse(node.props.get('data'))
+
+        # Check that the microcode appears immediately after the Fdt
+        # This matches the concatenation of the data properties in
+        # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
+        ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
+                                 0x78235609)
+        self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
+
+        # Check that the microcode pointer was inserted. It should match the
+        # expected offset and size
+        pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
+                                   len(ucode_data))
+        u_boot = data[:len(nodtb_data)]
+        return u_boot, pos_and_size
+
+    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
+        """
+        first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
+                                                     U_BOOT_NODTB_DATA)
+        self.assertEqual('nodtb with microcode' + pos_and_size +
+                         ' somewhere in here', first)
+
+    def _RunPackUbootSingleMicrocode(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
+            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().
+        data = self._DoReadFile('035_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]
+
+        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 offset 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()
+
+    def testUBootImg(self):
+        """Test that u-boot.img can be put in a file"""
+        data = self._DoReadFile('036_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('037_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('038_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('039_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
+        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()
+            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('040_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 "
+                "section 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('044_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('041_unknown_pos_size.dts', True)
+        self.assertIn("Section '/binman': Unable to set offset/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('042_intel-fsp.dts')
+        self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
+
+    def testPackCmc(self):
+        """Test that an image with a CMC binary can be created"""
+        data = self._DoReadFile('043_intel-cmc.dts')
+        self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
+
+    def testPackVbt(self):
+        """Test that an image with a VBT binary can be created"""
+        data = self._DoReadFile('046_intel-vbt.dts')
+        self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
+
+    def testSplBssPad(self):
+        """Test that we can pad SPL's BSS with zeros"""
+        # ELF file with a '__bss_size' symbol
+        self._SetupSplElf()
+        data = self._DoReadFile('047_spl_bss_pad.dts')
+        self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
+
+    def testSplBssPadMissing(self):
+        """Test that a missing symbol is detected"""
+        self._SetupSplElf('u_boot_ucode_ptr')
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('047_spl_bss_pad.dts')
+        self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
+                      str(e.exception))
+
+    def testPackStart16Spl(self):
+        """Test that an image with an x86 start16 SPL region can be created"""
+        data = self._DoReadFile('048_x86-start16-spl.dts')
+        self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
+
+    def _PackUbootSplMicrocode(self, dts, ucode_second=False):
+        """Helper function for microcode tests
+
+        We expect to see the following in the image, in order:
+            u-boot-spl-nodtb.bin with a microcode pointer inserted at the
+                correct place
+            u-boot.dtb with the microcode removed
+            the microcode
+
+        Args:
+            dts: Device tree file to use for test
+            ucode_second: True if the microsecond entry is second instead of
+                third
+        """
+        self._SetupSplElf('u_boot_ucode_ptr')
+        first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
+                                                     ucode_second=ucode_second)
+        self.assertEqual('splnodtb with microc' + pos_and_size +
+                         'ter somewhere in here', first)
+
+    def testPackUbootSplMicrocode(self):
+        """Test that x86 microcode can be handled correctly in SPL"""
+        self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
+
+    def testPackUbootSplMicrocodeReorder(self):
+        """Test that order doesn't matter for microcode entries
+
+        This is the same as testPackUbootSplMicrocode but when we process the
+        u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
+        entry, so we reply on binman to try later.
+        """
+        self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
+                                    ucode_second=True)
+
+    def testPackMrc(self):
+        """Test that an image with an MRC binary can be created"""
+        data = self._DoReadFile('050_intel_mrc.dts')
+        self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
+
+    def testSplDtb(self):
+        """Test that an image with spl/u-boot-spl.dtb can be created"""
+        data = self._DoReadFile('051_u_boot_spl_dtb.dts')
+        self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
+
+    def testSplNoDtb(self):
+        """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
+        data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
+        self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
+
+    def testSymbols(self):
+        """Test binman can assign symbols embedded in U-Boot"""
+        elf_fname = self.TestFile('u_boot_binman_syms')
+        syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
+        addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
+        self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
+
+        self._SetupSplElf('u_boot_binman_syms')
+        data = self._DoReadFile('053_symbols.dts')
+        sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
+        expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
+                    U_BOOT_DATA +
+                    sym_values + U_BOOT_SPL_DATA[16:])
+        self.assertEqual(expected, data)
+
+    def testPackUnitAddress(self):
+        """Test that we support multiple binaries with the same name"""
+        data = self._DoReadFile('054_unit_address.dts')
+        self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
+
+    def testSections(self):
+        """Basic test of sections"""
+        data = self._DoReadFile('055_sections.dts')
+        expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
+                    U_BOOT_DATA + '&' * 4)
+        self.assertEqual(expected, data)
+
+    def testMap(self):
+        """Tests outputting a map of the images"""
+        _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
+        self.assertEqual('''ImagePos    Offset      Size  Name
+00000000  00000000  00000028  main-section
+00000000   00000000  00000010  section@0
+00000000    00000000  00000004  u-boot
+00000010   00000010  00000010  section@1
+00000010    00000000  00000004  u-boot
+00000020   00000020  00000004  section@2
+00000020    00000000  00000004  u-boot
+''', map_data)
+
+    def testNamePrefix(self):
+        """Tests that name prefixes are used"""
+        _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
+        self.assertEqual('''ImagePos    Offset      Size  Name
+00000000  00000000  00000028  main-section
+00000000   00000000  00000010  section@0
+00000000    00000000  00000004  ro-u-boot
+00000010   00000010  00000010  section@1
+00000010    00000000  00000004  rw-u-boot
+''', map_data)
+
+    def testUnknownContents(self):
+        """Test that obtaining the contents works as expected"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('057_unknown_contents.dts', True)
+        self.assertIn("Section '/binman': Internal error: Could not complete "
+                "processing of contents: remaining [<_testing.Entry__testing ",
+                str(e.exception))
+
+    def testBadChangeSize(self):
+        """Test that trying to change the size of an entry fails"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('059_change_size.dts', True)
+        self.assertIn("Node '/binman/_testing': Cannot update entry size from "
+                      '2 to 1', str(e.exception))
+
+    def testUpdateFdt(self):
+        """Test that we can update the device tree with offset/size info"""
+        _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
+                                                     update_dtb=True)
+        dtb = fdt.Fdt(out_dtb_fname)
+        dtb.Scan()
+        props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
+        self.assertEqual({
+            'image-pos': 0,
+            'offset': 0,
+            '_testing:offset': 32,
+            '_testing:size': 1,
+            '_testing:image-pos': 32,
+            'section@0/u-boot:offset': 0,
+            'section@0/u-boot:size': len(U_BOOT_DATA),
+            'section@0/u-boot:image-pos': 0,
+            'section@0:offset': 0,
+            'section@0:size': 16,
+            'section@0:image-pos': 0,
+
+            'section@1/u-boot:offset': 0,
+            'section@1/u-boot:size': len(U_BOOT_DATA),
+            'section@1/u-boot:image-pos': 16,
+            'section@1:offset': 16,
+            'section@1:size': 16,
+            'section@1:image-pos': 16,
+            'size': 40
+        }, props)
+
+    def testUpdateFdtBad(self):
+        """Test that we detect when ProcessFdt never completes"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
+        self.assertIn('Could not complete processing of Fdt: remaining '
+                      '[<_testing.Entry__testing', str(e.exception))
+
+    def testEntryArgs(self):
+        """Test passing arguments to entries from the command line"""
+        entry_args = {
+            'test-str-arg': 'test1',
+            'test-int-arg': '456',
+        }
+        self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
+        self.assertIn('image', control.images)
+        entry = control.images['image'].GetEntries()['_testing']
+        self.assertEqual('test0', entry.test_str_fdt)
+        self.assertEqual('test1', entry.test_str_arg)
+        self.assertEqual(123, entry.test_int_fdt)
+        self.assertEqual(456, entry.test_int_arg)
+
+    def testEntryArgsMissing(self):
+        """Test missing arguments and properties"""
+        entry_args = {
+            'test-int-arg': '456',
+        }
+        self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
+        entry = control.images['image'].GetEntries()['_testing']
+        self.assertEqual('test0', entry.test_str_fdt)
+        self.assertEqual(None, entry.test_str_arg)
+        self.assertEqual(None, entry.test_int_fdt)
+        self.assertEqual(456, entry.test_int_arg)
+
+    def testEntryArgsRequired(self):
+        """Test missing arguments and properties"""
+        entry_args = {
+            'test-int-arg': '456',
+        }
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('064_entry_args_required.dts')
+        self.assertIn("Node '/binman/_testing': Missing required "
+            'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
+            str(e.exception))
+
+    def testEntryArgsInvalidFormat(self):
+        """Test that an invalid entry-argument format is detected"""
+        args = ['-d', self.TestFile('064_entry_args_required.dts'), '-ano-value']
+        with self.assertRaises(ValueError) as e:
+            self._DoBinman(*args)
+        self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
+
+    def testEntryArgsInvalidInteger(self):
+        """Test that an invalid entry-argument integer is detected"""
+        entry_args = {
+            'test-int-arg': 'abc',
+        }
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
+        self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
+                      "'test-int-arg' (value 'abc') to integer",
+            str(e.exception))
+
+    def testEntryArgsInvalidDatatype(self):
+        """Test that an invalid entry-argument datatype is detected
+
+        This test could be written in entry_test.py except that it needs
+        access to control.entry_args, which seems more than that module should
+        be able to see.
+        """
+        entry_args = {
+            'test-bad-datatype-arg': '12',
+        }
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
+                                entry_args=entry_args)
+        self.assertIn('GetArg() internal error: Unknown data type ',
+                      str(e.exception))
+
+    def testText(self):
+        """Test for a text entry type"""
+        entry_args = {
+            'test-id': TEXT_DATA,
+            'test-id2': TEXT_DATA2,
+            'test-id3': TEXT_DATA3,
+        }
+        data, _, _, _ = self._DoReadFileDtb('066_text.dts',
+                                            entry_args=entry_args)
+        expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
+                    TEXT_DATA3 + 'some text')
+        self.assertEqual(expected, data)
+
+    def testEntryDocs(self):
+        """Test for creation of entry documentation"""
+        with test_util.capture_sys_output() as (stdout, stderr):
+            control.WriteEntryDocs(binman.GetEntryModules())
+        self.assertTrue(len(stdout.getvalue()) > 0)
+
+    def testEntryDocsMissing(self):
+        """Test handling of missing entry documentation"""
+        with self.assertRaises(ValueError) as e:
+            with test_util.capture_sys_output() as (stdout, stderr):
+                control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
+        self.assertIn('Documentation is missing for modules: u_boot',
+                      str(e.exception))
+
+    def testFmap(self):
+        """Basic test of generation of a flashrom fmap"""
+        data = self._DoReadFile('067_fmap.dts')
+        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
+        expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
+        self.assertEqual(expected, data[:32])
+        self.assertEqual('__FMAP__', fhdr.signature)
+        self.assertEqual(1, fhdr.ver_major)
+        self.assertEqual(0, fhdr.ver_minor)
+        self.assertEqual(0, fhdr.base)
+        self.assertEqual(16 + 16 +
+                         fmap_util.FMAP_HEADER_LEN +
+                         fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
+        self.assertEqual('FMAP', fhdr.name)
+        self.assertEqual(3, fhdr.nareas)
+        for fentry in fentries:
+            self.assertEqual(0, fentry.flags)
+
+        self.assertEqual(0, fentries[0].offset)
+        self.assertEqual(4, fentries[0].size)
+        self.assertEqual('RO_U_BOOT', fentries[0].name)
+
+        self.assertEqual(16, fentries[1].offset)
+        self.assertEqual(4, fentries[1].size)
+        self.assertEqual('RW_U_BOOT', fentries[1].name)
+
+        self.assertEqual(32, fentries[2].offset)
+        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
+                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
+        self.assertEqual('FMAP', fentries[2].name)
+
+    def testBlobNamedByArg(self):
+        """Test we can add a blob with the filename coming from an entry arg"""
+        entry_args = {
+            'cros-ec-rw-path': 'ecrw.bin',
+        }
+        data, _, _, _ = self._DoReadFileDtb('068_blob_named_by_arg.dts',
+                                            entry_args=entry_args)
+
+    def testFill(self):
+        """Test for an fill entry type"""
+        data = self._DoReadFile('069_fill.dts')
+        expected = 8 * chr(0xff) + 8 * chr(0)
+        self.assertEqual(expected, data)
+
+    def testFillNoSize(self):
+        """Test for an fill entry type with no size"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('070_fill_no_size.dts')
+        self.assertIn("'fill' entry must have a size property",
+                      str(e.exception))
+
+    def _HandleGbbCommand(self, pipe_list):
+        """Fake calls to the futility utility"""
+        if pipe_list[0][0] == 'futility':
+            fname = pipe_list[0][-1]
+            # Append our GBB data to the file, which will happen every time the
+            # futility command is called.
+            with open(fname, 'a') as fd:
+                fd.write(GBB_DATA)
+            return command.CommandResult()
+
+    def testGbb(self):
+        """Test for the Chromium OS Google Binary Block"""
+        command.test_result = self._HandleGbbCommand
+        entry_args = {
+            'keydir': 'devkeys',
+            'bmpblk': 'bmpblk.bin',
+        }
+        data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
+
+        # Since futility
+        expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
+        self.assertEqual(expected, data)
+
+    def testGbbTooSmall(self):
+        """Test for the Chromium OS Google Binary Block being large enough"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('072_gbb_too_small.dts')
+        self.assertIn("Node '/binman/gbb': GBB is too small",
+                      str(e.exception))
+
+    def testGbbNoSize(self):
+        """Test for the Chromium OS Google Binary Block having a size"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('073_gbb_no_size.dts')
+        self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
+                      str(e.exception))
+
+    def _HandleVblockCommand(self, pipe_list):
+        """Fake calls to the futility utility"""
+        if pipe_list[0][0] == 'futility':
+            fname = pipe_list[0][3]
+            with open(fname, 'wb') as fd:
+                fd.write(VBLOCK_DATA)
+            return command.CommandResult()
+
+    def testVblock(self):
+        """Test for the Chromium OS Verified Boot Block"""
+        command.test_result = self._HandleVblockCommand
+        entry_args = {
+            'keydir': 'devkeys',
+        }
+        data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
+                                            entry_args=entry_args)
+        expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
+        self.assertEqual(expected, data)
+
+    def testVblockNoContent(self):
+        """Test we detect a vblock which has no content to sign"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('075_vblock_no_content.dts')
+        self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
+                      'property', str(e.exception))
+
+    def testVblockBadPhandle(self):
+        """Test that we detect a vblock with an invalid phandle in contents"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('076_vblock_bad_phandle.dts')
+        self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
+                      '1000', str(e.exception))
+
+    def testVblockBadEntry(self):
+        """Test that we detect an entry that points to a non-entry"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFile('077_vblock_bad_entry.dts')
+        self.assertIn("Node '/binman/vblock': Cannot find entry for node "
+                      "'other'", str(e.exception))
+
+    def testTpl(self):
+        """Test that an image with TPL and ots device tree can be created"""
+        # ELF file with a '__bss_size' symbol
+        with open(self.TestFile('bss_data')) as fd:
+            TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
+        data = self._DoReadFile('078_u_boot_tpl.dts')
+        self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
+
+    def testUsesPos(self):
+        """Test that the 'pos' property cannot be used anymore"""
+        with self.assertRaises(ValueError) as e:
+           data = self._DoReadFile('079_uses_pos.dts')
+        self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
+                      "'pos'", str(e.exception))
+
+    def testFillZero(self):
+        """Test for an fill entry type with a size of 0"""
+        data = self._DoReadFile('080_fill_empty.dts')
+        self.assertEqual(chr(0) * 16, data)
+
+    def testTextMissing(self):
+        """Test for a text entry type where there is no text"""
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('066_text.dts',)
+        self.assertIn("Node '/binman/text': No value provided for text label "
+                      "'test-id'", str(e.exception))
+
+    def testPackStart16Tpl(self):
+        """Test that an image with an x86 start16 TPL region can be created"""
+        data = self._DoReadFile('081_x86-start16-tpl.dts')
+        self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
+
+    def testSelectImage(self):
+        """Test that we can select which images to build"""
+        with test_util.capture_sys_output() as (stdout, stderr):
+            retcode = self._DoTestFile('006_dual_image.dts', images=['image2'])
+        self.assertEqual(0, retcode)
+        self.assertIn('Skipping images: image1', stdout.getvalue())
+
+        self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
+        self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
+
+    def testUpdateFdtAll(self):
+        """Test that all device trees are updated with offset/size info"""
+        data, _, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
+                                            use_real_dtb=True, update_dtb=True)
+
+        base_expected = {
+            'section:image-pos': 0,
+            'u-boot-tpl-dtb:size': 513,
+            'u-boot-spl-dtb:size': 513,
+            'u-boot-spl-dtb:offset': 493,
+            'image-pos': 0,
+            'section/u-boot-dtb:image-pos': 0,
+            'u-boot-spl-dtb:image-pos': 493,
+            'section/u-boot-dtb:size': 493,
+            'u-boot-tpl-dtb:image-pos': 1006,
+            'section/u-boot-dtb:offset': 0,
+            'section:size': 493,
+            'offset': 0,
+            'section:offset': 0,
+            'u-boot-tpl-dtb:offset': 1006,
+            'size': 1519
+        }
+
+        # We expect three device-tree files in the output, one after the other.
+        # Read them in sequence. We look for an 'spl' property in the SPL tree,
+        # and 'tpl' in the TPL tree, to make sure they are distinct from the
+        # main U-Boot tree. All three should have the same postions and offset.
+        start = 0
+        for item in ['', 'spl', 'tpl']:
+            dtb = fdt.Fdt.FromData(data[start:])
+            dtb.Scan()
+            props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
+                                            'spl', 'tpl'])
+            expected = dict(base_expected)
+            if item:
+                expected[item] = 0
+            self.assertEqual(expected, props)
+            start += dtb._fdt_obj.totalsize()
+
+    def testUpdateFdtOutput(self):
+        """Test that output DTB files are updated"""
+        try:
+            data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
+                    use_real_dtb=True, update_dtb=True, reset_dtbs=False)
+
+            # Unfortunately, compiling a source file always results in a file
+            # called source.dtb (see fdt_util.EnsureCompiled()). The test
+            # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
+            # binman as a file called u-boot.dtb. To fix this, copy the file
+            # over to the expected place.
+            #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
+                    #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
+            start = 0
+            for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
+                          'tpl/u-boot-tpl.dtb.out']:
+                dtb = fdt.Fdt.FromData(data[start:])
+                size = dtb._fdt_obj.totalsize()
+                pathname = tools.GetOutputFilename(os.path.split(fname)[1])
+                outdata = tools.ReadFile(pathname)
+                name = os.path.split(fname)[0]
+
+                if name:
+                    orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
+                else:
+                    orig_indata = dtb_data
+                self.assertNotEqual(outdata, orig_indata,
+                        "Expected output file '%s' be updated" % pathname)
+                self.assertEqual(outdata, data[start:start + size],
+                        "Expected output file '%s' to match output image" %
+                        pathname)
+                start += size
+        finally:
+            self._ResetDtbs()
+
+    def _decompress(self, data):
+        out = os.path.join(self._indir, 'lz4.tmp')
+        with open(out, 'wb') as fd:
+            fd.write(data)
+        return tools.Run('lz4', '-dc', out)
+        '''
+        try:
+            orig = lz4.frame.decompress(data)
+        except AttributeError:
+            orig = lz4.decompress(data)
+        '''
+
+    def testCompress(self):
+        """Test compression of blobs"""
+        data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
+                                            use_real_dtb=True, update_dtb=True)
+        dtb = fdt.Fdt(out_dtb_fname)
+        dtb.Scan()
+        props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
+        orig = self._decompress(data)
+        self.assertEquals(COMPRESS_DATA, orig)
+        expected = {
+            'blob:uncomp-size': len(COMPRESS_DATA),
+            'blob:size': len(data),
+            'size': len(data),
+            }
+        self.assertEqual(expected, props)
+
+    def testFiles(self):
+        """Test bringing in multiple files"""
+        data = self._DoReadFile('084_files.dts')
+        self.assertEqual(FILES_DATA, data)
+
+    def testFilesCompress(self):
+        """Test bringing in multiple files and compressing them"""
+        data = self._DoReadFile('085_files_compress.dts')
+
+        image = control.images['image']
+        entries = image.GetEntries()
+        files = entries['files']
+        entries = files._section._entries
+
+        orig = ''
+        for i in range(1, 3):
+            key = '%d.dat' % i
+            start = entries[key].image_pos
+            len = entries[key].size
+            chunk = data[start:start + len]
+            orig += self._decompress(chunk)
+
+        self.assertEqual(FILES_DATA, orig)
+
+    def testFilesMissing(self):
+        """Test missing files"""
+        with self.assertRaises(ValueError) as e:
+            data = self._DoReadFile('086_files_none.dts')
+        self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
+                      'no files', str(e.exception))
+
+    def testFilesNoPattern(self):
+        """Test missing files"""
+        with self.assertRaises(ValueError) as e:
+            data = self._DoReadFile('087_files_no_pattern.dts')
+        self.assertIn("Node '/binman/files': Missing 'pattern' property",
+                      str(e.exception))
+
+    def testExpandSize(self):
+        """Test an expanding entry"""
+        data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
+                                                   map=True)
+        expect = ('a' * 8 + U_BOOT_DATA +
+                  MRC_DATA + 'b' * 1 + U_BOOT_DATA +
+                  'c' * 8 + U_BOOT_DATA +
+                  'd' * 8)
+        self.assertEqual(expect, data)
+        self.assertEqual('''ImagePos    Offset      Size  Name
+00000000  00000000  00000028  main-section
+00000000   00000000  00000008  fill
+00000008   00000008  00000004  u-boot
+0000000c   0000000c  00000004  section
+0000000c    00000000  00000003  intel-mrc
+00000010   00000010  00000004  u-boot2
+00000014   00000014  0000000c  section2
+00000014    00000000  00000008  fill
+0000001c    00000008  00000004  u-boot
+00000020   00000020  00000008  fill2
+''', map_data)
+
+    def testExpandSizeBad(self):
+        """Test an expanding entry which fails to provide contents"""
+        with test_util.capture_sys_output() as (stdout, stderr):
+            with self.assertRaises(ValueError) as e:
+                self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
+        self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
+                      'expanding entry', str(e.exception))
+
+    def testHash(self):
+        """Test hashing of the contents of an entry"""
+        _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
+                use_real_dtb=True, update_dtb=True)
+        dtb = fdt.Fdt(out_dtb_fname)
+        dtb.Scan()
+        hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
+        m = hashlib.sha256()
+        m.update(U_BOOT_DATA)
+        self.assertEqual(m.digest(), ''.join(hash_node.value))
+
+    def testHashNoAlgo(self):
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
+        self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
+                      'hash node', str(e.exception))
+
+    def testHashBadAlgo(self):
+        with self.assertRaises(ValueError) as e:
+            self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
+        self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
+                      str(e.exception))
+
+    def testHashSection(self):
+        """Test hashing of the contents of an entry"""
+        _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
+                use_real_dtb=True, update_dtb=True)
+        dtb = fdt.Fdt(out_dtb_fname)
+        dtb.Scan()
+        hash_node = dtb.GetNode('/binman/section/hash').props['value']
+        m = hashlib.sha256()
+        m.update(U_BOOT_DATA)
+        m.update(16 * 'a')
+        self.assertEqual(m.digest(), ''.join(hash_node.value))
+
+    def testPackUBootTplMicrocode(self):
+        """Test that x86 microcode can be handled correctly in TPL
+
+        We expect to see the following in the image, in order:
+            u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
+                place
+            u-boot-tpl.dtb with the microcode removed
+            the microcode
+        """
+        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
+            TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
+        first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
+                                                     U_BOOT_TPL_NODTB_DATA)
+        self.assertEqual('tplnodtb with microc' + pos_and_size +
+                         'ter somewhere in here', first)
+
+    def testFmapX86(self):
+        """Basic test of generation of a flashrom fmap"""
+        data = self._DoReadFile('094_fmap_x86.dts')
+        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
+        expected = U_BOOT_DATA + MRC_DATA + 'a' * (32 - 7)
+        self.assertEqual(expected, data[:32])
+        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
+
+        self.assertEqual(0x100, fhdr.image_size)
+
+        self.assertEqual(0, fentries[0].offset)
+        self.assertEqual(4, fentries[0].size)
+        self.assertEqual('U_BOOT', fentries[0].name)
+
+        self.assertEqual(4, fentries[1].offset)
+        self.assertEqual(3, fentries[1].size)
+        self.assertEqual('INTEL_MRC', fentries[1].name)
+
+        self.assertEqual(32, fentries[2].offset)
+        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
+                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
+        self.assertEqual('FMAP', fentries[2].name)
+
+    def testFmapX86Section(self):
+        """Basic test of generation of a flashrom fmap"""
+        data = self._DoReadFile('095_fmap_x86_section.dts')
+        expected = U_BOOT_DATA + MRC_DATA + 'b' * (32 - 7)
+        self.assertEqual(expected, data[:32])
+        fhdr, fentries = fmap_util.DecodeFmap(data[36:])
+
+        self.assertEqual(0x100, fhdr.image_size)
+
+        self.assertEqual(0, fentries[0].offset)
+        self.assertEqual(4, fentries[0].size)
+        self.assertEqual('U_BOOT', fentries[0].name)
+
+        self.assertEqual(4, fentries[1].offset)
+        self.assertEqual(3, fentries[1].size)
+        self.assertEqual('INTEL_MRC', fentries[1].name)
+
+        self.assertEqual(36, fentries[2].offset)
+        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
+                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
+        self.assertEqual('FMAP', fentries[2].name)
+
+    def testElf(self):
+        """Basic test of ELF entries"""
+        self._SetupSplElf()
+        with open(self.TestFile('bss_data')) as fd:
+            TestFunctional._MakeInputFile('-boot', fd.read())
+        data = self._DoReadFile('096_elf.dts')
+
+    def testElfStripg(self):
+        """Basic test of ELF entries"""
+        self._SetupSplElf()
+        with open(self.TestFile('bss_data')) as fd:
+            TestFunctional._MakeInputFile('-boot', fd.read())
+        data = self._DoReadFile('097_elf_strip.dts')
+
+    def testPackOverlapMap(self):
+        """Test that overlapping regions are detected"""
+        with test_util.capture_sys_output() as (stdout, stderr):
+            with self.assertRaises(ValueError) as e:
+                self._DoTestFile('014_pack_overlap.dts', map=True)
+        map_fname = tools.GetOutputFilename('image.map')
+        self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
+                         stdout.getvalue())
+
+        # We should not get an inmage, but there should be a map file
+        self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
+        self.assertTrue(os.path.exists(map_fname))
+        map_data = tools.ReadFile(map_fname)
+        self.assertEqual('''ImagePos    Offset      Size  Name
+<none>    00000000  00000007  main-section
+<none>     00000000  00000004  u-boot
+<none>     00000003  00000004  u-boot-align
+''', map_data)
+
+    def testPacRefCode(self):
+        """Test that an image with an Intel Reference code binary works"""
+        data = self._DoReadFile('100_intel_refcode.dts')
+        self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tools/binman/image.py b/tools/binman/image.py
new file mode 100644
index 0000000..f237ae3
--- /dev/null
+++ b/tools/binman/image.py
@@ -0,0 +1,153 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Class for an image, the output of binman
+#
+
+from __future__ import print_function
+
+from collections import OrderedDict
+from operator import attrgetter
+import re
+import sys
+
+import fdt_util
+import bsection
+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
+        _filename: Output filename for image
+        _sections: Sections present in this image (may be one or more)
+
+    Args:
+        test: True if this is being called from a test of Images. This this case
+            there is no device tree defining the structure of the section, so
+            we create a section manually.
+    """
+    def __init__(self, name, node, test=False):
+        self._node = node
+        self._name = name
+        self._size = None
+        self._filename = '%s.bin' % self._name
+        if test:
+            self._section = bsection.Section('main-section', None, self._node,
+                                             self, True)
+        else:
+            self._ReadNode()
+
+    def _ReadNode(self):
+        """Read properties from the image node"""
+        self._size = fdt_util.GetInt(self._node, 'size')
+        filename = fdt_util.GetString(self._node, 'filename')
+        if filename:
+            self._filename = filename
+        self._section = bsection.Section('main-section', None, self._node, self)
+
+    def GetFdtSet(self):
+        """Get the set of device tree files used by this image"""
+        return self._section.GetFdtSet()
+
+    def ExpandEntries(self):
+        """Expand out any entries which have calculated sub-entries
+
+        Some entries are expanded out at runtime, e.g. 'files', which produces
+        a section containing a list of files. Process these entries so that
+        this information is added to the device tree.
+        """
+        self._section.ExpandEntries()
+
+    def AddMissingProperties(self):
+        """Add properties that are not present in the device tree
+
+        When binman has completed packing the entries the offset and size of
+        each entry are known. But before this the device tree may not specify
+        these. Add any missing properties, with a dummy value, so that the
+        size of the entry is correct. That way we can insert the correct values
+        later.
+        """
+        self._section.AddMissingProperties()
+
+    def ProcessFdt(self, fdt):
+        """Allow entries to adjust the device tree
+
+        Some entries need to adjust the device tree for their purposes. This
+        may involve adding or deleting properties.
+        """
+        return self._section.ProcessFdt(fdt)
+
+    def GetEntryContents(self):
+        """Call ObtainContents() for the section
+        """
+        self._section.GetEntryContents()
+
+    def GetEntryOffsets(self):
+        """Handle entries that want to set the offset/size of other entries
+
+        This calls each entry's GetOffsets() method. If it returns a list
+        of entries to update, it updates them.
+        """
+        self._section.GetEntryOffsets()
+
+    def PackEntries(self):
+        """Pack all entries into the image"""
+        self._section.PackEntries()
+
+    def CheckSize(self):
+        """Check that the image contents does not exceed its size, etc."""
+        self._size = self._section.CheckSize()
+
+    def CheckEntries(self):
+        """Check that entries do not overlap or extend outside the image"""
+        self._section.CheckEntries()
+
+    def SetCalculatedProperties(self):
+        self._section.SetCalculatedProperties()
+
+    def SetImagePos(self):
+        self._section.SetImagePos(0)
+
+    def ProcessEntryContents(self):
+        """Call the ProcessContents() method for each entry
+
+        This is intended to adjust the contents as needed by the entry type.
+        """
+        self._section.ProcessEntryContents()
+
+    def WriteSymbols(self):
+        """Write symbol values into binary files for access at run time"""
+        self._section.WriteSymbols()
+
+    def BuildImage(self):
+        """Write the image to a file"""
+        fname = tools.GetOutputFilename(self._filename)
+        with open(fname, 'wb') as fd:
+            self._section.BuildSection(fd, 0)
+
+    def GetEntries(self):
+        return self._section.GetEntries()
+
+    def WriteMap(self):
+        """Write a map of the image to a .map file
+
+        Returns:
+            Filename of map file written
+        """
+        filename = '%s.map' % self._name
+        fname = tools.GetOutputFilename(filename)
+        with open(fname, 'w') as fd:
+            print('%8s  %8s  %8s  %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
+                  file=fd)
+            self._section.WriteMap(fd, 0)
+        return fname
diff --git a/tools/binman/image_test.py b/tools/binman/image_test.py
new file mode 100644
index 0000000..3775e1a
--- /dev/null
+++ b/tools/binman/image_test.py
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2017 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Test for the image module
+
+import unittest
+
+from image import Image
+from test_util import capture_sys_output
+
+class TestImage(unittest.TestCase):
+    def testInvalidFormat(self):
+        image = Image('name', 'node', test=True)
+        section = image._section
+        with self.assertRaises(ValueError) as e:
+            section.LookupSymbol('_binman_something_prop_', False, 'msg')
+        self.assertIn(
+            "msg: Symbol '_binman_something_prop_' has invalid format",
+            str(e.exception))
+
+    def testMissingSymbol(self):
+        image = Image('name', 'node', test=True)
+        section = image._section
+        section._entries = {}
+        with self.assertRaises(ValueError) as e:
+            section.LookupSymbol('_binman_type_prop_pname', False, 'msg')
+        self.assertIn("msg: Entry 'type' not found in list ()",
+                      str(e.exception))
+
+    def testMissingSymbolOptional(self):
+        image = Image('name', 'node', test=True)
+        section = image._section
+        section._entries = {}
+        with capture_sys_output() as (stdout, stderr):
+            val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg')
+        self.assertEqual(val, None)
+        self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n",
+                         stderr.getvalue())
+        self.assertEqual('', stdout.getvalue())
+
+    def testBadProperty(self):
+        image = Image('name', 'node', test=True)
+        section = image._section
+        section._entries = {'u-boot': 1}
+        with self.assertRaises(ValueError) as e:
+            section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
+        self.assertIn("msg: No such property 'bad", str(e.exception))
diff --git a/tools/binman/state.py b/tools/binman/state.py
new file mode 100644
index 0000000..d945e4b
--- /dev/null
+++ b/tools/binman/state.py
@@ -0,0 +1,253 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2018 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# Holds and modifies the state information held by binman
+#
+
+import hashlib
+import re
+from sets import Set
+
+import os
+import tools
+
+# Records the device-tree files known to binman, keyed by filename (e.g.
+# 'u-boot-spl.dtb')
+fdt_files = {}
+
+# Arguments passed to binman to provide arguments to entries
+entry_args = {}
+
+# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
+# ftest.py)
+use_fake_dtb = False
+
+# Set of all device tree files references by images
+fdt_set = Set()
+
+# Same as above, but excluding the main one
+fdt_subset = Set()
+
+# The DTB which contains the full image information
+main_dtb = None
+
+def GetFdt(fname):
+    """Get the Fdt object for a particular device-tree filename
+
+    Binman keeps track of at least one device-tree file called u-boot.dtb but
+    can also have others (e.g. for SPL). This function looks up the given
+    filename and returns the associated Fdt object.
+
+    Args:
+        fname: Filename to look up (e.g. 'u-boot.dtb').
+
+    Returns:
+        Fdt object associated with the filename
+    """
+    return fdt_files[fname]
+
+def GetFdtPath(fname):
+    """Get the full pathname of a particular Fdt object
+
+    Similar to GetFdt() but returns the pathname associated with the Fdt.
+
+    Args:
+        fname: Filename to look up (e.g. 'u-boot.dtb').
+
+    Returns:
+        Full path name to the associated Fdt
+    """
+    return fdt_files[fname]._fname
+
+def GetFdtContents(fname):
+    """Looks up the FDT pathname and contents
+
+    This is used to obtain the Fdt pathname and contents when needed by an
+    entry. It supports a 'fake' dtb, allowing tests to substitute test data for
+    the real dtb.
+
+    Args:
+        fname: Filename to look up (e.g. 'u-boot.dtb').
+
+    Returns:
+        tuple:
+            pathname to Fdt
+            Fdt data (as bytes)
+    """
+    if fname in fdt_files and not use_fake_dtb:
+        pathname = GetFdtPath(fname)
+        data = GetFdt(fname).GetContents()
+    else:
+        pathname = tools.GetInputFilename(fname)
+        data = tools.ReadFile(pathname)
+    return pathname, data
+
+def SetEntryArgs(args):
+    """Set the value of the entry args
+
+    This sets up the entry_args dict which is used to supply entry arguments to
+    entries.
+
+    Args:
+        args: List of entry arguments, each in the format "name=value"
+    """
+    global entry_args
+
+    entry_args = {}
+    if args:
+        for arg in args:
+            m = re.match('([^=]*)=(.*)', arg)
+            if not m:
+                raise ValueError("Invalid entry arguemnt '%s'" % arg)
+            entry_args[m.group(1)] = m.group(2)
+
+def GetEntryArg(name):
+    """Get the value of an entry argument
+
+    Args:
+        name: Name of argument to retrieve
+
+    Returns:
+        String value of argument
+    """
+    return entry_args.get(name)
+
+def Prepare(images, dtb):
+    """Get device tree files ready for use
+
+    This sets up a set of device tree files that can be retrieved by GetFdts().
+    At present there is only one, that for U-Boot proper.
+
+    Args:
+        images: List of images being used
+        dtb: Main dtb
+    """
+    global fdt_set, fdt_subset, fdt_files, main_dtb
+    # Import these here in case libfdt.py is not available, in which case
+    # the above help option still works.
+    import fdt
+    import fdt_util
+
+    # If we are updating the DTBs we need to put these updated versions
+    # where Entry_blob_dtb can find them. We can ignore 'u-boot.dtb'
+    # since it is assumed to be the one passed in with options.dt, and
+    # was handled just above.
+    main_dtb = dtb
+    fdt_files.clear()
+    fdt_files['u-boot.dtb'] = dtb
+    fdt_subset = Set()
+    if not use_fake_dtb:
+        for image in images.values():
+            fdt_subset.update(image.GetFdtSet())
+        fdt_subset.discard('u-boot.dtb')
+        for other_fname in fdt_subset:
+            infile = tools.GetInputFilename(other_fname)
+            other_fname_dtb = fdt_util.EnsureCompiled(infile)
+            out_fname = tools.GetOutputFilename('%s.out' %
+                    os.path.split(other_fname)[1])
+            tools.WriteFile(out_fname, tools.ReadFile(other_fname_dtb))
+            other_dtb = fdt.FdtScan(out_fname)
+            fdt_files[other_fname] = other_dtb
+
+def GetFdts():
+    """Yield all device tree files being used by binman
+
+    Yields:
+        Device trees being used (U-Boot proper, SPL, TPL)
+    """
+    yield main_dtb
+    for other_fname in fdt_subset:
+        yield fdt_files[other_fname]
+
+def GetUpdateNodes(node):
+    """Yield all the nodes that need to be updated in all device trees
+
+    The property referenced by this node is added to any device trees which
+    have the given node. Due to removable of unwanted notes, SPL and TPL may
+    not have this node.
+
+    Args:
+        node: Node object in the main device tree to look up
+
+    Yields:
+        Node objects in each device tree that is in use (U-Boot proper, which
+            is node, SPL and TPL)
+    """
+    yield node
+    for dtb in fdt_files.values():
+        if dtb != node.GetFdt():
+            other_node = dtb.GetNode(node.path)
+            if other_node:
+                yield other_node
+
+def AddZeroProp(node, prop):
+    """Add a new property to affected device trees with an integer value of 0.
+
+    Args:
+        prop_name: Name of property
+    """
+    for n in GetUpdateNodes(node):
+        n.AddZeroProp(prop)
+
+def AddSubnode(node, name):
+    """Add a new subnode to a node in affected device trees
+
+    Args:
+        node: Node to add to
+        name: name of node to add
+
+    Returns:
+        New subnode that was created in main tree
+    """
+    first = None
+    for n in GetUpdateNodes(node):
+        subnode = n.AddSubnode(name)
+        if not first:
+            first = subnode
+    return first
+
+def AddString(node, prop, value):
+    """Add a new string property to affected device trees
+
+    Args:
+        prop_name: Name of property
+        value: String value (which will be \0-terminated in the DT)
+    """
+    for n in GetUpdateNodes(node):
+        n.AddString(prop, value)
+
+def SetInt(node, prop, value):
+    """Update an integer property in affected device trees with an integer value
+
+    This is not allowed to change the size of the FDT.
+
+    Args:
+        prop_name: Name of property
+    """
+    for n in GetUpdateNodes(node):
+        n.SetInt(prop, value)
+
+def CheckAddHashProp(node):
+    hash_node = node.FindNode('hash')
+    if hash_node:
+        algo = hash_node.props.get('algo')
+        if not algo:
+            return "Missing 'algo' property for hash node"
+        if algo.value == 'sha256':
+            size = 32
+        else:
+            return "Unknown hash algorithm '%s'" % algo
+        for n in GetUpdateNodes(hash_node):
+            n.AddEmptyProp('value', size)
+
+def CheckSetHashValue(node, get_data_func):
+    hash_node = node.FindNode('hash')
+    if hash_node:
+        algo = hash_node.props.get('algo').value
+        if algo == 'sha256':
+            m = hashlib.sha256()
+            m.update(get_data_func())
+            data = m.digest()
+        for n in GetUpdateNodes(hash_node):
+            n.SetData('value', data)
diff --git a/tools/binman/test/001_invalid.dts b/tools/binman/test/001_invalid.dts
new file mode 100644
index 0000000..7d00455
--- /dev/null
+++ b/tools/binman/test/001_invalid.dts
@@ -0,0 +1,5 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
diff --git a/tools/binman/test/002_missing_node.dts b/tools/binman/test/002_missing_node.dts
new file mode 100644
index 0000000..3a51ec2
--- /dev/null
+++ b/tools/binman/test/002_missing_node.dts
@@ -0,0 +1,6 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+};
diff --git a/tools/binman/test/003_empty.dts b/tools/binman/test/003_empty.dts
new file mode 100644
index 0000000..493c9a0
--- /dev/null
+++ b/tools/binman/test/003_empty.dts
@@ -0,0 +1,9 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+	};
+};
diff --git a/tools/binman/test/004_invalid_entry.dts b/tools/binman/test/004_invalid_entry.dts
new file mode 100644
index 0000000..b043455
--- /dev/null
+++ b/tools/binman/test/004_invalid_entry.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		not-a-valid-type {
+		};
+	};
+};
diff --git a/tools/binman/test/005_simple.dts b/tools/binman/test/005_simple.dts
new file mode 100644
index 0000000..3771aa2
--- /dev/null
+++ b/tools/binman/test/005_simple.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/006_dual_image.dts b/tools/binman/test/006_dual_image.dts
new file mode 100644
index 0000000..78be16f
--- /dev/null
+++ b/tools/binman/test/006_dual_image.dts
@@ -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/007_bad_align.dts b/tools/binman/test/007_bad_align.dts
new file mode 100644
index 0000000..123bb13
--- /dev/null
+++ b/tools/binman/test/007_bad_align.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+			align = <23>;
+		};
+	};
+};
diff --git a/tools/binman/test/008_pack.dts b/tools/binman/test/008_pack.dts
new file mode 100644
index 0000000..a88785d
--- /dev/null
+++ b/tools/binman/test/008_pack.dts
@@ -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";
+			offset = <61>;
+		};
+	};
+};
diff --git a/tools/binman/test/009_pack_extra.dts b/tools/binman/test/009_pack_extra.dts
new file mode 100644
index 0000000..0765707
--- /dev/null
+++ b/tools/binman/test/009_pack_extra.dts
@@ -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/010_pack_align_power2.dts b/tools/binman/test/010_pack_align_power2.dts
new file mode 100644
index 0000000..8f6253a
--- /dev/null
+++ b/tools/binman/test/010_pack_align_power2.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+			align = <5>;
+		};
+	};
+};
diff --git a/tools/binman/test/011_pack_align_size_power2.dts b/tools/binman/test/011_pack_align_size_power2.dts
new file mode 100644
index 0000000..04f7672
--- /dev/null
+++ b/tools/binman/test/011_pack_align_size_power2.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+			align-size = <55>;
+		};
+	};
+};
diff --git a/tools/binman/test/012_pack_inv_align.dts b/tools/binman/test/012_pack_inv_align.dts
new file mode 100644
index 0000000..d8dd600
--- /dev/null
+++ b/tools/binman/test/012_pack_inv_align.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+			offset = <5>;
+			align = <4>;
+		};
+	};
+};
diff --git a/tools/binman/test/013_pack_inv_size_align.dts b/tools/binman/test/013_pack_inv_size_align.dts
new file mode 100644
index 0000000..dfafa13
--- /dev/null
+++ b/tools/binman/test/013_pack_inv_size_align.dts
@@ -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/014_pack_overlap.dts b/tools/binman/test/014_pack_overlap.dts
new file mode 100644
index 0000000..3895cba
--- /dev/null
+++ b/tools/binman/test/014_pack_overlap.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+		};
+
+		u-boot-align {
+			type = "u-boot";
+			offset = <3>;
+		};
+	};
+};
diff --git a/tools/binman/test/015_pack_overflow.dts b/tools/binman/test/015_pack_overflow.dts
new file mode 100644
index 0000000..6f65433
--- /dev/null
+++ b/tools/binman/test/015_pack_overflow.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+			size = <3>;
+		};
+	};
+};
diff --git a/tools/binman/test/016_pack_image_overflow.dts b/tools/binman/test/016_pack_image_overflow.dts
new file mode 100644
index 0000000..6ae66f3
--- /dev/null
+++ b/tools/binman/test/016_pack_image_overflow.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <3>;
+
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/017_pack_image_size.dts b/tools/binman/test/017_pack_image_size.dts
new file mode 100644
index 0000000..2360eb5
--- /dev/null
+++ b/tools/binman/test/017_pack_image_size.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <7>;
+
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/018_pack_image_align.dts b/tools/binman/test/018_pack_image_align.dts
new file mode 100644
index 0000000..16cd2a4
--- /dev/null
+++ b/tools/binman/test/018_pack_image_align.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		align-size = <16>;
+
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/019_pack_inv_image_align.dts b/tools/binman/test/019_pack_inv_image_align.dts
new file mode 100644
index 0000000..e5ee87b
--- /dev/null
+++ b/tools/binman/test/019_pack_inv_image_align.dts
@@ -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/020_pack_inv_image_align_power2.dts b/tools/binman/test/020_pack_inv_image_align_power2.dts
new file mode 100644
index 0000000..a428c4b
--- /dev/null
+++ b/tools/binman/test/020_pack_inv_image_align_power2.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		align-size = <131>;
+
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/021_image_pad.dts b/tools/binman/test/021_image_pad.dts
new file mode 100644
index 0000000..c651668
--- /dev/null
+++ b/tools/binman/test/021_image_pad.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0xff>;
+		u-boot-spl {
+		};
+
+		u-boot {
+			offset = <20>;
+		};
+	};
+};
diff --git a/tools/binman/test/022_image_name.dts b/tools/binman/test/022_image_name.dts
new file mode 100644
index 0000000..94fc069
--- /dev/null
+++ b/tools/binman/test/022_image_name.dts
@@ -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/023_blob.dts b/tools/binman/test/023_blob.dts
new file mode 100644
index 0000000..7dcff69
--- /dev/null
+++ b/tools/binman/test/023_blob.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		blob {
+			filename = "blobfile";
+		};
+	};
+};
diff --git a/tools/binman/test/024_sorted.dts b/tools/binman/test/024_sorted.dts
new file mode 100644
index 0000000..d35d39f
--- /dev/null
+++ b/tools/binman/test/024_sorted.dts
@@ -0,0 +1,17 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		u-boot {
+			offset = <22>;
+		};
+
+		u-boot-spl {
+			offset = <1>;
+		};
+	};
+};
diff --git a/tools/binman/test/025_pack_zero_size.dts b/tools/binman/test/025_pack_zero_size.dts
new file mode 100644
index 0000000..e863c44
--- /dev/null
+++ b/tools/binman/test/025_pack_zero_size.dts
@@ -0,0 +1,15 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot {
+		};
+
+		u-boot-spl {
+			offset = <0>;
+		};
+	};
+};
diff --git a/tools/binman/test/026_pack_u_boot_dtb.dts b/tools/binman/test/026_pack_u_boot_dtb.dts
new file mode 100644
index 0000000..2707a73
--- /dev/null
+++ b/tools/binman/test/026_pack_u_boot_dtb.dts
@@ -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/027_pack_4gb_no_size.dts b/tools/binman/test/027_pack_4gb_no_size.dts
new file mode 100644
index 0000000..371cca1
--- /dev/null
+++ b/tools/binman/test/027_pack_4gb_no_size.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		u-boot {
+			offset = <0xfffffff0>;
+		};
+
+		u-boot-spl {
+			offset = <0xfffffff7>;
+		};
+	};
+};
diff --git a/tools/binman/test/028_pack_4gb_outside.dts b/tools/binman/test/028_pack_4gb_outside.dts
new file mode 100644
index 0000000..2216abf
--- /dev/null
+++ b/tools/binman/test/028_pack_4gb_outside.dts
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <32>;
+		u-boot {
+			offset = <0>;
+		};
+
+		u-boot-spl {
+			offset = <0xffffffeb>;
+		};
+	};
+};
diff --git a/tools/binman/test/029_x86-rom.dts b/tools/binman/test/029_x86-rom.dts
new file mode 100644
index 0000000..d5c69f9
--- /dev/null
+++ b/tools/binman/test/029_x86-rom.dts
@@ -0,0 +1,19 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <32>;
+		u-boot {
+			offset = <0xffffffe0>;
+		};
+
+		u-boot-spl {
+			offset = <0xffffffeb>;
+		};
+	};
+};
diff --git a/tools/binman/test/030_x86-rom-me-no-desc.dts b/tools/binman/test/030_x86-rom-me-no-desc.dts
new file mode 100644
index 0000000..796cb87
--- /dev/null
+++ b/tools/binman/test/030_x86-rom-me-no-desc.dts
@@ -0,0 +1,16 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <16>;
+		intel-me {
+			filename = "me.bin";
+			offset-unset;
+		};
+	};
+};
diff --git a/tools/binman/test/031_x86-rom-me.dts b/tools/binman/test/031_x86-rom-me.dts
new file mode 100644
index 0000000..b8b0a5a
--- /dev/null
+++ b/tools/binman/test/031_x86-rom-me.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x800000>;
+		intel-descriptor {
+			filename = "descriptor.bin";
+		};
+
+		intel-me {
+			filename = "me.bin";
+			offset-unset;
+		};
+	};
+};
diff --git a/tools/binman/test/032_intel-vga.dts b/tools/binman/test/032_intel-vga.dts
new file mode 100644
index 0000000..9c532d0
--- /dev/null
+++ b/tools/binman/test/032_intel-vga.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-vga {
+			filename = "vga.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/033_x86-start16.dts b/tools/binman/test/033_x86-start16.dts
new file mode 100644
index 0000000..2e279de
--- /dev/null
+++ b/tools/binman/test/033_x86-start16.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		x86-start16 {
+		};
+	};
+};
diff --git a/tools/binman/test/034_x86_ucode.dts b/tools/binman/test/034_x86_ucode.dts
new file mode 100644
index 0000000..4072573
--- /dev/null
+++ b/tools/binman/test/034_x86_ucode.dts
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/035_x86_single_ucode.dts b/tools/binman/test/035_x86_single_ucode.dts
new file mode 100644
index 0000000..2b1f086
--- /dev/null
+++ b/tools/binman/test/035_x86_single_ucode.dts
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/036_u_boot_img.dts b/tools/binman/test/036_u_boot_img.dts
new file mode 100644
index 0000000..aa5a3fe
--- /dev/null
+++ b/tools/binman/test/036_u_boot_img.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot-img {
+		};
+	};
+};
diff --git a/tools/binman/test/037_x86_no_ucode.dts b/tools/binman/test/037_x86_no_ucode.dts
new file mode 100644
index 0000000..6da49c3
--- /dev/null
+++ b/tools/binman/test/037_x86_no_ucode.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x200>;
+		u-boot-with-ucode-ptr {
+		};
+
+		u-boot-dtb-with-ucode {
+		};
+
+		u-boot-ucode {
+		};
+	};
+};
diff --git a/tools/binman/test/038_x86_ucode_missing_node.dts b/tools/binman/test/038_x86_ucode_missing_node.dts
new file mode 100644
index 0000000..720677c
--- /dev/null
+++ b/tools/binman/test/038_x86_ucode_missing_node.dts
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/039_x86_ucode_missing_node2.dts b/tools/binman/test/039_x86_ucode_missing_node2.dts
new file mode 100644
index 0000000..10ac086
--- /dev/null
+++ b/tools/binman/test/039_x86_ucode_missing_node2.dts
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/040_x86_ucode_not_in_image.dts b/tools/binman/test/040_x86_ucode_not_in_image.dts
new file mode 100644
index 0000000..6097258
--- /dev/null
+++ b/tools/binman/test/040_x86_ucode_not_in_image.dts
@@ -0,0 +1,28 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/041_unknown_pos_size.dts b/tools/binman/test/041_unknown_pos_size.dts
new file mode 100644
index 0000000..94fe821
--- /dev/null
+++ b/tools/binman/test/041_unknown_pos_size.dts
@@ -0,0 +1,12 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			return-invalid-entry;
+		};
+        };
+};
diff --git a/tools/binman/test/042_intel-fsp.dts b/tools/binman/test/042_intel-fsp.dts
new file mode 100644
index 0000000..8a7c889
--- /dev/null
+++ b/tools/binman/test/042_intel-fsp.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-fsp {
+			filename = "fsp.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/043_intel-cmc.dts b/tools/binman/test/043_intel-cmc.dts
new file mode 100644
index 0000000..5a56c7d
--- /dev/null
+++ b/tools/binman/test/043_intel-cmc.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-cmc {
+			filename = "cmc.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/044_x86_optional_ucode.dts b/tools/binman/test/044_x86_optional_ucode.dts
new file mode 100644
index 0000000..24a7040
--- /dev/null
+++ b/tools/binman/test/044_x86_optional_ucode.dts
@@ -0,0 +1,30 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		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/045_prop_test.dts b/tools/binman/test/045_prop_test.dts
new file mode 100644
index 0000000..064de2b
--- /dev/null
+++ b/tools/binman/test/045_prop_test.dts
@@ -0,0 +1,23 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <16>;
+		intel-me {
+			filename = "me.bin";
+			offset-unset;
+			intval = <3>;
+			intarray = <5 6>;
+			byteval = [08];
+			bytearray = [01 23 34];
+			longbytearray = [09 0a 0b 0c];
+			stringval = "message2";
+			stringarray = "another", "multi-word", "message";
+		};
+	};
+};
diff --git a/tools/binman/test/046_intel-vbt.dts b/tools/binman/test/046_intel-vbt.dts
new file mode 100644
index 0000000..733f575
--- /dev/null
+++ b/tools/binman/test/046_intel-vbt.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-vbt {
+			filename = "vbt.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/047_spl_bss_pad.dts b/tools/binman/test/047_spl_bss_pad.dts
new file mode 100644
index 0000000..6bd88b8
--- /dev/null
+++ b/tools/binman/test/047_spl_bss_pad.dts
@@ -0,0 +1,17 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot-spl {
+		};
+
+		u-boot-spl-bss-pad {
+		};
+
+		u-boot {
+		};
+	};
+};
diff --git a/tools/binman/test/048_x86-start16-spl.dts b/tools/binman/test/048_x86-start16-spl.dts
new file mode 100644
index 0000000..e2009f1
--- /dev/null
+++ b/tools/binman/test/048_x86-start16-spl.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		x86-start16-spl {
+		};
+	};
+};
diff --git a/tools/binman/test/049_x86_ucode_spl.dts b/tools/binman/test/049_x86_ucode_spl.dts
new file mode 100644
index 0000000..350d2c4
--- /dev/null
+++ b/tools/binman/test/049_x86_ucode_spl.dts
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x200>;
+		u-boot-spl-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/050_intel_mrc.dts b/tools/binman/test/050_intel_mrc.dts
new file mode 100644
index 0000000..54cd52a
--- /dev/null
+++ b/tools/binman/test/050_intel_mrc.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-mrc {
+		};
+	};
+};
diff --git a/tools/binman/test/051_u_boot_spl_dtb.dts b/tools/binman/test/051_u_boot_spl_dtb.dts
new file mode 100644
index 0000000..3912f86
--- /dev/null
+++ b/tools/binman/test/051_u_boot_spl_dtb.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		u-boot-spl-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/052_u_boot_spl_nodtb.dts b/tools/binman/test/052_u_boot_spl_nodtb.dts
new file mode 100644
index 0000000..7f4e277
--- /dev/null
+++ b/tools/binman/test/052_u_boot_spl_nodtb.dts
@@ -0,0 +1,11 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot-spl-nodtb {
+		};
+	};
+};
diff --git a/tools/binman/test/053_symbols.dts b/tools/binman/test/053_symbols.dts
new file mode 100644
index 0000000..9f13567
--- /dev/null
+++ b/tools/binman/test/053_symbols.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0xff>;
+		u-boot-spl {
+		};
+
+		u-boot {
+			offset = <20>;
+		};
+
+		u-boot-spl2 {
+			type = "u-boot-spl";
+		};
+	};
+};
diff --git a/tools/binman/test/054_unit_address.dts b/tools/binman/test/054_unit_address.dts
new file mode 100644
index 0000000..3216dbb
--- /dev/null
+++ b/tools/binman/test/054_unit_address.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot@0 {
+		};
+		u-boot@1 {
+		};
+	};
+};
diff --git a/tools/binman/test/055_sections.dts b/tools/binman/test/055_sections.dts
new file mode 100644
index 0000000..6b306ae
--- /dev/null
+++ b/tools/binman/test/055_sections.dts
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+		section@2 {
+			u-boot {
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/056_name_prefix.dts b/tools/binman/test/056_name_prefix.dts
new file mode 100644
index 0000000..f38c80e
--- /dev/null
+++ b/tools/binman/test/056_name_prefix.dts
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/057_unknown_contents.dts b/tools/binman/test/057_unknown_contents.dts
new file mode 100644
index 0000000..6ea98d7
--- /dev/null
+++ b/tools/binman/test/057_unknown_contents.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			return-unknown-contents;
+		};
+	};
+};
diff --git a/tools/binman/test/058_x86_ucode_spl_needs_retry.dts b/tools/binman/test/058_x86_ucode_spl_needs_retry.dts
new file mode 100644
index 0000000..a04adaa
--- /dev/null
+++ b/tools/binman/test/058_x86_ucode_spl_needs_retry.dts
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x200>;
+		u-boot-spl-with-ucode-ptr {
+		};
+
+		/*
+		 * Microcode goes before the DTB which contains it, so binman
+		 * will need to obtain the contents of the next section before
+		 * obtaining the contents of this one.
+		 */
+		u-boot-ucode {
+		};
+
+		u-boot-dtb-with-ucode {
+		};
+	};
+
+	microcode {
+		update@0 {
+			data = <0x12345678 0x12345679>;
+		};
+		update@1 {
+			data = <0xabcd0000 0x78235609>;
+		};
+	};
+};
diff --git a/tools/binman/test/059_change_size.dts b/tools/binman/test/059_change_size.dts
new file mode 100644
index 0000000..1a69026
--- /dev/null
+++ b/tools/binman/test/059_change_size.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			bad-update-contents;
+		};
+	};
+};
diff --git a/tools/binman/test/060_fdt_update.dts b/tools/binman/test/060_fdt_update.dts
new file mode 100644
index 0000000..f53c8a5
--- /dev/null
+++ b/tools/binman/test/060_fdt_update.dts
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+		_testing {
+		};
+	};
+};
diff --git a/tools/binman/test/061_fdt_update_bad.dts b/tools/binman/test/061_fdt_update_bad.dts
new file mode 100644
index 0000000..e5abf31
--- /dev/null
+++ b/tools/binman/test/061_fdt_update_bad.dts
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		pad-byte = <0x26>;
+		size = <0x28>;
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+		_testing {
+			never-complete-process-fdt;
+		};
+	};
+};
diff --git a/tools/binman/test/062_entry_args.dts b/tools/binman/test/062_entry_args.dts
new file mode 100644
index 0000000..4d4f102
--- /dev/null
+++ b/tools/binman/test/062_entry_args.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			test-str-fdt = "test0";
+			test-int-fdt = <123>;
+		};
+	};
+};
diff --git a/tools/binman/test/063_entry_args_missing.dts b/tools/binman/test/063_entry_args_missing.dts
new file mode 100644
index 0000000..1644e2f
--- /dev/null
+++ b/tools/binman/test/063_entry_args_missing.dts
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			test-str-fdt = "test0";
+		};
+	};
+};
diff --git a/tools/binman/test/064_entry_args_required.dts b/tools/binman/test/064_entry_args_required.dts
new file mode 100644
index 0000000..705be10
--- /dev/null
+++ b/tools/binman/test/064_entry_args_required.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			require-args;
+			test-str-fdt = "test0";
+		};
+	};
+};
diff --git a/tools/binman/test/065_entry_args_unknown_datatype.dts b/tools/binman/test/065_entry_args_unknown_datatype.dts
new file mode 100644
index 0000000..3e4838f
--- /dev/null
+++ b/tools/binman/test/065_entry_args_unknown_datatype.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		_testing {
+			test-str-fdt = "test0";
+			test-int-fdt = <123>;
+			force-bad-datatype;
+		};
+	};
+};
diff --git a/tools/binman/test/066_text.dts b/tools/binman/test/066_text.dts
new file mode 100644
index 0000000..59b1fed
--- /dev/null
+++ b/tools/binman/test/066_text.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		text {
+			size = <8>;
+			text-label = "test-id";
+		};
+		text2 {
+			type = "text";
+			text-label = "test-id2";
+		};
+		text3 {
+			type = "text";
+			text-label = "test-id3";
+		};
+		/* This one does not use command-line args */
+		text4 {
+			type = "text";
+			text-label = "test-id4";
+			test-id4 = "some text";
+		};
+	};
+};
diff --git a/tools/binman/test/067_fmap.dts b/tools/binman/test/067_fmap.dts
new file mode 100644
index 0000000..9c0e293
--- /dev/null
+++ b/tools/binman/test/067_fmap.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		section@0 {
+			read-only;
+			name-prefix = "ro-";
+			size = <0x10>;
+			pad-byte = <0x21>;
+
+			u-boot {
+			};
+		};
+		section@1 {
+			name-prefix = "rw-";
+			size = <0x10>;
+			pad-byte = <0x61>;
+
+			u-boot {
+			};
+		};
+		fmap {
+		};
+	};
+};
diff --git a/tools/binman/test/068_blob_named_by_arg.dts b/tools/binman/test/068_blob_named_by_arg.dts
new file mode 100644
index 0000000..e129f84
--- /dev/null
+++ b/tools/binman/test/068_blob_named_by_arg.dts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		cros-ec-rw {
+		};
+	};
+};
diff --git a/tools/binman/test/069_fill.dts b/tools/binman/test/069_fill.dts
new file mode 100644
index 0000000..e372ea3
--- /dev/null
+++ b/tools/binman/test/069_fill.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+		fill {
+			size = <8>;
+			fill-byte = [ff];
+		};
+	};
+};
diff --git a/tools/binman/test/070_fill_no_size.dts b/tools/binman/test/070_fill_no_size.dts
new file mode 100644
index 0000000..7b1fcf1
--- /dev/null
+++ b/tools/binman/test/070_fill_no_size.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+		fill {
+			fill-byte = [ff];
+		};
+	};
+};
diff --git a/tools/binman/test/071_gbb.dts b/tools/binman/test/071_gbb.dts
new file mode 100644
index 0000000..5517563
--- /dev/null
+++ b/tools/binman/test/071_gbb.dts
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		gbb {
+			size = <0x2180>;
+			flags {
+				dev-screen-short-delay;
+				load-option-roms;
+				enable-alternate-os;
+				force-dev-switch-on;
+				force-dev-boot-usb;
+				disable-fw-rollback-check;
+				enter-triggers-tonorm;
+				force-dev-boot-legacy;
+				faft-key-override;
+				disable-ec-software-sync;
+				default-dev-boot-legacy;
+				disable-pd-software-sync;
+				disable-lid-shutdown;
+				force-dev-boot-fastboot-full-cap;
+				enable-serial;
+				disable-dwmp;
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/072_gbb_too_small.dts b/tools/binman/test/072_gbb_too_small.dts
new file mode 100644
index 0000000..c088f36
--- /dev/null
+++ b/tools/binman/test/072_gbb_too_small.dts
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		gbb {
+			size = <0x200>;
+		};
+	};
+};
diff --git a/tools/binman/test/073_gbb_no_size.dts b/tools/binman/test/073_gbb_no_size.dts
new file mode 100644
index 0000000..83be403
--- /dev/null
+++ b/tools/binman/test/073_gbb_no_size.dts
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		gbb {
+		};
+	};
+};
diff --git a/tools/binman/test/074_vblock.dts b/tools/binman/test/074_vblock.dts
new file mode 100644
index 0000000..f0c21bf
--- /dev/null
+++ b/tools/binman/test/074_vblock.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u_boot: u-boot {
+		};
+
+		vblock {
+			content = <&u_boot &dtb>;
+			keyblock = "firmware.keyblock";
+			signprivate = "firmware_data_key.vbprivk";
+			version = <1>;
+			kernelkey = "kernel_subkey.vbpubk";
+			preamble-flags = <1>;
+		};
+
+		/*
+		 * Put this after the vblock so that its contents are not
+		 * available when the vblock first tries to obtain its contents
+		 */
+		dtb: u-boot-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/075_vblock_no_content.dts b/tools/binman/test/075_vblock_no_content.dts
new file mode 100644
index 0000000..676d947
--- /dev/null
+++ b/tools/binman/test/075_vblock_no_content.dts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u_boot: u-boot {
+		};
+
+		vblock {
+			keyblock = "firmware.keyblock";
+			signprivate = "firmware_data_key.vbprivk";
+			version = <1>;
+			kernelkey = "kernel_subkey.vbpubk";
+			preamble-flags = <1>;
+		};
+
+		dtb: u-boot-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/076_vblock_bad_phandle.dts b/tools/binman/test/076_vblock_bad_phandle.dts
new file mode 100644
index 0000000..ffbd0c3
--- /dev/null
+++ b/tools/binman/test/076_vblock_bad_phandle.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u_boot: u-boot {
+		};
+
+		vblock {
+			content = <1000>;
+			keyblock = "firmware.keyblock";
+			signprivate = "firmware_data_key.vbprivk";
+			version = <1>;
+			kernelkey = "kernel_subkey.vbpubk";
+			preamble-flags = <1>;
+		};
+
+		dtb: u-boot-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/077_vblock_bad_entry.dts b/tools/binman/test/077_vblock_bad_entry.dts
new file mode 100644
index 0000000..764c42a
--- /dev/null
+++ b/tools/binman/test/077_vblock_bad_entry.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u_boot: u-boot {
+		};
+
+		vblock {
+			content = <&u_boot &other>;
+			keyblock = "firmware.keyblock";
+			signprivate = "firmware_data_key.vbprivk";
+			version = <1>;
+			kernelkey = "kernel_subkey.vbpubk";
+			preamble-flags = <1>;
+		};
+
+		dtb: u-boot-dtb {
+		};
+	};
+
+	other: other {
+	};
+};
diff --git a/tools/binman/test/078_u_boot_tpl.dts b/tools/binman/test/078_u_boot_tpl.dts
new file mode 100644
index 0000000..6c60b4c
--- /dev/null
+++ b/tools/binman/test/078_u_boot_tpl.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		u-boot-tpl {
+		};
+		u-boot-tpl-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/079_uses_pos.dts b/tools/binman/test/079_uses_pos.dts
new file mode 100644
index 0000000..7638b9b
--- /dev/null
+++ b/tools/binman/test/079_uses_pos.dts
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		u-boot {
+			pos = <10>;
+		};
+	};
+};
diff --git a/tools/binman/test/080_fill_empty.dts b/tools/binman/test/080_fill_empty.dts
new file mode 100644
index 0000000..2b78d3a
--- /dev/null
+++ b/tools/binman/test/080_fill_empty.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+		fill {
+			size = <0>;
+			fill-byte = [ff];
+		};
+	};
+};
diff --git a/tools/binman/test/081_x86-start16-tpl.dts b/tools/binman/test/081_x86-start16-tpl.dts
new file mode 100644
index 0000000..68e6bbd
--- /dev/null
+++ b/tools/binman/test/081_x86-start16-tpl.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		x86-start16-tpl {
+		};
+	};
+};
diff --git a/tools/binman/test/082_fdt_update_all.dts b/tools/binman/test/082_fdt_update_all.dts
new file mode 100644
index 0000000..284975c
--- /dev/null
+++ b/tools/binman/test/082_fdt_update_all.dts
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		section {
+			u-boot-dtb {
+			};
+		};
+		u-boot-spl-dtb {
+		};
+		u-boot-tpl-dtb {
+		};
+	};
+};
diff --git a/tools/binman/test/083_compress.dts b/tools/binman/test/083_compress.dts
new file mode 100644
index 0000000..07813bd
--- /dev/null
+++ b/tools/binman/test/083_compress.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		blob {
+			filename = "compress";
+			compress = "lz4";
+		};
+	};
+};
diff --git a/tools/binman/test/084_files.dts b/tools/binman/test/084_files.dts
new file mode 100644
index 0000000..83ddb78
--- /dev/null
+++ b/tools/binman/test/084_files.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		files {
+			pattern = "files/*.dat";
+			compress = "none";
+		};
+	};
+};
diff --git a/tools/binman/test/085_files_compress.dts b/tools/binman/test/085_files_compress.dts
new file mode 100644
index 0000000..847b398
--- /dev/null
+++ b/tools/binman/test/085_files_compress.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		files {
+			pattern = "files/*.dat";
+			compress = "lz4";
+		};
+	};
+};
diff --git a/tools/binman/test/086_files_none.dts b/tools/binman/test/086_files_none.dts
new file mode 100644
index 0000000..34bd92f
--- /dev/null
+++ b/tools/binman/test/086_files_none.dts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		files {
+			pattern = "files/*.none";
+			compress = "none";
+			require-matches;
+		};
+	};
+};
diff --git a/tools/binman/test/087_files_no_pattern.dts b/tools/binman/test/087_files_no_pattern.dts
new file mode 100644
index 0000000..0cb5b46
--- /dev/null
+++ b/tools/binman/test/087_files_no_pattern.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		files {
+			compress = "none";
+			require-matches;
+		};
+	};
+};
diff --git a/tools/binman/test/088_expand_size.dts b/tools/binman/test/088_expand_size.dts
new file mode 100644
index 0000000..c8a0130
--- /dev/null
+++ b/tools/binman/test/088_expand_size.dts
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		size = <40>;
+		fill {
+			expand-size;
+			fill-byte = [61];
+			size = <0>;
+		};
+		u-boot {
+			offset = <8>;
+		};
+		section {
+			expand-size;
+			pad-byte = <0x62>;
+			intel-mrc {
+			};
+		};
+		u-boot2 {
+			type = "u-boot";
+			offset = <16>;
+		};
+		section2 {
+			type = "section";
+			fill {
+				expand-size;
+				fill-byte = [63];
+				size = <0>;
+			};
+			u-boot {
+				offset = <8>;
+			};
+		};
+		fill2 {
+			type = "fill";
+			expand-size;
+			fill-byte = [64];
+			size = <0>;
+		};
+	};
+};
diff --git a/tools/binman/test/089_expand_size_bad.dts b/tools/binman/test/089_expand_size_bad.dts
new file mode 100644
index 0000000..edc0e5c
--- /dev/null
+++ b/tools/binman/test/089_expand_size_bad.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		_testing {
+			expand-size;
+			return-contents-once;
+		};
+		u-boot {
+			offset = <8>;
+		};
+	};
+};
diff --git a/tools/binman/test/090_hash.dts b/tools/binman/test/090_hash.dts
new file mode 100644
index 0000000..2003045
--- /dev/null
+++ b/tools/binman/test/090_hash.dts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		u-boot {
+			hash {
+				algo = "sha256";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/091_hash_no_algo.dts b/tools/binman/test/091_hash_no_algo.dts
new file mode 100644
index 0000000..b64df20
--- /dev/null
+++ b/tools/binman/test/091_hash_no_algo.dts
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		u-boot {
+			hash {
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/092_hash_bad_algo.dts b/tools/binman/test/092_hash_bad_algo.dts
new file mode 100644
index 0000000..d240200
--- /dev/null
+++ b/tools/binman/test/092_hash_bad_algo.dts
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		u-boot {
+			hash {
+				algo = "invalid";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/093_x86_tpl_ucode.dts b/tools/binman/test/093_x86_tpl_ucode.dts
new file mode 100644
index 0000000..d7ed9fc
--- /dev/null
+++ b/tools/binman/test/093_x86_tpl_ucode.dts
@@ -0,0 +1,29 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		sort-by-offset;
+		end-at-4gb;
+		size = <0x200>;
+		u-boot-tpl-with-ucode-ptr {
+		};
+
+		u-boot-tpl-dtb-with-ucode {
+		};
+
+		u-boot-ucode {
+		};
+	};
+
+	microcode {
+		update@0 {
+			data = <0x12345678 0x12345679>;
+		};
+		update@1 {
+			data = <0xabcd0000 0x78235609>;
+		};
+	};
+};
diff --git a/tools/binman/test/094_fmap_x86.dts b/tools/binman/test/094_fmap_x86.dts
new file mode 100644
index 0000000..613c5da
--- /dev/null
+++ b/tools/binman/test/094_fmap_x86.dts
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		end-at-4gb;
+		size = <0x100>;
+		pad-byte = <0x61>;
+		u-boot {
+		};
+		intel-mrc {
+		};
+		fmap {
+			offset = <0xffffff20>;
+		};
+	};
+};
diff --git a/tools/binman/test/095_fmap_x86_section.dts b/tools/binman/test/095_fmap_x86_section.dts
new file mode 100644
index 0000000..4cfce45
--- /dev/null
+++ b/tools/binman/test/095_fmap_x86_section.dts
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		end-at-4gb;
+		size = <0x100>;
+		u-boot {
+		};
+		section {
+			pad-byte = <0x62>;
+			intel-mrc {
+			};
+			fmap {
+				offset = <0x20>;
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/096_elf.dts b/tools/binman/test/096_elf.dts
new file mode 100644
index 0000000..df3440c
--- /dev/null
+++ b/tools/binman/test/096_elf.dts
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot-elf {
+		};
+		u-boot-spl-elf {
+		};
+	};
+};
diff --git a/tools/binman/test/097_elf_strip.dts b/tools/binman/test/097_elf_strip.dts
new file mode 100644
index 0000000..6f3c66f
--- /dev/null
+++ b/tools/binman/test/097_elf_strip.dts
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		u-boot-elf {
+			strip;
+		};
+		u-boot-spl-elf {
+		};
+	};
+};
diff --git a/tools/binman/test/099_hash_section.dts b/tools/binman/test/099_hash_section.dts
new file mode 100644
index 0000000..dcd8683
--- /dev/null
+++ b/tools/binman/test/099_hash_section.dts
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0+
+/dts-v1/;
+
+/ {
+	binman {
+		section {
+			u-boot {
+			};
+			fill {
+				size = <0x10>;
+				fill-byte = [61];
+			};
+			hash {
+				algo = "sha256";
+			};
+		};
+	};
+};
diff --git a/tools/binman/test/100_intel_refcode.dts b/tools/binman/test/100_intel_refcode.dts
new file mode 100644
index 0000000..0a1a027
--- /dev/null
+++ b/tools/binman/test/100_intel_refcode.dts
@@ -0,0 +1,14 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <16>;
+
+		intel-refcode {
+			filename = "refcode.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/80_4gb_and_skip_at_start_together.dts b/tools/binman/test/80_4gb_and_skip_at_start_together.dts
new file mode 100644
index 0000000..90c467d
--- /dev/null
+++ b/tools/binman/test/80_4gb_and_skip_at_start_together.dts
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		size = <32>;
+		sort-by-offset;
+		end-at-4gb;
+		skip-at-start = <0xffffffe0>;
+		u-boot {
+			offset = <0xffffffe0>;
+		};
+	};
+};
diff --git a/tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts b/tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts
new file mode 100644
index 0000000..8f4b16c
--- /dev/null
+++ b/tools/binman/test/81_powerpc_mpc85xx_bootpg_resetvec.dts
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 NXP
+ */
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		powerpc-mpc85xx-bootpg-resetvec {
+		};
+	};
+};
diff --git a/tools/binman/test/Makefile b/tools/binman/test/Makefile
new file mode 100644
index 0000000..e58fc80
--- /dev/null
+++ b/tools/binman/test/Makefile
@@ -0,0 +1,55 @@
+#
+# Builds test programs
+#
+# Copyright (C) 2017 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+CFLAGS := -march=i386 -m32 -nostdlib -I ../../../include
+
+LDS_UCODE := -T u_boot_ucode_ptr.lds
+LDS_BINMAN := -T u_boot_binman_syms.lds
+LDS_BINMAN_BAD := -T u_boot_binman_syms_bad.lds
+
+TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \
+	u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \
+	u_boot_binman_syms_size
+
+all: $(TARGETS)
+
+u_boot_no_ucode_ptr: CFLAGS += $(LDS_UCODE)
+u_boot_no_ucode_ptr: u_boot_no_ucode_ptr.c
+
+u_boot_ucode_ptr: CFLAGS += $(LDS_UCODE)
+u_boot_ucode_ptr: u_boot_ucode_ptr.c
+
+bss_data: CFLAGS += bss_data.lds
+bss_data: bss_data.c
+
+u_boot_binman_syms.bin: u_boot_binman_syms
+	objcopy -O binary $< -R .note.gnu.build-id $@
+
+u_boot_binman_syms: CFLAGS += $(LDS_BINMAN)
+u_boot_binman_syms: u_boot_binman_syms.c
+
+u_boot_binman_syms_bad: CFLAGS += $(LDS_BINMAN_BAD)
+u_boot_binman_syms_bad: u_boot_binman_syms_bad.c
+
+u_boot_binman_syms_size: CFLAGS += $(LDS_BINMAN)
+u_boot_binman_syms_size: u_boot_binman_syms_size.c
+
+clean:
+	rm -f $(TARGETS)
+
+help:
+	@echo "Makefile for binman test programs"
+	@echo
+	@echo "Intended for use on x86 hosts"
+	@echo
+	@echo "Targets:"
+	@echo
+	@echo -e "\thelp	- Print help (this is it!)"
+	@echo -e "\tall	- Builds test programs (default targget)"
+	@echo -e "\tclean	- Delete output files"
diff --git a/tools/binman/test/bss_data b/tools/binman/test/bss_data
new file mode 100755
index 0000000..afa2828
--- /dev/null
+++ b/tools/binman/test/bss_data
Binary files differ
diff --git a/tools/binman/test/bss_data.c b/tools/binman/test/bss_data.c
new file mode 100644
index 0000000..79537c3
--- /dev/null
+++ b/tools/binman/test/bss_data.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * Simple program to create a _dt_ucode_base_size symbol which can be read
+ * by binutils. This is used by binman tests.
+ */
+
+int bss_data[10];
+int __bss_size = sizeof(bss_data);
+
+int main()
+{
+	bss_data[2] = 2;
+
+	return 0;
+}
diff --git a/tools/binman/test/bss_data.lds b/tools/binman/test/bss_data.lds
new file mode 100644
index 0000000..306dab5
--- /dev/null
+++ b/tools/binman/test/bss_data.lds
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+	. = 0xfffffdf0;
+	_start = .;
+	__bss_size = 10;
+}
diff --git a/tools/binman/test/descriptor.bin b/tools/binman/test/descriptor.bin
new file mode 100644
index 0000000..3d54943
--- /dev/null
+++ b/tools/binman/test/descriptor.bin
Binary files differ
diff --git a/tools/binman/test/files/1.dat b/tools/binman/test/files/1.dat
new file mode 100644
index 0000000..a952470
--- /dev/null
+++ b/tools/binman/test/files/1.dat
@@ -0,0 +1 @@
+sorry I'm late
diff --git a/tools/binman/test/files/2.dat b/tools/binman/test/files/2.dat
new file mode 100644
index 0000000..687ea52
--- /dev/null
+++ b/tools/binman/test/files/2.dat
@@ -0,0 +1 @@
+Oh, don't bother apologising, I'm sorry you're alive
diff --git a/tools/binman/test/files/ignored_dir.dat/ignore b/tools/binman/test/files/ignored_dir.dat/ignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/binman/test/files/ignored_dir.dat/ignore
diff --git a/tools/binman/test/files/not-this-one b/tools/binman/test/files/not-this-one
new file mode 100644
index 0000000..e71c225
--- /dev/null
+++ b/tools/binman/test/files/not-this-one
@@ -0,0 +1 @@
+this does not have a .dat extenion
diff --git a/tools/binman/test/u_boot_binman_syms b/tools/binman/test/u_boot_binman_syms
new file mode 100755
index 0000000..126a1a6
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms
Binary files differ
diff --git a/tools/binman/test/u_boot_binman_syms.c b/tools/binman/test/u_boot_binman_syms.c
new file mode 100644
index 0000000..4898f98
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 Google, Inc
+ *
+ * Simple program to create some binman symbols. This is used by binman tests.
+ */
+
+#define CONFIG_BINMAN
+#include <binman_sym.h>
+
+binman_sym_declare(unsigned long, u_boot_spl, offset);
+binman_sym_declare(unsigned long long, u_boot_spl2, offset);
+binman_sym_declare(unsigned long, u_boot_any, image_pos);
diff --git a/tools/binman/test/u_boot_binman_syms.lds b/tools/binman/test/u_boot_binman_syms.lds
new file mode 100644
index 0000000..29cf9d0
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms.lds
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+	. = 0x00000000;
+	_start = .;
+
+	. = ALIGN(4);
+	.text :
+	{
+		__image_copy_start = .;
+		*(.text*)
+	}
+
+	. = ALIGN(4);
+	.binman_sym_table : {
+		__binman_sym_start = .;
+		KEEP(*(SORT(.binman_sym*)));
+		__binman_sym_end = .;
+	}
+
+}
diff --git a/tools/binman/test/u_boot_binman_syms_bad b/tools/binman/test/u_boot_binman_syms_bad
new file mode 100755
index 0000000..8da3d9d
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms_bad
Binary files differ
diff --git a/tools/binman/test/u_boot_binman_syms_bad.c b/tools/binman/test/u_boot_binman_syms_bad.c
new file mode 120000
index 0000000..939b2e9
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms_bad.c
@@ -0,0 +1 @@
+u_boot_binman_syms.c
\ No newline at end of file
diff --git a/tools/binman/test/u_boot_binman_syms_bad.lds b/tools/binman/test/u_boot_binman_syms_bad.lds
new file mode 100644
index 0000000..849d158
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms_bad.lds
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+	. = 0x00000000;
+	_start = .;
+
+	. = ALIGN(4);
+	.text :
+	{
+		*(.text*)
+	}
+
+	. = ALIGN(4);
+	.binman_sym_table : {
+		__binman_sym_start = .;
+		KEEP(*(SORT(.binman_sym*)));
+		__binman_sym_end = .;
+	}
+
+}
diff --git a/tools/binman/test/u_boot_binman_syms_size b/tools/binman/test/u_boot_binman_syms_size
new file mode 100755
index 0000000..d691e89
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms_size
Binary files differ
diff --git a/tools/binman/test/u_boot_binman_syms_size.c b/tools/binman/test/u_boot_binman_syms_size.c
new file mode 100644
index 0000000..7224bc1
--- /dev/null
+++ b/tools/binman/test/u_boot_binman_syms_size.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2017 Google, Inc
+ *
+ * Simple program to create some binman symbols. This is used by binman tests.
+ */
+
+#define CONFIG_BINMAN
+#include <binman_sym.h>
+
+binman_sym_declare(char, u_boot_spl, pos);
diff --git a/tools/binman/test/u_boot_no_ucode_ptr b/tools/binman/test/u_boot_no_ucode_ptr
new file mode 100755
index 0000000..f72462f
--- /dev/null
+++ b/tools/binman/test/u_boot_no_ucode_ptr
Binary files 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
index 0000000..24cdb90
--- /dev/null
+++ b/tools/binman/test/u_boot_no_ucode_ptr.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * 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.
+ */
+
+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
index 0000000..dbfb184
--- /dev/null
+++ b/tools/binman/test/u_boot_ucode_ptr
Binary files 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
index 0000000..243c8e9
--- /dev/null
+++ b/tools/binman/test/u_boot_ucode_ptr.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Google, Inc
+ *
+ * Simple program to create a _dt_ucode_base_size symbol which can be read
+ * by binutils. This is used by binman tests.
+ */
+
+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
index 0000000..0cf9b76
--- /dev/null
+++ b/tools/binman/test/u_boot_ucode_ptr.lds
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2016 Google, Inc
+ */
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+	. = 0xfffffdf0;
+	_start = .;
+	.ucode : {
+		*(.ucode)
+	}
+}