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