Merge https://gitlab.denx.de/u-boot/custodians/u-boot-marvell
[oweals/u-boot.git] / tools / binman / fmap_util.py
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
9 import collections
10 import struct
11
12 # constants imported from lib/fmap.h
13 FMAP_SIGNATURE = '__FMAP__'
14 FMAP_VER_MAJOR = 1
15 FMAP_VER_MINOR = 0
16 FMAP_STRLEN = 32
17
18 FMAP_AREA_STATIC = 1 << 0
19 FMAP_AREA_COMPRESSED = 1 << 1
20 FMAP_AREA_RO = 1 << 2
21
22 FMAP_HEADER_LEN = 56
23 FMAP_AREA_LEN = 42
24
25 FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
26 FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
27
28 FMAP_HEADER_NAMES = (
29     'signature',
30     'ver_major',
31     'ver_minor',
32     'base',
33     'image_size',
34     'name',
35     'nareas',
36 )
37
38 FMAP_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)
48 FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
49 FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
50
51
52 def NameToFmap(name):
53     return name.replace('\0', '').replace('-', '_').upper()
54
55 def 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
70 def 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
93 def 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