binman: Add an image header

It is useful to be able to quickly locate the FDT map in the image. An
easy way to do this is with a pointer at the start or end of the image.

Add an 'image header' entry, which places a magic number followed by a
pointer to the FDT map. This can be located at the start or end of the
image, or at a chosen location.

As part of this, update GetSiblingImagePos() to detect missing siblings.

Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
Simon Glass 2019-07-08 14:25:28 -06:00
parent 086cec9f98
commit cf2289435c
10 changed files with 242 additions and 3 deletions

View File

@ -640,7 +640,9 @@ of each entry.
Alternatively, an FDT map entry can be used to add a special FDT containing
just the information about the image. This is preceded by a magic string so can
be located anywhere in the image.
be located anywhere in the image. An image header (typically at the start or end
of the image) can be used to point to the FDT map. See fdtmap and image-header
entries for more information.
Compression
@ -817,7 +819,6 @@ Some ideas:
- Add an option to decode an image into the constituent binaries
- Support building an image for a board (-b) more completely, with a
configurable build directory
- Support putting the FDT in an image with a suitable magic number
- Support listing files in images
- Support logging of binman's operations, with different levels of verbosity
- Support updating binaries in an image (with no size change / repacking)

View File

@ -331,6 +331,25 @@ README.chromium for how to obtain the required keys and tools.
Entry: image-header: An entry which contains a pointer to the FDT map
---------------------------------------------------------------------
Properties / Entry arguments:
location: Location of header ("start" or "end" of image). This is
optional. If omitted then the entry must have an offset property.
This adds an 8-byte entry to the start or end of the image, pointing to the
location of the FDT map. The format is a magic number followed by an offset
from the start or end of the image, in twos-compliment format.
This entry must be in the top-level part of the image.
NOTE: If the location is at the start/end, you will probably need to specify
sort-by-offset for the image, unless you actually put the image header
first/last in the entry list.
Entry: intel-cmc: Entry containing an Intel Chipset Micro Code (CMC) file
-------------------------------------------------------------------------

View File

@ -561,3 +561,14 @@ features to produce new behaviours.
else False
"""
return name in self.section.GetEntries()
def GetSiblingImagePos(self, name):
"""Return the image position of the given sibling
Returns:
Image position of sibling, or None if the sibling has no position,
or False if there is no such sibling
"""
if not self.HasSibling(name):
return False
return self.section.GetEntries()[name].image_pos

View File

@ -0,0 +1,76 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
"""Entry-type module for an image header which points to the FDT map
This creates an 8-byte entry with a magic number and the offset of the FDT map
(which is another entry in the image), relative to the start or end of the
image.
"""
import struct
from entry import Entry
import fdt_util
IMAGE_HEADER_MAGIC = b'BinM'
class Entry_image_header(Entry):
"""An entry which contains a pointer to the FDT map
Properties / Entry arguments:
location: Location of header ("start" or "end" of image). This is
optional. If omitted then the entry must have an offset property.
This adds an 8-byte entry to the start or end of the image, pointing to the
location of the FDT map. The format is a magic number followed by an offset
from the start or end of the image, in twos-compliment format.
This entry must be in the top-level part of the image.
NOTE: If the location is at the start/end, you will probably need to specify
sort-by-offset for the image, unless you actually put the image header
first/last in the entry list.
"""
def __init__(self, section, etype, node):
Entry.__init__(self, section, etype, node)
self.location = fdt_util.GetString(self._node, 'location')
def _GetHeader(self):
image_pos = self.GetSiblingImagePos('fdtmap')
if image_pos == False:
self.Raise("'image_header' section must have an 'fdtmap' sibling")
elif image_pos is None:
# This will be available when called from ProcessContents(), but not
# when called from ObtainContents()
offset = 0xffffffff
else:
image_size = self.section.GetImageSize() or 0
base = (0 if self.location != 'end' else image_size)
offset = (image_pos - base) & 0xffffffff
data = IMAGE_HEADER_MAGIC + struct.pack('<I', offset)
return data
def ObtainContents(self):
"""Obtain a placeholder for the header contents"""
self.SetContents(self._GetHeader())
return True
def Pack(self, offset):
"""Special pack method to set the offset to start/end of image"""
if not self.offset:
if self.location not in ['start', 'end']:
self.Raise("Invalid location '%s', expected 'start' or 'end'" %
self.location)
image_size = self.section.GetImageSize() or 0
self.offset = (0 if self.location != 'end' else image_size - 8)
return Entry.Pack(self, offset)
def ProcessContents(self):
"""Write an updated version of the FDT map to this entry
This is necessary since image_pos is not available when ObtainContents()
is called, since by then the entries have not been packed in the image.
"""
self.SetContents(self._GetHeader())

View File

@ -2076,7 +2076,7 @@ class TestFunctional(unittest.TestCase):
# Mangle the section name, which should cause a mismatch between the
# correct FDT path and the one expected by the section
image = control.images['image']
image._section._node.path += '-suffix'
image._node.path += '-suffix'
entries = image.GetEntries()
fdtmap = entries['fdtmap']
with self.assertRaises(ValueError) as e:
@ -2084,6 +2084,51 @@ class TestFunctional(unittest.TestCase):
self.assertIn("Cannot locate node for path '/binman-suffix'",
str(e.exception))
def testFdtmapHeader(self):
"""Test an FDT map and image header can be inserted in the image"""
data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
fdtmap_pos = len(U_BOOT_DATA)
fdtmap_data = data[fdtmap_pos:]
fdt_data = fdtmap_data[16:]
dtb = fdt.Fdt.FromData(fdt_data)
fdt_size = dtb.GetFdtObj().totalsize()
hdr_data = data[-8:]
self.assertEqual('BinM', hdr_data[:4])
offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
def testFdtmapHeaderStart(self):
"""Test an image header can be inserted at the image start"""
data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
fdtmap_pos = 0x100 + len(U_BOOT_DATA)
hdr_data = data[:8]
self.assertEqual('BinM', hdr_data[:4])
offset = struct.unpack('<I', hdr_data[4:])[0]
self.assertEqual(fdtmap_pos, offset)
def testFdtmapHeaderPos(self):
"""Test an image header can be inserted at a chosen position"""
data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
fdtmap_pos = 0x100 + len(U_BOOT_DATA)
hdr_data = data[0x80:0x88]
self.assertEqual('BinM', hdr_data[:4])
offset = struct.unpack('<I', hdr_data[4:])[0]
self.assertEqual(fdtmap_pos, offset)
def testHeaderMissingFdtmap(self):
"""Test an image header requires an fdtmap"""
with self.assertRaises(ValueError) as e:
self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
self.assertIn("'image_header' section must have an 'fdtmap' sibling",
str(e.exception))
def testHeaderNoLocation(self):
"""Test an image header with a no specified location is detected"""
with self.assertRaises(ValueError) as e:
self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
self.assertIn("Invalid location 'None', expected 'start' or 'end'",
str(e.exception))
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,17 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <0x400>;
u-boot {
};
fdtmap {
};
image-header {
location = "end";
};
};
};

View File

@ -0,0 +1,19 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <0x400>;
sort-by-offset;
u-boot {
offset = <0x100>;
};
fdtmap {
};
image-header {
location = "start";
};
};
};

View File

@ -0,0 +1,19 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <0x400>;
sort-by-offset;
u-boot {
offset = <0x100>;
};
fdtmap {
};
image-header {
offset = <0x80>;
};
};
};

View File

@ -0,0 +1,16 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
sort-by-offset;
u-boot {
};
image-header {
offset = <0x80>;
location = "start";
};
};
};

View File

@ -0,0 +1,16 @@
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
sort-by-offset;
u-boot {
};
fdtmap {
};
image-header {
};
};
};