Merge https://gitlab.denx.de/u-boot/custodians/u-boot-fsl-qoriq
[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 import sys
12
13 from patman import tools
14
15 # constants imported from lib/fmap.h
16 FMAP_SIGNATURE = b'__FMAP__'
17 FMAP_VER_MAJOR = 1
18 FMAP_VER_MINOR = 0
19 FMAP_STRLEN = 32
20
21 FMAP_AREA_STATIC = 1 << 0
22 FMAP_AREA_COMPRESSED = 1 << 1
23 FMAP_AREA_RO = 1 << 2
24
25 FMAP_HEADER_LEN = 56
26 FMAP_AREA_LEN = 42
27
28 FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
29 FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
30
31 FMAP_HEADER_NAMES = (
32     'signature',
33     'ver_major',
34     'ver_minor',
35     'base',
36     'image_size',
37     'name',
38     'nareas',
39 )
40
41 FMAP_AREA_NAMES = (
42     'offset',
43     'size',
44     'name',
45     'flags',
46 )
47
48 # These are the two data structures supported by flashrom, a header (which
49 # appears once at the start) and an area (which is repeated until the end of
50 # the list of areas)
51 FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
52 FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
53
54
55 def NameToFmap(name):
56     if type(name) == bytes and sys.version_info[0] >= 3:
57         name = name.decode('utf-8')  # pragma: no cover (for Python 2)
58     return name.replace('\0', '').replace('-', '_').upper()
59
60 def ConvertName(field_names, fields):
61     """Convert a name to something flashrom likes
62
63     Flashrom requires upper case, underscores instead of hyphens. We remove any
64     null characters as well. This updates the 'name' value in fields.
65
66     Args:
67         field_names: List of field names for this struct
68         fields: Dict:
69             key: Field name
70             value: value of that field (string for the ones we support)
71     """
72     name_index = field_names.index('name')
73     fields[name_index] = tools.ToBytes(NameToFmap(fields[name_index]))
74
75 def DecodeFmap(data):
76     """Decode a flashmap into a header and list of areas
77
78     Args:
79         data: Data block containing the FMAP
80
81     Returns:
82         Tuple:
83             header: FmapHeader object
84             List of FmapArea objects
85     """
86     fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
87     ConvertName(FMAP_HEADER_NAMES, fields)
88     header = FmapHeader(*fields)
89     areas = []
90     data = data[FMAP_HEADER_LEN:]
91     for area in range(header.nareas):
92         fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
93         ConvertName(FMAP_AREA_NAMES, fields)
94         areas.append(FmapArea(*fields))
95         data = data[FMAP_AREA_LEN:]
96     return header, areas
97
98 def EncodeFmap(image_size, name, areas):
99     """Create a new FMAP from a list of areas
100
101     Args:
102         image_size: Size of image, to put in the header
103         name: Name of image, to put in the header
104         areas: List of FmapArea objects
105
106     Returns:
107         String containing the FMAP created
108     """
109     def _FormatBlob(fmt, names, obj):
110         params = [getattr(obj, name) for name in names]
111         ConvertName(names, params)
112         return struct.pack(fmt, *params)
113
114     values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size,
115                         tools.FromUnicode(name), len(areas))
116     blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
117     for area in areas:
118         blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
119     return blob