diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/tools/binman | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/tools/binman')
312 files changed, 21112 insertions, 0 deletions
diff --git a/roms/u-boot/tools/binman/.gitignore b/roms/u-boot/tools/binman/.gitignore new file mode 100644 index 000000000..0d20b6487 --- /dev/null +++ b/roms/u-boot/tools/binman/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/roms/u-boot/tools/binman/README.rst b/roms/u-boot/tools/binman/README.rst new file mode 120000 index 000000000..b734f544b --- /dev/null +++ b/roms/u-boot/tools/binman/README.rst @@ -0,0 +1 @@ +binman.rst
\ No newline at end of file diff --git a/roms/u-boot/tools/binman/binman b/roms/u-boot/tools/binman/binman new file mode 120000 index 000000000..11a5d8e18 --- /dev/null +++ b/roms/u-boot/tools/binman/binman @@ -0,0 +1 @@ +main.py
\ No newline at end of file diff --git a/roms/u-boot/tools/binman/binman.rst b/roms/u-boot/tools/binman/binman.rst new file mode 100644 index 000000000..bc635aa00 --- /dev/null +++ b/roms/u-boot/tools/binman/binman.rst @@ -0,0 +1,1192 @@ +.. 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. + +Building firmware should be separate from packaging it. Many of the complexities +of modern firmware build systems come from trying to do both at once. With +binman, you build all the pieces that are needed, using whatever assortment of +projects and build systems are needed, then use binman to stitch everything +together. + + +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. + +Binman provides a mechanism for building images, from simple SPL + U-Boot +combinations, to more complex arrangements with many parts. It also allows +users to inspect images, extract and replace binaries within them, repacking if +needed. + + +Features +-------- + +Apart from basic padding, alignment and positioning features, Binman supports +hierarchical images, compression, hashing and dealing with the binary blobs +which are a sad trend in open-source firmware at present. + +Executable binaries can access the location of other binaries in an image by +using special linker symbols (zero-overhead but somewhat limited) or by reading +the devicetree description of the image. + +Binman is designed primarily for use with U-Boot and associated binaries such +as ARM Trusted Firmware, but it is suitable for use with other projects, such +as Zephyr. Binman also provides facilities useful in Chromium OS, such as CBFS, +vblocks and and the like. + +Binman provides a way to process binaries before they are included, by adding a +Python plug-in. + +Binman is intended for use with U-Boot but is designed to be general enough +to be useful in other image-packaging situations. + + +Motivation +---------- + +As mentioned above, 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 accommodated + - 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). + + +Using 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: + + #. It uses a custom mksunxiboot tool to build an SPL image called + sunxi-spl.bin. This should probably move into mkimage. + + #. 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. + + #. 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 +-------------- + +First install prerequisites, e.g:: + + sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ + liblz4-tool + +Type:: + + binman build -b <board_name> + +to build an image for a board. The board name is the same name used when +configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). +Binman assumes that the input files for the build are in ../b/<board_name>. + +Or you can specify this explicitly:: + + binman build -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. You should be +able to enable CONFIG_BINMAN to enable this rule. + +The output file is typically named image.bin and is located in the output +directory. If input files are needed to you add these to INPUTS-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. + + +Using binman with OF_BOARD or OF_PRIOR_STAGE +-------------------------------------------- + +Normally binman is used with a board configured with OF_SEPARATE or OF_EMBED. +This is a typical scenario where a device tree source that contains the binman +node is provided in the arch/<arch>/dts directory for a specific board. + +However for a board configured with OF_BOARD or OF_PRIOR_STAGE, no device tree +blob is provided in the U-Boot build phase hence the binman node information +is not available. In order to support such use case, a new Kconfig option +BINMAN_STANDALONE_FDT is introduced, to tell the build system that a standalone +device tree blob containing binman node is explicitly required. + +Note there is a Kconfig option BINMAN_FDT which enables U-Boot run time to +access information about binman entries, stored in the device tree in a binman +node. Generally speaking, this option makes sense for OF_SEPARATE or OF_EMBED. +For the other OF_CONTROL methods, it's quite possible binman node is not +available as binman is invoked during the build phase, thus this option is not +turned on by default for these OF_CONTROL methods. + +See qemu-riscv64_spl_defconfig for an example of how binman is used with +OF_PRIOR_STAGE to generate u-boot.itb image. + + +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, image_pos); + +declares a ulong value which will be assigned to the image-pos 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, image_pos); + +Thus u_boot_offset will be set to the image-pos 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 and TPL. In principle it is +possible to fill in such symbols in U-Boot proper, as well, but a future C +library is planned for this instead, to read from the device tree. + +As well as image-pos, it is possible to read the size of an entry and its +offset (which is the start position of the entry within its parent). + +A small technical note: Binman automatically adds the base address of the image +(i.e. __image_copy_start) to the value of the image-pos symbol, so that when the +image is loaded to its linked address, the value will be correct and actually +point into the image. + +For example, say SPL is at the start of the image and linked to start at address +80108000. If U-Boot's image-pos is 0x8000 then binman will write an image-pos +for U-Boot of 80110000 into the SPL binary, since it assumes the image is loaded +to 80108000, with SPL at 80108000 and U-Boot at 80110000. + +For x86 devices (with the end-at-4gb property) this base address is not added +since it is assumed that images are XIP and the offsets already include the +address. + + +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. + +Alternatively, an FDT map entry can be used to add a special FDT containing +just the information about the image. This is preceded by a magic string so can +be located anywhere in the image. An image header (typically at the start or end +of the image) can be used to point to the FDT map. See fdtmap and image-header +entries for more information. + + +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. + + +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 containing + section or image. For example 'align = <16>' means that the entry will + start on a 16-byte boundary. This may mean that padding is added before + the entry. The padding is part of the containing section but is not + included in the entry, meaning that an empty space may be created before + the entry starts. Alignment should 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 used + to offset the entry contents a little. While this does not affect the + contents of the entry within binman itself (the padding is performed + only when its parent section is assembled), the end result will be that + the entry starts with the padding bytes, so may grow. 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. While this does not affect the contents of + the entry within binman itself (the padding is performed only when its + parent section is assembled), the end result will be that the entry ends + with the padding bytes, so may grow. 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. + While this does not affect the contents of the entry within binman + itself (the padding is performed only when its parent section is + assembled), the end result is that the entry ends with the padding + bytes, so may grow. If 'align-size' is not provided, no alignment is + performed. + +align-end: + This sets the alignment of the end of an entry with respect to the + containing section. 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. While this does not + affect the contents of the entry within binman itself (the padding is + performed only when its parent section is assembled), the end result + is that the entry ends with the padding bytes, so may grow. + 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. + +compress: + Sets the compression algortihm to use (for blobs only). See the entry + documentation for details. + +missing-msg: + Sets the tag of the message to show if this entry is missing. This is + used for external blobs. When they are missing it is helpful to show + information about what needs to be fixed. See missing-blob-help for the + message for each tag. + +no-expanded: + By default binman substitutes entries with expanded versions if available, + so that a `u-boot` entry type turns into `u-boot-expanded`, for example. The + `--no-expanded` command-line option disables this globally. The + `no-expanded` property disables this just for a single entry. Put the + `no-expanded` boolean property in the node to select this behaviour. + +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. + +offset: + This is similar to 'offset' in entries, setting the offset of a section + within the image or section containing it. The first byte of the section + 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-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. + +align-default: + Specifies the default alignment for entries in this section, if they do + not specify an alignment. Note that this only applies to top-level entries + in the section (direct subentries), not any subentries of those entries. + This means that each section must specify its own default alignment, if + required. + +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. + + +Image Properties +---------------- + +Image nodes act like sections but also have a few extra properties: + +filename: + Output filename for the image. This defaults to image.bin (or in the + case of multiple images <nodename>.bin where <nodename> is the name of + the image node. + +allow-repack: + Create an image that can be repacked. With this option it is possible + to change anything in the image after it is created, including updating + the position and size of image components. By default this is not + permitted since it is not possibly to know whether this might violate a + constraint in the image description. For example, if a section has to + increase in size to hold a larger binary, that might cause the section + to fall out of its allow region (e.g. read-only portion of flash). + + Adding this property causes the original offset and size values in the + image description to be stored in the FDT and fdtmap. + + +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. + + +Expanded entries +---------------- + +Binman automatically replaces 'u-boot' with an expanded version of that, i.e. +'u-boot-expanded'. This means that when you write:: + + u-boot { + }; + +you actually get:: + + u-boot { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + u-boot { + type = "section"; + + u-boot-nodtb { + }; + + u-boot-dtb { + }; + }; + +U-Boot's various phase binaries actually comprise two or three pieces. +For example, u-boot.bin has the executable followed by a devicetree. + +With binman we want to be able to update that devicetree with full image +information so that it is accessible to the executable. This is tricky +if it is not clear where the devicetree starts. + +The above feature ensures that the devicetree is clearly separated from the +U-Boot executable and can be updated separately by binman as needed. It can be +disabled with the --no-expanded flag if required. + +The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion +includes the BSS padding, so for example:: + + spl { + type = "u-boot-spl" + }; + +you actually get:: + + spl { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + spl { + type = "section"; + + u-boot-spl-nodtb { + }; + + u-boot-spl-bss-pad { + }; + + u-boot-spl-dtb { + }; + }; + +Of course we should not expand SPL if it has no devicetree. Also if the BSS +padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), +the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned +entry type is controlled by the UseExpanded() method. In the SPL case it checks +the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. + +For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All +entry args are provided by the U-Boot Makefile. + + +Compression +----------- + +Binman support compression for 'blob' entries (those of type 'blob' and +derivatives). To enable this for an entry, add a 'compress' property:: + + blob { + filename = "datafile"; + compress = "lz4"; + }; + +The entry will then contain the compressed data, using the 'lz4' compression +algorithm. Currently this is the only one that is supported. The uncompressed +size is written to the node in an 'uncomp-size' property, if -u is used. + +Compression is also supported for sections. In that case the entire section is +compressed in one block, including all its contents. This means that accessing +an entry from the section required decompressing the entire section. Also, the +size of a section indicates the space that it consumes in its parent section +(and typically the image). With compression, the section may contain more data, +and the uncomp-size property indicates that, as above. The contents of the +section is compressed first, before any padding is added. This ensures that the +padding itself is not compressed, which would be a waste of time. + + +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) + + +Entry Documentation +=================== + +For details on the various entry types supported by binman and how to use them, +see entries.rst which is generated from the source code using: + + binman entry-docs >tools/binman/entries.rst + +.. toctree:: + :maxdepth: 2 + + entries + + +Managing images +=============== + +Listing images +-------------- + +It is possible to list the entries in an existing firmware image created by +binman, provided that there is an 'fdtmap' entry in the image. For example:: + + $ binman ls -i image.bin + Name Image-pos Size Entry-type Offset Uncomp-size + ---------------------------------------------------------------------- + main-section c00 section 0 + u-boot 0 4 u-boot 0 + section 5fc section 4 + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + u-boot-dtb 500 1ff u-boot-dtb 400 3b5 + fdtmap 6fc 381 fdtmap 6fc + image-header bf8 8 image-header bf8 + +This shows the hierarchy of the image, the position, size and type of each +entry, the offset of each entry within its parent and the uncompressed size if +the entry is compressed. + +It is also possible to list just some files in an image, e.g.:: + + $ binman ls -i image.bin section/cbfs + Name Image-pos Size Entry-type Offset Uncomp-size + -------------------------------------------------------------------- + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + +or with wildcards:: + + $ binman ls -i image.bin "*cb*" "*head*" + Name Image-pos Size Entry-type Offset Uncomp-size + ---------------------------------------------------------------------- + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + image-header bf8 8 image-header bf8 + + +Extracting files from images +---------------------------- + +You can extract files from an existing firmware image created by binman, +provided that there is an 'fdtmap' entry in the image. For example:: + + $ binman extract -i image.bin section/cbfs/u-boot + +which will write the uncompressed contents of that entry to the file 'u-boot' in +the current directory. You can also extract to a particular file, in this case +u-boot.bin:: + + $ binman extract -i image.bin section/cbfs/u-boot -f u-boot.bin + +It is possible to extract all files into a destination directory, which will +put files in subdirectories matching the entry hierarchy:: + + $ binman extract -i image.bin -O outdir + +or just a selection:: + + $ binman extract -i image.bin "*u-boot*" -O outdir + + +Replacing files in an image +--------------------------- + +You can replace files in an existing firmware image created by binman, provided +that there is an 'fdtmap' entry in the image. For example: + + $ binman replace -i image.bin section/cbfs/u-boot + +which will write the contents of the file 'u-boot' from the current directory +to the that entry, compressing if necessary. If the entry size changes, you must +add the 'allow-repack' property to the original image before generating it (see +above), otherwise you will get an error. + +You can also use a particular file, in this case u-boot.bin:: + + $ binman replace -i image.bin section/cbfs/u-boot -f u-boot.bin + +It is possible to replace all files from a source directory which uses the same +hierarchy as the entries:: + + $ binman replace -i image.bin -I indir + +Files that are missing will generate a warning. + +You can also replace just a selection of entries:: + + $ binman replace -i image.bin "*u-boot*" -I indir + + +Logging +------- + +Binman normally operates silently unless there is an error, in which case it +just displays the error. The -D/--debug option can be used to create a full +backtrace when errors occur. You can use BINMAN_DEBUG=1 when building to select +this. + +Internally binman logs some output while it is running. This can be displayed +by increasing the -v/--verbosity from the default of 1: + + 0: silent + 1: warnings only + 2: notices (important messages) + 3: info about major operations + 4: detailed information about each operation + 5: debug (all output) + +You can use BINMAN_VERBOSE=5 (for example) when building to select this. + + +Technical details +================= + +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. + +Note: for sections, this also checks that the entries do not overlap, nor extend +outside the section. If the section does not have a defined size, the size is +set large enough to hold all the entries. + +6. SetImagePos() - sets the image position of every entry. This is the absolute +position 'image-pos', as opposed to 'offset' which is relative to the containing +section. This must be done after all offsets are known, which is why it is quite +late in the ordering. + +7. SetCalculatedProperties() - update any calculated properties in the device +tree. This sets the correct 'offset' and 'size' vaues, for example. + +8. 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 unless absolutely +necessary, since it requires a repack (going back to PackEntries()). + +9. ResetForPack() - if the ProcessEntryContents() step failed, in that an entry +has changed its size, then there is no alternative but to go back to step 5 and +try again, repacking the entries with the updated size. ResetForPack() removes +the fixed offset/size values added by binman, so that the packing can start from +scratch. + +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 + +12. WriteMap() - writes a text file containing a map of the image. This is the +final step. + + +External tools +-------------- + +Binman can make use of external command-line tools to handle processing of +entry contents or to generate entry contents. These tools are executed using +the 'tools' module's Run() method. The tools generally must exist on the PATH, +but the --toolpath option can be used to specify additional search paths to +use. This option can be specified multiple times to add more than one path. + +For some compile tools binman will use the versions specified by commonly-used +environment variables like CC and HOSTCC for the C compiler, based on whether +the tool's output will be used for the target or for the host machine. If those +aren't given, it will also try to derive target-specific versions from the +CROSS_COMPILE environment variable during a cross-compilation. + + +Code coverage +------------- + +Binman is a critical tool and is designed to be very testable. Entry +implementations target 100% test coverage. Run 'binman test -T' to check this. + +To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):: + + $ sudo apt-get install python-coverage python3-coverage python-pytest + + +Concurrent tests +---------------- + +Binman tries to run tests concurrently. This means that the tests make use of +all available CPUs to run. + + To enable this:: + + $ sudo apt-get install python-subunit python3-subunit + +Use '-P 1' to disable this. It is automatically disabled when code coverage is +being used (-T) since they are incompatible. + + +Debugging tests +--------------- + +Sometimes when debugging tests it is useful to keep the input and output +directories so they can be examined later. Use -X or --test-preserve-dirs for +this. + + +Running tests on non-x86 architectures +-------------------------------------- + +Binman's tests have been written under the assumption that they'll be run on a +x86-like host and there hasn't been an attempt to make them portable yet. +However, it's possible to run the tests by cross-compiling to x86. + +To install an x86 cross-compiler on Debian-type distributions (e.g. Ubuntu):: + + $ sudo apt-get install gcc-x86-64-linux-gnu + +Then, you can run the tests under cross-compilation:: + + $ CROSS_COMPILE=x86_64-linux-gnu- binman test -T + +You can also use gcc-i686-linux-gnu similar to the above. + + +Writing new entries and debugging +--------------------------------- + +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 qemu-x86_defconfig + make BINMAN_DEBUG=1 + +To enable verbose logging from binman, base BINMAN_VERBOSE to your build, which +adds a -v<level> option to the call to binman:: + + make qemu-x86_defconfig + make BINMAN_VERBOSE=5 + + +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). For now, limited info is + available via linker symbols +- Allow easy building of images by specifying just the board name +- Support building an image for a board (-b) more completely, with a + configurable build directory +- Detect invalid properties in nodes +- Sort the fdtmap by offset +- Output temporary files to a different directory + +-- +Simon Glass <sjg@chromium.org> +7/7/2016 diff --git a/roms/u-boot/tools/binman/cbfs_util.py b/roms/u-boot/tools/binman/cbfs_util.py new file mode 100644 index 000000000..39973371b --- /dev/null +++ b/roms/u-boot/tools/binman/cbfs_util.py @@ -0,0 +1,887 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> + +"""Support for coreboot's CBFS format + +CBFS supports a header followed by a number of files, generally targeted at SPI +flash. + +The format is somewhat defined by documentation in the coreboot tree although +it is necessary to rely on the C structures and source code (mostly cbfstool) +to fully understand it. + +Currently supported: raw and stage types with compression, padding empty areas + with empty files, fixed-offset files +""" + +from collections import OrderedDict +import io +import struct +import sys + +from binman import elf +from patman import command +from patman import tools + +# Set to True to enable printing output while working +DEBUG = False + +# Set to True to enable output from running cbfstool for debugging +VERBOSE = False + +# The master header, at the start of the CBFS +HEADER_FORMAT = '>IIIIIIII' +HEADER_LEN = 0x20 +HEADER_MAGIC = 0x4f524243 +HEADER_VERSION1 = 0x31313131 +HEADER_VERSION2 = 0x31313132 + +# The file header, at the start of each file in the CBFS +FILE_HEADER_FORMAT = b'>8sIIII' +FILE_HEADER_LEN = 0x18 +FILE_MAGIC = b'LARCHIVE' +FILENAME_ALIGN = 16 # Filename lengths are aligned to this + +# A stage header containing information about 'stage' files +# Yes this is correct: this header is in litte-endian format +STAGE_FORMAT = '<IQQII' +STAGE_LEN = 0x1c + +# An attribute describring the compression used in a file +ATTR_COMPRESSION_FORMAT = '>IIII' +ATTR_COMPRESSION_LEN = 0x10 + +# Attribute tags +# Depending on how the header was initialised, it may be backed with 0x00 or +# 0xff. Support both. +FILE_ATTR_TAG_UNUSED = 0 +FILE_ATTR_TAG_UNUSED2 = 0xffffffff +FILE_ATTR_TAG_COMPRESSION = 0x42435a4c +FILE_ATTR_TAG_HASH = 0x68736148 +FILE_ATTR_TAG_POSITION = 0x42435350 # PSCB +FILE_ATTR_TAG_ALIGNMENT = 0x42434c41 # ALCB +FILE_ATTR_TAG_PADDING = 0x47444150 # PDNG + +# This is 'the size of bootblock reserved in firmware image (cbfs.txt)' +# Not much more info is available, but we set it to 4, due to this comment in +# cbfstool.c: +# This causes 4 bytes to be left out at the end of the image, for two reasons: +# 1. The cbfs master header pointer resides there +# 2. Ssme cbfs implementations assume that an image that resides below 4GB has +# a bootblock and get confused when the end of the image is at 4GB == 0. +MIN_BOOTBLOCK_SIZE = 4 + +# Files start aligned to this boundary in the CBFS +ENTRY_ALIGN = 0x40 + +# CBFSs must declare an architecture since much of the logic is designed with +# x86 in mind. The effect of setting this value is not well documented, but in +# general x86 is used and this makes use of a boot block and an image that ends +# at the end of 32-bit address space. +ARCHITECTURE_UNKNOWN = 0xffffffff +ARCHITECTURE_X86 = 0x00000001 +ARCHITECTURE_ARM = 0x00000010 +ARCHITECTURE_AARCH64 = 0x0000aa64 +ARCHITECTURE_MIPS = 0x00000100 +ARCHITECTURE_RISCV = 0xc001d0de +ARCHITECTURE_PPC64 = 0x407570ff + +ARCH_NAMES = { + ARCHITECTURE_UNKNOWN : 'unknown', + ARCHITECTURE_X86 : 'x86', + ARCHITECTURE_ARM : 'arm', + ARCHITECTURE_AARCH64 : 'arm64', + ARCHITECTURE_MIPS : 'mips', + ARCHITECTURE_RISCV : 'riscv', + ARCHITECTURE_PPC64 : 'ppc64', + } + +# File types. Only supported ones are included here +TYPE_CBFSHEADER = 0x02 # Master header, HEADER_FORMAT +TYPE_STAGE = 0x10 # Stage, holding an executable, see STAGE_FORMAT +TYPE_RAW = 0x50 # Raw file, possibly compressed +TYPE_EMPTY = 0xffffffff # Empty data + +# Compression types +COMPRESS_NONE, COMPRESS_LZMA, COMPRESS_LZ4 = range(3) + +COMPRESS_NAMES = { + COMPRESS_NONE : 'none', + COMPRESS_LZMA : 'lzma', + COMPRESS_LZ4 : 'lz4', + } + +def find_arch(find_name): + """Look up an architecture name + + Args: + find_name: Architecture name to find + + Returns: + ARCHITECTURE_... value or None if not found + """ + for arch, name in ARCH_NAMES.items(): + if name == find_name: + return arch + return None + +def find_compress(find_name): + """Look up a compression algorithm name + + Args: + find_name: Compression algorithm name to find + + Returns: + COMPRESS_... value or None if not found + """ + for compress, name in COMPRESS_NAMES.items(): + if name == find_name: + return compress + return None + +def compress_name(compress): + """Look up the name of a compression algorithm + + Args: + compress: Compression algorithm number to find (COMPRESS_...) + + Returns: + Compression algorithm name (string) + + Raises: + KeyError if the algorithm number is invalid + """ + return COMPRESS_NAMES[compress] + +def align_int(val, align): + """Align a value up to the given alignment + + Args: + val: Integer value to align + align: Integer alignment value (e.g. 4 to align to 4-byte boundary) + + Returns: + integer value aligned to the required boundary, rounding up if necessary + """ + return int((val + align - 1) / align) * align + +def align_int_down(val, align): + """Align a value down to the given alignment + + Args: + val: Integer value to align + align: Integer alignment value (e.g. 4 to align to 4-byte boundary) + + Returns: + integer value aligned to the required boundary, rounding down if + necessary + """ + return int(val / align) * align + +def _pack_string(instr): + """Pack a string to the required aligned size by adding padding + + Args: + instr: String to process + + Returns: + String with required padding (at least one 0x00 byte) at the end + """ + val = tools.ToBytes(instr) + pad_len = align_int(len(val) + 1, FILENAME_ALIGN) + return val + tools.GetBytes(0, pad_len - len(val)) + + +class CbfsFile(object): + """Class to represent a single CBFS file + + This is used to hold the information about a file, including its contents. + Use the get_data_and_offset() method to obtain the raw output for writing to + CBFS. + + Properties: + name: Name of file + offset: Offset of file data from start of file header + cbfs_offset: Offset of file data in bytes from start of CBFS, or None to + place this file anyway + data: Contents of file, uncompressed + orig_data: Original data added to the file, possibly compressed + data_len: Length of (possibly compressed) data in bytes + ftype: File type (TYPE_...) + compression: Compression type (COMPRESS_...) + memlen: Length of data in memory, i.e. the uncompressed length, None if + no compression algortihm is selected + load: Load address in memory if known, else None + entry: Entry address in memory if known, else None. This is where + execution starts after the file is loaded + base_address: Base address to use for 'stage' files + erase_byte: Erase byte to use for padding between the file header and + contents (used for empty files) + size: Size of the file in bytes (used for empty files) + """ + def __init__(self, name, ftype, data, cbfs_offset, compress=COMPRESS_NONE): + self.name = name + self.offset = None + self.cbfs_offset = cbfs_offset + self.data = data + self.orig_data = data + self.ftype = ftype + self.compress = compress + self.memlen = None + self.load = None + self.entry = None + self.base_address = None + self.data_len = len(data) + self.erase_byte = None + self.size = None + + def decompress(self): + """Handle decompressing data if necessary""" + indata = self.data + if self.compress == COMPRESS_LZ4: + data = tools.Decompress(indata, 'lz4', with_header=False) + elif self.compress == COMPRESS_LZMA: + data = tools.Decompress(indata, 'lzma', with_header=False) + else: + data = indata + self.memlen = len(data) + self.data = data + self.data_len = len(indata) + + @classmethod + def stage(cls, base_address, name, data, cbfs_offset): + """Create a new stage file + + Args: + base_address: Int base address for memory-mapping of ELF file + name: String file name to put in CBFS (does not need to correspond + to the name that the file originally came from) + data: Contents of file + cbfs_offset: Offset of file data in bytes from start of CBFS, or + None to place this file anyway + + Returns: + CbfsFile object containing the file information + """ + cfile = CbfsFile(name, TYPE_STAGE, data, cbfs_offset) + cfile.base_address = base_address + return cfile + + @classmethod + def raw(cls, name, data, cbfs_offset, compress): + """Create a new raw file + + Args: + name: String file name to put in CBFS (does not need to correspond + to the name that the file originally came from) + data: Contents of file + cbfs_offset: Offset of file data in bytes from start of CBFS, or + None to place this file anyway + compress: Compression algorithm to use (COMPRESS_...) + + Returns: + CbfsFile object containing the file information + """ + return CbfsFile(name, TYPE_RAW, data, cbfs_offset, compress) + + @classmethod + def empty(cls, space_to_use, erase_byte): + """Create a new empty file of a given size + + Args: + space_to_use:: Size of available space, which must be at least as + large as the alignment size for this CBFS + erase_byte: Byte to use for contents of file (repeated through the + whole file) + + Returns: + CbfsFile object containing the file information + """ + cfile = CbfsFile('', TYPE_EMPTY, b'', None) + cfile.size = space_to_use - FILE_HEADER_LEN - FILENAME_ALIGN + cfile.erase_byte = erase_byte + return cfile + + def calc_start_offset(self): + """Check if this file needs to start at a particular offset in CBFS + + Returns: + None if the file can be placed anywhere, or + the largest offset where the file could start (integer) + """ + if self.cbfs_offset is None: + return None + return self.cbfs_offset - self.get_header_len() + + def get_header_len(self): + """Get the length of headers required for a file + + This is the minimum length required before the actual data for this file + could start. It might start later if there is padding. + + Returns: + Total length of all non-data fields, in bytes + """ + name = _pack_string(self.name) + hdr_len = len(name) + FILE_HEADER_LEN + if self.ftype == TYPE_STAGE: + pass + elif self.ftype == TYPE_RAW: + hdr_len += ATTR_COMPRESSION_LEN + elif self.ftype == TYPE_EMPTY: + pass + else: + raise ValueError('Unknown file type %#x\n' % self.ftype) + return hdr_len + + def get_data_and_offset(self, offset=None, pad_byte=None): + """Obtain the contents of the file, in CBFS format and the offset of + the data within the file + + Returns: + tuple: + bytes representing the contents of this file, packed and aligned + for directly inserting into the final CBFS output + offset to the file data from the start of the returned data. + """ + name = _pack_string(self.name) + hdr_len = len(name) + FILE_HEADER_LEN + attr_pos = 0 + content = b'' + attr = b'' + pad = b'' + data = self.data + if self.ftype == TYPE_STAGE: + elf_data = elf.DecodeElf(data, self.base_address) + content = struct.pack(STAGE_FORMAT, self.compress, + elf_data.entry, elf_data.load, + len(elf_data.data), elf_data.memsize) + data = elf_data.data + elif self.ftype == TYPE_RAW: + orig_data = data + if self.compress == COMPRESS_LZ4: + data = tools.Compress(orig_data, 'lz4', with_header=False) + elif self.compress == COMPRESS_LZMA: + data = tools.Compress(orig_data, 'lzma', with_header=False) + self.memlen = len(orig_data) + self.data_len = len(data) + attr = struct.pack(ATTR_COMPRESSION_FORMAT, + FILE_ATTR_TAG_COMPRESSION, ATTR_COMPRESSION_LEN, + self.compress, self.memlen) + elif self.ftype == TYPE_EMPTY: + data = tools.GetBytes(self.erase_byte, self.size) + else: + raise ValueError('Unknown type %#x when writing\n' % self.ftype) + if attr: + attr_pos = hdr_len + hdr_len += len(attr) + if self.cbfs_offset is not None: + pad_len = self.cbfs_offset - offset - hdr_len + if pad_len < 0: # pragma: no cover + # Test coverage of this is not available since this should never + # happen. It indicates that get_header_len() provided an + # incorrect value (too small) so that we decided that we could + # put this file at the requested place, but in fact a previous + # file extends far enough into the CBFS that this is not + # possible. + raise ValueError("Internal error: CBFS file '%s': Requested offset %#x but current output position is %#x" % + (self.name, self.cbfs_offset, offset)) + pad = tools.GetBytes(pad_byte, pad_len) + hdr_len += pad_len + + # This is the offset of the start of the file's data, + size = len(content) + len(data) + hdr = struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, size, + self.ftype, attr_pos, hdr_len) + + # Do a sanity check of the get_header_len() function, to ensure that it + # stays in lockstep with this function + expected_len = self.get_header_len() + actual_len = len(hdr + name + attr) + if expected_len != actual_len: # pragma: no cover + # Test coverage of this is not available since this should never + # happen. It probably indicates that get_header_len() is broken. + raise ValueError("Internal error: CBFS file '%s': Expected headers of %#x bytes, got %#d" % + (self.name, expected_len, actual_len)) + return hdr + name + attr + pad + content + data, hdr_len + + +class CbfsWriter(object): + """Class to handle writing a Coreboot File System (CBFS) + + Usage is something like: + + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', tools.ReadFile('u-boot.bin')) + ... + data, cbfs_offset = cbw.get_data_and_offset() + + Attributes: + _master_name: Name of the file containing the master header + _size: Size of the filesystem, in bytes + _files: Ordered list of files in the CBFS, each a CbfsFile + _arch: Architecture of the CBFS (ARCHITECTURE_...) + _bootblock_size: Size of the bootblock, typically at the end of the CBFS + _erase_byte: Byte to use for empty space in the CBFS + _align: Alignment to use for files, typically ENTRY_ALIGN + _base_address: Boot block offset in bytes from the start of CBFS. + Typically this is located at top of the CBFS. It is 0 when there is + no boot block + _header_offset: Offset of master header in bytes from start of CBFS + _contents_offset: Offset of first file header + _hdr_at_start: True if the master header is at the start of the CBFS, + instead of the end as normal for x86 + _add_fileheader: True to add a fileheader around the master header + """ + def __init__(self, size, arch=ARCHITECTURE_X86): + """Set up a new CBFS + + This sets up all properties to default values. Files can be added using + add_file_raw(), etc. + + Args: + size: Size of CBFS in bytes + arch: Architecture to declare for CBFS + """ + self._master_name = 'cbfs master header' + self._size = size + self._files = OrderedDict() + self._arch = arch + self._bootblock_size = 0 + self._erase_byte = 0xff + self._align = ENTRY_ALIGN + self._add_fileheader = False + if self._arch == ARCHITECTURE_X86: + # Allow 4 bytes for the header pointer. That holds the + # twos-compliment negative offset of the master header in bytes + # measured from one byte past the end of the CBFS + self._base_address = self._size - max(self._bootblock_size, + MIN_BOOTBLOCK_SIZE) + self._header_offset = self._base_address - HEADER_LEN + self._contents_offset = 0 + self._hdr_at_start = False + else: + # For non-x86, different rules apply + self._base_address = 0 + self._header_offset = align_int(self._base_address + + self._bootblock_size, 4) + self._contents_offset = align_int(self._header_offset + + FILE_HEADER_LEN + + self._bootblock_size, self._align) + self._hdr_at_start = True + + def _skip_to(self, fd, offset): + """Write out pad bytes until a given offset + + Args: + fd: File objext to write to + offset: Offset to write to + """ + if fd.tell() > offset: + raise ValueError('No space for data before offset %#x (current offset %#x)' % + (offset, fd.tell())) + fd.write(tools.GetBytes(self._erase_byte, offset - fd.tell())) + + def _pad_to(self, fd, offset): + """Write out pad bytes and/or an empty file until a given offset + + Args: + fd: File objext to write to + offset: Offset to write to + """ + self._align_to(fd, self._align) + upto = fd.tell() + if upto > offset: + raise ValueError('No space for data before pad offset %#x (current offset %#x)' % + (offset, upto)) + todo = align_int_down(offset - upto, self._align) + if todo: + cbf = CbfsFile.empty(todo, self._erase_byte) + fd.write(cbf.get_data_and_offset()[0]) + self._skip_to(fd, offset) + + def _align_to(self, fd, align): + """Write out pad bytes until a given alignment is reached + + This only aligns if the resulting output would not reach the end of the + CBFS, since we want to leave the last 4 bytes for the master-header + pointer. + + Args: + fd: File objext to write to + align: Alignment to require (e.g. 4 means pad to next 4-byte + boundary) + """ + offset = align_int(fd.tell(), align) + if offset < self._size: + self._skip_to(fd, offset) + + def add_file_stage(self, name, data, cbfs_offset=None): + """Add a new stage file to the CBFS + + Args: + name: String file name to put in CBFS (does not need to correspond + to the name that the file originally came from) + data: Contents of file + cbfs_offset: Offset of this file's data within the CBFS, in bytes, + or None to place this file anywhere + + Returns: + CbfsFile object created + """ + cfile = CbfsFile.stage(self._base_address, name, data, cbfs_offset) + self._files[name] = cfile + return cfile + + def add_file_raw(self, name, data, cbfs_offset=None, + compress=COMPRESS_NONE): + """Create a new raw file + + Args: + name: String file name to put in CBFS (does not need to correspond + to the name that the file originally came from) + data: Contents of file + cbfs_offset: Offset of this file's data within the CBFS, in bytes, + or None to place this file anywhere + compress: Compression algorithm to use (COMPRESS_...) + + Returns: + CbfsFile object created + """ + cfile = CbfsFile.raw(name, data, cbfs_offset, compress) + self._files[name] = cfile + return cfile + + def _write_header(self, fd, add_fileheader): + """Write out the master header to a CBFS + + Args: + fd: File object + add_fileheader: True to place the master header in a file header + record + """ + if fd.tell() > self._header_offset: + raise ValueError('No space for header at offset %#x (current offset %#x)' % + (self._header_offset, fd.tell())) + if not add_fileheader: + self._pad_to(fd, self._header_offset) + hdr = struct.pack(HEADER_FORMAT, HEADER_MAGIC, HEADER_VERSION2, + self._size, self._bootblock_size, self._align, + self._contents_offset, self._arch, 0xffffffff) + if add_fileheader: + name = _pack_string(self._master_name) + fd.write(struct.pack(FILE_HEADER_FORMAT, FILE_MAGIC, len(hdr), + TYPE_CBFSHEADER, 0, + FILE_HEADER_LEN + len(name))) + fd.write(name) + self._header_offset = fd.tell() + fd.write(hdr) + self._align_to(fd, self._align) + else: + fd.write(hdr) + + def get_data(self): + """Obtain the full contents of the CBFS + + Thhis builds the CBFS with headers and all required files. + + Returns: + 'bytes' type containing the data + """ + fd = io.BytesIO() + + # THe header can go at the start in some cases + if self._hdr_at_start: + self._write_header(fd, add_fileheader=self._add_fileheader) + self._skip_to(fd, self._contents_offset) + + # Write out each file + for cbf in self._files.values(): + # Place the file at its requested place, if any + offset = cbf.calc_start_offset() + if offset is not None: + self._pad_to(fd, align_int_down(offset, self._align)) + pos = fd.tell() + data, data_offset = cbf.get_data_and_offset(pos, self._erase_byte) + fd.write(data) + self._align_to(fd, self._align) + cbf.calced_cbfs_offset = pos + data_offset + if not self._hdr_at_start: + self._write_header(fd, add_fileheader=self._add_fileheader) + + # Pad to the end and write a pointer to the CBFS master header + self._pad_to(fd, self._base_address or self._size - 4) + rel_offset = self._header_offset - self._size + fd.write(struct.pack('<I', rel_offset & 0xffffffff)) + + return fd.getvalue() + + +class CbfsReader(object): + """Class to handle reading a Coreboot File System (CBFS) + + Usage is something like: + cbfs = cbfs_util.CbfsReader(data) + cfile = cbfs.files['u-boot'] + self.WriteFile('u-boot.bin', cfile.data) + + Attributes: + files: Ordered list of CbfsFile objects + align: Alignment to use for files, typically ENTRT_ALIGN + stage_base_address: Base address to use when mapping ELF files into the + CBFS for TYPE_STAGE files. If this is larger than the code address + of the ELF file, then data at the start of the ELF file will not + appear in the CBFS. Currently there are no tests for behaviour as + documentation is sparse + magic: Integer magic number from master header (HEADER_MAGIC) + version: Version number of CBFS (HEADER_VERSION2) + rom_size: Size of CBFS + boot_block_size: Size of boot block + cbfs_offset: Offset of the first file in bytes from start of CBFS + arch: Architecture of CBFS file (ARCHITECTURE_...) + """ + def __init__(self, data, read=True): + self.align = ENTRY_ALIGN + self.arch = None + self.boot_block_size = None + self.cbfs_offset = None + self.files = OrderedDict() + self.magic = None + self.rom_size = None + self.stage_base_address = 0 + self.version = None + self.data = data + if read: + self.read() + + def read(self): + """Read all the files in the CBFS and add them to self.files""" + with io.BytesIO(self.data) as fd: + # First, get the master header + if not self._find_and_read_header(fd, len(self.data)): + raise ValueError('Cannot find master header') + fd.seek(self.cbfs_offset) + + # Now read in the files one at a time + while True: + cfile = self._read_next_file(fd) + if cfile: + self.files[cfile.name] = cfile + elif cfile is False: + break + + def _find_and_read_header(self, fd, size): + """Find and read the master header in the CBFS + + This looks at the pointer word at the very end of the CBFS. This is an + offset to the header relative to the size of the CBFS, which is assumed + to be known. Note that the offset is in *little endian* format. + + Args: + fd: File to read from + size: Size of file + + Returns: + True if header was found, False if not + """ + orig_pos = fd.tell() + fd.seek(size - 4) + rel_offset, = struct.unpack('<I', fd.read(4)) + pos = (size + rel_offset) & 0xffffffff + fd.seek(pos) + found = self._read_header(fd) + if not found: + print('Relative offset seems wrong, scanning whole image') + for pos in range(0, size - HEADER_LEN, 4): + fd.seek(pos) + found = self._read_header(fd) + if found: + break + fd.seek(orig_pos) + return found + + def _read_next_file(self, fd): + """Read the next file from a CBFS + + Args: + fd: File to read from + + Returns: + CbfsFile object, if found + None if no object found, but data was parsed (e.g. TYPE_CBFSHEADER) + False if at end of CBFS and reading should stop + """ + file_pos = fd.tell() + data = fd.read(FILE_HEADER_LEN) + if len(data) < FILE_HEADER_LEN: + print('File header at %#x ran out of data' % file_pos) + return False + magic, size, ftype, attr, offset = struct.unpack(FILE_HEADER_FORMAT, + data) + if magic != FILE_MAGIC: + return False + pos = fd.tell() + name = self._read_string(fd) + if name is None: + print('String at %#x ran out of data' % pos) + return False + + if DEBUG: + print('name', name) + + # If there are attribute headers present, read those + compress = self._read_attr(fd, file_pos, attr, offset) + if compress is None: + return False + + # Create the correct CbfsFile object depending on the type + cfile = None + cbfs_offset = file_pos + offset + fd.seek(cbfs_offset, io.SEEK_SET) + if ftype == TYPE_CBFSHEADER: + self._read_header(fd) + elif ftype == TYPE_STAGE: + data = fd.read(STAGE_LEN) + cfile = CbfsFile.stage(self.stage_base_address, name, b'', + cbfs_offset) + (cfile.compress, cfile.entry, cfile.load, cfile.data_len, + cfile.memlen) = struct.unpack(STAGE_FORMAT, data) + cfile.data = fd.read(cfile.data_len) + elif ftype == TYPE_RAW: + data = fd.read(size) + cfile = CbfsFile.raw(name, data, cbfs_offset, compress) + cfile.decompress() + if DEBUG: + print('data', data) + elif ftype == TYPE_EMPTY: + # Just read the data and discard it, since it is only padding + fd.read(size) + cfile = CbfsFile('', TYPE_EMPTY, b'', cbfs_offset) + else: + raise ValueError('Unknown type %#x when reading\n' % ftype) + if cfile: + cfile.offset = offset + + # Move past the padding to the start of a possible next file. If we are + # already at an alignment boundary, then there is no padding. + pad = (self.align - fd.tell() % self.align) % self.align + fd.seek(pad, io.SEEK_CUR) + return cfile + + @classmethod + def _read_attr(cls, fd, file_pos, attr, offset): + """Read attributes from the file + + CBFS files can have attributes which are things that cannot fit into the + header. The only attributes currently supported are compression and the + unused tag. + + Args: + fd: File to read from + file_pos: Position of file in fd + attr: Offset of attributes, 0 if none + offset: Offset of file data (used to indicate the end of the + attributes) + + Returns: + Compression to use for the file (COMPRESS_...) + """ + compress = COMPRESS_NONE + if not attr: + return compress + attr_size = offset - attr + fd.seek(file_pos + attr, io.SEEK_SET) + while attr_size: + pos = fd.tell() + hdr = fd.read(8) + if len(hdr) < 8: + print('Attribute tag at %x ran out of data' % pos) + return None + atag, alen = struct.unpack(">II", hdr) + data = hdr + fd.read(alen - 8) + if atag == FILE_ATTR_TAG_COMPRESSION: + # We don't currently use this information + atag, alen, compress, _decomp_size = struct.unpack( + ATTR_COMPRESSION_FORMAT, data) + elif atag == FILE_ATTR_TAG_UNUSED2: + break + else: + print('Unknown attribute tag %x' % atag) + attr_size -= len(data) + return compress + + def _read_header(self, fd): + """Read the master header + + Reads the header and stores the information obtained into the member + variables. + + Args: + fd: File to read from + + Returns: + True if header was read OK, False if it is truncated or has the + wrong magic or version + """ + pos = fd.tell() + data = fd.read(HEADER_LEN) + if len(data) < HEADER_LEN: + print('Header at %x ran out of data' % pos) + return False + (self.magic, self.version, self.rom_size, self.boot_block_size, + self.align, self.cbfs_offset, self.arch, _) = struct.unpack( + HEADER_FORMAT, data) + return self.magic == HEADER_MAGIC and ( + self.version == HEADER_VERSION1 or + self.version == HEADER_VERSION2) + + @classmethod + def _read_string(cls, fd): + """Read a string from a file + + This reads a string and aligns the data to the next alignment boundary + + Args: + fd: File to read from + + Returns: + string read ('str' type) encoded to UTF-8, or None if we ran out of + data + """ + val = b'' + while True: + data = fd.read(FILENAME_ALIGN) + if len(data) < FILENAME_ALIGN: + return None + pos = data.find(b'\0') + if pos == -1: + val += data + else: + val += data[:pos] + break + return val.decode('utf-8') + + +def cbfstool(fname, *cbfs_args, **kwargs): + """Run cbfstool with provided arguments + + If the tool fails then this function raises an exception and prints out the + output and stderr. + + Args: + fname: Filename of CBFS + *cbfs_args: List of arguments to pass to cbfstool + + Returns: + CommandResult object containing the results + """ + args = ['cbfstool', fname] + list(cbfs_args) + if kwargs.get('base') is not None: + args += ['-b', '%#x' % kwargs['base']] + result = command.RunPipe([args], capture=not VERBOSE, + capture_stderr=not VERBOSE, raise_on_error=False) + if result.return_code: + print(result.stderr, file=sys.stderr) + raise Exception("Failed to run (error %d): '%s'" % + (result.return_code, ' '.join(args))) diff --git a/roms/u-boot/tools/binman/cbfs_util_test.py b/roms/u-boot/tools/binman/cbfs_util_test.py new file mode 100755 index 000000000..2c62c8a0f --- /dev/null +++ b/roms/u-boot/tools/binman/cbfs_util_test.py @@ -0,0 +1,623 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> + +"""Tests for cbfs_util + +These create and read various CBFSs and compare the results with expected +values and with cbfstool +""" + +import io +import os +import shutil +import struct +import tempfile +import unittest + +from binman import cbfs_util +from binman.cbfs_util import CbfsWriter +from binman import elf +from patman import test_util +from patman import tools + +U_BOOT_DATA = b'1234' +U_BOOT_DTB_DATA = b'udtb' +COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data' + + +class TestCbfs(unittest.TestCase): + """Test of cbfs_util classes""" + #pylint: disable=W0212 + @classmethod + def setUpClass(cls): + # Create a temporary directory for test files + cls._indir = tempfile.mkdtemp(prefix='cbfs_util.') + tools.SetInputDirs([cls._indir]) + + # Set up some useful data files + TestCbfs._make_input_file('u-boot.bin', U_BOOT_DATA) + TestCbfs._make_input_file('u-boot.dtb', U_BOOT_DTB_DATA) + TestCbfs._make_input_file('compress', COMPRESS_DATA) + + # Set up a temporary output directory, used by the tools library when + # compressing files + tools.PrepareOutputDir(None) + + cls.have_cbfstool = True + try: + tools.Run('which', 'cbfstool') + except: + cls.have_cbfstool = False + + cls.have_lz4 = True + try: + tools.Run('lz4', '--no-frame-crc', '-c', + tools.GetInputFilename('u-boot.bin'), binary=True) + except: + cls.have_lz4 = False + + @classmethod + def tearDownClass(cls): + """Remove the temporary input directory and its contents""" + if cls._indir: + shutil.rmtree(cls._indir) + cls._indir = None + tools.FinaliseOutputDir() + + @classmethod + def _make_input_file(cls, fname, contents): + """Create a new test input file, creating directories as needed + + Args: + fname: Filename to create + contents: File contents to write in to the file + Returns: + Full pathname of file created + """ + pathname = os.path.join(cls._indir, fname) + tools.WriteFile(pathname, contents) + return pathname + + def _check_hdr(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86): + """Check that the CBFS has the expected header + + Args: + data: Data to check + size: Expected ROM size + offset: Expected offset to first CBFS file + arch: Expected architecture + + Returns: + CbfsReader object containing the CBFS + """ + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(cbfs_util.HEADER_MAGIC, cbfs.magic) + self.assertEqual(cbfs_util.HEADER_VERSION2, cbfs.version) + self.assertEqual(size, cbfs.rom_size) + self.assertEqual(0, cbfs.boot_block_size) + self.assertEqual(cbfs_util.ENTRY_ALIGN, cbfs.align) + self.assertEqual(offset, cbfs.cbfs_offset) + self.assertEqual(arch, cbfs.arch) + return cbfs + + def _check_uboot(self, cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x38, + data=U_BOOT_DATA, cbfs_offset=None): + """Check that the U-Boot file is as expected + + Args: + cbfs: CbfsReader object to check + ftype: Expected file type + offset: Expected offset of file + data: Expected data in file + cbfs_offset: Expected CBFS offset for file's data + + Returns: + CbfsFile object containing the file + """ + self.assertIn('u-boot', cbfs.files) + cfile = cbfs.files['u-boot'] + self.assertEqual('u-boot', cfile.name) + self.assertEqual(offset, cfile.offset) + if cbfs_offset is not None: + self.assertEqual(cbfs_offset, cfile.cbfs_offset) + self.assertEqual(data, cfile.data) + self.assertEqual(ftype, cfile.ftype) + self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) + self.assertEqual(len(data), cfile.memlen) + return cfile + + def _check_dtb(self, cbfs, offset=0x38, data=U_BOOT_DTB_DATA, + cbfs_offset=None): + """Check that the U-Boot dtb file is as expected + + Args: + cbfs: CbfsReader object to check + offset: Expected offset of file + data: Expected data in file + cbfs_offset: Expected CBFS offset for file's data + """ + self.assertIn('u-boot-dtb', cbfs.files) + cfile = cbfs.files['u-boot-dtb'] + self.assertEqual('u-boot-dtb', cfile.name) + self.assertEqual(offset, cfile.offset) + if cbfs_offset is not None: + self.assertEqual(cbfs_offset, cfile.cbfs_offset) + self.assertEqual(U_BOOT_DTB_DATA, cfile.data) + self.assertEqual(cbfs_util.TYPE_RAW, cfile.ftype) + self.assertEqual(cbfs_util.COMPRESS_NONE, cfile.compress) + self.assertEqual(len(U_BOOT_DTB_DATA), cfile.memlen) + + def _check_raw(self, data, size, offset=0, arch=cbfs_util.ARCHITECTURE_X86): + """Check that two raw files are added as expected + + Args: + data: Data to check + size: Expected ROM size + offset: Expected offset to first CBFS file + arch: Expected architecture + """ + cbfs = self._check_hdr(data, size, offset=offset, arch=arch) + self._check_uboot(cbfs) + self._check_dtb(cbfs) + + def _get_expected_cbfs(self, size, arch='x86', compress=None, base=None): + """Get the file created by cbfstool for a particular scenario + + Args: + size: Size of the CBFS in bytes + arch: Architecture of the CBFS, as a string + compress: Compression to use, e.g. cbfs_util.COMPRESS_LZMA + base: Base address of file, or None to put it anywhere + + Returns: + Resulting CBFS file, or None if cbfstool is not available + """ + if not self.have_cbfstool or not self.have_lz4: + return None + cbfs_fname = os.path.join(self._indir, 'test.cbfs') + cbfs_util.cbfstool(cbfs_fname, 'create', '-m', arch, '-s', '%#x' % size) + if base: + base = [(1 << 32) - size + b for b in base] + cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot', '-t', 'raw', + '-c', compress and compress[0] or 'none', + '-f', tools.GetInputFilename( + compress and 'compress' or 'u-boot.bin'), + base=base[0] if base else None) + cbfs_util.cbfstool(cbfs_fname, 'add', '-n', 'u-boot-dtb', '-t', 'raw', + '-c', compress and compress[1] or 'none', + '-f', tools.GetInputFilename( + compress and 'compress' or 'u-boot.dtb'), + base=base[1] if base else None) + return cbfs_fname + + def _compare_expected_cbfs(self, data, cbfstool_fname): + """Compare against what cbfstool creates + + This compares what binman creates with what cbfstool creates for what + is proportedly the same thing. + + Args: + data: CBFS created by binman + cbfstool_fname: CBFS created by cbfstool + """ + if not self.have_cbfstool or not self.have_lz4: + return + expect = tools.ReadFile(cbfstool_fname) + if expect != data: + tools.WriteFile('/tmp/expect', expect) + tools.WriteFile('/tmp/actual', data) + print('diff -y <(xxd -g1 /tmp/expect) <(xxd -g1 /tmp/actual) | colordiff') + self.fail('cbfstool produced a different result') + + def test_cbfs_functions(self): + """Test global functions of cbfs_util""" + self.assertEqual(cbfs_util.ARCHITECTURE_X86, cbfs_util.find_arch('x86')) + self.assertIsNone(cbfs_util.find_arch('bad-arch')) + + self.assertEqual(cbfs_util.COMPRESS_LZMA, cbfs_util.find_compress('lzma')) + self.assertIsNone(cbfs_util.find_compress('bad-comp')) + + def test_cbfstool_failure(self): + """Test failure to run cbfstool""" + if not self.have_cbfstool: + self.skipTest('No cbfstool available') + try: + # In verbose mode this test fails since stderr is not captured. Fix + # this by turning off verbosity. + old_verbose = cbfs_util.VERBOSE + cbfs_util.VERBOSE = False + with test_util.capture_sys_output() as (_stdout, stderr): + with self.assertRaises(Exception) as e: + cbfs_util.cbfstool('missing-file', 'bad-command') + finally: + cbfs_util.VERBOSE = old_verbose + self.assertIn('Unknown command', stderr.getvalue()) + self.assertIn('Failed to run', str(e.exception)) + + def test_cbfs_raw(self): + """Test base handling of a Coreboot Filesystem (CBFS)""" + size = 0xb0 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) + data = cbw.get_data() + self._check_raw(data, size) + cbfs_fname = self._get_expected_cbfs(size=size) + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_invalid_file_type(self): + """Check handling of an invalid file type when outputiing a CBFS""" + size = 0xb0 + cbw = CbfsWriter(size) + cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA) + + # Change the type manually before generating the CBFS, and make sure + # that the generator complains + cfile.ftype = 0xff + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('Unknown type 0xff when writing', str(e.exception)) + + def test_cbfs_invalid_file_type_on_read(self): + """Check handling of an invalid file type when reading the CBFS""" + size = 0xb0 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + + data = cbw.get_data() + + # Read in the first file header + cbr = cbfs_util.CbfsReader(data, read=False) + with io.BytesIO(data) as fd: + self.assertTrue(cbr._find_and_read_header(fd, len(data))) + pos = fd.tell() + hdr_data = fd.read(cbfs_util.FILE_HEADER_LEN) + magic, size, ftype, attr, offset = struct.unpack( + cbfs_util.FILE_HEADER_FORMAT, hdr_data) + + # Create a new CBFS with a change to the file type + ftype = 0xff + newdata = data[:pos] + newdata += struct.pack(cbfs_util.FILE_HEADER_FORMAT, magic, size, ftype, + attr, offset) + newdata += data[pos + cbfs_util.FILE_HEADER_LEN:] + + # Read in this CBFS and make sure that the reader complains + with self.assertRaises(ValueError) as e: + cbfs_util.CbfsReader(newdata) + self.assertIn('Unknown type 0xff when reading', str(e.exception)) + + def test_cbfs_no_space(self): + """Check handling of running out of space in the CBFS""" + size = 0x60 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('No space for header', str(e.exception)) + + def test_cbfs_no_space_skip(self): + """Check handling of running out of space in CBFS with file header""" + size = 0x5c + cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64) + cbw._add_fileheader = True + cbw.add_file_raw('u-boot', U_BOOT_DATA) + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('No space for data before offset', str(e.exception)) + + def test_cbfs_no_space_pad(self): + """Check handling of running out of space in CBFS with file header""" + size = 0x70 + cbw = CbfsWriter(size) + cbw._add_fileheader = True + cbw.add_file_raw('u-boot', U_BOOT_DATA) + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('No space for data before pad offset', str(e.exception)) + + def test_cbfs_bad_header_ptr(self): + """Check handling of a bad master-header pointer""" + size = 0x70 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + data = cbw.get_data() + + # Add one to the pointer to make it invalid + newdata = data[:-4] + struct.pack('<I', cbw._header_offset + 1) + + # We should still be able to find the master header by searching + with test_util.capture_sys_output() as (stdout, _stderr): + cbfs = cbfs_util.CbfsReader(newdata) + self.assertIn('Relative offset seems wrong', stdout.getvalue()) + self.assertIn('u-boot', cbfs.files) + self.assertEqual(size, cbfs.rom_size) + + def test_cbfs_bad_header(self): + """Check handling of a bad master header""" + size = 0x70 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + data = cbw.get_data() + + # Drop most of the header and try reading the modified CBFS + newdata = data[:cbw._header_offset + 4] + + with test_util.capture_sys_output() as (stdout, _stderr): + with self.assertRaises(ValueError) as e: + cbfs_util.CbfsReader(newdata) + self.assertIn('Relative offset seems wrong', stdout.getvalue()) + self.assertIn('Cannot find master header', str(e.exception)) + + def test_cbfs_bad_file_header(self): + """Check handling of a bad file header""" + size = 0x70 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + data = cbw.get_data() + + # Read in the CBFS master header (only), then stop + cbr = cbfs_util.CbfsReader(data, read=False) + with io.BytesIO(data) as fd: + self.assertTrue(cbr._find_and_read_header(fd, len(data))) + pos = fd.tell() + + # Remove all but 4 bytes of the file headerm and try to read the file + newdata = data[:pos + 4] + with test_util.capture_sys_output() as (stdout, _stderr): + with io.BytesIO(newdata) as fd: + fd.seek(pos) + self.assertEqual(False, cbr._read_next_file(fd)) + self.assertIn('File header at 0x0 ran out of data', stdout.getvalue()) + + def test_cbfs_bad_file_string(self): + """Check handling of an incomplete filename string""" + size = 0x70 + cbw = CbfsWriter(size) + cbw.add_file_raw('16-characters xx', U_BOOT_DATA) + data = cbw.get_data() + + # Read in the CBFS master header (only), then stop + cbr = cbfs_util.CbfsReader(data, read=False) + with io.BytesIO(data) as fd: + self.assertTrue(cbr._find_and_read_header(fd, len(data))) + pos = fd.tell() + + # Create a new CBFS with only the first 16 bytes of the file name, then + # try to read the file + newdata = data[:pos + cbfs_util.FILE_HEADER_LEN + 16] + with test_util.capture_sys_output() as (stdout, _stderr): + with io.BytesIO(newdata) as fd: + fd.seek(pos) + self.assertEqual(False, cbr._read_next_file(fd)) + self.assertIn('String at %#x ran out of data' % + cbfs_util.FILE_HEADER_LEN, stdout.getvalue()) + + def test_cbfs_debug(self): + """Check debug output""" + size = 0x70 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + data = cbw.get_data() + + try: + cbfs_util.DEBUG = True + with test_util.capture_sys_output() as (stdout, _stderr): + cbfs_util.CbfsReader(data) + self.assertEqual('name u-boot\ndata %s\n' % U_BOOT_DATA, + stdout.getvalue()) + finally: + cbfs_util.DEBUG = False + + def test_cbfs_bad_attribute(self): + """Check handling of bad attribute tag""" + if not self.have_lz4: + self.skipTest('lz4 --no-frame-crc not available') + size = 0x140 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', COMPRESS_DATA, None, + compress=cbfs_util.COMPRESS_LZ4) + data = cbw.get_data() + + # Search the CBFS for the expected compression tag + with io.BytesIO(data) as fd: + while True: + pos = fd.tell() + tag, = struct.unpack('>I', fd.read(4)) + if tag == cbfs_util.FILE_ATTR_TAG_COMPRESSION: + break + + # Create a new CBFS with the tag changed to something invalid + newdata = data[:pos] + struct.pack('>I', 0x123) + data[pos + 4:] + with test_util.capture_sys_output() as (stdout, _stderr): + cbfs_util.CbfsReader(newdata) + self.assertEqual('Unknown attribute tag 123\n', stdout.getvalue()) + + def test_cbfs_missing_attribute(self): + """Check handling of an incomplete attribute tag""" + if not self.have_lz4: + self.skipTest('lz4 --no-frame-crc not available') + size = 0x140 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', COMPRESS_DATA, None, + compress=cbfs_util.COMPRESS_LZ4) + data = cbw.get_data() + + # Read in the CBFS master header (only), then stop + cbr = cbfs_util.CbfsReader(data, read=False) + with io.BytesIO(data) as fd: + self.assertTrue(cbr._find_and_read_header(fd, len(data))) + pos = fd.tell() + + # Create a new CBFS with only the first 4 bytes of the compression tag, + # then try to read the file + tag_pos = pos + cbfs_util.FILE_HEADER_LEN + cbfs_util.FILENAME_ALIGN + newdata = data[:tag_pos + 4] + with test_util.capture_sys_output() as (stdout, _stderr): + with io.BytesIO(newdata) as fd: + fd.seek(pos) + self.assertEqual(False, cbr._read_next_file(fd)) + self.assertIn('Attribute tag at %x ran out of data' % tag_pos, + stdout.getvalue()) + + def test_cbfs_file_master_header(self): + """Check handling of a file containing a master header""" + size = 0x100 + cbw = CbfsWriter(size) + cbw._add_fileheader = True + cbw.add_file_raw('u-boot', U_BOOT_DATA) + data = cbw.get_data() + + cbr = cbfs_util.CbfsReader(data) + self.assertIn('u-boot', cbr.files) + self.assertEqual(size, cbr.rom_size) + + def test_cbfs_arch(self): + """Test on non-x86 architecture""" + size = 0x100 + cbw = CbfsWriter(size, arch=cbfs_util.ARCHITECTURE_PPC64) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) + data = cbw.get_data() + self._check_raw(data, size, offset=0x40, + arch=cbfs_util.ARCHITECTURE_PPC64) + + # Compare against what cbfstool creates + cbfs_fname = self._get_expected_cbfs(size=size, arch='ppc64') + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_stage(self): + """Tests handling of a Coreboot Filesystem (CBFS)""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') + elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA) + + size = 0xb0 + cbw = CbfsWriter(size) + cbw.add_file_stage('u-boot', tools.ReadFile(elf_fname)) + + data = cbw.get_data() + cbfs = self._check_hdr(data, size) + load = 0xfef20000 + entry = load + 2 + + cfile = self._check_uboot(cbfs, cbfs_util.TYPE_STAGE, offset=0x28, + data=U_BOOT_DATA + U_BOOT_DTB_DATA) + + self.assertEqual(entry, cfile.entry) + self.assertEqual(load, cfile.load) + self.assertEqual(len(U_BOOT_DATA) + len(U_BOOT_DTB_DATA), + cfile.data_len) + + # Compare against what cbfstool creates + if self.have_cbfstool: + cbfs_fname = os.path.join(self._indir, 'test.cbfs') + cbfs_util.cbfstool(cbfs_fname, 'create', '-m', 'x86', '-s', + '%#x' % size) + cbfs_util.cbfstool(cbfs_fname, 'add-stage', '-n', 'u-boot', + '-f', elf_fname) + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_raw_compress(self): + """Test base handling of compressing raw files""" + if not self.have_lz4: + self.skipTest('lz4 --no-frame-crc not available') + size = 0x140 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', COMPRESS_DATA, None, + compress=cbfs_util.COMPRESS_LZ4) + cbw.add_file_raw('u-boot-dtb', COMPRESS_DATA, None, + compress=cbfs_util.COMPRESS_LZMA) + data = cbw.get_data() + + cbfs = self._check_hdr(data, size) + self.assertIn('u-boot', cbfs.files) + cfile = cbfs.files['u-boot'] + self.assertEqual(cfile.name, 'u-boot') + self.assertEqual(cfile.offset, 56) + self.assertEqual(cfile.data, COMPRESS_DATA) + self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW) + self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZ4) + self.assertEqual(cfile.memlen, len(COMPRESS_DATA)) + + self.assertIn('u-boot-dtb', cbfs.files) + cfile = cbfs.files['u-boot-dtb'] + self.assertEqual(cfile.name, 'u-boot-dtb') + self.assertEqual(cfile.offset, 56) + self.assertEqual(cfile.data, COMPRESS_DATA) + self.assertEqual(cfile.ftype, cbfs_util.TYPE_RAW) + self.assertEqual(cfile.compress, cbfs_util.COMPRESS_LZMA) + self.assertEqual(cfile.memlen, len(COMPRESS_DATA)) + + cbfs_fname = self._get_expected_cbfs(size=size, compress=['lz4', 'lzma']) + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_raw_space(self): + """Test files with unused space in the CBFS""" + size = 0xf0 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) + data = cbw.get_data() + self._check_raw(data, size) + cbfs_fname = self._get_expected_cbfs(size=size) + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_offset(self): + """Test a CBFS with files at particular offsets""" + size = 0x200 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x140) + + data = cbw.get_data() + cbfs = self._check_hdr(data, size) + self._check_uboot(cbfs, ftype=cbfs_util.TYPE_RAW, offset=0x40, + cbfs_offset=0x40) + self._check_dtb(cbfs, offset=0x40, cbfs_offset=0x140) + + cbfs_fname = self._get_expected_cbfs(size=size, base=(0x40, 0x140)) + self._compare_expected_cbfs(data, cbfs_fname) + + def test_cbfs_invalid_file_type_header(self): + """Check handling of an invalid file type when outputting a header""" + size = 0xb0 + cbw = CbfsWriter(size) + cfile = cbw.add_file_raw('u-boot', U_BOOT_DATA, 0) + + # Change the type manually before generating the CBFS, and make sure + # that the generator complains + cfile.ftype = 0xff + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('Unknown file type 0xff', str(e.exception)) + + def test_cbfs_offset_conflict(self): + """Test a CBFS with files that want to overlap""" + size = 0x200 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA, 0x40) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA, 0x80) + + with self.assertRaises(ValueError) as e: + cbw.get_data() + self.assertIn('No space for data before pad offset', str(e.exception)) + + def test_cbfs_check_offset(self): + """Test that we can discover the offset of a file after writing it""" + size = 0xb0 + cbw = CbfsWriter(size) + cbw.add_file_raw('u-boot', U_BOOT_DATA) + cbw.add_file_raw('u-boot-dtb', U_BOOT_DTB_DATA) + data = cbw.get_data() + + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(0x38, cbfs.files['u-boot'].cbfs_offset) + self.assertEqual(0x78, cbfs.files['u-boot-dtb'].cbfs_offset) + + +if __name__ == '__main__': + unittest.main() diff --git a/roms/u-boot/tools/binman/cmdline.py b/roms/u-boot/tools/binman/cmdline.py new file mode 100644 index 000000000..95f9ba27f --- /dev/null +++ b/roms/u-boot/tools/binman/cmdline.py @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Command-line parser for binman +# + +from argparse import ArgumentParser + +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 + """ + if '-H' in argv: + argv.append('build') + + epilog = '''Binman creates and manipulate images for a board from a set of binaries. Binman is +controlled by a description in the board device tree.''' + + parser = ArgumentParser(epilog=epilog) + parser.add_argument('-B', '--build-dir', type=str, default='b', + help='Directory containing the build output') + parser.add_argument('-D', '--debug', action='store_true', + help='Enabling debugging (provides a full traceback on error)') + parser.add_argument('-H', '--full-help', action='store_true', + default=False, help='Display the README file') + parser.add_argument('--toolpath', type=str, action='append', + help='Add a path to the directories containing tools') + parser.add_argument('-v', '--verbosity', default=1, + type=int, help='Control verbosity: 0=silent, 1=warnings, 2=notices, ' + '3=info, 4=detail, 5=debug') + + subparsers = parser.add_subparsers(dest='cmd') + subparsers.required = True + + build_parser = subparsers.add_parser('build', help='Build firmware image') + build_parser.add_argument('-a', '--entry-arg', type=str, action='append', + help='Set argument value arg=value') + build_parser.add_argument('-b', '--board', type=str, + help='Board name to build') + build_parser.add_argument('-d', '--dt', type=str, + help='Configuration file (.dtb) to use') + build_parser.add_argument('--fake-dtb', action='store_true', + help='Use fake device tree contents (for testing only)') + build_parser.add_argument('-i', '--image', type=str, action='append', + help='Image filename to build (if not specified, build all)') + build_parser.add_argument('-I', '--indir', action='append', + help='Add a path to the list of directories to use for input files') + build_parser.add_argument('-m', '--map', action='store_true', + default=False, help='Output a map file for each image') + build_parser.add_argument('-M', '--allow-missing', action='store_true', + default=False, help='Allow external blobs to be missing') + build_parser.add_argument('-n', '--no-expanded', action='store_true', + help="Don't use 'expanded' versions of entries where available; " + "normally 'u-boot' becomes 'u-boot-expanded', for example") + build_parser.add_argument('-O', '--outdir', type=str, + action='store', help='Path to directory to use for intermediate and ' + 'output files') + build_parser.add_argument('-p', '--preserve', action='store_true',\ + help='Preserve temporary output directory even if option -O is not ' + 'given') + build_parser.add_argument('-u', '--update-fdt', action='store_true', + default=False, help='Update the binman node with offset/size info') + + entry_parser = subparsers.add_parser('entry-docs', + help='Write out entry documentation (see entries.rst)') + + list_parser = subparsers.add_parser('ls', help='List files in an image') + list_parser.add_argument('-i', '--image', type=str, required=True, + help='Image filename to list') + list_parser.add_argument('paths', type=str, nargs='*', + help='Paths within file to list (wildcard)') + + extract_parser = subparsers.add_parser('extract', + help='Extract files from an image') + extract_parser.add_argument('-i', '--image', type=str, required=True, + help='Image filename to extract') + extract_parser.add_argument('-f', '--filename', type=str, + help='Output filename to write to') + extract_parser.add_argument('-O', '--outdir', type=str, default='', + help='Path to directory to use for output files') + extract_parser.add_argument('paths', type=str, nargs='*', + help='Paths within file to extract (wildcard)') + extract_parser.add_argument('-U', '--uncompressed', action='store_true', + help='Output raw uncompressed data for compressed entries') + + replace_parser = subparsers.add_parser('replace', + help='Replace entries in an image') + replace_parser.add_argument('-C', '--compressed', action='store_true', + help='Input data is already compressed if needed for the entry') + replace_parser.add_argument('-i', '--image', type=str, required=True, + help='Image filename to extract') + replace_parser.add_argument('-f', '--filename', type=str, + help='Input filename to read from') + replace_parser.add_argument('-F', '--fix-size', action='store_true', + help="Don't allow entries to be resized") + replace_parser.add_argument('-I', '--indir', type=str, default='', + help='Path to directory to use for input files') + replace_parser.add_argument('-m', '--map', action='store_true', + default=False, help='Output a map file for the updated image') + replace_parser.add_argument('paths', type=str, nargs='*', + help='Paths within file to extract (wildcard)') + + test_parser = subparsers.add_parser('test', help='Run tests') + test_parser.add_argument('-P', '--processes', type=int, + help='set number of processes to use for running tests') + test_parser.add_argument('-T', '--test-coverage', action='store_true', + default=False, help='run tests and check for 100%% coverage') + test_parser.add_argument('-X', '--test-preserve-dirs', action='store_true', + help='Preserve and display test-created input directories; also ' + 'preserve the output directory if a single test is run (pass test ' + 'name at the end of the command line') + test_parser.add_argument('tests', nargs='*', + help='Test names to run (omit for all)') + + return parser.parse_args(argv) diff --git a/roms/u-boot/tools/binman/control.py b/roms/u-boot/tools/binman/control.py new file mode 100644 index 000000000..f57e34daa --- /dev/null +++ b/roms/u-boot/tools/binman/control.py @@ -0,0 +1,650 @@ +# 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 glob +import os +import pkg_resources +import re + +import sys +from patman import tools + +from binman import cbfs_util +from binman import elf +from patman import command +from patman import tout + +# List of images we plan to create +# Make this global so that it can be referenced from tests +images = OrderedDict() + +# Help text for each type of missing blob, dict: +# key: Value of the entry's 'missing-msg' or entry name +# value: Text for the help +missing_blob_help = {} + +def _ReadImageDesc(binman_node, use_expanded): + """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 + use_expanded: True if the FDT will be updated with the entry information + 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, + use_expanded=use_expanded) + else: + images['image'] = Image('image', binman_node, use_expanded=use_expanded) + 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 _ReadMissingBlobHelp(): + """Read the missing-blob-help file + + This file containins help messages explaining what to do when external blobs + are missing. + + Returns: + Dict: + key: Message tag (str) + value: Message text (str) + """ + + def _FinishTag(tag, msg, result): + if tag: + result[tag] = msg.rstrip() + tag = None + msg = '' + return tag, msg + + my_data = pkg_resources.resource_string(__name__, 'missing-blob-help') + re_tag = re.compile('^([-a-z0-9]+):$') + result = {} + tag = None + msg = '' + for line in my_data.decode('utf-8').splitlines(): + if not line.startswith('#'): + m_tag = re_tag.match(line) + if m_tag: + _, msg = _FinishTag(tag, msg, result) + tag = m_tag.group(1) + elif tag: + msg += line + '\n' + _FinishTag(tag, msg, result) + return result + +def _ShowBlobHelp(path, text): + tout.Warning('\n%s:' % path) + for line in text.splitlines(): + tout.Warning(' %s' % line) + +def _ShowHelpForMissingBlobs(missing_list): + """Show help for each missing blob to help the user take action + + Args: + missing_list: List of Entry objects to show help for + """ + global missing_blob_help + + if not missing_blob_help: + missing_blob_help = _ReadMissingBlobHelp() + + for entry in missing_list: + tags = entry.GetHelpTags() + + # Show the first match help message + for tag in tags: + if tag in missing_blob_help: + _ShowBlobHelp(entry._node.path, missing_blob_help[tag]) + break + +def GetEntryModules(include_testing=True): + """Get a set of entry class implementations + + Returns: + Set of paths to entry class filenames + """ + glob_list = pkg_resources.resource_listdir(__name__, 'etype') + glob_list = [fname for fname in glob_list if fname.endswith('.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 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 binman.entry import Entry + Entry.WriteDocs(modules, test_missing) + + +def ListEntries(image_fname, entry_paths): + """List the entries in an image + + This decodes the supplied image and displays a table of entries from that + image, preceded by a header. + + Args: + image_fname: Image filename to process + entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*', + 'section/u-boot']) + """ + image = Image.FromFile(image_fname) + + entries, lines, widths = image.GetListEntries(entry_paths) + + num_columns = len(widths) + for linenum, line in enumerate(lines): + if linenum == 1: + # Print header line + print('-' * (sum(widths) + num_columns * 2)) + out = '' + for i, item in enumerate(line): + width = -widths[i] + if item.startswith('>'): + width = -width + item = item[1:] + txt = '%*s ' % (width, item) + out += txt + print(out.rstrip()) + + +def ReadEntry(image_fname, entry_path, decomp=True): + """Extract an entry from an image + + This extracts the data from a particular entry in an image + + Args: + image_fname: Image filename to process + entry_path: Path to entry to extract + decomp: True to return uncompressed data, if the data is compress + False to return the raw data + + Returns: + data extracted from the entry + """ + global Image + from binman.image import Image + + image = Image.FromFile(image_fname) + entry = image.FindEntryPath(entry_path) + return entry.ReadData(decomp) + + +def ExtractEntries(image_fname, output_fname, outdir, entry_paths, + decomp=True): + """Extract the data from one or more entries and write it to files + + Args: + image_fname: Image filename to process + output_fname: Single output filename to use if extracting one file, None + otherwise + outdir: Output directory to use (for any number of files), else None + entry_paths: List of entry paths to extract + decomp: True to decompress the entry data + + Returns: + List of EntryInfo records that were written + """ + image = Image.FromFile(image_fname) + + # Output an entry to a single file, as a special case + if output_fname: + if not entry_paths: + raise ValueError('Must specify an entry path to write with -f') + if len(entry_paths) != 1: + raise ValueError('Must specify exactly one entry path to write with -f') + entry = image.FindEntryPath(entry_paths[0]) + data = entry.ReadData(decomp) + tools.WriteFile(output_fname, data) + tout.Notice("Wrote %#x bytes to file '%s'" % (len(data), output_fname)) + return + + # Otherwise we will output to a path given by the entry path of each entry. + # This means that entries will appear in subdirectories if they are part of + # a sub-section. + einfos = image.GetListEntries(entry_paths)[0] + tout.Notice('%d entries match and will be written' % len(einfos)) + for einfo in einfos: + entry = einfo.entry + data = entry.ReadData(decomp) + path = entry.GetPath()[1:] + fname = os.path.join(outdir, path) + + # If this entry has children, create a directory for it and put its + # data in a file called 'root' in that directory + if entry.GetEntries(): + if fname and not os.path.exists(fname): + os.makedirs(fname) + fname = os.path.join(fname, 'root') + tout.Notice("Write entry '%s' size %x to '%s'" % + (entry.GetPath(), len(data), fname)) + tools.WriteFile(fname, data) + return einfos + + +def BeforeReplace(image, allow_resize): + """Handle getting an image ready for replacing entries in it + + Args: + image: Image to prepare + """ + state.PrepareFromLoadedData(image) + image.LoadData() + + # If repacking, drop the old offset/size values except for the original + # ones, so we are only left with the constraints. + if allow_resize: + image.ResetForPack() + + +def ReplaceOneEntry(image, entry, data, do_compress, allow_resize): + """Handle replacing a single entry an an image + + Args: + image: Image to update + entry: Entry to write + data: Data to replace with + do_compress: True to compress the data if needed, False if data is + already compressed so should be used as is + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + """ + if not entry.WriteData(data, do_compress): + if not image.allow_repack: + entry.Raise('Entry data size does not match, but allow-repack is not present for this image') + if not allow_resize: + entry.Raise('Entry data size does not match, but resize is disabled') + + +def AfterReplace(image, allow_resize, write_map): + """Handle write out an image after replacing entries in it + + Args: + image: Image to write + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + write_map: True to write a map file + """ + tout.Info('Processing image') + ProcessImage(image, update_fdt=True, write_map=write_map, + get_contents=False, allow_resize=allow_resize) + + +def WriteEntryToImage(image, entry, data, do_compress=True, allow_resize=True, + write_map=False): + BeforeReplace(image, allow_resize) + tout.Info('Writing data to %s' % entry.GetPath()) + ReplaceOneEntry(image, entry, data, do_compress, allow_resize) + AfterReplace(image, allow_resize=allow_resize, write_map=write_map) + + +def WriteEntry(image_fname, entry_path, data, do_compress=True, + allow_resize=True, write_map=False): + """Replace an entry in an image + + This replaces the data in a particular entry in an image. This size of the + new data must match the size of the old data unless allow_resize is True. + + Args: + image_fname: Image filename to process + entry_path: Path to entry to extract + data: Data to replace with + do_compress: True to compress the data if needed, False if data is + already compressed so should be used as is + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + write_map: True to write a map file + + Returns: + Image object that was updated + """ + tout.Info("Write entry '%s', file '%s'" % (entry_path, image_fname)) + image = Image.FromFile(image_fname) + entry = image.FindEntryPath(entry_path) + WriteEntryToImage(image, entry, data, do_compress=do_compress, + allow_resize=allow_resize, write_map=write_map) + + return image + + +def ReplaceEntries(image_fname, input_fname, indir, entry_paths, + do_compress=True, allow_resize=True, write_map=False): + """Replace the data from one or more entries from input files + + Args: + image_fname: Image filename to process + input_fname: Single input ilename to use if replacing one file, None + otherwise + indir: Input directory to use (for any number of files), else None + entry_paths: List of entry paths to extract + do_compress: True if the input data is uncompressed and may need to be + compressed if the entry requires it, False if the data is already + compressed. + write_map: True to write a map file + + Returns: + List of EntryInfo records that were written + """ + image = Image.FromFile(image_fname) + + # Replace an entry from a single file, as a special case + if input_fname: + if not entry_paths: + raise ValueError('Must specify an entry path to read with -f') + if len(entry_paths) != 1: + raise ValueError('Must specify exactly one entry path to write with -f') + entry = image.FindEntryPath(entry_paths[0]) + data = tools.ReadFile(input_fname) + tout.Notice("Read %#x bytes from file '%s'" % (len(data), input_fname)) + WriteEntryToImage(image, entry, data, do_compress=do_compress, + allow_resize=allow_resize, write_map=write_map) + return + + # Otherwise we will input from a path given by the entry path of each entry. + # This means that files must appear in subdirectories if they are part of + # a sub-section. + einfos = image.GetListEntries(entry_paths)[0] + tout.Notice("Replacing %d matching entries in image '%s'" % + (len(einfos), image_fname)) + + BeforeReplace(image, allow_resize) + + for einfo in einfos: + entry = einfo.entry + if entry.GetEntries(): + tout.Info("Skipping section entry '%s'" % entry.GetPath()) + continue + + path = entry.GetPath()[1:] + fname = os.path.join(indir, path) + + if os.path.exists(fname): + tout.Notice("Write entry '%s' from file '%s'" % + (entry.GetPath(), fname)) + data = tools.ReadFile(fname) + ReplaceOneEntry(image, entry, data, do_compress, allow_resize) + else: + tout.Warning("Skipping entry '%s' from missing file '%s'" % + (entry.GetPath(), fname)) + + AfterReplace(image, allow_resize=allow_resize, write_map=write_map) + return image + + +def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): + """Prepare the images to be processed and select the device tree + + This function: + - reads in the device tree + - finds and scans the binman node to create all entries + - selects which images to build + - Updates the device tress with placeholder properties for offset, + image-pos, etc. + + Args: + dtb_fname: Filename of the device tree file to use (.dts or .dtb) + selected_images: List of images to output, or None for all + update_fdt: True to update the FDT wth entry offsets, etc. + use_expanded: True to use expanded versions of entries, if available. + So if 'u-boot' is called for, we use 'u-boot-expanded' instead. This + is needed if update_fdt is True (although tests may disable it) + + Returns: + OrderedDict of images: + key: Image name (str) + value: Image object + """ + # Import these here in case libfdt.py is not available, in which case + # the above help option still works. + from dtoc import fdt + from dtoc import fdt_util + global images + + # 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, use_expanded) + + if select_images: + skip = [] + new_images = OrderedDict() + for name, image in images.items(): + if name in select_images: + new_images[name] = image + else: + skip.append(name) + images = new_images + tout.Notice('Skipping images: %s' % ', '.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 update_fdt: + image.AddMissingProperties(True) + image.ProcessFdt(dtb) + + for dtb_item in state.GetAllFdts(): + dtb_item.Sync(auto_resize=True) + dtb_item.Pack() + dtb_item.Flush() + return images + + +def ProcessImage(image, update_fdt, write_map, get_contents=True, + allow_resize=True, allow_missing=False): + """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. + + Args: + image: Image to process + update_fdt: True to update the FDT wth entry offsets, etc. + write_map: True to write a map file + get_contents: True to get the image contents from files, etc., False if + the contents is already present + allow_resize: True to allow entries to change size (this does a re-pack + of the entries), False to raise an exception + allow_missing: Allow blob_ext objects to be missing + + Returns: + True if one or more external blobs are missing, False if all are present + """ + if get_contents: + image.SetAllowMissing(allow_missing) + image.GetEntryContents() + image.GetEntryOffsets() + + # We need to pack the entries to figure out where everything + # should be placed. This sets the offset/size of each entry. + # However, after packing we call ProcessEntryContents() which + # may result in an entry changing size. In that case we need to + # do another pass. Since the device tree often contains the + # final offset/size information we try to make space for this in + # AddMissingProperties() above. However, if the device is + # compressed we cannot know this compressed size in advance, + # since changing an offset from 0x100 to 0x104 (for example) can + # alter the compressed size of the device tree. So we need a + # third pass for this. + passes = 5 + for pack_pass in range(passes): + try: + image.PackEntries() + except Exception as e: + if write_map: + fname = image.WriteMap() + print("Wrote map file '%s' to show errors" % fname) + raise + image.SetImagePos() + if update_fdt: + image.SetCalculatedProperties() + for dtb_item in state.GetAllFdts(): + dtb_item.Sync() + dtb_item.Flush() + image.WriteSymbols() + sizes_ok = image.ProcessEntryContents() + if sizes_ok: + break + image.ResetForPack() + tout.Info('Pack completed after %d pass(es)' % (pack_pass + 1)) + if not sizes_ok: + image.Raise('Entries changed size after packing (tried %s passes)' % + passes) + + image.BuildImage() + if write_map: + image.WriteMap() + missing_list = [] + image.CheckMissing(missing_list) + if missing_list: + tout.Warning("Image '%s' is missing external blobs and is non-functional: %s" % + (image.name, ' '.join([e.name for e in missing_list]))) + _ShowHelpForMissingBlobs(missing_list) + return bool(missing_list) + + +def Binman(args): + """The main control code for binman + + This assumes that help and test options have already been dealt with. It + deals with the core task of building images. + + Args: + args: Command line arguments Namespace object + """ + global Image + global state + + if args.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.rst') + command.Run(pager, fname) + return 0 + + # Put these here so that we can import this module without libfdt + from binman.image import Image + from binman import state + + if args.cmd in ['ls', 'extract', 'replace']: + try: + tout.Init(args.verbosity) + tools.PrepareOutputDir(None) + if args.cmd == 'ls': + ListEntries(args.image, args.paths) + + if args.cmd == 'extract': + ExtractEntries(args.image, args.filename, args.outdir, args.paths, + not args.uncompressed) + + if args.cmd == 'replace': + ReplaceEntries(args.image, args.filename, args.indir, args.paths, + do_compress=not args.compressed, + allow_resize=not args.fix_size, write_map=args.map) + except: + raise + finally: + tools.FinaliseOutputDir() + return 0 + + # Try to figure out which device tree contains our image description + if args.dt: + dtb_fname = args.dt + else: + board = args.board + if not board: + raise ValueError('Must provide a board to process (use -b <board>)') + board_pathname = os.path.join(args.build_dir, board) + dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') + if not args.indir: + args.indir = ['.'] + args.indir.append(board_pathname) + + try: + tout.Init(args.verbosity) + elf.debug = args.debug + cbfs_util.VERBOSE = args.verbosity > 2 + state.use_fake_dtb = args.fake_dtb + + # Normally we replace the 'u-boot' etype with 'u-boot-expanded', etc. + # When running tests this can be disabled using this flag. When not + # updating the FDT in image, it is not needed by binman, but we use it + # for consistency, so that the images look the same to U-Boot at + # runtime. + use_expanded = not args.no_expanded + try: + tools.SetInputDirs(args.indir) + tools.PrepareOutputDir(args.outdir, args.preserve) + tools.SetToolPaths(args.toolpath) + state.SetEntryArgs(args.entry_arg) + + images = PrepareImagesAndDtbs(dtb_fname, args.image, + args.update_fdt, use_expanded) + missing = False + for image in images.values(): + missing |= ProcessImage(image, args.update_fdt, args.map, + allow_missing=args.allow_missing) + + # Write the updated FDTs to our output files + for dtb_item in state.GetAllFdts(): + tools.WriteFile(dtb_item._fname, dtb_item.GetContents()) + + if missing: + tout.Warning("\nSome images are invalid") + finally: + tools.FinaliseOutputDir() + finally: + tout.Uninit() + + return 0 diff --git a/roms/u-boot/tools/binman/elf.py b/roms/u-boot/tools/binman/elf.py new file mode 100644 index 000000000..03b49d716 --- /dev/null +++ b/roms/u-boot/tools/binman/elf.py @@ -0,0 +1,303 @@ +# 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 io +import os +import re +import shutil +import struct +import tempfile + +from patman import command +from patman import tools +from patman import tout + +ELF_TOOLS = True +try: + from elftools.elf.elffile import ELFFile + from elftools.elf.sections import SymbolTableSection +except: # pragma: no cover + ELF_TOOLS = False + +Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak']) + +# Information about an ELF file: +# data: Extracted program contents of ELF file (this would be loaded by an +# ELF loader when reading this file +# load: Load address of code +# entry: Entry address of code +# memsize: Number of bytes in memory occupied by loading this ELF file +ElfInfo = namedtuple('ElfInfo', ['data', 'load', 'entry', 'memsize']) + + +def GetSymbols(fname, patterns): + """Get the symbols from an ELF file + + 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 = tools.Run('objdump', '-t', fname) + 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] if parts[2] != '.hidden' else parts[3] + syms[name] = Symbol(section, int(value, 16), int(size,16), + flags[1] == 'w') + + # Sort dict by address + return OrderedDict(sorted(syms.items(), 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.items(): + 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.GetImage().LookupImageSymbol(name, sym.weak, msg, + base.address) + if value is None: + value = -1 + pack_string = pack_string.lower() + value_bytes = struct.pack(pack_string, value) + tout.Debug('%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:]) + +def MakeElf(elf_fname, text, data): + """Make an elf file with the given data in a single section + + The output file has a several section including '.text' and '.data', + containing the info provided in arguments. + + Args: + elf_fname: Output filename + text: Text (code) to put in the file's .text section + data: Data to put in the file's .data section + """ + outdir = tempfile.mkdtemp(prefix='binman.elf.') + s_file = os.path.join(outdir, 'elf.S') + + # Spilt the text into two parts so that we can make the entry point two + # bytes after the start of the text section + text_bytes1 = ['\t.byte\t%#x' % byte for byte in text[:2]] + text_bytes2 = ['\t.byte\t%#x' % byte for byte in text[2:]] + data_bytes = ['\t.byte\t%#x' % byte for byte in data] + with open(s_file, 'w') as fd: + print('''/* Auto-generated C program to produce an ELF file for testing */ + +.section .text +.code32 +.globl _start +.type _start, @function +%s +_start: +%s +.ident "comment" + +.comm fred,8,4 + +.section .empty +.globl _empty +_empty: +.byte 1 + +.globl ernie +.data +.type ernie, @object +.size ernie, 4 +ernie: +%s +''' % ('\n'.join(text_bytes1), '\n'.join(text_bytes2), '\n'.join(data_bytes)), + file=fd) + lds_file = os.path.join(outdir, 'elf.lds') + + # Use a linker script to set the alignment and text address. + with open(lds_file, 'w') as fd: + print('''/* Auto-generated linker script to produce an ELF file for testing */ + +PHDRS +{ + text PT_LOAD ; + data PT_LOAD ; + empty PT_LOAD FLAGS ( 6 ) ; + note PT_NOTE ; +} + +SECTIONS +{ + . = 0xfef20000; + ENTRY(_start) + .text . : SUBALIGN(0) + { + *(.text) + } :text + .data : { + *(.data) + } :data + _bss_start = .; + .empty : { + *(.empty) + } :empty + /DISCARD/ : { + *(.note.gnu.property) + } + .note : { + *(.comment) + } :note + .bss _bss_start (OVERLAY) : { + *(.bss) + } +} +''', file=fd) + # -static: Avoid requiring any shared libraries + # -nostdlib: Don't link with C library + # -Wl,--build-id=none: Don't generate a build ID, so that we just get the + # text section at the start + # -m32: Build for 32-bit x86 + # -T...: Specifies the link script, which sets the start address + cc, args = tools.GetTargetCompileTool('cc') + args += ['-static', '-nostdlib', '-Wl,--build-id=none', '-m32', '-T', + lds_file, '-o', elf_fname, s_file] + stdout = command.Output(cc, *args) + shutil.rmtree(outdir) + +def DecodeElf(data, location): + """Decode an ELF file and return information about it + + Args: + data: Data from ELF file + location: Start address of data to return + + Returns: + ElfInfo object containing information about the decoded ELF file + """ + file_size = len(data) + with io.BytesIO(data) as fd: + elf = ELFFile(fd) + data_start = 0xffffffff; + data_end = 0; + mem_end = 0; + virt_to_phys = 0; + + for i in range(elf.num_segments()): + segment = elf.get_segment(i) + if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: + skipped = 1 # To make code-coverage see this line + continue + start = segment['p_paddr'] + mend = start + segment['p_memsz'] + rend = start + segment['p_filesz'] + data_start = min(data_start, start) + data_end = max(data_end, rend) + mem_end = max(mem_end, mend) + if not virt_to_phys: + virt_to_phys = segment['p_paddr'] - segment['p_vaddr'] + + output = bytearray(data_end - data_start) + for i in range(elf.num_segments()): + segment = elf.get_segment(i) + if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']: + skipped = 1 # To make code-coverage see this line + continue + start = segment['p_paddr'] + offset = 0 + if start < location: + offset = location - start + start = location + # A legal ELF file can have a program header with non-zero length + # but zero-length file size and a non-zero offset which, added + # together, are greater than input->size (i.e. the total file size). + # So we need to not even test in the case that p_filesz is zero. + # Note: All of this code is commented out since we don't have a test + # case for it. + size = segment['p_filesz'] + #if not size: + #continue + #end = segment['p_offset'] + segment['p_filesz'] + #if end > file_size: + #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n', + #file_size, end) + output[start - data_start:start - data_start + size] = ( + segment.data()[offset:]) + return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys, + mem_end - data_start) diff --git a/roms/u-boot/tools/binman/elf_test.py b/roms/u-boot/tools/binman/elf_test.py new file mode 100644 index 000000000..7a128018d --- /dev/null +++ b/roms/u-boot/tools/binman/elf_test.py @@ -0,0 +1,222 @@ +# 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 shutil +import sys +import tempfile +import unittest + +from binman import elf +from patman import command +from patman import test_util +from patman import tools +from patman import tout + +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 = tools.GetBytes(ord('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 LookupImageSymbol(self, name, weak, msg, base_addr): + """Fake implementation which returns the same value for all symbols""" + return self.sym_value + + def GetImage(self): + return self + +def BuildElfTestFiles(target_dir): + """Build ELF files used for testing in binman + + This compiles and links the test files into the specified directory. It the + Makefile and source files in the binman test/ directory. + + Args: + target_dir: Directory to put the files into + """ + if not os.path.exists(target_dir): + os.mkdir(target_dir) + testdir = os.path.join(binman_dir, 'test') + + # If binman is involved from the main U-Boot Makefile the -r and -R + # flags are set in MAKEFLAGS. This prevents this Makefile from working + # correctly. So drop any make flags here. + if 'MAKEFLAGS' in os.environ: + del os.environ['MAKEFLAGS'] + tools.Run('make', '-C', target_dir, '-f', + os.path.join(testdir, 'Makefile'), 'SRC=%s/' % testdir) + + +class TestElf(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls._indir = tempfile.mkdtemp(prefix='elf.') + tools.SetInputDirs(['.']) + BuildElfTestFiles(cls._indir) + + @classmethod + def tearDownClass(cls): + if cls._indir: + shutil.rmtree(cls._indir) + + @classmethod + def ElfTestFile(cls, fname): + return os.path.join(cls._indir, fname) + + def testAllSymbols(self): + """Test that we can obtain a symbol from the ELF file""" + fname = self.ElfTestFile('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 = self.ElfTestFile('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 = self.ElfTestFile('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 = self.ElfTestFile('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 =self.ElfTestFile('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(24) + section = FakeSection(sym_value=None) + elf_fname = self.ElfTestFile('u_boot_binman_syms') + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertEqual(tools.GetBytes(255, 20) + tools.GetBytes(ord('a'), 4), + entry.data) + + def testDebug(self): + """Check that enabling debug in the elf module produced debug output""" + try: + tout.Init(tout.DEBUG) + entry = FakeEntry(20) + section = FakeSection() + elf_fname = self.ElfTestFile('u_boot_binman_syms') + with test_util.capture_sys_output() as (stdout, stderr): + syms = elf.LookupAndWriteSymbols(elf_fname, entry, section) + self.assertTrue(len(stdout.getvalue()) > 0) + finally: + tout.Init(tout.WARNING) + + def testMakeElf(self): + """Test for the MakeElf function""" + outdir = tempfile.mkdtemp(prefix='elf.') + expected_text = b'1234' + expected_data = b'wxyz' + elf_fname = os.path.join(outdir, 'elf') + bin_fname = os.path.join(outdir, 'bin') + + # Make an Elf file and then convert it to a fkat binary file. This + # should produce the original data. + elf.MakeElf(elf_fname, expected_text, expected_data) + objcopy, args = tools.GetTargetCompileTool('objcopy') + args += ['-O', 'binary', elf_fname, bin_fname] + stdout = command.Output(objcopy, *args) + with open(bin_fname, 'rb') as fd: + data = fd.read() + self.assertEqual(expected_text + expected_data, data) + shutil.rmtree(outdir) + + def testDecodeElf(self): + """Test for the MakeElf function""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + outdir = tempfile.mkdtemp(prefix='elf.') + expected_text = b'1234' + expected_data = b'wxyz' + elf_fname = os.path.join(outdir, 'elf') + elf.MakeElf(elf_fname, expected_text, expected_data) + data = tools.ReadFile(elf_fname) + + load = 0xfef20000 + entry = load + 2 + expected = expected_text + expected_data + self.assertEqual(elf.ElfInfo(expected, load, entry, len(expected)), + elf.DecodeElf(data, 0)) + self.assertEqual(elf.ElfInfo(b'\0\0' + expected[2:], + load, entry, len(expected)), + elf.DecodeElf(data, load + 2)) + shutil.rmtree(outdir) + + +if __name__ == '__main__': + unittest.main() diff --git a/roms/u-boot/tools/binman/entries.rst b/roms/u-boot/tools/binman/entries.rst new file mode 100644 index 000000000..dcac700c4 --- /dev/null +++ b/roms/u-boot/tools/binman/entries.rst @@ -0,0 +1,1458 @@ +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: atf-bl31: ARM Trusted Firmware (ATF) BL31 blob +----------------------------------------------------- + +Properties / Entry arguments: + - atf-bl31-path: Filename of file to read into entry. This is typically + called bl31.bin or bl31.elf + +This entry holds the run-time firmware, typically started by U-Boot SPL. +See the U-Boot README for your architecture or board for how to use it. See +https://github.com/ARM-software/arm-trusted-firmware for more information +about ATF. + + + +Entry: blob: 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-ext: Externally built binary blob +--------------------------------------------- + +Note: This should not be used by itself. It is normally used as a parent +class by other entry types. + +If the file providing this blob is missing, binman can optionally ignore it +and produce a broken image with a warning. + +See 'blob' for Properties / Entry arguments. + + + +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 None) + +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: blob-phase: Section that holds a phase binary +---------------------------------------------------- + +This is a base class that should not normally be used directly. It is used +when converting a 'u-boot' entry automatically into a 'u-boot-expanded' +entry; similarly for SPL. + + + +Entry: cbfs: Coreboot Filesystem (CBFS) +--------------------------------------- + +A CBFS provides a way to group files into a group. It has a simple directory +structure and allows the position of individual files to be set, since it is +designed to support execute-in-place in an x86 SPI-flash device. Where XIP +is not used, it supports compression and storing ELF files. + +CBFS is used by coreboot as its way of orgnanising SPI-flash contents. + +The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + +This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb. +Note that the size is required since binman does not support calculating it. +The contents of each entry is just what binman would normally provide if it +were not a CBFS node. A blob type can be used to import arbitrary files as +with the second subnode below:: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-name = "BOOT"; + cbfs-type = "raw"; + }; + + dtb { + type = "blob"; + filename = "u-boot.dtb"; + cbfs-type = "raw"; + cbfs-compress = "lz4"; + cbfs-offset = <0x100000>; + }; + }; + +This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and +u-boot.dtb (named "dtb") and compressed with the lz4 algorithm. + + +Properties supported in the top-level CBFS node: + +cbfs-arch: + Defaults to "x86", but you can specify the architecture if needed. + + +Properties supported in the CBFS entry subnodes: + +cbfs-name: + This is the name of the file created in CBFS. It defaults to the entry + name (which is the node name), but you can override it with this + property. + +cbfs-type: + This is the CBFS file type. The following are supported: + + raw: + This is a 'raw' file, although compression is supported. It can be + used to store any file in CBFS. + + stage: + This is an ELF file that has been loaded (i.e. mapped to memory), so + appears in the CBFS as a flat binary. The input file must be an ELF + image, for example this puts "u-boot" (the ELF image) into a 'stage' + entry:: + + cbfs { + size = <0x100000>; + u-boot-elf { + cbfs-name = "BOOT"; + cbfs-type = "stage"; + }; + }; + + You can use your own ELF file with something like:: + + cbfs { + size = <0x100000>; + something { + type = "blob"; + filename = "cbfs-stage.elf"; + cbfs-type = "stage"; + }; + }; + + As mentioned, the file is converted to a flat binary, so it is + equivalent to adding "u-boot.bin", for example, but with the load and + start addresses specified by the ELF. At present there is no option + to add a flat binary with a load/start address, similar to the + 'add-flat-binary' option in cbfstool. + +cbfs-offset: + This is the offset of the file's data within the CBFS. It is used to + specify where the file should be placed in cases where a fixed position + is needed. Typical uses are for code which is not relocatable and must + execute in-place from a particular address. This works because SPI flash + is generally mapped into memory on x86 devices. The file header is + placed before this offset so that the data start lines up exactly with + the chosen offset. If this property is not provided, then the file is + placed in the next available spot. + +The current implementation supports only a subset of CBFS features. It does +not support other file types (e.g. payload), adding multiple files (like the +'files' entry with a pattern supported by binman), putting files at a +particular offset in the CBFS and a few other things. + +Of course binman can create images containing multiple CBFSs, simply by +defining these in the binman config:: + + + binman { + size = <0x800000>; + cbfs { + offset = <0x100000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + + cbfs2 { + offset = <0x700000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + image { + type = "blob"; + filename = "image.jpg"; + }; + }; + }; + +This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB, +both of size 1MB. + + + +Entry: collection: An entry which contains a collection of other entries +------------------------------------------------------------------------ + +Properties / Entry arguments: + - content: List of phandles to entries to include + +This allows reusing the contents of other entries. The contents of the +listed entries are combined to form this entry. This serves as a useful +base class for entry types which need to process data from elsewhere in +the image, not necessarily child entries. + + + +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: fdtmap: An entry which contains an FDT map +------------------------------------------------- + +Properties / Entry arguments: + None + +An FDT map is just a header followed by an FDT containing a list of all the +entries in the image. The root node corresponds to the image node in the +original FDT, and an image-name property indicates the image name in that +original tree. + +The header is the string _FDTMAP_ followed by 8 unused bytes. + +When used, this entry will be populated with an FDT map which reflects the +entries in the current image. Hierarchy is preserved, and all offsets and +sizes are included. + +Note that the -u option must be provided to ensure that binman updates the +FDT with the position of each entry. + +Example output for a simple image with U-Boot and an FDT map:: + + / { + image-name = "binman"; + size = <0x00000112>; + image-pos = <0x00000000>; + offset = <0x00000000>; + u-boot { + size = <0x00000004>; + image-pos = <0x00000000>; + offset = <0x00000000>; + }; + fdtmap { + size = <0x0000010e>; + image-pos = <0x00000004>; + offset = <0x00000004>; + }; + }; + +If allow-repack is used then 'orig-offset' and 'orig-size' properties are +added as necessary. See the binman README. + + + +Entry: files: A set of files arranged in a section +-------------------------------------------------- + +Properties / Entry arguments: + - pattern: Filename pattern to match the files to include + - files-compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + - files-align: Align each file to the given alignment + +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: fit: Flat Image Tree (FIT) +--------------------------------- + +This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the +input provided. + +Nodes for the FIT should be written out in the binman configuration just as +they would be in a file passed to mkimage. + +For example, this creates an image containing a FIT with U-Boot SPL:: + + binman { + fit { + description = "Test FIT"; + fit,fdt-list = "of-list"; + + images { + kernel@1 { + description = "SPL"; + os = "u-boot"; + type = "rkspi"; + arch = "arm"; + compression = "none"; + load = <0>; + entry = <0>; + + u-boot-spl { + }; + }; + }; + }; + }; + +U-Boot supports creating fdt and config nodes automatically. To do this, +pass an of-list property (e.g. -a of-list=file1 file2). This tells binman +that you want to generates nodes for two files: file1.dtb and file2.dtb +The fit,fdt-list property (see above) indicates that of-list should be used. +If the property is missing you will get an error. + +Then add a 'generator node', a node with a name starting with '@':: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + }; + }; + +This tells binman to create nodes fdt-1 and fdt-2 for each of your two +files. All the properties you specify will be included in the node. This +node acts like a template to generate the nodes. The generator node itself +does not appear in the output - it is replaced with what binman generates. + +You can create config nodes in a similar way:: + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "NAME"; + firmware = "atf"; + loadables = "uboot"; + fdt = "fdt-SEQ"; + }; + }; + +This tells binman to create nodes config-1 and config-2, i.e. a config for +each of your two files. + +Available substitutions for '@' nodes are: + +SEQ: + Sequence number of the generated fdt (1, 2, ...) +NAME + Name of the dtb as provided (i.e. without adding '.dtb') + +Note that if no devicetree files are provided (with '-a of-list' as above) +then no nodes will be generated. + +The 'default' property, if present, will be automatically set to the name +if of configuration whose devicetree matches the 'default-dt' entry +argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'. + +Available substitutions for '@' property values are + +DEFAULT-SEQ: + Sequence number of the default fdt,as provided by the 'default-dt' entry + argument + +Properties (in the 'fit' node itself): + fit,external-offset: Indicates that the contents of the FIT are external + and provides the external offset. This is passsed to mkimage via + the -E and -p flags. + + + + +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. Sections are represented as an area appearing +before its contents, so that it is possible to reconstruct the hierarchy +from the FMAP by using the offset information. This convention does not +seem to be documented, but is used in Chromium OS. + +CBFS entries appear as a single entry, i.e. the sub-entries are ignored. + + + +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: image-header: An entry which contains a pointer to the FDT map +--------------------------------------------------------------------- + +Properties / Entry arguments: + location: Location of header ("start" or "end" of image). This is + optional. If omitted then the entry must have an offset property. + +This adds an 8-byte entry to the start or end of the image, pointing to the +location of the FDT map. The format is a magic number followed by an offset +from the start or end of the image, in twos-compliment format. + +This entry must be in the top-level part of the image. + +NOTE: If the location is at the start/end, you will probably need to specify +sort-by-offset for the image, unless you actually put the image header +first/last in the entry list. + + + +Entry: intel-cmc: 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-fit: Intel Firmware Image Table (FIT) +-------------------------------------------------- + +This entry contains a dummy FIT as required by recent Intel CPUs. The FIT +contains information about the firmware and microcode available in the +image. + +At present binman only supports a basic FIT with no microcode. + + + +Entry: intel-fit-ptr: Intel Firmware Image Table (FIT) pointer +-------------------------------------------------------------- + +This entry contains a pointer to the FIT. It is required to be at address +0xffffffc0 in the image. + + + +Entry: intel-fsp: 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-fsp-m: Intel Firmware Support Package (FSP) memory init +-------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains a binary blob which is used on some devices to set up +SDRAM. U-Boot executes this code in SPL so that it can make full use of +memory. Documentation is typically not available in sufficient detail to +allow U-Boot do this this itself.. + +An example filename is 'fsp_m.bin' + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-fsp-s: Intel Firmware Support Package (FSP) silicon init +--------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains a binary blob which is used on some devices to set up +the silicon. U-Boot executes this code in U-Boot proper after SDRAM is +running, so that it can make full use of memory. Documentation is typically +not available in sufficient detail to allow U-Boot do this this itself. + +An example filename is 'fsp_s.bin' + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-fsp-t: Intel Firmware Support Package (FSP) temp ram init +---------------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry + +This file contains a binary blob which is used on some devices to set up +temporary memory (Cache-as-RAM or CAR). U-Boot executes this code in TPL so +that it has access to memory for its stack and initial storage. + +An example filename is 'fsp_t.bin' + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-ifwi: Intel Integrated Firmware Image (IFWI) file +-------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of file to read into entry. This is either the + IFWI file itself, or a file that can be converted into one using a + tool + - convert-fit: If present this indicates that the ifwitool should be + used to convert the provided file into a IFWI. + +This file contains code and data used by the SoC that is required to make +it work. It includes U-Boot TPL, microcode, things related to the CSE +(Converged Security Engine, the microcontroller that loads all the firmware) +and other items beyond the wit of man. + +A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a +file that will be converted to an IFWI. + +The position of this entry is generally set by the intel-descriptor entry. + +The contents of the IFWI are specified by the subnodes of the IFWI node. +Each subnode describes an entry which is placed into the IFWFI with a given +sub-partition (and optional entry name). + +Properties for subnodes: + - ifwi-subpart: sub-parition to put this entry into, e.g. "IBBP" + - ifwi-entry: entry name t use, e.g. "IBBL" + - ifwi-replace: if present, indicates that the item should be replaced + in the IFWI. Otherwise it is added. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-me: 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, display 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'. + +The position of this entry is generally set by the intel-descriptor entry. + +See README.x86 for information about x86 binary blobs. + + + +Entry: intel-mrc: 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-refcode: 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. + + + +Entry: intel-vbt: 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: 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: mkimage: Binary produced by mkimage +------------------------------------------ + +Properties / Entry arguments: + - datafile: Filename for -d argument + - args: Other arguments to pass + +The data passed to mkimage is collected from subnodes of the mkimage node, +e.g.:: + + mkimage { + args = "-n test -T imximage"; + + u-boot-spl { + }; + }; + +This calls mkimage to create an imximage with u-boot-spl.bin as the input +file. The output from mkimage then becomes part of the image produced by +binman. + + + +Entry: opensbi: RISC-V OpenSBI fw_dynamic blob +---------------------------------------------- + +Properties / Entry arguments: + - opensbi-path: Filename of file to read into entry. This is typically + called fw_dynamic.bin + +This entry holds the run-time firmware, typically started by U-Boot SPL. +See the U-Boot README for your architecture or board for how to use it. See +https://github.com/riscv/opensbi for more information about OpenSBI. + + + +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 entry 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: scp: System Control Processor (SCP) firmware blob +-------------------------------------------------------- + +Properties / Entry arguments: + - scp-path: Filename of file to read into the entry, typically scp.bin + +This entry holds firmware for an external platform-specific coprocessor. + + + +Entry: section: Entry that contains other entries +------------------------------------------------- + +Properties / Entry arguments: (see binman README for more information): + pad-byte: Pad byte to use when padding + sort-by-offset: True if entries should be sorted by offset, False if + they must be in-order in the device tree description + + end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + + skip-at-start: Number of bytes before the first entry starts. These + effectively adjust the starting offset of entries. For example, + if this is 16, then the first entry would start at 16. An entry + with offset = 20 would in fact be written at offset 4 in the image + file, since the first 16 bytes are skipped when writing. + name-prefix: Adds a prefix to the name of every entry in the section + when writing out the map + align_default: Default alignment for this section, if no alignment is + given in the entry + +Properties: + allow_missing: True if this section permits external blobs to be + missing their contents. The second will produce an image but of + course it will not work. + +Properties: + _allow_missing: True if this section permits external blobs to be + missing their contents. The second will produce an image but of + course it will not work. + +Since a section is also an entry, it inherits all the properies of entries +too. + +A section is an entry which can contain other entries, thus allowing +hierarchical images to be created. See 'Sections and hierarchical images' +in the binman README for more information. + + + +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. + <text>: The text to place in the entry (overrides the above mechanism). + This is useful when the text is constant. + +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" + }; + +or just:: + + text { + size = <8>; + text = "some text directly in the node" + }; + +The text is not itself nul-terminated. This can be achieved, if required, +by setting the size of the entry to something larger than the text. + + + +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. + +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. + +Note that this entry is automatically replaced with u-boot-expanded unless +--no-expanded is used or the node has a 'no-expanded' property. + + + +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-env: An entry which contains a U-Boot environment +--------------------------------------------------------------- + +Properties / Entry arguments: + - filename: File containing the environment text, with each line in the + form var=value + + + +Entry: u-boot-expanded: U-Boot flat binary broken out into its component parts +------------------------------------------------------------------------------ + +This is a section containing the U-Boot binary and a devicetree. Using this +entry type automatically creates this section, with the following entries +in it: + + u-boot-nodtb + u-boot-dtb + +Having the devicetree separate allows binman to update it in the final +image, so that the entries positions are provided to the running U-Boot. + + + +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 to include (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, normally expands to a +section containing u-boot and u-boot-dtb + + + +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. + +Note that this entry is automatically replaced with u-boot-spl-expanded +unless --no-expanded is used or the node has a 'no-expanded' property. + + + +Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region +--------------------------------------------------------------------- + +Properties / Entry arguments: + None + +This holds the padding added after the SPL binary to cover the BSS (Block +Started by Symbol) region. This region holds the various variables 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 contents of this entry will a certain number of zero bytes, determined +by __bss_size + +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-spl') + +This is the U-Boot SPL ELF image. It does not include a device tree but can +be relocated to any address for execution. + + + +Entry: u-boot-spl-expanded: U-Boot SPL flat binary broken out into its component parts +-------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - spl-dtb: Controls whether this entry is selected (set to 'y' or '1' to + select) + +This is a section containing the U-Boot binary, BSS padding if needed and a +devicetree. Using this entry type automatically creates this section, with +the following entries in it: + + u-boot-spl-nodtb + u-boot-spl-bss-pad + u-boot-dtb + +Having the devicetree separate allows binman to update it in the final +image, so that the entries positions are provided to the running U-Boot. + +This entry is selected based on the value of the 'spl-dtb' entryarg. If +this is non-empty (and not 'n' or '0') then this expanded entry is selected. + + + +Entry: u-boot-spl-nodtb: SPL binary without device tree appended +---------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename to include (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 operate on your platform. You can add a u-boot-spl-dtb +entry after this one, or use a u-boot-spl entry instead' which normally +expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and +u-boot-spl-dtb + +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-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. + +Note that this entry is automatically replaced with u-boot-tpl-expanded +unless --no-expanded is used or the node has a 'no-expanded' property. + + + +Entry: u-boot-tpl-bss-pad: U-Boot TPL binary padded with a BSS region +--------------------------------------------------------------------- + +Properties / Entry arguments: + None + +This holds the padding added after the TPL binary to cover the BSS (Block +Started by Symbol) region. This region holds the various variables used by +TPL. It is set to 0 by TPL when it starts up. If you want to append data to +the TPL 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 contents of this entry will a certain number of zero bytes, determined +by __bss_size + +The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since +binman uses that to look up the BSS address. + + + +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-elf: U-Boot TPL ELF image +------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of TPL u-boot (default 'tpl/u-boot-tpl') + +This is the U-Boot TPL ELF image. It does not include a device tree but can +be relocated to any address for execution. + + + +Entry: u-boot-tpl-expanded: U-Boot TPL flat binary broken out into its component parts +-------------------------------------------------------------------------------------- + +Properties / Entry arguments: + - tpl-dtb: Controls whether this entry is selected (set to 'y' or '1' to + select) + +This is a section containing the U-Boot binary, BSS padding if needed and a +devicetree. Using this entry type automatically creates this section, with +the following entries in it: + + u-boot-tpl-nodtb + u-boot-tpl-bss-pad + u-boot-dtb + +Having the devicetree separate allows binman to update it in the final +image, so that the entries positions are provided to the running U-Boot. + +This entry is selected based on the value of the 'tpl-dtb' entryarg. If +this is non-empty (and not 'n' or '0') then this expanded entry is selected. + + + +Entry: u-boot-tpl-nodtb: TPL binary without device tree appended +---------------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename to include (default 'tpl/u-boot-tpl-nodtb.bin') + +This is the U-Boot TPL binary, It does not include a device tree blob at +the end of it so may not be able to work without it, assuming TPL needs +a device tree to operate on your platform. You can add a u-boot-tpl-dtb +entry after this one, or use a u-boot-tpl entry instead, which normally +expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and +u-boot-tpl-dtb + +TPL 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-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.bin (default 'u-boot-nodtb.bin') + - 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: + - content: List of phandles to entries to sign + - 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-reset16: x86 16-bit reset code for U-Boot +---------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible +for jumping to the x86-start16 code, which continues execution. + +For 64-bit U-Boot, the 'x86_reset16_spl' entry type is used instead. + + + +Entry: x86-reset16-spl: x86 16-bit reset code for U-Boot +-------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible +for jumping to the x86-start16 code, which continues execution. + +For 32-bit U-Boot, the 'x86_reset_spl' entry type is used instead. + + + +Entry: x86-reset16-tpl: x86 16-bit reset code for U-Boot +-------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible +for jumping to the x86-start16 code, which continues execution. + +For 32-bit U-Boot, the 'x86_reset_tpl' entry type is used instead. + + + +Entry: x86-start16: x86 16-bit start-up code for U-Boot +------------------------------------------------------- + +Properties / Entry arguments: + - filename: Filename of u-boot-x86-start16.bin (default + 'u-boot-x86-start16.bin') + +x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code +must be placed in the top 64KB of the ROM. The reset code jumps to it. 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-start16-spl.bin (default + 'spl/u-boot-x86-start16-spl.bin') + +x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code +must be placed in the top 64KB of the ROM. The reset code jumps to it. 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 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-start16-tpl.bin (default + 'tpl/u-boot-x86-start16-tpl.bin') + +x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code +must be placed in the top 64KB of the ROM. The reset code jumps to it. 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). + +If TPL is not being used, the 'x86-start16-spl or 'x86-start16' entry types +may be used instead. + + + diff --git a/roms/u-boot/tools/binman/entry.py b/roms/u-boot/tools/binman/entry.py new file mode 100644 index 000000000..70222718e --- /dev/null +++ b/roms/u-boot/tools/binman/entry.py @@ -0,0 +1,963 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# +# Base class for all entries +# + +from collections import namedtuple +import importlib +import os +import sys + +from dtoc import fdt_util +from patman import tools +from patman.tools import ToHex, ToHexSize +from patman import tout + +modules = {} + + +# An argument which can be passed to entries on the command line, in lieu of +# device-tree properties. +EntryArg = namedtuple('EntryArg', ['name', 'datatype']) + +# Information about an entry for use when displaying summaries +EntryInfo = namedtuple('EntryInfo', ['indent', 'name', 'etype', 'size', + 'image_pos', 'uncomp_size', 'offset', + 'entry']) + +class Entry(object): + """An Entry in the section + + 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 + pre_reset_size: size as it was before ResetForPack(). This allows us to + keep track of the size we started with and detect size changes + uncomp_size: Size of uncompressed data in bytes, if the entry is + compressed, else None + contents_size: Size of contents in bytes, 0 by default + align: Entry start offset alignment relative to the start of the + containing section, or None + align_size: Entry size alignment, or None + align_end: Entry end offset alignment relative to the start of the + containing section, or None + pad_before: Number of pad bytes before the contents when it is placed + in the containing section, 0 if none. The pad bytes become part of + the entry. + pad_after: Number of pad bytes after the contents when it is placed in + the containing section, 0 if none. The pad bytes become part of + the entry. + data: Contents of entry (string of bytes). This does not include + padding created by pad_before or pad_after. If the entry is + compressed, this contains the compressed data. + uncomp_data: Original uncompressed data, if this entry is compressed, + else None + compress: Compression algoithm used (e.g. 'lz4'), 'none' if none + orig_offset: Original offset value read from node + orig_size: Original size value read from node + missing: True if this entry is missing its contents + allow_missing: Allow children of this entry to be missing (used by + subclasses such as Entry_section) + external: True if this entry contains an external binary blob + """ + def __init__(self, section, etype, node, name_prefix=''): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + 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.pre_reset_size = None + self.uncomp_size = None + self.data = None + self.uncomp_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 + self.compress = 'none' + self.missing = False + self.external = False + self.allow_missing = False + + @staticmethod + def Lookup(node_path, etype, expanded): + """Look up the entry class for a node. + + Args: + node_node: Path name of Node object containing information about + the entry to create (used for errors) + etype: Entry type to use + expanded: Use the expanded version of etype + + Returns: + The entry class object if found, else None if not found and expanded + is True + + Raise: + ValueError if expanded is False and the class is not found + """ + # 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] + if expanded: + module_name += '_expanded' + 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: + try: + module = importlib.import_module('binman.etype.' + module_name) + except ImportError as e: + if expanded: + return None + raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" % + (etype, node_path, module_name, e)) + modules[module_name] = module + + # Look up the expected class name + return getattr(module, 'Entry_%s' % module_name) + + @staticmethod + def Create(section, node, etype=None, expanded=False): + """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) + expanded: True to use expanded versions of entries, where available + + 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(node.path, etype, expanded) + if obj and expanded: + # Check whether to use the expanded entry + new_etype = etype + '-expanded' + can_expand = not fdt_util.GetBool(node, 'no-expanded') + if can_expand and obj.UseExpanded(node, etype, new_etype): + etype = new_etype + else: + obj = None + if not obj: + obj = Entry.Lookup(node.path, etype, False) + + # Call its constructor to get the object we want. + return obj(section, etype, node) + + def ReadNode(self): + """Read entry information from the node + + This must be called as the first thing after the Entry is created. + + 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.orig_offset = fdt_util.GetInt(self._node, 'orig-offset') + self.orig_size = fdt_util.GetInt(self._node, 'orig-size') + if self.GetImage().copy_to_orig: + self.orig_offset = self.offset + self.orig_size = self.size + + # These should not be set in input files, but are set in an FDT map, + # which is also read by this code. + self.image_pos = fdt_util.GetInt(self._node, 'image-pos') + self.uncomp_size = fdt_util.GetInt(self._node, 'uncomp-size') + + self.align = fdt_util.GetInt(self._node, 'align') + if tools.NotPowerOfTwo(self.align): + raise ValueError("Node '%s': Alignment %s must be a power of two" % + (self._node.path, self.align)) + if self.section and self.align is None: + self.align = self.section.align_default + 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): + self.Raise("Alignment size %s must be a power of two" % + self.align_size) + self.align_end = fdt_util.GetInt(self._node, 'align-end') + self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset') + self.expand_size = fdt_util.GetBool(self._node, 'expand-size') + self.missing_msg = fdt_util.GetString(self._node, 'missing-msg') + + # This is only supported by blobs and sections at present + self.compress = fdt_util.GetString(self._node, 'compress', 'none') + + def GetDefaultFilename(self): + return None + + def GetFdts(self): + """Get the device trees used by this entry + + Returns: + Empty dict, if this entry is not a .dtb, otherwise: + Dict: + key: Filename from this entry (without the path) + value: Tuple: + Entry object for this dtb + Filename of file containing this dtb + """ + return {} + + def ExpandEntries(self): + """Expand out entries which produce other entries + + Some entries generate subnodes automatically, from which sub-entries + are then created. This method allows those to be added to the binman + definition for the current image. An entry which implements this method + should call state.AddSubnode() to add a subnode and can add properties + with state.AddString(), etc. + + An example is 'files', which produces a section containing a list of + files. + """ + pass + + def AddMissingProperties(self, have_image_pos): + """Add new properties to the device tree as needed for this entry + + Args: + have_image_pos: True if this entry has an image position. This can + be False if its parent section is compressed, since compression + groups all entries together into a compressed block of data, + obscuring the start of each individual child entry + """ + for prop in ['offset', 'size']: + if not prop in self._node.props: + state.AddZeroProp(self._node, prop) + if have_image_pos and 'image-pos' not in self._node.props: + state.AddZeroProp(self._node, 'image-pos') + if self.GetImage().allow_repack: + if self.orig_offset is not None: + state.AddZeroProp(self._node, 'orig-offset', True) + if self.orig_size is not None: + state.AddZeroProp(self._node, 'orig-size', True) + + if self.compress != 'none': + state.AddZeroProp(self._node, 'uncomp-size') + 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) + base = self.section.GetRootSkipAtStart() if self.section else 0 + if self.image_pos is not None: + state.SetInt(self._node, 'image-pos', self.image_pos - base) + if self.GetImage().allow_repack: + if self.orig_offset is not None: + state.SetInt(self._node, 'orig-offset', self.orig_offset, True) + if self.orig_size is not None: + state.SetInt(self._node, 'orig-size', self.orig_size, True) + if self.uncomp_size is not None: + state.SetInt(self._node, 'uncomp-size', self.uncomp_size) + state.CheckSetHashValue(self._node, self.GetData) + + def ProcessFdt(self, fdt): + """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 (bytes) + """ + self.data = data + self.contents_size = len(self.data) + + def ProcessContentsUpdate(self, data): + """Update the contents of an entry, after the size is fixed + + This checks that the new data is the same size as the old. If the size + has changed, this triggers a re-run of the packing algorithm. + + Args: + data: Data to set to the contents (bytes) + + Raises: + ValueError if the new data size is not the same as the old + """ + size_ok = True + new_size = len(data) + if state.AllowEntryExpansion() and new_size > self.contents_size: + # self.data will indicate the new size needed + size_ok = False + elif state.AllowEntryContraction() and new_size < self.contents_size: + size_ok = False + + # If not allowed to change, try to deal with it or give up + if size_ok: + if new_size > self.contents_size: + self.Raise('Cannot update entry size from %d to %d' % + (self.contents_size, new_size)) + + # Don't let the data shrink. Pad it if necessary + if size_ok and new_size < self.contents_size: + data += tools.GetBytes(0, self.contents_size - new_size) + + if not size_ok: + tout.Debug("Entry '%s' size change from %s to %s" % ( + self._node.path, ToHex(self.contents_size), + ToHex(new_size))) + self.SetContents(data) + return size_ok + + 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 ResetForPack(self): + """Reset offset/size fields so that packing can be done again""" + self.Detail('ResetForPack: offset %s->%s, size %s->%s' % + (ToHex(self.offset), ToHex(self.orig_offset), + ToHex(self.size), ToHex(self.orig_size))) + self.pre_reset_size = self.size + self.offset = self.orig_offset + self.size = self.orig_size + + 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) + """ + self.Detail('Packing: offset=%s, size=%s, content_size=%x' % + (ToHex(self.offset), ToHex(self.size), + self.contents_size)) + 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)) + self.Detail(' - packed: offset=%#x, size=%#x, content_size=%#x, next_offset=%x' % + (self.offset, self.size, self.contents_size, new_offset)) + + 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 Info(self, msg): + """Convenience function to log info referencing a node""" + tag = "Info '%s'" % self._node.path + tout.Detail('%30s: %s' % (tag, msg)) + + def Detail(self, msg): + """Convenience function to log detail referencing a node""" + tag = "Node '%s'" % self._node.path + tout.Detail('%30s: %s' % (tag, 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.GetImage().MissingArgs(self, 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, required=True): + """Get the contents of an entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, excluding any padding. If the entry is + compressed, the compressed data is returned + """ + self.Detail('GetData: size %s' % ToHexSize(self.data)) + return self.data + + def GetPaddedData(self, data=None): + """Get the data for an entry including any padding + + Gets the entry data and uses its section's pad-byte value to add padding + before and after as defined by the pad-before and pad-after properties. + + This does not consider alignment. + + Returns: + Contents of the entry along with any pad bytes before and + after it (bytes) + """ + if data is None: + data = self.GetData() + return self.section.GetPaddedDataForEntry(self, data) + + def GetOffsets(self): + """Get the offsets for siblings + + Some entry types can contain information about the position or size of + other entries. An example of this is the Intel Flash Descriptor, which + knows where the Intel Management Engine section should go. + + If this entry knows about the position of other entries, it can specify + this by returning values here + + Returns: + Dict: + key: Entry type + value: List containing position and size of the given entry + type. Either can be None if not known + """ + return {} + + def SetOffsetSize(self, offset, size): + """Set the offset and/or size of an entry + + Args: + offset: New offset, or None to leave alone + size: New size, or None to leave alone + """ + if offset is not None: + self.offset = offset + if size is not None: + self.size = size + + def SetImagePos(self, image_pos): + """Set the position in the image + + Args: + image_pos: Position of this entry in the image + """ + self.image_pos = image_pos + self.offset + + def ProcessContents(self): + """Do any post-packing updates of entry contents + + This function should call ProcessContentsUpdate() to update the entry + contents, if necessary, returning its return value here. + + Args: + data: Data to set to the contents (bytes) + + Returns: + True if the new data size is OK, False if expansion is needed + + Raises: + ValueError if the new data size is not the same as the old and + state.AllowEntryExpansion() is False + """ + return True + + def WriteSymbols(self, section): + """Write symbol values into binary files for access at run time + + Args: + section: Section containing the entry + """ + pass + + def CheckEntries(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('WriteDocs', name, False) + 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') + + def HasSibling(self, name): + """Check if there is a sibling of a given name + + Returns: + True if there is an entry with this name in the the same section, + else False + """ + return name in self.section.GetEntries() + + def GetSiblingImagePos(self, name): + """Return the image position of the given sibling + + Returns: + Image position of sibling, or None if the sibling has no position, + or False if there is no such sibling + """ + if not self.HasSibling(name): + return False + return self.section.GetEntries()[name].image_pos + + @staticmethod + def AddEntryInfo(entries, indent, name, etype, size, image_pos, + uncomp_size, offset, entry): + """Add a new entry to the entries list + + Args: + entries: List (of EntryInfo objects) to add to + indent: Current indent level to add to list + name: Entry name (string) + etype: Entry type (string) + size: Entry size in bytes (int) + image_pos: Position within image in bytes (int) + uncomp_size: Uncompressed size if the entry uses compression, else + None + offset: Entry offset within parent in bytes (int) + entry: Entry object + """ + entries.append(EntryInfo(indent, name, etype, size, image_pos, + uncomp_size, offset, entry)) + + def ListEntries(self, entries, indent): + """Add files in this entry to the list of entries + + This can be overridden by subclasses which need different behaviour. + + Args: + entries: List (of EntryInfo objects) to add to + indent: Current indent level to add to list + """ + self.AddEntryInfo(entries, indent, self.name, self.etype, self.size, + self.image_pos, self.uncomp_size, self.offset, self) + + def ReadData(self, decomp=True): + """Read the data for an entry from the image + + This is used when the image has been read in and we want to extract the + data for a particular entry from that image. + + Args: + decomp: True to decompress any compressed data before returning it; + False to return the raw, uncompressed data + + Returns: + Entry data (bytes) + """ + # Use True here so that we get an uncompressed section to work from, + # although compressed sections are currently not supported + tout.Debug("ReadChildData section '%s', entry '%s'" % + (self.section.GetPath(), self.GetPath())) + data = self.section.ReadChildData(self, decomp) + return data + + def ReadChildData(self, child, decomp=True): + """Read the data for a particular child entry + + This reads data from the parent and extracts the piece that relates to + the given child. + + Args: + child: Child entry to read data for (must be valid) + decomp: True to decompress any compressed data before returning it; + False to return the raw, uncompressed data + + Returns: + Data for the child (bytes) + """ + pass + + def LoadData(self, decomp=True): + data = self.ReadData(decomp) + self.contents_size = len(data) + self.ProcessContentsUpdate(data) + self.Detail('Loaded data size %x' % len(data)) + + def GetImage(self): + """Get the image containing this entry + + Returns: + Image object containing this entry + """ + return self.section.GetImage() + + def WriteData(self, data, decomp=True): + """Write the data to an entry in the image + + This is used when the image has been read in and we want to replace the + data for a particular entry in that image. + + The image must be re-packed and written out afterwards. + + Args: + data: Data to replace it with + decomp: True to compress the data if needed, False if data is + already compressed so should be used as is + + Returns: + True if the data did not result in a resize of this entry, False if + the entry must be resized + """ + if self.size is not None: + self.contents_size = self.size + else: + self.contents_size = self.pre_reset_size + ok = self.ProcessContentsUpdate(data) + self.Detail('WriteData: size=%x, ok=%s' % (len(data), ok)) + section_ok = self.section.WriteChildData(self) + return ok and section_ok + + def WriteChildData(self, child): + """Handle writing the data in a child entry + + This should be called on the child's parent section after the child's + data has been updated. It + + This base-class implementation does nothing, since the base Entry object + does not have any children. + + Args: + child: Child Entry that was written + + Returns: + True if the section could be updated successfully, False if the + data is such that the section could not updat + """ + return True + + def GetSiblingOrder(self): + """Get the relative order of an entry amoung its siblings + + Returns: + 'start' if this entry is first among siblings, 'end' if last, + otherwise None + """ + entries = list(self.section.GetEntries().values()) + if entries: + if self == entries[0]: + return 'start' + elif self == entries[-1]: + return 'end' + return 'middle' + + def SetAllowMissing(self, allow_missing): + """Set whether a section allows missing external blobs + + Args: + allow_missing: True if allowed, False if not allowed + """ + # This is meaningless for anything other than sections + pass + + def CheckMissing(self, missing_list): + """Check if any entries in this section have missing external blobs + + If there are missing blobs, the entries are added to the list + + Args: + missing_list: List of Entry objects to be added to + """ + if self.missing: + missing_list.append(self) + + def GetAllowMissing(self): + """Get whether a section allows missing external blobs + + Returns: + True if allowed, False if not allowed + """ + return self.allow_missing + + def GetHelpTags(self): + """Get the tags use for missing-blob help + + Returns: + list of possible tags, most desirable first + """ + return list(filter(None, [self.missing_msg, self.name, self.etype])) + + def CompressData(self, indata): + """Compress data according to the entry's compression method + + Args: + indata: Data to compress + + Returns: + Compressed data (first word is the compressed size) + """ + self.uncomp_data = indata + if self.compress != 'none': + self.uncomp_size = len(indata) + data = tools.Compress(indata, self.compress) + return data + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + """Check whether to use an expanded entry type + + This is called by Entry.Create() when it finds an expanded version of + an entry type (e.g. 'u-boot-expanded'). If this method returns True then + it will be used (e.g. in place of 'u-boot'). If it returns False, it is + ignored. + + Args: + node: Node object containing information about the entry to + create + etype: Original entry type being used + new_etype: New entry type proposed + + Returns: + True to use this entry type, False to use the original one + """ + tout.Info("Node '%s': etype '%s': %s selected" % + (node.path, etype, new_etype)) + return True diff --git a/roms/u-boot/tools/binman/entry_test.py b/roms/u-boot/tools/binman/entry_test.py new file mode 100644 index 000000000..c3d5f3eef --- /dev/null +++ b/roms/u-boot/tools/binman/entry_test.py @@ -0,0 +1,104 @@ +# 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 + +from binman import entry +from dtoc import fdt +from dtoc import fdt_util +from patman import tools + +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 _ReloadEntry(self): + global entry + if entry: + if sys.version_info[0] >= 3: + import importlib + importlib.reload(entry) + else: + reload(entry) + else: + from binman import entry + + def testEntryContents(self): + """Test the Entry bass class""" + from binman import entry + base_entry = entry.Entry(None, None, None) + self.assertEqual(True, base_entry.ObtainContents()) + + def testUnknownEntry(self): + """Test that unknown entry types are detected""" + Node = collections.namedtuple('Node', ['name', 'path']) + node = Node('invalid-name', 'invalid-path') + with self.assertRaises(ValueError) as e: + entry.Entry.Create(None, node, node.name) + self.assertIn("Unknown entry type 'invalid-name' in node " + "'invalid-path'", str(e.exception)) + + def testUniqueName(self): + """Test Entry.GetUniqueName""" + Node = collections.namedtuple('Node', ['name', 'parent']) + base_node = Node('root', None) + base_entry = entry.Entry(None, None, base_node) + self.assertEqual('root', base_entry.GetUniqueName()) + sub_node = Node('subnode', base_node) + sub_entry = entry.Entry(None, None, sub_node) + self.assertEqual('root.subnode', sub_entry.GetUniqueName()) + + def testGetDefaultFilename(self): + """Trivial test for this base class function""" + base_entry = entry.Entry(None, None, None) + self.assertIsNone(base_entry.GetDefaultFilename()) + + def testBlobFdt(self): + """Test the GetFdtEtype() method of the blob-dtb entries""" + base = entry.Entry.Create(None, self.GetNode(), 'blob-dtb') + self.assertIsNone(base.GetFdtEtype()) + + dtb = entry.Entry.Create(None, self.GetNode(), 'u-boot-dtb') + self.assertEqual('u-boot-dtb', dtb.GetFdtEtype()) + + def testWriteChildData(self): + """Test the WriteChildData() method of the base class""" + base = entry.Entry.Create(None, self.GetNode(), 'blob-dtb') + self.assertTrue(base.WriteChildData(base)) + + def testReadChildData(self): + """Test the ReadChildData() method of the base class""" + base = entry.Entry.Create(None, self.GetNode(), 'blob-dtb') + self.assertIsNone(base.ReadChildData(base)) + + def testExpandedEntry(self): + """Test use of an expanded entry when available""" + base = entry.Entry.Create(None, self.GetNode()) + self.assertEqual('u-boot', base.etype) + + expanded = entry.Entry.Create(None, self.GetNode(), expanded=True) + self.assertEqual('u-boot-expanded', expanded.etype) + + with self.assertRaises(ValueError) as e: + entry.Entry.Create(None, self.GetNode(), 'missing', expanded=True) + self.assertIn("Unknown entry type 'missing' in node '/binman/u-boot'", + str(e.exception)) + +if __name__ == "__main__": + unittest.main() diff --git a/roms/u-boot/tools/binman/etype/_testing.py b/roms/u-boot/tools/binman/etype/_testing.py new file mode 100644 index 000000000..0800c2589 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/_testing.py @@ -0,0 +1,129 @@ +# 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 binman.entry import Entry, EntryArg +from dtoc import fdt_util +from patman 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: Return a larger size in ProcessContents + bad-shrink-contents: Return a larger size in ProcessContents + 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): + super().__init__(section, etype, node) + + def ReadNode(self): + super().ReadNode() + 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.bad_shrink_contents = fdt_util.GetBool(self._node, + 'bad-shrink-contents') + self.return_contents_once = fdt_util.GetBool(self._node, + 'return-contents-once') + self.bad_update_contents_twice = fdt_util.GetBool(self._node, + 'bad-update-contents-twice') + self.return_contents_later = fdt_util.GetBool(self._node, + 'return-contents-later') + + # 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 + self.contents = b'aa' + + def ObtainContents(self): + if self.return_unknown_contents or not self.return_contents: + return False + if self.return_contents_later: + self.return_contents_later = False + return False + self.data = self.contents + 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): + data = self.contents + if self.bad_update_contents: + # Request to update the contents with something larger, to cause a + # failure. + if self.bad_update_contents_twice: + data = self.data + b'a' + else: + data = b'aaa' + return self.ProcessContentsUpdate(data) + if self.bad_shrink_contents: + # Request to update the contents with something smaller, to cause a + # failure. + data = b'a' + return self.ProcessContentsUpdate(data) + if self.bad_shrink_contents: + # Request to update the contents with something smaller, to cause a + # failure. + data = b'a' + return self.ProcessContentsUpdate(data) + return True + + 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/roms/u-boot/tools/binman/etype/atf_bl31.py b/roms/u-boot/tools/binman/etype/atf_bl31.py new file mode 100644 index 000000000..2041da416 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/atf_bl31.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for ARM Trusted Firmware binary blob +# + +from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg + +class Entry_atf_bl31(Entry_blob_named_by_arg): + """ARM Trusted Firmware (ATF) BL31 blob + + Properties / Entry arguments: + - atf-bl31-path: Filename of file to read into entry. This is typically + called bl31.bin or bl31.elf + + This entry holds the run-time firmware, typically started by U-Boot SPL. + See the U-Boot README for your architecture or board for how to use it. See + https://github.com/ARM-software/arm-trusted-firmware for more information + about ATF. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'atf-bl31') + self.external = True diff --git a/roms/u-boot/tools/binman/etype/blob.py b/roms/u-boot/tools/binman/etype/blob.py new file mode 100644 index 000000000..018f8c9a3 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/blob.py @@ -0,0 +1,72 @@ +# 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 binman.entry import Entry +from dtoc import fdt_util +from patman import tools +from patman import tout + +class Entry_blob(Entry): + """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): + super().__init__(section, etype, node) + self._filename = fdt_util.GetString(self._node, 'filename', self.etype) + + def ObtainContents(self): + self._filename = self.GetDefaultFilename() + self._pathname = tools.GetInputFilename(self._filename, + self.external and self.section.GetAllowMissing()) + # Allow the file to be missing + if not self._pathname: + self.SetContents(b'') + self.missing = True + return True + + self.ReadBlobContents() + return True + + def ReadBlobContents(self): + """Read blob contents into memory + + This function compresses the data before storing if needed. + + We assume the data is small enough to fit into memory. If this + is used for large filesystem image that might not be true. + In that case, Image.BuildImage() could be adjusted to use a + new Entry method which can read in chunks. Then we could copy + the data in chunks and avoid reading it all at once. For now + this seems like an unnecessary complication. + """ + indata = tools.ReadFile(self._pathname) + data = self.CompressData(indata) + self.SetContents(data) + return True + + def GetDefaultFilename(self): + return self._filename + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + return self.ProcessContentsUpdate(self.data) diff --git a/roms/u-boot/tools/binman/etype/blob_dtb.py b/roms/u-boot/tools/binman/etype/blob_dtb.py new file mode 100644 index 000000000..3ce7511f6 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/blob_dtb.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 U-Boot device tree files +# + +from binman.entry import Entry +from binman.etype.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): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + + def ObtainContents(self): + """Get the device-tree from the list held by the 'state' module""" + self._filename = self.GetDefaultFilename() + self._pathname, _ = state.GetFdtContents(self.GetFdtEtype()) + return super().ReadBlobContents() + + def ProcessContents(self): + """Re-read the DTB contents so that we get any calculated properties""" + _, indata = state.GetFdtContents(self.GetFdtEtype()) + data = self.CompressData(indata) + return self.ProcessContentsUpdate(data) + + def GetFdtEtype(self): + """Get the entry type of this device tree + + This can be 'u-boot-dtb', 'u-boot-spl-dtb' or 'u-boot-tpl-dtb' + Returns: + Entry type if any, e.g. 'u-boot-dtb' + """ + return None + + def GetFdts(self): + fname = self.GetDefaultFilename() + return {self.GetFdtEtype(): [self, fname]} + + def WriteData(self, data, decomp=True): + ok = super().WriteData(data, decomp) + + # Update the state module, since it has the authoritative record of the + # device trees used. If we don't do this, then state.GetFdtContents() + # will still return the old contents + state.UpdateFdtContents(self.GetFdtEtype(), data) + return ok diff --git a/roms/u-boot/tools/binman/etype/blob_ext.py b/roms/u-boot/tools/binman/etype/blob_ext.py new file mode 100644 index 000000000..d6b0ca17c --- /dev/null +++ b/roms/u-boot/tools/binman/etype/blob_ext.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 external blobs, not built by U-Boot +# + +import os + +from binman.etype.blob import Entry_blob +from dtoc import fdt_util +from patman import tools +from patman import tout + +class Entry_blob_ext(Entry_blob): + """Externally built binary blob + + Note: This should not be used by itself. It is normally used as a parent + class by other entry types. + + If the file providing this blob is missing, binman can optionally ignore it + and produce a broken image with a warning. + + See 'blob' for Properties / Entry arguments. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.external = True diff --git a/roms/u-boot/tools/binman/etype/blob_named_by_arg.py b/roms/u-boot/tools/binman/etype/blob_named_by_arg.py new file mode 100644 index 000000000..7c486b2dc --- /dev/null +++ b/roms/u-boot/tools/binman/etype/blob_named_by_arg.py @@ -0,0 +1,36 @@ +# 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 binman.etype.blob import Entry_blob +from binman.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 None) + + 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, required=False): + super().__init__(section, etype, node) + filename, = self.GetEntryArgsOrProps( + [EntryArg('%s-path' % blob_fname, str)], required=required) + if filename: + self._filename = filename diff --git a/roms/u-boot/tools/binman/etype/blob_phase.py b/roms/u-boot/tools/binman/etype/blob_phase.py new file mode 100644 index 000000000..54ca54c50 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/blob_phase.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type base class for U-Boot or SPL binary with devicetree +# + +from binman.etype.section import Entry_section + +class Entry_blob_phase(Entry_section): + """Section that holds a phase binary + + This is a base class that should not normally be used directly. It is used + when converting a 'u-boot' entry automatically into a 'u-boot-expanded' + entry; similarly for SPL. + """ + def __init__(self, section, etype, node, root_fname, dtb_file, bss_pad): + """Set up a new blob for a phase + + This holds an executable for a U-Boot phase, optional BSS padding and + a devicetree + + Args: + section: entry_Section object for this entry's parent + etype: Type of object + node: Node defining this entry + root_fname: Root filename for the binary ('u-boot', + 'spl/u-boot-spl', etc.) + dtb_file: Name of devicetree file ('u-boot.dtb', u-boot-spl.dtb', + etc.) + bss_pad: True to add BSS padding before the devicetree + """ + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self.root_fname = root_fname + self.dtb_file = dtb_file + self.bss_pad = bss_pad + + def ExpandEntries(self): + """Create the subnodes""" + names = [self.root_fname + '-nodtb', self.root_fname + '-dtb'] + if self.bss_pad: + names.insert(1, self.root_fname + '-bss-pad') + for name in names: + subnode = state.AddSubnode(self._node, name) + + # Read entries again, now that we have some + self._ReadEntries() diff --git a/roms/u-boot/tools/binman/etype/cbfs.py b/roms/u-boot/tools/binman/etype/cbfs.py new file mode 100644 index 000000000..44db7b9bb --- /dev/null +++ b/roms/u-boot/tools/binman/etype/cbfs.py @@ -0,0 +1,285 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a Coreboot Filesystem (CBFS) +# + +from collections import OrderedDict + +from binman import cbfs_util +from binman.cbfs_util import CbfsWriter +from binman.entry import Entry +from dtoc import fdt_util + +class Entry_cbfs(Entry): + """Coreboot Filesystem (CBFS) + + A CBFS provides a way to group files into a group. It has a simple directory + structure and allows the position of individual files to be set, since it is + designed to support execute-in-place in an x86 SPI-flash device. Where XIP + is not used, it supports compression and storing ELF files. + + CBFS is used by coreboot as its way of orgnanising SPI-flash contents. + + The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + + This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb. + Note that the size is required since binman does not support calculating it. + The contents of each entry is just what binman would normally provide if it + were not a CBFS node. A blob type can be used to import arbitrary files as + with the second subnode below:: + + cbfs { + size = <0x100000>; + u-boot { + cbfs-name = "BOOT"; + cbfs-type = "raw"; + }; + + dtb { + type = "blob"; + filename = "u-boot.dtb"; + cbfs-type = "raw"; + cbfs-compress = "lz4"; + cbfs-offset = <0x100000>; + }; + }; + + This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and + u-boot.dtb (named "dtb") and compressed with the lz4 algorithm. + + + Properties supported in the top-level CBFS node: + + cbfs-arch: + Defaults to "x86", but you can specify the architecture if needed. + + + Properties supported in the CBFS entry subnodes: + + cbfs-name: + This is the name of the file created in CBFS. It defaults to the entry + name (which is the node name), but you can override it with this + property. + + cbfs-type: + This is the CBFS file type. The following are supported: + + raw: + This is a 'raw' file, although compression is supported. It can be + used to store any file in CBFS. + + stage: + This is an ELF file that has been loaded (i.e. mapped to memory), so + appears in the CBFS as a flat binary. The input file must be an ELF + image, for example this puts "u-boot" (the ELF image) into a 'stage' + entry:: + + cbfs { + size = <0x100000>; + u-boot-elf { + cbfs-name = "BOOT"; + cbfs-type = "stage"; + }; + }; + + You can use your own ELF file with something like:: + + cbfs { + size = <0x100000>; + something { + type = "blob"; + filename = "cbfs-stage.elf"; + cbfs-type = "stage"; + }; + }; + + As mentioned, the file is converted to a flat binary, so it is + equivalent to adding "u-boot.bin", for example, but with the load and + start addresses specified by the ELF. At present there is no option + to add a flat binary with a load/start address, similar to the + 'add-flat-binary' option in cbfstool. + + cbfs-offset: + This is the offset of the file's data within the CBFS. It is used to + specify where the file should be placed in cases where a fixed position + is needed. Typical uses are for code which is not relocatable and must + execute in-place from a particular address. This works because SPI flash + is generally mapped into memory on x86 devices. The file header is + placed before this offset so that the data start lines up exactly with + the chosen offset. If this property is not provided, then the file is + placed in the next available spot. + + The current implementation supports only a subset of CBFS features. It does + not support other file types (e.g. payload), adding multiple files (like the + 'files' entry with a pattern supported by binman), putting files at a + particular offset in the CBFS and a few other things. + + Of course binman can create images containing multiple CBFSs, simply by + defining these in the binman config:: + + + binman { + size = <0x800000>; + cbfs { + offset = <0x100000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + + cbfs2 { + offset = <0x700000>; + size = <0x100000>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + image { + type = "blob"; + filename = "image.jpg"; + }; + }; + }; + + This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB, + both of size 1MB. + """ + def __init__(self, section, etype, node): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86') + self.align_default = None + self._cbfs_entries = OrderedDict() + self._ReadSubnodes() + self.reader = None + + def ObtainContents(self, skip=None): + arch = cbfs_util.find_arch(self._cbfs_arg) + if arch is None: + self.Raise("Invalid architecture '%s'" % self._cbfs_arg) + if self.size is None: + self.Raise("'cbfs' entry must have a size property") + cbfs = CbfsWriter(self.size, arch) + for entry in self._cbfs_entries.values(): + # First get the input data and put it in a file. If not available, + # try later. + if entry != skip and not entry.ObtainContents(): + return False + data = entry.GetData() + cfile = None + if entry._type == 'raw': + cfile = cbfs.add_file_raw(entry._cbfs_name, data, + entry._cbfs_offset, + entry._cbfs_compress) + elif entry._type == 'stage': + cfile = cbfs.add_file_stage(entry._cbfs_name, data, + entry._cbfs_offset) + else: + entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" % + entry._type) + if cfile: + entry._cbfs_file = cfile + data = cbfs.get_data() + self.SetContents(data) + return True + + def _ReadSubnodes(self): + """Read the subnodes to find out what should go in this CBFS""" + for node in self._node.subnodes: + entry = Entry.Create(self, node) + entry.ReadNode() + entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name) + entry._type = fdt_util.GetString(node, 'cbfs-type') + compress = fdt_util.GetString(node, 'cbfs-compress', 'none') + entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset') + entry._cbfs_compress = cbfs_util.find_compress(compress) + if entry._cbfs_compress is None: + self.Raise("Invalid compression in '%s': '%s'" % + (node.name, compress)) + self._cbfs_entries[entry._cbfs_name] = entry + + def SetImagePos(self, image_pos): + """Override this function to set all the entry properties from CBFS + + We can only do this once image_pos is known + + Args: + image_pos: Position of this entry in the image + """ + super().SetImagePos(image_pos) + + # Now update the entries with info from the CBFS entries + for entry in self._cbfs_entries.values(): + cfile = entry._cbfs_file + entry.size = cfile.data_len + entry.offset = cfile.calced_cbfs_offset + entry.image_pos = self.image_pos + entry.offset + if entry._cbfs_compress: + entry.uncomp_size = cfile.memlen + + def AddMissingProperties(self, have_image_pos): + super().AddMissingProperties(have_image_pos) + for entry in self._cbfs_entries.values(): + entry.AddMissingProperties(have_image_pos) + if entry._cbfs_compress: + state.AddZeroProp(entry._node, 'uncomp-size') + # Store the 'compress' property, since we don't look at + # 'cbfs-compress' in Entry.ReadData() + state.AddString(entry._node, 'compress', + cbfs_util.compress_name(entry._cbfs_compress)) + + def SetCalculatedProperties(self): + """Set the value of device-tree properties calculated by binman""" + super().SetCalculatedProperties() + for entry in self._cbfs_entries.values(): + state.SetInt(entry._node, 'offset', entry.offset) + state.SetInt(entry._node, 'size', entry.size) + state.SetInt(entry._node, 'image-pos', entry.image_pos) + if entry.uncomp_size is not None: + state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size) + + def ListEntries(self, entries, indent): + """Override this method to list all files in the section""" + super().ListEntries(entries, indent) + for entry in self._cbfs_entries.values(): + entry.ListEntries(entries, indent + 1) + + def GetEntries(self): + return self._cbfs_entries + + def ReadData(self, decomp=True): + data = super().ReadData(True) + return data + + def ReadChildData(self, child, decomp=True): + if not self.reader: + data = super().ReadData(True) + self.reader = cbfs_util.CbfsReader(data) + reader = self.reader + cfile = reader.files.get(child.name) + return cfile.data if decomp else cfile.orig_data + + def WriteChildData(self, child): + self.ObtainContents(skip=child) + return True diff --git a/roms/u-boot/tools/binman/etype/collection.py b/roms/u-boot/tools/binman/etype/collection.py new file mode 100644 index 000000000..1625575fe --- /dev/null +++ b/roms/u-boot/tools/binman/etype/collection.py @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# + +# Support for a collection of entries from other parts of an image + +from collections import OrderedDict +import os + +from binman.entry import Entry +from dtoc import fdt_util + +class Entry_collection(Entry): + """An entry which contains a collection of other entries + + Properties / Entry arguments: + - content: List of phandles to entries to include + + This allows reusing the contents of other entries. The contents of the + listed entries are combined to form this entry. This serves as a useful + base class for entry types which need to process data from elsewhere in + the image, not necessarily child entries. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.content = fdt_util.GetPhandleList(self._node, 'content') + if not self.content: + self.Raise("Collection must have a 'content' property") + + def GetContents(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry + """ + # Join up all the data + self.Info('Getting contents, required=%s' % required) + data = b'' + for entry_phandle in self.content: + entry_data = self.section.GetContentsByPhandle(entry_phandle, self, + required) + if not required and entry_data is None: + self.Info('Contents not available yet') + # Data not available yet + return None + data += entry_data + + self.Info('Returning contents size %x' % len(data)) + + return data + + def ObtainContents(self): + data = self.GetContents(False) + if data is None: + return False + self.SetContents(data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + data = self.GetContents(True) + return self.ProcessContentsUpdate(data) diff --git a/roms/u-boot/tools/binman/etype/cros_ec_rw.py b/roms/u-boot/tools/binman/etype/cros_ec_rw.py new file mode 100644 index 000000000..bf676b2d1 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/cros_ec_rw.py @@ -0,0 +1,21 @@ +# 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 binman.etype.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): + super().__init__(section, etype, node, 'cros-ec-rw', required=True) + self.external = True diff --git a/roms/u-boot/tools/binman/etype/fdtmap.py b/roms/u-boot/tools/binman/etype/fdtmap.py new file mode 100644 index 000000000..2339feeba --- /dev/null +++ b/roms/u-boot/tools/binman/etype/fdtmap.py @@ -0,0 +1,149 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> + +"""# Entry-type module for a full map of the firmware image + +This handles putting an FDT into the image with just the information about the +image. +""" + +from binman.entry import Entry +from patman import tools +from patman import tout + +FDTMAP_MAGIC = b'_FDTMAP_' +FDTMAP_HDR_LEN = 16 + +def LocateFdtmap(data): + """Search an image for an fdt map + + Args: + data: Data to search + + Returns: + Position of fdt map in data, or None if not found. Note that the + position returned is of the FDT header, i.e. before the FDT data + """ + hdr_pos = data.find(FDTMAP_MAGIC) + size = len(data) + if hdr_pos != -1: + hdr = data[hdr_pos:hdr_pos + FDTMAP_HDR_LEN] + if len(hdr) == FDTMAP_HDR_LEN: + return hdr_pos + return None + +class Entry_fdtmap(Entry): + """An entry which contains an FDT map + + Properties / Entry arguments: + None + + An FDT map is just a header followed by an FDT containing a list of all the + entries in the image. The root node corresponds to the image node in the + original FDT, and an image-name property indicates the image name in that + original tree. + + The header is the string _FDTMAP_ followed by 8 unused bytes. + + When used, this entry will be populated with an FDT map which reflects the + entries in the current image. Hierarchy is preserved, and all offsets and + sizes are included. + + Note that the -u option must be provided to ensure that binman updates the + FDT with the position of each entry. + + Example output for a simple image with U-Boot and an FDT map:: + + / { + image-name = "binman"; + size = <0x00000112>; + image-pos = <0x00000000>; + offset = <0x00000000>; + u-boot { + size = <0x00000004>; + image-pos = <0x00000000>; + offset = <0x00000000>; + }; + fdtmap { + size = <0x0000010e>; + image-pos = <0x00000004>; + offset = <0x00000004>; + }; + }; + + If allow-repack is used then 'orig-offset' and 'orig-size' properties are + added as necessary. See the binman README. + """ + def __init__(self, section, etype, node): + # Put these here to allow entry-docs and help to work without libfdt + global libfdt + global state + global Fdt + + import libfdt + from binman import state + from dtoc.fdt import Fdt + + super().__init__(section, etype, node) + + def _GetFdtmap(self): + """Build an FDT map from the entries in the current image + + Returns: + FDT map binary data + """ + def _AddNode(node): + """Add a node to the FDT map""" + for pname, prop in node.props.items(): + fsw.property(pname, prop.bytes) + for subnode in node.subnodes: + with fsw.add_node(subnode.name): + _AddNode(subnode) + + data = state.GetFdtContents('fdtmap')[1] + # If we have an fdtmap it means that we are using this as the + # fdtmap for this image. + if data is None: + # Get the FDT data into an Fdt object + data = state.GetFdtContents()[1] + infdt = Fdt.FromData(data) + infdt.Scan() + + # Find the node for the image containing the Fdt-map entry + path = self.section.GetPath() + self.Detail("Fdtmap: Using section '%s' (path '%s')" % + (self.section.name, path)) + node = infdt.GetNode(path) + if not node: + self.Raise("Internal error: Cannot locate node for path '%s'" % + path) + + # Build a new tree with all nodes and properties starting from that + # node + fsw = libfdt.FdtSw() + fsw.finish_reservemap() + with fsw.add_node(''): + fsw.property_string('image-node', node.name) + _AddNode(node) + fdt = fsw.as_fdt() + + # Pack this new FDT and return its contents + fdt.pack() + outfdt = Fdt.FromData(fdt.as_bytearray()) + data = outfdt.GetContents() + data = FDTMAP_MAGIC + tools.GetBytes(0, 8) + data + return data + + def ObtainContents(self): + """Obtain a placeholder for the fdt-map contents""" + self.SetContents(self._GetFdtmap()) + return True + + def ProcessContents(self): + """Write an updated version of the FDT map to this entry + + This is necessary since new data may have been written back to it during + processing, e.g. the image-pos properties. + """ + return self.ProcessContentsUpdate(self._GetFdtmap()) diff --git a/roms/u-boot/tools/binman/etype/files.py b/roms/u-boot/tools/binman/etype/files.py new file mode 100644 index 000000000..5db36abef --- /dev/null +++ b/roms/u-boot/tools/binman/etype/files.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 set of files which are placed in individual +# sub-entries +# + +import glob +import os + +from binman.etype.section import Entry_section +from dtoc import fdt_util +from patman import tools + + +class Entry_files(Entry_section): + """A set of files arranged in a section + + Properties / Entry arguments: + - pattern: Filename pattern to match the files to include + - files-compress: Compression algorithm to use: + none: No compression + lz4: Use lz4 compression (via 'lz4' command-line utility) + - files-align: Align each file to the given alignment + + 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): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self._pattern = fdt_util.GetString(self._node, 'pattern') + if not self._pattern: + self.Raise("Missing 'pattern' property") + self._files_compress = fdt_util.GetString(self._node, 'files-compress', + 'none') + self._files_align = fdt_util.GetInt(self._node, 'files-align'); + 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._files_compress) + if self._files_align: + state.AddInt(subnode, 'align', self._files_align) + + # Read entries again, now that we have some + self._ReadEntries() diff --git a/roms/u-boot/tools/binman/etype/fill.py b/roms/u-boot/tools/binman/etype/fill.py new file mode 100644 index 000000000..efb2d13e9 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/fill.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from binman.entry import Entry +from dtoc import fdt_util +from patman import tools + +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): + super().__init__(section, etype, node) + + def ReadNode(self): + super().ReadNode() + 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(tools.GetBytes(self.fill_value, self.size)) + return True diff --git a/roms/u-boot/tools/binman/etype/fit.py b/roms/u-boot/tools/binman/etype/fit.py new file mode 100644 index 000000000..6936f5736 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/fit.py @@ -0,0 +1,297 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for producing a FIT +# + +from collections import defaultdict, OrderedDict +import libfdt + +from binman.entry import Entry, EntryArg +from dtoc import fdt_util +from dtoc.fdt import Fdt +from patman import tools + +class Entry_fit(Entry): + """Flat Image Tree (FIT) + + This calls mkimage to create a FIT (U-Boot Flat Image Tree) based on the + input provided. + + Nodes for the FIT should be written out in the binman configuration just as + they would be in a file passed to mkimage. + + For example, this creates an image containing a FIT with U-Boot SPL:: + + binman { + fit { + description = "Test FIT"; + fit,fdt-list = "of-list"; + + images { + kernel@1 { + description = "SPL"; + os = "u-boot"; + type = "rkspi"; + arch = "arm"; + compression = "none"; + load = <0>; + entry = <0>; + + u-boot-spl { + }; + }; + }; + }; + }; + + U-Boot supports creating fdt and config nodes automatically. To do this, + pass an of-list property (e.g. -a of-list=file1 file2). This tells binman + that you want to generates nodes for two files: file1.dtb and file2.dtb + The fit,fdt-list property (see above) indicates that of-list should be used. + If the property is missing you will get an error. + + Then add a 'generator node', a node with a name starting with '@':: + + images { + @fdt-SEQ { + description = "fdt-NAME"; + type = "flat_dt"; + compression = "none"; + }; + }; + + This tells binman to create nodes fdt-1 and fdt-2 for each of your two + files. All the properties you specify will be included in the node. This + node acts like a template to generate the nodes. The generator node itself + does not appear in the output - it is replaced with what binman generates. + + You can create config nodes in a similar way:: + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "NAME"; + firmware = "atf"; + loadables = "uboot"; + fdt = "fdt-SEQ"; + }; + }; + + This tells binman to create nodes config-1 and config-2, i.e. a config for + each of your two files. + + Available substitutions for '@' nodes are: + + SEQ: + Sequence number of the generated fdt (1, 2, ...) + NAME + Name of the dtb as provided (i.e. without adding '.dtb') + + Note that if no devicetree files are provided (with '-a of-list' as above) + then no nodes will be generated. + + The 'default' property, if present, will be automatically set to the name + if of configuration whose devicetree matches the 'default-dt' entry + argument, e.g. with '-a default-dt=sun50i-a64-pine64-lts'. + + Available substitutions for '@' property values are + + DEFAULT-SEQ: + Sequence number of the default fdt,as provided by the 'default-dt' entry + argument + + Properties (in the 'fit' node itself): + fit,external-offset: Indicates that the contents of the FIT are external + and provides the external offset. This is passsed to mkimage via + the -E and -p flags. + + """ + def __init__(self, section, etype, node): + """ + Members: + _fit: FIT file being built + _fit_sections: dict: + key: relative path to entry Node (from the base of the FIT) + value: Entry_section object comprising the contents of this + node + """ + super().__init__(section, etype, node) + self._fit = None + self._fit_sections = {} + self._fit_props = {} + for pname, prop in self._node.props.items(): + if pname.startswith('fit,'): + self._fit_props[pname] = prop + + self._fdts = None + self._fit_list_prop = self._fit_props.get('fit,fdt-list') + if self._fit_list_prop: + fdts, = self.GetEntryArgsOrProps( + [EntryArg(self._fit_list_prop.value, str)]) + if fdts is not None: + self._fdts = fdts.split() + self._fit_default_dt = self.GetEntryArgsOrProps([EntryArg('default-dt', + str)])[0] + + def ReadNode(self): + self._ReadSubnodes() + super().ReadNode() + + def _ReadSubnodes(self): + def _AddNode(base_node, depth, node): + """Add a node to the FIT + + Args: + base_node: Base Node of the FIT (with 'description' property) + depth: Current node depth (0 is the base node) + node: Current node to process + + There are two cases to deal with: + - hash and signature nodes which become part of the FIT + - binman entries which are used to define the 'data' for each + image + """ + for pname, prop in node.props.items(): + if not pname.startswith('fit,'): + if pname == 'default': + val = prop.value + # Handle the 'default' property + if val.startswith('@'): + if not self._fdts: + continue + if not self._fit_default_dt: + self.Raise("Generated 'default' node requires default-dt entry argument") + if self._fit_default_dt not in self._fdts: + self.Raise("default-dt entry argument '%s' not found in fdt list: %s" % + (self._fit_default_dt, + ', '.join(self._fdts))) + seq = self._fdts.index(self._fit_default_dt) + val = val[1:].replace('DEFAULT-SEQ', str(seq + 1)) + fsw.property_string(pname, val) + continue + fsw.property(pname, prop.bytes) + + rel_path = node.path[len(base_node.path):] + in_images = rel_path.startswith('/images') + has_images = depth == 2 and in_images + if has_images: + # This node is a FIT subimage node (e.g. "/images/kernel") + # containing content nodes. We collect the subimage nodes and + # section entries for them here to merge the content subnodes + # together and put the merged contents in the subimage node's + # 'data' property later. + entry = Entry.Create(self.section, node, etype='section') + entry.ReadNode() + self._fit_sections[rel_path] = entry + + for subnode in node.subnodes: + if has_images and not (subnode.name.startswith('hash') or + subnode.name.startswith('signature')): + # This subnode is a content node not meant to appear in + # the FIT (e.g. "/images/kernel/u-boot"), so don't call + # fsw.add_node() or _AddNode() for it. + pass + elif subnode.name.startswith('@'): + if self._fdts: + # Generate notes for each FDT + for seq, fdt_fname in enumerate(self._fdts): + node_name = subnode.name[1:].replace('SEQ', + str(seq + 1)) + fname = tools.GetInputFilename(fdt_fname + '.dtb') + with fsw.add_node(node_name): + for pname, prop in subnode.props.items(): + val = prop.bytes.replace( + b'NAME', tools.ToBytes(fdt_fname)) + val = val.replace( + b'SEQ', tools.ToBytes(str(seq + 1))) + fsw.property(pname, val) + + # Add data for 'fdt' nodes (but not 'config') + if depth == 1 and in_images: + fsw.property('data', + tools.ReadFile(fname)) + else: + if self._fdts is None: + if self._fit_list_prop: + self.Raise("Generator node requires '%s' entry argument" % + self._fit_list_prop.value) + else: + self.Raise("Generator node requires 'fit,fdt-list' property") + else: + with fsw.add_node(subnode.name): + _AddNode(base_node, depth + 1, subnode) + + # Build a new tree with all nodes and properties starting from the + # entry node + fsw = libfdt.FdtSw() + fsw.finish_reservemap() + with fsw.add_node(''): + _AddNode(self._node, 0, self._node) + fdt = fsw.as_fdt() + + # Pack this new FDT and scan it so we can add the data later + fdt.pack() + self._fdt = Fdt.FromData(fdt.as_bytearray()) + self._fdt.Scan() + + def ObtainContents(self): + """Obtain the contents of the FIT + + This adds the 'data' properties to the input ITB (Image-tree Binary) + then runs mkimage to process it. + """ + # self._BuildInput() either returns bytes or raises an exception. + data = self._BuildInput(self._fdt) + uniq = self.GetUniqueName() + input_fname = tools.GetOutputFilename('%s.itb' % uniq) + output_fname = tools.GetOutputFilename('%s.fit' % uniq) + tools.WriteFile(input_fname, data) + tools.WriteFile(output_fname, data) + + args = [] + ext_offset = self._fit_props.get('fit,external-offset') + if ext_offset is not None: + args += ['-E', '-p', '%x' % fdt_util.fdt32_to_cpu(ext_offset.value)] + tools.Run('mkimage', '-t', '-F', output_fname, *args) + + self.SetContents(tools.ReadFile(output_fname)) + return True + + def _BuildInput(self, fdt): + """Finish the FIT by adding the 'data' properties to it + + Arguments: + fdt: FIT to update + + Returns: + New fdt contents (bytes) + """ + for path, section in self._fit_sections.items(): + node = fdt.GetNode(path) + # Entry_section.ObtainContents() either returns True or + # raises an exception. + section.ObtainContents() + section.Pack(0) + data = section.GetData() + node.AddData('data', data) + + fdt.Sync(auto_resize=True) + data = fdt.GetContents() + return data + + def CheckMissing(self, missing_list): + """Check if any entries in this FIT have missing external blobs + + If there are missing blobs, the entries are added to the list + + Args: + missing_list: List of Entry objects to be added to + """ + for path, section in self._fit_sections.items(): + section.CheckMissing(missing_list) + + def SetAllowMissing(self, allow_missing): + for section in self._fit_sections.values(): + section.SetAllowMissing(allow_missing) diff --git a/roms/u-boot/tools/binman/etype/fmap.py b/roms/u-boot/tools/binman/etype/fmap.py new file mode 100644 index 000000000..cac99b60e --- /dev/null +++ b/roms/u-boot/tools/binman/etype/fmap.py @@ -0,0 +1,85 @@ +# 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 binman.entry import Entry +from binman import fmap_util +from patman import tools +from patman.tools import ToHexSize +from patman import tout + + +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. Sections are represented as an area appearing + before its contents, so that it is possible to reconstruct the hierarchy + from the FMAP by using the offset information. This convention does not + seem to be documented, but is used in Chromium OS. + + CBFS entries appear as a single entry, i.e. the sub-entries are ignored. + """ + def __init__(self, section, etype, node): + super().__init__(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() + tout.Debug("fmap: Add entry '%s' type '%s' (%s subentries)" % + (entry.GetPath(), entry.etype, ToHexSize(entries))) + if entries and entry.etype != 'cbfs': + # Create an area for the section, which encompasses all entries + # within it + if entry.image_pos is None: + pos = 0 + else: + pos = entry.image_pos - entry.GetRootSkipAtStart() + + # Drop @ symbols in name + name = entry.name.replace('@', '') + areas.append( + fmap_util.FmapArea(pos, entry.size or 0, name, 0)) + 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.GetImage().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): + return self.ProcessContentsUpdate(self._GetFmap()) diff --git a/roms/u-boot/tools/binman/etype/gbb.py b/roms/u-boot/tools/binman/etype/gbb.py new file mode 100644 index 000000000..41554eba8 --- /dev/null +++ b/roms/u-boot/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 + +from patman import command +from binman.entry import Entry, EntryArg + +from dtoc import fdt_util +from patman 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): + super().__init__(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.items(): + 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/roms/u-boot/tools/binman/etype/image_header.py b/roms/u-boot/tools/binman/etype/image_header.py new file mode 100644 index 000000000..240118849 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/image_header.py @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> + +"""Entry-type module for an image header which points to the FDT map + +This creates an 8-byte entry with a magic number and the offset of the FDT map +(which is another entry in the image), relative to the start or end of the +image. +""" + +import struct + +from binman.entry import Entry +from dtoc import fdt_util + +IMAGE_HEADER_MAGIC = b'BinM' +IMAGE_HEADER_LEN = 8 + +def LocateHeaderOffset(data): + """Search an image for an image header + + Args: + data: Data to search + + Returns: + Offset of image header in the image, or None if not found + """ + hdr_pos = data.find(IMAGE_HEADER_MAGIC) + if hdr_pos != -1: + size = len(data) + hdr = data[hdr_pos:hdr_pos + IMAGE_HEADER_LEN] + if len(hdr) == IMAGE_HEADER_LEN: + offset = struct.unpack('<I', hdr[4:])[0] + if hdr_pos == len(data) - IMAGE_HEADER_LEN: + pos = size + offset - (1 << 32) + else: + pos = offset + return pos + return None + +class Entry_image_header(Entry): + """An entry which contains a pointer to the FDT map + + Properties / Entry arguments: + location: Location of header ("start" or "end" of image). This is + optional. If omitted then the entry must have an offset property. + + This adds an 8-byte entry to the start or end of the image, pointing to the + location of the FDT map. The format is a magic number followed by an offset + from the start or end of the image, in twos-compliment format. + + This entry must be in the top-level part of the image. + + NOTE: If the location is at the start/end, you will probably need to specify + sort-by-offset for the image, unless you actually put the image header + first/last in the entry list. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.location = fdt_util.GetString(self._node, 'location') + + def _GetHeader(self): + image_pos = self.GetSiblingImagePos('fdtmap') + if image_pos == False: + self.Raise("'image_header' section must have an 'fdtmap' sibling") + elif image_pos is None: + # This will be available when called from ProcessContents(), but not + # when called from ObtainContents() + offset = 0xffffffff + else: + image_size = self.section.GetImageSize() or 0 + base = (0 if self.location != 'end' else image_size) + offset = (image_pos - base) & 0xffffffff + data = IMAGE_HEADER_MAGIC + struct.pack('<I', offset) + return data + + def ObtainContents(self): + """Obtain a placeholder for the header contents""" + self.SetContents(self._GetHeader()) + return True + + def Pack(self, offset): + """Special pack method to set the offset to start/end of image""" + if not self.offset: + if self.location not in ['start', 'end']: + self.Raise("Invalid location '%s', expected 'start' or 'end'" % + self.location) + order = self.GetSiblingOrder() + if self.location != order and not self.section.GetSort(): + self.Raise("Invalid sibling order '%s' for image-header: Must be at '%s' to match location" % + (order, self.location)) + if self.location != 'end': + offset = 0 + else: + image_size = self.section.GetImageSize() + if image_size is None: + # We don't know the image, but this must be the last entry, + # so we can assume it goes + offset = offset + else: + offset = image_size - IMAGE_HEADER_LEN + offset += self.section.GetStartOffset() + return super().Pack(offset) + + def ProcessContents(self): + """Write an updated version of the FDT map to this entry + + This is necessary since image_pos is not available when ObtainContents() + is called, since by then the entries have not been packed in the image. + """ + return self.ProcessContentsUpdate(self._GetHeader()) diff --git a/roms/u-boot/tools/binman/etype/intel_cmc.py b/roms/u-boot/tools/binman/etype/intel_cmc.py new file mode 100644 index 000000000..494d43c9c --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_cmc.py @@ -0,0 +1,22 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_cmc(Entry_blob_ext): + """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): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_descriptor.py b/roms/u-boot/tools/binman/etype/intel_descriptor.py new file mode 100644 index 000000000..7fe88a9ec --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_descriptor.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel flash descriptor +# + +import struct + +from binman.entry import Entry +from binman.etype.blob_ext import Entry_blob_ext + +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_ext): + """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): + super().__init__(section, etype, node) + self._regions = [] + + def Pack(self, offset): + """Put this entry at the start of the image""" + if self.offset is None: + offset = self.section.GetStartOffset() + return super().Pack(offset) + + def GetOffsets(self): + info = {} + if self.missing: + # Return zero offsets so that these entries get placed somewhere + if self.HasSibling('intel-me'): + info['intel-me'] = [0, None] + return info + offset = self.data.find(FD_SIGNATURE) + if offset == -1: + self.Raise('Cannot find Intel Flash Descriptor (FD) signature') + flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL', + self.data[offset:offset + 16]) + frba = ((flmap0 >> 16) & 0xff) << 4 + for i in range(MAX_REGIONS): + self._regions.append(Region(self.data, frba, i)) + + # Set the offset for ME (Management Engine) and IFWI (Integrated + # Firmware Image), for now, since the others are not used. + if self.HasSibling('intel-me'): + info['intel-me'] = [self._regions[REGION_ME].base, + self._regions[REGION_ME].size] + if self.HasSibling('intel-ifwi'): + info['intel-ifwi'] = [self._regions[REGION_BIOS].base, None] + return info diff --git a/roms/u-boot/tools/binman/etype/intel_fit.py b/roms/u-boot/tools/binman/etype/intel_fit.py new file mode 100644 index 000000000..f1a10c55a --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fit.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 Intel Firmware Image Table +# + +import struct + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fit(Entry_blob_ext): + """Intel Firmware Image Table (FIT) + + This entry contains a dummy FIT as required by recent Intel CPUs. The FIT + contains information about the firmware and microcode available in the + image. + + At present binman only supports a basic FIT with no microcode. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def ReadNode(self): + """Force 16-byte alignment as required by FIT pointer""" + super().ReadNode() + self.align = 16 + + def ObtainContents(self): + data = struct.pack('<8sIHBB', b'_FIT_ ', 1, 0x100, 0x80, 0x7d) + self.SetContents(data) + return True diff --git a/roms/u-boot/tools/binman/etype/intel_fit_ptr.py b/roms/u-boot/tools/binman/etype/intel_fit_ptr.py new file mode 100644 index 000000000..01f082281 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fit_ptr.py @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for a pointer to an Intel Firmware Image Table +# + +import struct + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fit_ptr(Entry_blob_ext): + """Intel Firmware Image Table (FIT) pointer + + This entry contains a pointer to the FIT. It is required to be at address + 0xffffffc0 in the image. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + if self.HasSibling('intel-fit') is False: + self.Raise("'intel-fit-ptr' section must have an 'intel-fit' sibling") + + def _GetContents(self): + fit_pos = self.GetSiblingImagePos('intel-fit') + return struct.pack('<II', fit_pos or 0, 0) + + def ObtainContents(self): + self.SetContents(self._GetContents()) + return True + + def ProcessContents(self): + """Write an updated version of the FIT pointer to this entry + + This is necessary since image_pos is not available when ObtainContents() + is called, since by then the entries have not been packed in the image. + """ + return self.ProcessContentsUpdate(self._GetContents()) + + def Pack(self, offset): + """Special pack method to set the offset to the right place""" + return super().Pack(0xffffffc0) diff --git a/roms/u-boot/tools/binman/etype/intel_fsp.py b/roms/u-boot/tools/binman/etype/intel_fsp.py new file mode 100644 index 000000000..326cb7d09 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fsp.py @@ -0,0 +1,26 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fsp(Entry_blob_ext): + """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): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_fsp_m.py b/roms/u-boot/tools/binman/etype/intel_fsp_m.py new file mode 100644 index 000000000..9bcac790e --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fsp_m.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Firmware Support Package binary blob (M section) +# + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fsp_m(Entry_blob_ext): + """Intel Firmware Support Package (FSP) memory init + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains a binary blob which is used on some devices to set up + SDRAM. U-Boot executes this code in SPL so that it can make full use of + memory. Documentation is typically not available in sufficient detail to + allow U-Boot do this this itself.. + + An example filename is 'fsp_m.bin' + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_fsp_s.py b/roms/u-boot/tools/binman/etype/intel_fsp_s.py new file mode 100644 index 000000000..1d5046d45 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fsp_s.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Firmware Support Package binary blob (S section) +# + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fsp_s(Entry_blob_ext): + """Intel Firmware Support Package (FSP) silicon init + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains a binary blob which is used on some devices to set up + the silicon. U-Boot executes this code in U-Boot proper after SDRAM is + running, so that it can make full use of memory. Documentation is typically + not available in sufficient detail to allow U-Boot do this this itself. + + An example filename is 'fsp_s.bin' + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_fsp_t.py b/roms/u-boot/tools/binman/etype/intel_fsp_t.py new file mode 100644 index 000000000..80d95cc6f --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_fsp_t.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Firmware Support Package binary blob (T section) +# + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_fsp_t(Entry_blob_ext): + """Intel Firmware Support Package (FSP) temp ram init + + Properties / Entry arguments: + - filename: Filename of file to read into entry + + This file contains a binary blob which is used on some devices to set up + temporary memory (Cache-as-RAM or CAR). U-Boot executes this code in TPL so + that it has access to memory for its stack and initial storage. + + An example filename is 'fsp_t.bin' + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_ifwi.py b/roms/u-boot/tools/binman/etype/intel_ifwi.py new file mode 100644 index 000000000..903d39bdb --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_ifwi.py @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for Intel Management Engine binary blob +# + +from collections import OrderedDict + +from binman.entry import Entry +from binman.etype.blob_ext import Entry_blob_ext +from dtoc import fdt_util +from patman import tools + +class Entry_intel_ifwi(Entry_blob_ext): + """Intel Integrated Firmware Image (IFWI) file + + Properties / Entry arguments: + - filename: Filename of file to read into entry. This is either the + IFWI file itself, or a file that can be converted into one using a + tool + - convert-fit: If present this indicates that the ifwitool should be + used to convert the provided file into a IFWI. + + This file contains code and data used by the SoC that is required to make + it work. It includes U-Boot TPL, microcode, things related to the CSE + (Converged Security Engine, the microcontroller that loads all the firmware) + and other items beyond the wit of man. + + A typical filename is 'ifwi.bin' for an IFWI file, or 'fitimage.bin' for a + file that will be converted to an IFWI. + + The position of this entry is generally set by the intel-descriptor entry. + + The contents of the IFWI are specified by the subnodes of the IFWI node. + Each subnode describes an entry which is placed into the IFWFI with a given + sub-partition (and optional entry name). + + Properties for subnodes: + - ifwi-subpart: sub-parition to put this entry into, e.g. "IBBP" + - ifwi-entry: entry name t use, e.g. "IBBL" + - ifwi-replace: if present, indicates that the item should be replaced + in the IFWI. Otherwise it is added. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self._convert_fit = fdt_util.GetBool(self._node, 'convert-fit') + self._ifwi_entries = OrderedDict() + + def ReadNode(self): + self._ReadSubnodes() + super().ReadNode() + + def _BuildIfwi(self): + """Build the contents of the IFWI and write it to the 'data' property""" + # Create the IFWI file if needed + if self._convert_fit: + inname = self._pathname + outname = tools.GetOutputFilename('ifwi.bin') + tools.RunIfwiTool(inname, tools.CMD_CREATE, outname) + self._filename = 'ifwi.bin' + self._pathname = outname + else: + # Provide a different code path here to ensure we have test coverage + outname = self._pathname + + # Delete OBBP if it is there, then add the required new items. + tools.RunIfwiTool(outname, tools.CMD_DELETE, subpart='OBBP') + + for entry in self._ifwi_entries.values(): + # First get the input data and put it in a file + data = entry.GetPaddedData() + uniq = self.GetUniqueName() + input_fname = tools.GetOutputFilename('input.%s' % uniq) + tools.WriteFile(input_fname, data) + + tools.RunIfwiTool(outname, + tools.CMD_REPLACE if entry._ifwi_replace else tools.CMD_ADD, + input_fname, entry._ifwi_subpart, entry._ifwi_entry_name) + + self.ReadBlobContents() + return True + + def ObtainContents(self): + """Get the contents for the IFWI + + Unfortunately we cannot create anything from scratch here, as Intel has + tools which create precursor binaries with lots of data and settings, + and these are not incorporated into binman. + + The first step is to get a file in the IFWI format. This is either + supplied directly or is extracted from a fitimage using the 'create' + subcommand. + + After that we delete the OBBP sub-partition and add each of the files + that we want in the IFWI file, one for each sub-entry of the IWFI node. + """ + self._pathname = tools.GetInputFilename(self._filename, + self.section.GetAllowMissing()) + # Allow the file to be missing + if not self._pathname: + self.SetContents(b'') + self.missing = True + return True + for entry in self._ifwi_entries.values(): + if not entry.ObtainContents(): + return False + return self._BuildIfwi() + + def ProcessContents(self): + if self.missing: + return True + orig_data = self.data + self._BuildIfwi() + same = orig_data == self.data + return same + + def _ReadSubnodes(self): + """Read the subnodes to find out what should go in this IFWI""" + for node in self._node.subnodes: + entry = Entry.Create(self.section, node) + entry.ReadNode() + entry._ifwi_replace = fdt_util.GetBool(node, 'ifwi-replace') + entry._ifwi_subpart = fdt_util.GetString(node, 'ifwi-subpart') + entry._ifwi_entry_name = fdt_util.GetString(node, 'ifwi-entry') + self._ifwi_entries[entry._ifwi_subpart] = entry + + def WriteSymbols(self, section): + """Write symbol values into binary files for access at run time""" + if not self.missing: + for entry in self._ifwi_entries.values(): + entry.WriteSymbols(self) diff --git a/roms/u-boot/tools/binman/etype/intel_me.py b/roms/u-boot/tools/binman/etype/intel_me.py new file mode 100644 index 000000000..b93ebabdc --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_me.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 Intel Management Engine binary blob +# + +from binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_me(Entry_blob_ext): + """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, display 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'. + + The position of this entry is generally set by the intel-descriptor entry. + + See README.x86 for information about x86 binary blobs. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_mrc.py b/roms/u-boot/tools/binman/etype/intel_mrc.py new file mode 100644 index 000000000..bb8b26ff6 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_mrc.py @@ -0,0 +1,26 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_mrc(Entry_blob_ext): + """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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'mrc.bin' diff --git a/roms/u-boot/tools/binman/etype/intel_refcode.py b/roms/u-boot/tools/binman/etype/intel_refcode.py new file mode 100644 index 000000000..9112730a9 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_refcode.py @@ -0,0 +1,26 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_refcode(Entry_blob_ext): + """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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'refcode.bin' diff --git a/roms/u-boot/tools/binman/etype/intel_vbt.py b/roms/u-boot/tools/binman/etype/intel_vbt.py new file mode 100644 index 000000000..8afd57660 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_vbt.py @@ -0,0 +1,21 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_vbt(Entry_blob_ext): + """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): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/intel_vga.py b/roms/u-boot/tools/binman/etype/intel_vga.py new file mode 100644 index 000000000..51e6465f0 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/intel_vga.py @@ -0,0 +1,24 @@ +# 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 binman.etype.blob_ext import Entry_blob_ext + +class Entry_intel_vga(Entry_blob_ext): + """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): + super().__init__(section, etype, node) diff --git a/roms/u-boot/tools/binman/etype/mkimage.py b/roms/u-boot/tools/binman/etype/mkimage.py new file mode 100644 index 000000000..e49977522 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/mkimage.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 producing an image using mkimage +# + +from collections import OrderedDict + +from binman.entry import Entry +from dtoc import fdt_util +from patman import tools + +class Entry_mkimage(Entry): + """Binary produced by mkimage + + Properties / Entry arguments: + - datafile: Filename for -d argument + - args: Other arguments to pass + + The data passed to mkimage is collected from subnodes of the mkimage node, + e.g.:: + + mkimage { + args = "-n test -T imximage"; + + u-boot-spl { + }; + }; + + This calls mkimage to create an imximage with u-boot-spl.bin as the input + file. The output from mkimage then becomes part of the image produced by + binman. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self._args = fdt_util.GetString(self._node, 'args').split(' ') + self._mkimage_entries = OrderedDict() + self.align_default = None + self._ReadSubnodes() + + def ObtainContents(self): + data = b'' + for entry in self._mkimage_entries.values(): + # First get the input data and put it in a file. If not available, + # try later. + if not entry.ObtainContents(): + return False + data += entry.GetData() + uniq = self.GetUniqueName() + input_fname = tools.GetOutputFilename('mkimage.%s' % uniq) + tools.WriteFile(input_fname, data) + output_fname = tools.GetOutputFilename('mkimage-out.%s' % uniq) + tools.Run('mkimage', '-d', input_fname, *self._args, output_fname) + self.SetContents(tools.ReadFile(output_fname)) + return True + + def _ReadSubnodes(self): + """Read the subnodes to find out what should go in this image""" + for node in self._node.subnodes: + entry = Entry.Create(self, node) + entry.ReadNode() + self._mkimage_entries[entry.name] = entry diff --git a/roms/u-boot/tools/binman/etype/opensbi.py b/roms/u-boot/tools/binman/etype/opensbi.py new file mode 100644 index 000000000..74d473d53 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/opensbi.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com> +# +# Entry-type module for RISC-V OpenSBI binary blob +# + +from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg + +class Entry_opensbi(Entry_blob_named_by_arg): + """RISC-V OpenSBI fw_dynamic blob + + Properties / Entry arguments: + - opensbi-path: Filename of file to read into entry. This is typically + called fw_dynamic.bin + + This entry holds the run-time firmware, typically started by U-Boot SPL. + See the U-Boot README for your architecture or board for how to use it. See + https://github.com/riscv/opensbi for more information about OpenSBI. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'opensbi') + self.external = True diff --git a/roms/u-boot/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py b/roms/u-boot/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py new file mode 100644 index 000000000..3a92fa399 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2018 NXP +# +# Entry-type module for the PowerPC mpc85xx bootpg and resetvec code for U-Boot +# + +from binman.etype.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 entry 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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-br.bin' diff --git a/roms/u-boot/tools/binman/etype/scp.py b/roms/u-boot/tools/binman/etype/scp.py new file mode 100644 index 000000000..a9bee3ce8 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/scp.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Samuel Holland <samuel@sholland.org> +# +# Entry-type module for System Control Processor (SCP) firmware blob +# + +from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg + +class Entry_scp(Entry_blob_named_by_arg): + """System Control Processor (SCP) firmware blob + + Properties / Entry arguments: + - scp-path: Filename of file to read into the entry, typically scp.bin + + This entry holds firmware for an external platform-specific coprocessor. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'scp') + self.external = True diff --git a/roms/u-boot/tools/binman/etype/section.py b/roms/u-boot/tools/binman/etype/section.py new file mode 100644 index 000000000..c3bac026c --- /dev/null +++ b/roms/u-boot/tools/binman/etype/section.py @@ -0,0 +1,715 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> + +"""Entry-type module for sections (groups of entries) + +Sections are entries which can contain other entries. This allows hierarchical +images to be created. +""" + +from collections import OrderedDict +import re +import sys + +from binman.entry import Entry +from dtoc import fdt_util +from patman import tools +from patman import tout +from patman.tools import ToHexSize + + +class Entry_section(Entry): + """Entry that contains other entries + + Properties / Entry arguments: (see binman README for more information): + pad-byte: Pad byte to use when padding + sort-by-offset: True if entries should be sorted by offset, False if + they must be in-order in the device tree description + + end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32) + + skip-at-start: Number of bytes before the first entry starts. These + effectively adjust the starting offset of entries. For example, + if this is 16, then the first entry would start at 16. An entry + with offset = 20 would in fact be written at offset 4 in the image + file, since the first 16 bytes are skipped when writing. + name-prefix: Adds a prefix to the name of every entry in the section + when writing out the map + align_default: Default alignment for this section, if no alignment is + given in the entry + + Properties: + allow_missing: True if this section permits external blobs to be + missing their contents. The second will produce an image but of + course it will not work. + + Since a section is also an entry, it inherits all the properies of entries + too. + + A section is an entry which can contain other entries, thus allowing + hierarchical images to be created. See 'Sections and hierarchical images' + in the binman README for more information. + """ + def __init__(self, section, etype, node, test=False): + if not test: + super().__init__(section, etype, node) + self._entries = OrderedDict() + self._pad_byte = 0 + self._sort = False + self._skip_at_start = None + self._end_4gb = False + + def ReadNode(self): + """Read properties from the section node""" + super().ReadNode() + 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') + self.align_default = fdt_util.GetInt(self._node, 'align-default', 0) + filename = fdt_util.GetString(self._node, 'filename') + if filename: + self._filename = filename + + self._ReadEntries() + + def _ReadEntries(self): + for node in self._node.subnodes: + if node.name.startswith('hash') or node.name.startswith('signature'): + continue + entry = Entry.Create(self, node, + expanded=self.GetImage().use_expanded) + entry.ReadNode() + entry.SetPrefix(self._name_prefix) + self._entries[node.name] = entry + + 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 GetFdts(self): + fdts = {} + for entry in self._entries.values(): + fdts.update(entry.GetFdts()) + return fdts + + 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. + """ + todo = self._entries.values() + for passnum in range(3): + next_todo = [] + for entry in todo: + if not entry.ProcessFdt(fdt): + next_todo.append(entry) + todo = next_todo + if not todo: + break + if todo: + self.Raise('Internal error: Could not complete processing of Fdt: remaining %s' % + todo) + return True + + def ExpandEntries(self): + super().ExpandEntries() + for entry in self._entries.values(): + entry.ExpandEntries() + + def AddMissingProperties(self, have_image_pos): + """Add new properties to the device tree as needed for this entry""" + super().AddMissingProperties(have_image_pos) + if self.compress != 'none': + have_image_pos = False + for entry in self._entries.values(): + entry.AddMissingProperties(have_image_pos) + + def ObtainContents(self): + return self.GetEntryContents() + + def GetPaddedDataForEntry(self, entry, entry_data): + """Get the data for an entry including any padding + + Gets the entry data and uses the section pad-byte value to add padding + before and after as defined by the pad-before and pad-after properties. + This does not consider alignment. + + Args: + entry: Entry to check + + Returns: + Contents of the entry along with any pad bytes before and + after it (bytes) + """ + pad_byte = (entry._pad_byte if isinstance(entry, Entry_section) + else self._pad_byte) + + data = b'' + # Handle padding before the entry + if entry.pad_before: + data += tools.GetBytes(self._pad_byte, entry.pad_before) + + # Add in the actual entry data + data += entry_data + + # Handle padding after the entry + if entry.pad_after: + data += tools.GetBytes(self._pad_byte, entry.pad_after) + + if entry.size: + data += tools.GetBytes(pad_byte, entry.size - len(data)) + + self.Detail('GetPaddedDataForEntry: size %s' % ToHexSize(self.data)) + + return data + + def _BuildSectionData(self, required): + """Build the contents of a section + + This places all entries at the right place, dealing with padding before + and after entries. It does not do padding for the section itself (the + pad-before and pad-after properties in the section items) since that is + handled by the parent section. + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + Contents of the section (bytes) + """ + section_data = b'' + + for entry in self._entries.values(): + entry_data = entry.GetData(required) + if not required and entry_data is None: + return None + data = self.GetPaddedDataForEntry(entry, entry_data) + # Handle empty space before the entry + pad = (entry.offset or 0) - self._skip_at_start - len(section_data) + if pad > 0: + section_data += tools.GetBytes(self._pad_byte, pad) + + # Add in the actual entry data + section_data += data + + self.Detail('GetData: %d entries, total size %#x' % + (len(self._entries), len(section_data))) + return self.CompressData(section_data) + + def GetPaddedData(self, data=None): + """Get the data for a section including any padding + + Gets the section data and uses the parent section's pad-byte value to + add padding before and after as defined by the pad-before and pad-after + properties. If this is a top-level section (i.e. an image), this is the + same as GetData(), since padding is not supported. + + This does not consider alignment. + + Returns: + Contents of the section along with any pad bytes before and + after it (bytes) + """ + section = self.section or self + if data is None: + data = self.GetData() + return section.GetPaddedDataForEntry(self, data) + + def GetData(self, required=True): + """Get the contents of an entry + + This builds the contents of the section, stores this as the contents of + the section and returns it + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the section, made up for all all of its subentries. + This excludes any padding. If the section is compressed, the + compressed data is returned + """ + data = self._BuildSectionData(required) + if data is None: + return None + self.SetContents(data) + return data + + 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.GetEntryOffsets() + return {} + + def ResetForPack(self): + """Reset offset/size fields so that packing can be done again""" + super().ResetForPack() + for entry in self._entries.values(): + entry.ResetForPack() + + def Pack(self, offset): + """Pack all entries into the section""" + self._PackEntries() + if self._sort: + self._SortEntries() + self._ExpandEntries() + + data = self._BuildSectionData(True) + self.SetContents(data) + + self.CheckSize() + + offset = super().Pack(offset) + self.CheckEntries() + return offset + + def _PackEntries(self): + """Pack all entries into the section""" + offset = self._skip_at_start + for entry in self._entries.values(): + offset = entry.Pack(offset) + return offset + + def _ExpandEntries(self): + """Expand any entries that are permitted to""" + exp_entry = None + for entry in self._entries.values(): + if exp_entry: + exp_entry.ExpandToLimit(entry.offset) + exp_entry = None + if entry.expand_size: + exp_entry = entry + if exp_entry: + exp_entry.ExpandToLimit(self.size) + + def _SortEntries(self): + """Sort entries by offset""" + entries = sorted(self._entries.values(), key=lambda entry: entry.offset) + self._entries.clear() + for entry in entries: + self._entries[entry._node.name] = entry + + def CheckEntries(self): + """Check that entries do not overlap or extend outside the section""" + max_size = self.size if self.uncomp_size is None else self.uncomp_size + + offset = 0 + prev_name = 'None' + for entry in self._entries.values(): + entry.CheckEntries() + if (entry.offset < self._skip_at_start or + entry.offset + entry.size > self._skip_at_start + + max_size): + entry.Raise('Offset %#x (%d) size %#x (%d) is outside the ' + "section '%s' starting at %#x (%d) " + 'of size %#x (%d)' % + (entry.offset, entry.offset, entry.size, entry.size, + self._node.path, self._skip_at_start, + self._skip_at_start, max_size, max_size)) + if entry.offset < offset and entry.size: + entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' " + "ending at %#x (%d)" % + (entry.offset, entry.offset, prev_name, offset, offset)) + offset = entry.offset + entry.size + prev_name = entry.GetPath() + + def WriteSymbols(self, section): + """Write symbol values into binary files for access at run time""" + for entry in self._entries.values(): + entry.WriteSymbols(self) + + def SetCalculatedProperties(self): + super().SetCalculatedProperties() + for entry in self._entries.values(): + entry.SetCalculatedProperties() + + def SetImagePos(self, image_pos): + super().SetImagePos(image_pos) + if self.compress == 'none': + for entry in self._entries.values(): + entry.SetImagePos(image_pos + self.offset) + + def ProcessContents(self): + sizes_ok_base = super(Entry_section, self).ProcessContents() + sizes_ok = True + for entry in self._entries.values(): + if not entry.ProcessContents(): + sizes_ok = False + return sizes_ok and sizes_ok_base + + def WriteMap(self, fd, indent): + """Write a map of the section to a .map file + + Args: + fd: File to write the map to + """ + Entry.WriteMapLine(fd, indent, self.name, self.offset or 0, + self.size, self.image_pos) + for entry in self._entries.values(): + entry.WriteMap(fd, indent + 1) + + def GetEntries(self): + return self._entries + + def GetContentsByPhandle(self, phandle, source_entry, required): + """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 returns the contents of that entry. + + The node must be a direct subnode of this section. + + Args: + phandle: Phandle to look up (integer) + source_entry: Entry containing that phandle (used for error + reporting) + required: True if the data must be present, False if it is OK to + return None + + 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(required) + source_entry.Raise("Cannot find entry for node '%s'" % node.name) + + def LookupSymbol(self, sym_name, optional, msg, base_addr, entries=None): + """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 properties supported are: + offset + image_pos - 'base_addr' is added if this is not an end-at-4gb image + size + + 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 + base_addr: Base address of image. This is added to the returned + image_pos in most cases so that the returned position indicates + where the targetted entry/binary has actually been loaded. But + if end-at-4gb is used, this is not done, since the binary is + already assumed to be linked to the ROM position and using + execute-in-place (XIP). + + 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('_', '-') + if not entries: + entries = self._entries + entry = entries.get(entry_name) + if not entry: + if entry_name.endswith('-any'): + root = entry_name[:-4] + for name in entries: + if name.startswith(root): + rest = name[len(root):] + if rest in ['', '-img', '-nodtb']: + entry = entries[name] + if not entry: + err = ("%s: Entry '%s' not found in list (%s)" % + (msg, entry_name, ','.join(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': + value = entry.image_pos + if not self.GetImage()._end_4gb: + value += base_addr + return value + if prop_name == 'size': + return entry.size + else: + raise ValueError("%s: No such property '%s'" % (msg, prop_name)) + + def GetRootSkipAtStart(self): + """Get the skip-at-start value for the top-level section + + This is used to find out the starting offset for root section that + contains this section. If this is a top-level section then it returns + the skip-at-start offset for this section. + + This is used to get the absolute position of section within the image. + + Returns: + Integer skip-at-start value for the root section containing this + section + """ + if self.section: + return self.section.GetRootSkipAtStart() + return self._skip_at_start + + def GetStartOffset(self): + """Get the start offset for this section + + Returns: + The first available offset in this section (typically 0) + """ + return self._skip_at_start + + def GetImageSize(self): + """Get the size of the image containing this section + + Returns: + Image size as an integer number of bytes, which may be None if the + image size is dynamic and its sections have not yet been packed + """ + return self.GetImage().size + + def FindEntryType(self, etype): + """Find an entry type in the section + + Args: + etype: Entry type to find + Returns: + entry matching that type, or None if not found + """ + for entry in self._entries.values(): + if entry.etype == etype: + return entry + return None + + def GetEntryContents(self): + """Call ObtainContents() for each entry in the section + """ + todo = self._entries.values() + for passnum in range(3): + next_todo = [] + for entry in todo: + if not entry.ObtainContents(): + next_todo.append(entry) + todo = next_todo + if not todo: + break + if todo: + self.Raise('Internal error: Could not complete processing of contents: remaining %s' % + todo) + return True + + def _SetEntryOffsetSize(self, name, offset, size): + """Set the offset and size of an entry + + Args: + name: Entry name to update + offset: New offset, or None to leave alone + size: New size, or None to leave alone + """ + entry = self._entries.get(name) + if not entry: + self._Raise("Unable to set offset/size for unknown entry '%s'" % + name) + entry.SetOffsetSize(self._skip_at_start + offset if offset is not None + else None, size) + + def GetEntryOffsets(self): + """Handle entries that want to set the offset/size of other entries + + This calls each entry's GetOffsets() method. If it returns a list + of entries to update, it updates them. + """ + for entry in self._entries.values(): + offset_dict = entry.GetOffsets() + for name, info in offset_dict.items(): + self._SetEntryOffsetSize(name, *info) + + def CheckSize(self): + contents_size = len(self.data) + + size = self.size + if not size: + data = self.GetPaddedData(self.data) + size = len(data) + size = tools.Align(size, self.align_size) + + if self.size and contents_size > self.size: + self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" % + (contents_size, contents_size, self.size, self.size)) + if not self.size: + self.size = size + if self.size != tools.Align(self.size, self.align_size): + self._Raise("Size %#x (%d) does not match align-size %#x (%d)" % + (self.size, self.size, self.align_size, + self.align_size)) + return size + + def ListEntries(self, entries, indent): + """List the files in the section""" + Entry.AddEntryInfo(entries, indent, self.name, 'section', self.size, + self.image_pos, None, self.offset, self) + for entry in self._entries.values(): + entry.ListEntries(entries, indent + 1) + + def LoadData(self, decomp=True): + for entry in self._entries.values(): + entry.LoadData(decomp) + self.Detail('Loaded data') + + def GetImage(self): + """Get the image containing this section + + Note that a top-level section is actually an Image, so this function may + return self. + + Returns: + Image object containing this section + """ + if not self.section: + return self + return self.section.GetImage() + + def GetSort(self): + """Check if the entries in this section will be sorted + + Returns: + True if to be sorted, False if entries will be left in the order + they appear in the device tree + """ + return self._sort + + def ReadData(self, decomp=True): + tout.Info("ReadData path='%s'" % self.GetPath()) + parent_data = self.section.ReadData(True) + offset = self.offset - self.section._skip_at_start + data = parent_data[offset:offset + self.size] + tout.Info( + '%s: Reading data from offset %#x-%#x (real %#x), size %#x, got %#x' % + (self.GetPath(), self.offset, self.offset + self.size, offset, + self.size, len(data))) + return data + + def ReadChildData(self, child, decomp=True): + tout.Debug("ReadChildData for child '%s'" % child.GetPath()) + parent_data = self.ReadData(True) + offset = child.offset - self._skip_at_start + tout.Debug("Extract for child '%s': offset %#x, skip_at_start %#x, result %#x" % + (child.GetPath(), child.offset, self._skip_at_start, offset)) + data = parent_data[offset:offset + child.size] + if decomp: + indata = data + data = tools.Decompress(indata, child.compress) + if child.uncomp_size: + tout.Info("%s: Decompressing data size %#x with algo '%s' to data size %#x" % + (child.GetPath(), len(indata), child.compress, + len(data))) + return data + + def WriteChildData(self, child): + return True + + def SetAllowMissing(self, allow_missing): + """Set whether a section allows missing external blobs + + Args: + allow_missing: True if allowed, False if not allowed + """ + self.allow_missing = allow_missing + for entry in self._entries.values(): + entry.SetAllowMissing(allow_missing) + + def CheckMissing(self, missing_list): + """Check if any entries in this section have missing external blobs + + If there are missing blobs, the entries are added to the list + + Args: + missing_list: List of Entry objects to be added to + """ + for entry in self._entries.values(): + entry.CheckMissing(missing_list) + + def _CollectEntries(self, entries, entries_by_name, add_entry): + """Collect all the entries in an section + + This builds up a dict of entries in this section and all subsections. + Entries are indexed by path and by name. + + Since all paths are unique, entries will not have any conflicts. However + entries_by_name make have conflicts if two entries have the same name + (e.g. with different parent sections). In this case, an entry at a + higher level in the hierarchy will win over a lower-level entry. + + Args: + entries: dict to put entries: + key: entry path + value: Entry object + entries_by_name: dict to put entries + key: entry name + value: Entry object + add_entry: Entry to add + """ + entries[add_entry.GetPath()] = add_entry + to_add = add_entry.GetEntries() + if to_add: + for entry in to_add.values(): + entries[entry.GetPath()] = entry + for entry in to_add.values(): + self._CollectEntries(entries, entries_by_name, entry) + entries_by_name[add_entry.name] = add_entry + + def MissingArgs(self, entry, missing): + """Report a missing argument, if enabled + + For entries which require arguments, this reports an error if some are + missing. If missing entries are being ignored (e.g. because we read the + entry from an image rather than creating it), this function does + nothing. + + Args: + missing: List of missing properties / entry args, each a string + """ + if not self._ignore_missing: + entry.Raise('Missing required properties/entry args: %s' % + (', '.join(missing))) diff --git a/roms/u-boot/tools/binman/etype/text.py b/roms/u-boot/tools/binman/etype/text.py new file mode 100644 index 000000000..45dfcc401 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/text.py @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +from collections import OrderedDict + +from binman.entry import Entry, EntryArg +from dtoc import fdt_util +from patman import tools + + +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. + <text>: The text to place in the entry (overrides the above mechanism). + This is useful when the text is constant. + + 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" + }; + + or just:: + + text { + size = <8>; + text = "some text directly in the node" + }; + + The text is not itself nul-terminated. This can be achieved, if required, + by setting the size of the entry to something larger than the text. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + value = fdt_util.GetString(self._node, 'text') + if value: + value = tools.ToBytes(value) + else: + label, = self.GetEntryArgsOrProps([EntryArg('text-label', str)]) + self.text_label = label + if self.text_label: + value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, + str)]) + value = tools.ToBytes(value) if value is not None else value + self.value = value + + def ObtainContents(self): + if not self.value: + self.Raise("No value provided for text label '%s'" % + self.text_label) + self.SetContents(self.value) + return True diff --git a/roms/u-boot/tools/binman/etype/u_boot.py b/roms/u-boot/tools/binman/etype/u_boot.py new file mode 100644 index 000000000..e8d180a46 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for the expanded U-Boot binary +# + +from binman.entry import Entry +from binman.etype.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. + + 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. + + Note that this entry is automatically replaced with u-boot-expanded unless + --no-expanded is used or the node has a 'no-expanded' property. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.bin' diff --git a/roms/u-boot/tools/binman/etype/u_boot_dtb.py b/roms/u-boot/tools/binman/etype/u_boot_dtb.py new file mode 100644 index 000000000..65e71291d --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_dtb.py @@ -0,0 +1,31 @@ +# 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 binman.entry import Entry +from binman.etype.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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.dtb' + + def GetFdtEtype(self): + return 'u-boot-dtb' diff --git a/roms/u-boot/tools/binman/etype/u_boot_dtb_with_ucode.py b/roms/u-boot/tools/binman/etype/u_boot_dtb_with_ucode.py new file mode 100644 index 000000000..554b3b2e0 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -0,0 +1,92 @@ +# 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 binman.entry import Entry +from binman.etype.blob_dtb import Entry_blob_dtb +from patman 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): + # Put this here to allow entry-docs and help to work without libfdt + global state + from binman import state + + super().__init__(section, etype, node) + self.ucode_data = b'' + 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 GetFdtEtype(self): + return 'u-boot-dtb' + + def ProcessFdt(self, fdt): + # So the module can be loaded without it + from dtoc 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 + etype = self.GetFdtEtype() + fdt = state.GetFdtForEtype(etype) + self.ucode = fdt.GetNode('/microcode') + if not self.ucode: + raise self.Raise("No /microcode node found in '%s'" % etype) + + # 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 += 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. + super().ObtainContents() + 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/roms/u-boot/tools/binman/etype/u_boot_elf.py b/roms/u-boot/tools/binman/etype/u_boot_elf.py new file mode 100644 index 000000000..6614a75fa --- /dev/null +++ b/roms/u-boot/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 binman.entry import Entry +from binman.etype.blob import Entry_blob + +from dtoc import fdt_util +from patman 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): + super().__init__(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 + super().ReadBlobContents() + return True + + def GetDefaultFilename(self): + return 'u-boot' diff --git a/roms/u-boot/tools/binman/etype/u_boot_env.py b/roms/u-boot/tools/binman/etype/u_boot_env.py new file mode 100644 index 000000000..1694c2a6e --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_env.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +import struct +import zlib + +from binman.etype.blob import Entry_blob +from dtoc import fdt_util +from patman import tools + +class Entry_u_boot_env(Entry_blob): + """An entry which contains a U-Boot environment + + Properties / Entry arguments: + - filename: File containing the environment text, with each line in the + form var=value + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def ReadNode(self): + super().ReadNode() + if self.size is None: + self.Raise("'u-boot-env' entry must have a size property") + self.fill_value = fdt_util.GetByte(self._node, 'fill-byte', 0) + + def ReadBlobContents(self): + indata = tools.ReadFile(self._pathname) + data = b'' + for line in indata.splitlines(): + data += line + b'\0' + data += b'\0'; + pad = self.size - len(data) - 5 + if pad < 0: + self.Raise("'u-boot-env' entry too small to hold data (need %#x more bytes)" % -pad) + data += tools.GetBytes(self.fill_value, pad) + crc = zlib.crc32(data) + buf = struct.pack('<I', crc) + b'\x01' + data + self.SetContents(buf) + return True diff --git a/roms/u-boot/tools/binman/etype/u_boot_expanded.py b/roms/u-boot/tools/binman/etype/u_boot_expanded.py new file mode 100644 index 000000000..8797824c9 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_expanded.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for U-Boot binary +# + +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_expanded(Entry_blob_phase): + """U-Boot flat binary broken out into its component parts + + This is a section containing the U-Boot binary and a devicetree. Using this + entry type automatically creates this section, with the following entries + in it: + + u-boot-nodtb + u-boot-dtb + + Having the devicetree separate allows binman to update it in the final + image, so that the entries positions are provided to the running U-Boot. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'u-boot', 'u-boot-dtb', False) diff --git a/roms/u-boot/tools/binman/etype/u_boot_img.py b/roms/u-boot/tools/binman/etype/u_boot_img.py new file mode 100644 index 000000000..8a739d8ed --- /dev/null +++ b/roms/u-boot/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 binman.entry import Entry +from binman.etype.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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot.img' diff --git a/roms/u-boot/tools/binman/etype/u_boot_nodtb.py b/roms/u-boot/tools/binman/etype/u_boot_nodtb.py new file mode 100644 index 000000000..347ba7dc6 --- /dev/null +++ b/roms/u-boot/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 binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_u_boot_nodtb(Entry_blob): + """U-Boot flat binary without device tree appended + + Properties / Entry arguments: + - filename: Filename to include (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, normally expands to a + section containing u-boot and u-boot-dtb + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-nodtb.bin' diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl.py b/roms/u-boot/tools/binman/etype/u_boot_spl.py new file mode 100644 index 000000000..6f79bf59f --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl.py @@ -0,0 +1,45 @@ +# 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 +# + +from binman import elf +from binman.entry import Entry +from binman.etype.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. + + Note that this entry is automatically replaced with u-boot-spl-expanded + unless --no-expanded is used or the node has a 'no-expanded' property. + """ + def __init__(self, section, etype, node): + super().__init__(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.GetImage()) diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_bss_pad.py b/roms/u-boot/tools/binman/etype/u_boot_spl_bss_pad.py new file mode 100644 index 000000000..18c5596bd --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl_bss_pad.py @@ -0,0 +1,44 @@ +# 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. +# + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from patman 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 holds the padding added after the SPL binary to cover the BSS (Block + Started by Symbol) region. This region holds the various variables 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 contents of this entry will a certain number of zero bytes, determined + by __bss_size + + 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): + super().__init__(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(tools.GetBytes(0, bss_size)) + return True diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_dtb.py b/roms/u-boot/tools/binman/etype/u_boot_spl_dtb.py new file mode 100644 index 000000000..eefc4a44a --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl_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 in SPL (Secondary Program Loader) +# + +from binman.entry import Entry +from binman.etype.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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl.dtb' + + def GetFdtEtype(self): + return 'u-boot-spl-dtb' diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_elf.py b/roms/u-boot/tools/binman/etype/u_boot_spl_elf.py new file mode 100644 index 000000000..7f1236bcb --- /dev/null +++ b/roms/u-boot/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 binman.entry import Entry +from binman.etype.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-spl') + + This is the U-Boot SPL ELF image. It does not include a device tree but can + be relocated to any address for execution. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl' diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_expanded.py b/roms/u-boot/tools/binman/etype/u_boot_spl_expanded.py new file mode 100644 index 000000000..8e138e6a6 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl_expanded.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for expanded U-Boot SPL binary +# + +from patman import tout + +from binman import state +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_spl_expanded(Entry_blob_phase): + """U-Boot SPL flat binary broken out into its component parts + + Properties / Entry arguments: + - spl-dtb: Controls whether this entry is selected (set to 'y' or '1' to + select) + + This is a section containing the U-Boot binary, BSS padding if needed and a + devicetree. Using this entry type automatically creates this section, with + the following entries in it: + + u-boot-spl-nodtb + u-boot-spl-bss-pad + u-boot-dtb + + Having the devicetree separate allows binman to update it in the final + image, so that the entries positions are provided to the running U-Boot. + + This entry is selected based on the value of the 'spl-dtb' entryarg. If + this is non-empty (and not 'n' or '0') then this expanded entry is selected. + """ + def __init__(self, section, etype, node): + bss_pad = state.GetEntryArgBool('spl-bss-pad') + super().__init__(section, etype, node, 'u-boot-spl', 'u-boot-spl-dtb', + bss_pad) + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + val = state.GetEntryArgBool('spl-dtb') + tout.DoOutput(tout.INFO if val else tout.DETAIL, + "Node '%s': etype '%s': %s %sselected" % + (node.path, etype, new_etype, '' if val else 'not ')) + return val diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_nodtb.py b/roms/u-boot/tools/binman/etype/u_boot_spl_nodtb.py new file mode 100644 index 000000000..316b38172 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl_nodtb.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 'u-boot-spl-nodtb.bin' +# + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_u_boot_spl_nodtb(Entry_blob): + """SPL binary without device tree appended + + Properties / Entry arguments: + - filename: Filename to include (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 operate on your platform. You can add a u-boot-spl-dtb + entry after this one, or use a u-boot-spl entry instead' which normally + expands to a section containing u-boot-spl-dtb, u-boot-spl-bss-pad and + u-boot-spl-dtb + + 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): + super().__init__(section, etype, node) + self.elf_fname = 'spl/u-boot-spl' + + def GetDefaultFilename(self): + return 'spl/u-boot-spl-nodtb.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage()) diff --git a/roms/u-boot/tools/binman/etype/u_boot_spl_with_ucode_ptr.py b/roms/u-boot/tools/binman/etype/u_boot_spl_with_ucode_ptr.py new file mode 100644 index 000000000..72739a5eb --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_spl_with_ucode_ptr.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 an SPL binary with an embedded microcode pointer +# + +import struct + +from binman.etype.u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr + +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): + super().__init__(section, etype, node) + self.elf_fname = 'spl/u-boot-spl' + + def GetDefaultFilename(self): + return 'spl/u-boot-spl-nodtb.bin' diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl.py b/roms/u-boot/tools/binman/etype/u_boot_tpl.py new file mode 100644 index 000000000..0c575df8c --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl.py @@ -0,0 +1,45 @@ +# 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 +# + +from binman import elf +from binman.entry import Entry +from binman.etype.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. + + Note that this entry is automatically replaced with u-boot-tpl-expanded + unless --no-expanded is used or the node has a 'no-expanded' property. + """ + def __init__(self, section, etype, node): + super().__init__(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.GetImage()) diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_bss_pad.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_bss_pad.py new file mode 100644 index 000000000..521b24a38 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl_bss_pad.py @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for BSS padding for tpl/u-boot-tpl.bin. This padding +# can be added after the TPL binary to ensure that anything concatenated +# to it will appear to TPL to be at the end of BSS rather than the start. +# + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from patman import tools + +class Entry_u_boot_tpl_bss_pad(Entry_blob): + """U-Boot TPL binary padded with a BSS region + + Properties / Entry arguments: + None + + This holds the padding added after the TPL binary to cover the BSS (Block + Started by Symbol) region. This region holds the various variables used by + TPL. It is set to 0 by TPL when it starts up. If you want to append data to + the TPL 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 contents of this entry will a certain number of zero bytes, determined + by __bss_size + + The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since + binman uses that to look up the BSS address. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def ObtainContents(self): + fname = tools.GetInputFilename('tpl/u-boot-tpl') + bss_size = elf.GetSymbolAddress(fname, '__bss_size') + if not bss_size: + self.Raise('Expected __bss_size symbol in tpl/u-boot-tpl') + self.SetContents(tools.GetBytes(0, bss_size)) + return True diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_dtb.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_dtb.py new file mode 100644 index 000000000..2ff1d7ced --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl_dtb.py @@ -0,0 +1,28 @@ +# 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 binman.entry import Entry +from binman.etype.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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.dtb' + + def GetFdtEtype(self): + return 'u-boot-tpl-dtb' diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_dtb_with_ucode.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_dtb_with_ucode.py new file mode 100644 index 000000000..066f18dfe --- /dev/null +++ b/roms/u-boot/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 +# + +from binman.etype.u_boot_dtb_with_ucode import Entry_u_boot_dtb_with_ucode + +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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.dtb' + + def GetFdtEtype(self): + return 'u-boot-tpl-dtb' diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_elf.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_elf.py new file mode 100644 index 000000000..3f24d3aa7 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl_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 TPL ELF image +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_u_boot_tpl_elf(Entry_blob): + """U-Boot TPL ELF image + + Properties / Entry arguments: + - filename: Filename of TPL u-boot (default 'tpl/u-boot-tpl') + + This is the U-Boot TPL ELF image. It does not include a device tree but can + be relocated to any address for execution. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl' diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_expanded.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_expanded.py new file mode 100644 index 000000000..15cdac465 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl_expanded.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for expanded U-Boot TPL binary +# + +from patman import tout + +from binman import state +from binman.etype.blob_phase import Entry_blob_phase + +class Entry_u_boot_tpl_expanded(Entry_blob_phase): + """U-Boot TPL flat binary broken out into its component parts + + Properties / Entry arguments: + - tpl-dtb: Controls whether this entry is selected (set to 'y' or '1' to + select) + + This is a section containing the U-Boot binary, BSS padding if needed and a + devicetree. Using this entry type automatically creates this section, with + the following entries in it: + + u-boot-tpl-nodtb + u-boot-tpl-bss-pad + u-boot-dtb + + Having the devicetree separate allows binman to update it in the final + image, so that the entries positions are provided to the running U-Boot. + + This entry is selected based on the value of the 'tpl-dtb' entryarg. If + this is non-empty (and not 'n' or '0') then this expanded entry is selected. + """ + def __init__(self, section, etype, node): + bss_pad = state.GetEntryArgBool('tpl-bss-pad') + super().__init__(section, etype, node, 'u-boot-tpl', 'u-boot-tpl-dtb', + bss_pad) + + @classmethod + def UseExpanded(cls, node, etype, new_etype): + val = state.GetEntryArgBool('tpl-dtb') + tout.DoOutput(tout.INFO if val else tout.DETAIL, + "Node '%s': etype '%s': %s %sselected" % + (node.path, etype, new_etype, '' if val else 'not ')) + return val diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_nodtb.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_nodtb.py new file mode 100644 index 000000000..98f3853f4 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_tpl_nodtb.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass <sjg@chromium.org> +# +# Entry-type module for 'u-boot-tpl-nodtb.bin' +# + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_u_boot_tpl_nodtb(Entry_blob): + """TPL binary without device tree appended + + Properties / Entry arguments: + - filename: Filename to include (default 'tpl/u-boot-tpl-nodtb.bin') + + This is the U-Boot TPL binary, It does not include a device tree blob at + the end of it so may not be able to work without it, assuming TPL needs + a device tree to operate on your platform. You can add a u-boot-tpl-dtb + entry after this one, or use a u-boot-tpl entry instead, which normally + expands to a section containing u-boot-tpl-dtb, u-boot-tpl-bss-pad and + u-boot-tpl-dtb + + TPL 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): + super().__init__(section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl-nodtb.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section.GetImage()) diff --git a/roms/u-boot/tools/binman/etype/u_boot_tpl_with_ucode_ptr.py b/roms/u-boot/tools/binman/etype/u_boot_tpl_with_ucode_ptr.py new file mode 100644 index 000000000..c7f3f9ded --- /dev/null +++ b/roms/u-boot/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 + +from patman import command +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from binman.etype.u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr +from patman 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): + super().__init__(section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl-nodtb.bin' diff --git a/roms/u-boot/tools/binman/etype/u_boot_ucode.py b/roms/u-boot/tools/binman/etype/u_boot_ucode.py new file mode 100644 index 000000000..b4cb8cdb6 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/u_boot_ucode.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 a U-Boot binary with an embedded microcode pointer +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from patman 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): + super().__init__(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 = b'' + 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: + self.data = b'' + return True + if not fdt_entry.ready: + return False + + if not fdt_entry.collate: + # This binary can be empty + self.data = b'' + 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/roms/u-boot/tools/binman/etype/u_boot_with_ucode_ptr.py b/roms/u-boot/tools/binman/etype/u_boot_with_ucode_ptr.py new file mode 100644 index 000000000..20be22a1f --- /dev/null +++ b/roms/u-boot/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 + +from binman import elf +from binman.entry import Entry +from binman.etype.blob import Entry_blob +from dtoc import fdt_util +from patman import tools +from patman import command + +class Entry_u_boot_with_ucode_ptr(Entry_blob): + """U-Boot with embedded microcode pointer + + Properties / Entry arguments: + - filename: Filename of u-boot-nodtb.bin (default 'u-boot-nodtb.bin') + - 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): + super().__init__(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 True + + # 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 + return self.ProcessContentsUpdate(self.data[:self.target_offset] + + offset_and_size + + self.data[self.target_offset + 8:]) diff --git a/roms/u-boot/tools/binman/etype/vblock.py b/roms/u-boot/tools/binman/etype/vblock.py new file mode 100644 index 000000000..c0a6a28c9 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/vblock.py @@ -0,0 +1,95 @@ +# 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 binman.entry import EntryArg +from binman.etype.collection import Entry_collection + +from dtoc import fdt_util +from patman import tools + +class Entry_vblock(Entry_collection): + """An entry which contains a Chromium OS verified boot block + + Properties / Entry arguments: + - content: List of phandles to entries to sign + - 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): + super().__init__(section, etype, node) + (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 GetVblock(self, required): + """Get the contents of this entry + + Args: + required: True if the data must be present, False if it is OK to + return None + + Returns: + bytes content of the entry, which is the signed vblock for the + provided data + """ + # Join up the data files to be signed + input_data = self.GetContents(required) + if input_data is None: + return None + + 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) + return tools.ReadFile(output_fname) + + def ObtainContents(self): + data = self.GetVblock(False) + if data is None: + return False + self.SetContents(data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + data = self.GetVblock(True) + return self.ProcessContentsUpdate(data) diff --git a/roms/u-boot/tools/binman/etype/x86_reset16.py b/roms/u-boot/tools/binman/etype/x86_reset16.py new file mode 100644 index 000000000..5d49f16e2 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_reset16.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 the 16-bit x86 reset code for U-Boot +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_x86_reset16(Entry_blob): + """x86 16-bit reset code for U-Boot + + Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible + for jumping to the x86-start16 code, which continues execution. + + For 64-bit U-Boot, the 'x86_reset16_spl' entry type is used instead. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-x86-reset16.bin' diff --git a/roms/u-boot/tools/binman/etype/x86_reset16_spl.py b/roms/u-boot/tools/binman/etype/x86_reset16_spl.py new file mode 100644 index 000000000..775b90699 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_reset16_spl.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 the 16-bit x86 reset code for U-Boot +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_x86_reset16_spl(Entry_blob): + """x86 16-bit reset code for U-Boot + + Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible + for jumping to the x86-start16 code, which continues execution. + + For 32-bit U-Boot, the 'x86_reset_spl' entry type is used instead. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-x86-reset16-spl.bin' diff --git a/roms/u-boot/tools/binman/etype/x86_reset16_tpl.py b/roms/u-boot/tools/binman/etype/x86_reset16_tpl.py new file mode 100644 index 000000000..52d3f4869 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_reset16_tpl.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 the 16-bit x86 reset code for U-Boot +# + +from binman.entry import Entry +from binman.etype.blob import Entry_blob + +class Entry_x86_reset16_tpl(Entry_blob): + """x86 16-bit reset code for U-Boot + + Properties / Entry arguments: + - filename: Filename of u-boot-x86-reset16.bin (default + 'u-boot-x86-reset16.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_RESET_VEC_LOC. The code is responsible + for jumping to the x86-start16 code, which continues execution. + + For 32-bit U-Boot, the 'x86_reset_tpl' entry type is used instead. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-x86-reset16-tpl.bin' diff --git a/roms/u-boot/tools/binman/etype/x86_start16.py b/roms/u-boot/tools/binman/etype/x86_start16.py new file mode 100644 index 000000000..18fdd95d3 --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_start16.py @@ -0,0 +1,31 @@ +# 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 binman.entry import Entry +from binman.etype.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-start16.bin (default + 'u-boot-x86-start16.bin') + + x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code + must be placed in the top 64KB of the ROM. The reset code jumps to it. 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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'u-boot-x86-start16.bin' diff --git a/roms/u-boot/tools/binman/etype/x86_start16_spl.py b/roms/u-boot/tools/binman/etype/x86_start16_spl.py new file mode 100644 index 000000000..ac8e90f2e --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_start16_spl.py @@ -0,0 +1,31 @@ +# 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 binman.entry import Entry +from binman.etype.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-start16-spl.bin (default + 'spl/u-boot-x86-start16-spl.bin') + + x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code + must be placed in the top 64KB of the ROM. The reset code jumps to it. 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 32-bit U-Boot, the 'x86-start16' entry type is used instead. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-x86-start16-spl.bin' diff --git a/roms/u-boot/tools/binman/etype/x86_start16_tpl.py b/roms/u-boot/tools/binman/etype/x86_start16_tpl.py new file mode 100644 index 000000000..72d4608bb --- /dev/null +++ b/roms/u-boot/tools/binman/etype/x86_start16_tpl.py @@ -0,0 +1,32 @@ +# 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 binman.entry import Entry +from binman.etype.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-start16-tpl.bin (default + 'tpl/u-boot-x86-start16-tpl.bin') + + x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code + must be placed in the top 64KB of the ROM. The reset code jumps to it. 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). + + 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): + super().__init__(section, etype, node) + + def GetDefaultFilename(self): + return 'tpl/u-boot-x86-start16-tpl.bin' diff --git a/roms/u-boot/tools/binman/fdt_test.py b/roms/u-boot/tools/binman/fdt_test.py new file mode 100644 index 000000000..3e12540f6 --- /dev/null +++ b/roms/u-boot/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 + +from dtoc import fdt +from dtoc import fdt_util +from dtoc.fdt import FdtScan +from patman 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/roms/u-boot/tools/binman/fmap_util.py b/roms/u-boot/tools/binman/fmap_util.py new file mode 100644 index 000000000..827761976 --- /dev/null +++ b/roms/u-boot/tools/binman/fmap_util.py @@ -0,0 +1,118 @@ +# 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 +import sys + +from patman import tools + +# constants imported from lib/fmap.h +FMAP_SIGNATURE = b'__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): + if type(name) == bytes: + name = name.decode('utf-8') + 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] = tools.ToBytes(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/roms/u-boot/tools/binman/ftest.py b/roms/u-boot/tools/binman/ftest.py new file mode 100644 index 000000000..5383eec48 --- /dev/null +++ b/roms/u-boot/tools/binman/ftest.py @@ -0,0 +1,4546 @@ +# 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 collections +import gzip +import hashlib +from optparse import OptionParser +import os +import re +import shutil +import struct +import sys +import tempfile +import unittest + +from binman import cbfs_util +from binman import cmdline +from binman import control +from binman import elf +from binman import elf_test +from binman import fmap_util +from binman import state +from dtoc import fdt +from dtoc import fdt_util +from binman.etype import fdtmap +from binman.etype import image_header +from binman.image import Image +from patman import command +from patman import test_util +from patman import tools +from patman import tout + +# Contents of test files, corresponding to different entry types +U_BOOT_DATA = b'1234' +U_BOOT_IMG_DATA = b'img' +U_BOOT_SPL_DATA = b'56780123456789abcdefghi' +U_BOOT_TPL_DATA = b'tpl9876543210fedcbazyw' +BLOB_DATA = b'89' +ME_DATA = b'0abcd' +VGA_DATA = b'vga' +U_BOOT_DTB_DATA = b'udtb' +U_BOOT_SPL_DTB_DATA = b'spldtb' +U_BOOT_TPL_DTB_DATA = b'tpldtb' +X86_START16_DATA = b'start16' +X86_START16_SPL_DATA = b'start16spl' +X86_START16_TPL_DATA = b'start16tpl' +X86_RESET16_DATA = b'reset16' +X86_RESET16_SPL_DATA = b'reset16spl' +X86_RESET16_TPL_DATA = b'reset16tpl' +PPC_MPC85XX_BR_DATA = b'ppcmpc85xxbr' +U_BOOT_NODTB_DATA = b'nodtb with microcode pointer somewhere in here' +U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here' +U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here' +FSP_DATA = b'fsp' +CMC_DATA = b'cmc' +VBT_DATA = b'vbt' +MRC_DATA = b'mrc' +TEXT_DATA = 'text' +TEXT_DATA2 = 'text2' +TEXT_DATA3 = 'text3' +CROS_EC_RW_DATA = b'ecrw' +GBB_DATA = b'gbbd' +BMPBLK_DATA = b'bmp' +VBLOCK_DATA = b'vblk' +FILES_DATA = (b"sorry I'm late\nOh, don't bother apologising, I'm " + + b"sorry you're alive\n") +COMPRESS_DATA = b'compress xxxxxxxxxxxxxxxxxxxxxx data' +COMPRESS_DATA_BIG = COMPRESS_DATA * 2 +REFCODE_DATA = b'refcode' +FSP_M_DATA = b'fsp_m' +FSP_S_DATA = b'fsp_s' +FSP_T_DATA = b'fsp_t' +ATF_BL31_DATA = b'bl31' +OPENSBI_DATA = b'opensbi' +SCP_DATA = b'scp' +TEST_FDT1_DATA = b'fdt1' +TEST_FDT2_DATA = b'test-fdt2' +ENV_DATA = b'var1=1\nvar2="2"' + +# Subdirectory of the input dir to use to put test FDTs +TEST_FDT_SUBDIR = 'fdts' + +# The expected size for the device tree in some tests +EXTRACT_DTB_SIZE = 0x3c9 + +# Properties expected to be in the device tree when update_dtb is used +BASE_DTB_PROPS = ['offset', 'size', 'image-pos'] + +# Extra properties expected to be in the device tree when allow-repack is used +REPACK_DTB_PROPS = ['orig-offset', 'orig-size'] + + +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(cls): + global entry + from binman import entry + + # Handle the case where argv[0] is 'python' + cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + cls._binman_pathname = os.path.join(cls._binman_dir, 'binman') + + # Create a temporary directory for input files + cls._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) + cls._ResetDtbs() + + TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA) + + TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA) + TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin', + X86_START16_SPL_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin', + X86_START16_TPL_DATA) + + TestFunctional._MakeInputFile('u-boot-x86-reset16.bin', + X86_RESET16_DATA) + TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin', + X86_RESET16_SPL_DATA) + TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin', + X86_RESET16_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) + TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA) + TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA) + TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA) + + cls._elf_testdir = os.path.join(cls._indir, 'elftest') + elf_test.BuildElfTestFiles(cls._elf_testdir) + + # ELF file with a '_dt_ucode_base_size' symbol + TestFunctional._MakeInputFile('u-boot', + tools.ReadFile(cls.ElfTestFile('u_boot_ucode_ptr'))) + + # Intel flash descriptor file + cls._SetupDescriptor() + + shutil.copytree(cls.TestFile('files'), + os.path.join(cls._indir, 'files')) + + TestFunctional._MakeInputFile('compress', COMPRESS_DATA) + TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG) + TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) + TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) + TestFunctional._MakeInputFile('scp.bin', SCP_DATA) + + # Add a few .dtb files for testing + TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, + TEST_FDT1_DATA) + TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR, + TEST_FDT2_DATA) + + TestFunctional._MakeInputFile('env.txt', ENV_DATA) + + # Travis-CI may have an old lz4 + cls.have_lz4 = True + try: + tools.Run('lz4', '--no-frame-crc', '-c', + os.path.join(cls._indir, 'u-boot.bin'), binary=True) + except: + cls.have_lz4 = False + + @classmethod + def tearDownClass(cls): + """Remove the temporary input directory and its contents""" + if cls.preserve_indir: + print('Preserving input dir: %s' % cls._indir) + else: + if cls._indir: + shutil.rmtree(cls._indir) + cls._indir = None + + @classmethod + def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False, + toolpath=None, verbosity=None): + """Accept arguments controlling test execution + + Args: + preserve_indir: Preserve the shared input directory used by all + tests in this class. + preserve_outdir: Preserve the output directories used by tests. Each + test has its own, so this is normally only useful when running a + single test. + toolpath: ist of paths to use for tools + """ + cls.preserve_indir = preserve_indir + cls.preserve_outdirs = preserve_outdirs + cls.toolpath = toolpath + cls.verbosity = verbosity + + def _CheckLz4(self): + if not self.have_lz4: + self.skipTest('lz4 --no-frame-crc not available') + + def _CleanupOutputDir(self): + """Remove the temporary output directory""" + if self.preserve_outdirs: + print('Preserving output dir: %s' % tools.outdir) + else: + tools._FinaliseForTest() + + 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""" + self._CleanupOutputDir() + + def _SetupImageInTmpdir(self): + """Set up the output image in a new temporary directory + + This is used when an image has been generated in the output directory, + but we want to run binman again. This will create a new output + directory and fail to delete the original one. + + This creates a new temporary directory, copies the image to it (with a + new name) and removes the old output directory. + + Returns: + Tuple: + Temporary directory to use + New image filename + """ + image_fname = tools.GetOutputFilename('image.bin') + tmpdir = tempfile.mkdtemp(prefix='binman.') + updated_fname = os.path.join(tmpdir, 'image-updated.bin') + tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) + self._CleanupOutputDir() + return tmpdir, updated_fname + + @classmethod + def _ResetDtbs(cls): + 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, *argv): + """Run binman using directly (in the same process) + + Args: + Arguments to pass, as a list of strings + Returns: + Return value (0 for success) + """ + argv = list(argv) + args = cmdline.ParseArgs(argv) + args.pager = 'binman-invalid-pager' + args.build_dir = self._indir + + # For testing, you can force an increase in verbosity here + # args.verbosity = tout.DEBUG + return control.Binman(args) + + def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False, + entry_args=None, images=None, use_real_dtb=False, + use_expanded=False, verbosity=None, allow_missing=False, + extra_indirs=None): + """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 + 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. + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' + verbosity: Verbosity level to use (0-3, None=don't set it) + allow_missing: Set the '--allow-missing' flag so that missing + external binaries just produce a warning instead of an error + extra_indirs: Extra input directories to add using -I + """ + args = [] + if debug: + args.append('-D') + if verbosity is not None: + args.append('-v%d' % verbosity) + elif self.verbosity: + args.append('-v%d' % self.verbosity) + if self.toolpath: + for path in self.toolpath: + args += ['--toolpath', path] + args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)] + if map: + args.append('-m') + if update_dtb: + args.append('-u') + if not use_real_dtb: + args.append('--fake-dtb') + if not use_expanded: + args.append('--no-expanded') + if entry_args: + for arg, value in entry_args.items(): + args.append('-a%s=%s' % (arg, value)) + if allow_missing: + args.append('-M') + if images: + for image in images: + args += ['-i', image] + if extra_indirs: + for indir in extra_indirs: + args += ['-I', indir] + 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 + """ + tmpdir = tempfile.mkdtemp(prefix='binmant.') + dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir) + with open(dtb, 'rb') as fd: + data = fd.read() + TestFunctional._MakeInputFile(outfile, data) + shutil.rmtree(tmpdir) + 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. + + Args: + dtb_data: dtb data to modify (this should be a value devicetree) + name: Name of a new property to add + + Returns: + New dtb data with the property added + """ + 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, use_expanded=False, + map=False, update_dtb=False, entry_args=None, + reset_dtbs=True, extra_indirs=None): + """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. + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' + 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 + reset_dtbs: With use_real_dtb the test dtb is overwritten by this + function. If reset_dtbs is True, then the original test dtb + is written back before this function finishes + extra_indirs: Extra input directories to add using -I + + 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) + + # 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, + use_expanded=use_expanded, extra_indirs=extra_indirs) + 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, 'rb') 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 _DoReadFileRealDtb(self, fname): + """Run binman with a real .dtb file and return the resulting data + + Args: + fname: DT source filename to use (e.g. 082_fdt_update_all.dts) + + Returns: + Resulting image contents + """ + return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0] + + 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(cls, fname, contents): + """Create a new test input file, creating directories as needed + + Args: + fname: Filename to create + contents: File contents to write in to the file + Returns: + Full pathname of file created + """ + pathname = os.path.join(cls._indir, fname) + 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(cls, 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(cls._indir, dirname) + if not os.path.exists(pathname): + os.makedirs(pathname) + return pathname + + @classmethod + def _SetupSplElf(cls, 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 + """ + TestFunctional._MakeInputFile('spl/u-boot-spl', + tools.ReadFile(cls.ElfTestFile(src_fname))) + + @classmethod + def _SetupTplElf(cls, src_fname='bss_data'): + """Set up an ELF file with a '_dt_ucode_base_size' symbol + + Args: + Filename of ELF file to use as TPL + """ + TestFunctional._MakeInputFile('tpl/u-boot-tpl', + tools.ReadFile(cls.ElfTestFile(src_fname))) + + @classmethod + def _SetupDescriptor(cls): + with open(cls.TestFile('descriptor.bin'), 'rb') as fd: + TestFunctional._MakeInputFile('descriptor.bin', fd.read()) + + @classmethod + def TestFile(cls, fname): + return os.path.join(cls._binman_dir, 'test', fname) + + @classmethod + def ElfTestFile(cls, fname): + return os.path.join(cls._elf_testdir, 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: '%s' 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, prefix='/binman/'): + def AddNode(node, path): + if node.name != '/': + path += '/' + node.name + for prop in node.props.values(): + if prop.name in prop_names: + prop_path = path + ':' + prop.name + tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu( + prop.value) + for subnode in node.subnodes: + 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.rst') + # 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.rst') + 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('build', '-n', '-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('build') + 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('build', '-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('build', '-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('build', '-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('build', '-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('build', '-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""" + 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, 'rb') 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, 'rb') as fd: + data = fd.read() + self.assertEqual(U_BOOT_DATA, data[3:7]) + self.assertEqual(tools.GetBytes(0, 3), data[:3]) + self.assertEqual(tools.GetBytes(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""" + data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts', + update_dtb=True) + + 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) + self.assertEqual(U_BOOT_DATA, entry.data) + self.assertEqual(tools.GetBytes(0, 3) + U_BOOT_DATA + + tools.GetBytes(0, 5), data[:entry.size]) + pos = 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(pos, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + self.assertEqual(U_BOOT_DATA, entry.data) + self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size]) + pos += 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(pos, entry.offset) + self.assertEqual(32, entry.size) + self.assertEqual(U_BOOT_DATA, entry.data) + self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 32 - len(U_BOOT_DATA)), + data[pos:pos + entry.size]) + pos += 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) + self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) + self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 16 - len(U_BOOT_DATA)), + data[pos:pos + entry.size]) + pos += 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.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)]) + self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 64 - len(U_BOOT_DATA)), + data[pos:pos + entry.size]) + + self.CheckNoGaps(entries) + self.assertEqual(128, image.size) + + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos']) + expected = { + 'image-pos': 0, + 'offset': 0, + 'size': 128, + + 'u-boot:image-pos': 0, + 'u-boot:offset': 0, + 'u-boot:size': 3 + 5 + len(U_BOOT_DATA), + + 'u-boot-align-size-nop:image-pos': 12, + 'u-boot-align-size-nop:offset': 12, + 'u-boot-align-size-nop:size': 4, + + 'u-boot-align-size:image-pos': 16, + 'u-boot-align-size:offset': 16, + 'u-boot-align-size:size': 32, + + 'u-boot-align-end:image-pos': 48, + 'u-boot-align-end:offset': 48, + 'u-boot-align-end:size': 16, + + 'u-boot-align-both:image-pos': 64, + 'u-boot-align-both:offset': 64, + 'u-boot-align-both:size': 64, + } + self.assertEqual(expected, props) + + 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("Image '/binman': Alignment size 131 must be a power of " + "two", str(e.exception)) + + def testImagePadByte(self): + """Test that the image pad byte can be specified""" + self._SetupSplElf() + data = self._DoReadFile('021_image_pad.dts') + self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(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(tools.GetBytes(0, 1) + U_BOOT_SPL_DATA + + tools.GetBytes(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("Image '/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('098_4gb_and_skip_at_start_together.dts') + self.assertIn("Image '/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) size 0x4 (4) " + "is outside the section '/binman' starting at " + '0xffffffe0 (4294967264) of size 0x20 (32)', + 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 + tools.GetBytes(0, 3) + U_BOOT_SPL_DATA + + tools.GetBytes(0, 2), data) + + def testPackX86RomMeNoDesc(self): + """Test that an invalid Intel descriptor entry is detected""" + try: + TestFunctional._MakeInputFile('descriptor-empty.bin', b'') + with self.assertRaises(ValueError) as e: + self._DoTestFile('163_x86_rom_me_empty.dts') + self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature", + str(e.exception)) + finally: + self._SetupDescriptor() + + 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') + expected_desc = tools.ReadFile(self.TestFile('descriptor.bin')) + if data[:0x1000] != expected_desc: + self.fail('Expected descriptor binary at start of image') + 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('150_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(b'nodtb with microcode' + pos_and_size + + b' 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(b'nodtb with microcode' + pos_and_size + + b' 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: + TestFunctional._MakeInputFile('u-boot', + tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr'))) + + 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 + TestFunctional._MakeInputFile('u-boot', + tools.ReadFile(self.ElfTestFile('u_boot_ucode_ptr'))) + + 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)""" + TestFunctional._MakeInputFile('u-boot', + tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr'))) + 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(tools.GetBytes(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 + tools.GetBytes(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(b'splnodtb with microc' + pos_and_size + + b'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""" + self._SetupSplElf() + data = self._DoReadFile('052_u_boot_spl_nodtb.dts') + self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) + + def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None, + use_expanded=False): + """Check the image contains the expected symbol values + + Args: + dts: Device tree file to use for test + base_data: Data before and after 'u-boot' section + u_boot_offset: Offset of 'u-boot' section in image + entry_args: Dict of entry args to supply to binman + key: arg name + value: value of that arg + use_expanded: True to use expanded entries where available, e.g. + 'u-boot-expanded' instead of 'u-boot' + """ + elf_fname = self.ElfTestFile('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_any_prop_offset'].address, + addr) + + self._SetupSplElf('u_boot_binman_syms') + data = self._DoReadFileDtb(dts, entry_args=entry_args, + use_expanded=use_expanded)[0] + # The image should contain the symbols from u_boot_binman_syms.c + # Note that image_pos is adjusted by the base address of the image, + # which is 0x10 in our test image + sym_values = struct.pack('<LQLL', 0x00, + u_boot_offset + len(U_BOOT_DATA), + 0x10 + u_boot_offset, 0x04) + expected = (sym_values + base_data[20:] + + tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values + + base_data[20:]) + self.assertEqual(expected, data) + + def testSymbols(self): + """Test binman can assign symbols embedded in U-Boot""" + self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18) + + def testSymbolsNoDtb(self): + """Test binman can assign symbols embedded in U-Boot SPL""" + self.checkSymbols('196_symbols_nodtb.dts', + U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA, + 0x38) + + 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 + tools.GetBytes(ord('!'), 12) + + U_BOOT_DATA + tools.GetBytes(ord('a'), 12) + + U_BOOT_DATA + tools.GetBytes(ord('&'), 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("Image '/binman': Internal error: Could not complete " + "processing of contents: remaining [" + "<binman.etype._testing.Entry__testing ", str(e.exception)) + + def testBadChangeSize(self): + """Test that trying to change the size of an entry fails""" + try: + state.SetAllowEntryExpansion(False) + 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 3", + str(e.exception)) + finally: + state.SetAllowEntryExpansion(True) + + 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, BASE_DTB_PROPS + REPACK_DTB_PROPS) + self.assertEqual({ + 'image-pos': 0, + 'offset': 0, + '_testing:offset': 32, + '_testing:size': 2, + '_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 ' + '[<binman.etype._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 = ['build', '-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 = (tools.ToBytes(TEXT_DATA) + + tools.GetBytes(0, 8 - len(TEXT_DATA)) + + tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) + + b'some text' + b'more 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(control.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(control.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 + tools.GetBytes(ord('!'), 12) + + U_BOOT_DATA + tools.GetBytes(ord('a'), 12)) + self.assertEqual(expected, data[:32]) + self.assertEqual(b'__FMAP__', fhdr.signature) + self.assertEqual(1, fhdr.ver_major) + self.assertEqual(0, fhdr.ver_minor) + self.assertEqual(0, fhdr.base) + expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5 + self.assertEqual(16 + 16 + expect_size, fhdr.image_size) + self.assertEqual(b'FMAP', fhdr.name) + self.assertEqual(5, fhdr.nareas) + fiter = iter(fentries) + + fentry = next(fiter) + self.assertEqual(b'SECTION0', fentry.name) + self.assertEqual(0, fentry.offset) + self.assertEqual(16, fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'RO_U_BOOT', fentry.name) + self.assertEqual(0, fentry.offset) + self.assertEqual(4, fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'SECTION1', fentry.name) + self.assertEqual(16, fentry.offset) + self.assertEqual(16, fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'RW_U_BOOT', fentry.name) + self.assertEqual(16, fentry.offset) + self.assertEqual(4, fentry.size) + self.assertEqual(0, fentry.flags) + + fentry = next(fiter) + self.assertEqual(b'FMAP', fentry.name) + self.assertEqual(32, fentry.offset) + self.assertEqual(expect_size, fentry.size) + self.assertEqual(0, fentry.flags) + + 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', + } + 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 = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8) + 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, 'ab') 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 + tools.GetBytes(0, 8) + + tools.GetBytes(0, 0x2180 - 16)) + 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 + + The expected pipe is: + + [('futility', 'vbutil_firmware', '--vblock', + 'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock', + '--signprivate', 'devkeys/firmware_data_key.vbprivk', + '--version', '1', '--fv', 'input.vblock', '--kernelkey', + 'devkeys/kernel_subkey.vbpubk', '--flags', '1')] + + This writes to the output file (here, 'vblock.vblock'). If + self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash + of the input data (here, 'input.vblock'). + """ + if pipe_list[0][0] == 'futility': + fname = pipe_list[0][3] + with open(fname, 'wb') as fd: + if self._hash_data: + infile = pipe_list[0][11] + m = hashlib.sha256() + data = tools.ReadFile(infile) + m.update(data) + fd.write(m.digest()) + else: + fd.write(VBLOCK_DATA) + + return command.CommandResult() + + def testVblock(self): + """Test for the Chromium OS Verified Boot Block""" + self._hash_data = False + 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': Collection 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 testVblockContent(self): + """Test that the vblock signs the right data""" + self._hash_data = True + command.test_result = self._HandleVblockCommand + entry_args = { + 'keydir': 'devkeys', + } + data = self._DoReadFileDtb( + '189_vblock_content.dts', use_real_dtb=True, update_dtb=True, + entry_args=entry_args)[0] + hashlen = 32 # SHA256 hash is 32 bytes + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + hashval = data[-hashlen:] + dtb = data[len(U_BOOT_DATA):-hashlen] + + expected_data = U_BOOT_DATA + dtb + + # The hashval should be a hash of the dtb + m = hashlib.sha256() + m.update(expected_data) + expected_hashval = m.digest() + self.assertEqual(expected_hashval, hashval) + + def testTpl(self): + """Test that an image with TPL and its device tree can be created""" + # ELF file with a '__bss_size' symbol + self._SetupTplElf() + 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(tools.GetBytes(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""" + expected = 'Skipping images: image1' + + # We should only get the expected message in verbose mode + for verbosity in (0, 2): + with test_util.capture_sys_output() as (stdout, stderr): + retcode = self._DoTestFile('006_dual_image.dts', + verbosity=verbosity, + images=['image2']) + self.assertEqual(0, retcode) + if verbosity: + self.assertIn(expected, stdout.getvalue()) + else: + self.assertNotIn(expected, stdout.getvalue()) + + self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin'))) + self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin'))) + self._CleanupOutputDir() + + def testUpdateFdtAll(self): + """Test that all device trees are updated with offset/size info""" + data = self._DoReadFileRealDtb('082_fdt_update_all.dts') + + 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, BASE_DTB_PROPS + REPACK_DTB_PROPS + + ['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. + 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): + return tools.Decompress(data, 'lz4') + + def testCompress(self): + """Test compression of blobs""" + self._CheckLz4() + 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) + + # Do a sanity check on various fields + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(1, len(entries)) + + entry = entries['blob'] + self.assertEqual(COMPRESS_DATA, entry.uncomp_data) + self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size) + orig = self._decompress(entry.data) + self.assertEqual(orig, entry.uncomp_data) + + self.assertEqual(image.data, entry.data) + + 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""" + self._CheckLz4() + data = self._DoReadFile('085_files_compress.dts') + + image = control.images['image'] + entries = image.GetEntries() + files = entries['files'] + entries = files._entries + + orig = b'' + 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 = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA + + MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA + + tools.GetBytes(ord('c'), 8) + U_BOOT_DATA + + tools.GetBytes(ord('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(), b''.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(tools.GetBytes(ord('a'), 16)) + self.assertEqual(m.digest(), b''.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 + """ + self._SetupTplElf('u_boot_ucode_ptr') + first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts', + U_BOOT_TPL_NODTB_DATA) + self.assertEqual(b'tplnodtb with microc' + pos_and_size + + b'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 + tools.GetBytes(ord('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(b'U_BOOT', fentries[0].name) + + self.assertEqual(4, fentries[1].offset) + self.assertEqual(3, fentries[1].size) + self.assertEqual(b'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(b'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 + tools.GetBytes(ord('b'), 32 - 7) + self.assertEqual(expected, data[:32]) + fhdr, fentries = fmap_util.DecodeFmap(data[36:]) + + self.assertEqual(0x180, fhdr.image_size) + expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4 + fiter = iter(fentries) + + fentry = next(fiter) + self.assertEqual(b'U_BOOT', fentry.name) + self.assertEqual(0, fentry.offset) + self.assertEqual(4, fentry.size) + + fentry = next(fiter) + self.assertEqual(b'SECTION', fentry.name) + self.assertEqual(4, fentry.offset) + self.assertEqual(0x20 + expect_size, fentry.size) + + fentry = next(fiter) + self.assertEqual(b'INTEL_MRC', fentry.name) + self.assertEqual(4, fentry.offset) + self.assertEqual(3, fentry.size) + + fentry = next(fiter) + self.assertEqual(b'FMAP', fentry.name) + self.assertEqual(36, fentry.offset) + self.assertEqual(expect_size, fentry.size) + + def testElf(self): + """Basic test of ELF entries""" + self._SetupSplElf() + self._SetupTplElf() + with open(self.ElfTestFile('bss_data'), 'rb') as fd: + TestFunctional._MakeInputFile('-boot', fd.read()) + data = self._DoReadFile('096_elf.dts') + + def testElfStrip(self): + """Basic test of ELF entries""" + self._SetupSplElf() + with open(self.ElfTestFile('bss_data'), 'rb') 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, binary=False) + self.assertEqual('''ImagePos Offset Size Name +<none> 00000000 00000008 main-section +<none> 00000000 00000004 u-boot +<none> 00000003 00000004 u-boot-align +''', map_data) + + def testPackRefCode(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)]) + + def testSectionOffset(self): + """Tests use of a section with an offset""" + data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts', + map=True) + self.assertEqual('''ImagePos Offset Size Name +00000000 00000000 00000038 main-section +00000004 00000004 00000010 section@0 +00000004 00000000 00000004 u-boot +00000018 00000018 00000010 section@1 +00000018 00000000 00000004 u-boot +0000002c 0000002c 00000004 section@2 +0000002c 00000000 00000004 u-boot +''', map_data) + self.assertEqual(data, + tools.GetBytes(0x26, 4) + U_BOOT_DATA + + tools.GetBytes(0x21, 12) + + tools.GetBytes(0x26, 4) + U_BOOT_DATA + + tools.GetBytes(0x61, 12) + + tools.GetBytes(0x26, 4) + U_BOOT_DATA + + tools.GetBytes(0x26, 8)) + + def testCbfsRaw(self): + """Test base handling of a Coreboot Filesystem (CBFS) + + The exact contents of the CBFS is verified by similar tests in + cbfs_util_test.py. The tests here merely check that the files added to + the CBFS can be found in the final image. + """ + data = self._DoReadFile('102_cbfs_raw.dts') + size = 0xb0 + + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(size, cbfs.rom_size) + + self.assertIn('u-boot-dtb', cbfs.files) + cfile = cbfs.files['u-boot-dtb'] + self.assertEqual(U_BOOT_DTB_DATA, cfile.data) + + def testCbfsArch(self): + """Test on non-x86 architecture""" + data = self._DoReadFile('103_cbfs_raw_ppc.dts') + size = 0x100 + + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(size, cbfs.rom_size) + + self.assertIn('u-boot-dtb', cbfs.files) + cfile = cbfs.files['u-boot-dtb'] + self.assertEqual(U_BOOT_DTB_DATA, cfile.data) + + def testCbfsStage(self): + """Tests handling of a Coreboot Filesystem (CBFS)""" + if not elf.ELF_TOOLS: + self.skipTest('Python elftools not available') + elf_fname = os.path.join(self._indir, 'cbfs-stage.elf') + elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA) + size = 0xb0 + + data = self._DoReadFile('104_cbfs_stage.dts') + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(size, cbfs.rom_size) + + self.assertIn('u-boot', cbfs.files) + cfile = cbfs.files['u-boot'] + self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data) + + def testCbfsRawCompress(self): + """Test handling of compressing raw files""" + self._CheckLz4() + data = self._DoReadFile('105_cbfs_raw_compress.dts') + size = 0x140 + + cbfs = cbfs_util.CbfsReader(data) + self.assertIn('u-boot', cbfs.files) + cfile = cbfs.files['u-boot'] + self.assertEqual(COMPRESS_DATA, cfile.data) + + def testCbfsBadArch(self): + """Test handling of a bad architecture""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('106_cbfs_bad_arch.dts') + self.assertIn("Invalid architecture 'bad-arch'", str(e.exception)) + + def testCbfsNoSize(self): + """Test handling of a missing size property""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('107_cbfs_no_size.dts') + self.assertIn('entry must have a size property', str(e.exception)) + + def testCbfsNoCOntents(self): + """Test handling of a CBFS entry which does not provide contentsy""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('108_cbfs_no_contents.dts') + self.assertIn('Could not complete processing of contents', + str(e.exception)) + + def testCbfsBadCompress(self): + """Test handling of a bad architecture""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('109_cbfs_bad_compress.dts') + self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'", + str(e.exception)) + + def testCbfsNamedEntries(self): + """Test handling of named entries""" + data = self._DoReadFile('110_cbfs_name.dts') + + cbfs = cbfs_util.CbfsReader(data) + self.assertIn('FRED', cbfs.files) + cfile1 = cbfs.files['FRED'] + self.assertEqual(U_BOOT_DATA, cfile1.data) + + self.assertIn('hello', cbfs.files) + cfile2 = cbfs.files['hello'] + self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) + + def _SetupIfwi(self, fname): + """Set up to run an IFWI test + + Args: + fname: Filename of input file to provide (fitimage.bin or ifwi.bin) + """ + self._SetupSplElf() + self._SetupTplElf() + + # Intel Integrated Firmware Image (IFWI) file + with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd: + data = fd.read() + TestFunctional._MakeInputFile(fname,data) + + def _CheckIfwi(self, data): + """Check that an image with an IFWI contains the correct output + + Args: + data: Conents of output file + """ + expected_desc = tools.ReadFile(self.TestFile('descriptor.bin')) + if data[:0x1000] != expected_desc: + self.fail('Expected descriptor binary at start of image') + + # We expect to find the TPL wil in subpart IBBP entry IBBL + image_fname = tools.GetOutputFilename('image.bin') + tpl_fname = tools.GetOutputFilename('tpl.out') + tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname, + subpart='IBBP', entry_name='IBBL') + + tpl_data = tools.ReadFile(tpl_fname) + self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)]) + + def testPackX86RomIfwi(self): + """Test that an x86 ROM with Integrated Firmware Image can be created""" + self._SetupIfwi('fitimage.bin') + data = self._DoReadFile('111_x86_rom_ifwi.dts') + self._CheckIfwi(data) + + def testPackX86RomIfwiNoDesc(self): + """Test that an x86 ROM with IFWI can be created from an ifwi.bin file""" + self._SetupIfwi('ifwi.bin') + data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts') + self._CheckIfwi(data) + + def testPackX86RomIfwiNoData(self): + """Test that an x86 ROM with IFWI handles missing data""" + self._SetupIfwi('ifwi.bin') + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts') + self.assertIn('Could not complete processing of contents', + str(e.exception)) + + def testCbfsOffset(self): + """Test a CBFS with files at particular offsets + + Like all CFBS tests, this is just checking the logic that calls + cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()). + """ + data = self._DoReadFile('114_cbfs_offset.dts') + size = 0x200 + + cbfs = cbfs_util.CbfsReader(data) + self.assertEqual(size, cbfs.rom_size) + + self.assertIn('u-boot', cbfs.files) + cfile = cbfs.files['u-boot'] + self.assertEqual(U_BOOT_DATA, cfile.data) + self.assertEqual(0x40, cfile.cbfs_offset) + + self.assertIn('u-boot-dtb', cbfs.files) + cfile2 = cbfs.files['u-boot-dtb'] + self.assertEqual(U_BOOT_DTB_DATA, cfile2.data) + self.assertEqual(0x140, cfile2.cbfs_offset) + + def testFdtmap(self): + """Test an FDT map can be inserted in the image""" + data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts') + fdtmap_data = data[len(U_BOOT_DATA):] + magic = fdtmap_data[:8] + self.assertEqual(b'_FDTMAP_', magic) + self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16]) + + fdt_data = fdtmap_data[16:] + dtb = fdt.Fdt.FromData(fdt_data) + dtb.Scan() + props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') + self.assertEqual({ + 'image-pos': 0, + 'offset': 0, + 'u-boot:offset': 0, + 'u-boot:size': len(U_BOOT_DATA), + 'u-boot:image-pos': 0, + 'fdtmap:image-pos': 4, + 'fdtmap:offset': 4, + 'fdtmap:size': len(fdtmap_data), + 'size': len(data), + }, props) + + def testFdtmapNoMatch(self): + """Check handling of an FDT map when the section cannot be found""" + self.data = self._DoReadFileRealDtb('115_fdtmap.dts') + + # Mangle the section name, which should cause a mismatch between the + # correct FDT path and the one expected by the section + image = control.images['image'] + image._node.path += '-suffix' + entries = image.GetEntries() + fdtmap = entries['fdtmap'] + with self.assertRaises(ValueError) as e: + fdtmap._GetFdtmap() + self.assertIn("Cannot locate node for path '/binman-suffix'", + str(e.exception)) + + def testFdtmapHeader(self): + """Test an FDT map and image header can be inserted in the image""" + data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts') + fdtmap_pos = len(U_BOOT_DATA) + fdtmap_data = data[fdtmap_pos:] + fdt_data = fdtmap_data[16:] + dtb = fdt.Fdt.FromData(fdt_data) + fdt_size = dtb.GetFdtObj().totalsize() + hdr_data = data[-8:] + self.assertEqual(b'BinM', hdr_data[:4]) + offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff + self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32)) + + def testFdtmapHeaderStart(self): + """Test an image header can be inserted at the image start""" + data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') + fdtmap_pos = 0x100 + len(U_BOOT_DATA) + hdr_data = data[:8] + self.assertEqual(b'BinM', hdr_data[:4]) + offset = struct.unpack('<I', hdr_data[4:])[0] + self.assertEqual(fdtmap_pos, offset) + + def testFdtmapHeaderPos(self): + """Test an image header can be inserted at a chosen position""" + data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts') + fdtmap_pos = 0x100 + len(U_BOOT_DATA) + hdr_data = data[0x80:0x88] + self.assertEqual(b'BinM', hdr_data[:4]) + offset = struct.unpack('<I', hdr_data[4:])[0] + self.assertEqual(fdtmap_pos, offset) + + def testHeaderMissingFdtmap(self): + """Test an image header requires an fdtmap""" + with self.assertRaises(ValueError) as e: + self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts') + self.assertIn("'image_header' section must have an 'fdtmap' sibling", + str(e.exception)) + + def testHeaderNoLocation(self): + """Test an image header with a no specified location is detected""" + with self.assertRaises(ValueError) as e: + self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts') + self.assertIn("Invalid location 'None', expected 'start' or 'end'", + str(e.exception)) + + def testEntryExpand(self): + """Test expanding an entry after it is packed""" + data = self._DoReadFile('121_entry_expand.dts') + self.assertEqual(b'aaa', data[:3]) + self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) + self.assertEqual(b'aaa', data[-3:]) + + def testEntryExpandBad(self): + """Test expanding an entry after it is packed, twice""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('122_entry_expand_twice.dts') + self.assertIn("Image '/binman': Entries changed size after packing", + str(e.exception)) + + def testEntryExpandSection(self): + """Test expanding an entry within a section after it is packed""" + data = self._DoReadFile('123_entry_expand_section.dts') + self.assertEqual(b'aaa', data[:3]) + self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)]) + self.assertEqual(b'aaa', data[-3:]) + + def testCompressDtb(self): + """Test that compress of device-tree files is supported""" + self._CheckLz4() + data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts') + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + comp_data = data[len(U_BOOT_DATA):] + orig = self._decompress(comp_data) + dtb = fdt.Fdt.FromData(orig) + dtb.Scan() + props = self._GetPropTree(dtb, ['size', 'uncomp-size']) + expected = { + 'u-boot:size': len(U_BOOT_DATA), + 'u-boot-dtb:uncomp-size': len(orig), + 'u-boot-dtb:size': len(comp_data), + 'size': len(data), + } + self.assertEqual(expected, props) + + def testCbfsUpdateFdt(self): + """Test that we can update the device tree with CBFS offset/size info""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts', + update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size']) + del props['cbfs/u-boot:size'] + self.assertEqual({ + 'offset': 0, + 'size': len(data), + 'image-pos': 0, + 'cbfs:offset': 0, + 'cbfs:size': len(data), + 'cbfs:image-pos': 0, + 'cbfs/u-boot:offset': 0x38, + 'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA), + 'cbfs/u-boot:image-pos': 0x38, + 'cbfs/u-boot-dtb:offset': 0xb8, + 'cbfs/u-boot-dtb:size': len(U_BOOT_DATA), + 'cbfs/u-boot-dtb:image-pos': 0xb8, + }, props) + + def testCbfsBadType(self): + """Test an image header with a no specified location is detected""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('126_cbfs_bad_type.dts') + self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception)) + + def testList(self): + """Test listing the files in an image""" + self._CheckLz4() + data = self._DoReadFile('127_list.dts') + image = control.images['image'] + entries = image.BuildEntryList() + self.assertEqual(7, len(entries)) + + ent = entries[0] + self.assertEqual(0, ent.indent) + self.assertEqual('main-section', ent.name) + self.assertEqual('section', ent.etype) + self.assertEqual(len(data), ent.size) + self.assertEqual(0, ent.image_pos) + self.assertEqual(None, ent.uncomp_size) + self.assertEqual(0, ent.offset) + + ent = entries[1] + self.assertEqual(1, ent.indent) + self.assertEqual('u-boot', ent.name) + self.assertEqual('u-boot', ent.etype) + self.assertEqual(len(U_BOOT_DATA), ent.size) + self.assertEqual(0, ent.image_pos) + self.assertEqual(None, ent.uncomp_size) + self.assertEqual(0, ent.offset) + + ent = entries[2] + self.assertEqual(1, ent.indent) + self.assertEqual('section', ent.name) + self.assertEqual('section', ent.etype) + section_size = ent.size + self.assertEqual(0x100, ent.image_pos) + self.assertEqual(None, ent.uncomp_size) + self.assertEqual(0x100, ent.offset) + + ent = entries[3] + self.assertEqual(2, ent.indent) + self.assertEqual('cbfs', ent.name) + self.assertEqual('cbfs', ent.etype) + self.assertEqual(0x400, ent.size) + self.assertEqual(0x100, ent.image_pos) + self.assertEqual(None, ent.uncomp_size) + self.assertEqual(0, ent.offset) + + ent = entries[4] + self.assertEqual(3, ent.indent) + self.assertEqual('u-boot', ent.name) + self.assertEqual('u-boot', ent.etype) + self.assertEqual(len(U_BOOT_DATA), ent.size) + self.assertEqual(0x138, ent.image_pos) + self.assertEqual(None, ent.uncomp_size) + self.assertEqual(0x38, ent.offset) + + ent = entries[5] + self.assertEqual(3, ent.indent) + self.assertEqual('u-boot-dtb', ent.name) + self.assertEqual('text', ent.etype) + self.assertGreater(len(COMPRESS_DATA), ent.size) + self.assertEqual(0x178, ent.image_pos) + self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size) + self.assertEqual(0x78, ent.offset) + + ent = entries[6] + self.assertEqual(2, ent.indent) + self.assertEqual('u-boot-dtb', ent.name) + self.assertEqual('u-boot-dtb', ent.etype) + self.assertEqual(0x500, ent.image_pos) + self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size) + dtb_size = ent.size + # Compressing this data expands it since headers are added + self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA)) + self.assertEqual(0x400, ent.offset) + + self.assertEqual(len(data), 0x100 + section_size) + self.assertEqual(section_size, 0x400 + dtb_size) + + def testFindFdtmap(self): + """Test locating an FDT map in an image""" + self._CheckLz4() + data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') + image = control.images['image'] + entries = image.GetEntries() + entry = entries['fdtmap'] + self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data)) + + def testFindFdtmapMissing(self): + """Test failing to locate an FDP map""" + data = self._DoReadFile('005_simple.dts') + self.assertEqual(None, fdtmap.LocateFdtmap(data)) + + def testFindImageHeader(self): + """Test locating a image header""" + self._CheckLz4() + data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') + image = control.images['image'] + entries = image.GetEntries() + entry = entries['fdtmap'] + # The header should point to the FDT map + self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) + + def testFindImageHeaderStart(self): + """Test locating a image header located at the start of an image""" + data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts') + image = control.images['image'] + entries = image.GetEntries() + entry = entries['fdtmap'] + # The header should point to the FDT map + self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data)) + + def testFindImageHeaderMissing(self): + """Test failing to locate an image header""" + data = self._DoReadFile('005_simple.dts') + self.assertEqual(None, image_header.LocateHeaderOffset(data)) + + def testReadImage(self): + """Test reading an image and accessing its FDT map""" + self._CheckLz4() + data = self.data = self._DoReadFileRealDtb('128_decode_image.dts') + image_fname = tools.GetOutputFilename('image.bin') + orig_image = control.images['image'] + image = Image.FromFile(image_fname) + self.assertEqual(orig_image.GetEntries().keys(), + image.GetEntries().keys()) + + orig_entry = orig_image.GetEntries()['fdtmap'] + entry = image.GetEntries()['fdtmap'] + self.assertEquals(orig_entry.offset, entry.offset) + self.assertEquals(orig_entry.size, entry.size) + self.assertEquals(orig_entry.image_pos, entry.image_pos) + + def testReadImageNoHeader(self): + """Test accessing an image's FDT map without an image header""" + self._CheckLz4() + data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts') + image_fname = tools.GetOutputFilename('image.bin') + image = Image.FromFile(image_fname) + self.assertTrue(isinstance(image, Image)) + self.assertEqual('image', image.image_name[-5:]) + + def testReadImageFail(self): + """Test failing to read an image image's FDT map""" + self._DoReadFile('005_simple.dts') + image_fname = tools.GetOutputFilename('image.bin') + with self.assertRaises(ValueError) as e: + image = Image.FromFile(image_fname) + self.assertIn("Cannot find FDT map in image", str(e.exception)) + + def testListCmd(self): + """Test listing the files in an image using an Fdtmap""" + self._CheckLz4() + data = self._DoReadFileRealDtb('130_list_fdtmap.dts') + + # lz4 compression size differs depending on the version + image = control.images['image'] + entries = image.GetEntries() + section_size = entries['section'].size + fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size + fdtmap_offset = entries['fdtmap'].offset + + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + with test_util.capture_sys_output() as (stdout, stderr): + self._DoBinman('ls', '-i', updated_fname) + finally: + shutil.rmtree(tmpdir) + lines = stdout.getvalue().splitlines() + expected = [ +'Name Image-pos Size Entry-type Offset Uncomp-size', +'----------------------------------------------------------------------', +'main-section 0 c00 section 0', +' u-boot 0 4 u-boot 0', +' section 100 %x section 100' % section_size, +' cbfs 100 400 cbfs 0', +' u-boot 138 4 u-boot 38', +' u-boot-dtb 180 105 u-boot-dtb 80 3c9', +' u-boot-dtb 500 %x u-boot-dtb 400 3c9' % fdt_size, +' fdtmap %x 3bd fdtmap %x' % + (fdtmap_offset, fdtmap_offset), +' image-header bf8 8 image-header bf8', + ] + self.assertEqual(expected, lines) + + def testListCmdFail(self): + """Test failing to list an image""" + self._DoReadFile('005_simple.dts') + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + with self.assertRaises(ValueError) as e: + self._DoBinman('ls', '-i', updated_fname) + finally: + shutil.rmtree(tmpdir) + self.assertIn("Cannot find FDT map in image", str(e.exception)) + + def _RunListCmd(self, paths, expected): + """List out entries and check the result + + Args: + paths: List of paths to pass to the list command + expected: Expected list of filenames to be returned, in order + """ + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + image = Image.FromFile(image_fname) + lines = image.GetListEntries(paths)[1] + files = [line[0].strip() for line in lines[1:]] + self.assertEqual(expected, files) + + def testListCmdSection(self): + """Test listing the files in a section""" + self._RunListCmd(['section'], + ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) + + def testListCmdFile(self): + """Test listing a particular file""" + self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb']) + + def testListCmdWildcard(self): + """Test listing a wildcarded file""" + self._RunListCmd(['*boot*'], + ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb']) + + def testListCmdWildcardMulti(self): + """Test listing a wildcarded file""" + self._RunListCmd(['*cb*', '*head*'], + ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) + + def testListCmdEmpty(self): + """Test listing a wildcarded file""" + self._RunListCmd(['nothing'], []) + + def testListCmdPath(self): + """Test listing the files in a sub-entry of a section""" + self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb']) + + def _RunExtractCmd(self, entry_name, decomp=True): + """Extract an entry from an image + + Args: + entry_name: Entry name to extract + decomp: True to decompress the data if compressed, False to leave + it in its raw uncompressed format + + Returns: + data from entry + """ + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + return control.ReadEntry(image_fname, entry_name, decomp) + + def testExtractSimple(self): + """Test extracting a single file""" + data = self._RunExtractCmd('u-boot') + self.assertEqual(U_BOOT_DATA, data) + + def testExtractSection(self): + """Test extracting the files in a section""" + data = self._RunExtractCmd('section') + cbfs_data = data[:0x400] + cbfs = cbfs_util.CbfsReader(cbfs_data) + self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys())) + dtb_data = data[0x400:] + dtb = self._decompress(dtb_data) + self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) + + def testExtractCompressed(self): + """Test extracting compressed data""" + data = self._RunExtractCmd('section/u-boot-dtb') + self.assertEqual(EXTRACT_DTB_SIZE, len(data)) + + def testExtractRaw(self): + """Test extracting compressed data without decompressing it""" + data = self._RunExtractCmd('section/u-boot-dtb', decomp=False) + dtb = self._decompress(data) + self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) + + def testExtractCbfs(self): + """Test extracting CBFS data""" + data = self._RunExtractCmd('section/cbfs/u-boot') + self.assertEqual(U_BOOT_DATA, data) + + def testExtractCbfsCompressed(self): + """Test extracting CBFS compressed data""" + data = self._RunExtractCmd('section/cbfs/u-boot-dtb') + self.assertEqual(EXTRACT_DTB_SIZE, len(data)) + + def testExtractCbfsRaw(self): + """Test extracting CBFS compressed data without decompressing it""" + data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False) + dtb = tools.Decompress(data, 'lzma', with_header=False) + self.assertEqual(EXTRACT_DTB_SIZE, len(dtb)) + + def testExtractBadEntry(self): + """Test extracting a bad section path""" + with self.assertRaises(ValueError) as e: + self._RunExtractCmd('section/does-not-exist') + self.assertIn("Entry 'does-not-exist' not found in '/section'", + str(e.exception)) + + def testExtractMissingFile(self): + """Test extracting file that does not exist""" + with self.assertRaises(IOError) as e: + control.ReadEntry('missing-file', 'name') + + def testExtractBadFile(self): + """Test extracting an invalid file""" + fname = os.path.join(self._indir, 'badfile') + tools.WriteFile(fname, b'') + with self.assertRaises(ValueError) as e: + control.ReadEntry(fname, 'name') + + def testExtractCmd(self): + """Test extracting a file fron an image on the command line""" + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + fname = os.path.join(self._indir, 'output.extact') + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + with test_util.capture_sys_output() as (stdout, stderr): + self._DoBinman('extract', '-i', updated_fname, 'u-boot', + '-f', fname) + finally: + shutil.rmtree(tmpdir) + data = tools.ReadFile(fname) + self.assertEqual(U_BOOT_DATA, data) + + def testExtractOneEntry(self): + """Test extracting a single entry fron an image """ + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + fname = os.path.join(self._indir, 'output.extact') + control.ExtractEntries(image_fname, fname, None, ['u-boot']) + data = tools.ReadFile(fname) + self.assertEqual(U_BOOT_DATA, data) + + def _CheckExtractOutput(self, decomp): + """Helper to test file output with and without decompression + + Args: + decomp: True to decompress entry data, False to output it raw + """ + def _CheckPresent(entry_path, expect_data, expect_size=None): + """Check and remove expected file + + This checks the data/size of a file and removes the file both from + the outfiles set and from the output directory. Once all files are + processed, both the set and directory should be empty. + + Args: + entry_path: Entry path + expect_data: Data to expect in file, or None to skip check + expect_size: Size of data to expect in file, or None to skip + """ + path = os.path.join(outdir, entry_path) + data = tools.ReadFile(path) + os.remove(path) + if expect_data: + self.assertEqual(expect_data, data) + elif expect_size: + self.assertEqual(expect_size, len(data)) + outfiles.remove(path) + + def _CheckDirPresent(name): + """Remove expected directory + + This gives an error if the directory does not exist as expected + + Args: + name: Name of directory to remove + """ + path = os.path.join(outdir, name) + os.rmdir(path) + + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + outdir = os.path.join(self._indir, 'extract') + einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp) + + # Create a set of all file that were output (should be 9) + outfiles = set() + for root, dirs, files in os.walk(outdir): + outfiles |= set([os.path.join(root, fname) for fname in files]) + self.assertEqual(9, len(outfiles)) + self.assertEqual(9, len(einfos)) + + image = control.images['image'] + entries = image.GetEntries() + + # Check the 9 files in various ways + section = entries['section'] + section_entries = section.GetEntries() + cbfs_entries = section_entries['cbfs'].GetEntries() + _CheckPresent('u-boot', U_BOOT_DATA) + _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA) + dtb_len = EXTRACT_DTB_SIZE + if not decomp: + dtb_len = cbfs_entries['u-boot-dtb'].size + _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len) + if not decomp: + dtb_len = section_entries['u-boot-dtb'].size + _CheckPresent('section/u-boot-dtb', None, dtb_len) + + fdtmap = entries['fdtmap'] + _CheckPresent('fdtmap', fdtmap.data) + hdr = entries['image-header'] + _CheckPresent('image-header', hdr.data) + + _CheckPresent('section/root', section.data) + cbfs = section_entries['cbfs'] + _CheckPresent('section/cbfs/root', cbfs.data) + data = tools.ReadFile(image_fname) + _CheckPresent('root', data) + + # There should be no files left. Remove all the directories to check. + # If there are any files/dirs remaining, one of these checks will fail. + self.assertEqual(0, len(outfiles)) + _CheckDirPresent('section/cbfs') + _CheckDirPresent('section') + _CheckDirPresent('') + self.assertFalse(os.path.exists(outdir)) + + def testExtractAllEntries(self): + """Test extracting all entries""" + self._CheckLz4() + self._CheckExtractOutput(decomp=True) + + def testExtractAllEntriesRaw(self): + """Test extracting all entries without decompressing them""" + self._CheckLz4() + self._CheckExtractOutput(decomp=False) + + def testExtractSelectedEntries(self): + """Test extracting some entries""" + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + outdir = os.path.join(self._indir, 'extract') + einfos = control.ExtractEntries(image_fname, None, outdir, + ['*cb*', '*head*']) + + # File output is tested by testExtractAllEntries(), so just check that + # the expected entries are selected + names = [einfo.name for einfo in einfos] + self.assertEqual(names, + ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header']) + + def testExtractNoEntryPaths(self): + """Test extracting some entries""" + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + with self.assertRaises(ValueError) as e: + control.ExtractEntries(image_fname, 'fname', None, []) + self.assertIn('Must specify an entry path to write with -f', + str(e.exception)) + + def testExtractTooManyEntryPaths(self): + """Test extracting some entries""" + self._CheckLz4() + self._DoReadFileRealDtb('130_list_fdtmap.dts') + image_fname = tools.GetOutputFilename('image.bin') + with self.assertRaises(ValueError) as e: + control.ExtractEntries(image_fname, 'fname', None, ['a', 'b']) + self.assertIn('Must specify exactly one entry path to write with -f', + str(e.exception)) + + def testPackAlignSection(self): + """Test that sections can have alignment""" + self._DoReadFile('131_pack_align_section.dts') + + self.assertIn('image', control.images) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(3, len(entries)) + + # First u-boot + self.assertIn('u-boot', entries) + entry = entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(0, entry.image_pos) + self.assertEqual(len(U_BOOT_DATA), entry.contents_size) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Section0 + self.assertIn('section0', entries) + section0 = entries['section0'] + self.assertEqual(0x10, section0.offset) + self.assertEqual(0x10, section0.image_pos) + self.assertEqual(len(U_BOOT_DATA), section0.size) + + # Second u-boot + section_entries = section0.GetEntries() + self.assertIn('u-boot', section_entries) + entry = section_entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(0x10, entry.image_pos) + self.assertEqual(len(U_BOOT_DATA), entry.contents_size) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Section1 + self.assertIn('section1', entries) + section1 = entries['section1'] + self.assertEqual(0x14, section1.offset) + self.assertEqual(0x14, section1.image_pos) + self.assertEqual(0x20, section1.size) + + # Second u-boot + section_entries = section1.GetEntries() + self.assertIn('u-boot', section_entries) + entry = section_entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(0x14, entry.image_pos) + self.assertEqual(len(U_BOOT_DATA), entry.contents_size) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + # Section2 + self.assertIn('section2', section_entries) + section2 = section_entries['section2'] + self.assertEqual(0x4, section2.offset) + self.assertEqual(0x18, section2.image_pos) + self.assertEqual(4, section2.size) + + # Third u-boot + section_entries = section2.GetEntries() + self.assertIn('u-boot', section_entries) + entry = section_entries['u-boot'] + self.assertEqual(0, entry.offset) + self.assertEqual(0x18, entry.image_pos) + self.assertEqual(len(U_BOOT_DATA), entry.contents_size) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True, + dts='132_replace.dts'): + """Replace an entry in an image + + This writes the entry data to update it, then opens the updated file and + returns the value that it now finds there. + + Args: + entry_name: Entry name to replace + data: Data to replace it with + decomp: True to compress the data if needed, False if data is + already compressed so should be used as is + allow_resize: True to allow entries to change size, False to raise + an exception + + Returns: + Tuple: + data from entry + data from fdtmap (excluding header) + Image object that was modified + """ + dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True, + update_dtb=True)[1] + + self.assertIn('image', control.images) + image = control.images['image'] + entries = image.GetEntries() + orig_dtb_data = entries['u-boot-dtb'].data + orig_fdtmap_data = entries['fdtmap'].data + + image_fname = tools.GetOutputFilename('image.bin') + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) + image = control.WriteEntry(updated_fname, entry_name, data, decomp, + allow_resize) + data = control.ReadEntry(updated_fname, entry_name, decomp) + + # The DT data should not change unless resized: + if not allow_resize: + new_dtb_data = entries['u-boot-dtb'].data + self.assertEqual(new_dtb_data, orig_dtb_data) + new_fdtmap_data = entries['fdtmap'].data + self.assertEqual(new_fdtmap_data, orig_fdtmap_data) + + return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image + + def testReplaceSimple(self): + """Test replacing a single file""" + expected = b'x' * len(U_BOOT_DATA) + data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected, + allow_resize=False) + self.assertEqual(expected, data) + + # Test that the state looks right. There should be an FDT for the fdtmap + # that we jsut read back in, and it should match what we find in the + # 'control' tables. Checking for an FDT that does not exist should + # return None. + path, fdtmap = state.GetFdtContents('fdtmap') + self.assertIsNotNone(path) + self.assertEqual(expected_fdtmap, fdtmap) + + dtb = state.GetFdtForEtype('fdtmap') + self.assertEqual(dtb.GetContents(), fdtmap) + + missing_path, missing_fdtmap = state.GetFdtContents('missing') + self.assertIsNone(missing_path) + self.assertIsNone(missing_fdtmap) + + missing_dtb = state.GetFdtForEtype('missing') + self.assertIsNone(missing_dtb) + + self.assertEqual('/binman', state.fdt_path_prefix) + + def testReplaceResizeFail(self): + """Test replacing a file by something larger""" + expected = U_BOOT_DATA + b'x' + with self.assertRaises(ValueError) as e: + self._RunReplaceCmd('u-boot', expected, allow_resize=False, + dts='139_replace_repack.dts') + self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled", + str(e.exception)) + + def testReplaceMulti(self): + """Test replacing entry data where multiple images are generated""" + data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True, + update_dtb=True)[0] + expected = b'x' * len(U_BOOT_DATA) + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + entry_name = 'u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=False) + data = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, data) + + # Check the state looks right. + self.assertEqual('/binman/image', state.fdt_path_prefix) + + # Now check we can write the first image + image_fname = tools.GetOutputFilename('first-image.bin') + updated_fname = tools.GetOutputFilename('first-updated.bin') + tools.WriteFile(updated_fname, tools.ReadFile(image_fname)) + entry_name = 'u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=False) + data = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, data) + + # Check the state looks right. + self.assertEqual('/binman/first-image', state.fdt_path_prefix) + + def testUpdateFdtAllRepack(self): + """Test that all device trees are updated with offset/size info""" + data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts') + SECTION_SIZE = 0x300 + DTB_SIZE = 602 + FDTMAP_SIZE = 608 + base_expected = { + 'offset': 0, + 'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE, + 'image-pos': 0, + 'section:offset': 0, + 'section:size': SECTION_SIZE, + 'section:image-pos': 0, + 'section/u-boot-dtb:offset': 4, + 'section/u-boot-dtb:size': 636, + 'section/u-boot-dtb:image-pos': 4, + 'u-boot-spl-dtb:offset': SECTION_SIZE, + 'u-boot-spl-dtb:size': DTB_SIZE, + 'u-boot-spl-dtb:image-pos': SECTION_SIZE, + 'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE, + 'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE, + 'u-boot-tpl-dtb:size': DTB_SIZE, + 'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2, + 'fdtmap:size': FDTMAP_SIZE, + 'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2, + } + main_expected = { + 'section:orig-size': SECTION_SIZE, + 'section/u-boot-dtb:orig-offset': 4, + } + + # We expect three device-tree files in the output, with the first one + # within a fixed-size section. + # 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 positions and offset + # except that the main tree should include the main_expected properties + start = 4 + for item in ['', 'spl', 'tpl', None]: + if item is None: + start += 16 # Move past fdtmap header + dtb = fdt.Fdt.FromData(data[start:]) + dtb.Scan() + props = self._GetPropTree(dtb, + BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'], + prefix='/' if item is None else '/binman/') + expected = dict(base_expected) + if item: + expected[item] = 0 + else: + # Main DTB and fdtdec should include the 'orig-' properties + expected.update(main_expected) + # Helpful for debugging: + #for prop in sorted(props): + #print('prop %s %s %s' % (prop, props[prop], expected[prop])) + self.assertEqual(expected, props) + if item == '': + start = SECTION_SIZE + else: + start += dtb._fdt_obj.totalsize() + + def testFdtmapHeaderMiddle(self): + """Test an FDT map in the middle of an image when it should be at end""" + with self.assertRaises(ValueError) as e: + self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts') + self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location", + str(e.exception)) + + def testFdtmapHeaderStartBad(self): + """Test an FDT map in middle of an image when it should be at start""" + with self.assertRaises(ValueError) as e: + self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts') + self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location", + str(e.exception)) + + def testFdtmapHeaderEndBad(self): + """Test an FDT map at the start of an image when it should be at end""" + with self.assertRaises(ValueError) as e: + self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts') + self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location", + str(e.exception)) + + def testFdtmapHeaderNoSize(self): + """Test an image header at the end of an image with undefined size""" + self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts') + + def testReplaceResize(self): + """Test replacing a single file in an entry with a larger file""" + expected = U_BOOT_DATA + b'x' + data, _, image = self._RunReplaceCmd('u-boot', expected, + dts='139_replace_repack.dts') + self.assertEqual(expected, data) + + entries = image.GetEntries() + dtb_data = entries['u-boot-dtb'].data + dtb = fdt.Fdt.FromData(dtb_data) + dtb.Scan() + + # The u-boot section should now be larger in the dtb + node = dtb.GetNode('/binman/u-boot') + self.assertEqual(len(expected), fdt_util.GetInt(node, 'size')) + + # Same for the fdtmap + fdata = entries['fdtmap'].data + fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:]) + fdtb.Scan() + fnode = fdtb.GetNode('/u-boot') + self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size')) + + def testReplaceResizeNoRepack(self): + """Test replacing an entry with a larger file when not allowed""" + expected = U_BOOT_DATA + b'x' + with self.assertRaises(ValueError) as e: + self._RunReplaceCmd('u-boot', expected) + self.assertIn('Entry data size does not match, but allow-repack is not present for this image', + str(e.exception)) + + def testEntryShrink(self): + """Test contracting an entry after it is packed""" + try: + state.SetAllowEntryContraction(True) + data = self._DoReadFileDtb('140_entry_shrink.dts', + update_dtb=True)[0] + finally: + state.SetAllowEntryContraction(False) + self.assertEqual(b'a', data[:1]) + self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)]) + self.assertEqual(b'a', data[-1:]) + + def testEntryShrinkFail(self): + """Test not being allowed to contract an entry after it is packed""" + data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0] + + # In this case there is a spare byte at the end of the data. The size of + # the contents is only 1 byte but we still have the size before it + # shrunk. + self.assertEqual(b'a\0', data[:2]) + self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)]) + self.assertEqual(b'a\0', data[-2:]) + + def testDescriptorOffset(self): + """Test that the Intel descriptor is always placed at at the start""" + data = self._DoReadFileDtb('141_descriptor_offset.dts') + image = control.images['image'] + entries = image.GetEntries() + desc = entries['intel-descriptor'] + self.assertEqual(0xff800000, desc.offset); + self.assertEqual(0xff800000, desc.image_pos); + + def testReplaceCbfs(self): + """Test replacing a single file in CBFS without changing the size""" + self._CheckLz4() + expected = b'x' * len(U_BOOT_DATA) + data = self._DoReadFileRealDtb('142_replace_cbfs.dts') + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + entry_name = 'section/cbfs/u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=True) + data = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, data) + + def testReplaceResizeCbfs(self): + """Test replacing a single file in CBFS with one of a different size""" + self._CheckLz4() + expected = U_BOOT_DATA + b'x' + data = self._DoReadFileRealDtb('142_replace_cbfs.dts') + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + entry_name = 'section/cbfs/u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=True) + data = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, data) + + def _SetupForReplace(self): + """Set up some files to use to replace entries + + This generates an image, copies it to a new file, extracts all the files + in it and updates some of them + + Returns: + List + Image filename + Output directory + Expected values for updated entries, each a string + """ + data = self._DoReadFileRealDtb('143_replace_all.dts') + + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + + outdir = os.path.join(self._indir, 'extract') + einfos = control.ExtractEntries(updated_fname, None, outdir, []) + + expected1 = b'x' + U_BOOT_DATA + b'y' + u_boot_fname1 = os.path.join(outdir, 'u-boot') + tools.WriteFile(u_boot_fname1, expected1) + + expected2 = b'a' + U_BOOT_DATA + b'b' + u_boot_fname2 = os.path.join(outdir, 'u-boot2') + tools.WriteFile(u_boot_fname2, expected2) + + expected_text = b'not the same text' + text_fname = os.path.join(outdir, 'text') + tools.WriteFile(text_fname, expected_text) + + dtb_fname = os.path.join(outdir, 'u-boot-dtb') + dtb = fdt.FdtScan(dtb_fname) + node = dtb.GetNode('/binman/text') + node.AddString('my-property', 'the value') + dtb.Sync(auto_resize=True) + dtb.Flush() + + return updated_fname, outdir, expected1, expected2, expected_text + + def _CheckReplaceMultiple(self, entry_paths): + """Handle replacing the contents of multiple entries + + Args: + entry_paths: List of entry paths to replace + + Returns: + List + Dict of entries in the image: + key: Entry name + Value: Entry object + Expected values for updated entries, each a string + """ + updated_fname, outdir, expected1, expected2, expected_text = ( + self._SetupForReplace()) + control.ReplaceEntries(updated_fname, None, outdir, entry_paths) + + image = Image.FromFile(updated_fname) + image.LoadData() + return image.GetEntries(), expected1, expected2, expected_text + + def testReplaceAll(self): + """Test replacing the contents of all entries""" + entries, expected1, expected2, expected_text = ( + self._CheckReplaceMultiple([])) + data = entries['u-boot'].data + self.assertEqual(expected1, data) + + data = entries['u-boot2'].data + self.assertEqual(expected2, data) + + data = entries['text'].data + self.assertEqual(expected_text, data) + + # Check that the device tree is updated + data = entries['u-boot-dtb'].data + dtb = fdt.Fdt.FromData(data) + dtb.Scan() + node = dtb.GetNode('/binman/text') + self.assertEqual('the value', node.props['my-property'].value) + + def testReplaceSome(self): + """Test replacing the contents of a few entries""" + entries, expected1, expected2, expected_text = ( + self._CheckReplaceMultiple(['u-boot2', 'text'])) + + # This one should not change + data = entries['u-boot'].data + self.assertEqual(U_BOOT_DATA, data) + + data = entries['u-boot2'].data + self.assertEqual(expected2, data) + + data = entries['text'].data + self.assertEqual(expected_text, data) + + def testReplaceCmd(self): + """Test replacing a file fron an image on the command line""" + self._DoReadFileRealDtb('143_replace_all.dts') + + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + + fname = os.path.join(tmpdir, 'update-u-boot.bin') + expected = b'x' * len(U_BOOT_DATA) + tools.WriteFile(fname, expected) + + self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname) + data = tools.ReadFile(updated_fname) + self.assertEqual(expected, data[:len(expected)]) + map_fname = os.path.join(tmpdir, 'image-updated.map') + self.assertFalse(os.path.exists(map_fname)) + finally: + shutil.rmtree(tmpdir) + + def testReplaceCmdSome(self): + """Test replacing some files fron an image on the command line""" + updated_fname, outdir, expected1, expected2, expected_text = ( + self._SetupForReplace()) + + self._DoBinman('replace', '-i', updated_fname, '-I', outdir, + 'u-boot2', 'text') + + tools.PrepareOutputDir(None) + image = Image.FromFile(updated_fname) + image.LoadData() + entries = image.GetEntries() + + # This one should not change + data = entries['u-boot'].data + self.assertEqual(U_BOOT_DATA, data) + + data = entries['u-boot2'].data + self.assertEqual(expected2, data) + + data = entries['text'].data + self.assertEqual(expected_text, data) + + def testReplaceMissing(self): + """Test replacing entries where the file is missing""" + updated_fname, outdir, expected1, expected2, expected_text = ( + self._SetupForReplace()) + + # Remove one of the files, to generate a warning + u_boot_fname1 = os.path.join(outdir, 'u-boot') + os.remove(u_boot_fname1) + + with test_util.capture_sys_output() as (stdout, stderr): + control.ReplaceEntries(updated_fname, None, outdir, []) + self.assertIn("Skipping entry '/u-boot' from missing file", + stderr.getvalue()) + + def testReplaceCmdMap(self): + """Test replacing a file fron an image on the command line""" + self._DoReadFileRealDtb('143_replace_all.dts') + + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + + fname = os.path.join(self._indir, 'update-u-boot.bin') + expected = b'x' * len(U_BOOT_DATA) + tools.WriteFile(fname, expected) + + self._DoBinman('replace', '-i', updated_fname, 'u-boot', + '-f', fname, '-m') + map_fname = os.path.join(tmpdir, 'image-updated.map') + self.assertTrue(os.path.exists(map_fname)) + finally: + shutil.rmtree(tmpdir) + + def testReplaceNoEntryPaths(self): + """Test replacing an entry without an entry path""" + self._DoReadFileRealDtb('143_replace_all.dts') + image_fname = tools.GetOutputFilename('image.bin') + with self.assertRaises(ValueError) as e: + control.ReplaceEntries(image_fname, 'fname', None, []) + self.assertIn('Must specify an entry path to read with -f', + str(e.exception)) + + def testReplaceTooManyEntryPaths(self): + """Test extracting some entries""" + self._DoReadFileRealDtb('143_replace_all.dts') + image_fname = tools.GetOutputFilename('image.bin') + with self.assertRaises(ValueError) as e: + control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b']) + self.assertIn('Must specify exactly one entry path to write with -f', + str(e.exception)) + + def testPackReset16(self): + """Test that an image with an x86 reset16 region can be created""" + data = self._DoReadFile('144_x86_reset16.dts') + self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)]) + + def testPackReset16Spl(self): + """Test that an image with an x86 reset16-spl region can be created""" + data = self._DoReadFile('145_x86_reset16_spl.dts') + self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)]) + + def testPackReset16Tpl(self): + """Test that an image with an x86 reset16-tpl region can be created""" + data = self._DoReadFile('146_x86_reset16_tpl.dts') + self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)]) + + def testPackIntelFit(self): + """Test that an image with an Intel FIT and pointer can be created""" + data = self._DoReadFile('147_intel_fit.dts') + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + fit = data[16:32]; + self.assertEqual(b'_FIT_ \x01\x00\x00\x00\x00\x01\x80}' , fit) + ptr = struct.unpack('<i', data[0x40:0x44])[0] + + image = control.images['image'] + entries = image.GetEntries() + expected_ptr = entries['intel-fit'].image_pos - (1 << 32) + self.assertEqual(expected_ptr, ptr) + + def testPackIntelFitMissing(self): + """Test detection of a FIT pointer with not FIT region""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('148_intel_fit_missing.dts') + self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling", + str(e.exception)) + + def _CheckSymbolsTplSection(self, dts, expected_vals): + data = self._DoReadFile(dts) + sym_values = struct.pack('<LQLL', *expected_vals) + upto1 = 4 + len(U_BOOT_SPL_DATA) + expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:] + self.assertEqual(expected1, data[:upto1]) + + upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA) + expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:] + self.assertEqual(expected2, data[upto1:upto2]) + + upto3 = 0x34 + len(U_BOOT_DATA) + expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA + self.assertEqual(expected3, data[upto2:upto3]) + + expected4 = sym_values + U_BOOT_TPL_DATA[20:] + self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)]) + + def testSymbolsTplSection(self): + """Test binman can assign symbols embedded in U-Boot TPL in a section""" + self._SetupSplElf('u_boot_binman_syms') + self._SetupTplElf('u_boot_binman_syms') + self._CheckSymbolsTplSection('149_symbols_tpl.dts', + [0x04, 0x1c, 0x10 + 0x34, 0x04]) + + def testSymbolsTplSectionX86(self): + """Test binman can assign symbols in a section with end-at-4gb""" + self._SetupSplElf('u_boot_binman_syms_x86') + self._SetupTplElf('u_boot_binman_syms_x86') + self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts', + [0xffffff04, 0xffffff1c, 0xffffff34, + 0x04]) + + def testPackX86RomIfwiSectiom(self): + """Test that a section can be placed in an IFWI region""" + self._SetupIfwi('fitimage.bin') + data = self._DoReadFile('151_x86_rom_ifwi_section.dts') + self._CheckIfwi(data) + + def testPackFspM(self): + """Test that an image with a FSP memory-init binary can be created""" + data = self._DoReadFile('152_intel_fsp_m.dts') + self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)]) + + def testPackFspS(self): + """Test that an image with a FSP silicon-init binary can be created""" + data = self._DoReadFile('153_intel_fsp_s.dts') + self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)]) + + def testPackFspT(self): + """Test that an image with a FSP temp-ram-init binary can be created""" + data = self._DoReadFile('154_intel_fsp_t.dts') + self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)]) + + def testMkimage(self): + """Test using mkimage to build an image""" + data = self._DoReadFile('156_mkimage.dts') + + # Just check that the data appears in the file somewhere + self.assertIn(U_BOOT_SPL_DATA, data) + + def testExtblob(self): + """Test an image with an external blob""" + data = self._DoReadFile('157_blob_ext.dts') + self.assertEqual(REFCODE_DATA, data) + + def testExtblobMissing(self): + """Test an image with a missing external blob""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('158_blob_ext_missing.dts') + self.assertIn("Filename 'missing-file' not found in input path", + str(e.exception)) + + def testExtblobMissingOk(self): + """Test an image with an missing external blob that is allowed""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext") + + def testExtblobMissingOkSect(self): + """Test an image with an missing external blob that is allowed""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('159_blob_ext_missing_sect.dts', + allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: " + "blob-ext blob-ext2") + + def testPackX86RomMeMissingDesc(self): + """Test that an missing Intel descriptor entry is allowed""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, + "Image 'main-section'.*missing.*: intel-descriptor") + + def testPackX86RomMissingIfwi(self): + """Test that an x86 ROM with Integrated Firmware Image can be created""" + self._SetupIfwi('fitimage.bin') + pathname = os.path.join(self._indir, 'fitimage.bin') + os.remove(pathname) + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi") + + def testPackOverlap(self): + """Test that zero-size overlapping regions are ignored""" + self._DoTestFile('160_pack_overlap_zero.dts') + + def testSimpleFit(self): + """Test an image with a FIT inside""" + data = self._DoReadFile('161_fit.dts') + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + + # The data should be inside the FIT + dtb = fdt.Fdt.FromData(fit_data) + dtb.Scan() + fnode = dtb.GetNode('/images/kernel') + self.assertIn('data', fnode.props) + + fname = os.path.join(self._indir, 'fit_data.fit') + tools.WriteFile(fname, fit_data) + out = tools.Run('dumpimage', '-l', fname) + + # Check a few features to make sure the plumbing works. We don't need + # to test the operation of mkimage or dumpimage here. First convert the + # output into a dict where the keys are the fields printed by dumpimage + # and the values are a list of values for each field + lines = out.splitlines() + + # Converts "Compression: gzip compressed" into two groups: + # 'Compression' and 'gzip compressed' + re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$') + vals = collections.defaultdict(list) + for line in lines: + mat = re_line.match(line) + vals[mat.group(1)].append(mat.group(2)) + + self.assertEquals('FIT description: test-desc', lines[0]) + self.assertIn('Created:', lines[1]) + self.assertIn('Image 0 (kernel)', vals) + self.assertIn('Hash value', vals) + data_sizes = vals.get('Data Size') + self.assertIsNotNone(data_sizes) + self.assertEqual(2, len(data_sizes)) + # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word + self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0])) + self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0])) + + def testFitExternal(self): + """Test an image with an FIT with external images""" + data = self._DoReadFile('162_fit_external.dts') + fit_data = data[len(U_BOOT_DATA):-2] # _testing is 2 bytes + + # The data should be outside the FIT + dtb = fdt.Fdt.FromData(fit_data) + dtb.Scan() + fnode = dtb.GetNode('/images/kernel') + self.assertNotIn('data', fnode.props) + + def testSectionIgnoreHashSignature(self): + """Test that sections ignore hash, signature nodes for its data""" + data = self._DoReadFile('165_section_ignore_hash_signature.dts') + expected = (U_BOOT_DATA + U_BOOT_DATA) + self.assertEqual(expected, data) + + def testPadInSections(self): + """Test pad-before, pad-after for entries in sections""" + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '166_pad_in_sections.dts', update_dtb=True) + expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) + + U_BOOT_DATA + tools.GetBytes(ord('!'), 6) + + U_BOOT_DATA) + self.assertEqual(expected, data) + + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset']) + expected = { + 'image-pos': 0, + 'offset': 0, + 'size': 12 + 6 + 3 * len(U_BOOT_DATA), + + 'section:image-pos': 0, + 'section:offset': 0, + 'section:size': 12 + 6 + 3 * len(U_BOOT_DATA), + + 'section/before:image-pos': 0, + 'section/before:offset': 0, + 'section/before:size': len(U_BOOT_DATA), + + 'section/u-boot:image-pos': 4, + 'section/u-boot:offset': 4, + 'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6, + + 'section/after:image-pos': 26, + 'section/after:offset': 26, + 'section/after:size': len(U_BOOT_DATA), + } + self.assertEqual(expected, props) + + def testFitImageSubentryAlignment(self): + """Test relative alignability of FIT image subentries""" + entry_args = { + 'test-id': TEXT_DATA, + } + data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts', + entry_args=entry_args) + dtb = fdt.Fdt.FromData(data) + dtb.Scan() + + node = dtb.GetNode('/images/kernel') + data = dtb.GetProps(node)["data"].bytes + align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10) + expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA + + tools.GetBytes(0, align_pad) + U_BOOT_DATA) + self.assertEqual(expected, data) + + node = dtb.GetNode('/images/fdt-1') + data = dtb.GetProps(node)["data"].bytes + expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) + + tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) + + U_BOOT_DTB_DATA) + self.assertEqual(expected, data) + + def testFitExtblobMissingOk(self): + """Test a FIT with a missing external blob that is allowed""" + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('168_fit_missing_blob.dts', + allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31") + + def testBlobNamedByArgMissing(self): + """Test handling of a missing entry arg""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('068_blob_named_by_arg.dts') + self.assertIn("Missing required properties/entry args: cros-ec-rw-path", + str(e.exception)) + + def testPackBl31(self): + """Test that an image with an ATF BL31 binary can be created""" + data = self._DoReadFile('169_atf_bl31.dts') + self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) + + def testPackScp(self): + """Test that an image with an SCP binary can be created""" + data = self._DoReadFile('172_scp.dts') + self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) + + def testFitFdt(self): + """Test an image with an FIT with multiple FDT images""" + def _CheckFdt(seq, expected_data): + """Check the FDT nodes + + Args: + seq: Sequence number to check (0 or 1) + expected_data: Expected contents of 'data' property + """ + name = 'fdt-%d' % seq + fnode = dtb.GetNode('/images/%s' % name) + self.assertIsNotNone(fnode) + self.assertEqual({'description','type', 'compression', 'data'}, + set(fnode.props.keys())) + self.assertEqual(expected_data, fnode.props['data'].bytes) + self.assertEqual('fdt-test-fdt%d.dtb' % seq, + fnode.props['description'].value) + + def _CheckConfig(seq, expected_data): + """Check the configuration nodes + + Args: + seq: Sequence number to check (0 or 1) + expected_data: Expected contents of 'data' property + """ + cnode = dtb.GetNode('/configurations') + self.assertIn('default', cnode.props) + self.assertEqual('config-2', cnode.props['default'].value) + + name = 'config-%d' % seq + fnode = dtb.GetNode('/configurations/%s' % name) + self.assertIsNotNone(fnode) + self.assertEqual({'description','firmware', 'loadables', 'fdt'}, + set(fnode.props.keys())) + self.assertEqual('conf-test-fdt%d.dtb' % seq, + fnode.props['description'].value) + self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value) + + entry_args = { + 'of-list': 'test-fdt1 test-fdt2', + 'default-dt': 'test-fdt2', + } + data = self._DoReadFileDtb( + '170_fit_fdt.dts', + entry_args=entry_args, + extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] + self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) + fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + + dtb = fdt.Fdt.FromData(fit_data) + dtb.Scan() + fnode = dtb.GetNode('/images/kernel') + self.assertIn('data', fnode.props) + + # Check all the properties in fdt-1 and fdt-2 + _CheckFdt(1, TEST_FDT1_DATA) + _CheckFdt(2, TEST_FDT2_DATA) + + # Check configurations + _CheckConfig(1, TEST_FDT1_DATA) + _CheckConfig(2, TEST_FDT2_DATA) + + def testFitFdtMissingList(self): + """Test handling of a missing 'of-list' entry arg""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('170_fit_fdt.dts') + self.assertIn("Generator node requires 'of-list' entry argument", + str(e.exception)) + + def testFitFdtEmptyList(self): + """Test handling of an empty 'of-list' entry arg""" + entry_args = { + 'of-list': '', + } + data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0] + + def testFitFdtMissingProp(self): + """Test handling of a missing 'fit,fdt-list' property""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('171_fit_fdt_missing_prop.dts') + self.assertIn("Generator node requires 'fit,fdt-list' property", + str(e.exception)) + + def testFitFdtEmptyList(self): + """Test handling of an empty 'of-list' entry arg""" + entry_args = { + 'of-list': '', + } + data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0] + + def testFitFdtMissing(self): + """Test handling of a missing 'default-dt' entry arg""" + entry_args = { + 'of-list': 'test-fdt1 test-fdt2', + } + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb( + '170_fit_fdt.dts', + entry_args=entry_args, + extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] + self.assertIn("Generated 'default' node requires default-dt entry argument", + str(e.exception)) + + def testFitFdtNotInList(self): + """Test handling of a default-dt that is not in the of-list""" + entry_args = { + 'of-list': 'test-fdt1 test-fdt2', + 'default-dt': 'test-fdt3', + } + with self.assertRaises(ValueError) as e: + self._DoReadFileDtb( + '170_fit_fdt.dts', + entry_args=entry_args, + extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0] + self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2", + str(e.exception)) + + def testFitExtblobMissingHelp(self): + """Test display of help messages when an external blob is missing""" + control.missing_blob_help = control._ReadMissingBlobHelp() + control.missing_blob_help['wibble'] = 'Wibble test' + control.missing_blob_help['another'] = 'Another test' + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('168_fit_missing_blob.dts', + allow_missing=True) + err = stderr.getvalue() + + # We can get the tag from the name, the type or the missing-msg + # property. Check all three. + self.assertIn('You may need to build ARM Trusted', err) + self.assertIn('Wibble test', err) + self.assertIn('Another test', err) + + def testMissingBlob(self): + """Test handling of a blob containing a missing file""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('173_missing_blob.dts', allow_missing=True) + self.assertIn("Filename 'missing' not found in input path", + str(e.exception)) + + def testEnvironment(self): + """Test adding a U-Boot environment""" + data = self._DoReadFile('174_env.dts') + self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)]) + self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):]) + env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)] + self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff', + env) + + def testEnvironmentNoSize(self): + """Test that a missing 'size' property is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('175_env_no_size.dts') + self.assertIn("'u-boot-env' entry must have a size property", + str(e.exception)) + + def testEnvironmentTooSmall(self): + """Test handling of an environment that does not fit""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('176_env_too_small.dts') + + # checksum, start byte, environment with \0 terminator, final \0 + need = 4 + 1 + len(ENV_DATA) + 1 + 1 + short = need - 0x8 + self.assertIn("too small to hold data (need %#x more bytes)" % short, + str(e.exception)) + + def testSkipAtStart(self): + """Test handling of skip-at-start section""" + data = self._DoReadFile('177_skip_at_start.dts') + self.assertEqual(U_BOOT_DATA, data) + + image = control.images['image'] + entries = image.GetEntries() + section = entries['section'] + self.assertEqual(0, section.offset) + self.assertEqual(len(U_BOOT_DATA), section.size) + self.assertEqual(U_BOOT_DATA, section.GetData()) + + entry = section.GetEntries()['u-boot'] + self.assertEqual(16, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + self.assertEqual(U_BOOT_DATA, entry.data) + + def testSkipAtStartPad(self): + """Test handling of skip-at-start section with padded entry""" + data = self._DoReadFile('178_skip_at_start_pad.dts') + before = tools.GetBytes(0, 8) + after = tools.GetBytes(0, 4) + all = before + U_BOOT_DATA + after + self.assertEqual(all, data) + + image = control.images['image'] + entries = image.GetEntries() + section = entries['section'] + self.assertEqual(0, section.offset) + self.assertEqual(len(all), section.size) + self.assertEqual(all, section.GetData()) + + entry = section.GetEntries()['u-boot'] + self.assertEqual(16, entry.offset) + self.assertEqual(len(all), entry.size) + self.assertEqual(U_BOOT_DATA, entry.data) + + def testSkipAtStartSectionPad(self): + """Test handling of skip-at-start section with padding""" + data = self._DoReadFile('179_skip_at_start_section_pad.dts') + before = tools.GetBytes(0, 8) + after = tools.GetBytes(0, 4) + all = before + U_BOOT_DATA + after + self.assertEqual(all, data) + + image = control.images['image'] + entries = image.GetEntries() + section = entries['section'] + self.assertEqual(0, section.offset) + self.assertEqual(len(all), section.size) + self.assertEqual(U_BOOT_DATA, section.data) + self.assertEqual(all, section.GetPaddedData()) + + entry = section.GetEntries()['u-boot'] + self.assertEqual(16, entry.offset) + self.assertEqual(len(U_BOOT_DATA), entry.size) + self.assertEqual(U_BOOT_DATA, entry.data) + + def testSectionPad(self): + """Testing padding with sections""" + data = self._DoReadFile('180_section_pad.dts') + expected = (tools.GetBytes(ord('&'), 3) + + tools.GetBytes(ord('!'), 5) + + U_BOOT_DATA + + tools.GetBytes(ord('!'), 1) + + tools.GetBytes(ord('&'), 2)) + self.assertEqual(expected, data) + + def testSectionAlign(self): + """Testing alignment with sections""" + data = self._DoReadFileDtb('181_section_align.dts', map=True)[0] + expected = (b'\0' + # fill section + tools.GetBytes(ord('&'), 1) + # padding to section align + b'\0' + # fill section + tools.GetBytes(ord('!'), 3) + # padding to u-boot align + U_BOOT_DATA + + tools.GetBytes(ord('!'), 4) + # padding to u-boot size + tools.GetBytes(ord('!'), 4)) # padding to section size + self.assertEqual(expected, data) + + def testCompressImage(self): + """Test compression of the entire image""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '182_compress_image.dts', use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', + 'uncomp-size']) + orig = self._decompress(data) + self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) + + # Do a sanity check on various fields + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(2, len(entries)) + + entry = entries['blob'] + self.assertEqual(COMPRESS_DATA, entry.data) + self.assertEqual(len(COMPRESS_DATA), entry.size) + + entry = entries['u-boot'] + self.assertEqual(U_BOOT_DATA, entry.data) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + self.assertEqual(len(data), image.size) + self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data) + self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size) + orig = self._decompress(image.data) + self.assertEqual(orig, image.uncomp_data) + + expected = { + 'blob:offset': 0, + 'blob:size': len(COMPRESS_DATA), + 'u-boot:offset': len(COMPRESS_DATA), + 'u-boot:size': len(U_BOOT_DATA), + 'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), + 'offset': 0, + 'image-pos': 0, + 'size': len(data), + } + self.assertEqual(expected, props) + + def testCompressImageLess(self): + """Test compression where compression reduces the image size""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', + 'uncomp-size']) + orig = self._decompress(data) + + self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig) + + # Do a sanity check on various fields + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(2, len(entries)) + + entry = entries['blob'] + self.assertEqual(COMPRESS_DATA_BIG, entry.data) + self.assertEqual(len(COMPRESS_DATA_BIG), entry.size) + + entry = entries['u-boot'] + self.assertEqual(U_BOOT_DATA, entry.data) + self.assertEqual(len(U_BOOT_DATA), entry.size) + + self.assertEqual(len(data), image.size) + self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data) + self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA), + image.uncomp_size) + orig = self._decompress(image.data) + self.assertEqual(orig, image.uncomp_data) + + expected = { + 'blob:offset': 0, + 'blob:size': len(COMPRESS_DATA_BIG), + 'u-boot:offset': len(COMPRESS_DATA_BIG), + 'u-boot:size': len(U_BOOT_DATA), + 'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA), + 'offset': 0, + 'image-pos': 0, + 'size': len(data), + } + self.assertEqual(expected, props) + + def testCompressSectionSize(self): + """Test compression of a section with a fixed size""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', + 'uncomp-size']) + orig = self._decompress(data) + self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) + expected = { + 'section/blob:offset': 0, + 'section/blob:size': len(COMPRESS_DATA), + 'section/u-boot:offset': len(COMPRESS_DATA), + 'section/u-boot:size': len(U_BOOT_DATA), + 'section:offset': 0, + 'section:image-pos': 0, + 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), + 'section:size': 0x30, + 'offset': 0, + 'image-pos': 0, + 'size': 0x30, + } + self.assertEqual(expected, props) + + def testCompressSection(self): + """Test compression of a section with no fixed size""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '185_compress_section.dts', use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', + 'uncomp-size']) + orig = self._decompress(data) + self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig) + expected = { + 'section/blob:offset': 0, + 'section/blob:size': len(COMPRESS_DATA), + 'section/u-boot:offset': len(COMPRESS_DATA), + 'section/u-boot:size': len(U_BOOT_DATA), + 'section:offset': 0, + 'section:image-pos': 0, + 'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), + 'section:size': len(data), + 'offset': 0, + 'image-pos': 0, + 'size': len(data), + } + self.assertEqual(expected, props) + + def testCompressExtra(self): + """Test compression of a section with no fixed size""" + self._CheckLz4() + data, _, _, out_dtb_fname = self._DoReadFileDtb( + '186_compress_extra.dts', use_real_dtb=True, update_dtb=True) + dtb = fdt.Fdt(out_dtb_fname) + dtb.Scan() + props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size', + 'uncomp-size']) + + base = data[len(U_BOOT_DATA):] + self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)]) + rest = base[len(U_BOOT_DATA):] + + # Check compressed data + section1 = self._decompress(rest) + expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4') + self.assertEquals(expect1, rest[:len(expect1)]) + self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1) + rest1 = rest[len(expect1):] + + section2 = self._decompress(rest1) + expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4') + self.assertEquals(expect2, rest1[:len(expect2)]) + self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2) + rest2 = rest1[len(expect2):] + + expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) + + len(expect2) + len(U_BOOT_DATA)) + #self.assertEquals(expect_size, len(data)) + + #self.assertEquals(U_BOOT_DATA, rest2) + + self.maxDiff = None + expected = { + 'u-boot:offset': 0, + 'u-boot:image-pos': 0, + 'u-boot:size': len(U_BOOT_DATA), + + 'base:offset': len(U_BOOT_DATA), + 'base:image-pos': len(U_BOOT_DATA), + 'base:size': len(data) - len(U_BOOT_DATA), + 'base/u-boot:offset': 0, + 'base/u-boot:image-pos': len(U_BOOT_DATA), + 'base/u-boot:size': len(U_BOOT_DATA), + 'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) + + len(expect2), + 'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) + + len(expect2), + 'base/u-boot2:size': len(U_BOOT_DATA), + + 'base/section:offset': len(U_BOOT_DATA), + 'base/section:image-pos': len(U_BOOT_DATA) * 2, + 'base/section:size': len(expect1), + 'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA), + 'base/section/blob:offset': 0, + 'base/section/blob:size': len(COMPRESS_DATA), + 'base/section/u-boot:offset': len(COMPRESS_DATA), + 'base/section/u-boot:size': len(U_BOOT_DATA), + + 'base/section2:offset': len(U_BOOT_DATA) + len(expect1), + 'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1), + 'base/section2:size': len(expect2), + 'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA), + 'base/section2/blob:offset': 0, + 'base/section2/blob:size': len(COMPRESS_DATA), + 'base/section2/blob2:offset': len(COMPRESS_DATA), + 'base/section2/blob2:size': len(COMPRESS_DATA), + + 'offset': 0, + 'image-pos': 0, + 'size': len(data), + } + self.assertEqual(expected, props) + + def testSymbolsSubsection(self): + """Test binman can assign symbols from a subsection""" + self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18) + + def testReadImageEntryArg(self): + """Test reading an image that would need an entry arg to generate""" + entry_args = { + 'cros-ec-rw-path': 'ecrw.bin', + } + data = self.data = self._DoReadFileDtb( + '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True, + entry_args=entry_args) + + image_fname = tools.GetOutputFilename('image.bin') + orig_image = control.images['image'] + + # This should not generate an error about the missing 'cros-ec-rw-path' + # since we are reading the image from a file. Compare with + # testEntryArgsRequired() + image = Image.FromFile(image_fname) + self.assertEqual(orig_image.GetEntries().keys(), + image.GetEntries().keys()) + + def testFilesAlign(self): + """Test alignment with files""" + data = self._DoReadFile('190_files_align.dts') + + # The first string is 15 bytes so will align to 16 + expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:] + self.assertEqual(expect, data) + + def testReadImageSkip(self): + """Test reading an image and accessing its FDT map""" + data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts') + image_fname = tools.GetOutputFilename('image.bin') + orig_image = control.images['image'] + image = Image.FromFile(image_fname) + self.assertEqual(orig_image.GetEntries().keys(), + image.GetEntries().keys()) + + orig_entry = orig_image.GetEntries()['fdtmap'] + entry = image.GetEntries()['fdtmap'] + self.assertEqual(orig_entry.offset, entry.offset) + self.assertEqual(orig_entry.size, entry.size) + self.assertEqual(16, entry.image_pos) + + u_boot = image.GetEntries()['section'].GetEntries()['u-boot'] + + self.assertEquals(U_BOOT_DATA, u_boot.ReadData()) + + def testTplNoDtb(self): + """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created""" + self._SetupTplElf() + data = self._DoReadFile('192_u_boot_tpl_nodtb.dts') + self.assertEqual(U_BOOT_TPL_NODTB_DATA, + data[:len(U_BOOT_TPL_NODTB_DATA)]) + + def testTplBssPad(self): + """Test that we can pad TPL's BSS with zeros""" + # ELF file with a '__bss_size' symbol + self._SetupTplElf() + data = self._DoReadFile('193_tpl_bss_pad.dts') + self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA, + data) + + def testTplBssPadMissing(self): + """Test that a missing symbol is detected""" + self._SetupTplElf('u_boot_ucode_ptr') + with self.assertRaises(ValueError) as e: + self._DoReadFile('193_tpl_bss_pad.dts') + self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl', + str(e.exception)) + + def checkDtbSizes(self, data, pad_len, start): + """Check the size arguments in a dtb embedded in an image + + Args: + data: The image data + pad_len: Length of the pad section in the image, in bytes + start: Start offset of the devicetree to examine, within the image + + Returns: + Size of the devicetree in bytes + """ + dtb_data = data[start:] + dtb = fdt.Fdt.FromData(dtb_data) + fdt_size = dtb.GetFdtObj().totalsize() + dtb.Scan() + props = self._GetPropTree(dtb, 'size') + self.assertEqual({ + 'size': len(data), + 'u-boot-spl/u-boot-spl-bss-pad:size': pad_len, + 'u-boot-spl/u-boot-spl-dtb:size': 801, + 'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA), + 'u-boot-spl:size': 860, + 'u-boot-tpl:size': len(U_BOOT_TPL_DATA), + 'u-boot/u-boot-dtb:size': 781, + 'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA), + 'u-boot:size': 827, + }, props) + return fdt_size + + def testExpanded(self): + """Test that an expanded entry type is selected when needed""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + entry_args = { + 'spl-dtb': '1', + 'spl-bss-pad': 'y', + 'tpl-dtb': '', + } + self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(3, len(entries)) + + # First, u-boot, which should be expanded into u-boot-nodtb and dtb + self.assertIn('u-boot', entries) + entry = entries['u-boot'] + self.assertEqual('u-boot-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-nodtb', subent) + self.assertIn('u-boot-dtb', subent) + + # Second, u-boot-spl, which should be expanded into three parts + self.assertIn('u-boot-spl', entries) + entry = entries['u-boot-spl'] + self.assertEqual('u-boot-spl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(3, len(subent)) + self.assertIn('u-boot-spl-nodtb', subent) + self.assertIn('u-boot-spl-bss-pad', subent) + self.assertIn('u-boot-spl-dtb', subent) + + # Third, u-boot-tpl, which should be not be expanded, since TPL has no + # devicetree + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl', entry.etype) + self.assertEqual(None, entry.GetEntries()) + + def testExpandedTpl(self): + """Test that an expanded entry type is selected for TPL when needed""" + self._SetupTplElf() + + entry_args = { + 'tpl-bss-pad': 'y', + 'tpl-dtb': 'y', + } + self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(1, len(entries)) + + # We only have u-boot-tpl, which be expanded + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(3, len(subent)) + self.assertIn('u-boot-tpl-nodtb', subent) + self.assertIn('u-boot-tpl-bss-pad', subent) + self.assertIn('u-boot-tpl-dtb', subent) + + def testExpandedNoPad(self): + """Test an expanded entry without BSS pad enabled""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + entry_args = { + 'spl-dtb': 'something', + 'spl-bss-pad': 'n', + 'tpl-dtb': '', + } + self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + + # Just check u-boot-spl, which should be expanded into two parts + self.assertIn('u-boot-spl', entries) + entry = entries['u-boot-spl'] + self.assertEqual('u-boot-spl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-spl-nodtb', subent) + self.assertIn('u-boot-spl-dtb', subent) + + def testExpandedTplNoPad(self): + """Test that an expanded entry type with padding disabled in TPL""" + self._SetupTplElf() + + entry_args = { + 'tpl-bss-pad': '', + 'tpl-dtb': 'y', + } + self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True, + entry_args=entry_args) + image = control.images['image'] + entries = image.GetEntries() + self.assertEqual(1, len(entries)) + + # We only have u-boot-tpl, which be expanded + self.assertIn('u-boot-tpl', entries) + entry = entries['u-boot-tpl'] + self.assertEqual('u-boot-tpl-expanded', entry.etype) + subent = entry.GetEntries() + self.assertEqual(2, len(subent)) + self.assertIn('u-boot-tpl-nodtb', subent) + self.assertIn('u-boot-tpl-dtb', subent) + + def testFdtInclude(self): + """Test that an Fdt is update within all binaries""" + self._SetupSplElf() + self._SetupTplElf() + + # SPL has a devicetree, TPL does not + self.maxDiff = None + entry_args = { + 'spl-dtb': '1', + 'spl-bss-pad': 'y', + 'tpl-dtb': '', + } + # Build the image. It includes two separate devicetree binaries, each + # with their own contents, but all contain the binman definition. + data = self._DoReadFileDtb( + '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True, + update_dtb=True, entry_args=entry_args)[0] + pad_len = 10 + + # Check the U-Boot dtb + start = len(U_BOOT_NODTB_DATA) + fdt_size = self.checkDtbSizes(data, pad_len, start) + + # Now check SPL + start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len + fdt_size = self.checkDtbSizes(data, pad_len, start) + + # TPL has no devicetree + start += fdt_size + len(U_BOOT_TPL_DATA) + self.assertEqual(len(data), start) + + def testSymbolsExpanded(self): + """Test binman can assign symbols in expanded entries""" + entry_args = { + 'spl-dtb': '1', + } + self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA + + U_BOOT_SPL_DTB_DATA, 0x38, + entry_args=entry_args, use_expanded=True) + + def testCollection(self): + """Test a collection""" + data = self._DoReadFile('198_collection.dts') + self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + + tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA + + tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA, + data) + + def testCollectionSection(self): + """Test a collection where a section must be built first""" + # Sections never have their contents when GetData() is called, but when + # _BuildSectionData() is called with required=True, a section will force + # building the contents, producing an error is anything is still + # missing. + data = self._DoReadFile('199_collection_section.dts') + section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA + self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) + + section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA, + data) + + def testAlignDefault(self): + """Test that default alignment works on sections""" + data = self._DoReadFile('200_align_default.dts') + expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) + + U_BOOT_DATA) + # Special alignment for section + expected += tools.GetBytes(0, 32 - len(expected)) + # No alignment within the nested section + expected += U_BOOT_DATA + U_BOOT_NODTB_DATA; + # Now the final piece, which should be default-aligned + expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA + self.assertEqual(expected, data) + + def testPackOpenSBI(self): + """Test that an image with an OpenSBI binary can be created""" + data = self._DoReadFile('201_opensbi.dts') + self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)]) + +if __name__ == "__main__": + unittest.main() diff --git a/roms/u-boot/tools/binman/image.py b/roms/u-boot/tools/binman/image.py new file mode 100644 index 000000000..10778f47f --- /dev/null +++ b/roms/u-boot/tools/binman/image.py @@ -0,0 +1,387 @@ +# 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 collections import OrderedDict +import fnmatch +from operator import attrgetter +import os +import re +import sys + +from binman.entry import Entry +from binman.etype import fdtmap +from binman.etype import image_header +from binman.etype import section +from dtoc import fdt +from dtoc import fdt_util +from patman import tools +from patman import tout + +class Image(section.Entry_section): + """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. + + Attributes: + filename: Output filename for image + image_node: Name of node containing the description for this image + fdtmap_dtb: Fdt object for the fdtmap when loading from a file + fdtmap_data: Contents of the fdtmap when loading from a file + allow_repack: True to add properties to allow the image to be safely + repacked later + + Args: + copy_to_orig: Copy offset/size to orig_offset/orig_size after reading + from the device tree + 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. + ignore_missing: Ignore any missing entry arguments (i.e. don't raise an + exception). This should be used if the Image is being loaded from + a file rather than generated. In that case we obviously don't need + the entry arguments since the contents already exists. + use_expanded: True if we are updating the FDT wth entry offsets, etc. + and should use the expanded versions of the U-Boot entries. + Any entry type that includes a devicetree must put it in a + separate entry so that it will be updated. For example. 'u-boot' + normally just picks up 'u-boot.bin' which includes the + devicetree, but this is not updateable, since it comes into + binman as one piece and binman doesn't know that it is actually + an executable followed by a devicetree. Of course it could be + taught this, but then when reading an image (e.g. 'binman ls') + it may need to be able to split the devicetree out of the image + in order to determine the location of things. Instead we choose + to ignore 'u-boot-bin' in this case, and build it ourselves in + binman with 'u-boot-dtb.bin' and 'u-boot.dtb'. See + Entry_u_boot_expanded and Entry_blob_phase for details. + """ + def __init__(self, name, node, copy_to_orig=True, test=False, + ignore_missing=False, use_expanded=False): + super().__init__(None, 'section', node, test=test) + self.copy_to_orig = copy_to_orig + self.name = 'main-section' + self.image_name = name + self._filename = '%s.bin' % self.image_name + self.fdtmap_dtb = None + self.fdtmap_data = None + self.allow_repack = False + self._ignore_missing = ignore_missing + self.use_expanded = use_expanded + if not test: + self.ReadNode() + + def ReadNode(self): + super().ReadNode() + filename = fdt_util.GetString(self._node, 'filename') + if filename: + self._filename = filename + self.allow_repack = fdt_util.GetBool(self._node, 'allow-repack') + + @classmethod + def FromFile(cls, fname): + """Convert an image file into an Image for use in binman + + Args: + fname: Filename of image file to read + + Returns: + Image object on success + + Raises: + ValueError if something goes wrong + """ + data = tools.ReadFile(fname) + size = len(data) + + # First look for an image header + pos = image_header.LocateHeaderOffset(data) + if pos is None: + # Look for the FDT map + pos = fdtmap.LocateFdtmap(data) + if pos is None: + raise ValueError('Cannot find FDT map in image') + + # We don't know the FDT size, so check its header first + probe_dtb = fdt.Fdt.FromData( + data[pos + fdtmap.FDTMAP_HDR_LEN:pos + 256]) + dtb_size = probe_dtb.GetFdtObj().totalsize() + fdtmap_data = data[pos:pos + dtb_size + fdtmap.FDTMAP_HDR_LEN] + fdt_data = fdtmap_data[fdtmap.FDTMAP_HDR_LEN:] + out_fname = tools.GetOutputFilename('fdtmap.in.dtb') + tools.WriteFile(out_fname, fdt_data) + dtb = fdt.Fdt(out_fname) + dtb.Scan() + + # Return an Image with the associated nodes + root = dtb.GetRoot() + image = Image('image', root, copy_to_orig=False, ignore_missing=True) + + image.image_node = fdt_util.GetString(root, 'image-node', 'image') + image.fdtmap_dtb = dtb + image.fdtmap_data = fdtmap_data + image._data = data + image._filename = fname + image.image_name, _ = os.path.splitext(fname) + return image + + def Raise(self, msg): + """Convenience function to raise an error referencing an image""" + raise ValueError("Image '%s': %s" % (self._node.path, msg)) + + def PackEntries(self): + """Pack all entries into the image""" + super().Pack(0) + + def SetImagePos(self): + # This first section in the image so it starts at 0 + super().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. + + Returns: + True if the new data size is OK, False if expansion is needed + """ + return super().ProcessContents() + + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + super().WriteSymbols(self) + + def BuildImage(self): + """Write the image to a file""" + fname = tools.GetOutputFilename(self._filename) + tout.Info("Writing image to '%s'" % fname) + with open(fname, 'wb') as fd: + data = self.GetPaddedData() + fd.write(data) + tout.Info("Wrote %#x bytes" % len(data)) + + def WriteMap(self): + """Write a map of the image to a .map file + + Returns: + Filename of map file written + """ + filename = '%s.map' % self.image_name + fname = tools.GetOutputFilename(filename) + with open(fname, 'w') as fd: + print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'), + file=fd) + super().WriteMap(fd, 0) + return fname + + def BuildEntryList(self): + """List the files in an image + + Returns: + List of entry.EntryInfo objects describing all entries in the image + """ + entries = [] + self.ListEntries(entries, 0) + return entries + + def FindEntryPath(self, entry_path): + """Find an entry at a given path in the image + + Args: + entry_path: Path to entry (e.g. /ro-section/u-boot') + + Returns: + Entry object corresponding to that past + + Raises: + ValueError if no entry found + """ + parts = entry_path.split('/') + entries = self.GetEntries() + parent = '/' + for part in parts: + entry = entries.get(part) + if not entry: + raise ValueError("Entry '%s' not found in '%s'" % + (part, parent)) + parent = entry.GetPath() + entries = entry.GetEntries() + return entry + + def ReadData(self, decomp=True): + tout.Debug("Image '%s' ReadData(), size=%#x" % + (self.GetPath(), len(self._data))) + return self._data + + def GetListEntries(self, entry_paths): + """List the entries in an image + + This decodes the supplied image and returns a list of entries from that + image, preceded by a header. + + Args: + entry_paths: List of paths to match (each can have wildcards). Only + entries whose names match one of these paths will be printed + + Returns: + String error message if something went wrong, otherwise + 3-Tuple: + List of EntryInfo objects + List of lines, each + List of text columns, each a string + List of widths of each column + """ + def _EntryToStrings(entry): + """Convert an entry to a list of strings, one for each column + + Args: + entry: EntryInfo object containing information to output + + Returns: + List of strings, one for each field in entry + """ + def _AppendHex(val): + """Append a hex value, or an empty string if val is None + + Args: + val: Integer value, or None if none + """ + args.append('' if val is None else '>%x' % val) + + args = [' ' * entry.indent + entry.name] + _AppendHex(entry.image_pos) + _AppendHex(entry.size) + args.append(entry.etype) + _AppendHex(entry.offset) + _AppendHex(entry.uncomp_size) + return args + + def _DoLine(lines, line): + """Add a line to the output list + + This adds a line (a list of columns) to the output list. It also updates + the widths[] array with the maximum width of each column + + Args: + lines: List of lines to add to + line: List of strings, one for each column + """ + for i, item in enumerate(line): + widths[i] = max(widths[i], len(item)) + lines.append(line) + + def _NameInPaths(fname, entry_paths): + """Check if a filename is in a list of wildcarded paths + + Args: + fname: Filename to check + entry_paths: List of wildcarded paths (e.g. ['*dtb*', 'u-boot*', + 'section/u-boot']) + + Returns: + True if any wildcard matches the filename (using Unix filename + pattern matching, not regular expressions) + False if not + """ + for path in entry_paths: + if fnmatch.fnmatch(fname, path): + return True + return False + + entries = self.BuildEntryList() + + # This is our list of lines. Each item in the list is a list of strings, one + # for each column + lines = [] + HEADER = ['Name', 'Image-pos', 'Size', 'Entry-type', 'Offset', + 'Uncomp-size'] + num_columns = len(HEADER) + + # This records the width of each column, calculated as the maximum width of + # all the strings in that column + widths = [0] * num_columns + _DoLine(lines, HEADER) + + # We won't print anything unless it has at least this indent. So at the + # start we will print nothing, unless a path matches (or there are no + # entry paths) + MAX_INDENT = 100 + min_indent = MAX_INDENT + path_stack = [] + path = '' + indent = 0 + selected_entries = [] + for entry in entries: + if entry.indent > indent: + path_stack.append(path) + elif entry.indent < indent: + path_stack.pop() + if path_stack: + path = path_stack[-1] + '/' + entry.name + indent = entry.indent + + # If there are entry paths to match and we are not looking at a + # sub-entry of a previously matched entry, we need to check the path + if entry_paths and indent <= min_indent: + if _NameInPaths(path[1:], entry_paths): + # Print this entry and all sub-entries (=higher indent) + min_indent = indent + else: + # Don't print this entry, nor any following entries until we get + # a path match + min_indent = MAX_INDENT + continue + _DoLine(lines, _EntryToStrings(entry)) + selected_entries.append(entry) + return selected_entries, lines, widths + + def LookupImageSymbol(self, sym_name, optional, msg, base_addr): + """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. + + This searches through this image including all of its subsections. + + At present the only entry properties supported are: + offset + image_pos - 'base_addr' is added if this is not an end-at-4gb image + size + + 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 + base_addr: Base address of image. This is added to the returned + image_pos in most cases so that the returned position indicates + where the targeted entry/binary has actually been loaded. But + if end-at-4gb is used, this is not done, since the binary is + already assumed to be linked to the ROM position and using + execute-in-place (XIP). + + 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 + """ + entries = OrderedDict() + entries_by_name = {} + self._CollectEntries(entries, entries_by_name, self) + return self.LookupSymbol(sym_name, optional, msg, base_addr, + entries_by_name) diff --git a/roms/u-boot/tools/binman/image_test.py b/roms/u-boot/tools/binman/image_test.py new file mode 100644 index 000000000..e351fa84a --- /dev/null +++ b/roms/u-boot/tools/binman/image_test.py @@ -0,0 +1,44 @@ +# 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 binman.image import Image +from patman.test_util import capture_sys_output + +class TestImage(unittest.TestCase): + def testInvalidFormat(self): + image = Image('name', 'node', test=True) + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_something_prop_', False, 'msg', 0) + self.assertIn( + "msg: Symbol '_binman_something_prop_' has invalid format", + str(e.exception)) + + def testMissingSymbol(self): + image = Image('name', 'node', test=True) + image._entries = {} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_type_prop_pname', False, 'msg', 0) + self.assertIn("msg: Entry 'type' not found in list ()", + str(e.exception)) + + def testMissingSymbolOptional(self): + image = Image('name', 'node', test=True) + image._entries = {} + with capture_sys_output() as (stdout, stderr): + val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg', 0) + 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) + image._entries = {'u-boot': 1} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg', 0) + self.assertIn("msg: No such property 'bad", str(e.exception)) diff --git a/roms/u-boot/tools/binman/index.rst b/roms/u-boot/tools/binman/index.rst new file mode 100644 index 000000000..6eef7b5d0 --- /dev/null +++ b/roms/u-boot/tools/binman/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Binman +====== + +.. toctree:: + :maxdepth: 2 + + README diff --git a/roms/u-boot/tools/binman/main.py b/roms/u-boot/tools/binman/main.py new file mode 100755 index 000000000..8c1e478d5 --- /dev/null +++ b/roms/u-boot/tools/binman/main.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# 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""" + +from distutils.sysconfig import get_python_lib +import os +import site +import sys +import traceback +import unittest + +# Bring in the patman and dtoc libraries (but don't override the first path +# in PYTHONPATH) +our_path = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(2, os.path.join(our_path, '..')) + +from patman import test_util + +# Bring in the libfdt module +sys.path.insert(2, 'scripts/dtc/pylibfdt') +sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt')) +sys.path.insert(2, os.path.join(our_path, + '../../build-sandbox_spl/scripts/dtc/pylibfdt')) + +# When running under python-coverage on Ubuntu 16.04, the dist-packages +# directories are dropped from the python path. Add them in so that we can find +# the elffile module. We could use site.getsitepackages() here but unfortunately +# that is not available in a virtualenv. +sys.path.append(get_python_lib()) + +from binman import cmdline +from binman import control +from patman import test_util + +def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): + """Run the functional tests and any embedded doctests + + Args: + debug: True to enable debugging, which shows a full stack trace on error + verbosity: Verbosity level to use + test_preserve_dirs: True to preserve the input directory used by tests + so that it can be examined afterwards (only useful for debugging + tests). If a single test is selected (in args[0]) it also preserves + the output directory for this test. Both directories are displayed + on the command line. + processes: Number of processes to use to run tests (None=same as #CPUs) + args: List of positional args provided to binman. This can hold a test + name to execute (as in 'binman test testSections', for example) + toolpath: List of paths to use for tools + """ + from binman import cbfs_util_test + from binman import elf_test + from binman import entry_test + from binman import fdt_test + from binman import ftest + from binman import image_test + import doctest + + result = unittest.TestResult() + test_name = args and args[0] or None + + # Run the entry tests first ,since these need to be the first to import the + # 'entry' module. + test_util.RunTestSuites( + result, debug, verbosity, test_preserve_dirs, processes, test_name, + toolpath, + [entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, + elf_test.TestElf, image_test.TestImage, cbfs_util_test.TestCbfs]) + + return test_util.ReportResult('binman', test_name, result) + +def RunTestCoverage(toolpath): + """Run the tests and check that we get 100% coverage""" + glob_list = control.GetEntryModules(False) + all_set = set([os.path.splitext(os.path.basename(item))[0] + for item in glob_list if '_testing' not in item]) + extra_args = '' + if toolpath: + for path in toolpath: + extra_args += ' --toolpath %s' % path + test_util.RunTestCoverage('tools/binman/binman', None, + ['*test*', '*main.py', 'tools/patman/*', 'tools/dtoc/*'], + args.build_dir, all_set, extra_args or None) + +def RunBinman(args): + """Main entry point to binman once arguments are parsed + + Args: + args: Command line arguments Namespace object + """ + ret_code = 0 + + if not args.debug: + sys.tracebacklimit = 0 + + # Provide a default toolpath in the hope of finding a mkimage built from + # current source + if not args.toolpath: + args.toolpath = ['./tools', 'build-sandbox/tools'] + + if args.cmd == 'test': + if args.test_coverage: + RunTestCoverage(args.toolpath) + else: + ret_code = RunTests(args.debug, args.verbosity, args.processes, + args.test_preserve_dirs, args.tests, + args.toolpath) + + elif args.cmd == 'entry-docs': + control.WriteEntryDocs(control.GetEntryModules()) + + else: + try: + ret_code = control.Binman(args) + except Exception as e: + print('binman: %s' % e, file=sys.stderr) + if args.debug: + print() + traceback.print_exc() + ret_code = 1 + return ret_code + + +if __name__ == "__main__": + args = cmdline.ParseArgs(sys.argv[1:]) + + ret_code = RunBinman(args) + sys.exit(ret_code) diff --git a/roms/u-boot/tools/binman/missing-blob-help b/roms/u-boot/tools/binman/missing-blob-help new file mode 100644 index 000000000..f7bc80ea8 --- /dev/null +++ b/roms/u-boot/tools/binman/missing-blob-help @@ -0,0 +1,19 @@ +# This file contains help messages for missing external blobs. Each message has +# a tag (MUST be just lower-case text, digits and hyphens) starting in column 1, +# followed by a colon (:) to indicate its start. The message can include any +# number of lines, including blank lines. +# +# When looking for a tag, Binman uses the value of 'missing-msg' for the entry, +# the entry name or the entry type, in that order + +atf-bl31: +See the documentation for your board. You may need to build ARM Trusted +Firmware and build with BL31=/path/to/bl31.bin + +atf-bl31-sunxi: +Please read the section on ARM Trusted Firmware (ATF) in +board/sunxi/README.sunxi64 + +scp-sunxi: +SCP firmware is required for system suspend, but is otherwise optional. +Please read the section on SCP firmware in board/sunxi/README.sunxi64 diff --git a/roms/u-boot/tools/binman/setup.py b/roms/u-boot/tools/binman/setup.py new file mode 100644 index 000000000..5ed94abda --- /dev/null +++ b/roms/u-boot/tools/binman/setup.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ + +from distutils.core import setup +setup(name='binman', + version='1.0', + license='GPL-2.0+', + scripts=['binman'], + packages=['binman', 'binman.etype'], + package_dir={'binman': ''}, + package_data={'binman': ['README.rst', 'entries.rst']}, + classifiers=['Environment :: Console', + 'Topic :: Software Development :: Embedded Systems']) diff --git a/roms/u-boot/tools/binman/state.py b/roms/u-boot/tools/binman/state.py new file mode 100644 index 000000000..dfb176045 --- /dev/null +++ b/roms/u-boot/tools/binman/state.py @@ -0,0 +1,422 @@ +# 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 dtoc import fdt +import os +from patman import tools +from patman import tout + +# Map an dtb etype to its expected filename +DTB_TYPE_FNAME = { + 'u-boot-spl-dtb': 'spl/u-boot-spl.dtb', + 'u-boot-tpl-dtb': 'tpl/u-boot-tpl.dtb', + } + +# Records the device-tree files known to binman, keyed by entry type (e.g. +# 'u-boot-spl-dtb'). These are the output FDT files, which can be updated by +# binman. They have been copied to <xxx>.out files. +# +# key: entry type (e.g. 'u-boot-dtb) +# value: tuple: +# Fdt object +# Filename +output_fdt_info = {} + +# Prefix to add to an fdtmap path to turn it into a path to the /binman node +fdt_path_prefix = '' + +# 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 + +# The DTB which contains the full image information +main_dtb = None + +# Allow entries to expand after they have been packed. This is detected and +# forces a re-pack. If not allowed, any attempted expansion causes an error in +# Entry.ProcessContentsUpdate() +allow_entry_expansion = True + +# Don't allow entries to contract after they have been packed. Instead just +# leave some wasted space. If allowed, this is detected and forces a re-pack, +# but may result in entries that oscillate in size, thus causing a pack error. +# An example is a compressed device tree where the original offset values +# result in a larger compressed size than the new ones, but then after updating +# to the new ones, the compressed size increases, etc. +allow_entry_contraction = False + +def GetFdtForEtype(etype): + """Get the Fdt object for a particular device-tree entry + + 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 + entry and returns the associated Fdt object. + + Args: + etype: Entry type of device tree (e.g. 'u-boot-dtb') + + Returns: + Fdt object associated with the entry type + """ + value = output_fdt_info.get(etype); + if not value: + return None + return value[0] + +def GetFdtPath(etype): + """Get the full pathname of a particular Fdt object + + Similar to GetFdtForEtype() but returns the pathname associated with the + Fdt. + + Args: + etype: Entry type of device tree (e.g. 'u-boot-dtb') + + Returns: + Full path name to the associated Fdt + """ + return output_fdt_info[etype][0]._fname + +def GetFdtContents(etype='u-boot-dtb'): + """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: + etype: Entry type to look up (e.g. 'u-boot.dtb'). + + Returns: + tuple: + pathname to Fdt + Fdt data (as bytes) + """ + if etype not in output_fdt_info: + return None, None + if not use_fake_dtb: + pathname = GetFdtPath(etype) + data = GetFdtForEtype(etype).GetContents() + else: + fname = output_fdt_info[etype][1] + pathname = tools.GetInputFilename(fname) + data = tools.ReadFile(pathname) + return pathname, data + +def UpdateFdtContents(etype, data): + """Update the contents of a particular device tree + + The device tree is updated and written back to its file. This affects what + is returned from future called to GetFdtContents(), etc. + + Args: + etype: Entry type (e.g. 'u-boot-dtb') + data: Data to replace the DTB with + """ + dtb, fname = output_fdt_info[etype] + dtb_fname = dtb.GetFilename() + tools.WriteFile(dtb_fname, data) + dtb = fdt.FdtScan(dtb_fname) + output_fdt_info[etype] = [dtb, fname] + +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 = {} + tout.Debug('Processing entry args:') + if args: + for arg in args: + m = re.match('([^=]*)=(.*)', arg) + if not m: + raise ValueError("Invalid entry arguemnt '%s'" % arg) + name, value = m.groups() + tout.Debug(' %20s = %s' % (name, value)) + entry_args[name] = value + tout.Debug('Processing entry args done') + +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 GetEntryArgBool(name): + """Get the value of an entry argument as a boolean + + Args: + name: Name of argument to retrieve + + Returns: + False if the entry argument is consider False (empty, '0' or 'n'), else + True + """ + val = GetEntryArg(name) + return val and val not in ['n', '0'] + +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 + GetAllFdts(). This includes U-Boot proper and any SPL device trees. + + Args: + images: List of images being used + dtb: Main dtb + """ + global output_fdt_info, main_dtb, fdt_path_prefix + # Import these here in case libfdt.py is not available, in which case + # the above help option still works. + from dtoc import fdt + from dtoc 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 + output_fdt_info.clear() + fdt_path_prefix = '' + output_fdt_info['u-boot-dtb'] = [dtb, 'u-boot.dtb'] + if use_fake_dtb: + for etype, fname in DTB_TYPE_FNAME.items(): + output_fdt_info[etype] = [dtb, fname] + else: + fdt_set = {} + for etype, fname in DTB_TYPE_FNAME.items(): + infile = tools.GetInputFilename(fname, allow_missing=True) + if infile and os.path.exists(infile): + fname_dtb = fdt_util.EnsureCompiled(infile) + out_fname = tools.GetOutputFilename('%s.out' % + os.path.split(fname)[1]) + tools.WriteFile(out_fname, tools.ReadFile(fname_dtb)) + other_dtb = fdt.FdtScan(out_fname) + output_fdt_info[etype] = [other_dtb, out_fname] + + +def PrepareFromLoadedData(image): + """Get device tree files ready for use with a loaded image + + Loaded images are different from images that are being created by binman, + since there is generally already an fdtmap and we read the description from + that. This provides the position and size of every entry in the image with + no calculation required. + + This function uses the same output_fdt_info[] as Prepare(). It finds the + device tree files, adds a reference to the fdtmap and sets the FDT path + prefix to translate from the fdtmap (where the root node is the image node) + to the normal device tree (where the image node is under a /binman node). + + Args: + images: List of images being used + """ + global output_fdt_info, main_dtb, fdt_path_prefix + + tout.Info('Preparing device trees') + output_fdt_info.clear() + fdt_path_prefix = '' + output_fdt_info['fdtmap'] = [image.fdtmap_dtb, 'u-boot.dtb'] + main_dtb = None + tout.Info(" Found device tree type 'fdtmap' '%s'" % image.fdtmap_dtb.name) + for etype, value in image.GetFdts().items(): + entry, fname = value + out_fname = tools.GetOutputFilename('%s.dtb' % entry.etype) + tout.Info(" Found device tree type '%s' at '%s' path '%s'" % + (etype, out_fname, entry.GetPath())) + entry._filename = entry.GetDefaultFilename() + data = entry.ReadData() + + tools.WriteFile(out_fname, data) + dtb = fdt.Fdt(out_fname) + dtb.Scan() + image_node = dtb.GetNode('/binman') + if 'multiple-images' in image_node.props: + image_node = dtb.GetNode('/binman/%s' % image.image_node) + fdt_path_prefix = image_node.path + output_fdt_info[etype] = [dtb, None] + tout.Info(" FDT path prefix '%s'" % fdt_path_prefix) + + +def GetAllFdts(): + """Yield all device tree files being used by binman + + Yields: + Device trees being used (U-Boot proper, SPL, TPL) + """ + if main_dtb: + yield main_dtb + for etype in output_fdt_info: + dtb = output_fdt_info[etype][0] + if dtb != main_dtb: + yield dtb + +def GetUpdateNodes(node, for_repack=False): + """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 + for_repack: True if we want only nodes which need 'repack' properties + added to them (e.g. 'orig-offset'), False to return all nodes. We + don't add repack properties to SPL/TPL device trees. + + Yields: + Node objects in each device tree that is in use (U-Boot proper, which + is node, SPL and TPL) + """ + yield node + for entry_type, (dtb, fname) in output_fdt_info.items(): + if dtb != node.GetFdt(): + if for_repack and entry_type != 'u-boot-dtb': + continue + other_node = dtb.GetNode(fdt_path_prefix + node.path) + if other_node: + yield other_node + +def AddZeroProp(node, prop, for_repack=False): + """Add a new property to affected device trees with an integer value of 0. + + Args: + prop_name: Name of property + for_repack: True is this property is only needed for repacking + """ + for n in GetUpdateNodes(node, for_repack): + 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 AddInt(node, prop, value): + """Add a new string property to affected device trees + + Args: + prop_name: Name of property + val: Integer value of property + """ + for n in GetUpdateNodes(node): + n.AddInt(prop, value) + +def SetInt(node, prop, value, for_repack=False): + """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_repack: True is this property is only needed for repacking + """ + for n in GetUpdateNodes(node, for_repack): + tout.Detail("File %s: Update node '%s' prop '%s' to %#x" % + (n.GetFdt().name, n.path, prop, value)) + 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) + +def SetAllowEntryExpansion(allow): + """Set whether post-pack expansion of entries is allowed + + Args: + allow: True to allow expansion, False to raise an exception + """ + global allow_entry_expansion + + allow_entry_expansion = allow + +def AllowEntryExpansion(): + """Check whether post-pack expansion of entries is allowed + + Returns: + True if expansion should be allowed, False if an exception should be + raised + """ + return allow_entry_expansion + +def SetAllowEntryContraction(allow): + """Set whether post-pack contraction of entries is allowed + + Args: + allow: True to allow contraction, False to raise an exception + """ + global allow_entry_contraction + + allow_entry_contraction = allow + +def AllowEntryContraction(): + """Check whether post-pack contraction of entries is allowed + + Returns: + True if contraction should be allowed, False if an exception should be + raised + """ + return allow_entry_contraction diff --git a/roms/u-boot/tools/binman/test/001_invalid.dts b/roms/u-boot/tools/binman/test/001_invalid.dts new file mode 100644 index 000000000..7d00455d7 --- /dev/null +++ b/roms/u-boot/tools/binman/test/001_invalid.dts @@ -0,0 +1,5 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; diff --git a/roms/u-boot/tools/binman/test/002_missing_node.dts b/roms/u-boot/tools/binman/test/002_missing_node.dts new file mode 100644 index 000000000..3a51ec2be --- /dev/null +++ b/roms/u-boot/tools/binman/test/002_missing_node.dts @@ -0,0 +1,6 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; +}; diff --git a/roms/u-boot/tools/binman/test/003_empty.dts b/roms/u-boot/tools/binman/test/003_empty.dts new file mode 100644 index 000000000..493c9a04c --- /dev/null +++ b/roms/u-boot/tools/binman/test/003_empty.dts @@ -0,0 +1,9 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + }; +}; diff --git a/roms/u-boot/tools/binman/test/004_invalid_entry.dts b/roms/u-boot/tools/binman/test/004_invalid_entry.dts new file mode 100644 index 000000000..b043455bb --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/005_simple.dts b/roms/u-boot/tools/binman/test/005_simple.dts new file mode 100644 index 000000000..3771aa226 --- /dev/null +++ b/roms/u-boot/tools/binman/test/005_simple.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/006_dual_image.dts b/roms/u-boot/tools/binman/test/006_dual_image.dts new file mode 100644 index 000000000..78be16f16 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/007_bad_align.dts b/roms/u-boot/tools/binman/test/007_bad_align.dts new file mode 100644 index 000000000..123bb1355 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/008_pack.dts b/roms/u-boot/tools/binman/test/008_pack.dts new file mode 100644 index 000000000..a88785d83 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/009_pack_extra.dts b/roms/u-boot/tools/binman/test/009_pack_extra.dts new file mode 100644 index 000000000..1b3155577 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/010_pack_align_power2.dts b/roms/u-boot/tools/binman/test/010_pack_align_power2.dts new file mode 100644 index 000000000..8f6253a3d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/011_pack_align_size_power2.dts b/roms/u-boot/tools/binman/test/011_pack_align_size_power2.dts new file mode 100644 index 000000000..04f7672ea --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/012_pack_inv_align.dts b/roms/u-boot/tools/binman/test/012_pack_inv_align.dts new file mode 100644 index 000000000..d8dd600ed --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/013_pack_inv_size_align.dts b/roms/u-boot/tools/binman/test/013_pack_inv_size_align.dts new file mode 100644 index 000000000..dfafa134d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/014_pack_overlap.dts b/roms/u-boot/tools/binman/test/014_pack_overlap.dts new file mode 100644 index 000000000..3895cba3b --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/015_pack_overflow.dts b/roms/u-boot/tools/binman/test/015_pack_overflow.dts new file mode 100644 index 000000000..6f654330a --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/016_pack_image_overflow.dts b/roms/u-boot/tools/binman/test/016_pack_image_overflow.dts new file mode 100644 index 000000000..6ae66f3ac --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/017_pack_image_size.dts b/roms/u-boot/tools/binman/test/017_pack_image_size.dts new file mode 100644 index 000000000..2360eb5d1 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/018_pack_image_align.dts b/roms/u-boot/tools/binman/test/018_pack_image_align.dts new file mode 100644 index 000000000..16cd2a422 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/019_pack_inv_image_align.dts b/roms/u-boot/tools/binman/test/019_pack_inv_image_align.dts new file mode 100644 index 000000000..e5ee87b88 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/020_pack_inv_image_align_power2.dts b/roms/u-boot/tools/binman/test/020_pack_inv_image_align_power2.dts new file mode 100644 index 000000000..a428c4be5 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/021_image_pad.dts b/roms/u-boot/tools/binman/test/021_image_pad.dts new file mode 100644 index 000000000..1ff8dab29 --- /dev/null +++ b/roms/u-boot/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 = <24>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/022_image_name.dts b/roms/u-boot/tools/binman/test/022_image_name.dts new file mode 100644 index 000000000..94fc069c1 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/023_blob.dts b/roms/u-boot/tools/binman/test/023_blob.dts new file mode 100644 index 000000000..7dcff6966 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/024_sorted.dts b/roms/u-boot/tools/binman/test/024_sorted.dts new file mode 100644 index 000000000..b79d9adf6 --- /dev/null +++ b/roms/u-boot/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 = <26>; + }; + + u-boot-spl { + offset = <1>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/025_pack_zero_size.dts b/roms/u-boot/tools/binman/test/025_pack_zero_size.dts new file mode 100644 index 000000000..e863c44e3 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/026_pack_u_boot_dtb.dts b/roms/u-boot/tools/binman/test/026_pack_u_boot_dtb.dts new file mode 100644 index 000000000..2707a7347 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/027_pack_4gb_no_size.dts b/roms/u-boot/tools/binman/test/027_pack_4gb_no_size.dts new file mode 100644 index 000000000..371cca10d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/028_pack_4gb_outside.dts b/roms/u-boot/tools/binman/test/028_pack_4gb_outside.dts new file mode 100644 index 000000000..11a1f6059 --- /dev/null +++ b/roms/u-boot/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 = <0xffffffe7>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/029_x86_rom.dts b/roms/u-boot/tools/binman/test/029_x86_rom.dts new file mode 100644 index 000000000..88aa007bb --- /dev/null +++ b/roms/u-boot/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 = <0xffffffe7>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/030_x86_rom_me_no_desc.dts b/roms/u-boot/tools/binman/test/030_x86_rom_me_no_desc.dts new file mode 100644 index 000000000..796cb87af --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/031_x86_rom_me.dts b/roms/u-boot/tools/binman/test/031_x86_rom_me.dts new file mode 100644 index 000000000..b8b0a5a74 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/032_intel_vga.dts b/roms/u-boot/tools/binman/test/032_intel_vga.dts new file mode 100644 index 000000000..9c532d03d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/033_x86_start16.dts b/roms/u-boot/tools/binman/test/033_x86_start16.dts new file mode 100644 index 000000000..2e279dee9 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/034_x86_ucode.dts b/roms/u-boot/tools/binman/test/034_x86_ucode.dts new file mode 100644 index 000000000..40725731c --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/035_x86_single_ucode.dts b/roms/u-boot/tools/binman/test/035_x86_single_ucode.dts new file mode 100644 index 000000000..2b1f086a4 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/036_u_boot_img.dts b/roms/u-boot/tools/binman/test/036_u_boot_img.dts new file mode 100644 index 000000000..aa5a3fe48 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/037_x86_no_ucode.dts b/roms/u-boot/tools/binman/test/037_x86_no_ucode.dts new file mode 100644 index 000000000..6da49c3da --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/038_x86_ucode_missing_node.dts b/roms/u-boot/tools/binman/test/038_x86_ucode_missing_node.dts new file mode 100644 index 000000000..720677c9c --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/039_x86_ucode_missing_node2.dts b/roms/u-boot/tools/binman/test/039_x86_ucode_missing_node2.dts new file mode 100644 index 000000000..10ac086d5 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/040_x86_ucode_not_in_image.dts b/roms/u-boot/tools/binman/test/040_x86_ucode_not_in_image.dts new file mode 100644 index 000000000..609725824 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/041_unknown_pos_size.dts b/roms/u-boot/tools/binman/test/041_unknown_pos_size.dts new file mode 100644 index 000000000..94fe821c4 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/042_intel_fsp.dts b/roms/u-boot/tools/binman/test/042_intel_fsp.dts new file mode 100644 index 000000000..8a7c88925 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/043_intel_cmc.dts b/roms/u-boot/tools/binman/test/043_intel_cmc.dts new file mode 100644 index 000000000..5a56c7d88 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/044_x86_optional_ucode.dts b/roms/u-boot/tools/binman/test/044_x86_optional_ucode.dts new file mode 100644 index 000000000..24a7040d3 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/045_prop_test.dts b/roms/u-boot/tools/binman/test/045_prop_test.dts new file mode 100644 index 000000000..064de2b31 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/046_intel_vbt.dts b/roms/u-boot/tools/binman/test/046_intel_vbt.dts new file mode 100644 index 000000000..733f5751d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/047_spl_bss_pad.dts b/roms/u-boot/tools/binman/test/047_spl_bss_pad.dts new file mode 100644 index 000000000..6bd88b83f --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/048_x86_start16_spl.dts b/roms/u-boot/tools/binman/test/048_x86_start16_spl.dts new file mode 100644 index 000000000..e2009f15f --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/049_x86_ucode_spl.dts b/roms/u-boot/tools/binman/test/049_x86_ucode_spl.dts new file mode 100644 index 000000000..350d2c473 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/050_intel_mrc.dts b/roms/u-boot/tools/binman/test/050_intel_mrc.dts new file mode 100644 index 000000000..54cd52a2b --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/051_u_boot_spl_dtb.dts b/roms/u-boot/tools/binman/test/051_u_boot_spl_dtb.dts new file mode 100644 index 000000000..3912f86b4 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/052_u_boot_spl_nodtb.dts b/roms/u-boot/tools/binman/test/052_u_boot_spl_nodtb.dts new file mode 100644 index 000000000..7f4e27780 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/053_symbols.dts b/roms/u-boot/tools/binman/test/053_symbols.dts new file mode 100644 index 000000000..296580927 --- /dev/null +++ b/roms/u-boot/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 = <0x18>; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/054_unit_address.dts b/roms/u-boot/tools/binman/test/054_unit_address.dts new file mode 100644 index 000000000..3216dbbcc --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/055_sections.dts b/roms/u-boot/tools/binman/test/055_sections.dts new file mode 100644 index 000000000..6b306aeda --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/056_name_prefix.dts b/roms/u-boot/tools/binman/test/056_name_prefix.dts new file mode 100644 index 000000000..f38c80eb1 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/057_unknown_contents.dts b/roms/u-boot/tools/binman/test/057_unknown_contents.dts new file mode 100644 index 000000000..6ea98d7ca --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/058_x86_ucode_spl_needs_retry.dts b/roms/u-boot/tools/binman/test/058_x86_ucode_spl_needs_retry.dts new file mode 100644 index 000000000..a04adaaf7 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/059_change_size.dts b/roms/u-boot/tools/binman/test/059_change_size.dts new file mode 100644 index 000000000..1a69026a6 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/060_fdt_update.dts b/roms/u-boot/tools/binman/test/060_fdt_update.dts new file mode 100644 index 000000000..f53c8a505 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/061_fdt_update_bad.dts b/roms/u-boot/tools/binman/test/061_fdt_update_bad.dts new file mode 100644 index 000000000..e5abf3169 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/062_entry_args.dts b/roms/u-boot/tools/binman/test/062_entry_args.dts new file mode 100644 index 000000000..4d4f102d6 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/063_entry_args_missing.dts b/roms/u-boot/tools/binman/test/063_entry_args_missing.dts new file mode 100644 index 000000000..1644e2fef --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/064_entry_args_required.dts b/roms/u-boot/tools/binman/test/064_entry_args_required.dts new file mode 100644 index 000000000..705be1006 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/065_entry_args_unknown_datatype.dts b/roms/u-boot/tools/binman/test/065_entry_args_unknown_datatype.dts new file mode 100644 index 000000000..3e4838f4f --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/066_text.dts b/roms/u-boot/tools/binman/test/066_text.dts new file mode 100644 index 000000000..f23a75ae9 --- /dev/null +++ b/roms/u-boot/tools/binman/test/066_text.dts @@ -0,0 +1,33 @@ +// 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"; + }; + /* Put text directly in the node */ + text5 { + type = "text"; + text = "more text"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/067_fmap.dts b/roms/u-boot/tools/binman/test/067_fmap.dts new file mode 100644 index 000000000..9c0e293ac --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/068_blob_named_by_arg.dts b/roms/u-boot/tools/binman/test/068_blob_named_by_arg.dts new file mode 100644 index 000000000..e129f843c --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/069_fill.dts b/roms/u-boot/tools/binman/test/069_fill.dts new file mode 100644 index 000000000..e372ea37a --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/070_fill_no_size.dts b/roms/u-boot/tools/binman/test/070_fill_no_size.dts new file mode 100644 index 000000000..7b1fcf1b6 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/071_gbb.dts b/roms/u-boot/tools/binman/test/071_gbb.dts new file mode 100644 index 000000000..551756372 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/072_gbb_too_small.dts b/roms/u-boot/tools/binman/test/072_gbb_too_small.dts new file mode 100644 index 000000000..c088f36a1 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/073_gbb_no_size.dts b/roms/u-boot/tools/binman/test/073_gbb_no_size.dts new file mode 100644 index 000000000..83be40378 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/074_vblock.dts b/roms/u-boot/tools/binman/test/074_vblock.dts new file mode 100644 index 000000000..f0c21bfe9 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/075_vblock_no_content.dts b/roms/u-boot/tools/binman/test/075_vblock_no_content.dts new file mode 100644 index 000000000..676d9474b --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/076_vblock_bad_phandle.dts b/roms/u-boot/tools/binman/test/076_vblock_bad_phandle.dts new file mode 100644 index 000000000..ffbd0c335 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/077_vblock_bad_entry.dts b/roms/u-boot/tools/binman/test/077_vblock_bad_entry.dts new file mode 100644 index 000000000..764c42a56 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/078_u_boot_tpl.dts b/roms/u-boot/tools/binman/test/078_u_boot_tpl.dts new file mode 100644 index 000000000..6c60b4c46 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/079_uses_pos.dts b/roms/u-boot/tools/binman/test/079_uses_pos.dts new file mode 100644 index 000000000..7638b9b5e --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/080_fill_empty.dts b/roms/u-boot/tools/binman/test/080_fill_empty.dts new file mode 100644 index 000000000..2b78d3ae8 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/081_x86_start16_tpl.dts b/roms/u-boot/tools/binman/test/081_x86_start16_tpl.dts new file mode 100644 index 000000000..68e6bbd68 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/082_fdt_update_all.dts b/roms/u-boot/tools/binman/test/082_fdt_update_all.dts new file mode 100644 index 000000000..284975cc2 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/083_compress.dts b/roms/u-boot/tools/binman/test/083_compress.dts new file mode 100644 index 000000000..07813bdea --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/084_files.dts b/roms/u-boot/tools/binman/test/084_files.dts new file mode 100644 index 000000000..8f09afd24 --- /dev/null +++ b/roms/u-boot/tools/binman/test/084_files.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.dat"; + files-compress = "none"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/085_files_compress.dts b/roms/u-boot/tools/binman/test/085_files_compress.dts new file mode 100644 index 000000000..5aeead2e6 --- /dev/null +++ b/roms/u-boot/tools/binman/test/085_files_compress.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.dat"; + files-compress = "lz4"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/086_files_none.dts b/roms/u-boot/tools/binman/test/086_files_none.dts new file mode 100644 index 000000000..34bd92f22 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/087_files_no_pattern.dts b/roms/u-boot/tools/binman/test/087_files_no_pattern.dts new file mode 100644 index 000000000..0cb5b469c --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/088_expand_size.dts b/roms/u-boot/tools/binman/test/088_expand_size.dts new file mode 100644 index 000000000..c8a01308e --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/089_expand_size_bad.dts b/roms/u-boot/tools/binman/test/089_expand_size_bad.dts new file mode 100644 index 000000000..edc0e5cf6 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/090_hash.dts b/roms/u-boot/tools/binman/test/090_hash.dts new file mode 100644 index 000000000..200304599 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/091_hash_no_algo.dts b/roms/u-boot/tools/binman/test/091_hash_no_algo.dts new file mode 100644 index 000000000..b64df2051 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/092_hash_bad_algo.dts b/roms/u-boot/tools/binman/test/092_hash_bad_algo.dts new file mode 100644 index 000000000..d2402000d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/093_x86_tpl_ucode.dts b/roms/u-boot/tools/binman/test/093_x86_tpl_ucode.dts new file mode 100644 index 000000000..d7ed9fc66 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/094_fmap_x86.dts b/roms/u-boot/tools/binman/test/094_fmap_x86.dts new file mode 100644 index 000000000..613c5dab4 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/095_fmap_x86_section.dts b/roms/u-boot/tools/binman/test/095_fmap_x86_section.dts new file mode 100644 index 000000000..fd5f018c9 --- /dev/null +++ b/roms/u-boot/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 = <0x180>; + u-boot { + }; + section { + pad-byte = <0x62>; + intel-mrc { + }; + fmap { + offset = <0x20>; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/096_elf.dts b/roms/u-boot/tools/binman/test/096_elf.dts new file mode 100644 index 000000000..8e3f3f15e --- /dev/null +++ b/roms/u-boot/tools/binman/test/096_elf.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-elf { + }; + u-boot-spl-elf { + }; + u-boot-tpl-elf { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/097_elf_strip.dts b/roms/u-boot/tools/binman/test/097_elf_strip.dts new file mode 100644 index 000000000..6f3c66fd7 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/098_4gb_and_skip_at_start_together.dts b/roms/u-boot/tools/binman/test/098_4gb_and_skip_at_start_together.dts new file mode 100644 index 000000000..90c467d91 --- /dev/null +++ b/roms/u-boot/tools/binman/test/098_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/roms/u-boot/tools/binman/test/099_hash_section.dts b/roms/u-boot/tools/binman/test/099_hash_section.dts new file mode 100644 index 000000000..dcd8683d6 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/100_intel_refcode.dts b/roms/u-boot/tools/binman/test/100_intel_refcode.dts new file mode 100644 index 000000000..0a1a0270e --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/101_sections_offset.dts b/roms/u-boot/tools/binman/test/101_sections_offset.dts new file mode 100644 index 000000000..46708ff9b --- /dev/null +++ b/roms/u-boot/tools/binman/test/101_sections_offset.dts @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + size = <0x38>; + section@0 { + read-only; + offset = <0x4>; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + size = <0x10>; + pad-byte = <0x61>; + offset = <0x18>; + + u-boot { + }; + }; + section@2 { + offset = <0x2c>; + u-boot { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/102_cbfs_raw.dts b/roms/u-boot/tools/binman/test/102_cbfs_raw.dts new file mode 100644 index 000000000..779cbc121 --- /dev/null +++ b/roms/u-boot/tools/binman/test/102_cbfs_raw.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0xb0>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/103_cbfs_raw_ppc.dts b/roms/u-boot/tools/binman/test/103_cbfs_raw_ppc.dts new file mode 100644 index 000000000..df1caf092 --- /dev/null +++ b/roms/u-boot/tools/binman/test/103_cbfs_raw_ppc.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + cbfs-arch = "ppc64"; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/104_cbfs_stage.dts b/roms/u-boot/tools/binman/test/104_cbfs_stage.dts new file mode 100644 index 000000000..215e2f287 --- /dev/null +++ b/roms/u-boot/tools/binman/test/104_cbfs_stage.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0xb0>; + u-boot { + type = "blob"; + filename = "cbfs-stage.elf"; + cbfs-type = "stage"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/105_cbfs_raw_compress.dts b/roms/u-boot/tools/binman/test/105_cbfs_raw_compress.dts new file mode 100644 index 000000000..646168d84 --- /dev/null +++ b/roms/u-boot/tools/binman/test/105_cbfs_raw_compress.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x140>; + u-boot { + type = "text"; + text = "compress xxxxxxxxxxxxxxxxxxxxxx data"; + cbfs-type = "raw"; + cbfs-compress = "lz4"; + }; + u-boot-dtb { + type = "text"; + text = "compress xxxxxxxxxxxxxxxxxxxxxx data"; + cbfs-type = "raw"; + cbfs-compress = "lzma"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/106_cbfs_bad_arch.dts b/roms/u-boot/tools/binman/test/106_cbfs_bad_arch.dts new file mode 100644 index 000000000..4318d45a7 --- /dev/null +++ b/roms/u-boot/tools/binman/test/106_cbfs_bad_arch.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + cbfs-arch = "bad-arch"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/107_cbfs_no_size.dts b/roms/u-boot/tools/binman/test/107_cbfs_no_size.dts new file mode 100644 index 000000000..3592f62f7 --- /dev/null +++ b/roms/u-boot/tools/binman/test/107_cbfs_no_size.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/108_cbfs_no_contents.dts b/roms/u-boot/tools/binman/test/108_cbfs_no_contents.dts new file mode 100644 index 000000000..623346760 --- /dev/null +++ b/roms/u-boot/tools/binman/test/108_cbfs_no_contents.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + _testing { + return-unknown-contents; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/109_cbfs_bad_compress.dts b/roms/u-boot/tools/binman/test/109_cbfs_bad_compress.dts new file mode 100644 index 000000000..9695024ee --- /dev/null +++ b/roms/u-boot/tools/binman/test/109_cbfs_bad_compress.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0xb0>; + u-boot { + cbfs-type = "raw"; + cbfs-compress = "invalid-algo"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/110_cbfs_name.dts b/roms/u-boot/tools/binman/test/110_cbfs_name.dts new file mode 100644 index 000000000..98c16f30b --- /dev/null +++ b/roms/u-boot/tools/binman/test/110_cbfs_name.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + u-boot { + cbfs-name = "FRED"; + cbfs-type = "raw"; + }; + + hello { + type = "blob"; + filename = "u-boot.dtb"; + cbfs-type = "raw"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/111_x86_rom_ifwi.dts b/roms/u-boot/tools/binman/test/111_x86_rom_ifwi.dts new file mode 100644 index 000000000..c0ba4f2ea --- /dev/null +++ b/roms/u-boot/tools/binman/test/111_x86_rom_ifwi.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor.bin"; + }; + + intel-ifwi { + offset-unset; + filename = "fitimage.bin"; + convert-fit; + + u-boot-tpl { + ifwi-replace; + ifwi-subpart = "IBBP"; + ifwi-entry = "IBBL"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/112_x86_rom_ifwi_nodesc.dts b/roms/u-boot/tools/binman/test/112_x86_rom_ifwi_nodesc.dts new file mode 100644 index 000000000..0874440ab --- /dev/null +++ b/roms/u-boot/tools/binman/test/112_x86_rom_ifwi_nodesc.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor.bin"; + }; + + intel-ifwi { + offset-unset; + filename = "ifwi.bin"; + + u-boot-tpl { + ifwi-replace; + ifwi-subpart = "IBBP"; + ifwi-entry = "IBBL"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/113_x86_rom_ifwi_nodata.dts b/roms/u-boot/tools/binman/test/113_x86_rom_ifwi_nodata.dts new file mode 100644 index 000000000..82a4bc8cd --- /dev/null +++ b/roms/u-boot/tools/binman/test/113_x86_rom_ifwi_nodata.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor.bin"; + }; + + intel-ifwi { + offset-unset; + filename = "ifwi.bin"; + + _testing { + return-unknown-contents; + ifwi-replace; + ifwi-subpart = "IBBP"; + ifwi-entry = "IBBL"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/114_cbfs_offset.dts b/roms/u-boot/tools/binman/test/114_cbfs_offset.dts new file mode 100644 index 000000000..7aa9d9d4b --- /dev/null +++ b/roms/u-boot/tools/binman/test/114_cbfs_offset.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x200>; + cbfs { + size = <0x200>; + offset = <0xfffffe00>; + u-boot { + cbfs-offset = <0x40>; + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-offset = <0x140>; + cbfs-type = "raw"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/115_fdtmap.dts b/roms/u-boot/tools/binman/test/115_fdtmap.dts new file mode 100644 index 000000000..2450c41f2 --- /dev/null +++ b/roms/u-boot/tools/binman/test/115_fdtmap.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fdtmap { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/116_fdtmap_hdr.dts b/roms/u-boot/tools/binman/test/116_fdtmap_hdr.dts new file mode 100644 index 000000000..77a2194b3 --- /dev/null +++ b/roms/u-boot/tools/binman/test/116_fdtmap_hdr.dts @@ -0,0 +1,17 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x400>; + u-boot { + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/117_fdtmap_hdr_start.dts b/roms/u-boot/tools/binman/test/117_fdtmap_hdr_start.dts new file mode 100644 index 000000000..17b6be004 --- /dev/null +++ b/roms/u-boot/tools/binman/test/117_fdtmap_hdr_start.dts @@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x400>; + sort-by-offset; + u-boot { + offset = <0x100>; + }; + fdtmap { + }; + image-header { + location = "start"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/118_fdtmap_hdr_pos.dts b/roms/u-boot/tools/binman/test/118_fdtmap_hdr_pos.dts new file mode 100644 index 000000000..fd803f57f --- /dev/null +++ b/roms/u-boot/tools/binman/test/118_fdtmap_hdr_pos.dts @@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x400>; + sort-by-offset; + u-boot { + offset = <0x100>; + }; + fdtmap { + }; + image-header { + offset = <0x80>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/119_fdtmap_hdr_missing.dts b/roms/u-boot/tools/binman/test/119_fdtmap_hdr_missing.dts new file mode 100644 index 000000000..41bb680f0 --- /dev/null +++ b/roms/u-boot/tools/binman/test/119_fdtmap_hdr_missing.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + u-boot { + }; + image-header { + offset = <0x80>; + location = "start"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/120_hdr_no_location.dts b/roms/u-boot/tools/binman/test/120_hdr_no_location.dts new file mode 100644 index 000000000..585e21f45 --- /dev/null +++ b/roms/u-boot/tools/binman/test/120_hdr_no_location.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + u-boot { + }; + fdtmap { + }; + image-header { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/121_entry_expand.dts b/roms/u-boot/tools/binman/test/121_entry_expand.dts new file mode 100644 index 000000000..ebb7816db --- /dev/null +++ b/roms/u-boot/tools/binman/test/121_entry_expand.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + bad-update-contents; + }; + + u-boot { + }; + + _testing2 { + type = "_testing"; + bad-update-contents; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/122_entry_expand_twice.dts b/roms/u-boot/tools/binman/test/122_entry_expand_twice.dts new file mode 100644 index 000000000..258cf859f --- /dev/null +++ b/roms/u-boot/tools/binman/test/122_entry_expand_twice.dts @@ -0,0 +1,21 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + bad-update-contents; + bad-update-contents-twice; + }; + + u-boot { + }; + + _testing2 { + type = "_testing"; + bad-update-contents; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/123_entry_expand_section.dts b/roms/u-boot/tools/binman/test/123_entry_expand_section.dts new file mode 100644 index 000000000..046f72343 --- /dev/null +++ b/roms/u-boot/tools/binman/test/123_entry_expand_section.dts @@ -0,0 +1,22 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + bad-update-contents; + }; + + u-boot { + }; + + section { + _testing2 { + type = "_testing"; + bad-update-contents; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/124_compress_dtb.dts b/roms/u-boot/tools/binman/test/124_compress_dtb.dts new file mode 100644 index 000000000..46bfd8b26 --- /dev/null +++ b/roms/u-boot/tools/binman/test/124_compress_dtb.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-dtb { + compress = "lz4"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/125_cbfs_update.dts b/roms/u-boot/tools/binman/test/125_cbfs_update.dts new file mode 100644 index 000000000..6d2e8a0b8 --- /dev/null +++ b/roms/u-boot/tools/binman/test/125_cbfs_update.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + u-boot { + cbfs-type = "raw"; + cbfs-compress = "lz4"; + }; + u-boot-dtb { + cbfs-type = "raw"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/126_cbfs_bad_type.dts b/roms/u-boot/tools/binman/test/126_cbfs_bad_type.dts new file mode 100644 index 000000000..2cd6fc6d5 --- /dev/null +++ b/roms/u-boot/tools/binman/test/126_cbfs_bad_type.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cbfs { + size = <0x100>; + u-boot { + cbfs-type = "badtype"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/127_list.dts b/roms/u-boot/tools/binman/test/127_list.dts new file mode 100644 index 000000000..c1d6fce3f --- /dev/null +++ b/roms/u-boot/tools/binman/test/127_list.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + section { + align = <0x100>; + cbfs { + size = <0x400>; + u-boot { + cbfs-type = "raw"; + cbfs-offset = <0x38>; + }; + u-boot-dtb { + type = "text"; + text = "compress xxxxxxxxxxxxxxxxxxxxxx data"; + cbfs-type = "raw"; + cbfs-compress = "lzma"; + cbfs-offset = <0x78>; + }; + }; + u-boot-dtb { + compress = "lz4"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/128_decode_image.dts b/roms/u-boot/tools/binman/test/128_decode_image.dts new file mode 100644 index 000000000..449fccc41 --- /dev/null +++ b/roms/u-boot/tools/binman/test/128_decode_image.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + u-boot { + }; + section { + align = <0x100>; + cbfs { + size = <0x400>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + cbfs-compress = "lzma"; + cbfs-offset = <0x80>; + }; + }; + u-boot-dtb { + compress = "lz4"; + }; + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/129_decode_image_nohdr.dts b/roms/u-boot/tools/binman/test/129_decode_image_nohdr.dts new file mode 100644 index 000000000..90fdd8820 --- /dev/null +++ b/roms/u-boot/tools/binman/test/129_decode_image_nohdr.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + u-boot { + }; + section { + align = <0x100>; + cbfs { + size = <0x400>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + cbfs-compress = "lzma"; + cbfs-offset = <0x80>; + }; + }; + u-boot-dtb { + compress = "lz4"; + }; + }; + fdtmap { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/130_list_fdtmap.dts b/roms/u-boot/tools/binman/test/130_list_fdtmap.dts new file mode 100644 index 000000000..449fccc41 --- /dev/null +++ b/roms/u-boot/tools/binman/test/130_list_fdtmap.dts @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + u-boot { + }; + section { + align = <0x100>; + cbfs { + size = <0x400>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + cbfs-compress = "lzma"; + cbfs-offset = <0x80>; + }; + }; + u-boot-dtb { + compress = "lz4"; + }; + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/131_pack_align_section.dts b/roms/u-boot/tools/binman/test/131_pack_align_section.dts new file mode 100644 index 000000000..44478855b --- /dev/null +++ b/roms/u-boot/tools/binman/test/131_pack_align_section.dts @@ -0,0 +1,28 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + section0 { + type = "section"; + align = <0x10>; + u-boot { + }; + }; + section1 { + type = "section"; + align-size = <0x20>; + u-boot { + }; + section2 { + type = "section"; + u-boot { + }; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/132_replace.dts b/roms/u-boot/tools/binman/test/132_replace.dts new file mode 100644 index 000000000..6ebdcda45 --- /dev/null +++ b/roms/u-boot/tools/binman/test/132_replace.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + u-boot { + }; + fdtmap { + }; + u-boot-dtb { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/133_replace_multi.dts b/roms/u-boot/tools/binman/test/133_replace_multi.dts new file mode 100644 index 000000000..38b2f39d0 --- /dev/null +++ b/roms/u-boot/tools/binman/test/133_replace_multi.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + multiple-images; + first-image { + size = <0xc00>; + u-boot { + }; + fdtmap { + }; + u-boot-dtb { + }; + image-header { + location = "end"; + }; + }; + + image { + fdtmap { + }; + u-boot { + }; + u-boot-dtb { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/134_fdt_update_all_repack.dts b/roms/u-boot/tools/binman/test/134_fdt_update_all_repack.dts new file mode 100644 index 000000000..625d37673 --- /dev/null +++ b/roms/u-boot/tools/binman/test/134_fdt_update_all_repack.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + allow-repack; + section { + size = <0x300>; + u-boot-dtb { + offset = <4>; + }; + }; + u-boot-spl-dtb { + }; + u-boot-tpl-dtb { + }; + fdtmap { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/135_fdtmap_hdr_middle.dts b/roms/u-boot/tools/binman/test/135_fdtmap_hdr_middle.dts new file mode 100644 index 000000000..d6211da8a --- /dev/null +++ b/roms/u-boot/tools/binman/test/135_fdtmap_hdr_middle.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + image-header { + location = "end"; + }; + fdtmap { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/136_fdtmap_hdr_startbad.dts b/roms/u-boot/tools/binman/test/136_fdtmap_hdr_startbad.dts new file mode 100644 index 000000000..ec5f4bc7e --- /dev/null +++ b/roms/u-boot/tools/binman/test/136_fdtmap_hdr_startbad.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fdtmap { + }; + image-header { + location = "start"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/137_fdtmap_hdr_endbad.dts b/roms/u-boot/tools/binman/test/137_fdtmap_hdr_endbad.dts new file mode 100644 index 000000000..ebacd71eb --- /dev/null +++ b/roms/u-boot/tools/binman/test/137_fdtmap_hdr_endbad.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + image-header { + location = "end"; + }; + u-boot { + }; + fdtmap { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/138_fdtmap_hdr_nosize.dts b/roms/u-boot/tools/binman/test/138_fdtmap_hdr_nosize.dts new file mode 100644 index 000000000..c362f8fdf --- /dev/null +++ b/roms/u-boot/tools/binman/test/138_fdtmap_hdr_nosize.dts @@ -0,0 +1,16 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/139_replace_repack.dts b/roms/u-boot/tools/binman/test/139_replace_repack.dts new file mode 100644 index 000000000..a3daf6f9b --- /dev/null +++ b/roms/u-boot/tools/binman/test/139_replace_repack.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + allow-repack; + u-boot { + }; + fdtmap { + }; + u-boot-dtb { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/140_entry_shrink.dts b/roms/u-boot/tools/binman/test/140_entry_shrink.dts new file mode 100644 index 000000000..b750d6389 --- /dev/null +++ b/roms/u-boot/tools/binman/test/140_entry_shrink.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + bad-shrink-contents; + }; + + u-boot { + }; + + _testing2 { + type = "_testing"; + bad-shrink-contents; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/141_descriptor_offset.dts b/roms/u-boot/tools/binman/test/141_descriptor_offset.dts new file mode 100644 index 000000000..f9bff016a --- /dev/null +++ b/roms/u-boot/tools/binman/test/141_descriptor_offset.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + u-boot { + offset = <0xffff0000>; + }; + intel-descriptor { + filename = "descriptor.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/142_replace_cbfs.dts b/roms/u-boot/tools/binman/test/142_replace_cbfs.dts new file mode 100644 index 000000000..d64142f9d --- /dev/null +++ b/roms/u-boot/tools/binman/test/142_replace_cbfs.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xe00>; + allow-repack; + u-boot { + }; + section { + align = <0x100>; + cbfs { + size = <0x400>; + u-boot { + cbfs-type = "raw"; + }; + u-boot-dtb { + cbfs-type = "raw"; + cbfs-compress = "lzma"; + cbfs-offset = <0x80>; + }; + }; + u-boot-dtb { + compress = "lz4"; + }; + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/143_replace_all.dts b/roms/u-boot/tools/binman/test/143_replace_all.dts new file mode 100644 index 000000000..c5744a3c1 --- /dev/null +++ b/roms/u-boot/tools/binman/test/143_replace_all.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + allow-repack; + u-boot { + }; + fdtmap { + }; + u-boot2 { + type = "u-boot"; + }; + text { + text = "some text"; + }; + u-boot-dtb { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/144_x86_reset16.dts b/roms/u-boot/tools/binman/test/144_x86_reset16.dts new file mode 100644 index 000000000..ba90333b2 --- /dev/null +++ b/roms/u-boot/tools/binman/test/144_x86_reset16.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-reset16 { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/145_x86_reset16_spl.dts b/roms/u-boot/tools/binman/test/145_x86_reset16_spl.dts new file mode 100644 index 000000000..cc8d97a7e --- /dev/null +++ b/roms/u-boot/tools/binman/test/145_x86_reset16_spl.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-reset16-spl { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/146_x86_reset16_tpl.dts b/roms/u-boot/tools/binman/test/146_x86_reset16_tpl.dts new file mode 100644 index 000000000..041b16f3d --- /dev/null +++ b/roms/u-boot/tools/binman/test/146_x86_reset16_tpl.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-reset16-tpl { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/147_intel_fit.dts b/roms/u-boot/tools/binman/test/147_intel_fit.dts new file mode 100644 index 000000000..01ec40e5c --- /dev/null +++ b/roms/u-boot/tools/binman/test/147_intel_fit.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + end-at-4gb; + size = <0x80>; + + u-boot { + }; + + intel-fit { + }; + + intel-fit-ptr { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/148_intel_fit_missing.dts b/roms/u-boot/tools/binman/test/148_intel_fit_missing.dts new file mode 100644 index 000000000..388c76b1a --- /dev/null +++ b/roms/u-boot/tools/binman/test/148_intel_fit_missing.dts @@ -0,0 +1,17 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + end-at-4gb; + size = <0x80>; + + u-boot { + }; + + intel-fit-ptr { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/149_symbols_tpl.dts b/roms/u-boot/tools/binman/test/149_symbols_tpl.dts new file mode 100644 index 000000000..0a4ab3f1f --- /dev/null +++ b/roms/u-boot/tools/binman/test/149_symbols_tpl.dts @@ -0,0 +1,27 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + offset = <4>; + }; + + u-boot-spl2 { + offset = <0x1c>; + type = "u-boot-spl"; + }; + + u-boot { + offset = <0x34>; + }; + + section { + u-boot-tpl { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/150_powerpc_mpc85xx_bootpg_resetvec.dts b/roms/u-boot/tools/binman/test/150_powerpc_mpc85xx_bootpg_resetvec.dts new file mode 100644 index 000000000..8f4b16c39 --- /dev/null +++ b/roms/u-boot/tools/binman/test/150_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/roms/u-boot/tools/binman/test/151_x86_rom_ifwi_section.dts b/roms/u-boot/tools/binman/test/151_x86_rom_ifwi_section.dts new file mode 100644 index 000000000..7e455c3a4 --- /dev/null +++ b/roms/u-boot/tools/binman/test/151_x86_rom_ifwi_section.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor.bin"; + }; + + intel-ifwi { + offset-unset; + filename = "fitimage.bin"; + convert-fit; + + section { + ifwi-replace; + ifwi-subpart = "IBBP"; + ifwi-entry = "IBBL"; + u-boot-tpl { + }; + u-boot-dtb { + }; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/152_intel_fsp_m.dts b/roms/u-boot/tools/binman/test/152_intel_fsp_m.dts new file mode 100644 index 000000000..b6010f31c --- /dev/null +++ b/roms/u-boot/tools/binman/test/152_intel_fsp_m.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-fsp-m { + filename = "fsp_m.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/153_intel_fsp_s.dts b/roms/u-boot/tools/binman/test/153_intel_fsp_s.dts new file mode 100644 index 000000000..579618a8f --- /dev/null +++ b/roms/u-boot/tools/binman/test/153_intel_fsp_s.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-fsp-s { + filename = "fsp_s.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/154_intel_fsp_t.dts b/roms/u-boot/tools/binman/test/154_intel_fsp_t.dts new file mode 100644 index 000000000..8da749c15 --- /dev/null +++ b/roms/u-boot/tools/binman/test/154_intel_fsp_t.dts @@ -0,0 +1,14 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-fsp-t { + filename = "fsp_t.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/155_symbols_tpl_x86.dts b/roms/u-boot/tools/binman/test/155_symbols_tpl_x86.dts new file mode 100644 index 000000000..9d7dc51b3 --- /dev/null +++ b/roms/u-boot/tools/binman/test/155_symbols_tpl_x86.dts @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + end-at-4gb; + size = <0x100>; + u-boot-spl { + offset = <0xffffff04>; + }; + + u-boot-spl2 { + offset = <0xffffff1c>; + type = "u-boot-spl"; + }; + + u-boot { + offset = <0xffffff34>; + }; + + section { + u-boot-tpl { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/156_mkimage.dts b/roms/u-boot/tools/binman/test/156_mkimage.dts new file mode 100644 index 000000000..933b13143 --- /dev/null +++ b/roms/u-boot/tools/binman/test/156_mkimage.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x80>; + + mkimage { + args = "-n test -T script"; + + u-boot-spl { + }; + + _testing { + return-contents-later; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/157_blob_ext.dts b/roms/u-boot/tools/binman/test/157_blob_ext.dts new file mode 100644 index 000000000..8afdd5339 --- /dev/null +++ b/roms/u-boot/tools/binman/test/157_blob_ext.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/158_blob_ext_missing.dts b/roms/u-boot/tools/binman/test/158_blob_ext_missing.dts new file mode 100644 index 000000000..d315e5592 --- /dev/null +++ b/roms/u-boot/tools/binman/test/158_blob_ext_missing.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x80>; + + blob-ext { + filename = "missing-file"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/159_blob_ext_missing_sect.dts b/roms/u-boot/tools/binman/test/159_blob_ext_missing_sect.dts new file mode 100644 index 000000000..5f14c5413 --- /dev/null +++ b/roms/u-boot/tools/binman/test/159_blob_ext_missing_sect.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x80>; + + section { + blob-ext { + filename = "missing-file"; + }; + }; + + blob-ext2 { + type = "blob-ext"; + filename = "missing-file2"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/160_pack_overlap_zero.dts b/roms/u-boot/tools/binman/test/160_pack_overlap_zero.dts new file mode 100644 index 000000000..731aa1cbe --- /dev/null +++ b/roms/u-boot/tools/binman/test/160_pack_overlap_zero.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + + fill { + size = <0>; + offset = <3>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/161_fit.dts b/roms/u-boot/tools/binman/test/161_fit.dts new file mode 100644 index 000000000..c52d760b7 --- /dev/null +++ b/roms/u-boot/tools/binman/test/161_fit.dts @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + fdt-1 { + description = "Flattened Device Tree blob"; + type = "flat_dt"; + arch = "ppc"; + compression = "none"; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot-spl-dtb { + }; + }; + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "Boot Linux kernel with FDT blob"; + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/162_fit_external.dts b/roms/u-boot/tools/binman/test/162_fit_external.dts new file mode 100644 index 000000000..19518e05a --- /dev/null +++ b/roms/u-boot/tools/binman/test/162_fit_external.dts @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + fit,external-offset = <0>; + description = "test-desc"; + #address-cells = <1>; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + fdt-1 { + description = "Flattened Device Tree blob"; + type = "flat_dt"; + arch = "ppc"; + compression = "none"; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + _testing { + return-contents-later; + }; + }; + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "Boot Linux kernel with FDT blob"; + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/163_x86_rom_me_empty.dts b/roms/u-boot/tools/binman/test/163_x86_rom_me_empty.dts new file mode 100644 index 000000000..9349d2d72 --- /dev/null +++ b/roms/u-boot/tools/binman/test/163_x86_rom_me_empty.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor-empty.bin"; + }; + + intel-me { + filename = "me.bin"; + offset-unset; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/164_x86_rom_me_missing.dts b/roms/u-boot/tools/binman/test/164_x86_rom_me_missing.dts new file mode 100644 index 000000000..dce3be5e0 --- /dev/null +++ b/roms/u-boot/tools/binman/test/164_x86_rom_me_missing.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-offset; + end-at-4gb; + size = <0x800000>; + intel-descriptor { + filename = "descriptor-missing.bin"; + }; + + intel-me { + filename = "me.bin"; + offset-unset; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/165_section_ignore_hash_signature.dts b/roms/u-boot/tools/binman/test/165_section_ignore_hash_signature.dts new file mode 100644 index 000000000..8adbe2551 --- /dev/null +++ b/roms/u-boot/tools/binman/test/165_section_ignore_hash_signature.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section@0 { + u-boot { + }; + hash { + algo = "sha256"; + }; + signature { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + }; + }; + section@1 { + u-boot { + }; + hash-1 { + algo = "sha1"; + }; + hash-2 { + algo = "sha256"; + }; + signature-1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + signature-2 { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/166_pad_in_sections.dts b/roms/u-boot/tools/binman/test/166_pad_in_sections.dts new file mode 100644 index 000000000..f2b327ff9 --- /dev/null +++ b/roms/u-boot/tools/binman/test/166_pad_in_sections.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + section { + pad-byte = <0x21>; + + before { + type = "u-boot"; + }; + u-boot { + pad-before = <12>; + pad-after = <6>; + }; + after { + type = "u-boot"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/167_fit_image_subentry_alignment.dts b/roms/u-boot/tools/binman/test/167_fit_image_subentry_alignment.dts new file mode 100644 index 000000000..360cac526 --- /dev/null +++ b/roms/u-boot/tools/binman/test/167_fit_image_subentry_alignment.dts @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + fit { + description = "test-desc"; + #address-cells = <1>; + + images { + kernel { + description = "Offset-Align Test"; + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <00000000>; + entry = <00000000>; + u-boot-spl { + offset = <0x20>; + }; + u-boot { + align = <0x10>; + }; + }; + fdt-1 { + description = "Pad-Before-After Test"; + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + u-boot-spl-dtb { + }; + text { + text-label = "test-id"; + pad-before = <20>; + pad-after = <30>; + }; + u-boot-dtb { + }; + }; + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "Kernel with FDT blob"; + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/168_fit_missing_blob.dts b/roms/u-boot/tools/binman/test/168_fit_missing_blob.dts new file mode 100644 index 000000000..15f6cc07e --- /dev/null +++ b/roms/u-boot/tools/binman/test/168_fit_missing_blob.dts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "ATF BL31"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + atf-bl31 { + filename = "missing"; + }; + cros-ec-rw { + type = "atf-bl31"; + missing-msg = "wibble"; + }; + another { + type = "atf-bl31"; + }; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/169_atf_bl31.dts b/roms/u-boot/tools/binman/test/169_atf_bl31.dts new file mode 100644 index 000000000..2b7547d70 --- /dev/null +++ b/roms/u-boot/tools/binman/test/169_atf_bl31.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + atf-bl31 { + filename = "bl31.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/170_fit_fdt.dts b/roms/u-boot/tools/binman/test/170_fit_fdt.dts new file mode 100644 index 000000000..99d710c57 --- /dev/null +++ b/roms/u-boot/tools/binman/test/170_fit_fdt.dts @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + fit,fdt-list = "of-list"; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + }; + }; + + configurations { + default = "@config-DEFAULT-SEQ"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/171_fit_fdt_missing_prop.dts b/roms/u-boot/tools/binman/test/171_fit_fdt_missing_prop.dts new file mode 100644 index 000000000..c36134715 --- /dev/null +++ b/roms/u-boot/tools/binman/test/171_fit_fdt_missing_prop.dts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + fit { + description = "test-desc"; + #address-cells = <1>; + + images { + kernel { + description = "Vanilla Linux kernel"; + type = "kernel"; + arch = "ppc"; + os = "linux"; + compression = "gzip"; + load = <00000000>; + entry = <00000000>; + hash-1 { + algo = "crc32"; + }; + hash-2 { + algo = "sha1"; + }; + u-boot { + }; + }; + @fdt-SEQ { + description = "fdt-NAME.dtb"; + type = "flat_dt"; + compression = "none"; + }; + }; + + configurations { + default = "config-1"; + @config-SEQ { + description = "conf-NAME.dtb"; + firmware = "uboot"; + loadables = "atf"; + fdt = "fdt-SEQ"; + }; + }; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/172_scp.dts b/roms/u-boot/tools/binman/test/172_scp.dts new file mode 100644 index 000000000..354e4ef17 --- /dev/null +++ b/roms/u-boot/tools/binman/test/172_scp.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + scp { + filename = "scp.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/173_missing_blob.dts b/roms/u-boot/tools/binman/test/173_missing_blob.dts new file mode 100644 index 000000000..ffb655a1c --- /dev/null +++ b/roms/u-boot/tools/binman/test/173_missing_blob.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + blob { + filename = "missing"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/174_env.dts b/roms/u-boot/tools/binman/test/174_env.dts new file mode 100644 index 000000000..d1393d2db --- /dev/null +++ b/roms/u-boot/tools/binman/test/174_env.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-env { + filename = "env.txt"; + size = <0x18>; + fill-byte = [ff]; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/175_env_no_size.dts b/roms/u-boot/tools/binman/test/175_env_no_size.dts new file mode 100644 index 000000000..267acd154 --- /dev/null +++ b/roms/u-boot/tools/binman/test/175_env_no_size.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-env { + filename = "env.txt"; + fill-byte = [ff]; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/176_env_too_small.dts b/roms/u-boot/tools/binman/test/176_env_too_small.dts new file mode 100644 index 000000000..2db8d0546 --- /dev/null +++ b/roms/u-boot/tools/binman/test/176_env_too_small.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-env { + filename = "env.txt"; + size = <0x8>; + fill-byte = [ff]; + }; + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/177_skip_at_start.dts b/roms/u-boot/tools/binman/test/177_skip_at_start.dts new file mode 100644 index 000000000..021460b1a --- /dev/null +++ b/roms/u-boot/tools/binman/test/177_skip_at_start.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + skip-at-start = <16>; + u-boot { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/178_skip_at_start_pad.dts b/roms/u-boot/tools/binman/test/178_skip_at_start_pad.dts new file mode 100644 index 000000000..deda3c862 --- /dev/null +++ b/roms/u-boot/tools/binman/test/178_skip_at_start_pad.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + skip-at-start = <16>; + u-boot { + pad-before = <8>; + pad-after = <4>; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/179_skip_at_start_section_pad.dts b/roms/u-boot/tools/binman/test/179_skip_at_start_section_pad.dts new file mode 100644 index 000000000..bf2f8f69b --- /dev/null +++ b/roms/u-boot/tools/binman/test/179_skip_at_start_section_pad.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + skip-at-start = <16>; + pad-before = <8>; + pad-after = <4>; + + u-boot { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/180_section_pad.dts b/roms/u-boot/tools/binman/test/180_section_pad.dts new file mode 100644 index 000000000..7e4ebf257 --- /dev/null +++ b/roms/u-boot/tools/binman/test/180_section_pad.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + section@0 { + read-only; + + /* Padding for the section uses the 0x26 pad byte */ + pad-before = <3>; + pad-after = <2>; + + /* Set the padding byte for entries, i.e. u-boot */ + pad-byte = <0x21>; + + u-boot { + pad-before = <5>; + pad-after = <1>; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/181_section_align.dts b/roms/u-boot/tools/binman/test/181_section_align.dts new file mode 100644 index 000000000..90795d131 --- /dev/null +++ b/roms/u-boot/tools/binman/test/181_section_align.dts @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0x26>; + fill { + size = <1>; + }; + section@1 { + read-only; + + /* Padding for the section uses the 0x26 pad byte */ + align = <2>; + align-size = <0x10>; + + /* Set the padding byte for entries, i.e. u-boot */ + pad-byte = <0x21>; + + fill { + size = <1>; + }; + + u-boot { + align = <4>; + align-size = <8>; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/182_compress_image.dts b/roms/u-boot/tools/binman/test/182_compress_image.dts new file mode 100644 index 000000000..4176b7f2e --- /dev/null +++ b/roms/u-boot/tools/binman/test/182_compress_image.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + compress = "lz4"; + blob { + filename = "compress"; + }; + + u-boot { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/183_compress_image_less.dts b/roms/u-boot/tools/binman/test/183_compress_image_less.dts new file mode 100644 index 000000000..1d9d57b78 --- /dev/null +++ b/roms/u-boot/tools/binman/test/183_compress_image_less.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + compress = "lz4"; + blob { + filename = "compress_big"; + }; + + u-boot { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/184_compress_section_size.dts b/roms/u-boot/tools/binman/test/184_compress_section_size.dts new file mode 100644 index 000000000..95ed30add --- /dev/null +++ b/roms/u-boot/tools/binman/test/184_compress_section_size.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + section { + size = <0x30>; + compress = "lz4"; + blob { + filename = "compress"; + }; + + u-boot { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/185_compress_section.dts b/roms/u-boot/tools/binman/test/185_compress_section.dts new file mode 100644 index 000000000..dc3e340c5 --- /dev/null +++ b/roms/u-boot/tools/binman/test/185_compress_section.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + section { + compress = "lz4"; + blob { + filename = "compress"; + }; + + u-boot { + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/186_compress_extra.dts b/roms/u-boot/tools/binman/test/186_compress_extra.dts new file mode 100644 index 000000000..59aae8226 --- /dev/null +++ b/roms/u-boot/tools/binman/test/186_compress_extra.dts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + }; + base { + type = "section"; + u-boot { + }; + section { + compress = "lz4"; + blob { + filename = "compress"; + }; + + u-boot { + }; + }; + section2 { + type = "section"; + compress = "lz4"; + blob { + filename = "compress"; + }; + blob2 { + type = "blob"; + filename = "compress"; + }; + }; + u-boot2 { + type = "u-boot"; + }; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/187_symbols_sub.dts b/roms/u-boot/tools/binman/test/187_symbols_sub.dts new file mode 100644 index 000000000..54511a737 --- /dev/null +++ b/roms/u-boot/tools/binman/test/187_symbols_sub.dts @@ -0,0 +1,22 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + offset = <24>; + }; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/188_image_entryarg.dts b/roms/u-boot/tools/binman/test/188_image_entryarg.dts new file mode 100644 index 000000000..29d814916 --- /dev/null +++ b/roms/u-boot/tools/binman/test/188_image_entryarg.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0xc00>; + u-boot { + }; + cros-ec-rw { + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/189_vblock_content.dts b/roms/u-boot/tools/binman/test/189_vblock_content.dts new file mode 100644 index 000000000..dcc74449c --- /dev/null +++ b/roms/u-boot/tools/binman/test/189_vblock_content.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + dtb: u-boot-dtb { + }; + + /* + * Put the vblock after the dtb so that the dtb is updated + * before the vblock reads its data. At present binman does not + * understand dependencies between entries, but simply + * iterates again when it thinks something needs to be + * recalculated. + */ + vblock { + content = <&u_boot &dtb>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/190_files_align.dts b/roms/u-boot/tools/binman/test/190_files_align.dts new file mode 100644 index 000000000..213ba966d --- /dev/null +++ b/roms/u-boot/tools/binman/test/190_files_align.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + files { + pattern = "files/*.dat"; + files-compress = "none"; + files-align = <4>; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/191_read_image_skip.dts b/roms/u-boot/tools/binman/test/191_read_image_skip.dts new file mode 100644 index 000000000..31df518fa --- /dev/null +++ b/roms/u-boot/tools/binman/test/191_read_image_skip.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + end-at-4gb; + size = <0x400>; + section { + size = <0x10>; + u-boot { + }; + }; + fdtmap { + }; + image-header { + location = "end"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/192_u_boot_tpl_nodtb.dts b/roms/u-boot/tools/binman/test/192_u_boot_tpl_nodtb.dts new file mode 100644 index 000000000..94cef395e --- /dev/null +++ b/roms/u-boot/tools/binman/test/192_u_boot_tpl_nodtb.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/193_tpl_bss_pad.dts b/roms/u-boot/tools/binman/test/193_tpl_bss_pad.dts new file mode 100644 index 000000000..f5c2db064 --- /dev/null +++ b/roms/u-boot/tools/binman/test/193_tpl_bss_pad.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl { + }; + + u-boot-tpl-bss-pad { + }; + + u-boot { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/194_fdt_incl.dts b/roms/u-boot/tools/binman/test/194_fdt_incl.dts new file mode 100644 index 000000000..b14c8ff8f --- /dev/null +++ b/roms/u-boot/tools/binman/test/194_fdt_incl.dts @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot { + }; + u-boot-spl { + }; + u-boot-tpl { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/195_fdt_incl_tpl.dts b/roms/u-boot/tools/binman/test/195_fdt_incl_tpl.dts new file mode 100644 index 000000000..3756ac4fc --- /dev/null +++ b/roms/u-boot/tools/binman/test/195_fdt_incl_tpl.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-tpl { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/196_symbols_nodtb.dts b/roms/u-boot/tools/binman/test/196_symbols_nodtb.dts new file mode 100644 index 000000000..5c900d607 --- /dev/null +++ b/roms/u-boot/tools/binman/test/196_symbols_nodtb.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl-nodtb { + }; + u-boot-spl-dtb { + }; + + u-boot { + offset = <0x38>; + }; + + u-boot-spl2 { + type = "u-boot-spl-nodtb"; + }; + u-boot-spl-dtb2 { + type = "u-boot-spl-dtb"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/197_symbols_expand.dts b/roms/u-boot/tools/binman/test/197_symbols_expand.dts new file mode 100644 index 000000000..8aee76dc7 --- /dev/null +++ b/roms/u-boot/tools/binman/test/197_symbols_expand.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + offset = <0x38>; + no-expanded; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/198_collection.dts b/roms/u-boot/tools/binman/test/198_collection.dts new file mode 100644 index 000000000..484a1b005 --- /dev/null +++ b/roms/u-boot/tools/binman/test/198_collection.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <&u_boot_nodtb &dtb>; + }; + fill { + size = <2>; + fill-byte = [ff]; + }; + u_boot_nodtb: u-boot-nodtb { + }; + fill2 { + type = "fill"; + size = <3>; + fill-byte = [fe]; + }; + dtb: u-boot-dtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/199_collection_section.dts b/roms/u-boot/tools/binman/test/199_collection_section.dts new file mode 100644 index 000000000..03a73194c --- /dev/null +++ b/roms/u-boot/tools/binman/test/199_collection_section.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <§ion &u_boot>; + }; + fill { + size = <2>; + fill-byte = [ff]; + }; + section: section { + u-boot-nodtb { + }; + u-boot-dtb { + }; + }; + fill2 { + type = "fill"; + size = <3>; + fill-byte = [fe]; + }; + u_boot: u-boot { + no-expanded; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/200_align_default.dts b/roms/u-boot/tools/binman/test/200_align_default.dts new file mode 100644 index 000000000..1b155770d --- /dev/null +++ b/roms/u-boot/tools/binman/test/200_align_default.dts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + align-default = <8>; + u-boot { + }; + + u-boot-align { + type = "u-boot"; + }; + + section { + align = <32>; + u-boot { + }; + + u-boot-nodtb { + }; + }; + + u-boot-nodtb { + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/201_opensbi.dts b/roms/u-boot/tools/binman/test/201_opensbi.dts new file mode 100644 index 000000000..942183f99 --- /dev/null +++ b/roms/u-boot/tools/binman/test/201_opensbi.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + opensbi { + filename = "fw_dynamic.bin"; + }; + }; +}; diff --git a/roms/u-boot/tools/binman/test/Makefile b/roms/u-boot/tools/binman/test/Makefile new file mode 100644 index 000000000..0b19b7d99 --- /dev/null +++ b/roms/u-boot/tools/binman/test/Makefile @@ -0,0 +1,74 @@ +# +# Builds test programs. This is launched from elf_test.BuildElfTestFiles() +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +HOSTARCH := $(shell uname -m | sed -e s/i.86/x86/ ) +ifeq ($(findstring $(HOSTARCH),"x86" "x86_64"),) +ifeq ($(findstring $(MAKECMDGOALS),"help" "clean"),) +ifndef CROSS_COMPILE +$(error Binman tests need to compile to x86, but the CPU arch of your \ + machine is $(HOSTARCH). Set CROSS_COMPILE to a suitable cross compiler) +endif +endif +endif + +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +VPATH := $(SRC) +CFLAGS := -march=i386 -m32 -nostdlib -I $(SRC)../../../include \ + -Wl,--no-dynamic-linker + +LDS_UCODE := -T $(SRC)u_boot_ucode_ptr.lds +LDS_BINMAN := -T $(SRC)u_boot_binman_syms.lds +LDS_BINMAN_BAD := -T $(SRC)u_boot_binman_syms_bad.lds +LDS_BINMAN_X86 := -T $(SRC)u_boot_binman_syms_x86.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 u_boot_binman_syms_x86 + +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 += $(SRC)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_x86: CFLAGS += $(LDS_BINMAN_X86) +u_boot_binman_syms_x86: u_boot_binman_syms_x86.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/roms/u-boot/tools/binman/test/bss_data.c b/roms/u-boot/tools/binman/test/bss_data.c new file mode 100644 index 000000000..79537c31b --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/bss_data.lds b/roms/u-boot/tools/binman/test/bss_data.lds new file mode 100644 index 000000000..306dab504 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/files/1.dat b/roms/u-boot/tools/binman/test/files/1.dat new file mode 100644 index 000000000..a95247061 --- /dev/null +++ b/roms/u-boot/tools/binman/test/files/1.dat @@ -0,0 +1 @@ +sorry I'm late diff --git a/roms/u-boot/tools/binman/test/files/2.dat b/roms/u-boot/tools/binman/test/files/2.dat new file mode 100644 index 000000000..687ea5273 --- /dev/null +++ b/roms/u-boot/tools/binman/test/files/2.dat @@ -0,0 +1 @@ +Oh, don't bother apologising, I'm sorry you're alive diff --git a/roms/u-boot/tools/binman/test/files/ignored_dir.dat/ignore b/roms/u-boot/tools/binman/test/files/ignored_dir.dat/ignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/u-boot/tools/binman/test/files/ignored_dir.dat/ignore diff --git a/roms/u-boot/tools/binman/test/files/not-this-one b/roms/u-boot/tools/binman/test/files/not-this-one new file mode 100644 index 000000000..e71c2250f --- /dev/null +++ b/roms/u-boot/tools/binman/test/files/not-this-one @@ -0,0 +1 @@ +this does not have a .dat extenion diff --git a/roms/u-boot/tools/binman/test/fitimage.bin.gz b/roms/u-boot/tools/binman/test/fitimage.bin.gz Binary files differnew file mode 100644 index 000000000..0a9dcfc42 --- /dev/null +++ b/roms/u-boot/tools/binman/test/fitimage.bin.gz diff --git a/roms/u-boot/tools/binman/test/ifwi.bin.gz b/roms/u-boot/tools/binman/test/ifwi.bin.gz Binary files differnew file mode 100644 index 000000000..25d728929 --- /dev/null +++ b/roms/u-boot/tools/binman/test/ifwi.bin.gz diff --git a/roms/u-boot/tools/binman/test/u_boot_binman_syms.c b/roms/u-boot/tools/binman/test/u_boot_binman_syms.c new file mode 100644 index 000000000..37fc339ce --- /dev/null +++ b/roms/u-boot/tools/binman/test/u_boot_binman_syms.c @@ -0,0 +1,14 @@ +// 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_any, offset); +binman_sym_declare(unsigned long long, u_boot_spl2, offset); +binman_sym_declare(unsigned long, u_boot_any, image_pos); +binman_sym_declare(unsigned long, u_boot_any, size); diff --git a/roms/u-boot/tools/binman/test/u_boot_binman_syms.lds b/roms/u-boot/tools/binman/test/u_boot_binman_syms.lds new file mode 100644 index 000000000..825fc3f64 --- /dev/null +++ b/roms/u-boot/tools/binman/test/u_boot_binman_syms.lds @@ -0,0 +1,30 @@ +/* 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 +{ + . = 0x00000010; + _start = .; + + . = ALIGN(4); + .text : + { + __image_copy_start = .; + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + .interp : { *(.interp*) } + +} diff --git a/roms/u-boot/tools/binman/test/u_boot_binman_syms_bad.c b/roms/u-boot/tools/binman/test/u_boot_binman_syms_bad.c new file mode 120000 index 000000000..939b2e965 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/u_boot_binman_syms_bad.lds b/roms/u-boot/tools/binman/test/u_boot_binman_syms_bad.lds new file mode 100644 index 000000000..849d158ac --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/u_boot_binman_syms_size.c b/roms/u-boot/tools/binman/test/u_boot_binman_syms_size.c new file mode 100644 index 000000000..7224bc186 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.c b/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.c new file mode 120000 index 000000000..939b2e965 --- /dev/null +++ b/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.c @@ -0,0 +1 @@ +u_boot_binman_syms.c
\ No newline at end of file diff --git a/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.lds b/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.lds new file mode 100644 index 000000000..9daf86f83 --- /dev/null +++ b/roms/u-boot/tools/binman/test/u_boot_binman_syms_x86.lds @@ -0,0 +1,30 @@ +/* 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 +{ + . = 0xffffff00; + _start = .; + + . = ALIGN(4); + .text : + { + __image_copy_start = .; + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + .interp : { *(.interp*) } + +} diff --git a/roms/u-boot/tools/binman/test/u_boot_no_ucode_ptr.c b/roms/u-boot/tools/binman/test/u_boot_no_ucode_ptr.c new file mode 100644 index 000000000..24cdb909d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/u_boot_ucode_ptr.c b/roms/u-boot/tools/binman/test/u_boot_ucode_ptr.c new file mode 100644 index 000000000..243c8e9e1 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/tools/binman/test/u_boot_ucode_ptr.lds b/roms/u-boot/tools/binman/test/u_boot_ucode_ptr.lds new file mode 100644 index 000000000..cf4d1b8bb --- /dev/null +++ b/roms/u-boot/tools/binman/test/u_boot_ucode_ptr.lds @@ -0,0 +1,18 @@ +/* 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 +{ + . = 0xfffffe14; + _start = .; + .ucode : { + *(.ucode) + } + .interp : { *(.interp*) } +} |