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

lime2-spi
Tom Rini 6 years ago
commit a30691a538
  1. 2
      arch/arm/dts/sunxi-u-boot.dtsi
  2. 6
      arch/arm/dts/tegra-u-boot.dtsi
  3. 24
      arch/x86/dts/u-boot.dtsi
  4. 4
      common/spl/spl.c
  5. 2
      common/spl/spl_ram.c
  6. 2
      include/spl.h
  7. 135
      tools/binman/README
  8. 585
      tools/binman/README.entries
  9. 22
      tools/binman/binman.py
  10. 153
      tools/binman/bsection.py
  11. 6
      tools/binman/cmdline.py
  12. 35
      tools/binman/control.py
  13. 10
      tools/binman/elf.py
  14. 33
      tools/binman/elf_test.py
  15. 267
      tools/binman/entry.py
  16. 47
      tools/binman/etype/_testing.py
  17. 16
      tools/binman/etype/blob.py
  18. 34
      tools/binman/etype/blob_named_by_arg.py
  19. 22
      tools/binman/etype/cros_ec_rw.py
  20. 32
      tools/binman/etype/fill.py
  21. 61
      tools/binman/etype/fmap.py
  22. 96
      tools/binman/etype/gbb.py
  23. 10
      tools/binman/etype/intel_cmc.py
  24. 26
      tools/binman/etype/intel_descriptor.py
  25. 14
      tools/binman/etype/intel_fsp.py
  26. 15
      tools/binman/etype/intel_me.py
  27. 11
      tools/binman/etype/intel_mrc.py
  28. 10
      tools/binman/etype/intel_vbt.py
  29. 12
      tools/binman/etype/intel_vga.py
  30. 48
      tools/binman/etype/section.py
  31. 57
      tools/binman/etype/text.py
  32. 16
      tools/binman/etype/u_boot.py
  33. 9
      tools/binman/etype/u_boot_dtb.py
  34. 22
      tools/binman/etype/u_boot_dtb_with_ucode.py
  35. 11
      tools/binman/etype/u_boot_img.py
  36. 11
      tools/binman/etype/u_boot_nodtb.py
  37. 21
      tools/binman/etype/u_boot_spl.py
  38. 16
      tools/binman/etype/u_boot_spl_bss_pad.py
  39. 11
      tools/binman/etype/u_boot_spl_dtb.py
  40. 12
      tools/binman/etype/u_boot_spl_nodtb.py
  41. 43
      tools/binman/etype/u_boot_tpl.py
  42. 25
      tools/binman/etype/u_boot_tpl_dtb.py
  43. 12
      tools/binman/etype/u_boot_ucode.py
  44. 44
      tools/binman/etype/u_boot_with_ucode_ptr.py
  45. 74
      tools/binman/etype/vblock.py
  46. 14
      tools/binman/etype/x86_start16.py
  47. 14
      tools/binman/etype/x86_start16_spl.py
  48. 109
      tools/binman/fmap_util.py
  49. 428
      tools/binman/ftest.py
  50. 16
      tools/binman/image.py
  51. 2
      tools/binman/test/08_pack.dts
  52. 2
      tools/binman/test/12_pack_inv_align.dts
  53. 2
      tools/binman/test/14_pack_overlap.dts
  54. 2
      tools/binman/test/21_image_pad.dts
  55. 6
      tools/binman/test/24_sorted.dts
  56. 2
      tools/binman/test/25_pack_zero_size.dts
  57. 6
      tools/binman/test/27_pack_4gb_no_size.dts
  58. 6
      tools/binman/test/28_pack_4gb_outside.dts
  59. 6
      tools/binman/test/29_x86-rom.dts
  60. 4
      tools/binman/test/30_x86-rom-me-no-desc.dts
  61. 4
      tools/binman/test/31_x86-rom-me.dts
  62. 2
      tools/binman/test/34_x86_ucode.dts
  63. 2
      tools/binman/test/35_x86_single_ucode.dts
  64. 2
      tools/binman/test/37_x86_no_ucode.dts
  65. 2
      tools/binman/test/38_x86_ucode_missing_node.dts
  66. 2
      tools/binman/test/39_x86_ucode_missing_node2.dts
  67. 2
      tools/binman/test/40_x86_ucode_not_in_image.dts
  68. 2
      tools/binman/test/44_x86_optional_ucode.dts
  69. 4
      tools/binman/test/45_prop_test.dts
  70. 2
      tools/binman/test/49_x86_ucode_spl.dts
  71. 2
      tools/binman/test/53_symbols.dts
  72. 4
      tools/binman/test/55_sections.dts
  73. 2
      tools/binman/test/58_x86_ucode_spl_needs_retry.dts
  74. 14
      tools/binman/test/62_entry_args.dts
  75. 13
      tools/binman/test/63_entry_args_missing.dts
  76. 14
      tools/binman/test/64_entry_args_required.dts
  77. 15
      tools/binman/test/65_entry_args_unknown_datatype.dts
  78. 28
      tools/binman/test/66_text.dts
  79. 29
      tools/binman/test/67_fmap.dts
  80. 12
      tools/binman/test/68_blob_named_by_arg.dts
  81. 15
      tools/binman/test/69_fill.dts
  82. 14
      tools/binman/test/70_fill_no_size.dts
  83. 31
      tools/binman/test/71_gbb.dts
  84. 10
      tools/binman/test/72_gbb_too_small.dts
  85. 9
      tools/binman/test/73_gbb_no_size.dts
  86. 28
      tools/binman/test/74_vblock.dts
  87. 23
      tools/binman/test/75_vblock_no_content.dts
  88. 24
      tools/binman/test/76_vblock_bad_phandle.dts
  89. 27
      tools/binman/test/77_vblock_bad_entry.dts
  90. 11
      tools/binman/test/78_u_boot_tpl.dts
  91. 10
      tools/binman/test/79_uses_pos.dts
  92. BIN
      tools/binman/test/u_boot_binman_syms
  93. 6
      tools/binman/test/u_boot_binman_syms.c
  94. 23
      tools/dtoc/fdt.py
  95. 96
      tools/dtoc/fdt_util.py
  96. 49
      tools/dtoc/test_fdt.py
  97. 8
      tools/patman/command.py
  98. 80
      tools/patman/tools.py

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

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

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

@ -34,7 +34,7 @@ DECLARE_GLOBAL_DATA_PTR;
u32 *boot_params_ptr = NULL;
/* 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 */
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)
{
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;

@ -49,7 +49,7 @@ static int spl_ram_load_image(struct spl_image_info *spl_image,
load.read = spl_ram_load_read;
spl_load_simple_fit(spl_image, &load, 0, header);
} 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");
/*

@ -60,7 +60,7 @@ struct spl_load_info {
* 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.
*/
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.

@ -238,7 +238,7 @@ below:
filename = "spl/sunxi-spl.bin";
};
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.
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
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.
pos:
This sets the position of an entry within the image. The first byte
of the image is normally at position 0. If 'pos' is not provided,
binman sets it to the end of the previous region, or the start of
the image's entry area (normally 0) if there is no previous region.
offset:
This sets the offset of an entry within the image or section containing
it. The first byte of the image is normally at offset 0. If 'offset' is
not provided, binman sets it to the end of the previous region, or the
start of the image's entry area (normally 0) if there is no previous
region.
align:
This sets the alignment of the entry. The entry 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
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
@ -316,12 +317,18 @@ type:
possible to use any name, and then add (for example) 'type = "u-boot"'
to specify the type.
pos-unset:
Indicates that the position of this entry should not be set by placing
offset-unset:
Indicates that the offset of this entry should not be set by placing
it immediately after the entry before. Instead, is set by another
entry which knows where this entry should go. When this boolean
property is present, binman will give an error if another entry does
not set the 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
@ -338,7 +345,7 @@ align-size:
pad-before:
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:
This sets the padding after the image entries. The padding will be
@ -351,15 +358,15 @@ pad-byte:
filename:
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
are in increasing positional order. This can be used when your entry
order may not match the positional order. A common situation is where
the 'pos' properties are set by CONFIG options, so their ordering is
the 'offset' properties are set by CONFIG options, so their ordering is
not known a priori.
This is a boolean property so needs no value. To enable it, add a
line 'sort-by-pos;' to your description.
line 'sort-by-offset;' to your description.
multiple-images:
Normally only a single image is generated. To create more than one
@ -383,11 +390,11 @@ multiple-images:
};
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
option can be enabled to support this. The image size must be
provided so that binman knows when the image should start. For an
8MB ROM, the position of the first entry would be 0xfff80000 with
8MB ROM, the offset of the first entry would be 0xfff80000 with
this option, instead of 0 without this option.
@ -446,6 +453,15 @@ name-prefix:
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
------------------
@ -463,7 +479,7 @@ Order of image creation
Image creation proceeds in the following order, for each entry in the image.
1. AddMissingProperties() - binman can add calculated values to the device
tree as part of its processing, for example the 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
device tree as needed. These properties can have placeholder values which are
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
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
entry name and the value is a tuple (pos, size). This allows an entry to
provide the position and size for other entries. The default implementation
of GetEntryPositions() returns {}.
entry name and the value is a tuple (offset, size). This allows an entry to
provide the offset and size for other entries. The default implementation
of GetEntryOffsets() returns {}.
5. PackEntries() - calls Entry.Pack() which figures out the position and
size of an entry. The 'current' image position is passed in, and the function
returns the position immediately after the entry being packed. The default
5. PackEntries() - calls Entry.Pack() which figures out the offset and
size of an entry. The 'current' image offset is passed in, and the function
returns the offset immediately after the entry being packed. The default
implementation of Pack() is usually sufficient.
6. CheckSize() - checks that the contents of all the entries fits within
@ -505,16 +521,16 @@ large enough to hold all the entries.
outside the image.
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.
The default implementatoin does nothing. This can be overriden to adjust the
contents of an entry in some way. For example, it would be possible to create
an entry containing a hash of the contents of some other entries. At this
stage the position and size of entries should not be adjusted.
stage the offset and size of entries should not be adjusted.
10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary.
See 'Access to binman entry 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.
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)
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.
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
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.
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
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.
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
---------
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
00000000 00000010 section@0
00000000 00000004 u-boot
00000010 00000010 section@1
00000000 00000004 u-boot
Offset Size Name
00000000 00000028 main-section
00000000 00000010 section@0
00000000 00000004 u-boot
00000010 00000010 section@1
00000000 00000004 u-boot
This shows a hierarchical image with two sections, each with a single entry. The
positions 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 sections are absolute hex byte offsets within the image. The
offsets of the entries are relative to their respective sections. The size of
each entry is also shown, in bytes (hex). The indentation shows the entries
nested inside their sections.
Passing command-line arguments to entries
-----------------------------------------
Sometimes it is useful to pass binman the value of an entry property from the
command line. For example some entries need access to files and it is not
always convenient to put these filenames in the image definition (device tree).
The-a option supports this:
-a<prop>=<value>
where
<prop> is the property to set
<value> is the value to set it to
Not all properties can be provided this way. Only some entries support it,
typically for filenames.
Code coverage
-------------
@ -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,
when Entry_blob loads a file, it sets content_size to the size of the file.
Entry classes can adjust other entries. For example, an entry that knows
where other entries should be positioned can set up those entries' positions
where other entries should be positioned can set up those entries' offsets
so they don't need to be set in the binman decription. It can also adjust
entry contents.

@ -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.

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

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

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

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

@ -57,7 +57,9 @@ def GetSymbols(fname, patterns):
name = parts[2]
syms[name] = Symbol(section, int(value, 16), int(size,16),
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):
"""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
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
the entry (using the ELF file) and replacing them with values from binman's
data structures.
visible at run time. This is done by finding out the symbols offsets in the
entry (using the ELF file) and replacing them with values from binman's data
structures.
Args:
elf_fname: Filename of ELF image containing the symbol information for

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

@ -6,6 +6,8 @@
from __future__ import print_function
from collections import namedtuple
# importlib was introduced in Python 2.7 but there was a report of it not
# working in 2.7.12, so we work around this:
# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
@ -16,6 +18,7 @@ except:
have_importlib = False
import fdt_util
import control
import os
import sys
import tools
@ -24,6 +27,12 @@ modules = {}
our_path = os.path.dirname(os.path.realpath(__file__))
# An argument which can be passed to entries on the command line, in lieu of
# device-tree properties.
EntryArg = namedtuple('EntryArg', ['name', 'datatype'])
class Entry(object):
"""An Entry in the section
@ -36,14 +45,15 @@ class Entry(object):
Entry.
Attributes:
section: The section containing this entry
section: Section object containing 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
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_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_after: Number of pad bytes after the contents, 0 if none
data: Contents of entry (string of bytes)
@ -53,34 +63,33 @@ class Entry(object):
self.etype = etype
self._node = node
self.name = node and (name_prefix + node.name) or 'none'
self.pos = None
self.offset = None
self.size = None
self.data = ''
self.data = None
self.contents_size = 0
self.align = None
self.align_size = None
self.align_end = None
self.pad_before = 0
self.pad_after = 0
self.pos_unset = False
self.offset_unset = False
self.image_pos = None
if read_node:
self.ReadNode()
@staticmethod
def Create(section, node, etype=None):
"""Create a new entry for a node.
def Lookup(section, node_path, etype):
"""Look up the entry class for a node.
Args:
section: Image object containing this node
node: Node object containing information about the entry to create
etype: Entry type to use, or None to work it out (used for tests)
section: Section object containing this node
node_node: Path name of Node object containing information about
the entry to create (used for errors)
etype: Entry type to use
Returns:
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
# interested in the type.
module_name = etype.replace('-', '_')
@ -99,15 +108,34 @@ class Entry(object):
module = importlib.import_module(module_name)
else:
module = __import__(module_name)
except ImportError:
raise ValueError("Unknown entry type '%s' in node '%s'" %
(etype, node.path))
except ImportError as e:
raise ValueError("Unknown entry type '%s' in node '%s' (expected etype/%s.py, error '%s'" %
(etype, node_path, module_name, e))
finally:
sys.path = old_path
modules[module_name] = module
# Look up the expected class name
return getattr(module, 'Entry_%s' % module_name)
@staticmethod
def Create(section, node, etype=None):
"""Create a new entry for a node.
Args:
section: Section object containing this node
node: Node object containing information about the entry to
create
etype: Entry type to use, or None to work it out (used for tests)
Returns:
A new Entry object of the correct type (a subclass of Entry)
"""
if not etype:
etype = fdt_util.GetString(node, 'type', node.name)
obj = Entry.Lookup(section, node.path, etype)
# Call its constructor to get the object we want.
obj = getattr(module, 'Entry_%s' % module_name)
return obj(section, etype, node)
def ReadNode(self):
@ -115,7 +143,9 @@ class Entry(object):
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.align = fdt_util.GetInt(self._node, 'align')
if tools.NotPowerOfTwo(self.align):
@ -128,18 +158,19 @@ class Entry(object):
raise ValueError("Node '%s': Alignment size %s must be a power "
"of two" % (self._node.path, self.align_size))
self.align_end = fdt_util.GetInt(self._node, 'align-end')
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
self.offset_unset = fdt_util.GetBool(self._node, 'offset-unset')
def AddMissingProperties(self):
"""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:
self._node.AddZeroProp(prop)
def SetCalculatedProperties(self):
"""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('image-pos', self.image_pos)
def ProcessFdt(self, fdt):
return True
@ -190,39 +221,39 @@ class Entry(object):
# No contents by default: subclasses can implement this
return True
def Pack(self, pos):
def Pack(self, offset):
"""Figure out how to pack the entry into the section
Most of the time the entries are not fully specified. There may be
an alignment but no size. In that case we take the size from the
contents of the entry.
If an entry has no hard-coded 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.
Args:
Current section position pointer
Current section offset pointer
Returns:
New section position pointer (after this entry)
New section offset pointer (after this entry)
"""
if self.pos is None:
if self.pos_unset:
self.Raise('No position set with pos-unset: should another '
'entry provide this correct position?')
self.pos = tools.Align(pos, self.align)
if self.offset is None:
if self.offset_unset:
self.Raise('No offset set with offset-unset: should another '
'entry provide this correct offset?')
self.offset = tools.Align(offset, self.align)
needed = self.pad_before + self.contents_size + self.pad_after
needed = tools.Align(needed, self.align_size)
size = self.size
if not size:
size = needed
new_pos = self.pos + size
aligned_pos = tools.Align(new_pos, self.align_end)
if aligned_pos != new_pos:
size = aligned_pos - self.pos
new_pos = aligned_pos
new_offset = self.offset + size
aligned_offset = tools.Align(new_offset, self.align_end)
if aligned_offset != new_offset:
size = aligned_offset - self.offset
new_offset = aligned_offset
if not self.size:
self.size = size
@ -231,21 +262,48 @@ class Entry(object):
self.Raise("Entry contents size is %#x (%d) but entry size is "
"%#x (%d)" % (needed, needed, self.size, self.size))
# Check that the alignment is correct. It could be wrong if the
# and pos or size values were provided (i.e. not calculated), but
# and offset or size values were provided (i.e. not calculated), but
# conflict with the provided alignment values
if self.size != tools.Align(self.size, self.align_size):
self.Raise("Size %#x (%d) does not match align-size %#x (%d)" %
(self.size, self.size, self.align_size, self.align_size))
if self.pos != tools.Align(self.pos, self.align):
self.Raise("Position %#x (%d) does not match align %#x (%d)" %
(self.pos, self.pos, self.align, self.align))
if self.offset != tools.Align(self.offset, self.align):
self.Raise("Offset %#x (%d) does not match align %#x (%d)" %
(self.offset, self.offset, self.align, self.align))
return new_pos
return new_offset
def Raise(self, msg):
"""Convenience function to raise an error referencing a node"""
raise ValueError("Node '%s': %s" % (self._node.path, msg))
def GetEntryArgsOrProps(self, props, required=False):
"""Return the values of a set of properties
Args:
props: List of EntryArg objects
Raises:
ValueError if a property is not found
"""
values = []
missing = []
for prop in props:
python_prop = prop.name.replace('-', '_')
if hasattr(self, python_prop):
value = getattr(self, python_prop)
else:
value = None
if value is None:
value = self.GetArg(prop.name, prop.datatype)
if value is None and required:
missing.append(prop.name)
values.append(value)
if missing:
self.Raise('Missing required properties/entry args: %s' %
(', '.join(missing)))
return values
def GetPath(self):
"""Get the path of a node
@ -257,13 +315,21 @@ class Entry(object):
def GetData(self):
return self.data
def GetPositions(self):
def GetOffsets(self):
return {}
def SetPositionSize(self, pos, size):
self.pos = pos
def SetOffsetSize(self, pos, size):
self.offset = pos
self.size = size
def SetImagePos(self, image_pos):
"""Set the position in the image
Args:
image_pos: Position of this entry in the image
"""
self.image_pos = image_pos + self.offset
def ProcessContents(self):
pass
@ -275,15 +341,20 @@ class Entry(object):
"""
pass
def CheckPosition(self):
"""Check that the entry positions are correct
def CheckOffset(self):
"""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
this function and raise if there is a problem.
"""
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):
"""Write a map of the entry to a .map file
@ -291,5 +362,97 @@ class Entry(object):
fd: File to write the map to
indent: Curent indent level of map (0=none, 1=one level, etc.)
"""
print('%s%08x %08x %s' % (' ' * indent, self.pos, self.size,
self.name), file=fd)
self.WriteMapLine(fd, indent, self.name, self.offset, self.size,
self.image_pos)
def GetEntries(self):
"""Return a list of entries contained by this entry
Returns:
List of entries, or None if none. A normal entry has no entries
within it so will return None
"""
return None
def GetArg(self, name, datatype=str):
"""Get the value of an entry argument or device-tree-node property
Some node properties can be provided as arguments to binman. First check
the entry arguments, and fall back to the device tree if not found
Args:
name: Argument name
datatype: Data type (str or int)
Returns:
Value of argument as a string or int, or None if no value
Raises:
ValueError if the argument cannot be converted to in
"""
value = 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))

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

@ -10,6 +10,18 @@ import fdt_util
import tools
class Entry_blob(Entry):
"""Entry containing an arbitrary binary blob
Note: This should not be used by itself. It is normally used as a parent
class by other entry types.
Properties / Entry arguments:
- filename: Filename of file to read into entry
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):
Entry.__init__(self, section, etype, node)
self._filename = fdt_util.GetString(self._node, "filename", self.etype)
@ -17,10 +29,10 @@ class Entry_blob(Entry):
def ObtainContents(self):
self._filename = self.GetDefaultFilename()
self._pathname = tools.GetInputFilename(self._filename)
self.ReadContents()
self.ReadBlobContents()
return True
def ReadContents(self):
def ReadBlobContents(self):
with open(self._pathname) as fd:
# We assume the data is small enough to fit into memory. If this
# is used for large filesystem image that might not be true.

@ -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)])

@ -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')

@ -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

@ -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())

@ -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

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

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

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

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

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

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

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

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

@ -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

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

@ -9,6 +9,15 @@ from entry import Entry
from blob import 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):
Entry_blob.__init__(self, section, etype, node)

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

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

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

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

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

@ -2,13 +2,22 @@
# Copyright (c) 2016 Google, Inc
# 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 blob import 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):
Entry_blob.__init__(self, section, etype, node)

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

@ -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)

@ -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'

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

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

@ -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

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

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

@ -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

@ -21,6 +21,8 @@ import control
import elf
import fdt
import fdt_util
import fmap_util
import test_util
import tools
import tout
@ -28,11 +30,13 @@ import tout
U_BOOT_DATA = '1234'
U_BOOT_IMG_DATA = 'img'
U_BOOT_SPL_DATA = '56780123456789abcde'
U_BOOT_TPL_DATA = 'tpl'
BLOB_DATA = '89'
ME_DATA = '0abcd'
VGA_DATA = 'vga'
U_BOOT_DTB_DATA = 'udtb'
U_BOOT_SPL_DTB_DATA = 'spldtb'
U_BOOT_TPL_DTB_DATA = 'tpldtb'
X86_START16_DATA = 'start16'
X86_START16_SPL_DATA = 'start16spl'
U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here'
@ -41,6 +45,14 @@ FSP_DATA = 'fsp'
CMC_DATA = 'cmc'
VBT_DATA = 'vbt'
MRC_DATA = 'mrc'
TEXT_DATA = 'text'
TEXT_DATA2 = 'text2'
TEXT_DATA3 = 'text3'
CROS_EC_RW_DATA = 'ecrw'
GBB_DATA = 'gbbd'
BMPBLK_DATA = 'bmp'
VBLOCK_DATA = 'vblk'
class TestFunctional(unittest.TestCase):
"""Functional tests for binman
@ -72,11 +84,11 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
TestFunctional._MakeInputFile('me.bin', ME_DATA)
TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
self._ResetDtbs()
TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
X86_START16_SPL_DATA)
@ -87,6 +99,9 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
TestFunctional._MakeInputDir('devkeys')
TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
self._output_setup = False
# ELF file with a '_dt_ucode_base_size' symbol
@ -113,6 +128,12 @@ class TestFunctional(unittest.TestCase):
"""Remove the temporary output directory"""
tools._FinaliseForTest()
@classmethod
def _ResetDtbs(self):
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
def _RunBinman(self, *args, **kwargs):
"""Run binman using the command line
@ -146,14 +167,15 @@ class TestFunctional(unittest.TestCase):
# options.verbosity = tout.DEBUG
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
Args:
fname: Device-tree source filename to use (e.g. 05_simple.dts)
debug: True to enable debugging output
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
"""
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
@ -163,6 +185,9 @@ class TestFunctional(unittest.TestCase):
args.append('-m')
if update_dtb:
args.append('-up')
if entry_args:
for arg, value in entry_args.iteritems():
args.append('-a%s=%s' % (arg, value))
return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@ -188,7 +213,7 @@ class TestFunctional(unittest.TestCase):
return data
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
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.
But in some test we need the real contents.
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
Returns:
@ -212,6 +237,7 @@ class TestFunctional(unittest.TestCase):
Resulting image contents
Device tree contents
Map data showing contents of image (or None if none)
Output device tree binary filename ('u-boot.dtb' path)
"""
dtb_data = None
# Use the compiled test file as the u-boot-dtb input
@ -219,7 +245,8 @@ class TestFunctional(unittest.TestCase):
dtb_data = self._SetupDtb(fname)
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)
out_dtb_fname = control.GetFdtPath('u-boot.dtb')
@ -238,7 +265,7 @@ class TestFunctional(unittest.TestCase):
finally:
# Put the test file back
if use_real_dtb:
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
self._ResetDtbs()
def _DoReadFile(self, fname, use_real_dtb=False):
"""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
test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents.
Returns:
Resulting image contents
"""
return self._DoReadFileDtb(fname, use_real_dtb)[0]
@ -257,7 +287,7 @@ class TestFunctional(unittest.TestCase):
"""Create a new test input file, creating directories as needed
Args:
fname: Filenaem to create
fname: Filename to create
contents: File contents to write in to the file
Returns:
Full pathname of file created
@ -271,6 +301,21 @@ class TestFunctional(unittest.TestCase):
return pathname
@classmethod
def _MakeInputDir(self, dirname):
"""Create a new test input directory, creating directories as needed
Args:
dirname: Directory name to create
Returns:
Full pathname of directory created
"""
pathname = os.path.join(self._indir, dirname)
if not os.path.exists(pathname):
os.makedirs(pathname)
return pathname
@classmethod
def TestFile(self, fname):
return os.path.join(self._binman_dir, 'test', fname)
@ -292,10 +337,10 @@ class TestFunctional(unittest.TestCase):
Args:
entries: List of entries to check
"""
pos = 0
offset = 0
for entry in entries.values():
self.assertEqual(pos, entry.pos)
pos += entry.size
self.assertEqual(offset, entry.offset)
offset += entry.size
def GetFdtLen(self, dtb):
"""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]
def _GetPropTree(self, dtb_data, node_names):
def _GetPropTree(self, dtb, prop_names):
def AddNode(node, path):
if node.name != '/':
path += '/' + node.name
#print 'path', path
for subnode in node.subnodes:
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
tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
prop.value)
#print ' ', prop.name
AddNode(subnode, path)
tree = {}
dtb = fdt.Fdt(dtb_data)
dtb.Scan()
AddNode(dtb.GetRoot(), '')
return tree
@ -409,7 +450,6 @@ class TestFunctional(unittest.TestCase):
with self.assertRaises(Exception) as e:
result = self._RunBinman('-d',
self.TestFile('04_invalid_entry.dts'))
#print e.exception
self.assertIn("Unknown entry type 'not-a-valid-type' in node "
"'/binman/not-a-valid-type'", str(e.exception))
@ -467,32 +507,32 @@ class TestFunctional(unittest.TestCase):
# First u-boot
self.assertIn('u-boot', entries)
entry = entries['u-boot']
self.assertEqual(0, entry.pos)
self.assertEqual(0, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size)
# Second u-boot, aligned to 16-byte boundary
self.assertIn('u-boot-align', entries)
entry = entries['u-boot-align']
self.assertEqual(16, entry.pos)
self.assertEqual(16, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.size)
# Third u-boot, size 23 bytes
self.assertIn('u-boot-size', entries)
entry = entries['u-boot-size']
self.assertEqual(20, entry.pos)
self.assertEqual(20, entry.offset)
self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
self.assertEqual(23, entry.size)
# Fourth u-boot, placed immediate after the above
self.assertIn('u-boot-next', entries)
entry = entries['u-boot-next']
self.assertEqual(43, entry.pos)
self.assertEqual(43, entry.offset)
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)
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(65, image._size)
@ -510,32 +550,32 @@ class TestFunctional(unittest.TestCase):
# First u-boot with padding before and after
self.assertIn('u-boot', entries)
entry = entries['u-boot']
self.assertEqual(0, entry.pos)
self.assertEqual(0, entry.offset)
self.assertEqual(3, entry.pad_before)
self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
# Second u-boot has an aligned size, but it has no effect
self.assertIn('u-boot-align-size-nop', entries)
entry = entries['u-boot-align-size-nop']
self.assertEqual(12, entry.pos)
self.assertEqual(12, entry.offset)
self.assertEqual(4, entry.size)
# Third u-boot has an aligned size too
self.assertIn('u-boot-align-size', entries)
entry = entries['u-boot-align-size']
self.assertEqual(16, entry.pos)
self.assertEqual(16, entry.offset)
self.assertEqual(32, entry.size)
# Fourth u-boot has an aligned end
self.assertIn('u-boot-align-end', entries)
entry = entries['u-boot-align-end']
self.assertEqual(48, entry.pos)
self.assertEqual(48, entry.offset)
self.assertEqual(16, entry.size)
# Fifth u-boot immediately afterwards
self.assertIn('u-boot-align-both', entries)
entry = entries['u-boot-align-both']
self.assertEqual(64, entry.pos)
self.assertEqual(64, entry.offset)
self.assertEqual(64, entry.size)
self.CheckNoGaps(entries)
@ -556,10 +596,10 @@ class TestFunctional(unittest.TestCase):
"power of two", str(e.exception))
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:
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))
def testPackInvalidSizeAlign(self):
@ -573,7 +613,7 @@ class TestFunctional(unittest.TestCase):
"""Test that overlapping regions are detected"""
with self.assertRaises(ValueError) as e:
self._DoTestFile('14_pack_overlap.dts')
self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps "
self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
"with previous entry '/binman/u-boot' ending at 0x4 (4)",
str(e.exception))
@ -651,11 +691,11 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
U_BOOT_DATA, data)
def testPackZeroPosition(self):
"""Test that an entry at position 0 is not given a new position"""
def testPackZeroOffset(self):
"""Test that an entry at offset 0 is not given a new offset"""
with self.assertRaises(ValueError) as e:
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)",
str(e.exception))
@ -672,10 +712,10 @@ class TestFunctional(unittest.TestCase):
"using end-at-4gb", str(e.exception))
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:
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)",
str(e.exception))
@ -697,9 +737,9 @@ class TestFunctional(unittest.TestCase):
"""Test that the Intel requires a descriptor entry"""
with self.assertRaises(ValueError) as e:
self._DoTestFile('30_x86-rom-me-no-desc.dts')
self.assertIn("Node '/binman/intel-me': No position set with "
"pos-unset: should another entry provide this correct "
"position?", str(e.exception))
self.assertIn("Node '/binman/intel-me': No offset set with "
"offset-unset: should another entry provide this correct "
"offset?", str(e.exception))
def testPackX86RomMe(self):
"""Test that an x86 ROM with an ME region can be created"""
@ -728,7 +768,7 @@ class TestFunctional(unittest.TestCase):
Returns:
Tuple:
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)
"""
data = self._DoReadFile(dts_fname, True)
@ -761,7 +801,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
# 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,
len(ucode_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)
# 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,
len(ucode_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"""
with self.assertRaises(ValueError) as e:
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))
def testPackFsp(self):
@ -984,7 +1024,7 @@ class TestFunctional(unittest.TestCase):
elf_fname = self.TestFile('u_boot_binman_syms')
syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr)
self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
with open(self.TestFile('u_boot_binman_syms')) as fd:
TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
@ -1003,27 +1043,32 @@ class TestFunctional(unittest.TestCase):
def testSections(self):
"""Basic test of sections"""
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)
def testMap(self):
"""Tests outputting a map of the images"""
_, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 u-boot
00000010 00000010 section@1
00000000 00000004 u-boot
self.assertEqual('''ImagePos Offset Size Name
00000000 00000000 00000028 main-section
00000000 00000000 00000010 section@0
00000000 00000000 00000004 u-boot
00000010 00000010 00000010 section@1
00000010 00000000 00000004 u-boot
00000020 00000020 00000004 section@2
00000020 00000000 00000004 u-boot
''', map_data)
def testNamePrefix(self):
"""Tests that name prefixes are used"""
_, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 ro-u-boot
00000010 00000010 section@1
00000000 00000004 rw-u-boot
self.assertEqual('''ImagePos Offset Size Name
00000000 00000000 00000028 main-section
00000000 00000000 00000010 section@0
00000000 00000000 00000004 ro-u-boot
00000010 00000010 00000010 section@1
00000010 00000000 00000004 rw-u-boot
''', map_data)
def testUnknownContents(self):
@ -1042,25 +1087,31 @@ class TestFunctional(unittest.TestCase):
'2 to 1', str(e.exception))
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',
update_dtb=True)
props = self._GetPropTree(out_dtb_fname, ['pos', 'size'])
with open('/tmp/x.dtb', 'wb') as outf:
with open(out_dtb_fname) as inf:
outf.write(inf.read())
dtb = fdt.Fdt(out_dtb_fname)
dtb.Scan()
props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
self.assertEqual({
'_testing:pos': 32,
'image-pos': 0,
'offset': 0,
'_testing:offset': 32,
'_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:pos': 0,
'section@0/u-boot:image-pos': 0,
'section@0:offset': 0,
'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:pos': 16,
'section@1/u-boot:image-pos': 16,
'section@1:offset': 16,
'section@1:size': 16,
'section@1:image-pos': 16,
'size': 40
}, props)
@ -1071,5 +1122,248 @@ class TestFunctional(unittest.TestCase):
self.assertIn('Could not complete processing of Fdt: remaining '
'[<_testing.Entry__testing', str(e.exception))
def testEntryArgs(self):
"""Test passing arguments to entries from the command line"""
entry_args = {
'test-str-arg': 'test1',
'test-int-arg': '456',
}
self._DoReadFileDtb('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__":
unittest.main()

@ -57,7 +57,7 @@ class Image:
def AddMissingProperties(self):
"""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
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
@ -73,13 +73,13 @@ class Image:
"""
self._section.GetEntryContents()
def GetEntryPositions(self):
"""Handle entries that want to set the position/size of other entries
def GetEntryOffsets(self):
"""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.
"""
self._section.GetEntryPositions()
self._section.GetEntryOffsets()
def PackEntries(self):
"""Pack all entries into the image"""
@ -96,6 +96,9 @@ class Image:
def SetCalculatedProperties(self):
self._section.SetCalculatedProperties()
def SetImagePos(self):
self._section.SetImagePos(0)
def ProcessEntryContents(self):
"""Call the ProcessContents() method for each entry
@ -121,5 +124,6 @@ class Image:
filename = '%s.map' % self._name
fname = tools.GetOutputFilename(filename)
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)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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>;
};
};
};

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

@ -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";
};
};
};

@ -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;
};
};
};

@ -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";
};
};
};

@ -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 {
};
};
};

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

@ -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];
};
};
};

@ -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];
};
};
};

@ -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;
};
};
};
};

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

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

@ -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 {
};
};
};

@ -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 {
};
};
};

@ -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 {
};
};
};

@ -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 {
};
};

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

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

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

@ -181,7 +181,15 @@ class Node:
self.subnodes = []
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
Args:
@ -314,6 +322,17 @@ class Fdt:
with open(self._fname) as fd:
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='/'):
"""Scan a device tree, building up a tree of Node objects
@ -349,7 +368,7 @@ class Fdt:
if len(parts) < 2:
return None
for part in parts[1:]:
node = node._FindNode(part)
node = node.FindNode(part)
if not node:
return None
return node

@ -5,6 +5,9 @@
# 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 struct
import sys
@ -90,6 +93,16 @@ def EnsureCompiled(fname, capture_stderr=False):
return dtb_output
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)
if not prop:
return default
@ -100,6 +113,16 @@ def GetInt(node, propname, default=None):
return value
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)
if not prop:
return default
@ -110,6 +133,79 @@ def GetString(node, propname, default=None):
return value
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:
return True
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)

@ -115,6 +115,9 @@ class TestFdt(unittest.TestCase):
fdt.CheckErr(-libfdt.NOTFOUND, 'hello')
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):
"""Test operation of the Node class"""
@ -155,12 +158,12 @@ class TestNode(unittest.TestCase):
self.assertEqual(prop.value, value)
def testFindNode(self):
"""Tests that we can find a node using the _FindNode() functoin"""
node = self.dtb.GetRoot()._FindNode('i2c@0')
"""Tests that we can find a node using the FindNode() functoin"""
node = self.dtb.GetRoot().FindNode('i2c@0')
self.assertEqual('i2c@0', node.name)
subnode = node._FindNode('pmic@9')
subnode = node.FindNode('pmic@9')
self.assertEqual('pmic@9', subnode.name)
self.assertEqual(None, node._FindNode('missing'))
self.assertEqual(None, node.FindNode('missing'))
def testRefreshMissingNode(self):
"""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 ",
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):
"""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(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):
val = self.node.props['intarray'].value
self.assertEqual(0, fdt_util.fdt_cells_to_cpu(val, 0))

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

@ -3,16 +3,26 @@
# Copyright (c) 2016 Google, Inc
#
import command
import os
import shutil
import tempfile
import tout
# Output directly (generally this is temporary)
outdir = None
indirs = None
# True to keep the output directory around after exiting
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):
"""Select an output directory, ensuring it exists.
@ -106,8 +116,8 @@ def GetInputFilename(fname):
if os.path.exists(pathname):
return pathname
raise ValueError("Filename '%s' not found in input path (%s)" %
(fname, ','.join(indir)))
raise ValueError("Filename '%s' not found in input path (%s) (cwd='%s')" %
(fname, ','.join(indir), os.getcwd()))
def Align(pos, align):
if align:
@ -117,3 +127,67 @@ def Align(pos, align):
def NotPowerOfTwo(num):
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)

Loading…
Cancel
Save