diff options
author | Simon Glass <sjg@chromium.org> | 2018-07-17 13:25:38 -0600 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2018-08-01 16:30:48 -0600 |
commit | 11e36ccea174043229319263f9d0b5b7f7cca654 (patch) | |
tree | ae97c27416a1f44cf45da09364480818784e4eea | |
parent | 5a5da7ce153b19bc3106e0bdb625b2e211852914 (diff) | |
download | u-boot-11e36ccea174043229319263f9d0b5b7f7cca654.tar.gz |
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 <sjg@chromium.org>
-rw-r--r-- | tools/binman/README.entries | 20 | ||||
-rw-r--r-- | tools/binman/entry.py | 9 | ||||
-rw-r--r-- | tools/binman/etype/fmap.py | 61 | ||||
-rw-r--r-- | tools/binman/etype/section.py | 7 | ||||
-rw-r--r-- | tools/binman/fmap_util.py | 109 | ||||
-rw-r--r-- | tools/binman/ftest.py | 32 | ||||
-rw-r--r-- | tools/binman/test/67_fmap.dts | 29 |
7 files changed, 265 insertions, 2 deletions
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 <sjg@chromium.org> +# +# Entry-type module for a Flash map, as used by the flashrom SPI flash tool +# + +from entry import Entry +import fmap_util + + +class Entry_fmap(Entry): + """An entry which contains an Fmap section + + Properties / Entry arguments: + None + + FMAP is a simple format used by flashrom, an open-source utility for + reading and writing the SPI flash, typically on x86 CPUs. The format + provides flashrom with a list of areas, so it knows what it in the flash. + It can then read or write just a single area, instead of the whole flash. + + The format is defined by the flashrom project, in the file lib/fmap.h - + see www.flashrom.org/Flashrom for more information. + + When used, this entry will be populated with an FMAP which reflects the + entries in the current image. Note that any hierarchy is squashed, since + FMAP does not support this. + """ + def __init__(self, section, etype, node): + Entry.__init__(self, section, etype, node) + + def _GetFmap(self): + """Build an FMAP from the entries in the current image + + Returns: + FMAP binary data + """ + def _AddEntries(areas, entry): + entries = entry.GetEntries() + if entries: + for subentry in entries.values(): + _AddEntries(areas, subentry) + else: + areas.append(fmap_util.FmapArea(entry.image_pos or 0, + entry.size or 0, entry.name, 0)) + + entries = self.section.GetEntries() + areas = [] + for entry in entries.values(): + _AddEntries(areas, entry) + return fmap_util.EncodeFmap(self.section.GetSize() or 0, self.name, + areas) + + def ObtainContents(self): + """Obtain a placeholder for the fmap contents""" + self.SetContents(self._GetFmap()) + return True + + def ProcessContents(self): + self.SetContents(self._GetFmap()) 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 <sjg@chromium.org> +# +# Support for flashrom's FMAP format. This supports a header followed by a +# number of 'areas', describing regions of a firmware storage device, +# generally SPI flash. + +import collections +import struct + +# constants imported from lib/fmap.h +FMAP_SIGNATURE = '__FMAP__' +FMAP_VER_MAJOR = 1 +FMAP_VER_MINOR = 0 +FMAP_STRLEN = 32 + +FMAP_AREA_STATIC = 1 << 0 +FMAP_AREA_COMPRESSED = 1 << 1 +FMAP_AREA_RO = 1 << 2 + +FMAP_HEADER_LEN = 56 +FMAP_AREA_LEN = 42 + +FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN) +FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN) + +FMAP_HEADER_NAMES = ( + 'signature', + 'ver_major', + 'ver_minor', + 'base', + 'image_size', + 'name', + 'nareas', +) + +FMAP_AREA_NAMES = ( + 'offset', + 'size', + 'name', + 'flags', +) + +# These are the two data structures supported by flashrom, a header (which +# appears once at the start) and an area (which is repeated until the end of +# the list of areas) +FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES) +FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES) + + +def ConvertName(field_names, fields): + """Convert a name to something flashrom likes + + Flashrom requires upper case, underscores instead of hyphens. We remove any + null characters as well. This updates the 'name' value in fields. + + Args: + field_names: List of field names for this struct + fields: Dict: + key: Field name + value: value of that field (string for the ones we support) + """ + name_index = field_names.index('name') + fields[name_index] = fields[name_index].replace('\0', '').replace('-', '_').upper() + +def DecodeFmap(data): + """Decode a flashmap into a header and list of areas + + Args: + data: Data block containing the FMAP + + Returns: + Tuple: + header: FmapHeader object + List of FmapArea objects + """ + fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN])) + ConvertName(FMAP_HEADER_NAMES, fields) + header = FmapHeader(*fields) + areas = [] + data = data[FMAP_HEADER_LEN:] + for area in range(header.nareas): + fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN])) + ConvertName(FMAP_AREA_NAMES, fields) + areas.append(FmapArea(*fields)) + data = data[FMAP_AREA_LEN:] + return header, areas + +def EncodeFmap(image_size, name, areas): + """Create a new FMAP from a list of areas + + Args: + image_size: Size of image, to put in the header + name: Name of image, to put in the header + areas: List of FmapArea objects + + Returns: + String containing the FMAP created + """ + def _FormatBlob(fmt, names, obj): + params = [getattr(obj, name) for name in names] + return struct.pack(fmt, *params) + + values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas)) + blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values) + for area in areas: + blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area) + return blob diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 9c01805c72..bd4de4e287 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 fmap_util import test_util import tools import tout @@ -1192,6 +1193,37 @@ class TestFunctional(unittest.TestCase): self.assertIn('Documentation is missing for modules: u_boot', str(e.exception)) + def testFmap(self): + """Basic test of generation of a flashrom fmap""" + data = self._DoReadFile('67_fmap.dts') + fhdr, fentries = fmap_util.DecodeFmap(data[32:]) + expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + self.assertEqual(expected, data[:32]) + self.assertEqual('__FMAP__', fhdr.signature) + self.assertEqual(1, fhdr.ver_major) + self.assertEqual(0, fhdr.ver_minor) + self.assertEqual(0, fhdr.base) + self.assertEqual(16 + 16 + + fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size) + self.assertEqual('FMAP', fhdr.name) + self.assertEqual(3, fhdr.nareas) + for fentry in fentries: + self.assertEqual(0, fentry.flags) + + self.assertEqual(0, fentries[0].offset) + self.assertEqual(4, fentries[0].size) + self.assertEqual('RO_U_BOOT', fentries[0].name) + + self.assertEqual(16, fentries[1].offset) + self.assertEqual(4, fentries[1].size) + self.assertEqual('RW_U_BOOT', fentries[1].name) + + self.assertEqual(32, fentries[2].offset) + self.assertEqual(fmap_util.FMAP_HEADER_LEN + + fmap_util.FMAP_AREA_LEN * 3, fentries[2].size) + self.assertEqual('FMAP', fentries[2].name) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/67_fmap.dts b/tools/binman/test/67_fmap.dts new file mode 100644 index 0000000000..9c0e293ac8 --- /dev/null +++ b/tools/binman/test/67_fmap.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x10>; + pad-byte = <0x21>; + + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x10>; + pad-byte = <0x61>; + + u-boot { + }; + }; + fmap { + }; + }; +}; |