OLD | NEW |
(Empty) | |
| 1 #!/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 """ |
| 6 This module provides basic encode and decode functionality to the flashrom |
| 7 memory map (FMAP) structure. |
| 8 |
| 9 Usage: |
| 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 |
| 23 import struct |
| 24 import sys |
| 25 |
| 26 # constants imported from lib/fmap.h |
| 27 FMAP_SIGNATURE = "__FMAP__" |
| 28 FMAP_VER_MAJOR = 1 |
| 29 FMAP_VER_MINOR = 0 |
| 30 FMAP_STRLEN = 32 |
| 31 |
| 32 FMAP_FLAGS = { |
| 33 'FMAP_AREA_STATIC': 1 << 0, |
| 34 'FMAP_AREA_COMPRESSED': 1 << 1, |
| 35 } |
| 36 |
| 37 FMAP_HEADER_NAMES = ( |
| 38 'signature', |
| 39 'ver_major', |
| 40 'ver_minor', |
| 41 'base', |
| 42 'size', |
| 43 'name', |
| 44 'nareas', |
| 45 ) |
| 46 |
| 47 FMAP_AREA_NAMES = ( |
| 48 'offset', |
| 49 'size', |
| 50 'name', |
| 51 'flags', |
| 52 ) |
| 53 |
| 54 # format string |
| 55 FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % (FMAP_STRLEN) |
| 56 FMAP_AREA_FORMAT = "<II%dsH" % (FMAP_STRLEN) |
| 57 |
| 58 |
| 59 def _fmap_decode_header(blob, offset): |
| 60 """ (internal) Decodes a FMAP header from blob by offset""" |
| 61 header = {} |
| 62 for (name, value) in zip(FMAP_HEADER_NAMES, |
| 63 struct.unpack_from(FMAP_HEADER_FORMAT, |
| 64 blob, |
| 65 offset)): |
| 66 header[name] = value |
| 67 |
| 68 if header['signature'] != FMAP_SIGNATURE: |
| 69 raise struct.error('Invalid signature') |
| 70 if header['ver_major'] != FMAP_VER_MAJOR or \ |
| 71 header['ver_minor'] != FMAP_VER_MINOR: |
| 72 raise struct.error('Incompatible version') |
| 73 |
| 74 # convert null-terminated names |
| 75 header['name'] = header['name'].strip(chr(0)) |
| 76 return (header, struct.calcsize(FMAP_HEADER_FORMAT)) |
| 77 |
| 78 |
| 79 def _fmap_decode_area(blob, offset): |
| 80 """ (internal) Decodes a FMAP area record from blob by offset """ |
| 81 area = {} |
| 82 for (name, value) in zip(FMAP_AREA_NAMES, |
| 83 struct.unpack_from(FMAP_AREA_FORMAT, blob, offset)): |
| 84 area[name] = value |
| 85 # convert null-terminated names |
| 86 area['name'] = area['name'].strip(chr(0)) |
| 87 # add a (readonly) readable FLAGS |
| 88 area['FLAGS'] = _fmap_decode_area_flags(area['flags']) |
| 89 return (area, struct.calcsize(FMAP_AREA_FORMAT)) |
| 90 |
| 91 |
| 92 def _fmap_decode_area_flags(area_flags): |
| 93 """ (internal) Decodes a FMAP flags property """ |
| 94 return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]]) |
| 95 |
| 96 |
| 97 def fmap_decode(blob, offset=None): |
| 98 """ Decodes a blob to FMAP dictionary object. |
| 99 |
| 100 Arguments: |
| 101 blob: a binary data containing FMAP structure. |
| 102 offset: starting offset of FMAP. When omitted, fmap_decode will search in |
| 103 the blob. |
| 104 """ |
| 105 fmap = {} |
| 106 if offset == None: |
| 107 # try search magic in fmap |
| 108 offset = blob.find(FMAP_SIGNATURE) |
| 109 (fmap, size) = _fmap_decode_header(blob, offset) |
| 110 fmap['areas'] = [] |
| 111 offset = offset + size |
| 112 for i in range(fmap['nareas']): |
| 113 (area, size) = _fmap_decode_area(blob, offset) |
| 114 offset = offset + size |
| 115 fmap['areas'].append(area) |
| 116 return fmap |
| 117 |
| 118 |
| 119 def _fmap_encode_header(obj): |
| 120 """ (internal) Encodes a FMAP header """ |
| 121 values = [obj[name] for name in FMAP_HEADER_NAMES] |
| 122 return struct.pack(FMAP_HEADER_FORMAT, *values) |
| 123 |
| 124 |
| 125 def _fmap_encode_area(obj): |
| 126 """ (internal) Encodes a FMAP area entry """ |
| 127 values = [obj[name] for name in FMAP_AREA_NAMES] |
| 128 return struct.pack(FMAP_AREA_FORMAT, *values) |
| 129 |
| 130 |
| 131 def fmap_encode(obj): |
| 132 """ Encodes a FMAP dictionary object to blob. |
| 133 |
| 134 Arguments |
| 135 obj: a FMAP dictionary object. |
| 136 """ |
| 137 # fix up values |
| 138 obj['nareas'] = len(obj['areas']) |
| 139 # TODO(hungte) re-assign signature / version? |
| 140 blob = _fmap_encode_header(obj) |
| 141 for area in obj['areas']: |
| 142 blob = blob + _fmap_encode_area(area) |
| 143 return blob |
| 144 |
| 145 |
| 146 if __name__ == '__main__': |
| 147 # main entry, do a unit test |
| 148 blob = open('bin/example.bin').read() |
| 149 obj = fmap_decode(blob) |
| 150 print obj |
| 151 blob2 = fmap_encode(obj) |
| 152 obj2 = fmap_decode(blob2) |
| 153 print obj2 |
| 154 assert obj == obj2 |
OLD | NEW |