xref: /openbmc/u-boot/tools/binman/fmap_util.py (revision dd1033e4)
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 NameToFmap(name):
53    return name.replace('\0', '').replace('-', '_').upper()
54
55def ConvertName(field_names, fields):
56    """Convert a name to something flashrom likes
57
58    Flashrom requires upper case, underscores instead of hyphens. We remove any
59    null characters as well. This updates the 'name' value in fields.
60
61    Args:
62        field_names: List of field names for this struct
63        fields: Dict:
64            key: Field name
65            value: value of that field (string for the ones we support)
66    """
67    name_index = field_names.index('name')
68    fields[name_index] = NameToFmap(fields[name_index])
69
70def DecodeFmap(data):
71    """Decode a flashmap into a header and list of areas
72
73    Args:
74        data: Data block containing the FMAP
75
76    Returns:
77        Tuple:
78            header: FmapHeader object
79            List of FmapArea objects
80    """
81    fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
82    ConvertName(FMAP_HEADER_NAMES, fields)
83    header = FmapHeader(*fields)
84    areas = []
85    data = data[FMAP_HEADER_LEN:]
86    for area in range(header.nareas):
87        fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
88        ConvertName(FMAP_AREA_NAMES, fields)
89        areas.append(FmapArea(*fields))
90        data = data[FMAP_AREA_LEN:]
91    return header, areas
92
93def EncodeFmap(image_size, name, areas):
94    """Create a new FMAP from a list of areas
95
96    Args:
97        image_size: Size of image, to put in the header
98        name: Name of image, to put in the header
99        areas: List of FmapArea objects
100
101    Returns:
102        String containing the FMAP created
103    """
104    def _FormatBlob(fmt, names, obj):
105        params = [getattr(obj, name) for name in names]
106        ConvertName(names, params)
107        return struct.pack(fmt, *params)
108
109    values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas))
110    blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
111    for area in areas:
112        blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
113    return blob
114