blob: 942747aadb0bfe286bab98a14424a388b5084a43 [file] [log] [blame]
Hung-Te Linc83105f2011-03-16 18:38:04 +08001#!/usr/bin/env python
Hung-Te Lin1990b742017-08-09 17:34:57 +08002# Copyright 2010 The Chromium OS Authors. All rights reserved.
Hung-Te Linc83105f2011-03-16 18:38:04 +08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Drew Davenportc4e868e2017-05-26 09:35:03 -06005"""
6This module provides basic encode and decode functionality to the flashrom
7memory map (FMAP) structure.
8
9WARNING: This module has been copied from third_party/flashmap/fmap.py (see
10crbug/726356 for background). Please make modifications to this file there
11first and then copy changes to this file.
Hung-Te Linc83105f2011-03-16 18:38:04 +080012
13Usage:
14 (decode)
15 obj = fmap_decode(blob)
16 print obj
17
18 (encode)
19 blob = fmap_encode(obj)
20 open('output.bin', 'w').write(blob)
21
22 The object returned by fmap_decode is a dictionary with names defined in
23 fmap.h. A special property 'FLAGS' is provided as a readable and read-only
24 tuple of decoded area flags.
25"""
26
Tammo Spalink01e11722012-07-24 10:17:54 -070027
Hung-Te Line89b6302016-02-04 12:23:02 +080028import logging
Hung-Te Linc83105f2011-03-16 18:38:04 +080029import struct
Hung-Te Linc052ff32013-06-20 10:48:17 +080030import sys
Tammo Spalink01e11722012-07-24 10:17:54 -070031
Hung-Te Linc83105f2011-03-16 18:38:04 +080032
33# constants imported from lib/fmap.h
Hung-Te Lin56b18402015-01-16 14:52:30 +080034FMAP_SIGNATURE = '__FMAP__'
Hung-Te Linc83105f2011-03-16 18:38:04 +080035FMAP_VER_MAJOR = 1
36FMAP_VER_MINOR_MIN = 0
37FMAP_VER_MINOR_MAX = 1
38FMAP_STRLEN = 32
Hung-Te Line89b6302016-02-04 12:23:02 +080039FMAP_SEARCH_STRIDE = 4
Hung-Te Linc83105f2011-03-16 18:38:04 +080040
41FMAP_FLAGS = {
42 'FMAP_AREA_STATIC': 1 << 0,
43 'FMAP_AREA_COMPRESSED': 1 << 1,
44}
45
46FMAP_HEADER_NAMES = (
47 'signature',
48 'ver_major',
49 'ver_minor',
50 'base',
51 'size',
52 'name',
53 'nareas',
54)
55
56FMAP_AREA_NAMES = (
57 'offset',
58 'size',
59 'name',
60 'flags',
61)
62
Tammo Spalink01e11722012-07-24 10:17:54 -070063
Hung-Te Linc83105f2011-03-16 18:38:04 +080064# format string
Hung-Te Lin56b18402015-01-16 14:52:30 +080065FMAP_HEADER_FORMAT = '<8sBBQI%dsH' % (FMAP_STRLEN)
66FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
Hung-Te Linc83105f2011-03-16 18:38:04 +080067
68
69def _fmap_decode_header(blob, offset):
70 """ (internal) Decodes a FMAP header from blob by offset"""
71 header = {}
72 for (name, value) in zip(FMAP_HEADER_NAMES,
73 struct.unpack_from(FMAP_HEADER_FORMAT,
74 blob,
75 offset)):
76 header[name] = value
77
78 if header['signature'] != FMAP_SIGNATURE:
79 raise struct.error('Invalid signature')
80 if (header['ver_major'] != FMAP_VER_MAJOR or
81 header['ver_minor'] < FMAP_VER_MINOR_MIN or
82 header['ver_minor'] > FMAP_VER_MINOR_MAX):
83 raise struct.error('Incompatible version')
84
85 # convert null-terminated names
86 header['name'] = header['name'].strip(chr(0))
87 return (header, struct.calcsize(FMAP_HEADER_FORMAT))
88
89
90def _fmap_decode_area(blob, offset):
91 """ (internal) Decodes a FMAP area record from blob by offset """
92 area = {}
93 for (name, value) in zip(FMAP_AREA_NAMES,
94 struct.unpack_from(FMAP_AREA_FORMAT, blob, offset)):
95 area[name] = value
96 # convert null-terminated names
97 area['name'] = area['name'].strip(chr(0))
98 # add a (readonly) readable FLAGS
99 area['FLAGS'] = _fmap_decode_area_flags(area['flags'])
100 return (area, struct.calcsize(FMAP_AREA_FORMAT))
101
102
103def _fmap_decode_area_flags(area_flags):
104 """ (internal) Decodes a FMAP flags property """
105 return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]])
106
107
Hung-Te Line89b6302016-02-04 12:23:02 +0800108def _fmap_check_name(fmap, name):
109 """Checks if the FMAP structure has correct name.
110
111 Args:
112 fmap: A decoded FMAP structure.
113 name: A string to specify expected FMAP name.
114
115 Raises:
116 struct.error if the name does not match.
117 """
118 if fmap['name'] != name:
119 raise struct.error('Incorrect FMAP (found: "%s", expected: "%s")' %
120 (fmap['name'], name))
121
122
123def _fmap_search_header(blob, fmap_name=None):
124 """Searches FMAP headers in given blob.
125
126 Uses same logic from vboot_reference/host/lib/fmap.c.
127
128 Args:
129 blob: A string containing FMAP data.
130 fmap_name: A string to specify target FMAP name.
131
132 Returns:
133 A tuple of (fmap, size, offset).
134 """
135 lim = len(blob) - struct.calcsize(FMAP_HEADER_FORMAT)
136 align = FMAP_SEARCH_STRIDE
137
138 # Search large alignments before small ones to find "right" FMAP.
139 while align <= lim:
140 align *= 2
141
142 while align >= FMAP_SEARCH_STRIDE:
143 for offset in xrange(align, lim + 1, align * 2):
144 if not blob.startswith(FMAP_SIGNATURE, offset):
145 continue
146 try:
147 (fmap, size) = _fmap_decode_header(blob, offset)
148 if fmap_name is not None:
149 _fmap_check_name(fmap, fmap_name)
150 return (fmap, size, offset)
151 except struct.error as e:
152 # Search for next FMAP candidate.
153 logging.debug('Continue searching FMAP due to exception %r', e)
Hung-Te Line89b6302016-02-04 12:23:02 +0800154 align /= 2
155 raise struct.error('No valid FMAP signatures.')
156
157
Mary Ruthven56761952016-01-14 13:07:35 -0800158def fmap_decode(blob, offset=None, fmap_name=None):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800159 """ Decodes a blob to FMAP dictionary object.
160
161 Arguments:
162 blob: a binary data containing FMAP structure.
163 offset: starting offset of FMAP. When omitted, fmap_decode will search in
164 the blob.
165 """
166 fmap = {}
Hung-Te Line89b6302016-02-04 12:23:02 +0800167
Hung-Te Lin8a346442013-06-20 10:49:22 +0800168 if offset is None:
Hung-Te Line89b6302016-02-04 12:23:02 +0800169 (fmap, size, offset) = _fmap_search_header(blob, fmap_name)
Hung-Te Lin8a346442013-06-20 10:49:22 +0800170 else:
171 (fmap, size) = _fmap_decode_header(blob, offset)
Hung-Te Line89b6302016-02-04 12:23:02 +0800172 if fmap_name is not None:
173 _fmap_check_name(fmap, fmap_name)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800174 fmap['areas'] = []
175 offset = offset + size
Tammo Spalink01e11722012-07-24 10:17:54 -0700176 for _ in range(fmap['nareas']):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800177 (area, size) = _fmap_decode_area(blob, offset)
178 offset = offset + size
179 fmap['areas'].append(area)
180 return fmap
181
182
183def _fmap_encode_header(obj):
184 """ (internal) Encodes a FMAP header """
185 values = [obj[name] for name in FMAP_HEADER_NAMES]
186 return struct.pack(FMAP_HEADER_FORMAT, *values)
187
188
189def _fmap_encode_area(obj):
190 """ (internal) Encodes a FMAP area entry """
191 values = [obj[name] for name in FMAP_AREA_NAMES]
192 return struct.pack(FMAP_AREA_FORMAT, *values)
193
194
195def fmap_encode(obj):
196 """ Encodes a FMAP dictionary object to blob.
197
198 Arguments
199 obj: a FMAP dictionary object.
200 """
201 # fix up values
202 obj['nareas'] = len(obj['areas'])
203 # TODO(hungte) re-assign signature / version?
204 blob = _fmap_encode_header(obj)
205 for area in obj['areas']:
206 blob = blob + _fmap_encode_area(area)
207 return blob
208
209
Tammo Spalink01e11722012-07-24 10:17:54 -0700210def main():
Drew Davenportc4e868e2017-05-26 09:35:03 -0600211 """Decode FMAP from supplied file and print."""
212 if len(sys.argv) < 2:
213 print 'Usage: fmap.py <file>'
214 sys.exit(1)
215
216 filename = sys.argv[1]
Hung-Te Lin56b18402015-01-16 14:52:30 +0800217 print 'Decoding FMAP from: %s' % filename
Hung-Te Linc052ff32013-06-20 10:48:17 +0800218 blob = open(filename).read()
Hung-Te Linc83105f2011-03-16 18:38:04 +0800219 obj = fmap_decode(blob)
220 print obj
Tammo Spalink01e11722012-07-24 10:17:54 -0700221
222
223if __name__ == '__main__':
224 main()