blob: 7722989304f8ba562a79ad3524fe7ea9636640c0 [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
Tammo Spalink01e11722012-07-24 10:17:54 -070025
Hung-Te Linc83105f2011-03-16 18:38:04 +080026
27# constants imported from lib/fmap.h
28FMAP_SIGNATURE = "__FMAP__"
29FMAP_VER_MAJOR = 1
30FMAP_VER_MINOR_MIN = 0
31FMAP_VER_MINOR_MAX = 1
32FMAP_STRLEN = 32
33
34FMAP_FLAGS = {
35 'FMAP_AREA_STATIC': 1 << 0,
36 'FMAP_AREA_COMPRESSED': 1 << 1,
37}
38
39FMAP_HEADER_NAMES = (
40 'signature',
41 'ver_major',
42 'ver_minor',
43 'base',
44 'size',
45 'name',
46 'nareas',
47)
48
49FMAP_AREA_NAMES = (
50 'offset',
51 'size',
52 'name',
53 'flags',
54)
55
Tammo Spalink01e11722012-07-24 10:17:54 -070056
Hung-Te Linc83105f2011-03-16 18:38:04 +080057# format string
58FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % (FMAP_STRLEN)
59FMAP_AREA_FORMAT = "<II%dsH" % (FMAP_STRLEN)
60
61
62def _fmap_decode_header(blob, offset):
63 """ (internal) Decodes a FMAP header from blob by offset"""
64 header = {}
65 for (name, value) in zip(FMAP_HEADER_NAMES,
66 struct.unpack_from(FMAP_HEADER_FORMAT,
67 blob,
68 offset)):
69 header[name] = value
70
71 if header['signature'] != FMAP_SIGNATURE:
72 raise struct.error('Invalid signature')
73 if (header['ver_major'] != FMAP_VER_MAJOR or
74 header['ver_minor'] < FMAP_VER_MINOR_MIN or
75 header['ver_minor'] > FMAP_VER_MINOR_MAX):
76 raise struct.error('Incompatible version')
77
78 # convert null-terminated names
79 header['name'] = header['name'].strip(chr(0))
80 return (header, struct.calcsize(FMAP_HEADER_FORMAT))
81
82
83def _fmap_decode_area(blob, offset):
84 """ (internal) Decodes a FMAP area record from blob by offset """
85 area = {}
86 for (name, value) in zip(FMAP_AREA_NAMES,
87 struct.unpack_from(FMAP_AREA_FORMAT, blob, offset)):
88 area[name] = value
89 # convert null-terminated names
90 area['name'] = area['name'].strip(chr(0))
91 # add a (readonly) readable FLAGS
92 area['FLAGS'] = _fmap_decode_area_flags(area['flags'])
93 return (area, struct.calcsize(FMAP_AREA_FORMAT))
94
95
96def _fmap_decode_area_flags(area_flags):
97 """ (internal) Decodes a FMAP flags property """
98 return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]])
99
100
101def fmap_decode(blob, offset=None):
102 """ Decodes a blob to FMAP dictionary object.
103
104 Arguments:
105 blob: a binary data containing FMAP structure.
106 offset: starting offset of FMAP. When omitted, fmap_decode will search in
107 the blob.
108 """
109 fmap = {}
Hung-Te Lin8a346442013-06-20 10:49:22 +0800110 if offset is None:
Hung-Te Linc83105f2011-03-16 18:38:04 +0800111 # try search magic in fmap
Hung-Te Lin8a346442013-06-20 10:49:22 +0800112 while True:
113 offset = blob.find(FMAP_SIGNATURE, offset)
114 if offset == -1:
115 raise struct.error('No valid FMAP signatures.')
116 try:
117 (fmap, size) = _fmap_decode_header(blob, offset)
118 break
119 except struct.error:
120 offset += 1
121 else:
122 (fmap, size) = _fmap_decode_header(blob, offset)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800123 fmap['areas'] = []
124 offset = offset + size
Tammo Spalink01e11722012-07-24 10:17:54 -0700125 for _ in range(fmap['nareas']):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800126 (area, size) = _fmap_decode_area(blob, offset)
127 offset = offset + size
128 fmap['areas'].append(area)
129 return fmap
130
131
132def _fmap_encode_header(obj):
133 """ (internal) Encodes a FMAP header """
134 values = [obj[name] for name in FMAP_HEADER_NAMES]
135 return struct.pack(FMAP_HEADER_FORMAT, *values)
136
137
138def _fmap_encode_area(obj):
139 """ (internal) Encodes a FMAP area entry """
140 values = [obj[name] for name in FMAP_AREA_NAMES]
141 return struct.pack(FMAP_AREA_FORMAT, *values)
142
143
144def fmap_encode(obj):
145 """ Encodes a FMAP dictionary object to blob.
146
147 Arguments
148 obj: a FMAP dictionary object.
149 """
150 # fix up values
151 obj['nareas'] = len(obj['areas'])
152 # TODO(hungte) re-assign signature / version?
153 blob = _fmap_encode_header(obj)
154 for area in obj['areas']:
155 blob = blob + _fmap_encode_area(area)
156 return blob
157
158
Tammo Spalink01e11722012-07-24 10:17:54 -0700159def main():
160 """Unit test."""
Hung-Te Linc83105f2011-03-16 18:38:04 +0800161 blob = open('bin/example.bin').read()
162 obj = fmap_decode(blob)
163 print obj
164 blob2 = fmap_encode(obj)
165 obj2 = fmap_decode(blob2)
166 print obj2
167 assert obj == obj2
Tammo Spalink01e11722012-07-24 10:17:54 -0700168
169
170if __name__ == '__main__':
171 main()