blob: fc99e8f791bd786d968f72ff52395d4f6d0d26ba [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.
5"""
6This module provides basic encode and decode functionality to the flashrom
7memory map (FMAP) structure.
8
9Usage:
10 (decode)
11 obj = fmap_decode(blob)
12 print obj
13
14 (encode)
15 blob = fmap_encode(obj)
16 open('output.bin', 'w').write(blob)
17
18 The object returned by fmap_decode is a dictionary with names defined in
19 fmap.h. A special property 'FLAGS' is provided as a readable and read-only
20 tuple of decoded area flags.
21"""
22
Tammo Spalink01e11722012-07-24 10:17:54 -070023
Hung-Te Linc83105f2011-03-16 18:38:04 +080024import struct
Hung-Te Linc052ff32013-06-20 10:48:17 +080025import sys
Tammo Spalink01e11722012-07-24 10:17:54 -070026
Hung-Te Linc83105f2011-03-16 18:38:04 +080027
28# constants imported from lib/fmap.h
29FMAP_SIGNATURE = "__FMAP__"
30FMAP_VER_MAJOR = 1
31FMAP_VER_MINOR_MIN = 0
32FMAP_VER_MINOR_MAX = 1
33FMAP_STRLEN = 32
34
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
59FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % (FMAP_STRLEN)
60FMAP_AREA_FORMAT = "<II%dsH" % (FMAP_STRLEN)
61
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
102def fmap_decode(blob, offset=None):
103 """ Decodes a blob to FMAP dictionary object.
104
105 Arguments:
106 blob: a binary data containing FMAP structure.
107 offset: starting offset of FMAP. When omitted, fmap_decode will search in
108 the blob.
109 """
110 fmap = {}
Hung-Te Lin8a346442013-06-20 10:49:22 +0800111 if offset is None:
Hung-Te Linc83105f2011-03-16 18:38:04 +0800112 # try search magic in fmap
Hung-Te Lin8a346442013-06-20 10:49:22 +0800113 while True:
114 offset = blob.find(FMAP_SIGNATURE, offset)
115 if offset == -1:
116 raise struct.error('No valid FMAP signatures.')
117 try:
118 (fmap, size) = _fmap_decode_header(blob, offset)
119 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'
166 print "Decoding FMAP from: %s" % filename
167 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()