xref: /openbmc/u-boot/tools/binman/fmap_util.py (revision afaea1f5)
1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2018 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5# Support for flashrom's FMAP format. This supports a header followed by a
6# number of 'areas', describing regions of a firmware storage device,
7# generally SPI flash.
8
9import collections
10import struct
11
12# constants imported from lib/fmap.h
13FMAP_SIGNATURE = '__FMAP__'
14FMAP_VER_MAJOR = 1
15FMAP_VER_MINOR = 0
16FMAP_STRLEN = 32
17
18FMAP_AREA_STATIC = 1 << 0
19FMAP_AREA_COMPRESSED = 1 << 1
20FMAP_AREA_RO = 1 << 2
21
22FMAP_HEADER_LEN = 56
23FMAP_AREA_LEN = 42
24
25FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
26FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
27
28FMAP_HEADER_NAMES = (
29    'signature',
30    'ver_major',
31    'ver_minor',
32    'base',
33    'image_size',
34    'name',
35    'nareas',
36)
37
38FMAP_AREA_NAMES = (
39    'offset',
40    'size',
41    'name',
42    'flags',
43)
44
45# These are the two data structures supported by flashrom, a header (which
46# appears once at the start) and an area (which is repeated until the end of
47# the list of areas)
48FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
49FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
50
51
52def ConvertName(field_names, fields):
53    """Convert a name to something flashrom likes
54
55    Flashrom requires upper case, underscores instead of hyphens. We remove any
56    null characters as well. This updates the 'name' value in fields.
57
58    Args:
59        field_names: List of field names for this struct
60        fields: Dict:
61            key: Field name
62            value: value of that field (string for the ones we support)
63    """
64    name_index = field_names.index('name')
65    fields[name_index] = fields[name_index].replace('\0', '').replace('-', '_').upper()
66
67def DecodeFmap(data):
68    """Decode a flashmap into a header and list of areas
69
70    Args:
71        data: Data block containing the FMAP
72
73    Returns:
74        Tuple:
75            header: FmapHeader object
76            List of FmapArea objects
77    """
78    fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
79    ConvertName(FMAP_HEADER_NAMES, fields)
80    header = FmapHeader(*fields)
81    areas = []
82    data = data[FMAP_HEADER_LEN:]
83    for area in range(header.nareas):
84        fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
85        ConvertName(FMAP_AREA_NAMES, fields)
86        areas.append(FmapArea(*fields))
87        data = data[FMAP_AREA_LEN:]
88    return header, areas
89
90def EncodeFmap(image_size, name, areas):
91    """Create a new FMAP from a list of areas
92
93    Args:
94        image_size: Size of image, to put in the header
95        name: Name of image, to put in the header
96        areas: List of FmapArea objects
97
98    Returns:
99        String containing the FMAP created
100    """
101    def _FormatBlob(fmt, names, obj):
102        params = [getattr(obj, name) for name in names]
103        return struct.pack(fmt, *params)
104
105    values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))
106    blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
107    for area in areas:
108        blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
109    return blob
110