blob: e8ec156857c76758abeb77f5c9e88b0c086dcf7b [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 Linc83105f2011-03-16 18:38:04 +080022import struct
Hung-Te Linc052ff32013-06-20 10:48:17 +080023import sys
Tammo Spalink01e11722012-07-24 10:17:54 -070024
Hung-Te Linc83105f2011-03-16 18:38:04 +080025
26# constants imported from lib/fmap.h
Hung-Te Lin56b18402015-01-16 14:52:30 +080027FMAP_SIGNATURE = '__FMAP__'
Hung-Te Linc83105f2011-03-16 18:38:04 +080028FMAP_VER_MAJOR = 1
29FMAP_VER_MINOR_MIN = 0
30FMAP_VER_MINOR_MAX = 1
31FMAP_STRLEN = 32
32
33FMAP_FLAGS = {
34 'FMAP_AREA_STATIC': 1 << 0,
35 'FMAP_AREA_COMPRESSED': 1 << 1,
36}
37
38FMAP_HEADER_NAMES = (
39 'signature',
40 'ver_major',
41 'ver_minor',
42 'base',
43 'size',
44 'name',
45 'nareas',
46)
47
48FMAP_AREA_NAMES = (
49 'offset',
50 'size',
51 'name',
52 'flags',
53)
54
Tammo Spalink01e11722012-07-24 10:17:54 -070055
Hung-Te Linc83105f2011-03-16 18:38:04 +080056# format string
Hung-Te Lin56b18402015-01-16 14:52:30 +080057FMAP_HEADER_FORMAT = '<8sBBQI%dsH' % (FMAP_STRLEN)
58FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
Hung-Te Linc83105f2011-03-16 18:38:04 +080059
60
61def _fmap_decode_header(blob, offset):
62 """ (internal) Decodes a FMAP header from blob by offset"""
63 header = {}
64 for (name, value) in zip(FMAP_HEADER_NAMES,
65 struct.unpack_from(FMAP_HEADER_FORMAT,
66 blob,
67 offset)):
68 header[name] = value
69
70 if header['signature'] != FMAP_SIGNATURE:
71 raise struct.error('Invalid signature')
72 if (header['ver_major'] != FMAP_VER_MAJOR or
73 header['ver_minor'] < FMAP_VER_MINOR_MIN or
74 header['ver_minor'] > FMAP_VER_MINOR_MAX):
75 raise struct.error('Incompatible version')
76
77 # convert null-terminated names
78 header['name'] = header['name'].strip(chr(0))
79 return (header, struct.calcsize(FMAP_HEADER_FORMAT))
80
81
82def _fmap_decode_area(blob, offset):
83 """ (internal) Decodes a FMAP area record from blob by offset """
84 area = {}
85 for (name, value) in zip(FMAP_AREA_NAMES,
86 struct.unpack_from(FMAP_AREA_FORMAT, blob, offset)):
87 area[name] = value
88 # convert null-terminated names
89 area['name'] = area['name'].strip(chr(0))
90 # add a (readonly) readable FLAGS
91 area['FLAGS'] = _fmap_decode_area_flags(area['flags'])
92 return (area, struct.calcsize(FMAP_AREA_FORMAT))
93
94
95def _fmap_decode_area_flags(area_flags):
96 """ (internal) Decodes a FMAP flags property """
97 return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]])
98
99
Mary Ruthven56761952016-01-14 13:07:35 -0800100def fmap_decode(blob, offset=None, fmap_name=None):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800101 """ Decodes a blob to FMAP dictionary object.
102
103 Arguments:
104 blob: a binary data containing FMAP structure.
105 offset: starting offset of FMAP. When omitted, fmap_decode will search in
106 the blob.
107 """
108 fmap = {}
Hung-Te Lin8a346442013-06-20 10:49:22 +0800109 if offset is None:
Hung-Te Linc83105f2011-03-16 18:38:04 +0800110 # try search magic in fmap
Hung-Te Lin8a346442013-06-20 10:49:22 +0800111 while True:
112 offset = blob.find(FMAP_SIGNATURE, offset)
113 if offset == -1:
114 raise struct.error('No valid FMAP signatures.')
115 try:
116 (fmap, size) = _fmap_decode_header(blob, offset)
Mary Ruthven56761952016-01-14 13:07:35 -0800117 if fmap_name and fmap['name'] != fmap_name:
118 raise struct.error('Incorrect FMAP')
Hung-Te Lin8a346442013-06-20 10:49:22 +0800119 break
120 except struct.error:
121 offset += 1
122 else:
123 (fmap, size) = _fmap_decode_header(blob, offset)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800124 fmap['areas'] = []
125 offset = offset + size
Tammo Spalink01e11722012-07-24 10:17:54 -0700126 for _ in range(fmap['nareas']):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800127 (area, size) = _fmap_decode_area(blob, offset)
128 offset = offset + size
129 fmap['areas'].append(area)
130 return fmap
131
132
133def _fmap_encode_header(obj):
134 """ (internal) Encodes a FMAP header """
135 values = [obj[name] for name in FMAP_HEADER_NAMES]
136 return struct.pack(FMAP_HEADER_FORMAT, *values)
137
138
139def _fmap_encode_area(obj):
140 """ (internal) Encodes a FMAP area entry """
141 values = [obj[name] for name in FMAP_AREA_NAMES]
142 return struct.pack(FMAP_AREA_FORMAT, *values)
143
144
145def fmap_encode(obj):
146 """ Encodes a FMAP dictionary object to blob.
147
148 Arguments
149 obj: a FMAP dictionary object.
150 """
151 # fix up values
152 obj['nareas'] = len(obj['areas'])
153 # TODO(hungte) re-assign signature / version?
154 blob = _fmap_encode_header(obj)
155 for area in obj['areas']:
156 blob = blob + _fmap_encode_area(area)
157 return blob
158
159
Tammo Spalink01e11722012-07-24 10:17:54 -0700160def main():
161 """Unit test."""
Hung-Te Linc052ff32013-06-20 10:48:17 +0800162 if len(sys.argv) > 1:
163 filename = sys.argv[1]
164 else:
165 filename = 'bin/example.bin'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800166 print 'Decoding FMAP from: %s' % filename
Hung-Te Linc052ff32013-06-20 10:48:17 +0800167 blob = open(filename).read()
Hung-Te Linc83105f2011-03-16 18:38:04 +0800168 obj = fmap_decode(blob)
169 print obj
170 blob2 = fmap_encode(obj)
171 obj2 = fmap_decode(blob2)
172 print obj2
173 assert obj == obj2
Tammo Spalink01e11722012-07-24 10:17:54 -0700174
175
176if __name__ == '__main__':
177 main()