Merge git://git.denx.de/u-boot-dm

This commit is contained in:
Tom Rini 2018-08-03 10:08:13 -04:00
commit a30691a538
98 changed files with 3014 additions and 350 deletions

View file

@ -8,7 +8,7 @@
filename = "spl/sunxi-spl.bin"; filename = "spl/sunxi-spl.bin";
}; };
u-boot-img { u-boot-img {
pos = <CONFIG_SPL_PAD_TO>; offset = <CONFIG_SPL_PAD_TO>;
}; };
}; };
}; };

View file

@ -15,7 +15,7 @@
u-boot-spl { u-boot-spl {
}; };
u-boot { u-boot {
pos = <(U_BOOT_OFFSET)>; offset = <(U_BOOT_OFFSET)>;
}; };
}; };
@ -26,7 +26,7 @@
u-boot-spl { u-boot-spl {
}; };
u-boot { u-boot {
pos = <(U_BOOT_OFFSET)>; offset = <(U_BOOT_OFFSET)>;
}; };
}; };
@ -36,7 +36,7 @@
u-boot-spl { u-boot-spl {
}; };
u-boot-nodtb { u-boot-nodtb {
pos = <(U_BOOT_OFFSET)>; offset = <(U_BOOT_OFFSET)>;
}; };
}; };
}; };

View file

@ -11,7 +11,7 @@
binman { binman {
filename = "u-boot.rom"; filename = "u-boot.rom";
end-at-4gb; end-at-4gb;
sort-by-pos; sort-by-offset;
pad-byte = <0xff>; pad-byte = <0xff>;
size = <CONFIG_ROM_SIZE>; size = <CONFIG_ROM_SIZE>;
#ifdef CONFIG_HAVE_INTEL_ME #ifdef CONFIG_HAVE_INTEL_ME
@ -24,18 +24,18 @@
#endif #endif
#ifdef CONFIG_SPL #ifdef CONFIG_SPL
u-boot-spl-with-ucode-ptr { u-boot-spl-with-ucode-ptr {
pos = <CONFIG_SPL_TEXT_BASE>; offset = <CONFIG_SPL_TEXT_BASE>;
}; };
u-boot-dtb-with-ucode2 { u-boot-dtb-with-ucode2 {
type = "u-boot-dtb-with-ucode"; type = "u-boot-dtb-with-ucode";
}; };
u-boot { u-boot {
pos = <0xfff00000>; offset = <0xfff00000>;
}; };
#else #else
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {
pos = <CONFIG_SYS_TEXT_BASE>; offset = <CONFIG_SYS_TEXT_BASE>;
}; };
#endif #endif
u-boot-dtb-with-ucode { u-boot-dtb-with-ucode {
@ -45,45 +45,45 @@
}; };
#ifdef CONFIG_HAVE_MRC #ifdef CONFIG_HAVE_MRC
intel-mrc { intel-mrc {
pos = <CONFIG_X86_MRC_ADDR>; offset = <CONFIG_X86_MRC_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_HAVE_FSP #ifdef CONFIG_HAVE_FSP
intel-fsp { intel-fsp {
filename = CONFIG_FSP_FILE; filename = CONFIG_FSP_FILE;
pos = <CONFIG_FSP_ADDR>; offset = <CONFIG_FSP_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_HAVE_CMC #ifdef CONFIG_HAVE_CMC
intel-cmc { intel-cmc {
filename = CONFIG_CMC_FILE; filename = CONFIG_CMC_FILE;
pos = <CONFIG_CMC_ADDR>; offset = <CONFIG_CMC_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_HAVE_VGA_BIOS #ifdef CONFIG_HAVE_VGA_BIOS
intel-vga { intel-vga {
filename = CONFIG_VGA_BIOS_FILE; filename = CONFIG_VGA_BIOS_FILE;
pos = <CONFIG_VGA_BIOS_ADDR>; offset = <CONFIG_VGA_BIOS_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_HAVE_VBT #ifdef CONFIG_HAVE_VBT
intel-vbt { intel-vbt {
filename = CONFIG_VBT_FILE; filename = CONFIG_VBT_FILE;
pos = <CONFIG_VBT_ADDR>; offset = <CONFIG_VBT_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_HAVE_REFCODE #ifdef CONFIG_HAVE_REFCODE
intel-refcode { intel-refcode {
pos = <CONFIG_X86_REFCODE_ADDR>; offset = <CONFIG_X86_REFCODE_ADDR>;
}; };
#endif #endif
#ifdef CONFIG_SPL #ifdef CONFIG_SPL
x86-start16-spl { x86-start16-spl {
pos = <CONFIG_SYS_X86_START16>; offset = <CONFIG_SYS_X86_START16>;
}; };
#else #else
x86-start16 { x86-start16 {
pos = <CONFIG_SYS_X86_START16>; offset = <CONFIG_SYS_X86_START16>;
}; };
#endif #endif
}; };

View file

@ -34,7 +34,7 @@ DECLARE_GLOBAL_DATA_PTR;
u32 *boot_params_ptr = NULL; u32 *boot_params_ptr = NULL;
/* See spl.h for information about this */ /* See spl.h for information about this */
binman_sym_declare(ulong, u_boot_any, pos); binman_sym_declare(ulong, u_boot_any, image_pos);
/* Define board data structure */ /* Define board data structure */
static bd_t bdata __attribute__ ((section(".data"))); static bd_t bdata __attribute__ ((section(".data")));
@ -129,7 +129,7 @@ __weak void spl_board_prepare_for_boot(void)
void spl_set_header_raw_uboot(struct spl_image_info *spl_image) void spl_set_header_raw_uboot(struct spl_image_info *spl_image)
{ {
ulong u_boot_pos = binman_sym(ulong, u_boot_any, pos); ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos);
spl_image->size = CONFIG_SYS_MONITOR_LEN; spl_image->size = CONFIG_SYS_MONITOR_LEN;

View file

@ -49,7 +49,7 @@ static int spl_ram_load_image(struct spl_image_info *spl_image,
load.read = spl_ram_load_read; load.read = spl_ram_load_read;
spl_load_simple_fit(spl_image, &load, 0, header); spl_load_simple_fit(spl_image, &load, 0, header);
} else { } else {
ulong u_boot_pos = binman_sym(ulong, u_boot_any, pos); ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos);
debug("Legacy image\n"); debug("Legacy image\n");
/* /*

View file

@ -60,7 +60,7 @@ struct spl_load_info {
* image is found. For * example if u-boot.img is used we don't check that * image is found. For * example if u-boot.img is used we don't check that
* spl_parse_image_header() can parse a valid header. * spl_parse_image_header() can parse a valid header.
*/ */
binman_sym_extern(ulong, u_boot_any, pos); binman_sym_extern(ulong, u_boot_any, image_pos);
/** /**
* spl_load_simple_fit() - Loads a fit image from a device. * spl_load_simple_fit() - Loads a fit image from a device.

View file

@ -238,7 +238,7 @@ below:
filename = "spl/sunxi-spl.bin"; filename = "spl/sunxi-spl.bin";
}; };
u-boot { u-boot {
pos = <CONFIG_SPL_PAD_TO>; offset = <CONFIG_SPL_PAD_TO>;
}; };
}; };
@ -257,7 +257,7 @@ 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. 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 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. specify the start offset of an entry using the 'offset' property.
Note that due to a device tree requirement, all entries must have a unique 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 name. If you want to put the same binary in the image multiple times, you can
@ -265,14 +265,15 @@ use any unique name, with the 'type' property providing the type.
The attributes supported for entries are described below. The attributes supported for entries are described below.
pos: offset:
This sets the position of an entry within the image. The first byte This sets the offset of an entry within the image or section containing
of the image is normally at position 0. If 'pos' is not provided, it. The first byte of the image is normally at offset 0. If 'offset' is
binman sets it to the end of the previous region, or the start of not provided, binman sets it to the end of the previous region, or the
the image's entry area (normally 0) if there is no previous region. start of the image's entry area (normally 0) if there is no previous
region.
align: align:
This sets the alignment of the entry. The entry position is adjusted This sets the alignment of the entry. The entry offset is adjusted
so that the entry starts on an aligned boundary within the image. For 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 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 boundary. Alignment shold be a power of 2. If 'align' is not
@ -316,12 +317,18 @@ type:
possible to use any name, and then add (for example) 'type = "u-boot"' possible to use any name, and then add (for example) 'type = "u-boot"'
to specify the type. to specify the type.
pos-unset: offset-unset:
Indicates that the position of this entry should not be set by placing Indicates that the offset of this entry should not be set by placing
it immediately after the entry before. Instead, is set by another it immediately after the entry before. Instead, is set by another
entry which knows where this entry should go. When this boolean entry which knows where this entry should go. When this boolean
property is present, binman will give an error if another entry does property is present, binman will give an error if another entry does
not set the position (with the GetPositions() method). not set the offset (with the GetOffsets() method).
image-pos:
This cannot be set on entry (or at least it is ignored if it is), but
with the -u option, binman will set it to the absolute image position
for each entry. This makes it easy to find out exactly where the entry
ended up in the image, regardless of parent sections, etc.
The attributes supported for images are described below. Several are similar The attributes supported for images are described below. Several are similar
@ -338,7 +345,7 @@ align-size:
pad-before: pad-before:
This sets the padding before the image entries. The first entry will This sets the padding before the image entries. The first entry will
be positionad after the padding. This defaults to 0. be positioned after the padding. This defaults to 0.
pad-after: pad-after:
This sets the padding after the image entries. The padding will be This sets the padding after the image entries. The padding will be
@ -351,15 +358,15 @@ pad-byte:
filename: filename:
This specifies the image filename. It defaults to 'image.bin'. This specifies the image filename. It defaults to 'image.bin'.
sort-by-pos: sort-by-offset:
This causes binman to reorder the entries as needed to make sure they 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 are in increasing positional order. This can be used when your entry
order may not match the positional order. A common situation is where order may not match the positional order. A common situation is where
the 'pos' properties are set by CONFIG options, so their ordering is the 'offset' properties are set by CONFIG options, so their ordering is
not known a priori. not known a priori.
This is a boolean property so needs no value. To enable it, add a This is a boolean property so needs no value. To enable it, add a
line 'sort-by-pos;' to your description. line 'sort-by-offset;' to your description.
multiple-images: multiple-images:
Normally only a single image is generated. To create more than one Normally only a single image is generated. To create more than one
@ -383,11 +390,11 @@ multiple-images:
}; };
end-at-4gb: end-at-4gb:
For x86 machines the ROM positions start just before 4GB and extend For x86 machines the ROM offsets start just before 4GB and extend
up so that the image finished at the 4GB boundary. This boolean up so that the image finished at the 4GB boundary. This boolean
option can be enabled to support this. The image size must be option can be enabled to support this. The image size must be
provided so that binman knows when the image should start. For an provided so that binman knows when the image should start. For an
8MB ROM, the position of the first entry would be 0xfff80000 with 8MB ROM, the offset of the first entry would be 0xfff80000 with
this option, instead of 0 without this option. this option, instead of 0 without this option.
@ -446,6 +453,15 @@ name-prefix:
distinguish binaries with otherwise identical names. distinguish binaries with otherwise identical names.
Entry Documentation
-------------------
For details on the various entry types supported by binman and how to use them,
see README.entries. This is generated from the source code using:
binman -E >tools/binman/README.entries
Special properties Special properties
------------------ ------------------
@ -463,7 +479,7 @@ Order of image creation
Image creation proceeds in the following order, for each entry in the image. Image creation proceeds in the following order, for each entry in the image.
1. AddMissingProperties() - binman can add calculated values to the device 1. AddMissingProperties() - binman can add calculated values to the device
tree as part of its processing, for example the position and size of each tree as part of its processing, for example the offset and size of each
entry. This method adds any properties associated with this, expanding the entry. This method adds any properties associated with this, expanding the
device tree as needed. These properties can have placeholder values which are device tree as needed. These properties can have placeholder values which are
set later by SetCalculatedProperties(). By that stage the size of sections set later by SetCalculatedProperties(). By that stage the size of sections
@ -486,15 +502,15 @@ functions must return True when they have read the contents. Binman will
retry calling the functions a few times if False is returned, allowing retry calling the functions a few times if False is returned, allowing
dependencies between the contents of different entries. dependencies between the contents of different entries.
4. GetEntryPositions() - calls Entry.GetPositions() for each entry. This can 4. GetEntryOffsets() - calls Entry.GetOffsets() for each entry. This can
return a dict containing entries that need updating. The key should be the 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 entry name and the value is a tuple (offset, size). This allows an entry to
provide the position and size for other entries. The default implementation provide the offset and size for other entries. The default implementation
of GetEntryPositions() returns {}. of GetEntryOffsets() returns {}.
5. PackEntries() - calls Entry.Pack() which figures out the position and 5. PackEntries() - calls Entry.Pack() which figures out the offset and
size of an entry. The 'current' image position is passed in, and the function size of an entry. The 'current' image offset is passed in, and the function
returns the position immediately after the entry being packed. The default returns the offset immediately after the entry being packed. The default
implementation of Pack() is usually sufficient. implementation of Pack() is usually sufficient.
6. CheckSize() - checks that the contents of all the entries fits within 6. CheckSize() - checks that the contents of all the entries fits within
@ -505,16 +521,16 @@ large enough to hold all the entries.
outside the image. outside the image.
8. SetCalculatedProperties() - update any calculated properties in the device 8. SetCalculatedProperties() - update any calculated properties in the device
tree. This sets the correct 'pos' and 'size' vaues, for example. tree. This sets the correct 'offset' and 'size' vaues, for example.
9. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. 9. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry.
The default implementatoin does nothing. This can be overriden to adjust the 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 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 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. stage the offset and size of entries should not be adjusted.
10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary. 10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
See 'Access to binman entry positions at run time' below for a description of See 'Access to binman entry offsets at run time' below for a description of
what happens in this stage. what happens in this stage.
11. BuildImage() - builds the image and writes it to a file. This is the final 11. BuildImage() - builds the image and writes it to a file. This is the final
@ -549,8 +565,8 @@ the 'warning' line in scripts/Makefile.lib to see what it has found:
# u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw)
Access to binman entry positions at run time Access to binman entry offsets at run time (symbols)
-------------------------------------------- ----------------------------------------------------
Binman assembles images and determines where each entry is placed in the image. Binman assembles images and determines where each entry is placed in the image.
This information may be useful to U-Boot at run time. For example, in SPL it This information may be useful to U-Boot at run time. For example, in SPL it
@ -560,15 +576,15 @@ when SPL is finished.
Binman allows you to declare symbols in the SPL image which are filled in Binman allows you to declare symbols in the SPL image which are filled in
with their correct values during the build. For example: with their correct values during the build. For example:
binman_sym_declare(ulong, u_boot_any, pos); binman_sym_declare(ulong, u_boot_any, offset);
declares a ulong value which will be assigned to the position of any U-Boot declares a ulong value which will be assigned to the offset of any U-Boot
image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image.
You can access this value with something like: You can access this value with something like:
ulong u_boot_pos = binman_sym(ulong, u_boot_any, pos); ulong u_boot_offset = binman_sym(ulong, u_boot_any, offset);
Thus u_boot_pos will be set to the position of U-Boot in memory, assuming that Thus u_boot_offset will be set to the offset of U-Boot in memory, assuming that
the whole image has been loaded, or is available in flash. You can then jump to the whole image has been loaded, or is available in flash. You can then jump to
that address to start U-Boot. that address to start U-Boot.
@ -576,25 +592,58 @@ At present this feature is only supported in SPL. In principle it is possible
to fill in such symbols in U-Boot proper, as well. to fill in such symbols in U-Boot proper, as well.
Access to binman entry offsets at run time (fdt)
------------------------------------------------
Binman can update the U-Boot FDT to include the final position and size of
each entry in the images it processes. The option to enable this is -u and it
causes binman to make sure that the 'offset', 'image-pos' and 'size' properties
are set correctly for every entry. Since it is not necessary to specify these in
the image definition, binman calculates the final values and writes these to
the device tree. These can be used by U-Boot at run-time to find the location
of each entry.
Map files Map files
--------- ---------
The -m option causes binman to output a .map file for each image that it The -m option causes binman to output a .map file for each image that it
generates. This shows the position and size of each entry. For example: generates. This shows the offset and size of each entry. For example:
Position Size Name Offset Size Name
00000000 00000028 main-section
00000000 00000010 section@0 00000000 00000010 section@0
00000000 00000004 u-boot 00000000 00000004 u-boot
00000010 00000010 section@1 00000010 00000010 section@1
00000000 00000004 u-boot 00000000 00000004 u-boot
This shows a hierarchical image with two sections, each with a single entry. The This shows a hierarchical image with two sections, each with a single entry. The
positions of the sections are absolute hex byte offsets within the image. The offsets of the sections are absolute hex byte offsets within the image. The
positions of the entries are relative to their respective sections. The size of offsets of the entries are relative to their respective sections. The size of
each entry is also shown, in bytes (hex). The indentation shows the entries each entry is also shown, in bytes (hex). The indentation shows the entries
nested inside their sections. nested inside their sections.
Passing command-line arguments to entries
-----------------------------------------
Sometimes it is useful to pass binman the value of an entry property from the
command line. For example some entries need access to files and it is not
always convenient to put these filenames in the image definition (device tree).
The-a option supports this:
-a<prop>=<value>
where
<prop> is the property to set
<value> is the value to set it to
Not all properties can be provided this way. Only some entries support it,
typically for filenames.
Code coverage Code coverage
------------- -------------
@ -623,7 +672,7 @@ Entry properties are documented in entry.py. The entry subclasses are free
to change the values of properties to support special behaviour. For example, 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. 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 Entry classes can adjust other entries. For example, an entry that knows
where other entries should be positioned can set up those entries' positions where other entries should be positioned can set up those entries' offsets
so they don't need to be set in the binman decription. It can also adjust so they don't need to be set in the binman decription. It can also adjust
entry contents. entry contents.

585
tools/binman/README.entries Normal file
View file

@ -0,0 +1,585 @@
Binman Entry Documentation
===========================
This file describes the entry types supported by binman. These entry types can
be placed in an image one by one to build up a final firmware image. It is
fairly easy to create new entry types. Just add a new file to the 'etype'
directory. You can use the existing entries as examples.
Note that some entries are subclasses of others, using and extending their
features to produce new behaviours.
Entry: blob: Entry containing an arbitrary binary blob
------------------------------------------------------
Note: This should not be used by itself. It is normally used as a parent
class by other entry types.
Properties / Entry arguments:
- filename: Filename of file to read into entry
This entry reads data from a file and places it in the entry. The
default filename is often specified specified by the subclass. See for
example the 'u_boot' entry which provides the filename 'u-boot.bin'.
Entry: blob-named-by-arg: A blob entry which gets its filename property from its subclass
-----------------------------------------------------------------------------------------
Properties / Entry arguments:
- <xxx>-path: Filename containing the contents of this entry (optional,
defaults to 0)
where <xxx> is the blob_fname argument to the constructor.
This entry cannot be used directly. Instead, it is used as a parent class
for another entry, which defined blob_fname. This parameter is used to
set the entry-arg or property containing the filename. The entry-arg or
property is in turn used to set the actual filename.
See cros_ec_rw for an example of this.
Entry: cros-ec-rw: A blob entry which contains a Chromium OS read-write EC image
--------------------------------------------------------------------------------
Properties / Entry arguments:
- cros-ec-rw-path: Filename containing the EC image
This entry holds a Chromium OS EC (embedded controller) image, for use in
updating the EC on startup via software sync.
Entry: fill: An entry which is filled to a particular byte value
----------------------------------------------------------------
Properties / Entry arguments:
- fill-byte: Byte to use to fill the entry
Note that the size property must be set since otherwise this entry does not
know how large it should be.
You can often achieve the same effect using the pad-byte property of the
overall image, in that the space between entries will then be padded with
that byte. But this entry is sometimes useful for explicitly setting the
byte value of a region.
Entry: fmap: An entry which contains an Fmap section
----------------------------------------------------
Properties / Entry arguments:
None
FMAP is a simple format used by flashrom, an open-source utility for
reading and writing the SPI flash, typically on x86 CPUs. The format
provides flashrom with a list of areas, so it knows what it in the flash.
It can then read or write just a single area, instead of the whole flash.
The format is defined by the flashrom project, in the file lib/fmap.h -
see www.flashrom.org/Flashrom for more information.
When used, this entry will be populated with an FMAP which reflects the
entries in the current image. Note that any hierarchy is squashed, since
FMAP does not support this.
Entry: gbb: An entry which contains a Chromium OS Google Binary Block
---------------------------------------------------------------------
Properties / Entry arguments:
- hardware-id: Hardware ID to use for this build (a string)
- keydir: Directory containing the public keys to use
- bmpblk: Filename containing images used by recovery
Chromium OS uses a GBB to store various pieces of information, in particular
the root and recovery keys that are used to verify the boot process. Some
more details are here:
https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
but note that the page dates from 2013 so is quite out of date. See
README.chromium for how to obtain the required keys and tools.
Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file
-------------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains microcode for some devices in a special format. An
example filename is 'Microcode/C0_22211.BIN'.
See README.x86 for information about x86 binary blobs.
Entry: intel-descriptor: Intel flash descriptor block (4KB)
-----------------------------------------------------------
Properties / Entry arguments:
filename: Filename of file containing the descriptor. This is typically
a 4KB binary file, sometimes called 'descriptor.bin'
This entry is placed at the start of flash and provides information about
the SPI flash regions. In particular it provides the base address and
size of the ME (Management Engine) region, allowing us to place the ME
binary in the right place.
With this entry in your image, the position of the 'intel-me' entry will be
fixed in the image, which avoids you needed to specify an offset for that
region. This is useful, because it is not possible to change the position
of the ME region without updating the descriptor.
See README.x86 for information about x86 binary blobs.
Entry: intel-fsp: Entry containing an Intel Firmware Support Package (FSP) file
-------------------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains binary blobs which are used on some devices to make the
platform work. U-Boot executes this code since it is not possible to set up
the hardware using U-Boot open-source code. Documentation is typically not
available in sufficient detail to allow this.
An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd'
See README.x86 for information about x86 binary blobs.
Entry: intel-me: Entry containing an Intel Management Engine (ME) file
----------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code used by the SoC that is required to make it work.
The Management Engine is like a background task that runs things that are
not clearly documented, but may include keyboard, deplay and network
access. For platform that use ME it is not possible to disable it. U-Boot
does not directly execute code in the ME binary.
A typical filename is 'me.bin'.
See README.x86 for information about x86 binary blobs.
Entry: intel-mrc: Entry containing an Intel Memory Reference Code (MRC) file
----------------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code for setting up the SDRAM on some Intel systems. This
is executed by U-Boot when needed early during startup. A typical filename
is 'mrc.bin'.
See README.x86 for information about x86 binary blobs.
Entry: intel-vbt: Entry containing an Intel Video BIOS Table (VBT) file
-----------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code that sets up the integrated graphics subsystem on
some Intel SoCs. U-Boot executes this when the display is started up.
See README.x86 for information about Intel binary blobs.
Entry: intel-vga: Entry containing an Intel Video Graphics Adaptor (VGA) file
-----------------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code that sets up the integrated graphics subsystem on
some Intel SoCs. U-Boot executes this when the display is started up.
This is similar to the VBT file but in a different format.
See README.x86 for information about Intel binary blobs.
Entry: section: Entry that contains other entries
-------------------------------------------------
Properties / Entry arguments: (see binman README for more information)
- size: Size of section in bytes
- align-size: Align size to a particular power of two
- pad-before: Add padding before the entry
- pad-after: Add padding after the entry
- pad-byte: Pad byte to use when padding
- sort-by-offset: Reorder the entries by offset
- end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
- name-prefix: Adds a prefix to the name of every entry in the section
when writing out the map
A section is an entry which can contain other entries, thus allowing
hierarchical images to be created. See 'Sections and hierarchical images'
in the binman README for more information.
Entry: text: An entry which contains text
-----------------------------------------
The text can be provided either in the node itself or by a command-line
argument. There is a level of indirection to allow multiple text strings
and sharing of text.
Properties / Entry arguments:
text-label: The value of this string indicates the property / entry-arg
that contains the string to place in the entry
<xxx> (actual name is the value of text-label): contains the string to
place in the entry.
Example node:
text {
size = <50>;
text-label = "message";
};
You can then use:
binman -amessage="this is my message"
and binman will insert that string into the entry.
It is also possible to put the string directly in the node:
text {
size = <8>;
text-label = "message";
message = "a message directly in the node"
};
The text is not itself nul-terminated. This can be achieved, if required,
by setting the size of the entry to something larger than the text.
Entry: u-boot: U-Boot flat binary
---------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.bin (default 'u-boot.bin')
This is the U-Boot binary, containing relocation information to allow it
to relocate itself at runtime. The binary typically includes a device tree
blob at the end of it. Use u_boot_nodtb if you want to package the device
tree separately.
U-Boot can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (fdt)'
in the binman README for more information.
Entry: u-boot-dtb: U-Boot device tree
-------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'u-boot.dtb')
This is the U-Boot device tree, containing configuration information for
U-Boot. U-Boot needs this to know what devices are present and which drivers
to activate.
Entry: u-boot-dtb-with-ucode: A U-Boot device tree file, with the microcode removed
-----------------------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'u-boot.dtb')
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry provides the U-Boot device-tree file, which
contains the microcode. If the microcode is not being collated into one
place then the offset and size of the microcode is recorded by this entry,
for use by u_boot_with_ucode_ptr. If it is being collated, then this
entry deletes the microcode from the device tree (to save space) and makes
it available to u_boot_ucode.
Entry: u-boot-img: U-Boot legacy image
--------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.img (default 'u-boot.img')
This is the U-Boot binary as a packaged image, in legacy format. It has a
header which allows it to be loaded at the correct address for execution.
You should use FIT (Flat Image Tree) instead of the legacy image for new
applications.
Entry: u-boot-nodtb: U-Boot flat binary without device tree appended
--------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin')
This is the U-Boot binary, containing relocation information to allow it
to relocate itself at runtime. It does not include a device tree blob at
the end of it so normally cannot work without it. You can add a u_boot_dtb
entry after this one, or use a u_boot entry instead (which contains both
U-Boot and the device tree).
Entry: u-boot-spl: U-Boot SPL binary
------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin')
This is the U-Boot SPL (Secondary Program Loader) binary. This is a small
binary which loads before U-Boot proper, typically into on-chip SRAM. It is
responsible for locating, loading and jumping to U-Boot. Note that SPL is
not relocatable so must be loaded to the correct address in SRAM, or written
to run from the correct address if direct flash execution is possible (e.g.
on x86 devices).
SPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up symbols to write into the SPL binary.
Entry: u-boot-spl-bss-pad: U-Boot SPL binary padded with a BSS region
---------------------------------------------------------------------
Properties / Entry arguments:
None
This is similar to u_boot_spl except that padding is added after the SPL
binary to cover the BSS (Block Started by Symbol) region. This region holds
the various used by SPL. It is set to 0 by SPL when it starts up. If you
want to append data to the SPL image (such as a device tree file), you must
pad out the BSS region to avoid the data overlapping with U-Boot variables.
This entry is useful in that case. It automatically pads out the entry size
to cover both the code, data and BSS.
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up the BSS address.
Entry: u-boot-spl-dtb: U-Boot SPL device tree
---------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb')
This is the SPL device tree, containing configuration information for
SPL. SPL needs this to know what devices are present and which drivers
to activate.
Entry: u-boot-spl-nodtb: SPL binary without device tree appended
----------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of spl/u-boot-spl-nodtb.bin (default
'spl/u-boot-spl-nodtb.bin')
This is the U-Boot SPL binary, It does not include a device tree blob at
the end of it so may not be able to work without it, assuming SPL needs
a device tree to operation on your platform. You can add a u_boot_spl_dtb
entry after this one, or use a u_boot_spl entry instead (which contains
both SPL and the device tree).
Entry: u-boot-spl-with-ucode-ptr: U-Boot SPL with embedded microcode pointer
----------------------------------------------------------------------------
See Entry_u_boot_ucode for full details of the entries involved in this
process.
Entry: u-boot-tpl: U-Boot TPL binary
------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin')
This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small
binary which loads before SPL, typically into on-chip SRAM. It is
responsible for locating, loading and jumping to SPL, the next-stage
loader. Note that SPL is not relocatable so must be loaded to the correct
address in SRAM, or written to run from the correct address if direct
flash execution is possible (e.g. on x86 devices).
SPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
binman uses that to look up symbols to write into the TPL binary.
Entry: u-boot-tpl-dtb: U-Boot TPL device tree
---------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb')
This is the TPL device tree, containing configuration information for
TPL. TPL needs this to know what devices are present and which drivers
to activate.
Entry: u-boot-ucode: U-Boot microcode block
-------------------------------------------
Properties / Entry arguments:
None
The contents of this entry are filled in automatically by other entries
which must also be in the image.
U-Boot on x86 needs a single block of microcode. This is collected from
the various microcode update nodes in the device tree. It is also unable
to read the microcode from the device tree on platforms that use FSP
(Firmware Support Package) binaries, because the API requires that the
microcode is supplied before there is any SRAM available to use (i.e.
the FSP sets up the SRAM / cache-as-RAM but does so in the call that
requires the microcode!). To keep things simple, all x86 platforms handle
microcode the same way in U-Boot (even non-FSP platforms). This is that
a table is placed at _dt_ucode_base_size containing the base address and
size of the microcode. This is either passed to the FSP (for FSP
platforms), or used to set up the microcode (for non-FSP platforms).
This all happens in the build system since it is the only way to get
the microcode into a single blob and accessible without SRAM.
There are two cases to handle. If there is only one microcode blob in
the device tree, then the ucode pointer it set to point to that. This
entry (u-boot-ucode) is empty. If there is more than one update, then
this entry holds the concatenation of all updates, and the device tree
entry (u-boot-dtb-with-ucode) is updated to remove the microcode. This
last step ensures that that the microcode appears in one contiguous
block in the image and is not unnecessarily duplicated in the device
tree. It is referred to as 'collation' here.
Entry types that have a part to play in handling microcode:
Entry_u_boot_with_ucode_ptr:
Contains u-boot-nodtb.bin (i.e. U-Boot without the device tree).
It updates it with the address and size of the microcode so that
U-Boot can find it early on start-up.
Entry_u_boot_dtb_with_ucode:
Contains u-boot.dtb. It stores the microcode in a
'self.ucode_data' property, which is then read by this class to
obtain the microcode if needed. If collation is performed, it
removes the microcode from the device tree.
Entry_u_boot_ucode:
This class. If collation is enabled it reads the microcode from
the Entry_u_boot_dtb_with_ucode entry, and uses it as the
contents of this entry.
Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer
--------------------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry updates U-Boot with the offset and size of the
microcode, to allow early x86 boot code to find it without doing anything
complicated. Otherwise it is the same as the u_boot entry.
Entry: vblock: An entry which contains a Chromium OS verified boot block
------------------------------------------------------------------------
Properties / Entry arguments:
- keydir: Directory containing the public keys to use
- keyblock: Name of the key file to use (inside keydir)
- signprivate: Name of provide key file to use (inside keydir)
- version: Version number of the vblock (typically 1)
- kernelkey: Name of the kernel key to use (inside keydir)
- preamble-flags: Value of the vboot preamble flags (typically 0)
Chromium OS signs the read-write firmware and kernel, writing the signature
in this block. This allows U-Boot to verify that the next firmware stage
and kernel are genuine.
Entry: x86-start16: x86 16-bit start-up code for U-Boot
-------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of u-boot-x86-16bit.bin (default
'u-boot-x86-16bit.bin')
x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and jumping to U-Boot's entry point, which
requires 32-bit mode (for 32-bit U-Boot).
For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead.
Entry: x86-start16-spl: x86 16-bit start-up code for SPL
--------------------------------------------------------
Properties / Entry arguments:
- filename: Filename of spl/u-boot-x86-16bit-spl.bin (default
'spl/u-boot-x86-16bit-spl.bin')
x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and starting SPL, which in turn changes to
64-bit mode and jumps to U-Boot (for 64-bit U-Boot).
For 32-bit U-Boot, the 'x86_start16' entry type is used instead.

View file

@ -77,9 +77,20 @@ def RunTests(debug, args):
return 1 return 1
return 0 return 0
def GetEntryModules(include_testing=True):
"""Get a set of entry class implementations
Returns:
Set of paths to entry class filenames
"""
glob_list = glob.glob(os.path.join(our_path, 'etype/*.py'))
return set([os.path.splitext(os.path.basename(item))[0]
for item in glob_list
if include_testing or '_testing' not in item])
def RunTestCoverage(): def RunTestCoverage():
"""Run the tests and check that we get 100% coverage""" """Run the tests and check that we get 100% coverage"""
glob_list = glob.glob(os.path.join(our_path, 'etype/*.py')) glob_list = GetEntryModules(False)
all_set = set([os.path.splitext(os.path.basename(item))[0] all_set = set([os.path.splitext(os.path.basename(item))[0]
for item in glob_list if '_testing' not in item]) for item in glob_list if '_testing' not in item])
test_util.RunTestCoverage('tools/binman/binman.py', None, test_util.RunTestCoverage('tools/binman/binman.py', None,
@ -107,13 +118,8 @@ def RunBinman(options, args):
elif options.test_coverage: elif options.test_coverage:
RunTestCoverage() RunTestCoverage()
elif options.full_help: elif options.entry_docs:
pager = os.getenv('PAGER') control.WriteEntryDocs(GetEntryModules())
if not pager:
pager = 'more'
fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
'README')
command.Run(pager, fname)
else: else:
try: try:

View file

@ -25,22 +25,21 @@ class Section(object):
_size: Section size in bytes, or None if not known yet _size: Section size in bytes, or None if not known yet
_align_size: Section size alignment, or None _align_size: Section size alignment, or None
_pad_before: Number of bytes before the first entry starts. This _pad_before: Number of bytes before the first entry starts. This
effectively changes the place where entry position 0 starts effectively changes the place where entry offset 0 starts
_pad_after: Number of bytes after the last entry ends. The last _pad_after: Number of bytes after the last entry ends. The last
entry will finish on or before this boundary entry will finish on or before this boundary
_pad_byte: Byte to use to pad the section where there is no entry _pad_byte: Byte to use to pad the section where there is no entry
_sort: True if entries should be sorted by position, False if they _sort: True if entries should be sorted by offset, False if they
must be in-order in the device tree description must be in-order in the device tree description
_skip_at_start: Number of bytes before the first entry starts. These _skip_at_start: Number of bytes before the first entry starts. These
effectively adjust the starting position of entries. For example, effectively adjust the starting offset of entries. For example,
if _pad_before is 16, then the first entry would start at 16. 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 An entry with offset = 20 would in fact be written at offset 4
in the image file. in the image file.
_end_4gb: Indicates that the section ends at the 4GB boundary. This is _end_4gb: Indicates that the section ends at the 4GB boundary. This is
used for x86 images, which want to use positions such that a used for x86 images, which want to use offsets such that a memory
memory address (like 0xff800000) is the first entry position. address (like 0xff800000) is the first entry offset. This causes
This causes _skip_at_start to be set to the starting memory _skip_at_start to be set to the starting memory address.
address.
_name_prefix: Prefix to add to the name of all entries within this _name_prefix: Prefix to add to the name of all entries within this
section section
_entries: OrderedDict() of entries _entries: OrderedDict() of entries
@ -51,7 +50,9 @@ class Section(object):
import entry import entry
from entry import Entry from entry import Entry
self._name = name
self._node = node self._node = node
self._offset = 0
self._size = None self._size = None
self._align_size = None self._align_size = None
self._pad_before = 0 self._pad_before = 0
@ -76,7 +77,7 @@ class Section(object):
self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0) self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0) self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
self._sort = fdt_util.GetBool(self._node, 'sort-by-pos') self._sort = fdt_util.GetBool(self._node, 'sort-by-offset')
self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb') self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
if self._end_4gb and not self._size: if self._end_4gb and not self._size:
self._Raise("Section size must be provided when using end-at-4gb") self._Raise("Section size must be provided when using end-at-4gb")
@ -90,11 +91,21 @@ class Section(object):
entry.SetPrefix(self._name_prefix) entry.SetPrefix(self._name_prefix)
self._entries[node.name] = entry self._entries[node.name] = entry
def SetOffset(self, offset):
self._offset = offset
def AddMissingProperties(self): def AddMissingProperties(self):
"""Add new properties to the device tree as needed for this entry"""
for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props:
self._node.AddZeroProp(prop)
for entry in self._entries.values(): for entry in self._entries.values():
entry.AddMissingProperties() entry.AddMissingProperties()
def SetCalculatedProperties(self): def SetCalculatedProperties(self):
self._node.SetInt('offset', self._offset)
self._node.SetInt('size', self._size)
self._node.SetInt('image-pos', self._image_pos)
for entry in self._entries.values(): for entry in self._entries.values():
entry.SetCalculatedProperties() entry.SetCalculatedProperties()
@ -117,7 +128,7 @@ class Section(object):
"""Check that the section contents does not exceed its size, etc.""" """Check that the section contents does not exceed its size, etc."""
contents_size = 0 contents_size = 0
for entry in self._entries.values(): for entry in self._entries.values():
contents_size = max(contents_size, entry.pos + entry.size) contents_size = max(contents_size, entry.offset + entry.size)
contents_size -= self._skip_at_start contents_size -= self._skip_at_start
@ -190,39 +201,41 @@ class Section(object):
'contents: remaining %s' % todo) 'contents: remaining %s' % todo)
return True return True
def _SetEntryPosSize(self, name, pos, size): def _SetEntryOffsetSize(self, name, offset, size):
"""Set the position and size of an entry """Set the offset and size of an entry
Args: Args:
name: Entry name to update name: Entry name to update
pos: New position offset: New offset
size: New size size: New size
""" """
entry = self._entries.get(name) entry = self._entries.get(name)
if not entry: if not entry:
self._Raise("Unable to set pos/size for unknown entry '%s'" % name) self._Raise("Unable to set offset/size for unknown entry '%s'" %
entry.SetPositionSize(self._skip_at_start + pos, size) name)
entry.SetOffsetSize(self._skip_at_start + offset, size)
def GetEntryPositions(self): def GetEntryOffsets(self):
"""Handle entries that want to set the position/size of other entries """Handle entries that want to set the offset/size of other entries
This calls each entry's GetPositions() method. If it returns a list This calls each entry's GetOffsets() method. If it returns a list
of entries to update, it updates them. of entries to update, it updates them.
""" """
for entry in self._entries.values(): for entry in self._entries.values():
pos_dict = entry.GetPositions() offset_dict = entry.GetOffsets()
for name, info in pos_dict.iteritems(): for name, info in offset_dict.iteritems():
self._SetEntryPosSize(name, *info) self._SetEntryOffsetSize(name, *info)
def PackEntries(self): def PackEntries(self):
"""Pack all entries into the section""" """Pack all entries into the section"""
pos = self._skip_at_start offset = self._skip_at_start
for entry in self._entries.values(): for entry in self._entries.values():
pos = entry.Pack(pos) offset = entry.Pack(offset)
self._size = self.CheckSize()
def _SortEntries(self): def _SortEntries(self):
"""Sort entries by position""" """Sort entries by offset"""
entries = sorted(self._entries.values(), key=lambda entry: entry.pos) entries = sorted(self._entries.values(), key=lambda entry: entry.offset)
self._entries.clear() self._entries.clear()
for entry in entries: for entry in entries:
self._entries[entry._node.name] = entry self._entries[entry._node.name] = entry
@ -231,23 +244,28 @@ class Section(object):
"""Check that entries do not overlap or extend outside the section""" """Check that entries do not overlap or extend outside the section"""
if self._sort: if self._sort:
self._SortEntries() self._SortEntries()
pos = 0 offset = 0
prev_name = 'None' prev_name = 'None'
for entry in self._entries.values(): for entry in self._entries.values():
entry.CheckPosition() entry.CheckOffset()
if (entry.pos < self._skip_at_start or if (entry.offset < self._skip_at_start or
entry.pos >= self._skip_at_start + self._size): entry.offset >= self._skip_at_start + self._size):
entry.Raise("Position %#x (%d) is outside the section starting " entry.Raise("Offset %#x (%d) is outside the section starting "
"at %#x (%d)" % "at %#x (%d)" %
(entry.pos, entry.pos, self._skip_at_start, (entry.offset, entry.offset, self._skip_at_start,
self._skip_at_start)) self._skip_at_start))
if entry.pos < pos: if entry.offset < offset:
entry.Raise("Position %#x (%d) overlaps with previous entry '%s' " entry.Raise("Offset %#x (%d) overlaps with previous entry '%s' "
"ending at %#x (%d)" % "ending at %#x (%d)" %
(entry.pos, entry.pos, prev_name, pos, pos)) (entry.offset, entry.offset, prev_name, offset, offset))
pos = entry.pos + entry.size offset = entry.offset + entry.size
prev_name = entry.GetPath() prev_name = entry.GetPath()
def SetImagePos(self, image_pos):
self._image_pos = image_pos
for entry in self._entries.values():
entry.SetImagePos(image_pos)
def ProcessEntryContents(self): def ProcessEntryContents(self):
"""Call the ProcessContents() method for each entry """Call the ProcessContents() method for each entry
@ -261,18 +279,18 @@ class Section(object):
for entry in self._entries.values(): for entry in self._entries.values():
entry.WriteSymbols(self) entry.WriteSymbols(self)
def BuildSection(self, fd, base_pos): def BuildSection(self, fd, base_offset):
"""Write the section to a file""" """Write the section to a file"""
fd.seek(base_pos) fd.seek(base_offset)
fd.write(self.GetData()) fd.write(self.GetData())
def GetData(self): def GetData(self):
"""Write the section to a file""" """Get the contents of the section"""
section_data = chr(self._pad_byte) * self._size section_data = chr(self._pad_byte) * self._size
for entry in self._entries.values(): for entry in self._entries.values():
data = entry.GetData() data = entry.GetData()
base = self._pad_before + entry.pos - self._skip_at_start base = self._pad_before + entry.offset - self._skip_at_start
section_data = (section_data[:base] + data + section_data = (section_data[:base] + data +
section_data[base + len(data):]) section_data[base + len(data):])
return section_data return section_data
@ -283,15 +301,15 @@ class Section(object):
Looks up a symbol in an ELF file. Only entry types which come from an Looks up a symbol in an ELF file. Only entry types which come from an
ELF image can be used by this function. ELF image can be used by this function.
At present the only entry property supported is pos. At present the only entry property supported is offset.
Args: Args:
sym_name: Symbol name in the ELF file to look up in the format sym_name: Symbol name in the ELF file to look up in the format
_binman_<entry>_prop_<property> where <entry> is the name of _binman_<entry>_prop_<property> where <entry> is the name of
the entry and <property> is the property to find (e.g. the entry and <property> is the property to find (e.g.
_binman_u_boot_prop_pos). As a special case, you can append _binman_u_boot_prop_offset). As a special case, you can append
_any to <entry> to have it search for any matching entry. E.g. _any to <entry> to have it search for any matching entry. E.g.
_binman_u_boot_any_prop_pos will match entries called u-boot, _binman_u_boot_any_prop_offset will match entries called u-boot,
u-boot-img and u-boot-nodtb) u-boot-img and u-boot-nodtb)
optional: True if the symbol is optional. If False this function optional: True if the symbol is optional. If False this function
will raise if the symbol is not found will raise if the symbol is not found
@ -327,19 +345,64 @@ class Section(object):
print('Warning: %s' % err, file=sys.stderr) print('Warning: %s' % err, file=sys.stderr)
return None return None
raise ValueError(err) raise ValueError(err)
if prop_name == 'pos': if prop_name == 'offset':
return entry.pos return entry.offset
elif prop_name == 'image_pos':
return entry.image_pos
else: else:
raise ValueError("%s: No such property '%s'" % (msg, prop_name)) raise ValueError("%s: No such property '%s'" % (msg, prop_name))
def GetEntries(self): def GetEntries(self):
"""Get the number of entries in a section
Returns:
Number of entries in a section
"""
return self._entries return self._entries
def GetSize(self):
"""Get the size of a section in bytes
This is only meaningful if the section has a pre-defined size, or the
entries within it have been packed, so that the size has been
calculated.
Returns:
Entry size in bytes
"""
return self._size
def WriteMap(self, fd, indent): def WriteMap(self, fd, indent):
"""Write a map of the section to a .map file """Write a map of the section to a .map file
Args: Args:
fd: File to write the map to fd: File to write the map to
""" """
Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size,
self._image_pos)
for entry in self._entries.values(): for entry in self._entries.values():
entry.WriteMap(fd, indent) entry.WriteMap(fd, indent + 1)
def GetContentsByPhandle(self, phandle, source_entry):
"""Get the data contents of an entry specified by a phandle
This uses a phandle to look up a node and and find the entry
associated with it. Then it returnst he contents of that entry.
Args:
phandle: Phandle to look up (integer)
source_entry: Entry containing that phandle (used for error
reporting)
Returns:
data from associated entry (as a string), or None if not found
"""
node = self._node.GetFdt().LookupPhandle(phandle)
if not node:
source_entry.Raise("Cannot find node for phandle %d" % phandle)
for entry in self._entries.values():
if entry._node == node:
if entry.data is None:
return None
return entry.data
source_entry.Raise("Cannot find entry for node '%s'" % node.name)

View file

@ -18,6 +18,8 @@ def ParseArgs(argv):
args is a list of string arguments args is a list of string arguments
""" """
parser = OptionParser() parser = OptionParser()
parser.add_option('-a', '--entry-arg', type='string', action='append',
help='Set argument value arg=value')
parser.add_option('-b', '--board', type='string', parser.add_option('-b', '--board', type='string',
help='Board name to build') help='Board name to build')
parser.add_option('-B', '--build-dir', type='string', default='b', parser.add_option('-B', '--build-dir', type='string', default='b',
@ -26,6 +28,8 @@ def ParseArgs(argv):
help='Configuration file (.dtb) to use') help='Configuration file (.dtb) to use')
parser.add_option('-D', '--debug', action='store_true', parser.add_option('-D', '--debug', action='store_true',
help='Enabling debugging (provides a full traceback on error)') help='Enabling debugging (provides a full traceback on error)')
parser.add_option('-E', '--entry-docs', action='store_true',
help='Write out entry documentation (see README.entries)')
parser.add_option('-I', '--indir', action='append', parser.add_option('-I', '--indir', action='append',
help='Add a path to a directory to use for input files') help='Add a path to a directory to use for input files')
parser.add_option('-H', '--full-help', action='store_true', parser.add_option('-H', '--full-help', action='store_true',
@ -43,7 +47,7 @@ def ParseArgs(argv):
parser.add_option('-T', '--test-coverage', action='store_true', parser.add_option('-T', '--test-coverage', action='store_true',
default=False, help='run tests and check for 100% coverage') default=False, help='run tests and check for 100% coverage')
parser.add_option('-u', '--update-fdt', action='store_true', parser.add_option('-u', '--update-fdt', action='store_true',
default=False, help='Update the binman node with position/size info') default=False, help='Update the binman node with offset/size info')
parser.add_option('-v', '--verbosity', default=1, parser.add_option('-v', '--verbosity', default=1,
type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, ' type='int', help='Control verbosity: 0=silent, 1=progress, 3=full, '
'4=debug') '4=debug')

View file

@ -7,13 +7,12 @@
from collections import OrderedDict from collections import OrderedDict
import os import os
import re
import sys import sys
import tools import tools
import command import command
import elf import elf
import fdt
import fdt_util
from image import Image from image import Image
import tout import tout
@ -25,6 +24,9 @@ images = OrderedDict()
# 'u-boot-spl.dtb') # 'u-boot-spl.dtb')
fdt_files = {} fdt_files = {}
# Arguments passed to binman to provide arguments to entries
entry_args = {}
def _ReadImageDesc(binman_node): def _ReadImageDesc(binman_node):
"""Read the image descriptions from the /binman node """Read the image descriptions from the /binman node
@ -76,6 +78,24 @@ def GetFdt(fname):
def GetFdtPath(fname): def GetFdtPath(fname):
return fdt_files[fname]._fname return fdt_files[fname]._fname
def SetEntryArgs(args):
global entry_args
entry_args = {}
if args:
for arg in args:
m = re.match('([^=]*)=(.*)', arg)
if not m:
raise ValueError("Invalid entry arguemnt '%s'" % arg)
entry_args[m.group(1)] = m.group(2)
def GetEntryArg(name):
return entry_args.get(name)
def WriteEntryDocs(modules, test_missing=None):
from entry import Entry
Entry.WriteDocs(modules, test_missing)
def Binman(options, args): def Binman(options, args):
"""The main control code for binman """The main control code for binman
@ -111,11 +131,17 @@ def Binman(options, args):
options.indir.append(board_pathname) options.indir.append(board_pathname)
try: try:
# Import these here in case libfdt.py is not available, in which case
# the above help option still works.
import fdt
import fdt_util
tout.Init(options.verbosity) tout.Init(options.verbosity)
elf.debug = options.debug elf.debug = options.debug
try: try:
tools.SetInputDirs(options.indir) tools.SetInputDirs(options.indir)
tools.PrepareOutputDir(options.outdir, options.preserve) tools.PrepareOutputDir(options.outdir, options.preserve)
SetEntryArgs(options.entry_arg)
# Get the device tree ready by compiling it and copying the compiled # Get the device tree ready by compiling it and copying the compiled
# output into a file in our output directly. Then scan it for use # output into a file in our output directly. Then scan it for use
@ -142,7 +168,7 @@ def Binman(options, args):
# size of the device tree is correct. Later, in # size of the device tree is correct. Later, in
# SetCalculatedProperties() we will insert the correct values # SetCalculatedProperties() we will insert the correct values
# without changing the device-tree size, thus ensuring that our # without changing the device-tree size, thus ensuring that our
# entry positions remain the same. # entry offsets remain the same.
for image in images.values(): for image in images.values():
if options.update_fdt: if options.update_fdt:
image.AddMissingProperties() image.AddMissingProperties()
@ -157,10 +183,11 @@ def Binman(options, args):
# image will be reported after earlier images are already # image will be reported after earlier images are already
# completed and written, but that does not seem important. # completed and written, but that does not seem important.
image.GetEntryContents() image.GetEntryContents()
image.GetEntryPositions() image.GetEntryOffsets()
image.PackEntries() image.PackEntries()
image.CheckSize() image.CheckSize()
image.CheckEntries() image.CheckEntries()
image.SetImagePos()
if options.update_fdt: if options.update_fdt:
image.SetCalculatedProperties() image.SetCalculatedProperties()
image.ProcessEntryContents() image.ProcessEntryContents()

View file

@ -57,7 +57,9 @@ def GetSymbols(fname, patterns):
name = parts[2] name = parts[2]
syms[name] = Symbol(section, int(value, 16), int(size,16), syms[name] = Symbol(section, int(value, 16), int(size,16),
flags[1] == 'w') flags[1] == 'w')
return syms
# Sort dict by address
return OrderedDict(sorted(syms.iteritems(), key=lambda x: x[1].address))
def GetSymbolAddress(fname, sym_name): def GetSymbolAddress(fname, sym_name):
"""Get a value of a symbol from an ELF file """Get a value of a symbol from an ELF file
@ -79,9 +81,9 @@ def LookupAndWriteSymbols(elf_fname, entry, section):
"""Replace all symbols in an entry with their correct values """Replace all symbols in an entry with their correct values
The entry contents is updated so that values for referenced symbols will be The entry contents is updated so that values for referenced symbols will be
visible at run time. This is done by finding out the symbols positions in visible at run time. This is done by finding out the symbols offsets in the
the entry (using the ELF file) and replacing them with values from binman's entry (using the ELF file) and replacing them with values from binman's data
data structures. structures.
Args: Args:
elf_fname: Filename of ELF image containing the symbol information for elf_fname: Filename of ELF image containing the symbol information for

View file

@ -15,6 +15,10 @@ binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
class FakeEntry: class FakeEntry:
"""A fake Entry object, usedfor testing
This supports an entry with a given size.
"""
def __init__(self, contents_size): def __init__(self, contents_size):
self.contents_size = contents_size self.contents_size = contents_size
self.data = 'a' * contents_size self.data = 'a' * contents_size
@ -22,7 +26,14 @@ class FakeEntry:
def GetPath(self): def GetPath(self):
return 'entry_path' return 'entry_path'
class FakeSection: class FakeSection:
"""A fake Section object, used for testing
This has the minimum feature set needed to support testing elf functions.
A LookupSymbol() function is provided which returns a fake value for amu
symbol requested.
"""
def __init__(self, sym_value=1): def __init__(self, sym_value=1):
self.sym_value = sym_value self.sym_value = sym_value
@ -30,15 +41,19 @@ class FakeSection:
return 'section_path' return 'section_path'
def LookupSymbol(self, name, weak, msg): def LookupSymbol(self, name, weak, msg):
"""Fake implementation which returns the same value for all symbols"""
return self.sym_value return self.sym_value
class TestElf(unittest.TestCase): class TestElf(unittest.TestCase):
def testAllSymbols(self): def testAllSymbols(self):
"""Test that we can obtain a symbol from the ELF file"""
fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr')
syms = elf.GetSymbols(fname, []) syms = elf.GetSymbols(fname, [])
self.assertIn('.ucode', syms) self.assertIn('.ucode', syms)
def testRegexSymbols(self): def testRegexSymbols(self):
"""Test that we can obtain from the ELF file by regular expression"""
fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr')
syms = elf.GetSymbols(fname, ['ucode']) syms = elf.GetSymbols(fname, ['ucode'])
self.assertIn('.ucode', syms) self.assertIn('.ucode', syms)
@ -48,6 +63,7 @@ class TestElf(unittest.TestCase):
self.assertIn('.ucode', syms) self.assertIn('.ucode', syms)
def testMissingFile(self): def testMissingFile(self):
"""Test that a missing file is detected"""
entry = FakeEntry(10) entry = FakeEntry(10)
section = FakeSection() section = FakeSection()
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
@ -56,6 +72,7 @@ class TestElf(unittest.TestCase):
str(e.exception)) str(e.exception))
def testOutsideFile(self): def testOutsideFile(self):
"""Test a symbol which extends outside the entry area is detected"""
entry = FakeEntry(10) entry = FakeEntry(10)
section = FakeSection() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
@ -65,6 +82,11 @@ class TestElf(unittest.TestCase):
'is a', str(e.exception)) 'is a', str(e.exception))
def testMissingImageStart(self): def testMissingImageStart(self):
"""Test that we detect a missing __image_copy_start symbol
This is needed to mark the start of the image. Without it we cannot
locate the offset of a binman symbol within the image.
"""
entry = FakeEntry(10) entry = FakeEntry(10)
section = FakeSection() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
@ -72,6 +94,11 @@ class TestElf(unittest.TestCase):
None) None)
def testBadSymbolSize(self): def testBadSymbolSize(self):
"""Test that an attempt to use an 8-bit symbol are detected
Only 32 and 64 bits are supported, since we need to store an offset
into the image.
"""
entry = FakeEntry(10) entry = FakeEntry(10)
section = FakeSection() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size')
@ -81,6 +108,11 @@ class TestElf(unittest.TestCase):
str(e.exception)) str(e.exception))
def testNoValue(self): def testNoValue(self):
"""Test the case where we have no value for the symbol
This should produce -1 values for all thress symbols, taking up the
first 16 bytes of the image.
"""
entry = FakeEntry(20) entry = FakeEntry(20)
section = FakeSection(sym_value=None) section = FakeSection(sym_value=None)
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
@ -88,6 +120,7 @@ class TestElf(unittest.TestCase):
self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data) self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data)
def testDebug(self): def testDebug(self):
"""Check that enabling debug in the elf module produced debug output"""
elf.debug = True elf.debug = True
entry = FakeEntry(20) entry = FakeEntry(20)
section = FakeSection() section = FakeSection()

View file

@ -6,6 +6,8 @@
from __future__ import print_function from __future__ import print_function
from collections import namedtuple
# importlib was introduced in Python 2.7 but there was a report of it not # 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: # working in 2.7.12, so we work around this:
# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html # http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
@ -16,6 +18,7 @@ except:
have_importlib = False have_importlib = False
import fdt_util import fdt_util
import control
import os import os
import sys import sys
import tools import tools
@ -24,6 +27,12 @@ modules = {}
our_path = os.path.dirname(os.path.realpath(__file__)) our_path = os.path.dirname(os.path.realpath(__file__))
# An argument which can be passed to entries on the command line, in lieu of
# device-tree properties.
EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
class Entry(object): class Entry(object):
"""An Entry in the section """An Entry in the section
@ -36,14 +45,15 @@ class Entry(object):
Entry. Entry.
Attributes: Attributes:
section: The section containing this entry section: Section object containing this entry
node: The node that created this entry node: The node that created this entry
pos: Absolute position of entry within the section, None if not known offset: Offset of entry within the section, None if not known yet (in
which case it will be calculated by Pack())
size: Entry size in bytes, None if not known size: Entry size in bytes, None if not known
contents_size: Size of contents in bytes, 0 by default contents_size: Size of contents in bytes, 0 by default
align: Entry start position alignment, or None align: Entry start offset alignment, or None
align_size: Entry size alignment, or None align_size: Entry size alignment, or None
align_end: Entry end position alignment, or None align_end: Entry end offset alignment, or None
pad_before: Number of pad bytes before the contents, 0 if 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 pad_after: Number of pad bytes after the contents, 0 if none
data: Contents of entry (string of bytes) data: Contents of entry (string of bytes)
@ -53,34 +63,33 @@ class Entry(object):
self.etype = etype self.etype = etype
self._node = node self._node = node
self.name = node and (name_prefix + node.name) or 'none' self.name = node and (name_prefix + node.name) or 'none'
self.pos = None self.offset = None
self.size = None self.size = None
self.data = '' self.data = None
self.contents_size = 0 self.contents_size = 0
self.align = None self.align = None
self.align_size = None self.align_size = None
self.align_end = None self.align_end = None
self.pad_before = 0 self.pad_before = 0
self.pad_after = 0 self.pad_after = 0
self.pos_unset = False self.offset_unset = False
self.image_pos = None
if read_node: if read_node:
self.ReadNode() self.ReadNode()
@staticmethod @staticmethod
def Create(section, node, etype=None): def Lookup(section, node_path, etype):
"""Create a new entry for a node. """Look up the entry class for a node.
Args: Args:
section: Image object containing this node section: Section object containing this node
node: Node object containing information about the entry to create node_node: Path name of Node object containing information about
etype: Entry type to use, or None to work it out (used for tests) the entry to create (used for errors)
etype: Entry type to use
Returns: Returns:
A new Entry object of the correct type (a subclass of Entry) The entry class object if found, else None
""" """
if not etype:
etype = fdt_util.GetString(node, 'type', node.name)
# Convert something like 'u-boot@0' to 'u_boot' since we are only # Convert something like 'u-boot@0' to 'u_boot' since we are only
# interested in the type. # interested in the type.
module_name = etype.replace('-', '_') module_name = etype.replace('-', '_')
@ -99,15 +108,34 @@ class Entry(object):
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
else: else:
module = __import__(module_name) module = __import__(module_name)
except ImportError: except ImportError as e:
raise ValueError("Unknown entry type '%s' in node '%s'" % raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
(etype, node.path)) (etype, node_path, module_name, e))
finally: finally:
sys.path = old_path sys.path = old_path
modules[module_name] = module modules[module_name] = module
# Look up the expected class name
return getattr(module, 'Entry_%s' % module_name)
@staticmethod
def Create(section, node, etype=None):
"""Create a new entry for a node.
Args:
section: Section object containing this node
node: Node object containing information about the entry to
create
etype: Entry type to use, or None to work it out (used for tests)
Returns:
A new Entry object of the correct type (a subclass of Entry)
"""
if not etype:
etype = fdt_util.GetString(node, 'type', node.name)
obj = Entry.Lookup(section, node.path, etype)
# Call its constructor to get the object we want. # Call its constructor to get the object we want.
obj = getattr(module, 'Entry_%s' % module_name)
return obj(section, etype, node) return obj(section, etype, node)
def ReadNode(self): def ReadNode(self):
@ -115,7 +143,9 @@ class Entry(object):
This reads all the fields we recognise from the node, ready for use. This reads all the fields we recognise from the node, ready for use.
""" """
self.pos = fdt_util.GetInt(self._node, 'pos') if 'pos' in self._node.props:
self.Raise("Please use 'offset' instead of 'pos'")
self.offset = fdt_util.GetInt(self._node, 'offset')
self.size = fdt_util.GetInt(self._node, 'size') self.size = fdt_util.GetInt(self._node, 'size')
self.align = fdt_util.GetInt(self._node, 'align') self.align = fdt_util.GetInt(self._node, 'align')
if tools.NotPowerOfTwo(self.align): if tools.NotPowerOfTwo(self.align):
@ -128,18 +158,19 @@ class Entry(object):
raise ValueError("Node '%s': Alignment size %s must be a power " raise ValueError("Node '%s': Alignment size %s must be a power "
"of two" % (self._node.path, self.align_size)) "of two" % (self._node.path, self.align_size))
self.align_end = fdt_util.GetInt(self._node, 'align-end') self.align_end = fdt_util.GetInt(self._node, 'align-end')
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset') self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
def AddMissingProperties(self): def AddMissingProperties(self):
"""Add new properties to the device tree as needed for this entry""" """Add new properties to the device tree as needed for this entry"""
for prop in ['pos', 'size']: for prop in ['offset', 'size', 'image-pos']:
if not prop in self._node.props: if not prop in self._node.props:
self._node.AddZeroProp(prop) self._node.AddZeroProp(prop)
def SetCalculatedProperties(self): def SetCalculatedProperties(self):
"""Set the value of device-tree properties calculated by binman""" """Set the value of device-tree properties calculated by binman"""
self._node.SetInt('pos', self.pos) self._node.SetInt('offset', self.offset)
self._node.SetInt('size', self.size) self._node.SetInt('size', self.size)
self._node.SetInt('image-pos', self.image_pos)
def ProcessFdt(self, fdt): def ProcessFdt(self, fdt):
return True return True
@ -190,39 +221,39 @@ class Entry(object):
# No contents by default: subclasses can implement this # No contents by default: subclasses can implement this
return True return True
def Pack(self, pos): def Pack(self, offset):
"""Figure out how to pack the entry into the section """Figure out how to pack the entry into the section
Most of the time the entries are not fully specified. There may be 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 an alignment but no size. In that case we take the size from the
contents of the entry. contents of the entry.
If an entry has no hard-coded position, it will be placed at @pos. If an entry has no hard-coded offset, it will be placed at @offset.
Once this function is complete, both the position and size of the Once this function is complete, both the offset and size of the
entry will be know. entry will be know.
Args: Args:
Current section position pointer Current section offset pointer
Returns: Returns:
New section position pointer (after this entry) New section offset pointer (after this entry)
""" """
if self.pos is None: if self.offset is None:
if self.pos_unset: if self.offset_unset:
self.Raise('No position set with pos-unset: should another ' self.Raise('No offset set with offset-unset: should another '
'entry provide this correct position?') 'entry provide this correct offset?')
self.pos = tools.Align(pos, self.align) self.offset = tools.Align(offset, self.align)
needed = self.pad_before + self.contents_size + self.pad_after needed = self.pad_before + self.contents_size + self.pad_after
needed = tools.Align(needed, self.align_size) needed = tools.Align(needed, self.align_size)
size = self.size size = self.size
if not size: if not size:
size = needed size = needed
new_pos = self.pos + size new_offset = self.offset + size
aligned_pos = tools.Align(new_pos, self.align_end) aligned_offset = tools.Align(new_offset, self.align_end)
if aligned_pos != new_pos: if aligned_offset != new_offset:
size = aligned_pos - self.pos size = aligned_offset - self.offset
new_pos = aligned_pos new_offset = aligned_offset
if not self.size: if not self.size:
self.size = size self.size = size
@ -231,21 +262,48 @@ class Entry(object):
self.Raise("Entry contents size is %#x (%d) but entry size is " self.Raise("Entry contents size is %#x (%d) but entry size is "
"%#x (%d)" % (needed, needed, self.size, self.size)) "%#x (%d)" % (needed, needed, self.size, self.size))
# Check that the alignment is correct. It could be wrong if the # Check that the alignment is correct. It could be wrong if the
# and pos or size values were provided (i.e. not calculated), but # and offset or size values were provided (i.e. not calculated), but
# conflict with the provided alignment values # conflict with the provided alignment values
if self.size != tools.Align(self.size, self.align_size): if self.size != tools.Align(self.size, self.align_size):
self.Raise("Size %#x (%d) does not match align-size %#x (%d)" % self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
(self.size, self.size, self.align_size, self.align_size)) (self.size, self.size, self.align_size, self.align_size))
if self.pos != tools.Align(self.pos, self.align): if self.offset != tools.Align(self.offset, self.align):
self.Raise("Position %#x (%d) does not match align %#x (%d)" % self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
(self.pos, self.pos, self.align, self.align)) (self.offset, self.offset, self.align, self.align))
return new_pos return new_offset
def Raise(self, msg): def Raise(self, msg):
"""Convenience function to raise an error referencing a node""" """Convenience function to raise an error referencing a node"""
raise ValueError("Node '%s': %s" % (self._node.path, msg)) raise ValueError("Node '%s': %s" % (self._node.path, msg))
def GetEntryArgsOrProps(self, props, required=False):
"""Return the values of a set of properties
Args:
props: List of EntryArg objects
Raises:
ValueError if a property is not found
"""
values = []
missing = []
for prop in props:
python_prop = prop.name.replace('-', '_')
if hasattr(self, python_prop):
value = getattr(self, python_prop)
else:
value = None
if value is None:
value = self.GetArg(prop.name, prop.datatype)
if value is None and required:
missing.append(prop.name)
values.append(value)
if missing:
self.Raise('Missing required properties/entry args: %s' %
(', '.join(missing)))
return values
def GetPath(self): def GetPath(self):
"""Get the path of a node """Get the path of a node
@ -257,13 +315,21 @@ class Entry(object):
def GetData(self): def GetData(self):
return self.data return self.data
def GetPositions(self): def GetOffsets(self):
return {} return {}
def SetPositionSize(self, pos, size): def SetOffsetSize(self, pos, size):
self.pos = pos self.offset = pos
self.size = size self.size = size
def SetImagePos(self, image_pos):
"""Set the position in the image
Args:
image_pos: Position of this entry in the image
"""
self.image_pos = image_pos + self.offset
def ProcessContents(self): def ProcessContents(self):
pass pass
@ -275,15 +341,20 @@ class Entry(object):
""" """
pass pass
def CheckPosition(self): def CheckOffset(self):
"""Check that the entry positions are correct """Check that the entry offsets are correct
This is used for entries which have extra position requirements (other This is used for entries which have extra offset requirements (other
than having to be fully inside their section). Sub-classes can implement than having to be fully inside their section). Sub-classes can implement
this function and raise if there is a problem. this function and raise if there is a problem.
""" """
pass pass
@staticmethod
def WriteMapLine(fd, indent, name, offset, size, image_pos):
print('%08x %s%08x %08x %s' % (image_pos, ' ' * indent, offset,
size, name), file=fd)
def WriteMap(self, fd, indent): def WriteMap(self, fd, indent):
"""Write a map of the entry to a .map file """Write a map of the entry to a .map file
@ -291,5 +362,97 @@ class Entry(object):
fd: File to write the map to fd: File to write the map to
indent: Curent indent level of map (0=none, 1=one level, etc.) indent: Curent indent level of map (0=none, 1=one level, etc.)
""" """
print('%s%08x %08x %s' % (' ' * indent, self.pos, self.size, self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
self.name), file=fd) self.image_pos)
def GetEntries(self):
"""Return a list of entries contained by this entry
Returns:
List of entries, or None if none. A normal entry has no entries
within it so will return None
"""
return None
def GetArg(self, name, datatype=str):
"""Get the value of an entry argument or device-tree-node property
Some node properties can be provided as arguments to binman. First check
the entry arguments, and fall back to the device tree if not found
Args:
name: Argument name
datatype: Data type (str or int)
Returns:
Value of argument as a string or int, or None if no value
Raises:
ValueError if the argument cannot be converted to in
"""
value = control.GetEntryArg(name)
if value is not None:
if datatype == int:
try:
value = int(value)
except ValueError:
self.Raise("Cannot convert entry arg '%s' (value '%s') to integer" %
(name, value))
elif datatype == str:
pass
else:
raise ValueError("GetArg() internal error: Unknown data type '%s'" %
datatype)
else:
value = fdt_util.GetDatatype(self._node, name, datatype)
return value
@staticmethod
def WriteDocs(modules, test_missing=None):
"""Write out documentation about the various entry types to stdout
Args:
modules: List of modules to include
test_missing: Used for testing. This is a module to report
as missing
"""
print('''Binman Entry Documentation
===========================
This file describes the entry types supported by binman. These entry types can
be placed in an image one by one to build up a final firmware image. It is
fairly easy to create new entry types. Just add a new file to the 'etype'
directory. You can use the existing entries as examples.
Note that some entries are subclasses of others, using and extending their
features to produce new behaviours.
''')
modules = sorted(modules)
# Don't show the test entry
if '_testing' in modules:
modules.remove('_testing')
missing = []
for name in modules:
module = Entry.Lookup(name, name, name)
docs = getattr(module, '__doc__')
if test_missing == name:
docs = None
if docs:
lines = docs.splitlines()
first_line = lines[0]
rest = [line[4:] for line in lines[1:]]
hdr = 'Entry: %s: %s' % (name.replace('_', '-'), first_line)
print(hdr)
print('-' * len(hdr))
print('\n'.join(rest))
print()
print()
else:
missing.append(name)
if missing:
raise ValueError('Documentation is missing for modules: %s' %
', '.join(missing))

View file

@ -5,7 +5,9 @@
# Entry-type module for testing purposes. Not used in real images. # Entry-type module for testing purposes. Not used in real images.
# #
from entry import Entry from collections import OrderedDict
from entry import Entry, EntryArg
import fdt_util import fdt_util
import tools import tools
@ -13,8 +15,30 @@ import tools
class Entry__testing(Entry): class Entry__testing(Entry):
"""A fake entry used for testing """A fake entry used for testing
This entry should not be used in normal images. It is a special entry with
strange features used for testing.
Properties / Entry arguments
test-str-fdt: Test string, normally in the node
test-int-fdt: Test integer, normally in the node
test-str-arg: Test string, normally in the entry arguments
test-int-arg: Test integer, normally in the entry arguments
The entry has a single 'a' byte as its contents. Operation is controlled by
a number of properties in the node, as follows:
Properties: Properties:
return_invalid_entry: Return an invalid entry from GetPositions() return-invalid-entry: Return an invalid entry from GetOffsets()
return-unknown-contents: Refuse to provide any contents (to cause a
failure)
bad-update-contents: Implement ProcessContents() incorrectly so as to
cause a failure
never-complete-process-fdt: Refund to process the FDT (to cause a
failure)
require-args: Require that all used args are present (generating an
error if not)
force-bad-datatype: Force a call to GetEntryArgsOrProps() with a bad
data type (generating an error)
""" """
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node) Entry.__init__(self, section, etype, node)
@ -24,9 +48,26 @@ class Entry__testing(Entry):
'return-unknown-contents') 'return-unknown-contents')
self.bad_update_contents = fdt_util.GetBool(self._node, self.bad_update_contents = fdt_util.GetBool(self._node,
'bad-update-contents') 'bad-update-contents')
# Set to True when the entry is ready to process the FDT.
self.process_fdt_ready = False self.process_fdt_ready = False
self.never_complete_process_fdt = fdt_util.GetBool(self._node, self.never_complete_process_fdt = fdt_util.GetBool(self._node,
'never-complete-process-fdt') 'never-complete-process-fdt')
self.require_args = fdt_util.GetBool(self._node, 'require-args')
# This should be picked up by GetEntryArgsOrProps()
self.test_existing_prop = 'existing'
self.force_bad_datatype = fdt_util.GetBool(self._node,
'force-bad-datatype')
(self.test_str_fdt, self.test_str_arg, self.test_int_fdt,
self.test_int_arg, existing) = self.GetEntryArgsOrProps([
EntryArg('test-str-fdt', str),
EntryArg('test-str-arg', str),
EntryArg('test-int-fdt', int),
EntryArg('test-int-arg', int),
EntryArg('test-existing-prop', str)], self.require_args)
if self.force_bad_datatype:
self.GetEntryArgsOrProps([EntryArg('test-bad-datatype-arg', bool)])
def ObtainContents(self): def ObtainContents(self):
if self.return_unknown_contents: if self.return_unknown_contents:
@ -35,7 +76,7 @@ class Entry__testing(Entry):
self.contents_size = len(self.data) self.contents_size = len(self.data)
return True return True
def GetPositions(self): def GetOffsets(self):
if self.return_invalid_entry : if self.return_invalid_entry :
return {'invalid-entry': [1, 2]} return {'invalid-entry': [1, 2]}
return {} return {}

View file

@ -10,6 +10,18 @@ import fdt_util
import tools import tools
class Entry_blob(Entry): class Entry_blob(Entry):
"""Entry containing an arbitrary binary blob
Note: This should not be used by itself. It is normally used as a parent
class by other entry types.
Properties / Entry arguments:
- filename: Filename of file to read into entry
This entry reads data from a file and places it in the entry. The
default filename is often specified specified by the subclass. See for
example the 'u_boot' entry which provides the filename 'u-boot.bin'.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node) Entry.__init__(self, section, etype, node)
self._filename = fdt_util.GetString(self._node, "filename", self.etype) self._filename = fdt_util.GetString(self._node, "filename", self.etype)
@ -17,10 +29,10 @@ class Entry_blob(Entry):
def ObtainContents(self): def ObtainContents(self):
self._filename = self.GetDefaultFilename() self._filename = self.GetDefaultFilename()
self._pathname = tools.GetInputFilename(self._filename) self._pathname = tools.GetInputFilename(self._filename)
self.ReadContents() self.ReadBlobContents()
return True return True
def ReadContents(self): def ReadBlobContents(self):
with open(self._pathname) as fd: with open(self._pathname) as fd:
# We assume the data is small enough to fit into memory. If this # We assume the data is small enough to fit into memory. If this
# is used for large filesystem image that might not be true. # is used for large filesystem image that might not be true.

View file

@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for a blob where the filename comes from a property in the
# node or an entry argument. The property is called '<blob_fname>-path' where
# <blob_fname> is provided by the subclass using this entry type.
from collections import OrderedDict
from blob import Entry_blob
from entry import EntryArg
class Entry_blob_named_by_arg(Entry_blob):
"""A blob entry which gets its filename property from its subclass
Properties / Entry arguments:
- <xxx>-path: Filename containing the contents of this entry (optional,
defaults to 0)
where <xxx> is the blob_fname argument to the constructor.
This entry cannot be used directly. Instead, it is used as a parent class
for another entry, which defined blob_fname. This parameter is used to
set the entry-arg or property containing the filename. The entry-arg or
property is in turn used to set the actual filename.
See cros_ec_rw for an example of this.
"""
def __init__(self, section, etype, node, blob_fname):
Entry_blob.__init__(self, section, etype, node)
self._filename, = self.GetEntryArgsOrProps(
[EntryArg('%s-path' % blob_fname, str)])

View file

@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for a Chromium OS EC image (read-write section)
#
from blob_named_by_arg import Entry_blob_named_by_arg
class Entry_cros_ec_rw(Entry_blob_named_by_arg):
"""A blob entry which contains a Chromium OS read-write EC image
Properties / Entry arguments:
- cros-ec-rw-path: Filename containing the EC image
This entry holds a Chromium OS EC (embedded controller) image, for use in
updating the EC on startup via software sync.
"""
def __init__(self, section, etype, node):
Entry_blob_named_by_arg.__init__(self, section, etype, node,
'cros-ec-rw')

View file

@ -0,0 +1,32 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
from entry import Entry
import fdt_util
class Entry_fill(Entry):
"""An entry which is filled to a particular byte value
Properties / Entry arguments:
- fill-byte: Byte to use to fill the entry
Note that the size property must be set since otherwise this entry does not
know how large it should be.
You can often achieve the same effect using the pad-byte property of the
overall image, in that the space between entries will then be padded with
that byte. But this entry is sometimes useful for explicitly setting the
byte value of a region.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
if not self.size:
self.Raise("'fill' entry must have a size property")
self.fill_value = fdt_util.GetByte(self._node, 'fill-byte', 0)
def ObtainContents(self):
self.SetContents(chr(self.fill_value) * self.size)
return True

View file

@ -0,0 +1,61 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for a Flash map, as used by the flashrom SPI flash tool
#
from entry import Entry
import fmap_util
class Entry_fmap(Entry):
"""An entry which contains an Fmap section
Properties / Entry arguments:
None
FMAP is a simple format used by flashrom, an open-source utility for
reading and writing the SPI flash, typically on x86 CPUs. The format
provides flashrom with a list of areas, so it knows what it in the flash.
It can then read or write just a single area, instead of the whole flash.
The format is defined by the flashrom project, in the file lib/fmap.h -
see www.flashrom.org/Flashrom for more information.
When used, this entry will be populated with an FMAP which reflects the
entries in the current image. Note that any hierarchy is squashed, since
FMAP does not support this.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
def _GetFmap(self):
"""Build an FMAP from the entries in the current image
Returns:
FMAP binary data
"""
def _AddEntries(areas, entry):
entries = entry.GetEntries()
if entries:
for subentry in entries.values():
_AddEntries(areas, subentry)
else:
areas.append(fmap_util.FmapArea(entry.image_pos or 0,
entry.size or 0, entry.name, 0))
entries = self.section.GetEntries()
areas = []
for entry in entries.values():
_AddEntries(areas, entry)
return fmap_util.EncodeFmap(self.section.GetSize() or 0, self.name,
areas)
def ObtainContents(self):
"""Obtain a placeholder for the fmap contents"""
self.SetContents(self._GetFmap())
return True
def ProcessContents(self):
self.SetContents(self._GetFmap())

96
tools/binman/etype/gbb.py Normal file
View file

@ -0,0 +1,96 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Support for a Chromium OS Google Binary Block, used to record read-only
# information mostly used by firmware.
from collections import OrderedDict
import command
from entry import Entry, EntryArg
import fdt_util
import tools
# Build GBB flags.
# (src/platform/vboot_reference/firmware/include/gbb_header.h)
gbb_flag_properties = {
'dev-screen-short-delay': 0x1,
'load-option-roms': 0x2,
'enable-alternate-os': 0x4,
'force-dev-switch-on': 0x8,
'force-dev-boot-usb': 0x10,
'disable-fw-rollback-check': 0x20,
'enter-triggers-tonorm': 0x40,
'force-dev-boot-legacy': 0x80,
'faft-key-override': 0x100,
'disable-ec-software-sync': 0x200,
'default-dev-boot-legacy': 0x400,
'disable-pd-software-sync': 0x800,
'disable-lid-shutdown': 0x1000,
'force-dev-boot-fastboot-full-cap': 0x2000,
'enable-serial': 0x4000,
'disable-dwmp': 0x8000,
}
class Entry_gbb(Entry):
"""An entry which contains a Chromium OS Google Binary Block
Properties / Entry arguments:
- hardware-id: Hardware ID to use for this build (a string)
- keydir: Directory containing the public keys to use
- bmpblk: Filename containing images used by recovery
Chromium OS uses a GBB to store various pieces of information, in particular
the root and recovery keys that are used to verify the boot process. Some
more details are here:
https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts
but note that the page dates from 2013 so is quite out of date. See
README.chromium for how to obtain the required keys and tools.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self.hardware_id, self.keydir, self.bmpblk = self.GetEntryArgsOrProps(
[EntryArg('hardware-id', str),
EntryArg('keydir', str),
EntryArg('bmpblk', str)])
# Read in the GBB flags from the config
self.gbb_flags = 0
flags_node = node.FindNode('flags')
if flags_node:
for flag, value in gbb_flag_properties.iteritems():
if fdt_util.GetBool(flags_node, flag):
self.gbb_flags |= value
def ObtainContents(self):
gbb = 'gbb.bin'
fname = tools.GetOutputFilename(gbb)
if not self.size:
self.Raise('GBB must have a fixed size')
gbb_size = self.size
bmpfv_size = gbb_size - 0x2180
if bmpfv_size < 0:
self.Raise('GBB is too small (minimum 0x2180 bytes)')
sizes = [0x100, 0x1000, bmpfv_size, 0x1000]
sizes = ['%#x' % size for size in sizes]
keydir = tools.GetInputFilename(self.keydir)
gbb_set_command = [
'gbb_utility', '-s',
'--hwid=%s' % self.hardware_id,
'--rootkey=%s/root_key.vbpubk' % keydir,
'--recoverykey=%s/recovery_key.vbpubk' % keydir,
'--flags=%d' % self.gbb_flags,
'--bmpfv=%s' % tools.GetInputFilename(self.bmpblk),
fname]
tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname)
tools.Run('futility', *gbb_set_command)
self.SetContents(tools.ReadFile(fname))
return True

View file

@ -9,5 +9,15 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_cmc(Entry_blob): class Entry_intel_cmc(Entry_blob):
"""Entry containing an Intel Chipset Micro Code (CMC) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains microcode for some devices in a special format. An
example filename is 'Microcode/C0_22211.BIN'.
See README.x86 for information about x86 binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -13,6 +13,7 @@ from blob import Entry_blob
FD_SIGNATURE = struct.pack('<L', 0x0ff0a55a) FD_SIGNATURE = struct.pack('<L', 0x0ff0a55a)
MAX_REGIONS = 5 MAX_REGIONS = 5
# Region numbers supported by the Intel firmware format
(REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE, (REGION_DESCRIPTOR, REGION_BIOS, REGION_ME, REGION_GBE,
REGION_PDATA) = range(5) REGION_PDATA) = range(5)
@ -27,21 +28,32 @@ class Region:
class Entry_intel_descriptor(Entry_blob): class Entry_intel_descriptor(Entry_blob):
"""Intel flash descriptor block (4KB) """Intel flash descriptor block (4KB)
This is placed at the start of flash and provides information about Properties / Entry arguments:
filename: Filename of file containing the descriptor. This is typically
a 4KB binary file, sometimes called 'descriptor.bin'
This entry is placed at the start of flash and provides information about
the SPI flash regions. In particular it provides the base address and 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 size of the ME (Management Engine) region, allowing us to place the ME
place. binary in the right place.
With this entry in your image, the position of the 'intel-me' entry will be
fixed in the image, which avoids you needed to specify an offset for that
region. This is useful, because it is not possible to change the position
of the ME region without updating the descriptor.
See README.x86 for information about x86 binary blobs.
""" """
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)
self._regions = [] self._regions = []
def GetPositions(self): def GetOffsets(self):
pos = self.data.find(FD_SIGNATURE) offset = self.data.find(FD_SIGNATURE)
if pos == -1: if offset == -1:
self.Raise('Cannot find FD signature') self.Raise('Cannot find FD signature')
flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL', flvalsig, flmap0, flmap1, flmap2 = struct.unpack('<LLLL',
self.data[pos:pos + 16]) self.data[offset:offset + 16])
frba = ((flmap0 >> 16) & 0xff) << 4 frba = ((flmap0 >> 16) & 0xff) << 4
for i in range(MAX_REGIONS): for i in range(MAX_REGIONS):
self._regions.append(Region(self.data, frba, i)) self._regions.append(Region(self.data, frba, i))

View file

@ -9,5 +9,19 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_fsp(Entry_blob): class Entry_intel_fsp(Entry_blob):
"""Entry containing an Intel Firmware Support Package (FSP) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains binary blobs which are used on some devices to make the
platform work. U-Boot executes this code since it is not possible to set up
the hardware using U-Boot open-source code. Documentation is typically not
available in sufficient detail to allow this.
An example filename is 'FSP/QUEENSBAY_FSP_GOLD_001_20-DECEMBER-2013.fd'
See README.x86 for information about x86 binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,5 +9,20 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_me(Entry_blob): class Entry_intel_me(Entry_blob):
"""Entry containing an Intel Management Engine (ME) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code used by the SoC that is required to make it work.
The Management Engine is like a background task that runs things that are
not clearly documented, but may include keyboard, deplay and network
access. For platform that use ME it is not possible to disable it. U-Boot
does not directly execute code in the ME binary.
A typical filename is 'me.bin'.
See README.x86 for information about x86 binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,6 +9,17 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_mrc(Entry_blob): class Entry_intel_mrc(Entry_blob):
"""Entry containing an Intel Memory Reference Code (MRC) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code for setting up the SDRAM on some Intel systems. This
is executed by U-Boot when needed early during startup. A typical filename
is 'mrc.bin'.
See README.x86 for information about x86 binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -8,5 +8,15 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_vbt(Entry_blob): class Entry_intel_vbt(Entry_blob):
"""Entry containing an Intel Video BIOS Table (VBT) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code that sets up the integrated graphics subsystem on
some Intel SoCs. U-Boot executes this when the display is started up.
See README.x86 for information about Intel binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,5 +9,17 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_vga(Entry_blob): class Entry_intel_vga(Entry_blob):
"""Entry containing an Intel Video Graphics Adaptor (VGA) file
Properties / Entry arguments:
- filename: Filename of file to read into entry
This file contains code that sets up the integrated graphics subsystem on
some Intel SoCs. U-Boot executes this when the display is started up.
This is similar to the VBT file but in a different format.
See README.x86 for information about Intel binary blobs.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -13,8 +13,25 @@ import tools
import bsection import bsection
class Entry_section(Entry): class Entry_section(Entry):
def __init__(self, image, etype, node): """Entry that contains other entries
Entry.__init__(self, image, etype, node)
Properties / Entry arguments: (see binman README for more information)
- size: Size of section in bytes
- align-size: Align size to a particular power of two
- pad-before: Add padding before the entry
- pad-after: Add padding after the entry
- pad-byte: Pad byte to use when padding
- sort-by-offset: Reorder the entries by offset
- end-at-4gb: Used to build an x86 ROM which ends at 4GB (2^32)
- name-prefix: Adds a prefix to the name of every entry in the section
when writing out the map
A section is an entry which can contain other entries, thus allowing
hierarchical images to be created. See 'Sections and hierarchical images'
in the binman README for more information.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self._section = bsection.Section(node.name, node) self._section = bsection.Section(node.name, node)
def ProcessFdt(self, fdt): def ProcessFdt(self, fdt):
@ -30,20 +47,25 @@ class Entry_section(Entry):
def GetData(self): def GetData(self):
return self._section.GetData() return self._section.GetData()
def GetPositions(self): def GetOffsets(self):
"""Handle entries that want to set the position/size of other entries """Handle entries that want to set the offset/size of other entries
This calls each entry's GetPositions() method. If it returns a list This calls each entry's GetOffsets() method. If it returns a list
of entries to update, it updates them. of entries to update, it updates them.
""" """
self._section.GetEntryPositions() self._section.GetEntryOffsets()
return {} return {}
def Pack(self, pos): def Pack(self, offset):
"""Pack all entries into the section""" """Pack all entries into the section"""
self._section.PackEntries() self._section.PackEntries()
self.size = self._section.CheckSize() self._section.SetOffset(offset)
return super(Entry_section, self).Pack(pos) self.size = self._section.GetSize()
return super(Entry_section, self).Pack(offset)
def SetImagePos(self, image_pos):
Entry.SetImagePos(self, image_pos)
self._section.SetImagePos(image_pos + self.offset)
def WriteSymbols(self, section): def WriteSymbols(self, section):
"""Write symbol values into binary files for access at run time""" """Write symbol values into binary files for access at run time"""
@ -57,7 +79,7 @@ class Entry_section(Entry):
self._section.ProcessEntryContents() self._section.ProcessEntryContents()
super(Entry_section, self).ProcessContents() super(Entry_section, self).ProcessContents()
def CheckPosition(self): def CheckOffset(self):
self._section.CheckEntries() self._section.CheckEntries()
def WriteMap(self, fd, indent): def WriteMap(self, fd, indent):
@ -66,5 +88,7 @@ class Entry_section(Entry):
Args: Args:
fd: File to write the map to fd: File to write the map to
""" """
super(Entry_section, self).WriteMap(fd, indent) self._section.WriteMap(fd, indent)
self._section.WriteMap(fd, indent + 1)
def GetEntries(self):
return self._section.GetEntries()

View file

@ -0,0 +1,57 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
from collections import OrderedDict
from entry import Entry, EntryArg
import fdt_util
class Entry_text(Entry):
"""An entry which contains text
The text can be provided either in the node itself or by a command-line
argument. There is a level of indirection to allow multiple text strings
and sharing of text.
Properties / Entry arguments:
text-label: The value of this string indicates the property / entry-arg
that contains the string to place in the entry
<xxx> (actual name is the value of text-label): contains the string to
place in the entry.
Example node:
text {
size = <50>;
text-label = "message";
};
You can then use:
binman -amessage="this is my message"
and binman will insert that string into the entry.
It is also possible to put the string directly in the node:
text {
size = <8>;
text-label = "message";
message = "a message directly in the node"
};
The text is not itself nul-terminated. This can be achieved, if required,
by setting the size of the entry to something larger than the text.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self.text_label, = self.GetEntryArgsOrProps(
[EntryArg('text-label', str)])
self.value, = self.GetEntryArgsOrProps([EntryArg(self.text_label, str)])
def ObtainContents(self):
self.SetContents(self.value)
return True

View file

@ -9,6 +9,22 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot(Entry_blob): class Entry_u_boot(Entry_blob):
"""U-Boot flat binary
Properties / Entry arguments:
- filename: Filename of u-boot.bin (default 'u-boot.bin')
This is the U-Boot binary, containing relocation information to allow it
to relocate itself at runtime. The binary typically includes a device tree
blob at the end of it. Use u_boot_nodtb if you want to package the device
tree separately.
U-Boot can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (fdt)'
in the binman README for more information.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,6 +9,15 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_dtb(Entry_blob): class Entry_u_boot_dtb(Entry_blob):
"""U-Boot device tree
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'u-boot.dtb')
This is the U-Boot device tree, containing configuration information for
U-Boot. U-Boot needs this to know what devices are present and which drivers
to activate.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -6,7 +6,6 @@
# #
import control import control
import fdt
from entry import Entry from entry import Entry
from blob import Entry_blob from blob import Entry_blob
import tools import tools
@ -14,8 +13,16 @@ import tools
class Entry_u_boot_dtb_with_ucode(Entry_blob): class Entry_u_boot_dtb_with_ucode(Entry_blob):
"""A U-Boot device tree file, with the microcode removed """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 Properties / Entry arguments:
process. - filename: Filename of u-boot.dtb (default 'u-boot.dtb')
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry provides the U-Boot device-tree file, which
contains the microcode. If the microcode is not being collated into one
place then the offset and size of the microcode is recorded by this entry,
for use by u_boot_with_ucode_ptr. If it is being collated, then this
entry deletes the microcode from the device tree (to save space) and makes
it available to u_boot_ucode.
""" """
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)
@ -30,13 +37,16 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
return 'u-boot.dtb' return 'u-boot.dtb'
def ProcessFdt(self, fdt): def ProcessFdt(self, fdt):
# So the module can be loaded without it
import fdt
# If the section does not need microcode, there is nothing to do # If the section does not need microcode, there is nothing to do
ucode_dest_entry = self.section.FindEntryType( ucode_dest_entry = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr') 'u-boot-spl-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_pos: if not ucode_dest_entry or not ucode_dest_entry.target_offset:
ucode_dest_entry = self.section.FindEntryType( ucode_dest_entry = self.section.FindEntryType(
'u-boot-with-ucode-ptr') 'u-boot-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_pos: if not ucode_dest_entry or not ucode_dest_entry.target_offset:
return True return True
# Remove the microcode # Remove the microcode
@ -61,7 +71,7 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
# Call the base class just in case it does something important. # Call the base class just in case it does something important.
Entry_blob.ObtainContents(self) Entry_blob.ObtainContents(self)
self._pathname = control.GetFdtPath(self._filename) self._pathname = control.GetFdtPath(self._filename)
self.ReadContents() self.ReadBlobContents()
if self.ucode: if self.ucode:
for node in self.ucode.subnodes: for node in self.ucode.subnodes:
data_prop = node.props.get('data') data_prop = node.props.get('data')

View file

@ -9,6 +9,17 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_img(Entry_blob): class Entry_u_boot_img(Entry_blob):
"""U-Boot legacy image
Properties / Entry arguments:
- filename: Filename of u-boot.img (default 'u-boot.img')
This is the U-Boot binary as a packaged image, in legacy format. It has a
header which allows it to be loaded at the correct address for execution.
You should use FIT (Flat Image Tree) instead of the legacy image for new
applications.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,6 +9,17 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_nodtb(Entry_blob): class Entry_u_boot_nodtb(Entry_blob):
"""U-Boot flat binary without device tree appended
Properties / Entry arguments:
- filename: Filename of u-boot.bin (default 'u-boot-nodtb.bin')
This is the U-Boot binary, containing relocation information to allow it
to relocate itself at runtime. It does not include a device tree blob at
the end of it so normally cannot work without it. You can add a u_boot_dtb
entry after this one, or use a u_boot entry instead (which contains both
U-Boot and the device tree).
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -11,6 +11,27 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl(Entry_blob): class Entry_u_boot_spl(Entry_blob):
"""U-Boot SPL binary
Properties / Entry arguments:
- filename: Filename of u-boot-spl.bin (default 'spl/u-boot-spl.bin')
This is the U-Boot SPL (Secondary Program Loader) binary. This is a small
binary which loads before U-Boot proper, typically into on-chip SRAM. It is
responsible for locating, loading and jumping to U-Boot. Note that SPL is
not relocatable so must be loaded to the correct address in SRAM, or written
to run from the correct address if direct flash execution is possible (e.g.
on x86 devices).
SPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up symbols to write into the SPL binary.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)
self.elf_fname = 'spl/u-boot-spl' self.elf_fname = 'spl/u-boot-spl'

View file

@ -14,6 +14,22 @@ from blob import Entry_blob
import tools import tools
class Entry_u_boot_spl_bss_pad(Entry_blob): class Entry_u_boot_spl_bss_pad(Entry_blob):
"""U-Boot SPL binary padded with a BSS region
Properties / Entry arguments:
None
This is similar to u_boot_spl except that padding is added after the SPL
binary to cover the BSS (Block Started by Symbol) region. This region holds
the various used by SPL. It is set to 0 by SPL when it starts up. If you
want to append data to the SPL image (such as a device tree file), you must
pad out the BSS region to avoid the data overlapping with U-Boot variables.
This entry is useful in that case. It automatically pads out the entry size
to cover both the code, data and BSS.
The ELF file 'spl/u-boot-spl' must also be available for this to work, since
binman uses that to look up the BSS address.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -2,13 +2,22 @@
# Copyright (c) 2016 Google, Inc # Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org> # Written by Simon Glass <sjg@chromium.org>
# #
# Entry-type module for U-Boot device tree # Entry-type module for U-Boot device tree in SPL (Secondary Program Loader)
# #
from entry import Entry from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl_dtb(Entry_blob): class Entry_u_boot_spl_dtb(Entry_blob):
"""U-Boot SPL device tree
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'spl/u-boot-spl.dtb')
This is the SPL device tree, containing configuration information for
SPL. SPL needs this to know what devices are present and which drivers
to activate.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,6 +9,18 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl_nodtb(Entry_blob): class Entry_u_boot_spl_nodtb(Entry_blob):
"""SPL binary without device tree appended
Properties / Entry arguments:
- filename: Filename of spl/u-boot-spl-nodtb.bin (default
'spl/u-boot-spl-nodtb.bin')
This is the U-Boot SPL binary, It does not include a device tree blob at
the end of it so may not be able to work without it, assuming SPL needs
a device tree to operation on your platform. You can add a u_boot_spl_dtb
entry after this one, or use a u_boot_spl entry instead (which contains
both SPL and the device tree).
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -0,0 +1,43 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for tpl/u-boot-tpl.bin
#
import elf
from entry import Entry
from blob import Entry_blob
class Entry_u_boot_tpl(Entry_blob):
"""U-Boot TPL binary
Properties / Entry arguments:
- filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin')
This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small
binary which loads before SPL, typically into on-chip SRAM. It is
responsible for locating, loading and jumping to SPL, the next-stage
loader. Note that SPL is not relocatable so must be loaded to the correct
address in SRAM, or written to run from the correct address if direct
flash execution is possible (e.g. on x86 devices).
SPL can access binman symbols at runtime. See:
'Access to binman entry offsets at run time (symbols)'
in the binman README for more information.
The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since
binman uses that to look up symbols to write into the TPL binary.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
self.elf_fname = 'tpl/u-boot-tpl'
def GetDefaultFilename(self):
return 'tpl/u-boot-tpl.bin'
def WriteSymbols(self, section):
elf.LookupAndWriteSymbols(self.elf_fname, self, section)

View file

@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for U-Boot device tree in TPL (Tertiary Program Loader)
#
from entry import Entry
from blob import Entry_blob
class Entry_u_boot_tpl_dtb(Entry_blob):
"""U-Boot TPL device tree
Properties / Entry arguments:
- filename: Filename of u-boot.dtb (default 'tpl/u-boot-tpl.dtb')
This is the TPL device tree, containing configuration information for
TPL. TPL needs this to know what devices are present and which drivers
to activate.
"""
def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self):
return 'tpl/u-boot-tpl.dtb'

View file

@ -12,6 +12,12 @@ import tools
class Entry_u_boot_ucode(Entry_blob): class Entry_u_boot_ucode(Entry_blob):
"""U-Boot microcode block """U-Boot microcode block
Properties / Entry arguments:
None
The contents of this entry are filled in automatically by other entries
which must also be in the image.
U-Boot on x86 needs a single block of microcode. This is collected from 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 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 to read the microcode from the device tree on platforms that use FSP
@ -59,8 +65,8 @@ class Entry_u_boot_ucode(Entry_blob):
ucode_dest_entry = self.section.FindEntryType('u-boot-with-ucode-ptr') ucode_dest_entry = self.section.FindEntryType('u-boot-with-ucode-ptr')
ucode_dest_entry_spl = self.section.FindEntryType( ucode_dest_entry_spl = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr') 'u-boot-spl-with-ucode-ptr')
if ((not ucode_dest_entry or not ucode_dest_entry.target_pos) and if ((not ucode_dest_entry or not ucode_dest_entry.target_offset) and
(not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_pos)): (not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_offset)):
self.data = '' self.data = ''
return True return True
@ -86,6 +92,6 @@ class Entry_u_boot_ucode(Entry_blob):
fd.write(fdt_entry.ucode_data) fd.write(fdt_entry.ucode_data)
self._pathname = fname self._pathname = fname
self.ReadContents() self.ReadBlobContents()
return True return True

View file

@ -17,13 +17,18 @@ import tools
class Entry_u_boot_with_ucode_ptr(Entry_blob): class Entry_u_boot_with_ucode_ptr(Entry_blob):
"""U-Boot with embedded microcode pointer """U-Boot with embedded microcode pointer
See Entry_u_boot_ucode for full details of the 3 entries involved in this Properties / Entry arguments:
process. - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
See Entry_u_boot_ucode for full details of the three entries involved in
this process. This entry updates U-Boot with the offset and size of the
microcode, to allow early x86 boot code to find it without doing anything
complicated. Otherwise it is the same as the u_boot entry.
""" """
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)
self.elf_fname = 'u-boot' self.elf_fname = 'u-boot'
self.target_pos = None self.target_offset = None
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot-nodtb.bin' return 'u-boot-nodtb.bin'
@ -33,52 +38,53 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
fname = tools.GetInputFilename(self.elf_fname) fname = tools.GetInputFilename(self.elf_fname)
sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size') sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size')
if sym: if sym:
self.target_pos = sym self.target_offset = sym
elif not fdt_util.GetBool(self._node, 'optional-ucode'): elif not fdt_util.GetBool(self._node, 'optional-ucode'):
self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot') self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot')
return True return True
def ProcessContents(self): def ProcessContents(self):
# If the image does not need microcode, there is nothing to do # If the image does not need microcode, there is nothing to do
if not self.target_pos: if not self.target_offset:
return return
# Get the position of the microcode # Get the offset of the microcode
ucode_entry = self.section.FindEntryType('u-boot-ucode') ucode_entry = self.section.FindEntryType('u-boot-ucode')
if not ucode_entry: if not ucode_entry:
self.Raise('Cannot find microcode region u-boot-ucode') self.Raise('Cannot find microcode region u-boot-ucode')
# Check the target pos is in the section. If it is not, then U-Boot is # Check the target pos is in the section. If it is not, then U-Boot is
# being linked incorrectly, or is being placed at the wrong position # being linked incorrectly, or is being placed at the wrong offset
# in the section. # in the section.
# #
# The section must be set up so that U-Boot is placed at the # The section must be set up so that U-Boot is placed at the
# flash address to which it is linked. For example, if # flash address to which it is linked. For example, if
# CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then
# the U-Boot region must start at position 7MB in the section. In this # the U-Boot region must start at offset 7MB in the section. In this
# case the ROM starts at 0xff800000, so the position of the first # case the ROM starts at 0xff800000, so the offset of the first
# entry in the section corresponds to that. # entry in the section corresponds to that.
if (self.target_pos < self.pos or if (self.target_offset < self.offset or
self.target_pos >= self.pos + self.size): self.target_offset >= self.offset + self.size):
self.Raise('Microcode pointer _dt_ucode_base_size at %08x is ' self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
'outside the section ranging from %08x to %08x' % 'outside the section ranging from %08x to %08x' %
(self.target_pos, self.pos, self.pos + self.size)) (self.target_offset, self.offset, self.offset + self.size))
# Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode. # 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 # 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 # in the former. If we extracted the microcode from the device tree
# and collated it in one place, it will be in the latter. # and collated it in one place, it will be in the latter.
if ucode_entry.size: if ucode_entry.size:
pos, size = ucode_entry.pos, ucode_entry.size offset, size = ucode_entry.offset, ucode_entry.size
else: else:
dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode') dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
if not dtb_entry or not dtb_entry.ready: if not dtb_entry or not dtb_entry.ready:
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode') self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
pos = dtb_entry.pos + dtb_entry.ucode_offset offset = dtb_entry.offset + dtb_entry.ucode_offset
size = dtb_entry.ucode_size size = dtb_entry.ucode_size
# Write the microcode position and size into the entry # Write the microcode offset and size into the entry
pos_and_size = struct.pack('<2L', pos, size) offset_and_size = struct.pack('<2L', offset, size)
self.target_pos -= self.pos self.target_offset -= self.offset
self.ProcessContentsUpdate(self.data[:self.target_pos] + pos_and_size + self.ProcessContentsUpdate(self.data[:self.target_offset] +
self.data[self.target_pos + 8:]) offset_and_size +
self.data[self.target_offset + 8:])

View file

@ -0,0 +1,74 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Support for a Chromium OS verified boot block, used to sign a read-write
# section of the image.
from collections import OrderedDict
import os
from entry import Entry, EntryArg
import fdt_util
import tools
class Entry_vblock(Entry):
"""An entry which contains a Chromium OS verified boot block
Properties / Entry arguments:
- keydir: Directory containing the public keys to use
- keyblock: Name of the key file to use (inside keydir)
- signprivate: Name of provide key file to use (inside keydir)
- version: Version number of the vblock (typically 1)
- kernelkey: Name of the kernel key to use (inside keydir)
- preamble-flags: Value of the vboot preamble flags (typically 0)
Chromium OS signs the read-write firmware and kernel, writing the signature
in this block. This allows U-Boot to verify that the next firmware stage
and kernel are genuine.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self.content = fdt_util.GetPhandleList(self._node, 'content')
if not self.content:
self.Raise("Vblock must have a 'content' property")
(self.keydir, self.keyblock, self.signprivate, self.version,
self.kernelkey, self.preamble_flags) = self.GetEntryArgsOrProps([
EntryArg('keydir', str),
EntryArg('keyblock', str),
EntryArg('signprivate', str),
EntryArg('version', int),
EntryArg('kernelkey', str),
EntryArg('preamble-flags', int)])
def ObtainContents(self):
# Join up the data files to be signed
input_data = ''
for entry_phandle in self.content:
data = self.section.GetContentsByPhandle(entry_phandle, self)
if data is None:
# Data not available yet
return False
input_data += data
output_fname = tools.GetOutputFilename('vblock.%s' % self.name)
input_fname = tools.GetOutputFilename('input.%s' % self.name)
tools.WriteFile(input_fname, input_data)
prefix = self.keydir + '/'
args = [
'vbutil_firmware',
'--vblock', output_fname,
'--keyblock', prefix + self.keyblock,
'--signprivate', prefix + self.signprivate,
'--version', '%d' % self.version,
'--fv', input_fname,
'--kernelkey', prefix + self.kernelkey,
'--flags', '%d' % self.preamble_flags,
]
#out.Notice("Sign '%s' into %s" % (', '.join(self.value), self.label))
stdout = tools.Run('futility', *args)
#out.Debug(stdout)
self.SetContents(tools.ReadFile(output_fname))
return True

View file

@ -9,6 +9,20 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_x86_start16(Entry_blob): class Entry_x86_start16(Entry_blob):
"""x86 16-bit start-up code for U-Boot
Properties / Entry arguments:
- filename: Filename of u-boot-x86-16bit.bin (default
'u-boot-x86-16bit.bin')
x86 CPUs start up in 16-bit mode, even if they are 32-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and jumping to U-Boot's entry point, which
requires 32-bit mode (for 32-bit U-Boot).
For 64-bit U-Boot, the 'x86_start16_spl' entry type is used instead.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

View file

@ -9,6 +9,20 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_x86_start16_spl(Entry_blob): class Entry_x86_start16_spl(Entry_blob):
"""x86 16-bit start-up code for SPL
Properties / Entry arguments:
- filename: Filename of spl/u-boot-x86-16bit-spl.bin (default
'spl/u-boot-x86-16bit-spl.bin')
x86 CPUs start up in 16-bit mode, even if they are 64-bit CPUs. This code
must be placed at a particular address. This entry holds that code. It is
typically placed at offset CONFIG_SYS_X86_START16. The code is responsible
for changing to 32-bit mode and starting SPL, which in turn changes to
64-bit mode and jumps to U-Boot (for 64-bit U-Boot).
For 32-bit U-Boot, the 'x86_start16' entry type is used instead.
"""
def __init__(self, section, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, section, etype, node) Entry_blob.__init__(self, section, etype, node)

109
tools/binman/fmap_util.py Normal file
View file

@ -0,0 +1,109 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Support for flashrom's FMAP format. This supports a header followed by a
# number of 'areas', describing regions of a firmware storage device,
# generally SPI flash.
import collections
import struct
# constants imported from lib/fmap.h
FMAP_SIGNATURE = '__FMAP__'
FMAP_VER_MAJOR = 1
FMAP_VER_MINOR = 0
FMAP_STRLEN = 32
FMAP_AREA_STATIC = 1 << 0
FMAP_AREA_COMPRESSED = 1 << 1
FMAP_AREA_RO = 1 << 2
FMAP_HEADER_LEN = 56
FMAP_AREA_LEN = 42
FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
FMAP_HEADER_NAMES = (
'signature',
'ver_major',
'ver_minor',
'base',
'image_size',
'name',
'nareas',
)
FMAP_AREA_NAMES = (
'offset',
'size',
'name',
'flags',
)
# These are the two data structures supported by flashrom, a header (which
# appears once at the start) and an area (which is repeated until the end of
# the list of areas)
FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
def ConvertName(field_names, fields):
"""Convert a name to something flashrom likes
Flashrom requires upper case, underscores instead of hyphens. We remove any
null characters as well. This updates the 'name' value in fields.
Args:
field_names: List of field names for this struct
fields: Dict:
key: Field name
value: value of that field (string for the ones we support)
"""
name_index = field_names.index('name')
fields[name_index] = fields[name_index].replace('\0', '').replace('-', '_').upper()
def DecodeFmap(data):
"""Decode a flashmap into a header and list of areas
Args:
data: Data block containing the FMAP
Returns:
Tuple:
header: FmapHeader object
List of FmapArea objects
"""
fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
ConvertName(FMAP_HEADER_NAMES, fields)
header = FmapHeader(*fields)
areas = []
data = data[FMAP_HEADER_LEN:]
for area in range(header.nareas):
fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
ConvertName(FMAP_AREA_NAMES, fields)
areas.append(FmapArea(*fields))
data = data[FMAP_AREA_LEN:]
return header, areas
def EncodeFmap(image_size, name, areas):
"""Create a new FMAP from a list of areas
Args:
image_size: Size of image, to put in the header
name: Name of image, to put in the header
areas: List of FmapArea objects
Returns:
String containing the FMAP created
"""
def _FormatBlob(fmt, names, obj):
params = [getattr(obj, name) for name in names]
return struct.pack(fmt, *params)
values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))
blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
for area in areas:
blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
return blob

View file

@ -21,6 +21,8 @@ import control
import elf import elf
import fdt import fdt
import fdt_util import fdt_util
import fmap_util
import test_util
import tools import tools
import tout import tout
@ -28,11 +30,13 @@ import tout
U_BOOT_DATA = '1234' U_BOOT_DATA = '1234'
U_BOOT_IMG_DATA = 'img' U_BOOT_IMG_DATA = 'img'
U_BOOT_SPL_DATA = '56780123456789abcde' U_BOOT_SPL_DATA = '56780123456789abcde'
U_BOOT_TPL_DATA = 'tpl'
BLOB_DATA = '89' BLOB_DATA = '89'
ME_DATA = '0abcd' ME_DATA = '0abcd'
VGA_DATA = 'vga' VGA_DATA = 'vga'
U_BOOT_DTB_DATA = 'udtb' U_BOOT_DTB_DATA = 'udtb'
U_BOOT_SPL_DTB_DATA = 'spldtb' U_BOOT_SPL_DTB_DATA = 'spldtb'
U_BOOT_TPL_DTB_DATA = 'tpldtb'
X86_START16_DATA = 'start16' X86_START16_DATA = 'start16'
X86_START16_SPL_DATA = 'start16spl' X86_START16_SPL_DATA = 'start16spl'
U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here' U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
@ -41,6 +45,14 @@ FSP_DATA = 'fsp'
CMC_DATA = 'cmc' CMC_DATA = 'cmc'
VBT_DATA = 'vbt' VBT_DATA = 'vbt'
MRC_DATA = 'mrc' MRC_DATA = 'mrc'
TEXT_DATA = 'text'
TEXT_DATA2 = 'text2'
TEXT_DATA3 = 'text3'
CROS_EC_RW_DATA = 'ecrw'
GBB_DATA = 'gbbd'
BMPBLK_DATA = 'bmp'
VBLOCK_DATA = 'vblk'
class TestFunctional(unittest.TestCase): class TestFunctional(unittest.TestCase):
"""Functional tests for binman """Functional tests for binman
@ -72,11 +84,11 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA) TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA) TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA) TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
TestFunctional._MakeInputFile('blobfile', BLOB_DATA) TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
TestFunctional._MakeInputFile('me.bin', ME_DATA) TestFunctional._MakeInputFile('me.bin', ME_DATA)
TestFunctional._MakeInputFile('vga.bin', VGA_DATA) TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) self._ResetDtbs()
TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA) TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin', TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
X86_START16_SPL_DATA) X86_START16_SPL_DATA)
@ -87,6 +99,9 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
TestFunctional._MakeInputFile('vbt.bin', VBT_DATA) TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
TestFunctional._MakeInputFile('mrc.bin', MRC_DATA) TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
TestFunctional._MakeInputDir('devkeys')
TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
self._output_setup = False self._output_setup = False
# ELF file with a '_dt_ucode_base_size' symbol # ELF file with a '_dt_ucode_base_size' symbol
@ -113,6 +128,12 @@ class TestFunctional(unittest.TestCase):
"""Remove the temporary output directory""" """Remove the temporary output directory"""
tools._FinaliseForTest() tools._FinaliseForTest()
@classmethod
def _ResetDtbs(self):
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
def _RunBinman(self, *args, **kwargs): def _RunBinman(self, *args, **kwargs):
"""Run binman using the command line """Run binman using the command line
@ -146,14 +167,15 @@ class TestFunctional(unittest.TestCase):
# options.verbosity = tout.DEBUG # options.verbosity = tout.DEBUG
return control.Binman(options, args) return control.Binman(options, args)
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False): def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
entry_args=None):
"""Run binman with a given test file """Run binman with a given test file
Args: Args:
fname: Device-tree source filename to use (e.g. 05_simple.dts) fname: Device-tree source filename to use (e.g. 05_simple.dts)
debug: True to enable debugging output debug: True to enable debugging output
map: True to output map files for the images map: True to output map files for the images
update_dtb: Update the position and size of each entry in the device update_dtb: Update the offset and size of each entry in the device
tree before packing it into the image tree before packing it into the image
""" """
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)] args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
@ -163,6 +185,9 @@ class TestFunctional(unittest.TestCase):
args.append('-m') args.append('-m')
if update_dtb: if update_dtb:
args.append('-up') args.append('-up')
if entry_args:
for arg, value in entry_args.iteritems():
args.append('-a%s=%s' % (arg, value))
return self._DoBinman(*args) return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'): def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@ -188,7 +213,7 @@ class TestFunctional(unittest.TestCase):
return data return data
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False, def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
update_dtb=False): update_dtb=False, entry_args=None):
"""Run binman and return the resulting image """Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting This runs binman with a given test file and then reads the resulting
@ -204,7 +229,7 @@ class TestFunctional(unittest.TestCase):
test contents (the U_BOOT_DTB_DATA string) can be used. test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents. But in some test we need the real contents.
map: True to output map files for the images map: True to output map files for the images
update_dtb: Update the position and size of each entry in the device update_dtb: Update the offset and size of each entry in the device
tree before packing it into the image tree before packing it into the image
Returns: Returns:
@ -212,6 +237,7 @@ class TestFunctional(unittest.TestCase):
Resulting image contents Resulting image contents
Device tree contents Device tree contents
Map data showing contents of image (or None if none) Map data showing contents of image (or None if none)
Output device tree binary filename ('u-boot.dtb' path)
""" """
dtb_data = None dtb_data = None
# Use the compiled test file as the u-boot-dtb input # Use the compiled test file as the u-boot-dtb input
@ -219,7 +245,8 @@ class TestFunctional(unittest.TestCase):
dtb_data = self._SetupDtb(fname) dtb_data = self._SetupDtb(fname)
try: try:
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb) retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
entry_args=entry_args)
self.assertEqual(0, retcode) self.assertEqual(0, retcode)
out_dtb_fname = control.GetFdtPath('u-boot.dtb') out_dtb_fname = control.GetFdtPath('u-boot.dtb')
@ -238,7 +265,7 @@ class TestFunctional(unittest.TestCase):
finally: finally:
# Put the test file back # Put the test file back
if use_real_dtb: if use_real_dtb:
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) self._ResetDtbs()
def _DoReadFile(self, fname, use_real_dtb=False): def _DoReadFile(self, fname, use_real_dtb=False):
"""Helper function which discards the device-tree binary """Helper function which discards the device-tree binary
@ -249,6 +276,9 @@ class TestFunctional(unittest.TestCase):
the u-boot-dtb entry. Normally this is not needed and the the u-boot-dtb entry. Normally this is not needed and the
test contents (the U_BOOT_DTB_DATA string) can be used. test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents. But in some test we need the real contents.
Returns:
Resulting image contents
""" """
return self._DoReadFileDtb(fname, use_real_dtb)[0] return self._DoReadFileDtb(fname, use_real_dtb)[0]
@ -257,7 +287,7 @@ class TestFunctional(unittest.TestCase):
"""Create a new test input file, creating directories as needed """Create a new test input file, creating directories as needed
Args: Args:
fname: Filenaem to create fname: Filename to create
contents: File contents to write in to the file contents: File contents to write in to the file
Returns: Returns:
Full pathname of file created Full pathname of file created
@ -270,6 +300,21 @@ class TestFunctional(unittest.TestCase):
fd.write(contents) fd.write(contents)
return pathname return pathname
@classmethod
def _MakeInputDir(self, dirname):
"""Create a new test input directory, creating directories as needed
Args:
dirname: Directory name to create
Returns:
Full pathname of directory created
"""
pathname = os.path.join(self._indir, dirname)
if not os.path.exists(pathname):
os.makedirs(pathname)
return pathname
@classmethod @classmethod
def TestFile(self, fname): def TestFile(self, fname):
return os.path.join(self._binman_dir, 'test', fname) return os.path.join(self._binman_dir, 'test', fname)
@ -292,10 +337,10 @@ class TestFunctional(unittest.TestCase):
Args: Args:
entries: List of entries to check entries: List of entries to check
""" """
pos = 0 offset = 0
for entry in entries.values(): for entry in entries.values():
self.assertEqual(pos, entry.pos) self.assertEqual(offset, entry.offset)
pos += entry.size offset += entry.size
def GetFdtLen(self, dtb): def GetFdtLen(self, dtb):
"""Get the totalsize field from a device-tree binary """Get the totalsize field from a device-tree binary
@ -308,23 +353,19 @@ class TestFunctional(unittest.TestCase):
""" """
return struct.unpack('>L', dtb[4:8])[0] return struct.unpack('>L', dtb[4:8])[0]
def _GetPropTree(self, dtb_data, node_names): def _GetPropTree(self, dtb, prop_names):
def AddNode(node, path): def AddNode(node, path):
if node.name != '/': if node.name != '/':
path += '/' + node.name path += '/' + node.name
#print 'path', path
for subnode in node.subnodes: for subnode in node.subnodes:
for prop in subnode.props.values(): for prop in subnode.props.values():
if prop.name in node_names: if prop.name in prop_names:
prop_path = path + '/' + subnode.name + ':' + prop.name prop_path = path + '/' + subnode.name + ':' + prop.name
tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu( tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
prop.value) prop.value)
#print ' ', prop.name
AddNode(subnode, path) AddNode(subnode, path)
tree = {} tree = {}
dtb = fdt.Fdt(dtb_data)
dtb.Scan()
AddNode(dtb.GetRoot(), '') AddNode(dtb.GetRoot(), '')
return tree return tree
@ -409,7 +450,6 @@ class TestFunctional(unittest.TestCase):
with self.assertRaises(Exception) as e: with self.assertRaises(Exception) as e:
result = self._RunBinman('-d', result = self._RunBinman('-d',
self.TestFile('04_invalid_entry.dts')) self.TestFile('04_invalid_entry.dts'))
#print e.exception
self.assertIn("Unknown entry type 'not-a-valid-type' in node " self.assertIn("Unknown entry type 'not-a-valid-type' in node "
"'/binman/not-a-valid-type'", str(e.exception)) "'/binman/not-a-valid-type'", str(e.exception))
@ -467,32 +507,32 @@ class TestFunctional(unittest.TestCase):
# First u-boot # First u-boot
self.assertIn('u-boot', entries) self.assertIn('u-boot', entries)
entry = entries['u-boot'] entry = entries['u-boot']
self.assertEqual(0, entry.pos) self.assertEqual(0, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size) self.assertEqual(len(U_BOOT_DATA), entry.size)
# Second u-boot, aligned to 16-byte boundary # Second u-boot, aligned to 16-byte boundary
self.assertIn('u-boot-align', entries) self.assertIn('u-boot-align', entries)
entry = entries['u-boot-align'] entry = entries['u-boot-align']
self.assertEqual(16, entry.pos) self.assertEqual(16, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size) self.assertEqual(len(U_BOOT_DATA), entry.size)
# Third u-boot, size 23 bytes # Third u-boot, size 23 bytes
self.assertIn('u-boot-size', entries) self.assertIn('u-boot-size', entries)
entry = entries['u-boot-size'] entry = entries['u-boot-size']
self.assertEqual(20, entry.pos) self.assertEqual(20, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.contents_size) self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
self.assertEqual(23, entry.size) self.assertEqual(23, entry.size)
# Fourth u-boot, placed immediate after the above # Fourth u-boot, placed immediate after the above
self.assertIn('u-boot-next', entries) self.assertIn('u-boot-next', entries)
entry = entries['u-boot-next'] entry = entries['u-boot-next']
self.assertEqual(43, entry.pos) self.assertEqual(43, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size) self.assertEqual(len(U_BOOT_DATA), entry.size)
# Fifth u-boot, placed at a fixed position # Fifth u-boot, placed at a fixed offset
self.assertIn('u-boot-fixed', entries) self.assertIn('u-boot-fixed', entries)
entry = entries['u-boot-fixed'] entry = entries['u-boot-fixed']
self.assertEqual(61, entry.pos) self.assertEqual(61, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size) self.assertEqual(len(U_BOOT_DATA), entry.size)
self.assertEqual(65, image._size) self.assertEqual(65, image._size)
@ -510,32 +550,32 @@ class TestFunctional(unittest.TestCase):
# First u-boot with padding before and after # First u-boot with padding before and after
self.assertIn('u-boot', entries) self.assertIn('u-boot', entries)
entry = entries['u-boot'] entry = entries['u-boot']
self.assertEqual(0, entry.pos) self.assertEqual(0, entry.offset)
self.assertEqual(3, entry.pad_before) self.assertEqual(3, entry.pad_before)
self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size) self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
# Second u-boot has an aligned size, but it has no effect # Second u-boot has an aligned size, but it has no effect
self.assertIn('u-boot-align-size-nop', entries) self.assertIn('u-boot-align-size-nop', entries)
entry = entries['u-boot-align-size-nop'] entry = entries['u-boot-align-size-nop']
self.assertEqual(12, entry.pos) self.assertEqual(12, entry.offset)
self.assertEqual(4, entry.size) self.assertEqual(4, entry.size)
# Third u-boot has an aligned size too # Third u-boot has an aligned size too
self.assertIn('u-boot-align-size', entries) self.assertIn('u-boot-align-size', entries)
entry = entries['u-boot-align-size'] entry = entries['u-boot-align-size']
self.assertEqual(16, entry.pos) self.assertEqual(16, entry.offset)
self.assertEqual(32, entry.size) self.assertEqual(32, entry.size)
# Fourth u-boot has an aligned end # Fourth u-boot has an aligned end
self.assertIn('u-boot-align-end', entries) self.assertIn('u-boot-align-end', entries)
entry = entries['u-boot-align-end'] entry = entries['u-boot-align-end']
self.assertEqual(48, entry.pos) self.assertEqual(48, entry.offset)
self.assertEqual(16, entry.size) self.assertEqual(16, entry.size)
# Fifth u-boot immediately afterwards # Fifth u-boot immediately afterwards
self.assertIn('u-boot-align-both', entries) self.assertIn('u-boot-align-both', entries)
entry = entries['u-boot-align-both'] entry = entries['u-boot-align-both']
self.assertEqual(64, entry.pos) self.assertEqual(64, entry.offset)
self.assertEqual(64, entry.size) self.assertEqual(64, entry.size)
self.CheckNoGaps(entries) self.CheckNoGaps(entries)
@ -556,10 +596,10 @@ class TestFunctional(unittest.TestCase):
"power of two", str(e.exception)) "power of two", str(e.exception))
def testPackInvalidAlign(self): def testPackInvalidAlign(self):
"""Test detection of an position that does not match its alignment""" """Test detection of an offset that does not match its alignment"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('12_pack_inv_align.dts') self._DoTestFile('12_pack_inv_align.dts')
self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match " self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
"align 0x4 (4)", str(e.exception)) "align 0x4 (4)", str(e.exception))
def testPackInvalidSizeAlign(self): def testPackInvalidSizeAlign(self):
@ -573,7 +613,7 @@ class TestFunctional(unittest.TestCase):
"""Test that overlapping regions are detected""" """Test that overlapping regions are detected"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('14_pack_overlap.dts') self._DoTestFile('14_pack_overlap.dts')
self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps " self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
"with previous entry '/binman/u-boot' ending at 0x4 (4)", "with previous entry '/binman/u-boot' ending at 0x4 (4)",
str(e.exception)) str(e.exception))
@ -651,11 +691,11 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 + self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
U_BOOT_DATA, data) U_BOOT_DATA, data)
def testPackZeroPosition(self): def testPackZeroOffset(self):
"""Test that an entry at position 0 is not given a new position""" """Test that an entry at offset 0 is not given a new offset"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('25_pack_zero_size.dts') self._DoTestFile('25_pack_zero_size.dts')
self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps " self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
"with previous entry '/binman/u-boot' ending at 0x4 (4)", "with previous entry '/binman/u-boot' ending at 0x4 (4)",
str(e.exception)) str(e.exception))
@ -672,10 +712,10 @@ class TestFunctional(unittest.TestCase):
"using end-at-4gb", str(e.exception)) "using end-at-4gb", str(e.exception))
def testPackX86RomOutside(self): def testPackX86RomOutside(self):
"""Test that the end-at-4gb property checks for position boundaries""" """Test that the end-at-4gb property checks for offset boundaries"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('28_pack_4gb_outside.dts') self._DoTestFile('28_pack_4gb_outside.dts')
self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside " self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
"the section starting at 0xffffffe0 (4294967264)", "the section starting at 0xffffffe0 (4294967264)",
str(e.exception)) str(e.exception))
@ -697,9 +737,9 @@ class TestFunctional(unittest.TestCase):
"""Test that the Intel requires a descriptor entry""" """Test that the Intel requires a descriptor entry"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('30_x86-rom-me-no-desc.dts') self._DoTestFile('30_x86-rom-me-no-desc.dts')
self.assertIn("Node '/binman/intel-me': No position set with " self.assertIn("Node '/binman/intel-me': No offset set with "
"pos-unset: should another entry provide this correct " "offset-unset: should another entry provide this correct "
"position?", str(e.exception)) "offset?", str(e.exception))
def testPackX86RomMe(self): def testPackX86RomMe(self):
"""Test that an x86 ROM with an ME region can be created""" """Test that an x86 ROM with an ME region can be created"""
@ -728,7 +768,7 @@ class TestFunctional(unittest.TestCase):
Returns: Returns:
Tuple: Tuple:
Contents of first region (U-Boot or SPL) Contents of first region (U-Boot or SPL)
Position and size components of microcode pointer, as inserted Offset and size components of microcode pointer, as inserted
in the above (two 4-byte words) in the above (two 4-byte words)
""" """
data = self._DoReadFile(dts_fname, True) data = self._DoReadFile(dts_fname, True)
@ -761,7 +801,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(ucode_data, ucode_content[:len(ucode_data)]) self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
# Check that the microcode pointer was inserted. It should match the # Check that the microcode pointer was inserted. It should match the
# expected position and size # expected offset and size
pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
len(ucode_data)) len(ucode_data))
u_boot = data[:len(nodtb_data)] u_boot = data[:len(nodtb_data)]
@ -806,7 +846,7 @@ class TestFunctional(unittest.TestCase):
ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA) ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
# Check that the microcode pointer was inserted. It should match the # Check that the microcode pointer was inserted. It should match the
# expected position and size # expected offset and size
pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
len(ucode_data)) len(ucode_data))
first = data[:len(U_BOOT_NODTB_DATA)] first = data[:len(U_BOOT_NODTB_DATA)]
@ -890,7 +930,7 @@ class TestFunctional(unittest.TestCase):
"""Test that microcode must be placed within the image""" """Test that microcode must be placed within the image"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoReadFile('41_unknown_pos_size.dts', True) self._DoReadFile('41_unknown_pos_size.dts', True)
self.assertIn("Section '/binman': Unable to set pos/size for unknown " self.assertIn("Section '/binman': Unable to set offset/size for unknown "
"entry 'invalid-entry'", str(e.exception)) "entry 'invalid-entry'", str(e.exception))
def testPackFsp(self): def testPackFsp(self):
@ -984,7 +1024,7 @@ class TestFunctional(unittest.TestCase):
elf_fname = self.TestFile('u_boot_binman_syms') elf_fname = self.TestFile('u_boot_binman_syms')
syms = elf.GetSymbols(elf_fname, ['binman', 'image']) syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr) self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
with open(self.TestFile('u_boot_binman_syms')) as fd: with open(self.TestFile('u_boot_binman_syms')) as fd:
TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
@ -1003,27 +1043,32 @@ class TestFunctional(unittest.TestCase):
def testSections(self): def testSections(self):
"""Basic test of sections""" """Basic test of sections"""
data = self._DoReadFile('55_sections.dts') data = self._DoReadFile('55_sections.dts')
expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + '&' * 8 expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
U_BOOT_DATA + '&' * 4)
self.assertEqual(expected, data) self.assertEqual(expected, data)
def testMap(self): def testMap(self):
"""Tests outputting a map of the images""" """Tests outputting a map of the images"""
_, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True) _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
self.assertEqual('''Position Size Name self.assertEqual('''ImagePos Offset Size Name
00000000 00000010 section@0 00000000 00000000 00000028 main-section
00000000 00000004 u-boot 00000000 00000000 00000010 section@0
00000010 00000010 section@1 00000000 00000000 00000004 u-boot
00000000 00000004 u-boot 00000010 00000010 00000010 section@1
00000010 00000000 00000004 u-boot
00000020 00000020 00000004 section@2
00000020 00000000 00000004 u-boot
''', map_data) ''', map_data)
def testNamePrefix(self): def testNamePrefix(self):
"""Tests that name prefixes are used""" """Tests that name prefixes are used"""
_, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True) _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
self.assertEqual('''Position Size Name self.assertEqual('''ImagePos Offset Size Name
00000000 00000010 section@0 00000000 00000000 00000028 main-section
00000000 00000004 ro-u-boot 00000000 00000000 00000010 section@0
00000010 00000010 section@1 00000000 00000000 00000004 ro-u-boot
00000000 00000004 rw-u-boot 00000010 00000010 00000010 section@1
00000010 00000000 00000004 rw-u-boot
''', map_data) ''', map_data)
def testUnknownContents(self): def testUnknownContents(self):
@ -1042,25 +1087,31 @@ class TestFunctional(unittest.TestCase):
'2 to 1', str(e.exception)) '2 to 1', str(e.exception))
def testUpdateFdt(self): def testUpdateFdt(self):
"""Test that we can update the device tree with pos/size info""" """Test that we can update the device tree with offset/size info"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts', _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
update_dtb=True) update_dtb=True)
props = self._GetPropTree(out_dtb_fname, ['pos', 'size']) dtb = fdt.Fdt(out_dtb_fname)
with open('/tmp/x.dtb', 'wb') as outf: dtb.Scan()
with open(out_dtb_fname) as inf: props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
outf.write(inf.read())
self.assertEqual({ self.assertEqual({
'_testing:pos': 32, 'image-pos': 0,
'offset': 0,
'_testing:offset': 32,
'_testing:size': 1, '_testing:size': 1,
'section@0/u-boot:pos': 0, '_testing:image-pos': 32,
'section@0/u-boot:offset': 0,
'section@0/u-boot:size': len(U_BOOT_DATA), 'section@0/u-boot:size': len(U_BOOT_DATA),
'section@0:pos': 0, 'section@0/u-boot:image-pos': 0,
'section@0:offset': 0,
'section@0:size': 16, 'section@0:size': 16,
'section@0:image-pos': 0,
'section@1/u-boot:pos': 0, 'section@1/u-boot:offset': 0,
'section@1/u-boot:size': len(U_BOOT_DATA), 'section@1/u-boot:size': len(U_BOOT_DATA),
'section@1:pos': 16, 'section@1/u-boot:image-pos': 16,
'section@1:offset': 16,
'section@1:size': 16, 'section@1:size': 16,
'section@1:image-pos': 16,
'size': 40 'size': 40
}, props) }, props)
@ -1071,5 +1122,248 @@ class TestFunctional(unittest.TestCase):
self.assertIn('Could not complete processing of Fdt: remaining ' self.assertIn('Could not complete processing of Fdt: remaining '
'[<_testing.Entry__testing', str(e.exception)) '[<_testing.Entry__testing', str(e.exception))
def testEntryArgs(self):
"""Test passing arguments to entries from the command line"""
entry_args = {
'test-str-arg': 'test1',
'test-int-arg': '456',
}
self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
self.assertIn('image', control.images)
entry = control.images['image'].GetEntries()['_testing']
self.assertEqual('test0', entry.test_str_fdt)
self.assertEqual('test1', entry.test_str_arg)
self.assertEqual(123, entry.test_int_fdt)
self.assertEqual(456, entry.test_int_arg)
def testEntryArgsMissing(self):
"""Test missing arguments and properties"""
entry_args = {
'test-int-arg': '456',
}
self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
entry = control.images['image'].GetEntries()['_testing']
self.assertEqual('test0', entry.test_str_fdt)
self.assertEqual(None, entry.test_str_arg)
self.assertEqual(None, entry.test_int_fdt)
self.assertEqual(456, entry.test_int_arg)
def testEntryArgsRequired(self):
"""Test missing arguments and properties"""
entry_args = {
'test-int-arg': '456',
}
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('64_entry_args_required.dts')
self.assertIn("Node '/binman/_testing': Missing required "
'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
str(e.exception))
def testEntryArgsInvalidFormat(self):
"""Test that an invalid entry-argument format is detected"""
args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
with self.assertRaises(ValueError) as e:
self._DoBinman(*args)
self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
def testEntryArgsInvalidInteger(self):
"""Test that an invalid entry-argument integer is detected"""
entry_args = {
'test-int-arg': 'abc',
}
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
"'test-int-arg' (value 'abc') to integer",
str(e.exception))
def testEntryArgsInvalidDatatype(self):
"""Test that an invalid entry-argument datatype is detected
This test could be written in entry_test.py except that it needs
access to control.entry_args, which seems more than that module should
be able to see.
"""
entry_args = {
'test-bad-datatype-arg': '12',
}
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
entry_args=entry_args)
self.assertIn('GetArg() internal error: Unknown data type ',
str(e.exception))
def testText(self):
"""Test for a text entry type"""
entry_args = {
'test-id': TEXT_DATA,
'test-id2': TEXT_DATA2,
'test-id3': TEXT_DATA3,
}
data, _, _, _ = self._DoReadFileDtb('66_text.dts',
entry_args=entry_args)
expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
TEXT_DATA3 + 'some text')
self.assertEqual(expected, data)
def testEntryDocs(self):
"""Test for creation of entry documentation"""
with test_util.capture_sys_output() as (stdout, stderr):
control.WriteEntryDocs(binman.GetEntryModules())
self.assertTrue(len(stdout.getvalue()) > 0)
def testEntryDocsMissing(self):
"""Test handling of missing entry documentation"""
with self.assertRaises(ValueError) as e:
with test_util.capture_sys_output() as (stdout, stderr):
control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
self.assertIn('Documentation is missing for modules: u_boot',
str(e.exception))
def testFmap(self):
"""Basic test of generation of a flashrom fmap"""
data = self._DoReadFile('67_fmap.dts')
fhdr, fentries = fmap_util.DecodeFmap(data[32:])
expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
self.assertEqual(expected, data[:32])
self.assertEqual('__FMAP__', fhdr.signature)
self.assertEqual(1, fhdr.ver_major)
self.assertEqual(0, fhdr.ver_minor)
self.assertEqual(0, fhdr.base)
self.assertEqual(16 + 16 +
fmap_util.FMAP_HEADER_LEN +
fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
self.assertEqual('FMAP', fhdr.name)
self.assertEqual(3, fhdr.nareas)
for fentry in fentries:
self.assertEqual(0, fentry.flags)
self.assertEqual(0, fentries[0].offset)
self.assertEqual(4, fentries[0].size)
self.assertEqual('RO_U_BOOT', fentries[0].name)
self.assertEqual(16, fentries[1].offset)
self.assertEqual(4, fentries[1].size)
self.assertEqual('RW_U_BOOT', fentries[1].name)
self.assertEqual(32, fentries[2].offset)
self.assertEqual(fmap_util.FMAP_HEADER_LEN +
fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
self.assertEqual('FMAP', fentries[2].name)
def testBlobNamedByArg(self):
"""Test we can add a blob with the filename coming from an entry arg"""
entry_args = {
'cros-ec-rw-path': 'ecrw.bin',
}
data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
entry_args=entry_args)
def testFill(self):
"""Test for an fill entry type"""
data = self._DoReadFile('69_fill.dts')
expected = 8 * chr(0xff) + 8 * chr(0)
self.assertEqual(expected, data)
def testFillNoSize(self):
"""Test for an fill entry type with no size"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('70_fill_no_size.dts')
self.assertIn("'fill' entry must have a size property",
str(e.exception))
def _HandleGbbCommand(self, pipe_list):
"""Fake calls to the futility utility"""
if pipe_list[0][0] == 'futility':
fname = pipe_list[0][-1]
# Append our GBB data to the file, which will happen every time the
# futility command is called.
with open(fname, 'a') as fd:
fd.write(GBB_DATA)
return command.CommandResult()
def testGbb(self):
"""Test for the Chromium OS Google Binary Block"""
command.test_result = self._HandleGbbCommand
entry_args = {
'keydir': 'devkeys',
'bmpblk': 'bmpblk.bin',
}
data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
# Since futility
expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
self.assertEqual(expected, data)
def testGbbTooSmall(self):
"""Test for the Chromium OS Google Binary Block being large enough"""
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('72_gbb_too_small.dts')
self.assertIn("Node '/binman/gbb': GBB is too small",
str(e.exception))
def testGbbNoSize(self):
"""Test for the Chromium OS Google Binary Block having a size"""
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('73_gbb_no_size.dts')
self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
str(e.exception))
def _HandleVblockCommand(self, pipe_list):
"""Fake calls to the futility utility"""
if pipe_list[0][0] == 'futility':
fname = pipe_list[0][3]
with open(fname, 'w') as fd:
fd.write(VBLOCK_DATA)
return command.CommandResult()
def testVblock(self):
"""Test for the Chromium OS Verified Boot Block"""
command.test_result = self._HandleVblockCommand
entry_args = {
'keydir': 'devkeys',
}
data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
entry_args=entry_args)
expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
self.assertEqual(expected, data)
def testVblockNoContent(self):
"""Test we detect a vblock which has no content to sign"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('75_vblock_no_content.dts')
self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
'property', str(e.exception))
def testVblockBadPhandle(self):
"""Test that we detect a vblock with an invalid phandle in contents"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('76_vblock_bad_phandle.dts')
self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
'1000', str(e.exception))
def testVblockBadEntry(self):
"""Test that we detect an entry that points to a non-entry"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('77_vblock_bad_entry.dts')
self.assertIn("Node '/binman/vblock': Cannot find entry for node "
"'other'", str(e.exception))
def testTpl(self):
"""Test that an image with TPL and ots device tree can be created"""
# ELF file with a '__bss_size' symbol
with open(self.TestFile('bss_data')) as fd:
TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
data = self._DoReadFile('78_u_boot_tpl.dts')
self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
def testUsesPos(self):
"""Test that the 'pos' property cannot be used anymore"""
with self.assertRaises(ValueError) as e:
data = self._DoReadFile('79_uses_pos.dts')
self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
"'pos'", str(e.exception))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -57,7 +57,7 @@ class Image:
def AddMissingProperties(self): def AddMissingProperties(self):
"""Add properties that are not present in the device tree """Add properties that are not present in the device tree
When binman has completed packing the entries the position and size of When binman has completed packing the entries the offset and size of
each entry are known. But before this the device tree may not specify each entry are known. But before this the device tree may not specify
these. Add any missing properties, with a dummy value, so that the these. Add any missing properties, with a dummy value, so that the
size of the entry is correct. That way we can insert the correct values size of the entry is correct. That way we can insert the correct values
@ -73,13 +73,13 @@ class Image:
""" """
self._section.GetEntryContents() self._section.GetEntryContents()
def GetEntryPositions(self): def GetEntryOffsets(self):
"""Handle entries that want to set the position/size of other entries """Handle entries that want to set the offset/size of other entries
This calls each entry's GetPositions() method. If it returns a list This calls each entry's GetOffsets() method. If it returns a list
of entries to update, it updates them. of entries to update, it updates them.
""" """
self._section.GetEntryPositions() self._section.GetEntryOffsets()
def PackEntries(self): def PackEntries(self):
"""Pack all entries into the image""" """Pack all entries into the image"""
@ -96,6 +96,9 @@ class Image:
def SetCalculatedProperties(self): def SetCalculatedProperties(self):
self._section.SetCalculatedProperties() self._section.SetCalculatedProperties()
def SetImagePos(self):
self._section.SetImagePos(0)
def ProcessEntryContents(self): def ProcessEntryContents(self):
"""Call the ProcessContents() method for each entry """Call the ProcessContents() method for each entry
@ -121,5 +124,6 @@ class Image:
filename = '%s.map' % self._name filename = '%s.map' % self._name
fname = tools.GetOutputFilename(filename) fname = tools.GetOutputFilename(filename)
with open(fname, 'w') as fd: with open(fname, 'w') as fd:
print('%8s %8s %s' % ('Position', 'Size', 'Name'), file=fd) print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'),
file=fd)
self._section.WriteMap(fd, 0) self._section.WriteMap(fd, 0)

View file

@ -24,7 +24,7 @@
u-boot-fixed { u-boot-fixed {
type = "u-boot"; type = "u-boot";
pos = <61>; offset = <61>;
}; };
}; };
}; };

View file

@ -6,7 +6,7 @@
binman { binman {
u-boot { u-boot {
pos = <5>; offset = <5>;
align = <4>; align = <4>;
}; };
}; };

View file

@ -10,7 +10,7 @@
u-boot-align { u-boot-align {
type = "u-boot"; type = "u-boot";
pos = <3>; offset = <3>;
}; };
}; };
}; };

View file

@ -10,7 +10,7 @@
}; };
u-boot { u-boot {
pos = <20>; offset = <20>;
}; };
}; };
}; };

View file

@ -5,13 +5,13 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
u-boot { u-boot {
pos = <22>; offset = <22>;
}; };
u-boot-spl { u-boot-spl {
pos = <1>; offset = <1>;
}; };
}; };
}; };

View file

@ -9,7 +9,7 @@
}; };
u-boot-spl { u-boot-spl {
pos = <0>; offset = <0>;
}; };
}; };
}; };

View file

@ -5,14 +5,14 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
u-boot { u-boot {
pos = <0xfffffff0>; offset = <0xfffffff0>;
}; };
u-boot-spl { u-boot-spl {
pos = <0xfffffff7>; offset = <0xfffffff7>;
}; };
}; };
}; };

View file

@ -5,15 +5,15 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <32>; size = <32>;
u-boot { u-boot {
pos = <0>; offset = <0>;
}; };
u-boot-spl { u-boot-spl {
pos = <0xffffffeb>; offset = <0xffffffeb>;
}; };
}; };
}; };

View file

@ -5,15 +5,15 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <32>; size = <32>;
u-boot { u-boot {
pos = <0xffffffe0>; offset = <0xffffffe0>;
}; };
u-boot-spl { u-boot-spl {
pos = <0xffffffeb>; offset = <0xffffffeb>;
}; };
}; };
}; };

View file

@ -5,12 +5,12 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <16>; size = <16>;
intel-me { intel-me {
filename = "me.bin"; filename = "me.bin";
pos-unset; offset-unset;
}; };
}; };
}; };

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x800000>; size = <0x800000>;
intel-descriptor { intel-descriptor {
@ -14,7 +14,7 @@
intel-me { intel-me {
filename = "me.bin"; filename = "me.bin";
pos-unset; offset-unset;
}; };
}; };
}; };

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {
}; };

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-with-ucode-ptr { u-boot-with-ucode-ptr {

View file

@ -5,12 +5,12 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <16>; size = <16>;
intel-me { intel-me {
filename = "me.bin"; filename = "me.bin";
pos-unset; offset-unset;
intval = <3>; intval = <3>;
intarray = <5 6>; intarray = <5 6>;
byteval = [08]; byteval = [08];

View file

@ -5,7 +5,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-spl-with-ucode-ptr { u-boot-spl-with-ucode-ptr {

View file

@ -10,7 +10,7 @@
}; };
u-boot { u-boot {
pos = <20>; offset = <20>;
}; };
u-boot-spl2 { u-boot-spl2 {

View file

@ -24,5 +24,9 @@
u-boot { u-boot {
}; };
}; };
section@2 {
u-boot {
};
};
}; };
}; };

View file

@ -7,7 +7,7 @@
#size-cells = <1>; #size-cells = <1>;
binman { binman {
sort-by-pos; sort-by-offset;
end-at-4gb; end-at-4gb;
size = <0x200>; size = <0x200>;
u-boot-spl-with-ucode-ptr { u-boot-spl-with-ucode-ptr {

View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
test-str-fdt = "test0";
test-int-fdt = <123>;
};
};
};

View file

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
test-str-fdt = "test0";
};
};
};

View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
require-args;
test-str-fdt = "test0";
};
};
};

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
_testing {
test-str-fdt = "test0";
test-int-fdt = <123>;
force-bad-datatype;
};
};
};

View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
text {
size = <8>;
text-label = "test-id";
};
text2 {
type = "text";
text-label = "test-id2";
};
text3 {
type = "text";
text-label = "test-id3";
};
/* This one does not use command-line args */
text4 {
type = "text";
text-label = "test-id4";
test-id4 = "some text";
};
};
};

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
section@0 {
read-only;
name-prefix = "ro-";
size = <0x10>;
pad-byte = <0x21>;
u-boot {
};
};
section@1 {
name-prefix = "rw-";
size = <0x10>;
pad-byte = <0x61>;
u-boot {
};
};
fmap {
};
};
};

View file

@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
cros-ec-rw {
};
};
};

View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <16>;
fill {
size = <8>;
fill-byte = [ff];
};
};
};

View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <16>;
fill {
fill-byte = [ff];
};
};
};

View file

@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
gbb {
size = <0x2180>;
flags {
dev-screen-short-delay;
load-option-roms;
enable-alternate-os;
force-dev-switch-on;
force-dev-boot-usb;
disable-fw-rollback-check;
enter-triggers-tonorm;
force-dev-boot-legacy;
faft-key-override;
disable-ec-software-sync;
default-dev-boot-legacy;
disable-pd-software-sync;
disable-lid-shutdown;
force-dev-boot-fastboot-full-cap;
enable-serial;
disable-dwmp;
};
};
};
};

View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
gbb {
size = <0x200>;
};
};
};

View file

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
gbb {
};
};
};

View file

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u_boot: u-boot {
};
vblock {
content = <&u_boot &dtb>;
keyblock = "firmware.keyblock";
signprivate = "firmware_data_key.vbprivk";
version = <1>;
kernelkey = "kernel_subkey.vbpubk";
preamble-flags = <1>;
};
/*
* Put this after the vblock so that its contents are not
* available when the vblock first tries to obtain its contents
*/
dtb: u-boot-dtb {
};
};
};

View file

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u_boot: u-boot {
};
vblock {
keyblock = "firmware.keyblock";
signprivate = "firmware_data_key.vbprivk";
version = <1>;
kernelkey = "kernel_subkey.vbpubk";
preamble-flags = <1>;
};
dtb: u-boot-dtb {
};
};
};

View file

@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u_boot: u-boot {
};
vblock {
content = <1000>;
keyblock = "firmware.keyblock";
signprivate = "firmware_data_key.vbprivk";
version = <1>;
kernelkey = "kernel_subkey.vbpubk";
preamble-flags = <1>;
};
dtb: u-boot-dtb {
};
};
};

View file

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u_boot: u-boot {
};
vblock {
content = <&u_boot &other>;
keyblock = "firmware.keyblock";
signprivate = "firmware_data_key.vbprivk";
version = <1>;
kernelkey = "kernel_subkey.vbpubk";
preamble-flags = <1>;
};
dtb: u-boot-dtb {
};
};
other: other {
};
};

View file

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot-tpl {
};
u-boot-tpl-dtb {
};
};
};

View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
binman {
u-boot {
pos = <10>;
};
};
};

Binary file not shown.

View file

@ -8,6 +8,6 @@
#define CONFIG_BINMAN #define CONFIG_BINMAN
#include <binman_sym.h> #include <binman_sym.h>
binman_sym_declare(unsigned long, u_boot_spl, pos); binman_sym_declare(unsigned long, u_boot_spl, offset);
binman_sym_declare(unsigned long long, u_boot_spl2, pos); binman_sym_declare(unsigned long long, u_boot_spl2, offset);
binman_sym_declare(unsigned long, u_boot_any, pos); binman_sym_declare(unsigned long, u_boot_any, image_pos);

View file

@ -181,7 +181,15 @@ class Node:
self.subnodes = [] self.subnodes = []
self.props = {} self.props = {}
def _FindNode(self, name): def GetFdt(self):
"""Get the Fdt object for this node
Returns:
Fdt object
"""
return self._fdt
def FindNode(self, name):
"""Find a node given its name """Find a node given its name
Args: Args:
@ -314,6 +322,17 @@ class Fdt:
with open(self._fname) as fd: with open(self._fname) as fd:
self._fdt_obj = libfdt.Fdt(fd.read()) self._fdt_obj = libfdt.Fdt(fd.read())
def LookupPhandle(self, phandle):
"""Look up a phandle
Args:
phandle: Phandle to look up (int)
Returns:
Node object the phandle points to
"""
return self.phandle_to_node.get(phandle)
def Scan(self, root='/'): def Scan(self, root='/'):
"""Scan a device tree, building up a tree of Node objects """Scan a device tree, building up a tree of Node objects
@ -349,7 +368,7 @@ class Fdt:
if len(parts) < 2: if len(parts) < 2:
return None return None
for part in parts[1:]: for part in parts[1:]:
node = node._FindNode(part) node = node.FindNode(part)
if not node: if not node:
return None return None
return node return node

View file

@ -5,6 +5,9 @@
# Written by Simon Glass <sjg@chromium.org> # Written by Simon Glass <sjg@chromium.org>
# #
# Utility functions for reading from a device tree. Once the upstream pylibfdt
# implementation advances far enough, we should be able to drop these.
import os import os
import struct import struct
import sys import sys
@ -90,6 +93,16 @@ def EnsureCompiled(fname, capture_stderr=False):
return dtb_output return dtb_output
def GetInt(node, propname, default=None): def GetInt(node, propname, default=None):
"""Get an integer from a property
Args:
node: Node object to read from
propname: property name to read
default: Default value to use if the node/property do not exist
Returns:
Integer value read, or default if none
"""
prop = node.props.get(propname) prop = node.props.get(propname)
if not prop: if not prop:
return default return default
@ -100,6 +113,16 @@ def GetInt(node, propname, default=None):
return value return value
def GetString(node, propname, default=None): def GetString(node, propname, default=None):
"""Get a string from a property
Args:
node: Node object to read from
propname: property name to read
default: Default value to use if the node/property do not exist
Returns:
String value read, or default if none
"""
prop = node.props.get(propname) prop = node.props.get(propname)
if not prop: if not prop:
return default return default
@ -110,6 +133,79 @@ def GetString(node, propname, default=None):
return value return value
def GetBool(node, propname, default=False): def GetBool(node, propname, default=False):
"""Get an boolean from a property
Args:
node: Node object to read from
propname: property name to read
default: Default value to use if the node/property do not exist
Returns:
Boolean value read, or default if none (if you set this to True the
function will always return True)
"""
if propname in node.props: if propname in node.props:
return True return True
return default return default
def GetByte(node, propname, default=None):
"""Get an byte from a property
Args:
node: Node object to read from
propname: property name to read
default: Default value to use if the node/property do not exist
Returns:
Byte value read, or default if none
"""
prop = node.props.get(propname)
if not prop:
return default
value = prop.value
if isinstance(value, list):
raise ValueError("Node '%s' property '%s' has list value: expecting "
"a single byte" % (node.name, propname))
if len(value) != 1:
raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
(node.name, propname, len(value), 1))
return ord(value[0])
def GetPhandleList(node, propname):
"""Get a list of phandles from a property
Args:
node: Node object to read from
propname: property name to read
Returns:
List of phandles read, each an integer
"""
prop = node.props.get(propname)
if not prop:
return None
value = prop.value
if not isinstance(value, list):
value = [value]
return [fdt32_to_cpu(v) for v in value]
def GetDatatype(node, propname, datatype):
"""Get a value of a given type from a property
Args:
node: Node object to read from
propname: property name to read
datatype: Type to read (str or int)
Returns:
value read, or None if none
Raises:
ValueError if datatype is not str or int
"""
if datatype == str:
return GetString(node, propname)
elif datatype == int:
return GetInt(node, propname)
raise ValueError("fdt_util internal error: Unknown data type '%s'" %
datatype)

View file

@ -115,6 +115,9 @@ class TestFdt(unittest.TestCase):
fdt.CheckErr(-libfdt.NOTFOUND, 'hello') fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception)) self.assertIn('FDT_ERR_NOTFOUND: hello', str(e.exception))
def testGetFdt(self):
node = self.dtb.GetNode('/spl-test')
self.assertEqual(self.dtb, node.GetFdt())
class TestNode(unittest.TestCase): class TestNode(unittest.TestCase):
"""Test operation of the Node class""" """Test operation of the Node class"""
@ -155,12 +158,12 @@ class TestNode(unittest.TestCase):
self.assertEqual(prop.value, value) self.assertEqual(prop.value, value)
def testFindNode(self): def testFindNode(self):
"""Tests that we can find a node using the _FindNode() functoin""" """Tests that we can find a node using the FindNode() functoin"""
node = self.dtb.GetRoot()._FindNode('i2c@0') node = self.dtb.GetRoot().FindNode('i2c@0')
self.assertEqual('i2c@0', node.name) self.assertEqual('i2c@0', node.name)
subnode = node._FindNode('pmic@9') subnode = node.FindNode('pmic@9')
self.assertEqual('pmic@9', subnode.name) self.assertEqual('pmic@9', subnode.name)
self.assertEqual(None, node._FindNode('missing')) self.assertEqual(None, node.FindNode('missing'))
def testRefreshMissingNode(self): def testRefreshMissingNode(self):
"""Test refreshing offsets when an extra node is present in dtb""" """Test refreshing offsets when an extra node is present in dtb"""
@ -188,6 +191,14 @@ class TestNode(unittest.TestCase):
self.assertIn("Internal error, property 'notstring' missing, offset ", self.assertIn("Internal error, property 'notstring' missing, offset ",
str(e.exception)) str(e.exception))
def testLookupPhandle(self):
"""Test looking up a single phandle"""
dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
node = dtb.GetNode('/phandle-source2')
prop = node.props['clocks']
target = dtb.GetNode('/phandle-target')
self.assertEqual(target, dtb.LookupPhandle(fdt32_to_cpu(prop.value)))
class TestProp(unittest.TestCase): class TestProp(unittest.TestCase):
"""Test operation of the Prop class""" """Test operation of the Prop class"""
@ -380,6 +391,36 @@ class TestFdtUtil(unittest.TestCase):
self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True)) self.assertEqual(True, fdt_util.GetBool(self.node, 'missing', True))
self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False)) self.assertEqual(False, fdt_util.GetBool(self.node, 'missing', False))
def testGetByte(self):
self.assertEqual(5, fdt_util.GetByte(self.node, 'byteval'))
self.assertEqual(3, fdt_util.GetByte(self.node, 'missing', 3))
with self.assertRaises(ValueError) as e:
fdt_util.GetByte(self.node, 'longbytearray')
self.assertIn("property 'longbytearray' has list value: expecting a "
'single byte', str(e.exception))
with self.assertRaises(ValueError) as e:
fdt_util.GetByte(self.node, 'intval')
self.assertIn("property 'intval' has length 4, expecting 1",
str(e.exception))
def testGetPhandleList(self):
dtb = fdt.FdtScan('tools/dtoc/dtoc_test_phandle.dts')
node = dtb.GetNode('/phandle-source2')
self.assertEqual([1], fdt_util.GetPhandleList(node, 'clocks'))
node = dtb.GetNode('/phandle-source')
self.assertEqual([1, 2, 11, 3, 12, 13, 1],
fdt_util.GetPhandleList(node, 'clocks'))
self.assertEqual(None, fdt_util.GetPhandleList(node, 'missing'))
def testGetDataType(self):
self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int))
self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval',
str))
with self.assertRaises(ValueError) as e:
self.assertEqual(3, fdt_util.GetDatatype(self.node, 'boolval',
bool))
def testFdtCellsToCpu(self): def testFdtCellsToCpu(self):
val = self.node.props['intarray'].value val = self.node.props['intarray'].value
self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0)) self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))

View file

@ -61,8 +61,12 @@ def RunPipe(pipe_list, infile=None, outfile=None,
""" """
if test_result: if test_result:
if hasattr(test_result, '__call__'): if hasattr(test_result, '__call__'):
return test_result(pipe_list=pipe_list) result = test_result(pipe_list=pipe_list)
if result:
return result
else:
return test_result return test_result
# No result: fall through to normal processing
result = CommandResult() result = CommandResult()
last_pipe = None last_pipe = None
pipeline = list(pipe_list) pipeline = list(pipe_list)

View file

@ -3,16 +3,26 @@
# Copyright (c) 2016 Google, Inc # Copyright (c) 2016 Google, Inc
# #
import command
import os import os
import shutil import shutil
import tempfile import tempfile
import tout import tout
# Output directly (generally this is temporary)
outdir = None outdir = None
indirs = None
# True to keep the output directory around after exiting
preserve_outdir = False preserve_outdir = False
# Path to the Chrome OS chroot, if we know it
chroot_path = None
# Search paths to use for Filename(), used to find files
search_paths = []
def PrepareOutputDir(dirname, preserve=False): def PrepareOutputDir(dirname, preserve=False):
"""Select an output directory, ensuring it exists. """Select an output directory, ensuring it exists.
@ -106,8 +116,8 @@ def GetInputFilename(fname):
if os.path.exists(pathname): if os.path.exists(pathname):
return pathname return pathname
raise ValueError("Filename '%s' not found in input path (%s)" % raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
(fname, ','.join(indir))) (fname, ','.join(indir), os.getcwd()))
def Align(pos, align): def Align(pos, align):
if align: if align:
@ -117,3 +127,67 @@ def Align(pos, align):
def NotPowerOfTwo(num): def NotPowerOfTwo(num):
return num and (num & (num - 1)) return num and (num & (num - 1))
def Run(name, *args):
command.Run(name, *args, cwd=outdir)
def Filename(fname):
"""Resolve a file path to an absolute path.
If fname starts with ##/ and chroot is available, ##/ gets replaced with
the chroot path. If chroot is not available, this file name can not be
resolved, `None' is returned.
If fname is not prepended with the above prefix, and is not an existing
file, the actual file name is retrieved from the passed in string and the
search_paths directories (if any) are searched to for the file. If found -
the path to the found file is returned, `None' is returned otherwise.
Args:
fname: a string, the path to resolve.
Returns:
Absolute path to the file or None if not found.
"""
if fname.startswith('##/'):
if chroot_path:
fname = os.path.join(chroot_path, fname[3:])
else:
return None
# Search for a pathname that exists, and return it if found
if fname and not os.path.exists(fname):
for path in search_paths:
pathname = os.path.join(path, os.path.basename(fname))
if os.path.exists(pathname):
return pathname
# If not found, just return the standard, unchanged path
return fname
def ReadFile(fname):
"""Read and return the contents of a file.
Args:
fname: path to filename to read, where ## signifiies the chroot.
Returns:
data read from file, as a string.
"""
with open(Filename(fname), 'rb') as fd:
data = fd.read()
#self._out.Info("Read file '%s' size %d (%#0x)" %
#(fname, len(data), len(data)))
return data
def WriteFile(fname, data):
"""Write data into a file.
Args:
fname: path to filename to write
data: data to write to file, as a string
"""
#self._out.Info("Write file '%s' size %d (%#0x)" %
#(fname, len(data), len(data)))
with open(Filename(fname), 'wb') as fd:
fd.write(data)