blob: be3cbee87bd151e32eb3a180bd4b6584a45f74fc [file] [log] [blame]
Simon Glass704784b2018-07-17 13:25:38 -06001# 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
Simon Glassc64aea52018-09-14 04:57:34 -060052def NameToFmap(name):
53 return name.replace('\0', '').replace('-', '_').upper()
54
Simon Glass704784b2018-07-17 13:25:38 -060055def 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')
Simon Glassc64aea52018-09-14 04:57:34 -060068 fields[name_index] = NameToFmap(fields[name_index])
Simon Glass704784b2018-07-17 13:25:38 -060069
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]
Simon Glassc64aea52018-09-14 04:57:34 -0600106 ConvertName(names, params)
Simon Glass704784b2018-07-17 13:25:38 -0600107 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