From 46d61a2f2aeefcd622c9678716429e68a3a98811 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:24 -0600 Subject: [PATCH 01/28] binman: Don't depend on dict order in ELF testOutsideFile() At present this test assumes that the symbols are returned in address order. However, objdump can list symbols in any order and dictionaries do not guarantee any particular order when iterating through item. Update elf.GetSymbols() to return an OrderedDict, sorted by address, to avoid any problems. Signed-off-by: Simon Glass --- tools/binman/elf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 0ae3b611ba..8c23040d8c 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -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 From 3ab9598df714556b649048b2a387071253e9e731 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Aug 2018 15:22:37 -0600 Subject: [PATCH 02/28] binman: Rename 'position' to 'offset' After some thought, I believe there is an unfortunate naming flaw in binman. Entries have a position and size, but now that we support hierarchical sections it is unclear whether a position should be an absolute position within the image, or a relative position within its parent section. At present 'position' actually means the relative position. This indicates a need for an 'image position' for code that wants to find the location of an entry without having to do calculations back through parents to discover this image position. A better name for the current 'position' or 'pos' is 'offset'. It is not always an absolute position, but it is always an offset from its parent offset. It is unfortunate to rename this concept now, 18 months after binman was introduced. However I believe it is the right thing to do. The impact is mostly limited to binman itself and a few changes to in-tree users to binman: tegra sunxi x86 The change makes old binman definitions (e.g. downstream or out-of-tree) incompatible if they use the 'pos = <...>' property. Later work will adjust binman to generate an error when it is used. Signed-off-by: Simon Glass --- arch/arm/dts/sunxi-u-boot.dtsi | 2 +- arch/arm/dts/tegra-u-boot.dtsi | 6 +- arch/x86/dts/u-boot.dtsi | 24 ++--- common/spl/spl.c | 4 +- common/spl/spl_ram.c | 2 +- include/spl.h | 2 +- tools/binman/README | 79 ++++++++-------- tools/binman/bsection.py | 88 +++++++++--------- tools/binman/cmdline.py | 2 +- tools/binman/control.py | 4 +- tools/binman/elf.py | 6 +- tools/binman/entry.py | 73 ++++++++------- tools/binman/etype/_testing.py | 4 +- tools/binman/etype/intel_descriptor.py | 8 +- tools/binman/etype/section.py | 14 +-- tools/binman/etype/u_boot_dtb_with_ucode.py | 4 +- tools/binman/etype/u_boot_ucode.py | 4 +- tools/binman/etype/u_boot_with_ucode_ptr.py | 35 +++---- tools/binman/ftest.py | 85 +++++++++-------- tools/binman/image.py | 12 +-- tools/binman/test/08_pack.dts | 2 +- tools/binman/test/12_pack_inv_align.dts | 2 +- tools/binman/test/14_pack_overlap.dts | 2 +- tools/binman/test/21_image_pad.dts | 2 +- tools/binman/test/24_sorted.dts | 6 +- tools/binman/test/25_pack_zero_size.dts | 2 +- tools/binman/test/27_pack_4gb_no_size.dts | 6 +- tools/binman/test/28_pack_4gb_outside.dts | 6 +- tools/binman/test/29_x86-rom.dts | 6 +- tools/binman/test/30_x86-rom-me-no-desc.dts | 4 +- tools/binman/test/31_x86-rom-me.dts | 4 +- tools/binman/test/34_x86_ucode.dts | 2 +- tools/binman/test/35_x86_single_ucode.dts | 2 +- tools/binman/test/37_x86_no_ucode.dts | 2 +- .../binman/test/38_x86_ucode_missing_node.dts | 2 +- .../test/39_x86_ucode_missing_node2.dts | 2 +- .../binman/test/40_x86_ucode_not_in_image.dts | 2 +- tools/binman/test/44_x86_optional_ucode.dts | 2 +- tools/binman/test/45_prop_test.dts | 4 +- tools/binman/test/49_x86_ucode_spl.dts | 2 +- tools/binman/test/53_symbols.dts | 2 +- .../test/58_x86_ucode_spl_needs_retry.dts | 2 +- tools/binman/test/u_boot_binman_syms | Bin 4921 -> 4916 bytes tools/binman/test/u_boot_binman_syms.c | 6 +- 44 files changed, 267 insertions(+), 263 deletions(-) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index 5adfd9bca2..8a9f2a6417 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -8,7 +8,7 @@ filename = "spl/sunxi-spl.bin"; }; u-boot-img { - pos = ; + offset = ; }; }; }; diff --git a/arch/arm/dts/tegra-u-boot.dtsi b/arch/arm/dts/tegra-u-boot.dtsi index 4f692ee975..fe19619919 100644 --- a/arch/arm/dts/tegra-u-boot.dtsi +++ b/arch/arm/dts/tegra-u-boot.dtsi @@ -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)>; }; }; }; diff --git a/arch/x86/dts/u-boot.dtsi b/arch/x86/dts/u-boot.dtsi index 1253fa51c2..1050236330 100644 --- a/arch/x86/dts/u-boot.dtsi +++ b/arch/x86/dts/u-boot.dtsi @@ -11,7 +11,7 @@ binman { filename = "u-boot.rom"; end-at-4gb; - sort-by-pos; + sort-by-offset; pad-byte = <0xff>; size = ; #ifdef CONFIG_HAVE_INTEL_ME @@ -24,18 +24,18 @@ #endif #ifdef CONFIG_SPL u-boot-spl-with-ucode-ptr { - pos = ; + offset = ; }; u-boot-dtb-with-ucode2 { type = "u-boot-dtb-with-ucode"; }; u-boot { - pos = <0xfff00000>; + offset = <0xfff00000>; }; #else u-boot-with-ucode-ptr { - pos = ; + offset = ; }; #endif u-boot-dtb-with-ucode { @@ -45,45 +45,45 @@ }; #ifdef CONFIG_HAVE_MRC intel-mrc { - pos = ; + offset = ; }; #endif #ifdef CONFIG_HAVE_FSP intel-fsp { filename = CONFIG_FSP_FILE; - pos = ; + offset = ; }; #endif #ifdef CONFIG_HAVE_CMC intel-cmc { filename = CONFIG_CMC_FILE; - pos = ; + offset = ; }; #endif #ifdef CONFIG_HAVE_VGA_BIOS intel-vga { filename = CONFIG_VGA_BIOS_FILE; - pos = ; + offset = ; }; #endif #ifdef CONFIG_HAVE_VBT intel-vbt { filename = CONFIG_VBT_FILE; - pos = ; + offset = ; }; #endif #ifdef CONFIG_HAVE_REFCODE intel-refcode { - pos = ; + offset = ; }; #endif #ifdef CONFIG_SPL x86-start16-spl { - pos = ; + offset = ; }; #else x86-start16 { - pos = ; + offset = ; }; #endif }; diff --git a/common/spl/spl.c b/common/spl/spl.c index a1e7b9fa91..b959aa0254 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -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, offset); /* 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, offset); spl_image->size = CONFIG_SYS_MONITOR_LEN; diff --git a/common/spl/spl_ram.c b/common/spl/spl_ram.c index e8701934b8..6000f495cc 100644 --- a/common/spl/spl_ram.c +++ b/common/spl/spl_ram.c @@ -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, offset); debug("Legacy image\n"); /* diff --git a/include/spl.h b/include/spl.h index 86287874e1..8c5523083b 100644 --- a/include/spl.h +++ b/include/spl.h @@ -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, offset); /** * spl_load_simple_fit() - Loads a fit image from a device. diff --git a/tools/binman/README b/tools/binman/README index 207928aa95..5ff20f15c6 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -238,7 +238,7 @@ below: filename = "spl/sunxi-spl.bin"; }; u-boot { - pos = ; + offset = ; }; }; @@ -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,12 @@ 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). The attributes supported for images are described below. Several are similar @@ -338,7 +339,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 +352,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 +384,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. @@ -463,7 +464,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 +487,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 +506,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 +550,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 +------------------------------------------ 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 +561,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. @@ -580,17 +581,17 @@ 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 + Offset Size Name 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. @@ -623,7 +624,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. diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index de439ef625..d78a25e83d 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -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 @@ -52,6 +51,7 @@ class Section(object): from entry import Entry self._node = node + self._offset = 0 self._size = None self._align_size = None self._pad_before = 0 @@ -76,7 +76,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") @@ -117,7 +117,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 +190,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,21 +233,21 @@ 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 ProcessEntryContents(self): @@ -261,9 +263,9 @@ 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): @@ -272,7 +274,7 @@ class Section(object): 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 +285,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__prop_ where is the name of the entry and 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 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,8 +329,8 @@ 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 else: raise ValueError("%s: No such property '%s'" % (msg, prop_name)) diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index ae2d1670f9..5c9b4dfead 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -43,7 +43,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') diff --git a/tools/binman/control.py b/tools/binman/control.py index a40b300fda..17c6d7a2d2 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -142,7 +142,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,7 +157,7 @@ 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() diff --git a/tools/binman/elf.py b/tools/binman/elf.py index 8c23040d8c..97df8e32c5 100644 --- a/tools/binman/elf.py +++ b/tools/binman/elf.py @@ -81,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 diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 6a173e663d..c9529d8322 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -38,12 +38,13 @@ class Entry(object): Attributes: section: The section 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,7 +54,7 @@ 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.contents_size = 0 @@ -62,7 +63,7 @@ class Entry(object): self.align_end = None self.pad_before = 0 self.pad_after = 0 - self.pos_unset = False + self.offset_unset = False if read_node: self.ReadNode() @@ -115,7 +116,7 @@ class Entry(object): This reads all the fields we recognise from the node, ready for use. """ - self.pos = fdt_util.GetInt(self._node, '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,17 +129,17 @@ 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', 'global-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) def ProcessFdt(self, fdt): @@ -190,39 +191,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,16 +232,16 @@ 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""" @@ -257,11 +258,11 @@ 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 ProcessContents(self): @@ -275,10 +276,10 @@ 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. """ @@ -291,5 +292,5 @@ 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, + print('%s%08x %08x %s' % (' ' * indent, self.offset, self.size, self.name), file=fd) diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py index 6a1af57798..31f625c026 100644 --- a/tools/binman/etype/_testing.py +++ b/tools/binman/etype/_testing.py @@ -14,7 +14,7 @@ class Entry__testing(Entry): """A fake entry used for testing Properties: - return_invalid_entry: Return an invalid entry from GetPositions() + return_invalid_entry: Return an invalid entry from GetOffsets() """ def __init__(self, section, etype, node): Entry.__init__(self, section, etype, node) @@ -35,7 +35,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 {} diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py index 0e7865521e..0a5151d466 100644 --- a/tools/binman/etype/intel_descriptor.py +++ b/tools/binman/etype/intel_descriptor.py @@ -36,12 +36,12 @@ class Entry_intel_descriptor(Entry_blob): 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('> 16) & 0xff) << 4 for i in range(MAX_REGIONS): self._regions.append(Region(self.data, frba, i)) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 787257d3ec..5a0b0b8c70 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -30,20 +30,20 @@ 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) + return super(Entry_section, self).Pack(offset) def WriteSymbols(self, section): """Write symbol values into binary files for access at run time""" @@ -57,7 +57,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): diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py index 3808d6d278..55d3a38a64 100644 --- a/tools/binman/etype/u_boot_dtb_with_ucode.py +++ b/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -33,10 +33,10 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob): # 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 diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py index ea0c85cc5c..922a607cfe 100644 --- a/tools/binman/etype/u_boot_ucode.py +++ b/tools/binman/etype/u_boot_ucode.py @@ -59,8 +59,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 diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py index 8b1e41152e..97e58a0bfc 100644 --- a/tools/binman/etype/u_boot_with_ucode_ptr.py +++ b/tools/binman/etype/u_boot_with_ucode_ptr.py @@ -23,7 +23,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob): 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 +33,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:]) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 12164a85b4..1efedc2ec3 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -153,7 +153,7 @@ class TestFunctional(unittest.TestCase): 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)] @@ -204,7 +204,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: @@ -257,7 +257,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 @@ -292,10 +292,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 @@ -319,7 +319,6 @@ class TestFunctional(unittest.TestCase): 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 = {} @@ -467,32 +466,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 +509,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 +555,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 +572,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 +650,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 +671,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 +696,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 +727,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 +760,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 +805,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 +889,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 +983,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()) @@ -1009,7 +1008,7 @@ class TestFunctional(unittest.TestCase): def testMap(self): """Tests outputting a map of the images""" _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True) - self.assertEqual('''Position Size Name + self.assertEqual(''' Offset Size Name 00000000 00000010 section@0 00000000 00000004 u-boot 00000010 00000010 section@1 @@ -1019,7 +1018,7 @@ class TestFunctional(unittest.TestCase): def testNamePrefix(self): """Tests that name prefixes are used""" _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True) - self.assertEqual('''Position Size Name + self.assertEqual(''' Offset Size Name 00000000 00000010 section@0 00000000 00000004 ro-u-boot 00000010 00000010 section@1 @@ -1042,24 +1041,24 @@ 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']) + props = self._GetPropTree(out_dtb_fname, ['offset', 'size']) with open('/tmp/x.dtb', 'wb') as outf: with open(out_dtb_fname) as inf: outf.write(inf.read()) self.assertEqual({ - '_testing:pos': 32, + '_testing:offset': 32, '_testing:size': 1, - 'section@0/u-boot:pos': 0, + 'section@0/u-boot:offset': 0, 'section@0/u-boot:size': len(U_BOOT_DATA), - 'section@0:pos': 0, + 'section@0:offset': 0, 'section@0:size': 16, - '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:offset': 16, 'section@1:size': 16, 'size': 40 }, props) diff --git a/tools/binman/image.py b/tools/binman/image.py index 94028f19a5..ed8261a652 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -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""" @@ -121,5 +121,5 @@ 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 %s' % ('Offset', 'Size', 'Name'), file=fd) self._section.WriteMap(fd, 0) diff --git a/tools/binman/test/08_pack.dts b/tools/binman/test/08_pack.dts index dc63d99dcb..a88785d835 100644 --- a/tools/binman/test/08_pack.dts +++ b/tools/binman/test/08_pack.dts @@ -24,7 +24,7 @@ u-boot-fixed { type = "u-boot"; - pos = <61>; + offset = <61>; }; }; }; diff --git a/tools/binman/test/12_pack_inv_align.dts b/tools/binman/test/12_pack_inv_align.dts index 1d9d80a65c..d8dd600edb 100644 --- a/tools/binman/test/12_pack_inv_align.dts +++ b/tools/binman/test/12_pack_inv_align.dts @@ -6,7 +6,7 @@ binman { u-boot { - pos = <5>; + offset = <5>; align = <4>; }; }; diff --git a/tools/binman/test/14_pack_overlap.dts b/tools/binman/test/14_pack_overlap.dts index 611cfd9730..3895cba3bd 100644 --- a/tools/binman/test/14_pack_overlap.dts +++ b/tools/binman/test/14_pack_overlap.dts @@ -10,7 +10,7 @@ u-boot-align { type = "u-boot"; - pos = <3>; + offset = <3>; }; }; }; diff --git a/tools/binman/test/21_image_pad.dts b/tools/binman/test/21_image_pad.dts index bf39dc1b6f..c6516689d9 100644 --- a/tools/binman/test/21_image_pad.dts +++ b/tools/binman/test/21_image_pad.dts @@ -10,7 +10,7 @@ }; u-boot { - pos = <20>; + offset = <20>; }; }; }; diff --git a/tools/binman/test/24_sorted.dts b/tools/binman/test/24_sorted.dts index 43a7831341..d35d39f077 100644 --- a/tools/binman/test/24_sorted.dts +++ b/tools/binman/test/24_sorted.dts @@ -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>; }; }; }; diff --git a/tools/binman/test/25_pack_zero_size.dts b/tools/binman/test/25_pack_zero_size.dts index 7d2baad3c6..e863c44e3f 100644 --- a/tools/binman/test/25_pack_zero_size.dts +++ b/tools/binman/test/25_pack_zero_size.dts @@ -9,7 +9,7 @@ }; u-boot-spl { - pos = <0>; + offset = <0>; }; }; }; diff --git a/tools/binman/test/27_pack_4gb_no_size.dts b/tools/binman/test/27_pack_4gb_no_size.dts index e0b6519e75..371cca10d5 100644 --- a/tools/binman/test/27_pack_4gb_no_size.dts +++ b/tools/binman/test/27_pack_4gb_no_size.dts @@ -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>; }; }; }; diff --git a/tools/binman/test/28_pack_4gb_outside.dts b/tools/binman/test/28_pack_4gb_outside.dts index 18d6bb5b8a..2216abfb70 100644 --- a/tools/binman/test/28_pack_4gb_outside.dts +++ b/tools/binman/test/28_pack_4gb_outside.dts @@ -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>; }; }; }; diff --git a/tools/binman/test/29_x86-rom.dts b/tools/binman/test/29_x86-rom.dts index d49078e19e..d5c69f9d4a 100644 --- a/tools/binman/test/29_x86-rom.dts +++ b/tools/binman/test/29_x86-rom.dts @@ -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>; }; }; }; diff --git a/tools/binman/test/30_x86-rom-me-no-desc.dts b/tools/binman/test/30_x86-rom-me-no-desc.dts index 68d3ce09ec..796cb87afc 100644 --- a/tools/binman/test/30_x86-rom-me-no-desc.dts +++ b/tools/binman/test/30_x86-rom-me-no-desc.dts @@ -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; }; }; }; diff --git a/tools/binman/test/31_x86-rom-me.dts b/tools/binman/test/31_x86-rom-me.dts index ee3dac875a..b8b0a5a74b 100644 --- a/tools/binman/test/31_x86-rom-me.dts +++ b/tools/binman/test/31_x86-rom-me.dts @@ -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; }; }; }; diff --git a/tools/binman/test/34_x86_ucode.dts b/tools/binman/test/34_x86_ucode.dts index 64a6c2c3d5..40725731cd 100644 --- a/tools/binman/test/34_x86_ucode.dts +++ b/tools/binman/test/34_x86_ucode.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/35_x86_single_ucode.dts b/tools/binman/test/35_x86_single_ucode.dts index 973e97f864..2b1f086a41 100644 --- a/tools/binman/test/35_x86_single_ucode.dts +++ b/tools/binman/test/35_x86_single_ucode.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/37_x86_no_ucode.dts b/tools/binman/test/37_x86_no_ucode.dts index 9e12156ee2..6da49c3da6 100644 --- a/tools/binman/test/37_x86_no_ucode.dts +++ b/tools/binman/test/37_x86_no_ucode.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/38_x86_ucode_missing_node.dts b/tools/binman/test/38_x86_ucode_missing_node.dts index d6cf0d844e..720677c9c1 100644 --- a/tools/binman/test/38_x86_ucode_missing_node.dts +++ b/tools/binman/test/38_x86_ucode_missing_node.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/39_x86_ucode_missing_node2.dts b/tools/binman/test/39_x86_ucode_missing_node2.dts index b7e26c5ae4..10ac086d54 100644 --- a/tools/binman/test/39_x86_ucode_missing_node2.dts +++ b/tools/binman/test/39_x86_ucode_missing_node2.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/40_x86_ucode_not_in_image.dts b/tools/binman/test/40_x86_ucode_not_in_image.dts index 67d17d392f..609725824a 100644 --- a/tools/binman/test/40_x86_ucode_not_in_image.dts +++ b/tools/binman/test/40_x86_ucode_not_in_image.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; size = <0x200>; u-boot-with-ucode-ptr { }; diff --git a/tools/binman/test/44_x86_optional_ucode.dts b/tools/binman/test/44_x86_optional_ucode.dts index abe1322798..24a7040d31 100644 --- a/tools/binman/test/44_x86_optional_ucode.dts +++ b/tools/binman/test/44_x86_optional_ucode.dts @@ -5,7 +5,7 @@ #size-cells = <1>; binman { - sort-by-pos; + sort-by-offset; end-at-4gb; size = <0x200>; u-boot-with-ucode-ptr { diff --git a/tools/binman/test/45_prop_test.dts b/tools/binman/test/45_prop_test.dts index d22e460d29..064de2b316 100644 --- a/tools/binman/test/45_prop_test.dts +++ b/tools/binman/test/45_prop_test.dts @@ -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]; diff --git a/tools/binman/test/49_x86_ucode_spl.dts b/tools/binman/test/49_x86_ucode_spl.dts index 67db93ad50..350d2c4730 100644 --- a/tools/binman/test/49_x86_ucode_spl.dts +++ b/tools/binman/test/49_x86_ucode_spl.dts @@ -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 { diff --git a/tools/binman/test/53_symbols.dts b/tools/binman/test/53_symbols.dts index 980b066eb2..9f135676cb 100644 --- a/tools/binman/test/53_symbols.dts +++ b/tools/binman/test/53_symbols.dts @@ -10,7 +10,7 @@ }; u-boot { - pos = <20>; + offset = <20>; }; u-boot-spl2 { diff --git a/tools/binman/test/58_x86_ucode_spl_needs_retry.dts b/tools/binman/test/58_x86_ucode_spl_needs_retry.dts index e2cb80cf6e..a04adaaf7b 100644 --- a/tools/binman/test/58_x86_ucode_spl_needs_retry.dts +++ b/tools/binman/test/58_x86_ucode_spl_needs_retry.dts @@ -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 { diff --git a/tools/binman/test/u_boot_binman_syms b/tools/binman/test/u_boot_binman_syms index 2e02dc0ca9e2651bc12ac6d61d95dfa5ccee7a68..f2dcb887ed84e4799ae9eca1d2d39585635fb4c6 100755 GIT binary patch delta 350 zcmdm~wnc4%0+Wo;Mx}TB;meYz8))l=?8tMFe6oF=pXpo`4}S(U;O^{frJxa-RGL>( zs$i;TqGzCMRtn}B))|`V8JOrj} zU}7)>(gr}x0_BGSX%!^?L?9am1Q-|@N`M>_sDJ=ae=Cp;QV#+k{*1|uLgwB^@dZWs z1@ZZ5X~n4}4Dm^sdAW&s@ul%e`S~UBiFuXyq>Br3kfkRx3R_H$7r4r3GTBs6nd!3N ku+Gp#&%i{_SQ8>X`I6u@M(xQfg_N1%1ty;pQsX@!$iQ#`XdW8~ zPi7RBW}P6!z_4=iMIqsdGh`;W2$<9(+s4B1@xK^QKm~|dfLM@$iNOd-fdB&|gC&r! z0mK|o`7j`D0K_o$ML@O*5QBgKP=7O!2AKx}ApSHUjo>p*Ru|Nq+%L$+RghoI5MNx9 XSX46ER#1u=$ekQ3C<&%#3t9jG`lCCI diff --git a/tools/binman/test/u_boot_binman_syms.c b/tools/binman/test/u_boot_binman_syms.c index d8371610a5..cbb2f3f7b1 100644 --- a/tools/binman/test/u_boot_binman_syms.c +++ b/tools/binman/test/u_boot_binman_syms.c @@ -8,6 +8,6 @@ #define CONFIG_BINMAN #include -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, offset); From b2b0df8f39a093baf40c8cc775f99fefbaeb7e4e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:26 -0600 Subject: [PATCH 03/28] binman: Add comments to elf_test The purpose of some of the tests is not obvious from the function names. Add a few comments to help with understanding. Signed-off-by: Simon Glass --- tools/binman/elf_test.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py index 9c8f1feca8..c16f71401d 100644 --- a/tools/binman/elf_test.py +++ b/tools/binman/elf_test.py @@ -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() From ea6922e3d6a1b6b73d7baef4998f8bef0fe332ad Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:27 -0600 Subject: [PATCH 04/28] binman: Tidy up some comments in the tests A few lines are commented out and can be removed. Also fix return-value docs for _DoReadFile() and _DoReadFileDtb(). Signed-off-by: Simon Glass --- tools/binman/ftest.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 1efedc2ec3..91b59f84ea 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -212,6 +212,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 @@ -249,6 +250,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] @@ -312,7 +316,6 @@ class TestFunctional(unittest.TestCase): 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: @@ -408,7 +411,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)) From 8122f3967f6eaab7134051d1d8c9b1bfc3babadb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:28 -0600 Subject: [PATCH 05/28] binman: Enhance the map and fdt-update output At present the .map file produced for each image does not include the overall image size. This is useful information. Update the code to generate it in the .map file as well as the updated FDT. Also fix a few comments while we are here. Signed-off-by: Simon Glass --- tools/binman/README | 9 +++++---- tools/binman/bsection.py | 32 +++++++++++++++++++++++++++++-- tools/binman/entry.py | 13 ++++++++----- tools/binman/etype/section.py | 6 +++--- tools/binman/ftest.py | 24 ++++++++++++++--------- tools/binman/test/55_sections.dts | 4 ++++ 6 files changed, 65 insertions(+), 23 deletions(-) diff --git a/tools/binman/README b/tools/binman/README index 5ff20f15c6..4b13776ffa 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -584,10 +584,11 @@ The -m option causes binman to output a .map file for each image that it generates. This shows the offset and size of each entry. For example: Offset Size Name - 00000000 00000010 section@0 - 00000000 00000004 u-boot - 00000010 00000010 section@1 - 00000000 00000004 u-boot + 00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 u-boot + 00000010 00000010 section@1 + 00000000 00000004 u-boot This shows a hierarchical image with two sections, each with a single entry. The offsets of the sections are absolute hex byte offsets within the image. The diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index d78a25e83d..1604b9915e 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -50,6 +50,7 @@ class Section(object): import entry from entry import Entry + self._name = name self._node = node self._offset = 0 self._size = None @@ -90,11 +91,20 @@ 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']: + 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) for entry in self._entries.values(): entry.SetCalculatedProperties() @@ -269,7 +279,7 @@ class Section(object): 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(): @@ -335,13 +345,31 @@ class Section(object): 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) for entry in self._entries.values(): - entry.WriteMap(fd, indent) + entry.WriteMap(fd, indent + 1) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index c9529d8322..cb693c9382 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -36,7 +36,7 @@ class Entry(object): Entry. Attributes: - section: The section containing this entry + section: Section object containing this entry node: The node that created this entry offset: Offset of entry within the section, None if not known yet (in which case it will be calculated by Pack()) @@ -72,7 +72,7 @@ class Entry(object): """Create a new entry for a node. Args: - section: Image object containing this node + 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) @@ -133,7 +133,7 @@ class Entry(object): def AddMissingProperties(self): """Add new properties to the device tree as needed for this entry""" - for prop in ['offset', 'size', 'global-pos']: + for prop in ['offset', 'size']: if not prop in self._node.props: self._node.AddZeroProp(prop) @@ -285,6 +285,10 @@ class Entry(object): """ pass + @staticmethod + def WriteMapLine(fd, indent, name, offset, size): + print('%s%08x %08x %s' % (' ' * indent, offset, size, name), file=fd) + def WriteMap(self, fd, indent): """Write a map of the entry to a .map file @@ -292,5 +296,4 @@ 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.offset, self.size, - self.name), file=fd) + self.WriteMapLine(fd, indent, self.name, self.offset, self.size) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 5a0b0b8c70..1d27301ae9 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -42,7 +42,8 @@ class Entry_section(Entry): def Pack(self, offset): """Pack all entries into the section""" self._section.PackEntries() - self.size = self._section.CheckSize() + self._section.SetOffset(offset) + self.size = self._section.GetSize() return super(Entry_section, self).Pack(offset) def WriteSymbols(self, section): @@ -66,5 +67,4 @@ 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) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 91b59f84ea..94e48f3ab3 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1004,27 +1004,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(''' Offset Size Name -00000000 00000010 section@0 - 00000000 00000004 u-boot -00000010 00000010 section@1 - 00000000 00000004 u-boot +00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 u-boot + 00000010 00000010 section@1 + 00000000 00000004 u-boot + 00000020 00000004 section@2 + 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(''' Offset Size Name -00000000 00000010 section@0 - 00000000 00000004 ro-u-boot -00000010 00000010 section@1 - 00000000 00000004 rw-u-boot +00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 ro-u-boot + 00000010 00000010 section@1 + 00000000 00000004 rw-u-boot ''', map_data) def testUnknownContents(self): @@ -1051,6 +1056,7 @@ class TestFunctional(unittest.TestCase): with open(out_dtb_fname) as inf: outf.write(inf.read()) self.assertEqual({ + 'offset': 0, '_testing:offset': 32, '_testing:size': 1, 'section@0/u-boot:offset': 0, diff --git a/tools/binman/test/55_sections.dts b/tools/binman/test/55_sections.dts index 2ada395b03..6b306aeda4 100644 --- a/tools/binman/test/55_sections.dts +++ b/tools/binman/test/55_sections.dts @@ -24,5 +24,9 @@ u-boot { }; }; + section@2 { + u-boot { + }; + }; }; }; From dbf6be9f7f3b650ae5248eb7e2c00e94b4da867c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 1 Aug 2018 15:22:42 -0600 Subject: [PATCH 06/28] binman: Add a new 'image-pos' property At present each entry has an offset within its parent section. This is useful for figuring out how entries relate to one another. However it is sometimes necessary to locate an entry within an image, regardless of which sections it is nested inside. Add a new 'image-pos' property to provide this information. Also add some documentation for the -u option binman provides, which updates the device tree with final entry information. Since the image position is a better symbol to use for the position of U-Boot as obtained by SPL, update the SPL symbols to use this instead of offset, which might be incorrect if hierarchical sections are used. Signed-off-by: Simon Glass --- common/spl/spl.c | 4 ++-- common/spl/spl_ram.c | 2 +- include/spl.h | 2 +- tools/binman/README | 22 ++++++++++++++++++++-- tools/binman/bsection.py | 10 +++++++++- tools/binman/control.py | 1 + tools/binman/entry.py | 12 +++++++++++- tools/binman/etype/section.py | 4 ++++ tools/binman/ftest.py | 9 ++++++++- tools/binman/image.py | 3 +++ tools/binman/test/u_boot_binman_syms | Bin 4916 -> 4924 bytes tools/binman/test/u_boot_binman_syms.c | 2 +- 12 files changed, 61 insertions(+), 10 deletions(-) diff --git a/common/spl/spl.c b/common/spl/spl.c index b959aa0254..eda84d0c74 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -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, offset); +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, offset); + ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos); spl_image->size = CONFIG_SYS_MONITOR_LEN; diff --git a/common/spl/spl_ram.c b/common/spl/spl_ram.c index 6000f495cc..e594beaeaa 100644 --- a/common/spl/spl_ram.c +++ b/common/spl/spl_ram.c @@ -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, offset); + ulong u_boot_pos = binman_sym(ulong, u_boot_any, image_pos); debug("Legacy image\n"); /* diff --git a/include/spl.h b/include/spl.h index 8c5523083b..7fad62c043 100644 --- a/include/spl.h +++ b/include/spl.h @@ -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, offset); +binman_sym_extern(ulong, u_boot_any, image_pos); /** * spl_load_simple_fit() - Loads a fit image from a device. diff --git a/tools/binman/README b/tools/binman/README index 4b13776ffa..df88819a1c 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -324,6 +324,12 @@ offset-unset: property is present, binman will give an error if another entry does not set the offset (with the GetOffsets() method). +image-pos: + This cannot be set on entry (or at least it is ignored if it is), but + with the -u option, binman will set it to the absolute image position + for each entry. This makes it easy to find out exactly where the entry + ended up in the image, regardless of parent sections, etc. + The attributes supported for images are described below. Several are similar to those for entries. @@ -550,8 +556,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 offsets 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 @@ -577,6 +583,18 @@ 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 --------- diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index 1604b9915e..08c6f0cda8 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -96,7 +96,7 @@ class Section(object): def AddMissingProperties(self): """Add new properties to the device tree as needed for this entry""" - for prop in ['offset', 'size']: + for prop in ['offset', 'size', 'image-pos']: if not prop in self._node.props: self._node.AddZeroProp(prop) for entry in self._entries.values(): @@ -105,6 +105,7 @@ class Section(object): 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() @@ -260,6 +261,11 @@ class Section(object): 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 @@ -341,6 +347,8 @@ class Section(object): raise ValueError(err) if prop_name == 'offset': return entry.offset + elif prop_name == 'image_pos': + return entry.image_pos else: raise ValueError("%s: No such property '%s'" % (msg, prop_name)) diff --git a/tools/binman/control.py b/tools/binman/control.py index 17c6d7a2d2..9ac392b7e5 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -161,6 +161,7 @@ def Binman(options, args): image.PackEntries() image.CheckSize() image.CheckEntries() + image.SetImagePos() if options.update_fdt: image.SetCalculatedProperties() image.ProcessEntryContents() diff --git a/tools/binman/entry.py b/tools/binman/entry.py index cb693c9382..8004918eb5 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -64,6 +64,7 @@ class Entry(object): self.pad_before = 0 self.pad_after = 0 self.offset_unset = False + self.image_pos = None if read_node: self.ReadNode() @@ -133,7 +134,7 @@ class Entry(object): def AddMissingProperties(self): """Add new properties to the device tree as needed for this entry""" - for prop in ['offset', 'size']: + for prop in ['offset', 'size', 'image-pos']: if not prop in self._node.props: self._node.AddZeroProp(prop) @@ -141,6 +142,7 @@ class Entry(object): """Set the value of device-tree properties calculated by binman""" 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 @@ -265,6 +267,14 @@ class Entry(object): 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 diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 1d27301ae9..b90b80e4ce 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -46,6 +46,10 @@ class Entry_section(Entry): self.size = self._section.GetSize() return super(Entry_section, self).Pack(offset) + def SetImagePos(self, image_pos): + Entry.SetImagePos(self, image_pos) + self._section.SetImagePos(image_pos + self.offset) + def WriteSymbols(self, section): """Write symbol values into binary files for access at run time""" self._section.WriteSymbols() diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 94e48f3ab3..94a50aac16 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1051,23 +1051,30 @@ class TestFunctional(unittest.TestCase): """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, ['offset', 'size']) + props = self._GetPropTree(out_dtb_fname, ['offset', 'size', + 'image-pos']) with open('/tmp/x.dtb', 'wb') as outf: with open(out_dtb_fname) as inf: outf.write(inf.read()) self.assertEqual({ + 'image-pos': 0, 'offset': 0, '_testing:offset': 32, '_testing:size': 1, + '_testing:image-pos': 32, 'section@0/u-boot:offset': 0, 'section@0/u-boot:size': len(U_BOOT_DATA), + 'section@0/u-boot:image-pos': 0, 'section@0:offset': 0, 'section@0:size': 16, + 'section@0:image-pos': 0, 'section@1/u-boot:offset': 0, 'section@1/u-boot:size': len(U_BOOT_DATA), + 'section@1/u-boot:image-pos': 16, 'section@1:offset': 16, 'section@1:size': 16, + 'section@1:image-pos': 16, 'size': 40 }, props) diff --git a/tools/binman/image.py b/tools/binman/image.py index ed8261a652..4debc73451 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -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 diff --git a/tools/binman/test/u_boot_binman_syms b/tools/binman/test/u_boot_binman_syms index f2dcb887ed84e4799ae9eca1d2d39585635fb4c6..126a1a623092cbe25f7a24118d26d4a0e8d0e0fc 100755 GIT binary patch delta 184 zcmdm@wnuG(0;9@C#rOOoD(}AY&Moeq!@T5hu`>ITRenxu^(Lzb7%>?dOb!>&o!ltk z?W@ATz`z2;f(%RyQ-CxK2rw`*gaSDRKn#;F2eM6|3Iu@aJAiDEeh>ihXHS+C44K>` zC^$J@P@FR}H!(dmz97GN@@&DClbwZ98BHgz6jElqHTkTNI%Ce{uR`jqI)V%g>n2|m G5(WUg>nL;p delta 188 zcmdm^wnc4%0;9}E#rOOo%aW%XXzPXS$a9c0A{L|H&!>MvRt|BLs9OcL;d+ z8Za<0umG_j0~13ikXAwBPXw}IK!Aafp#;b=feHu!)wcrKAoU;s;?I~YD;P3)f*|MQ vXhHSKO9Yos_7O^9G?~0nNSX2S Date: Tue, 17 Jul 2018 13:25:31 -0600 Subject: [PATCH 07/28] dtoc: Add missing comments to fdt_util This module has a few missing comments. Add them. Signed-off-by: Simon Glass --- tools/dtoc/fdt_util.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index 5b631419a9..05cb9c0775 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -5,6 +5,9 @@ # Written by Simon Glass # +# 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,17 @@ 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 From 53af22a9958ca93c89056ad2750ad0d46a51b6c8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:32 -0600 Subject: [PATCH 08/28] binman: Add support for passing 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). Add a -a option which can be used like this: -a= where is the property to set is the value to set it to Signed-off-by: Simon Glass --- tools/binman/README | 20 +++++ tools/binman/cmdline.py | 2 + tools/binman/control.py | 19 +++++ tools/binman/entry.py | 69 +++++++++++++++ tools/binman/etype/_testing.py | 19 ++++- tools/binman/ftest.py | 83 ++++++++++++++++++- tools/binman/test/62_entry_args.dts | 14 ++++ tools/binman/test/63_entry_args_missing.dts | 13 +++ tools/binman/test/64_entry_args_required.dts | 14 ++++ .../test/65_entry_args_unknown_datatype.dts | 15 ++++ tools/dtoc/fdt_util.py | 21 +++++ tools/dtoc/test_fdt.py | 8 ++ 12 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 tools/binman/test/62_entry_args.dts create mode 100644 tools/binman/test/63_entry_args_missing.dts create mode 100644 tools/binman/test/64_entry_args_required.dts create mode 100644 tools/binman/test/65_entry_args_unknown_datatype.dts diff --git a/tools/binman/README b/tools/binman/README index df88819a1c..d60c7fd6a3 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -615,6 +615,26 @@ 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= + +where + + is the property to set + 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 ------------- diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 5c9b4dfead..54e4fb13dc 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -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', diff --git a/tools/binman/control.py b/tools/binman/control.py index 9ac392b7e5..ab894a8aa8 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -7,6 +7,7 @@ from collections import OrderedDict import os +import re import sys import tools @@ -25,6 +26,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 +80,20 @@ 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 Binman(options, args): """The main control code for binman @@ -116,6 +134,7 @@ def Binman(options, args): 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 diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 8004918eb5..de07f27215 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -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 @@ -249,6 +258,33 @@ class Entry(object): """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 @@ -307,3 +343,36 @@ class Entry(object): indent: Curent indent level of map (0=none, 1=one level, etc.) """ self.WriteMapLine(fd, indent, self.name, self.offset, self.size) + + 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 diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py index 31f625c026..3eeec72b36 100644 --- a/tools/binman/etype/_testing.py +++ b/tools/binman/etype/_testing.py @@ -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 @@ -27,6 +29,21 @@ class Entry__testing(Entry): 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: diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 94a50aac16..c54cd12e71 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -146,7 +146,8 @@ 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: @@ -163,6 +164,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 +192,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 @@ -220,7 +224,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') @@ -1085,5 +1090,77 @@ 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)) + + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/62_entry_args.dts b/tools/binman/test/62_entry_args.dts new file mode 100644 index 0000000000..4d4f102d60 --- /dev/null +++ b/tools/binman/test/62_entry_args.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + test-int-fdt = <123>; + }; + }; +}; diff --git a/tools/binman/test/63_entry_args_missing.dts b/tools/binman/test/63_entry_args_missing.dts new file mode 100644 index 0000000000..1644e2fef3 --- /dev/null +++ b/tools/binman/test/63_entry_args_missing.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + }; + }; +}; diff --git a/tools/binman/test/64_entry_args_required.dts b/tools/binman/test/64_entry_args_required.dts new file mode 100644 index 0000000000..705be10069 --- /dev/null +++ b/tools/binman/test/64_entry_args_required.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + require-args; + test-str-fdt = "test0"; + }; + }; +}; diff --git a/tools/binman/test/65_entry_args_unknown_datatype.dts b/tools/binman/test/65_entry_args_unknown_datatype.dts new file mode 100644 index 0000000000..3e4838f4ff --- /dev/null +++ b/tools/binman/test/65_entry_args_unknown_datatype.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + _testing { + test-str-fdt = "test0"; + test-int-fdt = <123>; + force-bad-datatype; + }; + }; +}; diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index 05cb9c0775..b229038569 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -147,3 +147,24 @@ def GetBool(node, propname, default=False): if propname in node.props: return True return default + +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) diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index f085b1dd1a..03cf4b4f7c 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -380,6 +380,14 @@ 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 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)) From bb74837c9a32e51187d843c2469f0176d674c5f9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:33 -0600 Subject: [PATCH 09/28] binman: Support an entry that holds text It is useful to able to write an identifying string to the image within an entry. Add a 'text' entry type to handle this. The actual text is typically passed to binman on the command line. The text is not itself nul-terminated but this can be achieved if required by setting the size of the entry to something larger than the text. Signed-off-by: Simon Glass --- tools/binman/etype/text.py | 50 +++++++++++++++++++++++++++++++++++ tools/binman/ftest.py | 16 +++++++++++ tools/binman/test/66_text.dts | 28 ++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 tools/binman/etype/text.py create mode 100644 tools/binman/test/66_text.dts diff --git a/tools/binman/etype/text.py b/tools/binman/etype/text.py new file mode 100644 index 0000000000..1c69afa72e --- /dev/null +++ b/tools/binman/etype/text.py @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# + +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. + + 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 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index c54cd12e71..696889601e 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -41,6 +41,9 @@ FSP_DATA = 'fsp' CMC_DATA = 'cmc' VBT_DATA = 'vbt' MRC_DATA = 'mrc' +TEXT_DATA = 'text' +TEXT_DATA2 = 'text2' +TEXT_DATA3 = 'text3' class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -1161,6 +1164,19 @@ class TestFunctional(unittest.TestCase): 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) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/66_text.dts b/tools/binman/test/66_text.dts new file mode 100644 index 0000000000..59b1fed0ef --- /dev/null +++ b/tools/binman/test/66_text.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + text { + size = <8>; + text-label = "test-id"; + }; + text2 { + type = "text"; + text-label = "test-id2"; + }; + text3 { + type = "text"; + text-label = "test-id3"; + }; + /* This one does not use command-line args */ + text4 { + type = "text"; + text-label = "test-id4"; + test-id4 = "some text"; + }; + }; +}; From 9b1a804d52832aa2ae62bef0254451606d5a1901 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:34 -0600 Subject: [PATCH 10/28] binman: Allow help to work without libfdt At present binman needs libfdt.py to be available before it will do anything, even print help. Import those modules later to avoid this, as it is bad practice to fail to even show help on startup. Signed-off-by: Simon Glass --- tools/binman/control.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/binman/control.py b/tools/binman/control.py index ab894a8aa8..3c931d9aeb 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -13,8 +13,6 @@ import tools import command import elf -import fdt -import fdt_util from image import Image import tout @@ -129,6 +127,11 @@ 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: From 3fb397bba01a511f8ca2b091a359495e18badd7e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:35 -0600 Subject: [PATCH 11/28] binman: Expand documentation for entries At present only the more complex entries are documented. It is useful to have documentation for all entries in one place. As a first step, add and expand the documentation to cover all entries. Signed-off-by: Simon Glass --- tools/binman/etype/_testing.py | 26 ++++++++++++++++++++- tools/binman/etype/blob.py | 12 ++++++++++ tools/binman/etype/intel_cmc.py | 10 ++++++++ tools/binman/etype/intel_descriptor.py | 18 +++++++++++--- tools/binman/etype/intel_fsp.py | 14 +++++++++++ tools/binman/etype/intel_me.py | 15 ++++++++++++ tools/binman/etype/intel_mrc.py | 11 +++++++++ tools/binman/etype/intel_vbt.py | 10 ++++++++ tools/binman/etype/intel_vga.py | 12 ++++++++++ tools/binman/etype/section.py | 17 ++++++++++++++ tools/binman/etype/text.py | 9 ++++++- tools/binman/etype/u_boot.py | 16 +++++++++++++ tools/binman/etype/u_boot_dtb.py | 9 +++++++ tools/binman/etype/u_boot_dtb_with_ucode.py | 12 ++++++++-- tools/binman/etype/u_boot_img.py | 11 +++++++++ tools/binman/etype/u_boot_nodtb.py | 11 +++++++++ tools/binman/etype/u_boot_spl.py | 21 +++++++++++++++++ tools/binman/etype/u_boot_spl_bss_pad.py | 16 +++++++++++++ tools/binman/etype/u_boot_spl_dtb.py | 9 +++++++ tools/binman/etype/u_boot_spl_nodtb.py | 12 ++++++++++ tools/binman/etype/u_boot_ucode.py | 6 +++++ tools/binman/etype/u_boot_with_ucode_ptr.py | 9 +++++-- tools/binman/etype/x86_start16.py | 14 +++++++++++ tools/binman/etype/x86_start16_spl.py | 14 +++++++++++ 24 files changed, 305 insertions(+), 9 deletions(-) diff --git a/tools/binman/etype/_testing.py b/tools/binman/etype/_testing.py index 3eeec72b36..02c165c0c3 100644 --- a/tools/binman/etype/_testing.py +++ b/tools/binman/etype/_testing.py @@ -15,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 GetOffsets() + 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) @@ -26,6 +48,8 @@ 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') diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index 28e6651a93..692d8683c3 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -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) diff --git a/tools/binman/etype/intel_cmc.py b/tools/binman/etype/intel_cmc.py index b8621e0cc5..fa6f7793c6 100644 --- a/tools/binman/etype/intel_cmc.py +++ b/tools/binman/etype/intel_cmc.py @@ -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) diff --git a/tools/binman/etype/intel_descriptor.py b/tools/binman/etype/intel_descriptor.py index 0a5151d466..6acbbd8b7a 100644 --- a/tools/binman/etype/intel_descriptor.py +++ b/tools/binman/etype/intel_descriptor.py @@ -13,6 +13,7 @@ from blob import Entry_blob FD_SIGNATURE = struct.pack(' (actual name is the value of text-label): contains the string to + place in the entry. Example node: diff --git a/tools/binman/etype/u_boot.py b/tools/binman/etype/u_boot.py index b6058bf6b5..23dd12ce43 100644 --- a/tools/binman/etype/u_boot.py +++ b/tools/binman/etype/u_boot.py @@ -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) diff --git a/tools/binman/etype/u_boot_dtb.py b/tools/binman/etype/u_boot_dtb.py index dd3c5b2790..fb3dd1cf64 100644 --- a/tools/binman/etype/u_boot_dtb.py +++ b/tools/binman/etype/u_boot_dtb.py @@ -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) diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py index 55d3a38a64..752d0ac0ba 100644 --- a/tools/binman/etype/u_boot_dtb_with_ucode.py +++ b/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -14,8 +14,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) diff --git a/tools/binman/etype/u_boot_img.py b/tools/binman/etype/u_boot_img.py index 6e0b736af1..1ec0757c7f 100644 --- a/tools/binman/etype/u_boot_img.py +++ b/tools/binman/etype/u_boot_img.py @@ -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) diff --git a/tools/binman/etype/u_boot_nodtb.py b/tools/binman/etype/u_boot_nodtb.py index ca9e53a094..a4b95a4390 100644 --- a/tools/binman/etype/u_boot_nodtb.py +++ b/tools/binman/etype/u_boot_nodtb.py @@ -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) diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index 9edd2dad03..0e99a2d2a7 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -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 is 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' diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py index 65f631d3c5..00b7ac5004 100644 --- a/tools/binman/etype/u_boot_spl_bss_pad.py +++ b/tools/binman/etype/u_boot_spl_bss_pad.py @@ -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) diff --git a/tools/binman/etype/u_boot_spl_dtb.py b/tools/binman/etype/u_boot_spl_dtb.py index eefa1ff53a..6a30edc83c 100644 --- a/tools/binman/etype/u_boot_spl_dtb.py +++ b/tools/binman/etype/u_boot_spl_dtb.py @@ -9,6 +9,15 @@ 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) diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py index 99e56ebe3b..41c17366b1 100644 --- a/tools/binman/etype/u_boot_spl_nodtb.py +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -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) diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py index 922a607cfe..0f1761afc8 100644 --- a/tools/binman/etype/u_boot_ucode.py +++ b/tools/binman/etype/u_boot_ucode.py @@ -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 diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py index 97e58a0bfc..51e7ba48f5 100644 --- a/tools/binman/etype/u_boot_with_ucode_ptr.py +++ b/tools/binman/etype/u_boot_with_ucode_ptr.py @@ -17,8 +17,13 @@ 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) diff --git a/tools/binman/etype/x86_start16.py b/tools/binman/etype/x86_start16.py index 23d27f0704..7d32ecd321 100644 --- a/tools/binman/etype/x86_start16.py +++ b/tools/binman/etype/x86_start16.py @@ -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) diff --git a/tools/binman/etype/x86_start16_spl.py b/tools/binman/etype/x86_start16_spl.py index 176420ba88..d85909e7ae 100644 --- a/tools/binman/etype/x86_start16_spl.py +++ b/tools/binman/etype/x86_start16_spl.py @@ -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) From fd8d1f79623d2944d9ca8469a3681d53b8b277f9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:36 -0600 Subject: [PATCH 12/28] binman: Allow creation of entry documentation Binman supports quite a number of different entries now. The operation of these is not always obvious but at present the source code is the only reference for understanding how an entry works. Add a way to create documentation (from the source code) which can be put in a new 'README.entries' file. Signed-off-by: Simon Glass --- tools/binman/binman.py | 22 +++-- tools/binman/cmdline.py | 2 + tools/binman/control.py | 4 + tools/binman/entry.py | 93 ++++++++++++++++++--- tools/binman/etype/u_boot_dtb_with_ucode.py | 4 +- tools/binman/ftest.py | 15 ++++ 6 files changed, 118 insertions(+), 22 deletions(-) diff --git a/tools/binman/binman.py b/tools/binman/binman.py index 52e02ed91b..1536e95651 100755 --- a/tools/binman/binman.py +++ b/tools/binman/binman.py @@ -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: diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 54e4fb13dc..f0de4ded44 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -28,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', diff --git a/tools/binman/control.py b/tools/binman/control.py index 3c931d9aeb..2de1c86ecf 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -92,6 +92,10 @@ def SetEntryArgs(args): 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 diff --git a/tools/binman/entry.py b/tools/binman/entry.py index de07f27215..dc09b81677 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -78,20 +78,18 @@ class Entry(object): 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: 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) + 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('-', '_') @@ -110,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): @@ -376,3 +393,53 @@ class Entry(object): 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)) diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py index 752d0ac0ba..adcb898a7b 100644 --- a/tools/binman/etype/u_boot_dtb_with_ucode.py +++ b/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -6,7 +6,6 @@ # import control -import fdt from entry import Entry from blob import Entry_blob import tools @@ -38,6 +37,9 @@ 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') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 696889601e..9c01805c72 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -21,6 +21,7 @@ import control import elf import fdt import fdt_util +import test_util import tools import tout @@ -1177,6 +1178,20 @@ class TestFunctional(unittest.TestCase): 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)) + if __name__ == "__main__": unittest.main() From 5a5da7ce153b19bc3106e0bdb625b2e211852914 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:37 -0600 Subject: [PATCH 13/28] binman: Create README.entries Create a new README containing documentation for the entry types supported by binman. This provides an easy reference in one place. It is automatically generated from the source-code documentation. Add a reference to this from the binman README. Signed-off-by: Simon Glass --- tools/binman/README | 9 + tools/binman/README.entries | 448 ++++++++++++++++++++++++++++++++++++ 2 files changed, 457 insertions(+) create mode 100644 tools/binman/README.entries diff --git a/tools/binman/README b/tools/binman/README index d60c7fd6a3..cb34171e5f 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -453,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 ------------------ diff --git a/tools/binman/README.entries b/tools/binman/README.entries new file mode 100644 index 0000000000..60cb2488c0 --- /dev/null +++ b/tools/binman/README.entries @@ -0,0 +1,448 @@ +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: 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 + (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 is 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-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: 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. + + + From 11e36ccea174043229319263f9d0b5b7f7cca654 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:38 -0600 Subject: [PATCH 14/28] binman: Add support for flashrom FMAP Add an entry which can hold an FMAP region as used by flashrom, an open-source flashing tool used on Linux x86 machines. This provides a simplified non-hierarchical view of the entries in the image and has a signature at the start to allow flashrom to find it in the image. Signed-off-by: Simon Glass --- tools/binman/README.entries | 20 +++++++ tools/binman/entry.py | 9 +++ tools/binman/etype/fmap.py | 61 +++++++++++++++++++ tools/binman/etype/section.py | 7 ++- tools/binman/fmap_util.py | 109 ++++++++++++++++++++++++++++++++++ tools/binman/ftest.py | 32 ++++++++++ tools/binman/test/67_fmap.dts | 29 +++++++++ 7 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 tools/binman/etype/fmap.py create mode 100644 tools/binman/fmap_util.py create mode 100644 tools/binman/test/67_fmap.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 60cb2488c0..0b3be69f5e 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -26,6 +26,26 @@ example the 'u_boot' entry which provides the filename 'u-boot.bin'. +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: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file ------------------------------------------------------------------------- diff --git a/tools/binman/entry.py b/tools/binman/entry.py index dc09b81677..8b910feff6 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -361,6 +361,15 @@ class Entry(object): """ self.WriteMapLine(fd, indent, self.name, self.offset, self.size) + 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 diff --git a/tools/binman/etype/fmap.py b/tools/binman/etype/fmap.py new file mode 100644 index 0000000000..f1dd81ec49 --- /dev/null +++ b/tools/binman/etype/fmap.py @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# +# 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()) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 2e68f276f0..f5b2ed67cf 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -30,8 +30,8 @@ class Entry_section(Entry): hierarchical images to be created. See 'Sections and hierarchical images' in the binman README for more information. """ - def __init__(self, image, etype, node): - Entry.__init__(self, image, etype, node) + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) self._section = bsection.Section(node.name, node) def ProcessFdt(self, fdt): @@ -89,3 +89,6 @@ class Entry_section(Entry): fd: File to write the map to """ self._section.WriteMap(fd, indent) + + def GetEntries(self): + return self._section.GetEntries() diff --git a/tools/binman/fmap_util.py b/tools/binman/fmap_util.py new file mode 100644 index 0000000000..7d520e3391 --- /dev/null +++ b/tools/binman/fmap_util.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# +# 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 = '; + #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 { + }; + }; +}; From ec127af0429ad6f9818297f9c3ee77edb2154182 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:39 -0600 Subject: [PATCH 15/28] binman: Add support for a cros_ec image Add an entry type which can hold a Chrome OS EC. To make this work a new entry type is created, which supports getting a blob filename from the command line. Signed-off-by: Simon Glass --- tools/binman/README.entries | 29 ++++++++++++++++++ tools/binman/etype/blob_named_by_arg.py | 34 ++++++++++++++++++++++ tools/binman/etype/cros_ec_rw.py | 22 ++++++++++++++ tools/binman/ftest.py | 11 +++++++ tools/binman/test/68_blob_named_by_arg.dts | 12 ++++++++ 5 files changed, 108 insertions(+) create mode 100644 tools/binman/etype/blob_named_by_arg.py create mode 100644 tools/binman/etype/cros_ec_rw.py create mode 100644 tools/binman/test/68_blob_named_by_arg.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 0b3be69f5e..147dd40bff 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -26,6 +26,35 @@ 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: + - -path: Filename containing the contents of this entry (optional, + defaults to 0) + +where 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: fmap: An entry which contains an Fmap section ---------------------------------------------------- diff --git a/tools/binman/etype/blob_named_by_arg.py b/tools/binman/etype/blob_named_by_arg.py new file mode 100644 index 0000000000..344112bc42 --- /dev/null +++ b/tools/binman/etype/blob_named_by_arg.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# +# Entry-type module for a blob where the filename comes from a property in the +# node or an entry argument. The property is called '-path' where +# 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: + - -path: Filename containing the contents of this entry (optional, + defaults to 0) + + where is the blob_fname argument to the constructor. + + This entry cannot be used directly. Instead, it is used as a parent class + for another entry, which defined blob_fname. This parameter is used to + set the entry-arg or property containing the filename. The entry-arg or + property is in turn used to set the actual filename. + + See cros_ec_rw for an example of this. + """ + def __init__(self, section, etype, node, blob_fname): + Entry_blob.__init__(self, section, etype, node) + self._filename, = self.GetEntryArgsOrProps( + [EntryArg('%s-path' % blob_fname, str)]) diff --git a/tools/binman/etype/cros_ec_rw.py b/tools/binman/etype/cros_ec_rw.py new file mode 100644 index 0000000000..261f8657a6 --- /dev/null +++ b/tools/binman/etype/cros_ec_rw.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# +# Entry-type module for a Chromium OS EC image (read-write section) +# + +from blob_named_by_arg import Entry_blob_named_by_arg + + +class Entry_cros_ec_rw(Entry_blob_named_by_arg): + """A blob entry which contains a Chromium OS read-write EC image + + Properties / Entry arguments: + - cros-ec-rw-path: Filename containing the EC image + + This entry holds a Chromium OS EC (embedded controller) image, for use in + updating the EC on startup via software sync. + """ + def __init__(self, section, etype, node): + Entry_blob_named_by_arg.__init__(self, section, etype, node, + 'cros-ec-rw') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index bd4de4e287..5428ee651a 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -46,6 +46,8 @@ MRC_DATA = 'mrc' TEXT_DATA = 'text' TEXT_DATA2 = 'text2' TEXT_DATA3 = 'text3' +CROS_EC_RW_DATA = 'ecrw' + class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -92,6 +94,7 @@ 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) self._output_setup = False # ELF file with a '_dt_ucode_base_size' symbol @@ -1224,6 +1227,14 @@ class TestFunctional(unittest.TestCase): 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) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/68_blob_named_by_arg.dts b/tools/binman/test/68_blob_named_by_arg.dts new file mode 100644 index 0000000000..e129f843cd --- /dev/null +++ b/tools/binman/test/68_blob_named_by_arg.dts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + cros-ec-rw { + }; + }; +}; From 3af8e49ceff044021725fc547b19ebac22d0b0f7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:40 -0600 Subject: [PATCH 16/28] binman: Add an entry filled with a repeating byte It is sometimes useful to have an area of the image which is all zeroes, or all 0xff. This can often be achieved by padding the size of an an existing entry and setting the pad byte for an entry or image. But it is useful to have an explicit means of adding blocks of repeating data to the image. Add a 'fill' entry type to handle this. Signed-off-by: Simon Glass --- tools/binman/README.entries | 16 ++++++++++++++ tools/binman/etype/fill.py | 32 +++++++++++++++++++++++++++ tools/binman/ftest.py | 13 +++++++++++ tools/binman/test/69_fill.dts | 15 +++++++++++++ tools/binman/test/70_fill_no_size.dts | 14 ++++++++++++ tools/dtoc/fdt_util.py | 23 +++++++++++++++++++ tools/dtoc/test_fdt.py | 15 ++++++++++++- 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 tools/binman/etype/fill.py create mode 100644 tools/binman/test/69_fill.dts create mode 100644 tools/binman/test/70_fill_no_size.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 147dd40bff..6f09626b24 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -55,6 +55,22 @@ 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 ---------------------------------------------------- diff --git a/tools/binman/etype/fill.py b/tools/binman/etype/fill.py new file mode 100644 index 0000000000..7210a8324a --- /dev/null +++ b/tools/binman/etype/fill.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# + +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 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 5428ee651a..4e467145d7 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1235,6 +1235,19 @@ class TestFunctional(unittest.TestCase): 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)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/69_fill.dts b/tools/binman/test/69_fill.dts new file mode 100644 index 0000000000..e372ea37aa --- /dev/null +++ b/tools/binman/test/69_fill.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + fill { + size = <8>; + fill-byte = [ff]; + }; + }; +}; diff --git a/tools/binman/test/70_fill_no_size.dts b/tools/binman/test/70_fill_no_size.dts new file mode 100644 index 0000000000..7b1fcf1b68 --- /dev/null +++ b/tools/binman/test/70_fill_no_size.dts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + fill { + fill-byte = [ff]; + }; + }; +}; diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index b229038569..d762f93a9a 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -148,6 +148,29 @@ def GetBool(node, propname, default=False): 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 GetDatatype(node, propname, datatype): """Get a value of a given type from a property diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index 03cf4b4f7c..38e1732f52 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -380,6 +380,20 @@ 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 testGetDataType(self): self.assertEqual(1, fdt_util.GetDatatype(self.node, 'intval', int)) self.assertEqual('message', fdt_util.GetDatatype(self.node, 'stringval', @@ -387,7 +401,6 @@ class TestFdtUtil(unittest.TestCase): 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)) From 1d85888cdc1d831ff7a70b95922ee85195e7e09f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:41 -0600 Subject: [PATCH 17/28] dtoc: Export the _FindNode() function This is useful for clients that want to find a node. Export it so it can be used by others. Signed-off-by: Simon Glass --- tools/dtoc/fdt.py | 4 ++-- tools/dtoc/test_fdt.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index 9d69b426c1..01e39b8a2a 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -181,7 +181,7 @@ class Node: self.subnodes = [] self.props = {} - def _FindNode(self, name): + def FindNode(self, name): """Find a node given its name Args: @@ -349,7 +349,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 diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index 38e1732f52..abec9e57e1 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -155,12 +155,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""" From 2b19321ef9cdc77e00c4d063330e2a4a6818c4fc Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:42 -0600 Subject: [PATCH 18/28] patman: Allow test commands to fall back to real ones Tests use the 'test_result' feature to return a predetermined command result for particular commands. The avoids needing to have the real command available just to run a test. It works by calling the function provided by the test, to get the value. However sometimes the test does need to run the real command. Allow it to fall back to do this when the function does not return a result. Signed-off-by: Simon Glass --- tools/patman/command.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/patman/command.py b/tools/patman/command.py index 598bfdcd90..14edcdaffd 100644 --- a/tools/patman/command.py +++ b/tools/patman/command.py @@ -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) From aeffc5e95627a788c328cb92f82ad9a38d792ed8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:43 -0600 Subject: [PATCH 19/28] patman: Add a few more helpers to the tools library Add functions to read and write a file, looking through a list of search paths to find it. Signed-off-by: Simon Glass --- tools/patman/tools.py | 76 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 700cb4505d..006306bd60 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -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. @@ -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) From 0ef87aa332e024c94d07a897c9f595be37077de8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:44 -0600 Subject: [PATCH 20/28] binman: Add support for Chromium GBB This entry contains a Google Binary Block, used to store keys and bitmaps in a Chromium image. Signed-off-by: Simon Glass --- tools/binman/README.entries | 19 +++++ tools/binman/etype/gbb.py | 96 ++++++++++++++++++++++++++ tools/binman/ftest.py | 56 +++++++++++++++ tools/binman/test/71_gbb.dts | 31 +++++++++ tools/binman/test/72_gbb_too_small.dts | 10 +++ tools/binman/test/73_gbb_no_size.dts | 9 +++ 6 files changed, 221 insertions(+) create mode 100644 tools/binman/etype/gbb.py create mode 100644 tools/binman/test/71_gbb.dts create mode 100644 tools/binman/test/72_gbb_too_small.dts create mode 100644 tools/binman/test/73_gbb_no_size.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 6f09626b24..41b70192c0 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -91,6 +91,25 @@ 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 ------------------------------------------------------------------------- diff --git a/tools/binman/etype/gbb.py b/tools/binman/etype/gbb.py new file mode 100644 index 0000000000..8fe10f4713 --- /dev/null +++ b/tools/binman/etype/gbb.py @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# + +# Support for a Chromium OS Google Binary Block, used to record read-only +# information mostly used by firmware. + +from collections import OrderedDict + +import command +from entry import Entry, EntryArg + +import fdt_util +import tools + +# Build GBB flags. +# (src/platform/vboot_reference/firmware/include/gbb_header.h) +gbb_flag_properties = { + 'dev-screen-short-delay': 0x1, + 'load-option-roms': 0x2, + 'enable-alternate-os': 0x4, + 'force-dev-switch-on': 0x8, + 'force-dev-boot-usb': 0x10, + 'disable-fw-rollback-check': 0x20, + 'enter-triggers-tonorm': 0x40, + 'force-dev-boot-legacy': 0x80, + 'faft-key-override': 0x100, + 'disable-ec-software-sync': 0x200, + 'default-dev-boot-legacy': 0x400, + 'disable-pd-software-sync': 0x800, + 'disable-lid-shutdown': 0x1000, + 'force-dev-boot-fastboot-full-cap': 0x2000, + 'enable-serial': 0x4000, + 'disable-dwmp': 0x8000, +} + + +class Entry_gbb(Entry): + """An entry which contains a Chromium OS Google Binary Block + + Properties / Entry arguments: + - hardware-id: Hardware ID to use for this build (a string) + - keydir: Directory containing the public keys to use + - bmpblk: Filename containing images used by recovery + + Chromium OS uses a GBB to store various pieces of information, in particular + the root and recovery keys that are used to verify the boot process. Some + more details are here: + + https://www.chromium.org/chromium-os/firmware-porting-guide/2-concepts + + but note that the page dates from 2013 so is quite out of date. See + README.chromium for how to obtain the required keys and tools. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + self.hardware_id, self.keydir, self.bmpblk = self.GetEntryArgsOrProps( + [EntryArg('hardware-id', str), + EntryArg('keydir', str), + EntryArg('bmpblk', str)]) + + # Read in the GBB flags from the config + self.gbb_flags = 0 + flags_node = node.FindNode('flags') + if flags_node: + for flag, value in gbb_flag_properties.iteritems(): + if fdt_util.GetBool(flags_node, flag): + self.gbb_flags |= value + + def ObtainContents(self): + gbb = 'gbb.bin' + fname = tools.GetOutputFilename(gbb) + if not self.size: + self.Raise('GBB must have a fixed size') + gbb_size = self.size + bmpfv_size = gbb_size - 0x2180 + if bmpfv_size < 0: + self.Raise('GBB is too small (minimum 0x2180 bytes)') + sizes = [0x100, 0x1000, bmpfv_size, 0x1000] + sizes = ['%#x' % size for size in sizes] + keydir = tools.GetInputFilename(self.keydir) + gbb_set_command = [ + 'gbb_utility', '-s', + '--hwid=%s' % self.hardware_id, + '--rootkey=%s/root_key.vbpubk' % keydir, + '--recoverykey=%s/recovery_key.vbpubk' % keydir, + '--flags=%d' % self.gbb_flags, + '--bmpfv=%s' % tools.GetInputFilename(self.bmpblk), + fname] + + tools.Run('futility', 'gbb_utility', '-c', ','.join(sizes), fname) + tools.Run('futility', *gbb_set_command) + + self.SetContents(tools.ReadFile(fname)) + return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 4e467145d7..f15b215c60 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -47,6 +47,8 @@ TEXT_DATA = 'text' TEXT_DATA2 = 'text2' TEXT_DATA3 = 'text3' CROS_EC_RW_DATA = 'ecrw' +GBB_DATA = 'gbbd' +BMPBLK_DATA = 'bmp' class TestFunctional(unittest.TestCase): @@ -95,6 +97,8 @@ class TestFunctional(unittest.TestCase): 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 @@ -287,6 +291,21 @@ class TestFunctional(unittest.TestCase): fd.write(contents) return pathname + @classmethod + def _MakeInputDir(self, dirname): + """Create a new test input directory, creating directories as needed + + Args: + dirname: Directory name to create + + Returns: + Full pathname of directory created + """ + pathname = os.path.join(self._indir, dirname) + if not os.path.exists(pathname): + os.makedirs(pathname) + return pathname + @classmethod def TestFile(self, fname): return os.path.join(self._binman_dir, 'test', fname) @@ -1248,6 +1267,43 @@ class TestFunctional(unittest.TestCase): 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)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/71_gbb.dts b/tools/binman/test/71_gbb.dts new file mode 100644 index 0000000000..551756372a --- /dev/null +++ b/tools/binman/test/71_gbb.dts @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + gbb { + size = <0x2180>; + flags { + dev-screen-short-delay; + load-option-roms; + enable-alternate-os; + force-dev-switch-on; + force-dev-boot-usb; + disable-fw-rollback-check; + enter-triggers-tonorm; + force-dev-boot-legacy; + faft-key-override; + disable-ec-software-sync; + default-dev-boot-legacy; + disable-pd-software-sync; + disable-lid-shutdown; + force-dev-boot-fastboot-full-cap; + enable-serial; + disable-dwmp; + }; + }; + }; +}; diff --git a/tools/binman/test/72_gbb_too_small.dts b/tools/binman/test/72_gbb_too_small.dts new file mode 100644 index 0000000000..c088f36a1d --- /dev/null +++ b/tools/binman/test/72_gbb_too_small.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + gbb { + size = <0x200>; + }; + }; +}; diff --git a/tools/binman/test/73_gbb_no_size.dts b/tools/binman/test/73_gbb_no_size.dts new file mode 100644 index 0000000000..83be403785 --- /dev/null +++ b/tools/binman/test/73_gbb_no_size.dts @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + gbb { + }; + }; +}; From 4f5dea4543f2b7ebea803fe9b176abf5b637d988 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:45 -0600 Subject: [PATCH 21/28] patman: Show the current directory in GetInputFilename() When this fails it is useful to see the current directory, since U-Boot's build system will typically change into the output directory during the build. Add this information to the error. Signed-off-by: Simon Glass --- tools/patman/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/patman/tools.py b/tools/patman/tools.py index 006306bd60..e80481438b 100644 --- a/tools/patman/tools.py +++ b/tools/patman/tools.py @@ -116,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: From 94a7c603b45b9abdd9e6960ed2b096dd4553c91c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:46 -0600 Subject: [PATCH 22/28] dtoc: Add a function to obtain a list of phandles Add a function which can decode a property containing a list of phandles. This is useful for finding nodes linked to a property. Also provide a way to look up a single phandle and get the Fdt object from a Node. Signed-off-by: Simon Glass --- tools/dtoc/fdt.py | 19 +++++++++++++++++++ tools/dtoc/fdt_util.py | 18 ++++++++++++++++++ tools/dtoc/test_fdt.py | 20 ++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py index 01e39b8a2a..d36179bad3 100644 --- a/tools/dtoc/fdt.py +++ b/tools/dtoc/fdt.py @@ -181,6 +181,14 @@ class Node: self.subnodes = [] self.props = {} + 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 @@ -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 diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index d762f93a9a..5fbfc8877b 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -171,6 +171,24 @@ def GetByte(node, propname, default=None): (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 diff --git a/tools/dtoc/test_fdt.py b/tools/dtoc/test_fdt.py index abec9e57e1..6fe03ac53d 100755 --- a/tools/dtoc/test_fdt.py +++ b/tools/dtoc/test_fdt.py @@ -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""" @@ -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""" @@ -394,6 +405,15 @@ class TestFdtUtil(unittest.TestCase): 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', From 24d0d3c30db0bba6579ae55e1d6202e229c23a0e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:47 -0600 Subject: [PATCH 23/28] binman: Add an entry for a Chromium vblock This adds support for a Chromium verified boot block, used to sign a read-write section of the image. Signed-off-by: Simon Glass --- tools/binman/README.entries | 17 +++++ tools/binman/bsection.py | 24 +++++++ tools/binman/entry.py | 2 +- tools/binman/etype/vblock.py | 74 +++++++++++++++++++++ tools/binman/ftest.py | 41 ++++++++++++ tools/binman/test/74_vblock.dts | 28 ++++++++ tools/binman/test/75_vblock_no_content.dts | 23 +++++++ tools/binman/test/76_vblock_bad_phandle.dts | 24 +++++++ tools/binman/test/77_vblock_bad_entry.dts | 27 ++++++++ 9 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 tools/binman/etype/vblock.py create mode 100644 tools/binman/test/74_vblock.dts create mode 100644 tools/binman/test/75_vblock_no_content.dts create mode 100644 tools/binman/test/76_vblock_bad_phandle.dts create mode 100644 tools/binman/test/77_vblock_bad_entry.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 41b70192c0..1b75ca0052 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -496,6 +496,23 @@ 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 ------------------------------------------------------- diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index 08c6f0cda8..70a6ec1776 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -381,3 +381,27 @@ class Section(object): Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size) for entry in self._entries.values(): entry.WriteMap(fd, indent + 1) + + def GetContentsByPhandle(self, phandle, source_entry): + """Get the data contents of an entry specified by a phandle + + This uses a phandle to look up a node and and find the entry + associated with it. Then it returnst he contents of that entry. + + Args: + phandle: Phandle to look up (integer) + source_entry: Entry containing that phandle (used for error + reporting) + + Returns: + data from associated entry (as a string), or None if not found + """ + node = self._node.GetFdt().LookupPhandle(phandle) + if not node: + source_entry.Raise("Cannot find node for phandle %d" % phandle) + for entry in self._entries.values(): + if entry._node == node: + if entry.data is None: + return None + return entry.data + source_entry.Raise("Cannot find entry for node '%s'" % node.name) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 8b910feff6..996f03e3a6 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -65,7 +65,7 @@ class Entry(object): self.name = node and (name_prefix + node.name) or 'none' self.offset = None self.size = None - self.data = '' + self.data = None self.contents_size = 0 self.align = None self.align_size = None diff --git a/tools/binman/etype/vblock.py b/tools/binman/etype/vblock.py new file mode 100644 index 0000000000..595af5456d --- /dev/null +++ b/tools/binman/etype/vblock.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# + +# 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 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f15b215c60..a6de4cb93b 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -49,6 +49,7 @@ TEXT_DATA3 = 'text3' CROS_EC_RW_DATA = 'ecrw' GBB_DATA = 'gbbd' BMPBLK_DATA = 'bmp' +VBLOCK_DATA = 'vblk' class TestFunctional(unittest.TestCase): @@ -1304,6 +1305,46 @@ class TestFunctional(unittest.TestCase): 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)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/74_vblock.dts b/tools/binman/test/74_vblock.dts new file mode 100644 index 0000000000..f0c21bfe9f --- /dev/null +++ b/tools/binman/test/74_vblock.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <&u_boot &dtb>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + /* + * Put this after the vblock so that its contents are not + * available when the vblock first tries to obtain its contents + */ + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/binman/test/75_vblock_no_content.dts b/tools/binman/test/75_vblock_no_content.dts new file mode 100644 index 0000000000..676d9474b3 --- /dev/null +++ b/tools/binman/test/75_vblock_no_content.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/binman/test/76_vblock_bad_phandle.dts b/tools/binman/test/76_vblock_bad_phandle.dts new file mode 100644 index 0000000000..ffbd0c335c --- /dev/null +++ b/tools/binman/test/76_vblock_bad_phandle.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <1000>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; +}; diff --git a/tools/binman/test/77_vblock_bad_entry.dts b/tools/binman/test/77_vblock_bad_entry.dts new file mode 100644 index 0000000000..764c42a56e --- /dev/null +++ b/tools/binman/test/77_vblock_bad_entry.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u_boot: u-boot { + }; + + vblock { + content = <&u_boot &other>; + keyblock = "firmware.keyblock"; + signprivate = "firmware_data_key.vbprivk"; + version = <1>; + kernelkey = "kernel_subkey.vbpubk"; + preamble-flags = <1>; + }; + + dtb: u-boot-dtb { + }; + }; + + other: other { + }; +}; From b8ef5b6bc871e12d036869172aa3599f5be7ee09 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:48 -0600 Subject: [PATCH 24/28] binman: Add support for adding TPL binaries Add support for U-Boot's TPL and TPL device tree. Also fix a few comments in the other device-tree entries. Signed-off-by: Simon Glass --- tools/binman/README.entries | 38 +++++++++++++++++++++++- tools/binman/etype/u_boot_spl.py | 2 +- tools/binman/etype/u_boot_spl_dtb.py | 2 +- tools/binman/etype/u_boot_tpl.py | 43 ++++++++++++++++++++++++++++ tools/binman/etype/u_boot_tpl_dtb.py | 25 ++++++++++++++++ tools/binman/ftest.py | 22 ++++++++++++-- tools/binman/test/78_u_boot_tpl.dts | 11 +++++++ 7 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 tools/binman/etype/u_boot_tpl.py create mode 100644 tools/binman/etype/u_boot_tpl_dtb.py create mode 100644 tools/binman/test/78_u_boot_tpl.dts diff --git a/tools/binman/README.entries b/tools/binman/README.entries index 1b75ca0052..c6e7b22609 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -365,7 +365,7 @@ 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 is direct flash execution is possible (e.g. +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: @@ -433,6 +433,42 @@ 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 ------------------------------------------- diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index 0e99a2d2a7..ab78714c8d 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -20,7 +20,7 @@ class Entry_u_boot_spl(Entry_blob): 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 is direct flash execution is possible (e.g. + 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: diff --git a/tools/binman/etype/u_boot_spl_dtb.py b/tools/binman/etype/u_boot_spl_dtb.py index 6a30edc83c..cb29ba3fd8 100644 --- a/tools/binman/etype/u_boot_spl_dtb.py +++ b/tools/binman/etype/u_boot_spl_dtb.py @@ -2,7 +2,7 @@ # Copyright (c) 2016 Google, Inc # Written by Simon Glass # -# 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 diff --git a/tools/binman/etype/u_boot_tpl.py b/tools/binman/etype/u_boot_tpl.py new file mode 100644 index 0000000000..4d4bb92596 --- /dev/null +++ b/tools/binman/etype/u_boot_tpl.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass +# +# Entry-type module for tpl/u-boot-tpl.bin +# + +import elf + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_tpl(Entry_blob): + """U-Boot TPL binary + + Properties / Entry arguments: + - filename: Filename of u-boot-tpl.bin (default 'tpl/u-boot-tpl.bin') + + This is the U-Boot TPL (Tertiary Program Loader) binary. This is a small + binary which loads before SPL, typically into on-chip SRAM. It is + responsible for locating, loading and jumping to SPL, the next-stage + loader. Note that SPL is not relocatable so must be loaded to the correct + address in SRAM, or written to run from the correct address if direct + flash execution is possible (e.g. on x86 devices). + + SPL can access binman symbols at runtime. See: + + 'Access to binman entry offsets at run time (symbols)' + + in the binman README for more information. + + The ELF file 'tpl/u-boot-tpl' must also be available for this to work, since + binman uses that to look up symbols to write into the TPL binary. + """ + def __init__(self, section, etype, node): + Entry_blob.__init__(self, section, etype, node) + self.elf_fname = 'tpl/u-boot-tpl' + + def GetDefaultFilename(self): + return 'tpl/u-boot-tpl.bin' + + def WriteSymbols(self, section): + elf.LookupAndWriteSymbols(self.elf_fname, self, section) diff --git a/tools/binman/etype/u_boot_tpl_dtb.py b/tools/binman/etype/u_boot_tpl_dtb.py new file mode 100644 index 0000000000..9c4e668347 --- /dev/null +++ b/tools/binman/etype/u_boot_tpl_dtb.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass +# +# 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' diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index a6de4cb93b..d09868588c 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -30,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' @@ -82,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) @@ -126,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 @@ -257,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 @@ -1345,6 +1353,14 @@ class TestFunctional(unittest.TestCase): 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) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/78_u_boot_tpl.dts b/tools/binman/test/78_u_boot_tpl.dts new file mode 100644 index 0000000000..6c60b4c46f --- /dev/null +++ b/tools/binman/test/78_u_boot_tpl.dts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot-tpl { + }; + u-boot-tpl-dtb { + }; + }; +}; From 1be70d20d8466c287cd1dc6f590171794ba67f07 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:49 -0600 Subject: [PATCH 25/28] binman: Show the image position in the map At present the map only shows the offset and size for each region. The image position provides the actual position of each entry in the image, regardless of the section hierarchy. Add the image position to the map. Signed-off-by: Simon Glass --- tools/binman/bsection.py | 3 ++- tools/binman/entry.py | 8 +++++--- tools/binman/ftest.py | 28 ++++++++++++++-------------- tools/binman/image.py | 3 ++- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/tools/binman/bsection.py b/tools/binman/bsection.py index 70a6ec1776..a0bd1b6d34 100644 --- a/tools/binman/bsection.py +++ b/tools/binman/bsection.py @@ -378,7 +378,8 @@ class Section(object): Args: fd: File to write the map to """ - Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size) + Entry.WriteMapLine(fd, indent, self._name, self._offset, self._size, + self._image_pos) for entry in self._entries.values(): entry.WriteMap(fd, indent + 1) diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 996f03e3a6..6ce5dbdc90 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -349,8 +349,9 @@ class Entry(object): pass @staticmethod - def WriteMapLine(fd, indent, name, offset, size): - print('%s%08x %08x %s' % (' ' * indent, offset, size, name), file=fd) + 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 @@ -359,7 +360,8 @@ class Entry(object): fd: File to write the map to indent: Curent indent level of map (0=none, 1=one level, etc.) """ - self.WriteMapLine(fd, indent, self.name, self.offset, self.size) + self.WriteMapLine(fd, indent, self.name, self.offset, self.size, + self.image_pos) def GetEntries(self): """Return a list of entries contained by this entry diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index d09868588c..ce473dfaff 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1052,25 +1052,25 @@ class TestFunctional(unittest.TestCase): def testMap(self): """Tests outputting a map of the images""" _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True) - self.assertEqual(''' Offset Size Name -00000000 00000028 main-section - 00000000 00000010 section@0 - 00000000 00000004 u-boot - 00000010 00000010 section@1 - 00000000 00000004 u-boot - 00000020 00000004 section@2 - 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(''' Offset Size Name -00000000 00000028 main-section - 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): diff --git a/tools/binman/image.py b/tools/binman/image.py index 4debc73451..68126bc3e6 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -124,5 +124,6 @@ class Image: filename = '%s.map' % self._name fname = tools.GetOutputFilename(filename) with open(fname, 'w') as fd: - print('%8s %8s %s' % ('Offset', 'Size', 'Name'), file=fd) + print('%8s %8s %8s %s' % ('ImagePos', 'Offset', 'Size', 'Name'), + file=fd) self._section.WriteMap(fd, 0) From 7e7c587760ca42f47d9a3b6869f39f7c309f50e1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:50 -0600 Subject: [PATCH 26/28] binman: Rename ReadContents() to ReadBlobContents() This function name is too generic for its purpose and is therefore confusing. It actually only applies to blobs, so rename it to indicate this. Signed-off-by: Simon Glass --- tools/binman/etype/blob.py | 4 ++-- tools/binman/etype/u_boot_dtb_with_ucode.py | 2 +- tools/binman/etype/u_boot_ucode.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/binman/etype/blob.py b/tools/binman/etype/blob.py index 692d8683c3..3f46eecf30 100644 --- a/tools/binman/etype/blob.py +++ b/tools/binman/etype/blob.py @@ -29,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. diff --git a/tools/binman/etype/u_boot_dtb_with_ucode.py b/tools/binman/etype/u_boot_dtb_with_ucode.py index adcb898a7b..285a28dd1e 100644 --- a/tools/binman/etype/u_boot_dtb_with_ucode.py +++ b/tools/binman/etype/u_boot_dtb_with_ucode.py @@ -71,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') diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py index 0f1761afc8..6acf94d8cb 100644 --- a/tools/binman/etype/u_boot_ucode.py +++ b/tools/binman/etype/u_boot_ucode.py @@ -92,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 From 15a587c9cec246ec69d96fce8f146e0c835b3670 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:51 -0600 Subject: [PATCH 27/28] binman: Add a test to catch use of the old 'pos' property This property has been changed to 'offset'. To help downstream users who might still be using 'pos', add a check that this is not used by mistake. Signed-off-by: Simon Glass --- tools/binman/entry.py | 2 ++ tools/binman/ftest.py | 7 +++++++ tools/binman/test/79_uses_pos.dts | 10 ++++++++++ 3 files changed, 19 insertions(+) create mode 100644 tools/binman/test/79_uses_pos.dts diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 6ce5dbdc90..77cfab9c5d 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -143,6 +143,8 @@ class Entry(object): This reads all the fields we recognise from the node, ready for use. """ + if 'pos' in self._node.props: + self.Raise("Please use 'offset' instead of 'pos'") self.offset = fdt_util.GetInt(self._node, 'offset') self.size = fdt_util.GetInt(self._node, 'size') self.align = fdt_util.GetInt(self._node, 'align') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index ce473dfaff..3d5f23558c 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -1361,6 +1361,13 @@ class TestFunctional(unittest.TestCase): 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() diff --git a/tools/binman/test/79_uses_pos.dts b/tools/binman/test/79_uses_pos.dts new file mode 100644 index 0000000000..7638b9b5e0 --- /dev/null +++ b/tools/binman/test/79_uses_pos.dts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + binman { + u-boot { + pos = <10>; + }; + }; +}; From cee02e6ff422fdb8b543a8097b84a9682785f46d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 17 Jul 2018 13:25:52 -0600 Subject: [PATCH 28/28] binman: Adjust _GetPropTree() parameters At present this function takes a filename, but it is better to use an Fdt object so that the caller can control this, perhaps obtainint the device tree from a bytearray. Update the method accordingly and also fix a confusing parameter name. Signed-off-by: Simon Glass --- tools/binman/ftest.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 3d5f23558c..a8456c2615 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -353,21 +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 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) AddNode(subnode, path) tree = {} - dtb = fdt.Fdt(dtb_data) - dtb.Scan() AddNode(dtb.GetRoot(), '') return tree @@ -1092,11 +1090,9 @@ class TestFunctional(unittest.TestCase): """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, ['offset', 'size', - 'image-pos']) - 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({ 'image-pos': 0, 'offset': 0,