]>
Commit | Line | Data |
---|---|---|
11e36cce SG |
1 | # SPDX-License-Identifier: GPL-2.0+ |
2 | # Copyright (c) 2018 Google, Inc | |
3 | # Written by Simon Glass <[email protected]> | |
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 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 | ||
67 | def 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 | ||
90 | def 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 |