blob: 6dfd1f3ade4ee01079eff666bd72226422eb3a63 [file] [log] [blame]
Hung-Te Linc83105f2011-03-16 18:38:04 +08001#!/usr/bin/env python
2# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Hung-Te Lin7b596ff2015-01-16 20:19:15 +08005"""Basic encode and decode function for flashrom memory map (FMAP) structure.
Hung-Te Linc83105f2011-03-16 18:38:04 +08006
7Usage:
8 (decode)
9 obj = fmap_decode(blob)
10 print obj
11
12 (encode)
13 blob = fmap_encode(obj)
14 open('output.bin', 'w').write(blob)
15
16 The object returned by fmap_decode is a dictionary with names defined in
17 fmap.h. A special property 'FLAGS' is provided as a readable and read-only
18 tuple of decoded area flags.
19"""
20
Tammo Spalink01e11722012-07-24 10:17:54 -070021
Hung-Te Line89b6302016-02-04 12:23:02 +080022import logging
Hung-Te Linc83105f2011-03-16 18:38:04 +080023import struct
Hung-Te Linc052ff32013-06-20 10:48:17 +080024import sys
Tammo Spalink01e11722012-07-24 10:17:54 -070025
Hung-Te Linc83105f2011-03-16 18:38:04 +080026
27# constants imported from lib/fmap.h
Hung-Te Lin56b18402015-01-16 14:52:30 +080028FMAP_SIGNATURE = '__FMAP__'
Hung-Te Linc83105f2011-03-16 18:38:04 +080029FMAP_VER_MAJOR = 1
30FMAP_VER_MINOR_MIN = 0
31FMAP_VER_MINOR_MAX = 1
32FMAP_STRLEN = 32
Hung-Te Line89b6302016-02-04 12:23:02 +080033FMAP_SEARCH_STRIDE = 4
Hung-Te Linc83105f2011-03-16 18:38:04 +080034
35FMAP_FLAGS = {
36 'FMAP_AREA_STATIC': 1 << 0,
37 'FMAP_AREA_COMPRESSED': 1 << 1,
38}
39
40FMAP_HEADER_NAMES = (
41 'signature',
42 'ver_major',
43 'ver_minor',
44 'base',
45 'size',
46 'name',
47 'nareas',
48)
49
50FMAP_AREA_NAMES = (
51 'offset',
52 'size',
53 'name',
54 'flags',
55)
56
Tammo Spalink01e11722012-07-24 10:17:54 -070057
Hung-Te Linc83105f2011-03-16 18:38:04 +080058# format string
Hung-Te Lin56b18402015-01-16 14:52:30 +080059FMAP_HEADER_FORMAT = '<8sBBQI%dsH' % (FMAP_STRLEN)
60FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
Hung-Te Linc83105f2011-03-16 18:38:04 +080061
62
63def _fmap_decode_header(blob, offset):
64 """ (internal) Decodes a FMAP header from blob by offset"""
65 header = {}
66 for (name, value) in zip(FMAP_HEADER_NAMES,
67 struct.unpack_from(FMAP_HEADER_FORMAT,
68 blob,
69 offset)):
70 header[name] = value
71
72 if header['signature'] != FMAP_SIGNATURE:
73 raise struct.error('Invalid signature')
74 if (header['ver_major'] != FMAP_VER_MAJOR or
75 header['ver_minor'] < FMAP_VER_MINOR_MIN or
76 header['ver_minor'] > FMAP_VER_MINOR_MAX):
77 raise struct.error('Incompatible version')
78
79 # convert null-terminated names
80 header['name'] = header['name'].strip(chr(0))
81 return (header, struct.calcsize(FMAP_HEADER_FORMAT))
82
83
84def _fmap_decode_area(blob, offset):
85 """ (internal) Decodes a FMAP area record from blob by offset """
86 area = {}
87 for (name, value) in zip(FMAP_AREA_NAMES,
88 struct.unpack_from(FMAP_AREA_FORMAT, blob, offset)):
89 area[name] = value
90 # convert null-terminated names
91 area['name'] = area['name'].strip(chr(0))
92 # add a (readonly) readable FLAGS
93 area['FLAGS'] = _fmap_decode_area_flags(area['flags'])
94 return (area, struct.calcsize(FMAP_AREA_FORMAT))
95
96
97def _fmap_decode_area_flags(area_flags):
98 """ (internal) Decodes a FMAP flags property """
99 return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]])
100
101
Hung-Te Line89b6302016-02-04 12:23:02 +0800102def _fmap_check_name(fmap, name):
103 """Checks if the FMAP structure has correct name.
104
105 Args:
106 fmap: A decoded FMAP structure.
107 name: A string to specify expected FMAP name.
108
109 Raises:
110 struct.error if the name does not match.
111 """
112 if fmap['name'] != name:
113 raise struct.error('Incorrect FMAP (found: "%s", expected: "%s")' %
114 (fmap['name'], name))
115
116
117def _fmap_search_header(blob, fmap_name=None):
118 """Searches FMAP headers in given blob.
119
120 Uses same logic from vboot_reference/host/lib/fmap.c.
121
122 Args:
123 blob: A string containing FMAP data.
124 fmap_name: A string to specify target FMAP name.
125
126 Returns:
127 A tuple of (fmap, size, offset).
128 """
129 lim = len(blob) - struct.calcsize(FMAP_HEADER_FORMAT)
130 align = FMAP_SEARCH_STRIDE
131
132 # Search large alignments before small ones to find "right" FMAP.
133 while align <= lim:
134 align *= 2
135
136 while align >= FMAP_SEARCH_STRIDE:
137 for offset in xrange(align, lim + 1, align * 2):
138 if not blob.startswith(FMAP_SIGNATURE, offset):
139 continue
140 try:
141 (fmap, size) = _fmap_decode_header(blob, offset)
142 if fmap_name is not None:
143 _fmap_check_name(fmap, fmap_name)
144 return (fmap, size, offset)
145 except struct.error as e:
146 # Search for next FMAP candidate.
147 logging.debug('Continue searching FMAP due to exception %r', e)
Hung-Te Line89b6302016-02-04 12:23:02 +0800148 align /= 2
149 raise struct.error('No valid FMAP signatures.')
150
151
Mary Ruthven56761952016-01-14 13:07:35 -0800152def fmap_decode(blob, offset=None, fmap_name=None):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800153 """ Decodes a blob to FMAP dictionary object.
154
155 Arguments:
156 blob: a binary data containing FMAP structure.
157 offset: starting offset of FMAP. When omitted, fmap_decode will search in
158 the blob.
159 """
160 fmap = {}
Hung-Te Line89b6302016-02-04 12:23:02 +0800161
Hung-Te Lin8a346442013-06-20 10:49:22 +0800162 if offset is None:
Hung-Te Line89b6302016-02-04 12:23:02 +0800163 (fmap, size, offset) = _fmap_search_header(blob, fmap_name)
Hung-Te Lin8a346442013-06-20 10:49:22 +0800164 else:
165 (fmap, size) = _fmap_decode_header(blob, offset)
Hung-Te Line89b6302016-02-04 12:23:02 +0800166 if fmap_name is not None:
167 _fmap_check_name(fmap, fmap_name)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800168 fmap['areas'] = []
169 offset = offset + size
Tammo Spalink01e11722012-07-24 10:17:54 -0700170 for _ in range(fmap['nareas']):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800171 (area, size) = _fmap_decode_area(blob, offset)
172 offset = offset + size
173 fmap['areas'].append(area)
174 return fmap
175
176
177def _fmap_encode_header(obj):
178 """ (internal) Encodes a FMAP header """
179 values = [obj[name] for name in FMAP_HEADER_NAMES]
180 return struct.pack(FMAP_HEADER_FORMAT, *values)
181
182
183def _fmap_encode_area(obj):
184 """ (internal) Encodes a FMAP area entry """
185 values = [obj[name] for name in FMAP_AREA_NAMES]
186 return struct.pack(FMAP_AREA_FORMAT, *values)
187
188
189def fmap_encode(obj):
190 """ Encodes a FMAP dictionary object to blob.
191
192 Arguments
193 obj: a FMAP dictionary object.
194 """
195 # fix up values
196 obj['nareas'] = len(obj['areas'])
197 # TODO(hungte) re-assign signature / version?
198 blob = _fmap_encode_header(obj)
199 for area in obj['areas']:
200 blob = blob + _fmap_encode_area(area)
201 return blob
202
203
Tammo Spalink01e11722012-07-24 10:17:54 -0700204def main():
205 """Unit test."""
Hung-Te Linc052ff32013-06-20 10:48:17 +0800206 if len(sys.argv) > 1:
207 filename = sys.argv[1]
208 else:
209 filename = 'bin/example.bin'
Hung-Te Line89b6302016-02-04 12:23:02 +0800210 logging.basicConfig(level=logging.DEBUG)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800211 print 'Decoding FMAP from: %s' % filename
Hung-Te Linc052ff32013-06-20 10:48:17 +0800212 blob = open(filename).read()
Hung-Te Linc83105f2011-03-16 18:38:04 +0800213 obj = fmap_decode(blob)
214 print obj
215 blob2 = fmap_encode(obj)
Hung-Te Line89b6302016-02-04 12:23:02 +0800216 obj2 = fmap_decode(blob2, 0)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800217 print obj2
218 assert obj == obj2
Tammo Spalink01e11722012-07-24 10:17:54 -0700219
220
221if __name__ == '__main__':
222 main()