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)
+ }
+}