commit
36737f22b7
@ -0,0 +1,14 @@ |
||||
#include <config.h> |
||||
|
||||
/ { |
||||
binman { |
||||
filename = "u-boot-sunxi-with-spl.bin"; |
||||
pad-byte = <0xff>; |
||||
blob { |
||||
filename = "spl/sunxi-spl.bin"; |
||||
}; |
||||
u-boot-img { |
||||
pos = <CONFIG_SPL_PAD_TO>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,15 @@ |
||||
/* |
||||
* Copyright (C) 2016 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/ { |
||||
host1x@50000000 { |
||||
u-boot,dm-pre-reloc; |
||||
dc@54200000 { |
||||
u-boot,dm-pre-reloc; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,8 @@ |
||||
/ { |
||||
host1x@50000000 { |
||||
u-boot,dm-pre-reloc; |
||||
dc@54200000 { |
||||
u-boot,dm-pre-reloc; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,18 @@ |
||||
/* |
||||
* Copyright (C) 2016 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <u-boot.dtsi> |
||||
|
||||
#ifdef CONFIG_ROM_SIZE |
||||
/ { |
||||
binman { |
||||
u-boot-with-ucode-ptr { |
||||
optional-ucode; |
||||
}; |
||||
}; |
||||
}; |
||||
#endif |
@ -0,0 +1,62 @@ |
||||
/* |
||||
* Copyright (C) 2016 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
|
||||
#ifdef CONFIG_ROM_SIZE |
||||
/ { |
||||
binman { |
||||
filename = "u-boot.rom"; |
||||
end-at-4gb; |
||||
sort-by-pos; |
||||
pad-byte = <0xff>; |
||||
size = <CONFIG_ROM_SIZE>; |
||||
#ifdef CONFIG_HAVE_INTEL_ME |
||||
intel-descriptor { |
||||
}; |
||||
intel-me { |
||||
}; |
||||
#endif |
||||
u-boot-with-ucode-ptr { |
||||
pos = <CONFIG_SYS_TEXT_BASE>; |
||||
}; |
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
u-boot-ucode { |
||||
align = <16>; |
||||
}; |
||||
#ifdef CONFIG_HAVE_MRC |
||||
intel-mrc { |
||||
pos = <CONFIG_X86_MRC_ADDR>; |
||||
}; |
||||
#endif |
||||
#ifdef CONFIG_HAVE_FSP |
||||
intel-fsp { |
||||
pos = <CONFIG_FSP_ADDR>; |
||||
}; |
||||
#endif |
||||
#ifdef CONFIG_HAVE_CMC |
||||
intel-cmc { |
||||
pos = <CONFIG_CMC_ADDR>; |
||||
}; |
||||
#endif |
||||
#ifdef CONFIG_HAVE_VGA_BIOS |
||||
intel-vga { |
||||
pos = <CONFIG_VGA_BIOS_ADDR>; |
||||
}; |
||||
#endif |
||||
#ifdef CONFIG_HAVE_REFCODE |
||||
intel-refcode { |
||||
pos = <CONFIG_X86_REFCODE_ADDR>; |
||||
}; |
||||
#endif |
||||
x86-start16 { |
||||
pos = <CONFIG_SYS_X86_START16>; |
||||
}; |
||||
}; |
||||
}; |
||||
#endif |
@ -0,0 +1 @@ |
||||
*.pyc |
@ -0,0 +1,541 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
|
||||
Introduction |
||||
------------ |
||||
|
||||
Firmware often consists of several components which must be packaged together. |
||||
For example, we may have SPL, U-Boot, a device tree and an environment area |
||||
grouped together and placed in MMC flash. When the system starts, it must be |
||||
able to find these pieces. |
||||
|
||||
So far U-Boot has not provided a way to handle creating such images in a |
||||
general way. Each SoC does what it needs to build an image, often packing or |
||||
concatenating images in the U-Boot build system. |
||||
|
||||
Binman aims to provide a mechanism for building images, from simple |
||||
SPL + U-Boot combinations, to more complex arrangements with many parts. |
||||
|
||||
|
||||
What it does |
||||
------------ |
||||
|
||||
Binman reads your board's device tree and finds a node which describes the |
||||
required image layout. It uses this to work out what to place where. The |
||||
output file normally contains the device tree, so it is in principle possible |
||||
to read an image and extract its constituent parts. |
||||
|
||||
|
||||
Features |
||||
-------- |
||||
|
||||
So far binman is pretty simple. It supports binary blobs, such as 'u-boot', |
||||
'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can |
||||
place entries at a fixed location in the image, or fit them together with |
||||
suitable padding and alignment. It provides a way to process binaries before |
||||
they are included, by adding a Python plug-in. The device tree is available |
||||
to U-Boot at run-time so that the images can be interpreted. |
||||
|
||||
Binman does not yet update the device tree with the final location of |
||||
everything when it is done. A simple C structure could be generated for |
||||
constrained environments like SPL (using dtoc) but this is also not |
||||
implemented. |
||||
|
||||
Binman can also support incorporating filesystems in the image if required. |
||||
For example x86 platforms may use CBFS in some cases. |
||||
|
||||
Binman is intended for use with U-Boot but is designed to be general enough |
||||
to be useful in other image-packaging situations. |
||||
|
||||
|
||||
Motivation |
||||
---------- |
||||
|
||||
Packaging of firmware is quite a different task from building the various |
||||
parts. In many cases the various binaries which go into the image come from |
||||
separate build systems. For example, ARM Trusted Firmware is used on ARMv8 |
||||
devices but is not built in the U-Boot tree. If a Linux kernel is included |
||||
in the firmware image, it is built elsewhere. |
||||
|
||||
It is of course possible to add more and more build rules to the U-Boot |
||||
build system to cover these cases. It can shell out to other Makefiles and |
||||
build scripts. But it seems better to create a clear divide between building |
||||
software and packaging it. |
||||
|
||||
At present this is handled by manual instructions, different for each board, |
||||
on how to create images that will boot. By turning these instructions into a |
||||
standard format, we can support making valid images for any board without |
||||
manual effort, lots of READMEs, etc. |
||||
|
||||
Benefits: |
||||
- Each binary can have its own build system and tool chain without creating |
||||
any dependencies between them |
||||
- Avoids the need for a single-shot build: individual parts can be updated |
||||
and brought in as needed |
||||
- Provides for a standard image description available in the build and at |
||||
run-time |
||||
- SoC-specific image-signing tools can be accomodated |
||||
- Avoids cluttering the U-Boot build system with image-building code |
||||
- The image description is automatically available at run-time in U-Boot, |
||||
SPL. It can be made available to other software also |
||||
- The image description is easily readable (it's a text file in device-tree |
||||
format) and permits flexible packing of binaries |
||||
|
||||
|
||||
Terminology |
||||
----------- |
||||
|
||||
Binman uses the following terms: |
||||
|
||||
- image - an output file containing a firmware image |
||||
- binary - an input binary that goes into the image |
||||
|
||||
|
||||
Relationship to FIT |
||||
------------------- |
||||
|
||||
FIT is U-Boot's official image format. It supports multiple binaries with |
||||
load / execution addresses, compression. It also supports verification |
||||
through hashing and RSA signatures. |
||||
|
||||
FIT was originally designed to support booting a Linux kernel (with an |
||||
optional ramdisk) and device tree chosen from various options in the FIT. |
||||
Now that U-Boot supports configuration via device tree, it is possible to |
||||
load U-Boot from a FIT, with the device tree chosen by SPL. |
||||
|
||||
Binman considers FIT to be one of the binaries it can place in the image. |
||||
|
||||
Where possible it is best to put as much as possible in the FIT, with binman |
||||
used to deal with cases not covered by FIT. Examples include initial |
||||
execution (since FIT itself does not have an executable header) and dealing |
||||
with device boundaries, such as the read-only/read-write separation in SPI |
||||
flash. |
||||
|
||||
For U-Boot, binman should not be used to create ad-hoc images in place of |
||||
FIT. |
||||
|
||||
|
||||
Relationship to mkimage |
||||
----------------------- |
||||
|
||||
The mkimage tool provides a means to create a FIT. Traditionally it has |
||||
needed an image description file: a device tree, like binman, but in a |
||||
different format. More recently it has started to support a '-f auto' mode |
||||
which can generate that automatically. |
||||
|
||||
More relevant to binman, mkimage also permits creation of many SoC-specific |
||||
image types. These can be listed by running 'mkimage -T list'. Examples |
||||
include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often |
||||
called from the U-Boot build system for this reason. |
||||
|
||||
Binman considers the output files created by mkimage to be binary blobs |
||||
which it can place in an image. Binman does not replace the mkimage tool or |
||||
this purpose. It would be possible in some situtions to create a new entry |
||||
type for the images in mkimage, but this would not add functionality. It |
||||
seems better to use the mkiamge tool to generate binaries and avoid blurring |
||||
the boundaries between building input files (mkimage) and packaging then |
||||
into a final image (binman). |
||||
|
||||
|
||||
Example use of binman in U-Boot |
||||
------------------------------- |
||||
|
||||
Binman aims to replace some of the ad-hoc image creation in the U-Boot |
||||
build system. |
||||
|
||||
Consider sunxi. It has the following steps: |
||||
|
||||
1. It uses a custom mksunxiboot tool to build an SPL image called |
||||
sunxi-spl.bin. This should probably move into mkimage. |
||||
|
||||
2. It uses mkimage to package U-Boot into a legacy image file (so that it can |
||||
hold the load and execution address) called u-boot.img. |
||||
|
||||
3. It builds a final output image called u-boot-sunxi-with-spl.bin which |
||||
consists of sunxi-spl.bin, some padding and u-boot.img. |
||||
|
||||
Binman is intended to replace the last step. The U-Boot build system builds |
||||
u-boot.bin and sunxi-spl.bin. Binman can then take over creation of |
||||
sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any |
||||
case, it would then create the image from the component parts. |
||||
|
||||
This simplifies the U-Boot Makefile somewhat, since various pieces of logic |
||||
can be replaced by a call to binman. |
||||
|
||||
|
||||
Example use of binman for x86 |
||||
----------------------------- |
||||
|
||||
In most cases x86 images have a lot of binary blobs, 'black-box' code |
||||
provided by Intel which must be run for the platform to work. Typically |
||||
these blobs are not relocatable and must be placed at fixed areas in the |
||||
firmare image. |
||||
|
||||
Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA |
||||
BIOS, reference code and Intel ME binaries into a u-boot.rom file. |
||||
|
||||
Binman is intended to replace all of this, with ifdtool left to handle only |
||||
the configuration of the Intel-format descriptor. |
||||
|
||||
|
||||
Running binman |
||||
-------------- |
||||
|
||||
Type: |
||||
|
||||
binman -b <board_name> |
||||
|
||||
to build an image for a board. The board name is the same name used when |
||||
configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). |
||||
Binman assumes that the input files for the build are in ../b/<board_name>. |
||||
|
||||
Or you can specify this explicitly: |
||||
|
||||
binman -I <build_path> |
||||
|
||||
where <build_path> is the build directory containing the output of the U-Boot |
||||
build. |
||||
|
||||
(Future work will make this more configurable) |
||||
|
||||
In either case, binman picks up the device tree file (u-boot.dtb) and looks |
||||
for its instructions in the 'binman' node. |
||||
|
||||
Binman has a few other options which you can see by running 'binman -h'. |
||||
|
||||
|
||||
Image description format |
||||
------------------------ |
||||
|
||||
The binman node is called 'binman'. An example image description is shown |
||||
below: |
||||
|
||||
binman { |
||||
filename = "u-boot-sunxi-with-spl.bin"; |
||||
pad-byte = <0xff>; |
||||
blob { |
||||
filename = "spl/sunxi-spl.bin"; |
||||
}; |
||||
u-boot { |
||||
pos = <CONFIG_SPL_PAD_TO>; |
||||
}; |
||||
}; |
||||
|
||||
|
||||
This requests binman to create an image file called u-boot-sunxi-with-spl.bin |
||||
consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the |
||||
normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The |
||||
padding comes from the fact that the second binary is placed at |
||||
CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would |
||||
immediately follow the SPL binary. |
||||
|
||||
The binman node describes an image. The sub-nodes describe entries in the |
||||
image. Each entry represents a region within the overall image. The name of |
||||
the entry (blob, u-boot) tells binman what to put there. For 'blob' we must |
||||
provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'. |
||||
|
||||
Entries are normally placed into the image sequentially, one after the other. |
||||
The image size is the total size of all entries. As you can see, you can |
||||
specify the start position of an entry using the 'pos' property. |
||||
|
||||
Note that due to a device tree requirement, all entries must have a unique |
||||
name. If you want to put the same binary in the image multiple times, you can |
||||
use any unique name, with the 'type' property providing the type. |
||||
|
||||
The attributes supported for entries are described below. |
||||
|
||||
pos: |
||||
This sets the position of an entry within the image. The first byte |
||||
of the image is normally at position 0. If 'pos' is not provided, |
||||
binman sets it to the end of the previous region, or the start of |
||||
the image's entry area (normally 0) if there is no previous region. |
||||
|
||||
align: |
||||
This sets the alignment of the entry. The entry position is adjusted |
||||
so that the entry starts on an aligned boundary within the image. For |
||||
example 'align = <16>' means that the entry will start on a 16-byte |
||||
boundary. Alignment shold be a power of 2. If 'align' is not |
||||
provided, no alignment is performed. |
||||
|
||||
size: |
||||
This sets the size of the entry. The contents will be padded out to |
||||
this size. If this is not provided, it will be set to the size of the |
||||
contents. |
||||
|
||||
pad-before: |
||||
Padding before the contents of the entry. Normally this is 0, meaning |
||||
that the contents start at the beginning of the entry. This can be |
||||
offset the entry contents a little. Defaults to 0. |
||||
|
||||
pad-after: |
||||
Padding after the contents of the entry. Normally this is 0, meaning |
||||
that the entry ends at the last byte of content (unless adjusted by |
||||
other properties). This allows room to be created in the image for |
||||
this entry to expand later. Defaults to 0. |
||||
|
||||
align-size: |
||||
This sets the alignment of the entry size. For example, to ensure |
||||
that the size of an entry is a multiple of 64 bytes, set this to 64. |
||||
If 'align-size' is not provided, no alignment is performed. |
||||
|
||||
align-end: |
||||
This sets the alignment of the end of an entry. Some entries require |
||||
that they end on an alignment boundary, regardless of where they |
||||
start. If 'align-end' is not provided, no alignment is performed. |
||||
|
||||
Note: This is not yet implemented in binman. |
||||
|
||||
filename: |
||||
For 'blob' types this provides the filename containing the binary to |
||||
put into the entry. If binman knows about the entry type (like |
||||
u-boot-bin), then there is no need to specify this. |
||||
|
||||
type: |
||||
Sets the type of an entry. This defaults to the entry name, but it is |
||||
possible to use any name, and then add (for example) 'type = "u-boot"' |
||||
to specify the type. |
||||
|
||||
|
||||
The attributes supported for images are described below. Several are similar |
||||
to those for entries. |
||||
|
||||
size: |
||||
Sets the image size in bytes, for example 'size = <0x100000>' for a |
||||
1MB image. |
||||
|
||||
align-size: |
||||
This sets the alignment of the image size. For example, to ensure |
||||
that the image ends on a 512-byte boundary, use 'align-size = <512>'. |
||||
If 'align-size' is not provided, no alignment is performed. |
||||
|
||||
pad-before: |
||||
This sets the padding before the image entries. The first entry will |
||||
be positionad after the padding. This defaults to 0. |
||||
|
||||
pad-after: |
||||
This sets the padding after the image entries. The padding will be |
||||
placed after the last entry. This defaults to 0. |
||||
|
||||
pad-byte: |
||||
This specifies the pad byte to use when padding in the image. It |
||||
defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. |
||||
|
||||
filename: |
||||
This specifies the image filename. It defaults to 'image.bin'. |
||||
|
||||
sort-by-pos: |
||||
This causes binman to reorder the entries as needed to make sure they |
||||
are in increasing positional order. This can be used when your entry |
||||
order may not match the positional order. A common situation is where |
||||
the 'pos' properties are set by CONFIG options, so their ordering is |
||||
not known a priori. |
||||
|
||||
This is a boolean property so needs no value. To enable it, add a |
||||
line 'sort-by-pos;' to your description. |
||||
|
||||
multiple-images: |
||||
Normally only a single image is generated. To create more than one |
||||
image, put this property in the binman node. For example, this will |
||||
create image1.bin containing u-boot.bin, and image2.bin containing |
||||
both spl/u-boot-spl.bin and u-boot.bin: |
||||
|
||||
binman { |
||||
multiple-images; |
||||
image1 { |
||||
u-boot { |
||||
}; |
||||
}; |
||||
|
||||
image2 { |
||||
spl { |
||||
}; |
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
end-at-4gb: |
||||
For x86 machines the ROM positions start just before 4GB and extend |
||||
up so that the image finished at the 4GB boundary. This boolean |
||||
option can be enabled to support this. The image size must be |
||||
provided so that binman knows when the image should start. For an |
||||
8MB ROM, the position of the first entry would be 0xfff80000 with |
||||
this option, instead of 0 without this option. |
||||
|
||||
|
||||
Examples of the above options can be found in the tests. See the |
||||
tools/binman/test directory. |
||||
|
||||
|
||||
Special properties |
||||
------------------ |
||||
|
||||
Some entries support special properties, documented here: |
||||
|
||||
u-boot-with-ucode-ptr: |
||||
optional-ucode: boolean property to make microcode optional. If the |
||||
u-boot.bin image does not include microcode, no error will |
||||
be generated. |
||||
|
||||
|
||||
Order of image creation |
||||
----------------------- |
||||
|
||||
Image creation proceeds in the following order, for each entry in the image. |
||||
|
||||
1. GetEntryContents() - the contents of each entry are obtained, normally by |
||||
reading from a file. This calls the Entry.ObtainContents() to read the |
||||
contents. The default version of Entry.ObtainContents() calls |
||||
Entry.GetDefaultFilename() and then reads that file. So a common mechanism |
||||
to select a file to read is to override that function in the subclass. The |
||||
functions must return True when they have read the contents. Binman will |
||||
retry calling the functions a few times if False is returned, allowing |
||||
dependencies between the contents of different entries. |
||||
|
||||
2. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can |
||||
return a dict containing entries that need updating. The key should be the |
||||
entry name and the value is a tuple (pos, size). This allows an entry to |
||||
provide the position and size for other entries. The default implementation |
||||
of GetEntryPositions() returns {}. |
||||
|
||||
3. PackEntries() - calls Entry.Pack() which figures out the position and |
||||
size of an entry. The 'current' image position is passed in, and the function |
||||
returns the position immediately after the entry being packed. The default |
||||
implementation of Pack() is usually sufficient. |
||||
|
||||
4. CheckSize() - checks that the contents of all the entries fits within |
||||
the image size. If the image does not have a defined size, the size is set |
||||
large enough to hold all the entries. |
||||
|
||||
5. CheckEntries() - checks that the entries do not overlap, nor extend |
||||
outside the image. |
||||
|
||||
6. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. |
||||
The default implementatoin does nothing. This can be overriden to adjust the |
||||
contents of an entry in some way. For example, it would be possible to create |
||||
an entry containing a hash of the contents of some other entries. At this |
||||
stage the position and size of entries should not be adjusted. |
||||
|
||||
7. BuildImage() - builds the image and writes it to a file. This is the final |
||||
step. |
||||
|
||||
|
||||
Automatic .dtsi inclusion |
||||
------------------------- |
||||
|
||||
It is sometimes inconvenient to add a 'binman' node to the .dts file for each |
||||
board. This can be done by using #include to bring in a common file. Another |
||||
approach supported by the U-Boot build system is to automatically include |
||||
a common header. You can then put the binman node (and anything else that is |
||||
specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header |
||||
file. |
||||
|
||||
Binman will search for the following files in arch/<arch>/dts: |
||||
|
||||
<dts>-u-boot.dtsi where <dts> is the base name of the .dts file |
||||
<CONFIG_SYS_SOC>-u-boot.dtsi |
||||
<CONFIG_SYS_CPU>-u-boot.dtsi |
||||
<CONFIG_SYS_VENDOR>-u-boot.dtsi |
||||
u-boot.dtsi |
||||
|
||||
U-Boot will only use the first one that it finds. If you need to include a |
||||
more general file you can do that from the more specific file using #include. |
||||
If you are having trouble figuring out what is going on, you can uncomment |
||||
the 'warning' line in scripts/Makefile.lib to see what it has found: |
||||
|
||||
# Uncomment for debugging |
||||
# $(warning binman_dtsi_options: $(binman_dtsi_options)) |
||||
|
||||
|
||||
Code coverage |
||||
------------- |
||||
|
||||
Binman is a critical tool and is designed to be very testable. Entry |
||||
implementations target 100% test coverage. Run 'binman -T' to check this. |
||||
|
||||
To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): |
||||
|
||||
$ sudo apt-get install python-pip python-pytest |
||||
$ sudo pip install coverage |
||||
|
||||
|
||||
Advanced Features / Technical docs |
||||
---------------------------------- |
||||
|
||||
The behaviour of entries is defined by the Entry class. All other entries are |
||||
a subclass of this. An important subclass is Entry_blob which takes binary |
||||
data from a file and places it in the entry. In fact most entry types are |
||||
subclasses of Entry_blob. |
||||
|
||||
Each entry type is a separate file in the tools/binman/etype directory. Each |
||||
file contains a class called Entry_<type> where <type> is the entry type. |
||||
New entry types can be supported by adding new files in that directory. |
||||
These will automatically be detected by binman when needed. |
||||
|
||||
Entry properties are documented in entry.py. The entry subclasses are free |
||||
to change the values of properties to support special behaviour. For example, |
||||
when Entry_blob loads a file, it sets content_size to the size of the file. |
||||
Entry classes can adjust other entries. For example, an entry that knows |
||||
where other entries should be positioned can set up those entries' positions |
||||
so they don't need to be set in the binman decription. It can also adjust |
||||
entry contents. |
||||
|
||||
Most of the time such essoteric behaviour is not needed, but it can be |
||||
essential for complex images. |
||||
|
||||
|
||||
History / Credits |
||||
----------------- |
||||
|
||||
Binman takes a lot of inspiration from a Chrome OS tool called |
||||
'cros_bundle_firmware', which I wrote some years ago. That tool was based on |
||||
a reasonably simple and sound design but has expanded greatly over the |
||||
years. In particular its handling of x86 images is convoluted. |
||||
|
||||
Quite a few lessons have been learned which are hopefully be applied here. |
||||
|
||||
|
||||
Design notes |
||||
------------ |
||||
|
||||
On the face of it, a tool to create firmware images should be fairly simple: |
||||
just find all the input binaries and place them at the right place in the |
||||
image. The difficulty comes from the wide variety of input types (simple |
||||
flat binaries containing code, packaged data with various headers), packing |
||||
requirments (alignment, spacing, device boundaries) and other required |
||||
features such as hierarchical images. |
||||
|
||||
The design challenge is to make it easy to create simple images, while |
||||
allowing the more complex cases to be supported. For example, for most |
||||
images we don't much care exactly where each binary ends up, so we should |
||||
not have to specify that unnecessarily. |
||||
|
||||
New entry types should aim to provide simple usage where possible. If new |
||||
core features are needed, they can be added in the Entry base class. |
||||
|
||||
|
||||
To do |
||||
----- |
||||
|
||||
Some ideas: |
||||
- Fill out the device tree to include the final position and size of each |
||||
entry (since the input file may not always specify these) |
||||
- Use of-platdata to make the information available to code that is unable |
||||
to use device tree (such as a very small SPL image) |
||||
- Write an image map to a text file |
||||
- Allow easy building of images by specifying just the board name |
||||
- Produce a full Python binding for libfdt (for upstream) |
||||
- Add an option to decode an image into the constituent binaries |
||||
- Suppoort hierarchical images (packing of binaries into another binary |
||||
which is then placed in the image) |
||||
- Support building an image for a board (-b) more completely, with a |
||||
configurable build directory |
||||
- Consider making binman work with buildman, although if it is used in the |
||||
Makefile, this will be automatic |
||||
- Implement align-end |
||||
|
||||
-- |
||||
Simon Glass <sjg@chromium.org> |
||||
7/7/2016 |
@ -0,0 +1 @@ |
||||
binman.py |
@ -0,0 +1,114 @@ |
||||
#!/usr/bin/python |
||||
|
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Creates binary images from input files controlled by a description |
||||
# |
||||
|
||||
"""See README for more information""" |
||||
|
||||
import os |
||||
import sys |
||||
import traceback |
||||
import unittest |
||||
|
||||
# Bring in the patman and dtoc libraries |
||||
our_path = os.path.dirname(os.path.realpath(__file__)) |
||||
sys.path.append(os.path.join(our_path, '../patman')) |
||||
sys.path.append(os.path.join(our_path, '../dtoc')) |
||||
|
||||
# Also allow entry-type modules to be brought in from the etype directory. |
||||
sys.path.append(os.path.join(our_path, 'etype')) |
||||
|
||||
import cmdline |
||||
import command |
||||
import control |
||||
|
||||
def RunTests(): |
||||
"""Run the functional tests and any embedded doctests""" |
||||
import entry_test |
||||
import fdt_test |
||||
import func_test |
||||
import test |
||||
import doctest |
||||
|
||||
result = unittest.TestResult() |
||||
for module in []: |
||||
suite = doctest.DocTestSuite(module) |
||||
suite.run(result) |
||||
|
||||
sys.argv = [sys.argv[0]] |
||||
for module in (func_test.TestFunctional, fdt_test.TestFdt, |
||||
entry_test.TestEntry): |
||||
suite = unittest.TestLoader().loadTestsFromTestCase(module) |
||||
suite.run(result) |
||||
|
||||
print result |
||||
for test, err in result.errors: |
||||
print test.id(), err |
||||
for test, err in result.failures: |
||||
print err |
||||
|
||||
def RunTestCoverage(): |
||||
"""Run the tests and check that we get 100% coverage""" |
||||
# This uses the build output from sandbox_spl to get _libfdt.so |
||||
cmd = ('PYTHONPATH=%s/sandbox_spl/tools coverage run ' |
||||
'--include "tools/binman/*.py" --omit "*test*,*binman.py" ' |
||||
'tools/binman/binman.py -t' % options.build_dir) |
||||
os.system(cmd) |
||||
stdout = command.Output('coverage', 'report') |
||||
coverage = stdout.splitlines()[-1].split(' ')[-1] |
||||
if coverage != '100%': |
||||
print stdout |
||||
print "Type 'coverage html' to get a report in htmlcov/index.html" |
||||
raise ValueError('Coverage error: %s, but should be 100%%' % coverage) |
||||
|
||||
|
||||
def RunBinman(options, args): |
||||
"""Main entry point to binman once arguments are parsed |
||||
|
||||
Args: |
||||
options: Command-line options |
||||
args: Non-option arguments |
||||
""" |
||||
ret_code = 0 |
||||
|
||||
# For testing: This enables full exception traces. |
||||
#options.debug = True |
||||
|
||||
if not options.debug: |
||||
sys.tracebacklimit = 0 |
||||
|
||||
if options.test: |
||||
RunTests() |
||||
|
||||
elif options.test_coverage: |
||||
RunTestCoverage() |
||||
|
||||
elif options.full_help: |
||||
pager = os.getenv('PAGER') |
||||
if not pager: |
||||
pager = 'more' |
||||
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), |
||||
'README') |
||||
command.Run(pager, fname) |
||||
|
||||
else: |
||||
try: |
||||
ret_code = control.Binman(options, args) |
||||
except Exception as e: |
||||
print 'binman: %s' % e |
||||
if options.debug: |
||||
print |
||||
traceback.print_exc() |
||||
ret_code = 1 |
||||
return ret_code |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
(options, args) = cmdline.ParseArgs(sys.argv) |
||||
ret_code = RunBinman(options, args) |
||||
sys.exit(ret_code) |
@ -0,0 +1,53 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Command-line parser for binman |
||||
# |
||||
|
||||
from optparse import OptionParser |
||||
|
||||
def ParseArgs(argv): |
||||
"""Parse the binman command-line arguments |
||||
|
||||
Args: |
||||
argv: List of string arguments |
||||
Returns: |
||||
Tuple (options, args) with the command-line options and arugments. |
||||
options provides access to the options (e.g. option.debug) |
||||
args is a list of string arguments |
||||
""" |
||||
parser = OptionParser() |
||||
parser.add_option('-b', '--board', type='string', |
||||
help='Board name to build') |
||||
parser.add_option('-B', '--build-dir', type='string', default='b', |
||||
help='Directory containing the build output') |
||||
parser.add_option('-d', '--dt', type='string', |
||||
help='Configuration file (.dtb) to use') |
||||
parser.add_option('-D', '--debug', action='store_true', |
||||
help='Enabling debugging (provides a full traceback on error)') |
||||
parser.add_option('-I', '--indir', action='append', |
||||
help='Add a path to a directory to use for input files') |
||||
parser.add_option('-H', '--full-help', action='store_true', |
||||
default=False, help='Display the README file') |
||||
parser.add_option('-O', '--outdir', type='string', |
||||
action='store', help='Path to directory to use for intermediate and ' |
||||
'output files') |
||||
parser.add_option('-p', '--preserve', action='store_true',\ |
||||
help='Preserve temporary output directory even if option -O is not ' |
||||
'given') |
||||
parser.add_option('-t', '--test', action='store_true', |
||||
default=False, help='run tests') |
||||
parser.add_option('-T', '--test-coverage', action='store_true', |
||||
default=False, help='run tests and check for 100% coverage') |
||||
parser.add_option('-v', '--verbosity', default=1, |
||||
type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, ' |
||||
'4=debug') |
||||
|
||||
parser.usage += """ |
||||
|
||||
Create images for a board from a set of binaries. It is controlled by a |
||||
description in the board device tree.""" |
||||
|
||||
return parser.parse_args(argv) |
@ -0,0 +1,118 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Creates binary images from input files controlled by a description |
||||
# |
||||
|
||||
from collections import OrderedDict |
||||
import os |
||||
import sys |
||||
import tools |
||||
|
||||
import command |
||||
import fdt_select |
||||
import fdt_util |
||||
from image import Image |
||||
import tout |
||||
|
||||
# List of images we plan to create |
||||
# Make this global so that it can be referenced from tests |
||||
images = OrderedDict() |
||||
|
||||
def _ReadImageDesc(binman_node): |
||||
"""Read the image descriptions from the /binman node |
||||
|
||||
This normally produces a single Image object called 'image'. But if |
||||
multiple images are present, they will all be returned. |
||||
|
||||
Args: |
||||
binman_node: Node object of the /binman node |
||||
Returns: |
||||
OrderedDict of Image objects, each of which describes an image |
||||
""" |
||||
images = OrderedDict() |
||||
if 'multiple-images' in binman_node.props: |
||||
for node in binman_node.subnodes: |
||||
images[node.name] = Image(node.name, node) |
||||
else: |
||||
images['image'] = Image('image', binman_node) |
||||
return images |
||||
|
||||
def _FindBinmanNode(fdt): |
||||
"""Find the 'binman' node in the device tree |
||||
|
||||
Args: |
||||
fdt: Fdt object to scan |
||||
Returns: |
||||
Node object of /binman node, or None if not found |
||||
""" |
||||
for node in fdt.GetRoot().subnodes: |
||||
if node.name == 'binman': |
||||
return node |
||||
return None |
||||
|
||||
def Binman(options, args): |
||||
"""The main control code for binman |
||||
|
||||
This assumes that help and test options have already been dealt with. It |
||||
deals with the core task of building images. |
||||
|
||||
Args: |
||||
options: Command line options object |
||||
args: Command line arguments (list of strings) |
||||
""" |
||||
global images |
||||
|
||||
if options.full_help: |
||||
pager = os.getenv('PAGER') |
||||
if not pager: |
||||
pager = 'more' |
||||
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), |
||||
'README') |
||||
command.Run(pager, fname) |
||||
return 0 |
||||
|
||||
# Try to figure out which device tree contains our image description |
||||
if options.dt: |
||||
dtb_fname = options.dt |
||||
else: |
||||
board = options.board |
||||
if not board: |
||||
raise ValueError('Must provide a board to process (use -b <board>)') |
||||
board_pathname = os.path.join(options.build_dir, board) |
||||
dtb_fname = os.path.join(board_pathname, 'u-boot.dtb') |
||||
if not options.indir: |
||||
options.indir = ['.'] |
||||
options.indir.append(board_pathname) |
||||
|
||||
try: |
||||
tout.Init(options.verbosity) |
||||
try: |
||||
tools.SetInputDirs(options.indir) |
||||
tools.PrepareOutputDir(options.outdir, options.preserve) |
||||
fdt = fdt_select.FdtScan(dtb_fname) |
||||
node = _FindBinmanNode(fdt) |
||||
if not node: |
||||
raise ValueError("Device tree '%s' does not have a 'binman' " |
||||
"node" % dtb_fname) |
||||
images = _ReadImageDesc(node) |
||||
for image in images.values(): |
||||
# Perform all steps for this image, including checking and |
||||
# writing it. This means that errors found with a later |
||||
# image will be reported after earlier images are already |
||||
# completed and written, but that does not seem important. |
||||
image.GetEntryContents() |
||||
image.GetEntryPositions() |
||||
image.PackEntries() |
||||
image.CheckSize() |
||||
image.CheckEntries() |
||||
image.ProcessEntryContents() |
||||
image.BuildImage() |
||||
finally: |
||||
tools.FinaliseOutputDir() |
||||
finally: |
||||
tout.Uninit() |
||||
|
||||
return 0 |
@ -0,0 +1,27 @@ |
||||
# |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Test for the Entry class |
||||
|
||||
import collections |
||||
import unittest |
||||
|
||||
import entry |
||||
|
||||
class TestEntry(unittest.TestCase): |
||||
def testEntryContents(self): |
||||
"""Test the Entry bass class""" |
||||
base_entry = entry.Entry(None, None, None, read_node=False) |
||||
self.assertEqual(True, base_entry.ObtainContents()) |
||||
|
||||
def testUnknownEntry(self): |
||||
"""Test that unknown entry types are detected""" |
||||
Node = collections.namedtuple('Node', ['name', 'path']) |
||||
node = Node('invalid-name', 'invalid-path') |
||||
with self.assertRaises(ValueError) as e: |
||||
entry.Entry.Create(None, node, node.name) |
||||
self.assertIn("Unknown entry type 'invalid-name' in node " |
||||
"'invalid-path'", str(e.exception)) |
@ -0,0 +1,26 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for testing purposes. Not used in real images. |
||||
# |
||||
|
||||
from entry import Entry |
||||
import fdt_util |
||||
import tools |
||||
|
||||
class Entry__testing(Entry): |
||||
def __init__(self, image, etype, node): |
||||
Entry.__init__(self, image, etype, node) |
||||
|
||||
def ObtainContents(self): |
||||
self.data = 'a' |
||||
self.contents_size = len(self.data) |
||||
return True |
||||
|
||||
def ReadContents(self): |
||||
return True |
||||
|
||||
def GetPositions(self): |
||||
return {'invalid-entry': [1, 2]} |
@ -0,0 +1,37 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for blobs, which are binary objects read from files |
||||
# |
||||
|
||||
from entry import Entry |
||||
import fdt_util |
||||
import tools |
||||
|
||||
class Entry_blob(Entry): |
||||
def __init__(self, image, etype, node): |
||||
Entry.__init__(self, image, etype, node) |
||||
self._filename = fdt_util.GetString(self._node, "filename", self.etype) |
||||
|
||||
def ObtainContents(self): |
||||
self._filename = self.GetDefaultFilename() |
||||
self._pathname = tools.GetInputFilename(self._filename) |
||||
self.ReadContents() |
||||
return True |
||||
|
||||
def ReadContents(self): |
||||
with open(self._pathname) as fd: |
||||
# We assume the data is small enough to fit into memory. If this |
||||
# is used for large filesystem image that might not be true. |
||||
# In that case, Image.BuildImage() could be adjusted to use a |
||||
# new Entry method which can read in chunks. Then we could copy |
||||
# the data in chunks and avoid reading it all at once. For now |
||||
# this seems like an unnecessary complication. |
||||
self.data = fd.read() |
||||
self.contents_size = len(self.data) |
||||
return True |
||||
|
||||
def GetDefaultFilename(self): |
||||
return self._filename |
@ -0,0 +1,200 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Base class for all entries |
||||
# |
||||
|
||||
# importlib was introduced in Python 2.7 but there was a report of it not |
||||
# working in 2.7.12, so we work around this: |
||||
# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html |
||||
try: |
||||
import importlib |
||||
have_importlib = True |
||||
except: |
||||
have_importlib = False |
||||
|
||||
import fdt_util |
||||
import tools |
||||
|
||||
modules = {} |
||||
|
||||
class Entry(object): |
||||
"""An Entry in the image |
||||
|
||||
An entry corresponds to a single node in the device-tree description |
||||
of the image. Each entry ends up being a part of the final image. |
||||
Entries can be placed either right next to each other, or with padding |
||||
between them. The type of the entry determines the data that is in it. |
||||
|
||||
This class is not used by itself. All entry objects are subclasses of |
||||
Entry. |
||||
|
||||
Attributes: |
||||
image: The image containing this entry |
||||
node: The node that created this entry |
||||
pos: Absolute position of entry within the image, None if not known |
||||
size: Entry size in bytes, None if not known |
||||
contents_size: Size of contents in bytes, 0 by default |
||||
align: Entry start position alignment, or None |
||||
align_size: Entry size alignment, or None |
||||
align_end: Entry end position alignment, or None |
||||
pad_before: Number of pad bytes before the contents, 0 if none |
||||
pad_after: Number of pad bytes after the contents, 0 if none |
||||
data: Contents of entry (string of bytes) |
||||
""" |
||||
def __init__(self, image, etype, node, read_node=True): |
||||
self.image = image |
||||
self.etype = etype |
||||
self._node = node |
||||
self.pos = None |
||||
self.size = None |
||||
self.contents_size = 0 |
||||
self.align = None |
||||
self.align_size = None |
||||
self.align_end = None |
||||
self.pad_before = 0 |
||||
self.pad_after = 0 |
||||
self.pos_unset = False |
||||
if read_node: |
||||
self.ReadNode() |
||||
|
||||
@staticmethod |
||||
def Create(image, node, etype=None): |
||||
"""Create a new entry for a node. |
||||
|
||||
Args: |
||||
image: Image object containing this node |
||||
node: Node object containing information about the entry to create |
||||
etype: Entry type to use, or None to work it out (used for tests) |
||||
|
||||
Returns: |
||||
A new Entry object of the correct type (a subclass of Entry) |
||||
""" |
||||
if not etype: |
||||
etype = fdt_util.GetString(node, 'type', node.name) |
||||
module_name = etype.replace('-', '_') |
||||
module = modules.get(module_name) |
||||
|
||||
# Import the module if we have not already done so. |
||||
if not module: |
||||
try: |
||||
if have_importlib: |
||||
module = importlib.import_module(module_name) |
||||
else: |
||||
module = __import__(module_name) |
||||
except ImportError: |
||||
raise ValueError("Unknown entry type '%s' in node '%s'" % |
||||
(etype, node.path)) |
||||
modules[module_name] = module |
||||
|
||||
# Call its constructor to get the object we want. |
||||
obj = getattr(module, 'Entry_%s' % module_name) |
||||
return obj(image, etype, node) |
||||
|
||||
def ReadNode(self): |
||||
"""Read entry information from the node |
||||
|
||||
This reads all the fields we recognise from the node, ready for use. |
||||
""" |
||||
self.pos = fdt_util.GetInt(self._node, 'pos') |
||||
self.size = fdt_util.GetInt(self._node, 'size') |
||||
self.align = fdt_util.GetInt(self._node, 'align') |
||||
if tools.NotPowerOfTwo(self.align): |
||||
raise ValueError("Node '%s': Alignment %s must be a power of two" % |
||||
(self._node.path, self.align)) |
||||
self.pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) |
||||
self.pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) |
||||
self.align_size = fdt_util.GetInt(self._node, 'align-size') |
||||
if tools.NotPowerOfTwo(self.align_size): |
||||
raise ValueError("Node '%s': Alignment size %s must be a power " |
||||
"of two" % (self._node.path, self.align_size)) |
||||
self.align_end = fdt_util.GetInt(self._node, 'align-end') |
||||
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset') |
||||
|
||||
def ObtainContents(self): |
||||
"""Figure out the contents of an entry. |
||||
|
||||
Returns: |
||||
True if the contents were found, False if another call is needed |
||||
after the other entries are processed. |
||||
""" |
||||
# No contents by default: subclasses can implement this |
||||
return True |
||||
|
||||
def Pack(self, pos): |
||||
"""Figure out how to pack the entry into the image |
||||
|
||||
Most of the time the entries are not fully specified. There may be |
||||
an alignment but no size. In that case we take the size from the |
||||
contents of the entry. |
||||
|
||||
If an entry has no hard-coded position, it will be placed at @pos. |
||||
|
||||
Once this function is complete, both the position and size of the |
||||
entry will be know. |
||||
|
||||
Args: |
||||
Current image position pointer |
||||
|
||||
Returns: |
||||
New image position pointer (after this entry) |
||||
""" |
||||
if self.pos is None: |
||||
if self.pos_unset: |
||||
self.Raise('No position set with pos-unset: should another ' |
||||
'entry provide this correct position?') |
||||
self.pos = tools.Align(pos, self.align) |
||||
needed = self.pad_before + self.contents_size + self.pad_after |
||||
needed = tools.Align(needed, self.align_size) |
||||
size = self.size |
||||
if not size: |
||||
size = needed |
||||
new_pos = self.pos + size |
||||
aligned_pos = tools.Align(new_pos, self.align_end) |
||||
if aligned_pos != new_pos: |
||||
size = aligned_pos - self.pos |
||||
new_pos = aligned_pos |
||||
|
||||
if not self.size: |
||||
self.size = size |
||||
|
||||
if self.size < needed: |
||||
self.Raise("Entry contents size is %#x (%d) but entry size is " |
||||
"%#x (%d)" % (needed, needed, self.size, self.size)) |
||||
# Check that the alignment is correct. It could be wrong if the |
||||
# and pos or size values were provided (i.e. not calculated), but |
||||
# conflict with the provided alignment values |
||||
if self.size != tools.Align(self.size, self.align_size): |
||||
self.Raise("Size %#x (%d) does not match align-size %#x (%d)" % |
||||
(self.size, self.size, self.align_size, self.align_size)) |
||||
if self.pos != tools.Align(self.pos, self.align): |
||||
self.Raise("Position %#x (%d) does not match align %#x (%d)" % |
||||
(self.pos, self.pos, self.align, self.align)) |
||||
|
||||
return new_pos |
||||
|
||||
def Raise(self, msg): |
||||
"""Convenience function to raise an error referencing a node""" |
||||
raise ValueError("Node '%s': %s" % (self._node.path, msg)) |
||||
|
||||
def GetPath(self): |
||||
"""Get the path of a node |
||||
|
||||
Returns: |
||||
Full path of the node for this entry |
||||
""" |
||||
return self._node.path |
||||
|
||||
def GetData(self): |
||||
return self.data |
||||
|
||||
def GetPositions(self): |
||||
return {} |
||||
|
||||
def SetPositionSize(self, pos, size): |
||||
self.pos = pos |
||||
self.size = size |
||||
|
||||
def ProcessContents(self): |
||||
pass |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for Intel Chip Microcode binary blob |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_intel_cmc(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'cmc.bin' |
@ -0,0 +1,55 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for 'u-boot' |
||||
# |
||||
|
||||
import struct |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
FD_SIGNATURE = struct.pack('<L', 0x0ff0a55a) |
||||
MAX_REGIONS = 5 |
||||
|
||||
(REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE, |
||||
REGION_PDATA) = range(5) |
||||
|
||||
class Region: |
||||
def __init__(self, data, frba, region_num): |
||||
pos = frba + region_num * 4 |
||||
val = struct.unpack('<L', data[pos:pos + 4])[0] |
||||
self.base = (val & 0xfff) << 12 |
||||
self.limit = ((val & 0x0fff0000) >> 4) | 0xfff |
||||
self.size = self.limit - self.base + 1 |
||||
|
||||
class Entry_intel_descriptor(Entry_blob): |
||||
"""Intel flash descriptor block (4KB) |
||||
|
||||
This is placed at the start of flash and provides information about |
||||
the SPI flash regions. In particular it provides the base address and |
||||
size of the ME region, allowing us to place the ME binary in the right |
||||
place. |
||||
""" |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
self._regions = [] |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'descriptor.bin' |
||||
|
||||
def GetPositions(self): |
||||
pos = self.data.find(FD_SIGNATURE) |
||||
if pos == -1: |
||||
self.Raise('Cannot find FD signature') |
||||
flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL', |
||||
self.data[pos:pos + 16]) |
||||
frba = ((flmap0 >> 16) & 0xff) << 4 |
||||
for i in range(MAX_REGIONS): |
||||
self._regions.append(Region(self.data, frba, i)) |
||||
|
||||
# Set the offset for ME only, for now, since the others are not used |
||||
return {'intel-me': [self._regions[REGION_ME].base, |
||||
self._regions[REGION_ME].size]} |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for Intel Firmware Support Package binary blob |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_intel_fsp(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'fsp.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for Intel Management Engine binary blob |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_intel_me(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'me.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for Intel Memory Reference Code binary blob |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_intel_mrc(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'mrc.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for x86 VGA ROM binary blob |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_intel_vga(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'vga.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for U-Boot binary |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_u_boot(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for U-Boot device tree |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_u_boot_dtb(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot.dtb' |
@ -0,0 +1,78 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
## Written by Simon Glass <sjg@chromium.org> |
||||
|
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for U-Boot device tree with the microcode removed |
||||
# |
||||
|
||||
import fdt_select |
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
import tools |
||||
|
||||
class Entry_u_boot_dtb_with_ucode(Entry_blob): |
||||
"""A U-Boot device tree file, with the microcode removed |
||||
|
||||
See Entry_u_boot_ucode for full details of the 3 entries involved in this |
||||
process. |
||||
""" |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
self.ucode_data = '' |
||||
self.collate = False |
||||
self.ucode_offset = None |
||||
self.ucode_size = None |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot.dtb' |
||||
|
||||
def ObtainContents(self): |
||||
Entry_blob.ObtainContents(self) |
||||
|
||||
# If the image does not need microcode, there is nothing to do |
||||
ucode_dest_entry = self.image.FindEntryType('u-boot-spl-with-ucode-ptr') |
||||
if not ucode_dest_entry or not ucode_dest_entry.target_pos: |
||||
ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr') |
||||
if not ucode_dest_entry or not ucode_dest_entry.target_pos: |
||||
return True |
||||
|
||||
# Create a new file to hold the copied device tree |
||||
dtb_name = 'u-boot-dtb-with-ucode.dtb' |
||||
fname = tools.GetOutputFilename(dtb_name) |
||||
with open(fname, 'wb') as fd: |
||||
fd.write(self.data) |
||||
|
||||
# Remove the microcode |
||||
fdt = fdt_select.FdtScan(fname) |
||||
fdt.Scan() |
||||
ucode = fdt.GetNode('/microcode') |
||||
if not ucode: |
||||
raise self.Raise("No /microcode node found in '%s'" % fname) |
||||
|
||||
# There's no need to collate it (move all microcode into one place) |
||||
# if we only have one chunk of microcode. |
||||
self.collate = len(ucode.subnodes) > 1 |
||||
for node in ucode.subnodes: |
||||
data_prop = node.props.get('data') |
||||
if data_prop: |
||||
self.ucode_data += ''.join(data_prop.bytes) |
||||
if not self.collate: |
||||
poffset = data_prop.GetOffset() |
||||
if poffset is None: |
||||
# We cannot obtain a property offset. Collate instead. |
||||
self.collate = True |
||||
else: |
||||
# Find the offset in the device tree of the ucode data |
||||
self.ucode_offset = poffset + 12 |
||||
self.ucode_size = len(data_prop.bytes) |
||||
if self.collate: |
||||
prop = node.DeleteProp('data') |
||||
if self.collate: |
||||
fdt.Pack() |
||||
fdt.Flush() |
||||
|
||||
# Make this file the contents of this entry |
||||
self._pathname = fname |
||||
self.ReadContents() |
||||
return True |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for U-Boot binary |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_u_boot_img(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot.img' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for 'u-boot-nodtb.bin' |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_u_boot_nodtb(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot-nodtb.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for spl/u-boot-spl.bin |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_u_boot_spl(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'spl/u-boot-spl.bin' |
@ -0,0 +1,26 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for BSS padding for spl/u-boot-spl.bin. This padding |
||||
# can be added after the SPL binary to ensure that anything concatenated |
||||
# to it will appear to SPL to be at the end of BSS rather than the start. |
||||
# |
||||
|
||||
import command |
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
import tools |
||||
|
||||
class Entry_u_boot_spl_bss_pad(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def ObtainContents(self): |
||||
fname = tools.GetInputFilename('spl/u-boot-spl') |
||||
args = [['nm', fname], ['grep', '__bss_size']] |
||||
out = command.RunPipe(args, capture=True).stdout.splitlines() |
||||
bss_size = int(out[0].split()[0], 16) |
||||
self.data = chr(0) * bss_size |
||||
self.contents_size = bss_size |
@ -0,0 +1,28 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for an SPL binary with an embedded microcode pointer |
||||
# |
||||
|
||||
import struct |
||||
|
||||
import command |
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
from u_boot_with_ucode_ptr import Entry_u_boot_with_ucode_ptr |
||||
import tools |
||||
|
||||
class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr): |
||||
"""U-Boot SPL with embedded microcode pointer |
||||
|
||||
See Entry_u_boot_ucode for full details of the entries involved in this |
||||
process. |
||||
""" |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
self.elf_fname = 'spl/u-boot-spl' |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'spl/u-boot-spl.bin' |
@ -0,0 +1,84 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for a U-Boot binary with an embedded microcode pointer |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
import tools |
||||
|
||||
class Entry_u_boot_ucode(Entry_blob): |
||||
"""U-Boot microcode block |
||||
|
||||
U-Boot on x86 needs a single block of microcode. This is collected from |
||||
the various microcode update nodes in the device tree. It is also unable |
||||
to read the microcode from the device tree on platforms that use FSP |
||||
(Firmware Support Package) binaries, because the API requires that the |
||||
microcode is supplied before there is any SRAM available to use (i.e. |
||||
the FSP sets up the SRAM / cache-as-RAM but does so in the call that |
||||
requires the microcode!). To keep things simple, all x86 platforms handle |
||||
microcode the same way in U-Boot (even non-FSP platforms). This is that |
||||
a table is placed at _dt_ucode_base_size containing the base address and |
||||
size of the microcode. This is either passed to the FSP (for FSP |
||||
platforms), or used to set up the microcode (for non-FSP platforms). |
||||
This all happens in the build system since it is the only way to get |
||||
the microcode into a single blob and accessible without SRAM. |
||||
|
||||
There are two cases to handle. If there is only one microcode blob in |
||||
the device tree, then the ucode pointer it set to point to that. This |
||||
entry (u-boot-ucode) is empty. If there is more than one update, then |
||||
this entry holds the concatenation of all updates, and the device tree |
||||
entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This |
||||
last step ensures that that the microcode appears in one contiguous |
||||
block in the image and is not unnecessarily duplicated in the device |
||||
tree. It is referred to as 'collation' here. |
||||
|
||||
Entry types that have a part to play in handling microcode: |
||||
|
||||
Entry_u_boot_with_ucode_ptr: |
||||
Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree). |
||||
It updates it with the address and size of the microcode so that |
||||
U-Boot can find it early on start-up. |
||||
Entry_u_boot_dtb_with_ucode: |
||||
Contains u-boot.dtb. It stores the microcode in a |
||||
'self.ucode_data' property, which is then read by this class to |
||||
obtain the microcode if needed. If collation is performed, it |
||||
removes the microcode from the device tree. |
||||
Entry_u_boot_ucode: |
||||
This class. If collation is enabled it reads the microcode from |
||||
the Entry_u_boot_dtb_with_ucode entry, and uses it as the |
||||
contents of this entry. |
||||
""" |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def ObtainContents(self): |
||||
# If the image does not need microcode, there is nothing to do |
||||
ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr') |
||||
if ucode_dest_entry and not ucode_dest_entry.target_pos: |
||||
self.data = '' |
||||
return True |
||||
|
||||
# Get the microcode from the device tree entry |
||||
fdt_entry = self.image.FindEntryType('u-boot-dtb-with-ucode') |
||||
if not fdt_entry or not fdt_entry.ucode_data: |
||||
return False |
||||
|
||||
if not fdt_entry.collate: |
||||
# This section can be empty |
||||
self.data = '' |
||||
return True |
||||
|
||||
# Write it out to a file |
||||
dtb_name = 'u-boot-ucode.bin' |
||||
fname = tools.GetOutputFilename(dtb_name) |
||||
with open(fname, 'wb') as fd: |
||||
fd.write(fdt_entry.ucode_data) |
||||
|
||||
self._pathname = fname |
||||
self.ReadContents() |
||||
|
||||
return True |
@ -0,0 +1,87 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for a U-Boot binary with an embedded microcode pointer |
||||
# |
||||
|
||||
import struct |
||||
|
||||
import command |
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
import fdt_util |
||||
import tools |
||||
|
||||
class Entry_u_boot_with_ucode_ptr(Entry_blob): |
||||
"""U-Boot with embedded microcode pointer |
||||
|
||||
See Entry_u_boot_ucode for full details of the 3 entries involved in this |
||||
process. |
||||
""" |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
self.elf_fname = 'u-boot' |
||||
self.target_pos = None |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot-nodtb.bin' |
||||
|
||||
def ObtainContents(self): |
||||
# Figure out where to put the microcode pointer |
||||
fname = tools.GetInputFilename(self.elf_fname) |
||||
args = [['nm', fname], ['grep', '-w', '_dt_ucode_base_size']] |
||||
out = (command.RunPipe(args, capture=True, raise_on_error=False). |
||||
stdout.splitlines()) |
||||
if len(out) == 1: |
||||
self.target_pos = int(out[0].split()[0], 16) |
||||
elif not fdt_util.GetBool(self._node, 'optional-ucode'): |
||||
self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot') |
||||
|
||||
return Entry_blob.ObtainContents(self) |
||||
|
||||
def ProcessContents(self): |
||||
# If the image does not need microcode, there is nothing to do |
||||
if not self.target_pos: |
||||
return |
||||
|
||||
# Get the position of the microcode |
||||
ucode_entry = self.image.FindEntryType('u-boot-ucode') |
||||
if not ucode_entry: |
||||
self.Raise('Cannot find microcode region u-boot-ucode') |
||||
|
||||
# Check the target pos is in the image. If it is not, then U-Boot is |
||||
# being linked incorrectly, or is being placed at the wrong position |
||||
# in the image. |
||||
# |
||||
# The image must be set up so that U-Boot is placed at the |
||||
# flash address to which it is linked. For example, if |
||||
# CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then |
||||
# the U-Boot region must start at position 7MB in the image. In this |
||||
# case the ROM starts at 0xff800000, so the position of the first |
||||
# entry in the image corresponds to that. |
||||
if (self.target_pos < self.pos or |
||||
self.target_pos >= self.pos + self.size): |
||||
self.Raise('Microcode pointer _dt_ucode_base_size at %08x is ' |
||||
'outside the image ranging from %08x to %08x' % |
||||
(self.target_pos, self.pos, self.pos + self.size)) |
||||
|
||||
# Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode. |
||||
# If we have left the microcode in the device tree, then it will be |
||||
# in the former. If we extracted the microcode from the device tree |
||||
# and collated it in one place, it will be in the latter. |
||||
if ucode_entry.size: |
||||
pos, size = ucode_entry.pos, ucode_entry.size |
||||
else: |
||||
dtb_entry = self.image.FindEntryType('u-boot-dtb-with-ucode') |
||||
if not dtb_entry: |
||||
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode') |
||||
pos = dtb_entry.pos + dtb_entry.ucode_offset |
||||
size = dtb_entry.ucode_size |
||||
|
||||
# Write the microcode position and size into the entry |
||||
pos_and_size = struct.pack('<2L', pos, size) |
||||
self.target_pos -= self.pos |
||||
self.data = (self.data[:self.target_pos] + pos_and_size + |
||||
self.data[self.target_pos + 8:]) |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for the 16-bit x86 start-up code for U-Boot |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_x86_start16(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'u-boot-x86-16bit.bin' |
@ -0,0 +1,17 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Entry-type module for the 16-bit x86 start-up code for U-Boot SPL |
||||
# |
||||
|
||||
from entry import Entry |
||||
from blob import Entry_blob |
||||
|
||||
class Entry_x86_start16_spl(Entry_blob): |
||||
def __init__(self, image, etype, node): |
||||
Entry_blob.__init__(self, image, etype, node) |
||||
|
||||
def GetDefaultFilename(self): |
||||
return 'spl/u-boot-x86-16bit-spl.bin' |
@ -0,0 +1,48 @@ |
||||
# |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Test for the fdt modules |
||||
|
||||
import os |
||||
import sys |
||||
import tempfile |
||||
import unittest |
||||
|
||||
from fdt_select import FdtScan |
||||
import fdt_util |
||||
import tools |
||||
|
||||
class TestFdt(unittest.TestCase): |
||||
@classmethod |
||||
def setUpClass(self): |
||||
self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) |
||||
self._indir = tempfile.mkdtemp(prefix='binmant.') |
||||
tools.PrepareOutputDir(self._indir, True) |
||||
|
||||
def TestFile(self, fname): |
||||
return os.path.join(self._binman_dir, 'test', fname) |
||||
|
||||
def GetCompiled(self, fname): |
||||
return fdt_util.EnsureCompiled(self.TestFile(fname)) |
||||
|
||||
def _DeleteProp(self, fdt): |
||||
node = fdt.GetNode('/microcode/update@0') |
||||
node.DeleteProp('data') |
||||
|
||||
def testFdtNormal(self): |
||||
fname = self.GetCompiled('34_x86_ucode.dts') |
||||
fdt = FdtScan(fname) |
||||
self._DeleteProp(fdt) |
||||
|
||||
def testFdtFallback(self): |
||||
fname = self.GetCompiled('34_x86_ucode.dts') |
||||
fdt = FdtScan(fname, True) |
||||
fdt.GetProp('/microcode/update@0', 'data') |
||||
self.assertEqual('fred', |
||||
fdt.GetProp('/microcode/update@0', 'none', default='fred')) |
||||
self.assertEqual('12345678 12345679', |
||||
fdt.GetProp('/microcode/update@0', 'data', typespec='x')) |
||||
self._DeleteProp(fdt) |
@ -0,0 +1,822 @@ |
||||
# |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# To run a single test, change to this directory, and: |
||||
# |
||||
# python -m unittest func_test.TestFunctional.testHelp |
||||
|
||||
from optparse import OptionParser |
||||
import os |
||||
import shutil |
||||
import struct |
||||
import sys |
||||
import tempfile |
||||
import unittest |
||||
|
||||
import binman |
||||
import cmdline |
||||
import command |
||||
import control |
||||
import entry |
||||
import fdt_select |
||||
import fdt_util |
||||
import tools |
||||
import tout |
||||
|
||||
# Contents of test files, corresponding to different entry types |
||||
U_BOOT_DATA = '1234' |
||||
U_BOOT_IMG_DATA = 'img' |
||||
U_BOOT_SPL_DATA = '567' |
||||
BLOB_DATA = '89' |
||||
ME_DATA = '0abcd' |
||||
VGA_DATA = 'vga' |
||||
U_BOOT_DTB_DATA = 'udtb' |
||||
X86_START16_DATA = 'start16' |
||||
U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here' |
||||
FSP_DATA = 'fsp' |
||||
CMC_DATA = 'cmc' |
||||
|
||||
class TestFunctional(unittest.TestCase): |
||||
"""Functional tests for binman |
||||
|
||||
Most of these use a sample .dts file to build an image and then check |
||||
that it looks correct. The sample files are in the test/ subdirectory |
||||
and are numbered. |
||||
|
||||
For each entry type a very small test file is created using fixed |
||||
string contents. This makes it easy to test that things look right, and |
||||
debug problems. |
||||
|
||||
In some cases a 'real' file must be used - these are also supplied in |
||||
the test/ diurectory. |
||||
""" |
||||
@classmethod |
||||
def setUpClass(self): |
||||
# Handle the case where argv[0] is 'python' |
||||
self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) |
||||
self._binman_pathname = os.path.join(self._binman_dir, 'binman') |
||||
|
||||
# Create a temporary directory for input files |
||||
self._indir = tempfile.mkdtemp(prefix='binmant.') |
||||
|
||||
# Create some test files |
||||
TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA) |
||||
TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA) |
||||
TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA) |
||||
TestFunctional._MakeInputFile('blobfile', BLOB_DATA) |
||||
TestFunctional._MakeInputFile('me.bin', ME_DATA) |
||||
TestFunctional._MakeInputFile('vga.bin', VGA_DATA) |
||||
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) |
||||
TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA) |
||||
TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA) |
||||
TestFunctional._MakeInputFile('fsp.bin', FSP_DATA) |
||||
TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) |
||||
self._output_setup = False |
||||
|
||||
# ELF file with a '_dt_ucode_base_size' symbol |
||||
with open(self.TestFile('u_boot_ucode_ptr')) as fd: |
||||
TestFunctional._MakeInputFile('u-boot', fd.read()) |
||||
|
||||
# Intel flash descriptor file |
||||
with open(self.TestFile('descriptor.bin')) as fd: |
||||
TestFunctional._MakeInputFile('descriptor.bin', fd.read()) |
||||
|
||||
@classmethod |
||||
def tearDownClass(self): |
||||
"""Remove the temporary input directory and its contents""" |
||||
if self._indir: |
||||
shutil.rmtree(self._indir) |
||||
self._indir = None |
||||
|
||||
def setUp(self): |
||||
# Enable this to turn on debugging output |
||||
# tout.Init(tout.DEBUG) |
||||
command.test_result = None |
||||
|
||||
def tearDown(self): |
||||
"""Remove the temporary output directory""" |
||||
tools._FinaliseForTest() |
||||
|
||||
def _RunBinman(self, *args, **kwargs): |
||||
"""Run binman using the command line |
||||
|
||||
Args: |
||||
Arguments to pass, as a list of strings |
||||
kwargs: Arguments to pass to Command.RunPipe() |
||||
""" |
||||
result = command.RunPipe([[self._binman_pathname] + list(args)], |
||||
capture=True, capture_stderr=True, raise_on_error=False) |
||||
if result.return_code and kwargs.get('raise_on_error', True): |
||||
raise Exception("Error running '%s': %s" % (' '.join(args), |
||||
result.stdout + result.stderr)) |
||||
return result |
||||
|
||||
def _DoBinman(self, *args): |
||||
"""Run binman using directly (in the same process) |
||||
|
||||
Args: |
||||
Arguments to pass, as a list of strings |
||||
Returns: |
||||
Return value (0 for success) |
||||
""" |
||||
(options, args) = cmdline.ParseArgs(list(args)) |
||||
options.pager = 'binman-invalid-pager' |
||||
options.build_dir = self._indir |
||||
|
||||
# For testing, you can force an increase in verbosity here |
||||
# options.verbosity = tout.DEBUG |
||||
return control.Binman(options, args) |
||||
|
||||
def _DoTestFile(self, fname): |
||||
"""Run binman with a given test file |
||||
|
||||
Args: |
||||
fname: Device tree source filename to use (e.g. 05_simple.dts) |
||||
""" |
||||
return self._DoBinman('-p', '-I', self._indir, |
||||
'-d', self.TestFile(fname)) |
||||
|
||||
def _SetupDtb(self, fname, outfile='u-boot.dtb'): |
||||
"""Set up a new test device-tree file |
||||
|
||||
The given file is compiled and set up as the device tree to be used |
||||
for ths test. |
||||
|
||||
Args: |
||||
fname: Filename of .dts file to read |
||||
outfile: Output filename for compiled device tree binary |
||||
|
||||
Returns: |
||||
Contents of device tree binary |
||||
""" |
||||
if not self._output_setup: |
||||
tools.PrepareOutputDir(self._indir, True) |
||||
self._output_setup = True |
||||
dtb = fdt_util.EnsureCompiled(self.TestFile(fname)) |
||||
with open(dtb) as fd: |
||||
data = fd.read() |
||||
TestFunctional._MakeInputFile(outfile, data) |
||||
return data |
||||
|
||||
def _DoReadFileDtb(self, fname, use_real_dtb=False): |
||||
"""Run binman and return the resulting image |
||||
|
||||
This runs binman with a given test file and then reads the resulting |
||||
output file. It is a shortcut function since most tests need to do |
||||
these steps. |
||||
|
||||
Raises an assertion failure if binman returns a non-zero exit code. |
||||
|
||||
Args: |
||||
fname: Device tree source filename to use (e.g. 05_simple.dts) |
||||
use_real_dtb: True to use the test file as the contents of |
||||
the u-boot-dtb entry. Normally this is not needed and the |
||||
test contents (the U_BOOT_DTB_DATA string) can be used. |
||||
But in some test we need the real contents. |
||||
|
||||
Returns: |
||||
Tuple: |
||||
Resulting image contents |
||||
Device tree contents |
||||
""" |
||||
dtb_data = None |
||||
# Use the compiled test file as the u-boot-dtb input |
||||
if use_real_dtb: |
||||
dtb_data = self._SetupDtb(fname) |
||||
|
||||
try: |
||||
retcode = self._DoTestFile(fname) |
||||
self.assertEqual(0, retcode) |
||||
|
||||
# Find the (only) image, read it and return its contents |
||||
image = control.images['image'] |
||||
fname = tools.GetOutputFilename('image.bin') |
||||
self.assertTrue(os.path.exists(fname)) |
||||
with open(fname) as fd: |
||||
return fd.read(), dtb_data |
||||
finally: |
||||
# Put the test file back |
||||
if use_real_dtb: |
||||
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) |
||||
|
||||
def _DoReadFile(self, fname, use_real_dtb=False): |
||||
"""Helper function which discards the device-tree binary""" |
||||
return self._DoReadFileDtb(fname, use_real_dtb)[0] |
||||
|
||||
@classmethod |
||||
def _MakeInputFile(self, fname, contents): |
||||
"""Create a new test input file, creating directories as needed |
||||
|
||||
Args: |
||||
fname: Filenaem to create |
||||
contents: File contents to write in to the file |
||||
Returns: |
||||
Full pathname of file created |
||||
""" |
||||
pathname = os.path.join(self._indir, fname) |
||||
dirname = os.path.dirname(pathname) |
||||
if dirname and not os.path.exists(dirname): |
||||
os.makedirs(dirname) |
||||
with open(pathname, 'wb') as fd: |
||||
fd.write(contents) |
||||
return pathname |
||||
|
||||
@classmethod |
||||
def TestFile(self, fname): |
||||
return os.path.join(self._binman_dir, 'test', fname) |
||||
|
||||
def AssertInList(self, grep_list, target): |
||||
"""Assert that at least one of a list of things is in a target |
||||
|
||||
Args: |
||||
grep_list: List of strings to check |
||||
target: Target string |
||||
""" |
||||
for grep in grep_list: |
||||
if grep in target: |
||||
return |
||||
self.fail("Error: '%' not found in '%s'" % (grep_list, target)) |
||||
|
||||
def CheckNoGaps(self, entries): |
||||
"""Check that all entries fit together without gaps |
||||
|
||||
Args: |
||||
entries: List of entries to check |
||||
""" |
||||
pos = 0 |
||||
for entry in entries.values(): |
||||
self.assertEqual(pos, entry.pos) |
||||
pos += entry.size |
||||
|
||||
def GetFdtLen(self, dtb): |
||||
"""Get the totalsize field from a device tree binary |
||||
|
||||
Args: |
||||
dtb: Device tree binary contents |
||||
|
||||
Returns: |
||||
Total size of device tree binary, from the header |
||||
""" |
||||
return struct.unpack('>L', dtb[4:8])[0] |
||||
|
||||
def testRun(self): |
||||
"""Test a basic run with valid args""" |
||||
result = self._RunBinman('-h') |
||||
|
||||
def testFullHelp(self): |
||||
"""Test that the full help is displayed with -H""" |
||||
result = self._RunBinman('-H') |
||||
help_file = os.path.join(self._binman_dir, 'README') |
||||
self.assertEqual(len(result.stdout), os.path.getsize(help_file)) |
||||
self.assertEqual(0, len(result.stderr)) |
||||
self.assertEqual(0, result.return_code) |
||||
|
||||
def testFullHelpInternal(self): |
||||
"""Test that the full help is displayed with -H""" |
||||
try: |
||||
command.test_result = command.CommandResult() |
||||
result = self._DoBinman('-H') |
||||
help_file = os.path.join(self._binman_dir, 'README') |
||||
finally: |
||||
command.test_result = None |
||||
|
||||
def testHelp(self): |
||||
"""Test that the basic help is displayed with -h""" |
||||
result = self._RunBinman('-h') |
||||
self.assertTrue(len(result.stdout) > 200) |
||||
self.assertEqual(0, len(result.stderr)) |
||||
self.assertEqual(0, result.return_code) |
||||
|
||||
# Not yet available. |
||||
def testBoard(self): |
||||
"""Test that we can run it with a specific board""" |
||||
self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb') |
||||
TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA) |
||||
result = self._DoBinman('-b', 'sandbox') |
||||
self.assertEqual(0, result) |
||||
|
||||
def testNeedBoard(self): |
||||
"""Test that we get an error when no board ius supplied""" |
||||
with self.assertRaises(ValueError) as e: |
||||
result = self._DoBinman() |
||||
self.assertIn("Must provide a board to process (use -b <board>)", |
||||
str(e.exception)) |
||||
|
||||
def testMissingDt(self): |
||||
"""Test that an invalid device tree file generates an error""" |
||||
with self.assertRaises(Exception) as e: |
||||
self._RunBinman('-d', 'missing_file') |
||||
# We get one error from libfdt, and a different one from fdtget. |
||||
self.AssertInList(["Couldn't open blob from 'missing_file'", |
||||
'No such file or directory'], str(e.exception)) |
||||
|
||||
def testBrokenDt(self): |
||||
"""Test that an invalid device tree source file generates an error |
||||
|
||||
Since this is a source file it should be compiled and the error |
||||
will come from the device-tree compiler (dtc). |
||||
""" |
||||
with self.assertRaises(Exception) as e: |
||||
self._RunBinman('-d', self.TestFile('01_invalid.dts')) |
||||
self.assertIn("FATAL ERROR: Unable to parse input tree", |
||||
str(e.exception)) |
||||
|
||||
def testMissingNode(self): |
||||
"""Test that a device tree without a 'binman' node generates an error""" |
||||
with self.assertRaises(Exception) as e: |
||||
self._DoBinman('-d', self.TestFile('02_missing_node.dts')) |
||||
self.assertIn("does not have a 'binman' node", str(e.exception)) |
||||
|
||||
def testEmpty(self): |
||||
"""Test that an empty binman node works OK (i.e. does nothing)""" |
||||
result = self._RunBinman('-d', self.TestFile('03_empty.dts')) |
||||
self.assertEqual(0, len(result.stderr)) |
||||
self.assertEqual(0, result.return_code) |
||||
|
||||
def testInvalidEntry(self): |
||||
"""Test that an invalid entry is flagged""" |
||||
with self.assertRaises(Exception) as e: |
||||
result = self._RunBinman('-d', |
||||
self.TestFile('04_invalid_entry.dts')) |
||||
#print e.exception |
||||
self.assertIn("Unknown entry type 'not-a-valid-type' in node " |
||||
"'/binman/not-a-valid-type'", str(e.exception)) |
||||
|
||||
def testSimple(self): |
||||
"""Test a simple binman with a single file""" |
||||
data = self._DoReadFile('05_simple.dts') |
||||
self.assertEqual(U_BOOT_DATA, data) |
||||
|
||||
def testDual(self): |
||||
"""Test that we can handle creating two images |
||||
|
||||
This also tests image padding. |
||||
""" |
||||
retcode = self._DoTestFile('06_dual_image.dts') |
||||
self.assertEqual(0, retcode) |
||||
|
||||
image = control.images['image1'] |
||||
self.assertEqual(len(U_BOOT_DATA), image._size) |
||||
fname = tools.GetOutputFilename('image1.bin') |
||||
self.assertTrue(os.path.exists(fname)) |
||||
with open(fname) as fd: |
||||
data = fd.read() |
||||
self.assertEqual(U_BOOT_DATA, data) |
||||
|
||||
image = control.images['image2'] |
||||
self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size) |
||||
fname = tools.GetOutputFilename('image2.bin') |
||||
self.assertTrue(os.path.exists(fname)) |
||||
with open(fname) as fd: |
||||
data = fd.read() |
||||
self.assertEqual(U_BOOT_DATA, data[3:7]) |
||||
self.assertEqual(chr(0) * 3, data[:3]) |
||||
self.assertEqual(chr(0) * 5, data[7:]) |
||||
|
||||
def testBadAlign(self): |
||||
"""Test that an invalid alignment value is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('07_bad_align.dts') |
||||
self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power " |
||||
"of two", str(e.exception)) |
||||
|
||||
def testPackSimple(self): |
||||
"""Test that packing works as expected""" |
||||
retcode = self._DoTestFile('08_pack.dts') |
||||
self.assertEqual(0, retcode) |
||||
self.assertIn('image', control.images) |
||||
image = control.images['image'] |
||||
entries = image._entries |
||||
self.assertEqual(5, len(entries)) |
||||
|
||||
# First u-boot |
||||
self.assertIn('u-boot', entries) |
||||
entry = entries['u-boot'] |
||||
self.assertEqual(0, entry.pos) |
||||
self.assertEqual(len(U_BOOT_DATA), entry.size) |
||||
|
||||
# Second u-boot, aligned to 16-byte boundary |
||||
self.assertIn('u-boot-align', entries) |
||||
entry = entries['u-boot-align'] |
||||
self.assertEqual(16, entry.pos) |
||||
self.assertEqual(len(U_BOOT_DATA), entry.size) |
||||
|
||||
# Third u-boot, size 23 bytes |
||||
self.assertIn('u-boot-size', entries) |
||||
entry = entries['u-boot-size'] |
||||
self.assertEqual(20, entry.pos) |
||||
self.assertEqual(len(U_BOOT_DATA), entry.contents_size) |
||||
self.assertEqual(23, entry.size) |
||||
|
||||
# Fourth u-boot, placed immediate after the above |
||||
self.assertIn('u-boot-next', entries) |
||||
entry = entries['u-boot-next'] |
||||
self.assertEqual(43, entry.pos) |
||||
self.assertEqual(len(U_BOOT_DATA), entry.size) |
||||
|
||||
# Fifth u-boot, placed at a fixed position |
||||
self.assertIn('u-boot-fixed', entries) |
||||
entry = entries['u-boot-fixed'] |
||||
self.assertEqual(61, entry.pos) |
||||
self.assertEqual(len(U_BOOT_DATA), entry.size) |
||||
|
||||
self.assertEqual(65, image._size) |
||||
|
||||
def testPackExtra(self): |
||||
"""Test that extra packing feature works as expected""" |
||||
retcode = self._DoTestFile('09_pack_extra.dts') |
||||
|
||||
self.assertEqual(0, retcode) |
||||
self.assertIn('image', control.images) |
||||
image = control.images['image'] |
||||
entries = image._entries |
||||
self.assertEqual(5, len(entries)) |
||||
|
||||
# First u-boot with padding before and after |
||||
self.assertIn('u-boot', entries) |
||||
entry = entries['u-boot'] |
||||
self.assertEqual(0, entry.pos) |
||||
self.assertEqual(3, entry.pad_before) |
||||
self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) |
||||
|
||||
# Second u-boot has an aligned size, but it has no effect |
||||
self.assertIn('u-boot-align-size-nop', entries) |
||||
entry = entries['u-boot-align-size-nop'] |
||||
self.assertEqual(12, entry.pos) |
||||
self.assertEqual(4, entry.size) |
||||
|
||||
# Third u-boot has an aligned size too |
||||
self.assertIn('u-boot-align-size', entries) |
||||
entry = entries['u-boot-align-size'] |
||||
self.assertEqual(16, entry.pos) |
||||
self.assertEqual(32, entry.size) |
||||
|
||||
# Fourth u-boot has an aligned end |
||||
self.assertIn('u-boot-align-end', entries) |
||||
entry = entries['u-boot-align-end'] |
||||
self.assertEqual(48, entry.pos) |
||||
self.assertEqual(16, entry.size) |
||||
|
||||
# Fifth u-boot immediately afterwards |
||||
self.assertIn('u-boot-align-both', entries) |
||||
entry = entries['u-boot-align-both'] |
||||
self.assertEqual(64, entry.pos) |
||||
self.assertEqual(64, entry.size) |
||||
|
||||
self.CheckNoGaps(entries) |
||||
self.assertEqual(128, image._size) |
||||
|
||||
def testPackAlignPowerOf2(self): |
||||
"""Test that invalid entry alignment is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('10_pack_align_power2.dts') |
||||
self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power " |
||||
"of two", str(e.exception)) |
||||
|
||||
def testPackAlignSizePowerOf2(self): |
||||
"""Test that invalid entry size alignment is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('11_pack_align_size_power2.dts') |
||||
self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a " |
||||
"power of two", str(e.exception)) |
||||
|
||||
def testPackInvalidAlign(self): |
||||
"""Test detection of an position that does not match its alignment""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('12_pack_inv_align.dts') |
||||
self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match " |
||||
"align 0x4 (4)", str(e.exception)) |
||||
|
||||
def testPackInvalidSizeAlign(self): |
||||
"""Test that invalid entry size alignment is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('13_pack_inv_size_align.dts') |
||||
self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match " |
||||
"align-size 0x4 (4)", str(e.exception)) |
||||
|
||||
def testPackOverlap(self): |
||||
"""Test that overlapping regions are detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('14_pack_overlap.dts') |
||||
self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps " |
||||
"with previous entry '/binman/u-boot' ending at 0x4 (4)", |
||||
str(e.exception)) |
||||
|
||||
def testPackEntryOverflow(self): |
||||
"""Test that entries that overflow their size are detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('15_pack_overflow.dts') |
||||
self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) " |
||||
"but entry size is 0x3 (3)", str(e.exception)) |
||||
|
||||
def testPackImageOverflow(self): |
||||
"""Test that entries which overflow the image size are detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('16_pack_image_overflow.dts') |
||||
self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image " |
||||
"size 0x3 (3)", str(e.exception)) |
||||
|
||||
def testPackImageSize(self): |
||||
"""Test that the image size can be set""" |
||||
retcode = self._DoTestFile('17_pack_image_size.dts') |
||||
self.assertEqual(0, retcode) |
||||
self.assertIn('image', control.images) |
||||
image = control.images['image'] |
||||
self.assertEqual(7, image._size) |
||||
|
||||
def testPackImageSizeAlign(self): |
||||
"""Test that image size alignemnt works as expected""" |
||||
retcode = self._DoTestFile('18_pack_image_align.dts') |
||||
self.assertEqual(0, retcode) |
||||
self.assertIn('image', control.images) |
||||
image = control.images['image'] |
||||
self.assertEqual(16, image._size) |
||||
|
||||
def testPackInvalidImageAlign(self): |
||||
"""Test that invalid image alignment is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('19_pack_inv_image_align.dts') |
||||
self.assertIn("Image '/binman': Size 0x7 (7) does not match " |
||||
"align-size 0x8 (8)", str(e.exception)) |
||||
|
||||
def testPackAlignPowerOf2(self): |
||||
"""Test that invalid image alignment is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('20_pack_inv_image_align_power2.dts') |
||||
self.assertIn("Image '/binman': Alignment size 131 must be a power of " |
||||
"two", str(e.exception)) |
||||
|
||||
def testImagePadByte(self): |
||||
"""Test that the image pad byte can be specified""" |
||||
data = self._DoReadFile('21_image_pad.dts') |
||||
self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 9) + U_BOOT_DATA, data) |
||||
|
||||
def testImageName(self): |
||||
"""Test that image files can be named""" |
||||
retcode = self._DoTestFile('22_image_name.dts') |
||||
self.assertEqual(0, retcode) |
||||
image = control.images['image1'] |
||||
fname = tools.GetOutputFilename('test-name') |
||||
self.assertTrue(os.path.exists(fname)) |
||||
|
||||
image = control.images['image2'] |
||||
fname = tools.GetOutputFilename('test-name.xx') |
||||
self.assertTrue(os.path.exists(fname)) |
||||
|
||||
def testBlobFilename(self): |
||||
"""Test that generic blobs can be provided by filename""" |
||||
data = self._DoReadFile('23_blob.dts') |
||||
self.assertEqual(BLOB_DATA, data) |
||||
|
||||
def testPackSorted(self): |
||||
"""Test that entries can be sorted""" |
||||
data = self._DoReadFile('24_sorted.dts') |
||||
self.assertEqual(chr(0) * 5 + U_BOOT_SPL_DATA + chr(0) * 2 + |
||||
U_BOOT_DATA, data) |
||||
|
||||
def testPackZeroPosition(self): |
||||
"""Test that an entry at position 0 is not given a new position""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('25_pack_zero_size.dts') |
||||
self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps " |
||||
"with previous entry '/binman/u-boot' ending at 0x4 (4)", |
||||
str(e.exception)) |
||||
|
||||
def testPackUbootDtb(self): |
||||
"""Test that a device tree can be added to U-Boot""" |
||||
data = self._DoReadFile('26_pack_u_boot_dtb.dts') |
||||
self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data) |
||||
|
||||
def testPackX86RomNoSize(self): |
||||
"""Test that the end-at-4gb property requires a size property""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('27_pack_4gb_no_size.dts') |
||||
self.assertIn("Image '/binman': Image size must be provided when " |
||||
"using end-at-4gb", str(e.exception)) |
||||
|
||||
def testPackX86RomOutside(self): |
||||
"""Test that the end-at-4gb property checks for position boundaries""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('28_pack_4gb_outside.dts') |
||||
self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside " |
||||
"the image starting at 0xfffffff0 (4294967280)", |
||||
str(e.exception)) |
||||
|
||||
def testPackX86Rom(self): |
||||
"""Test that a basic x86 ROM can be created""" |
||||
data = self._DoReadFile('29_x86-rom.dts') |
||||
self.assertEqual(U_BOOT_DATA + chr(0) * 3 + U_BOOT_SPL_DATA + |
||||
chr(0) * 6, data) |
||||
|
||||
def testPackX86RomMeNoDesc(self): |
||||
"""Test that an invalid Intel descriptor entry is detected""" |
||||
TestFunctional._MakeInputFile('descriptor.bin', '') |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('31_x86-rom-me.dts') |
||||
self.assertIn("Node '/binman/intel-descriptor': Cannot find FD " |
||||
"signature", str(e.exception)) |
||||
|
||||
def testPackX86RomBadDesc(self): |
||||
"""Test that the Intel requires a descriptor entry""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoTestFile('30_x86-rom-me-no-desc.dts') |
||||
self.assertIn("Node '/binman/intel-me': No position set with " |
||||
"pos-unset: should another entry provide this correct " |
||||
"position?", str(e.exception)) |
||||
|
||||
def testPackX86RomMe(self): |
||||
"""Test that an x86 ROM with an ME region can be created""" |
||||
data = self._DoReadFile('31_x86-rom-me.dts') |
||||
self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)]) |
||||
|
||||
def testPackVga(self): |
||||
"""Test that an image with a VGA binary can be created""" |
||||
data = self._DoReadFile('32_intel-vga.dts') |
||||
self.assertEqual(VGA_DATA, data[:len(VGA_DATA)]) |
||||
|
||||
def testPackStart16(self): |
||||
"""Test that an image with an x86 start16 region can be created""" |
||||
data = self._DoReadFile('33_x86-start16.dts') |
||||
self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) |
||||
|
||||
def testPackUbootMicrocode(self): |
||||
"""Test that x86 microcode can be handled correctly |
||||
|
||||
We expect to see the following in the image, in order: |
||||
u-boot-nodtb.bin with a microcode pointer inserted at the correct |
||||
place |
||||
u-boot.dtb with the microcode removed |
||||
the microcode |
||||
""" |
||||
data = self._DoReadFile('34_x86_ucode.dts', True) |
||||
|
||||
# Now check the device tree has no microcode |
||||
second = data[len(U_BOOT_NODTB_DATA):] |
||||
fname = tools.GetOutputFilename('test.dtb') |
||||
with open(fname, 'wb') as fd: |
||||
fd.write(second) |
||||
fdt = fdt_select.FdtScan(fname) |
||||
ucode = fdt.GetNode('/microcode') |
||||
self.assertTrue(ucode) |
||||
for node in ucode.subnodes: |
||||
self.assertFalse(node.props.get('data')) |
||||
|
||||
fdt_len = self.GetFdtLen(second) |
||||
third = second[fdt_len:] |
||||
|
||||
# Check that the microcode appears immediately after the Fdt |
||||
# This matches the concatenation of the data properties in |
||||
# the /microcode/update@xxx nodes in x86_ucode.dts. |
||||
ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, |
||||
0x78235609) |
||||
self.assertEqual(ucode_data, third[:len(ucode_data)]) |
||||
ucode_pos = len(U_BOOT_NODTB_DATA) + fdt_len |
||||
|
||||
# Check that the microcode pointer was inserted. It should match the |
||||
# expected position and size |
||||
pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, |
||||
len(ucode_data)) |
||||
first = data[:len(U_BOOT_NODTB_DATA)] |
||||
self.assertEqual('nodtb with microcode' + pos_and_size + |
||||
' somewhere in here', first) |
||||
|
||||
def _RunPackUbootSingleMicrocode(self, collate): |
||||
"""Test that x86 microcode can be handled correctly |
||||
|
||||
We expect to see the following in the image, in order: |
||||
u-boot-nodtb.bin with a microcode pointer inserted at the correct |
||||
place |
||||
u-boot.dtb with the microcode |
||||
an empty microcode region |
||||
""" |
||||
# We need the libfdt library to run this test since only that allows |
||||
# finding the offset of a property. This is required by |
||||
# Entry_u_boot_dtb_with_ucode.ObtainContents(). |
||||
if not fdt_select.have_libfdt: |
||||
return |
||||
data = self._DoReadFile('35_x86_single_ucode.dts', True) |
||||
|
||||
second = data[len(U_BOOT_NODTB_DATA):] |
||||
|
||||
fdt_len = self.GetFdtLen(second) |
||||
third = second[fdt_len:] |
||||
second = second[:fdt_len] |
||||
|
||||
if not collate: |
||||
ucode_data = struct.pack('>2L', 0x12345678, 0x12345679) |
||||
self.assertIn(ucode_data, second) |
||||
ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) |
||||
|
||||
# Check that the microcode pointer was inserted. It should match the |
||||
# expected position and size |
||||
pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, |
||||
len(ucode_data)) |
||||
first = data[:len(U_BOOT_NODTB_DATA)] |
||||
self.assertEqual('nodtb with microcode' + pos_and_size + |
||||
' somewhere in here', first) |
||||
|
||||
def testPackUbootSingleMicrocode(self): |
||||
"""Test that x86 microcode can be handled correctly with fdt_normal. |
||||
""" |
||||
self._RunPackUbootSingleMicrocode(False) |
||||
|
||||
def testPackUbootSingleMicrocodeFallback(self): |
||||
"""Test that x86 microcode can be handled correctly with fdt_fallback. |
||||
|
||||
This only supports collating the microcode. |
||||
""" |
||||
try: |
||||
old_val = fdt_select.UseFallback(True) |
||||
self._RunPackUbootSingleMicrocode(True) |
||||
finally: |
||||
fdt_select.UseFallback(old_val) |
||||
|
||||
def testUBootImg(self): |
||||
"""Test that u-boot.img can be put in a file""" |
||||
data = self._DoReadFile('36_u_boot_img.dts') |
||||
self.assertEqual(U_BOOT_IMG_DATA, data) |
||||
|
||||
def testNoMicrocode(self): |
||||
"""Test that a missing microcode region is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoReadFile('37_x86_no_ucode.dts', True) |
||||
self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode " |
||||
"node found in ", str(e.exception)) |
||||
|
||||
def testMicrocodeWithoutNode(self): |
||||
"""Test that a missing u-boot-dtb-with-ucode node is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoReadFile('38_x86_ucode_missing_node.dts', True) |
||||
self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " |
||||
"microcode region u-boot-dtb-with-ucode", str(e.exception)) |
||||
|
||||
def testMicrocodeWithoutNode2(self): |
||||
"""Test that a missing u-boot-ucode node is detected""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoReadFile('39_x86_ucode_missing_node2.dts', True) |
||||
self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find " |
||||
"microcode region u-boot-ucode", str(e.exception)) |
||||
|
||||
def testMicrocodeWithoutPtrInElf(self): |
||||
"""Test that a U-Boot binary without the microcode symbol is detected""" |
||||
# ELF file without a '_dt_ucode_base_size' symbol |
||||
if not fdt_select.have_libfdt: |
||||
return |
||||
try: |
||||
with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: |
||||
TestFunctional._MakeInputFile('u-boot', fd.read()) |
||||
|
||||
with self.assertRaises(ValueError) as e: |
||||
self._RunPackUbootSingleMicrocode(False) |
||||
self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate " |
||||
"_dt_ucode_base_size symbol in u-boot", str(e.exception)) |
||||
|
||||
finally: |
||||
# Put the original file back |
||||
with open(self.TestFile('u_boot_ucode_ptr')) as fd: |
||||
TestFunctional._MakeInputFile('u-boot', fd.read()) |
||||
|
||||
def testMicrocodeNotInImage(self): |
||||
"""Test that microcode must be placed within the image""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoReadFile('40_x86_ucode_not_in_image.dts', True) |
||||
self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " |
||||
"pointer _dt_ucode_base_size at fffffe14 is outside the " |
||||
"image ranging from 00000000 to 0000002e", str(e.exception)) |
||||
|
||||
def testWithoutMicrocode(self): |
||||
"""Test that we can cope with an image without microcode (e.g. qemu)""" |
||||
with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: |
||||
TestFunctional._MakeInputFile('u-boot', fd.read()) |
||||
data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True) |
||||
|
||||
# Now check the device tree has no microcode |
||||
self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) |
||||
second = data[len(U_BOOT_NODTB_DATA):] |
||||
|
||||
fdt_len = self.GetFdtLen(second) |
||||
self.assertEqual(dtb, second[:fdt_len]) |
||||
|
||||
used_len = len(U_BOOT_NODTB_DATA) + fdt_len |
||||
third = data[used_len:] |
||||
self.assertEqual(chr(0) * (0x200 - used_len), third) |
||||
|
||||
def testUnknownPosSize(self): |
||||
"""Test that microcode must be placed within the image""" |
||||
with self.assertRaises(ValueError) as e: |
||||
self._DoReadFile('41_unknown_pos_size.dts', True) |
||||
self.assertIn("Image '/binman': Unable to set pos/size for unknown " |
||||
"entry 'invalid-entry'", str(e.exception)) |
||||
|
||||
def testPackFsp(self): |
||||
"""Test that an image with a FSP binary can be created""" |
||||
data = self._DoReadFile('42_intel-fsp.dts') |
||||
self.assertEqual(FSP_DATA, data[:len(FSP_DATA)]) |
||||
|
||||
def testPackCmc(self): |
||||
"""Test that an image with a FSP binary can be created""" |
||||
data = self._DoReadFile('43_intel-cmc.dts') |
||||
self.assertEqual(CMC_DATA, data[:len(CMC_DATA)]) |
@ -0,0 +1,229 @@ |
||||
# Copyright (c) 2016 Google, Inc |
||||
# Written by Simon Glass <sjg@chromium.org> |
||||
# |
||||
# SPDX-License-Identifier: GPL-2.0+ |
||||
# |
||||
# Class for an image, the output of binman |
||||
# |
||||
|
||||
from collections import OrderedDict |
||||
from operator import attrgetter |
||||
|
||||
import entry |
||||
from entry import Entry |
||||
import fdt_util |
||||
import tools |
||||
|
||||
class Image: |
||||
"""A Image, representing an output from binman |
||||
|
||||
An image is comprised of a collection of entries each containing binary |
||||
data. The image size must be large enough to hold all of this data. |
||||
|
||||
This class implements the various operations needed for images. |
||||
|
||||
Atrtributes: |
||||
_node: Node object that contains the image definition in device tree |
||||
_name: Image name |
||||
_size: Image size in bytes, or None if not known yet |
||||
_align_size: Image size alignment, or None |
||||
_pad_before: Number of bytes before the first entry starts. This |
||||
effectively changes the place where entry position 0 starts |
||||
_pad_after: Number of bytes after the last entry ends. The last |
||||
entry will finish on or before this boundary |
||||
_pad_byte: Byte to use to pad the image where there is no entry |
||||
_filename: Output filename for image |
||||
_sort: True if entries should be sorted by position, False if they |
||||
must be in-order in the device tree description |
||||
_skip_at_start: Number of bytes before the first entry starts. These |
||||
effecively adjust the starting position of entries. For example, |
||||
if _pad_before is 16, then the first entry would start at 16. |
||||
An entry with pos = 20 would in fact be written at position 4 |
||||
in the image file. |
||||
_end_4gb: Indicates that the image ends at the 4GB boundary. This is |
||||
used for x86 images, which want to use positions such that a |
||||
memory address (like 0xff800000) is the first entry position. |
||||
This causes _skip_at_start to be set to the starting memory |
||||
address. |
||||
_entries: OrderedDict() of entries |
||||
""" |
||||
def __init__(self, name, node): |
||||
self._node = node |
||||
self._name = name |
||||
self._size = None |
||||
self._align_size = None |
||||
self._pad_before = 0 |
||||
self._pad_after = 0 |
||||
self._pad_byte = 0 |
||||
self._filename = '%s.bin' % self._name |
||||
self._sort = False |
||||
self._skip_at_start = 0 |
||||
self._end_4gb = False |
||||
self._entries = OrderedDict() |
||||
|
||||
self._ReadNode() |
||||
self._ReadEntries() |
||||
|
||||
def _ReadNode(self): |
||||
"""Read properties from the image node""" |
||||
self._size = fdt_util.GetInt(self._node, 'size') |
||||
self._align_size = fdt_util.GetInt(self._node, 'align-size') |
||||
if tools.NotPowerOfTwo(self._align_size): |
||||
self._Raise("Alignment size %s must be a power of two" % |
||||
self._align_size) |
||||
self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) |
||||
self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) |
||||
self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) |
||||
filename = fdt_util.GetString(self._node, 'filename') |
||||
if filename: |
||||
self._filename = filename |
||||
self._sort = fdt_util.GetBool(self._node, 'sort-by-pos') |
||||
self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb') |
||||
if self._end_4gb and not self._size: |
||||
self._Raise("Image size must be provided when using end-at-4gb") |
||||
if self._end_4gb: |
||||
self._skip_at_start = 0x100000000 - self._size |
||||
|
||||
def CheckSize(self): |
||||
"""Check that the image contents does not exceed its size, etc.""" |
||||
contents_size = 0 |
||||
for entry in self._entries.values(): |
||||
contents_size = max(contents_size, entry.pos + entry.size) |
||||
|
||||
contents_size -= self._skip_at_start |
||||
|
||||
size = self._size |
||||
if not size: |
||||
size = self._pad_before + contents_size + self._pad_after |
||||
size = tools.Align(size, self._align_size) |
||||
|
||||
if self._size and contents_size > self._size: |
||||
self._Raise("contents size %#x (%d) exceeds image size %#x (%d)" % |
||||
(contents_size, contents_size, self._size, self._size)) |
||||
if not self._size: |
||||
self._size = size |
||||
if self._size != tools.Align(self._size, self._align_size): |
||||
self._Raise("Size %#x (%d) does not match align-size %#x (%d)" % |
||||
(self._size, self._size, self._align_size, self._align_size)) |
||||
|
||||
def _Raise(self, msg): |
||||
"""Raises an error for this image |
||||
|
||||
Args: |
||||
msg: Error message to use in the raise string |
||||
Raises: |
||||
ValueError() |
||||
""" |
||||
raise ValueError("Image '%s': %s" % (self._node.path, msg)) |
||||
|
||||
def _ReadEntries(self): |
||||
for node in self._node.subnodes: |
||||
self._entries[node.name] = Entry.Create(self, node) |
||||
|
||||
def FindEntryType(self, etype): |
||||
"""Find an entry type in the image |
||||
|
||||
Args: |
||||
etype: Entry type to find |
||||
Returns: |
||||
entry matching that type, or None if not found |
||||
""" |
||||
for entry in self._entries.values(): |
||||
if entry.etype == etype: |
||||
return entry |
||||
return None |
||||
|
||||
def GetEntryContents(self): |
||||
"""Call ObtainContents() for each entry |
||||
|
||||
This calls each entry's ObtainContents() a few times until they all |
||||
return True. We stop calling an entry's function once it returns |
||||
True. This allows the contents of one entry to depend on another. |
||||
|
||||
After 3 rounds we give up since it's likely an error. |
||||
""" |
||||
todo = self._entries.values() |
||||
for passnum in range(3): |
||||
next_todo = [] |
||||
for entry in todo: |
||||
if not entry.ObtainContents(): |
||||
next_todo.append(entry) |
||||
todo = next_todo |
||||
if not todo: |
||||
break |
||||
|
||||
def _SetEntryPosSize(self, name, pos, size): |
||||
"""Set the position and size of an entry |
||||
|
||||
Args: |
||||
name: Entry name to update |
||||
pos: New position |
||||
size: New size |
||||
""" |
||||
entry = self._entries.get(name) |
||||
if not entry: |
||||
self._Raise("Unable to set pos/size for unknown entry '%s'" % name) |
||||
entry.SetPositionSize(self._skip_at_start + pos, size) |
||||
|
||||
def GetEntryPositions(self): |
||||
"""Handle entries that want to set the position/size of other entries |
||||
|
||||
This calls each entry's GetPositions() method. If it returns a list |
||||
of entries to update, it updates them. |
||||
""" |
||||
for entry in self._entries.values(): |
||||
pos_dict = entry.GetPositions() |
||||
for name, info in pos_dict.iteritems(): |
||||
self._SetEntryPosSize(name, *info) |
||||
|
||||
def PackEntries(self): |
||||
"""Pack all entries into the image""" |
||||
pos = self._skip_at_start |
||||
for entry in self._entries.values(): |
||||
pos = entry.Pack(pos) |
||||
|
||||
def _SortEntries(self): |
||||
"""Sort entries by position""" |
||||
entries = sorted(self._entries.values(), key=lambda entry: entry.pos) |
||||
self._entries.clear() |
||||
for entry in entries: |
||||
self._entries[entry._node.name] = entry |
||||
|
||||
def CheckEntries(self): |
||||
"""Check that entries do not overlap or extend outside the image""" |
||||
if self._sort: |
||||
self._SortEntries() |
||||
pos = 0 |
||||
prev_name = 'None' |
||||
for entry in self._entries.values(): |
||||
if (entry.pos < self._skip_at_start or |
||||
entry.pos >= self._skip_at_start + self._size): |
||||
entry.Raise("Position %#x (%d) is outside the image starting " |
||||
"at %#x (%d)" % |
||||
(entry.pos, entry.pos, self._skip_at_start, |
||||
self._skip_at_start)) |
||||
if entry.pos < pos: |
||||
entry.Raise("Position %#x (%d) overlaps with previous entry '%s' " |
||||
"ending at %#x (%d)" % |
||||
(entry.pos, entry.pos, prev_name, pos, pos)) |
||||
pos = entry.pos + entry.size |
||||
prev_name = entry.GetPath() |
||||
|
||||
def ProcessEntryContents(self): |
||||
"""Call the ProcessContents() method for each entry |
||||
|
||||
This is intended to adjust the contents as needed by the entry type. |
||||
""" |
||||
for entry in self._entries.values(): |
||||
entry.ProcessContents() |
||||
|
||||
def BuildImage(self): |
||||
"""Write the image to a file""" |
||||
fname = tools.GetOutputFilename(self._filename) |
||||
with open(fname, 'wb') as fd: |
||||
fd.write(chr(self._pad_byte) * self._size) |
||||
|
||||
for entry in self._entries.values(): |
||||
data = entry.GetData() |
||||
fd.seek(self._pad_before + entry.pos - self._skip_at_start) |
||||
fd.write(data) |
@ -0,0 +1,5 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
@ -0,0 +1,6 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
}; |
@ -0,0 +1,9 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
}; |
||||
}; |
@ -0,0 +1,11 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
not-a-valid-type { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,11 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -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 { |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
align = <23>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,30 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
}; |
||||
|
||||
u-boot-align { |
||||
type = "u-boot"; |
||||
align = <16>; |
||||
}; |
||||
|
||||
u-boot-size { |
||||
type = "u-boot"; |
||||
size = <23>; |
||||
}; |
||||
|
||||
u-boot-next { |
||||
type = "u-boot"; |
||||
}; |
||||
|
||||
u-boot-fixed { |
||||
type = "u-boot"; |
||||
pos = <61>; |
||||
}; |
||||
}; |
||||
}; |
@ -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>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
align = <5>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
align-size = <55>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
pos = <5>; |
||||
align = <4>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
size = <5>; |
||||
align-size = <4>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,16 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
}; |
||||
|
||||
u-boot-align { |
||||
type = "u-boot"; |
||||
pos = <3>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
size = <3>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <3>; |
||||
|
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <7>; |
||||
|
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
align-size = <16>; |
||||
|
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,14 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <7>; |
||||
align-size = <8>; |
||||
|
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
align-size = <131>; |
||||
|
||||
u-boot { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,16 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
pad-byte = <0xff>; |
||||
u-boot-spl { |
||||
}; |
||||
|
||||
u-boot { |
||||
pos = <12>; |
||||
}; |
||||
}; |
||||
}; |
@ -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 { |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
blob { |
||||
filename = "blobfile"; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,17 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
u-boot { |
||||
pos = <10>; |
||||
}; |
||||
|
||||
u-boot-spl { |
||||
pos = <5>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,15 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot { |
||||
}; |
||||
|
||||
u-boot-spl { |
||||
pos = <0>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,14 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot-nodtb { |
||||
}; |
||||
|
||||
u-boot-dtb { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,18 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
u-boot { |
||||
pos = <0xfffffff0>; |
||||
}; |
||||
|
||||
u-boot-spl { |
||||
pos = <0xfffffff7>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,19 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <16>; |
||||
u-boot { |
||||
pos = <0>; |
||||
}; |
||||
|
||||
u-boot-spl { |
||||
pos = <0xfffffff7>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,19 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <16>; |
||||
u-boot { |
||||
pos = <0xfffffff0>; |
||||
}; |
||||
|
||||
u-boot-spl { |
||||
pos = <0xfffffff7>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,15 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <16>; |
||||
intel-me { |
||||
pos-unset; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,18 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x800000>; |
||||
intel-descriptor { |
||||
}; |
||||
|
||||
intel-me { |
||||
pos-unset; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <16>; |
||||
|
||||
intel-vga { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <16>; |
||||
|
||||
x86-start16 { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,29 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
|
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
update@1 { |
||||
data = <0xabcd0000 0x78235609>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,26 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
|
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,11 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
u-boot-img { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,20 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
|
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,26 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
update@1 { |
||||
data = <0xabcd0000 0x78235609>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,23 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
update@1 { |
||||
data = <0xabcd0000 0x78235609>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,28 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
}; |
||||
|
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
update@1 { |
||||
data = <0xabcd0000 0x78235609>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,11 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
_testing { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <16>; |
||||
|
||||
intel-fsp { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,13 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
size = <16>; |
||||
|
||||
intel-cmc { |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,30 @@ |
||||
/dts-v1/; |
||||
|
||||
/ { |
||||
#address-cells = <1>; |
||||
#size-cells = <1>; |
||||
|
||||
binman { |
||||
sort-by-pos; |
||||
end-at-4gb; |
||||
size = <0x200>; |
||||
u-boot-with-ucode-ptr { |
||||
optional-ucode; |
||||
}; |
||||
|
||||
u-boot-dtb-with-ucode { |
||||
}; |
||||
|
||||
u-boot-ucode { |
||||
}; |
||||
}; |
||||
|
||||
microcode { |
||||
update@0 { |
||||
data = <0x12345678 0x12345679>; |
||||
}; |
||||
update@1 { |
||||
data = <0xabcd0000 0x78235609>; |
||||
}; |
||||
}; |
||||
}; |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,15 @@ |
||||
/*
|
||||
* Copyright (c) 2016 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
* |
||||
* Simple program to create a bad _dt_ucode_base_size symbol to create an |
||||
* error when it is used. This is used by binman tests. |
||||
* |
||||
* Build with: |
||||
* cc -march=i386 -m32 -o u_boot_no_ucode_ptr -T u_boot_ucode_ptr.lds \
|
||||
-nostdlib u_boot_no_ucode_ptr.c |
||||
*/ |
||||
|
||||
static unsigned long not__dt_ucode_base_size[2] |
||||
__attribute__((section(".ucode"))) = {1, 2}; |
Binary file not shown.
@ -0,0 +1,15 @@ |
||||
/*
|
||||
* Copyright (c) 2016 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
* |
||||
* Simple program to create a _dt_ucode_base_size symbol which can be read |
||||
* by 'nm'. This is used by binman tests. |
||||
* |
||||
* Build with: |
||||
* cc -march=i386 -m32 -o u_boot_ucode_ptr -T u_boot_ucode_ptr.lds -nostdlib \
|
||||
u_boot_ucode_ptr.c |
||||
*/ |
||||
|
||||
static unsigned long _dt_ucode_base_size[2] |
||||
__attribute__((section(".ucode"))) = {1, 2}; |
@ -0,0 +1,18 @@ |
||||
/* |
||||
* Copyright (c) 2016 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") |
||||
OUTPUT_ARCH(i386) |
||||
ENTRY(_start) |
||||
|
||||
SECTIONS |
||||
{ |
||||
. = 0xfffffdf0; |
||||
_start = .; |
||||
.ucode : { |
||||
*(.ucode) |
||||
} |
||||
} |
Loading…
Reference in new issue