blob: 4ada30a7592d4186a4d87c9c20584b60e993a007 [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 Lin56b18402015-01-16 14:52:30 +08005"""This module provides basic encode and decode functionality to the 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
100def fmap_decode(blob, offset=None):
101 """ 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)
117 break
118 except struct.error:
119 offset += 1
120 else:
121 (fmap, size) = _fmap_decode_header(blob, offset)
Hung-Te Linc83105f2011-03-16 18:38:04 +0800122 fmap['areas'] = []
123 offset = offset + size
Tammo Spalink01e11722012-07-24 10:17:54 -0700124 for _ in range(fmap['nareas']):
Hung-Te Linc83105f2011-03-16 18:38:04 +0800125 (area, size) = _fmap_decode_area(blob, offset)
126 offset = offset + size
127 fmap['areas'].append(area)
128 return fmap
129
130
131def _fmap_encode_header(obj):
132 """ (internal) Encodes a FMAP header """
133 values = [obj[name] for name in FMAP_HEADER_NAMES]
134 return struct.pack(FMAP_HEADER_FORMAT, *values)
135
136
137def _fmap_encode_area(obj):
138 """ (internal) Encodes a FMAP area entry """
139 values = [obj[name] for name in FMAP_AREA_NAMES]
140 return struct.pack(FMAP_AREA_FORMAT, *values)
141
142
143def fmap_encode(obj):
144 """ Encodes a FMAP dictionary object to blob.
145
146 Arguments
147 obj: a FMAP dictionary object.
148 """
149 # fix up values
150 obj['nareas'] = len(obj['areas'])
151 # TODO(hungte) re-assign signature / version?
152 blob = _fmap_encode_header(obj)
153 for area in obj['areas']:
154 blob = blob + _fmap_encode_area(area)
155 return blob
156
157
Tammo Spalink01e11722012-07-24 10:17:54 -0700158def main():
159 """Unit test."""
Hung-Te Linc052ff32013-06-20 10:48:17 +0800160 if len(sys.argv) > 1:
161 filename = sys.argv[1]
162 else:
163 filename = 'bin/example.bin'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800164 print 'Decoding FMAP from: %s' % filename
Hung-Te Linc052ff32013-06-20 10:48:17 +0800165 blob = open(filename).read()
Hung-Te Linc83105f2011-03-16 18:38:04 +0800166 obj = fmap_decode(blob)
167 print obj
168 blob2 = fmap_encode(obj)
169 obj2 = fmap_decode(blob2)
170 print obj2
171 assert obj == obj2
Tammo Spalink01e11722012-07-24 10:17:54 -0700172
173
174if __name__ == '__main__':
175 main()