flashmap: Bring over changes from factory repo
fmap.py is identical to the one in factory/setup/fmap.py except:
- no test functionality in main function
- module docstring and license
Added some unit tests for the new functionality
BUG=chromium:726356
TEST=ran unittests
Change-Id: Ib67bd4260cf4e1cc08543b822521a4adcf0cbce6
Reviewed-on: https://chromium-review.googlesource.com/516382
Commit-Ready: Drew Davenport <ddavenport@chromium.org>
Tested-by: Drew Davenport <ddavenport@chromium.org>
Reviewed-by: Drew Davenport <ddavenport@chromium.org>
diff --git a/fmap.py b/fmap.py
index 21519a4..830a64e 100644
--- a/fmap.py
+++ b/fmap.py
@@ -52,6 +52,7 @@
"""
+import logging
import struct
import sys
@@ -59,8 +60,10 @@
# constants imported from lib/fmap.h
FMAP_SIGNATURE = '__FMAP__'
FMAP_VER_MAJOR = 1
-FMAP_VER_MINOR = 0
+FMAP_VER_MINOR_MIN = 0
+FMAP_VER_MINOR_MAX = 1
FMAP_STRLEN = 32
+FMAP_SEARCH_STRIDE = 4
FMAP_FLAGS = {
'FMAP_AREA_STATIC': 1 << 0,
@@ -102,7 +105,8 @@
if header['signature'] != FMAP_SIGNATURE:
raise struct.error('Invalid signature')
if (header['ver_major'] != FMAP_VER_MAJOR or
- header['ver_minor'] != FMAP_VER_MINOR):
+ header['ver_minor'] < FMAP_VER_MINOR_MIN or
+ header['ver_minor'] > FMAP_VER_MINOR_MAX):
raise struct.error('Incompatible version')
# convert null-terminated names
@@ -128,7 +132,58 @@
return tuple([name for name in FMAP_FLAGS if area_flags & FMAP_FLAGS[name]])
-def fmap_decode(blob, offset=None):
+def _fmap_check_name(fmap, name):
+ """Checks if the FMAP structure has correct name.
+
+ Args:
+ fmap: A decoded FMAP structure.
+ name: A string to specify expected FMAP name.
+
+ Raises:
+ struct.error if the name does not match.
+ """
+ if fmap['name'] != name:
+ raise struct.error('Incorrect FMAP (found: "%s", expected: "%s")' %
+ (fmap['name'], name))
+
+
+def _fmap_search_header(blob, fmap_name=None):
+ """Searches FMAP headers in given blob.
+
+ Uses same logic from vboot_reference/host/lib/fmap.c.
+
+ Args:
+ blob: A string containing FMAP data.
+ fmap_name: A string to specify target FMAP name.
+
+ Returns:
+ A tuple of (fmap, size, offset).
+ """
+ lim = len(blob) - struct.calcsize(FMAP_HEADER_FORMAT)
+ align = FMAP_SEARCH_STRIDE
+
+ # Search large alignments before small ones to find "right" FMAP.
+ while align <= lim:
+ align *= 2
+
+ while align >= FMAP_SEARCH_STRIDE:
+ for offset in xrange(align, lim + 1, align * 2):
+ if not blob.startswith(FMAP_SIGNATURE, offset):
+ continue
+ try:
+ (fmap, size) = _fmap_decode_header(blob, offset)
+ if fmap_name is not None:
+ _fmap_check_name(fmap, fmap_name)
+ return (fmap, size, offset)
+ except struct.error as e:
+ # Search for next FMAP candidate.
+ logging.debug('Continue searching FMAP due to exception %r', e)
+ pass
+ align /= 2
+ raise struct.error('No valid FMAP signatures.')
+
+
+def fmap_decode(blob, offset=None, fmap_name=None):
""" Decodes a blob to FMAP dictionary object.
Arguments:
@@ -137,10 +192,13 @@
the blob.
"""
fmap = {}
+
if offset is None:
- # try search magic in fmap
- offset = blob.find(FMAP_SIGNATURE)
- (fmap, size) = _fmap_decode_header(blob, offset)
+ (fmap, size, offset) = _fmap_search_header(blob, fmap_name)
+ else:
+ (fmap, size) = _fmap_decode_header(blob, offset)
+ if fmap_name is not None:
+ _fmap_check_name(fmap, fmap_name)
fmap['areas'] = []
offset = offset + size
for _ in range(fmap['nareas']):
diff --git a/fmap_unittest.py b/fmap_unittest.py
index 7006520..4473da9 100644
--- a/fmap_unittest.py
+++ b/fmap_unittest.py
@@ -5,6 +5,7 @@
# found in the LICENSE file.
"""Unit test for fmap module."""
+import struct
import unittest
import fmap
@@ -57,6 +58,30 @@
decoded = fmap.fmap_decode(self.example_blob)
self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+ def testDecodeWithOffset(self):
+ decoded = fmap.fmap_decode(self.example_blob, 512)
+ self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+
+ def testDecodeWithName(self):
+ decoded = fmap.fmap_decode(self.example_blob, fmap_name='example')
+ self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+ decoded = fmap.fmap_decode(self.example_blob, 512, 'example')
+ self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+
+ def testDecodeWithWrongName(self):
+ with self.assertRaises(struct.error):
+ decoded = fmap.fmap_decode(self.example_blob, fmap_name='banana')
+ with self.assertRaises(struct.error):
+ decoded = fmap.fmap_decode(self.example_blob, 512, 'banana')
+
+ def testDecodeWithOffset(self):
+ decoded = fmap.fmap_decode(self.example_blob, 512)
+ self.assertEquals(_EXAMPLE_BIN_FMAP, decoded)
+
+ def testDecodeWithWrongOffset(self):
+ with self.assertRaises(struct.error):
+ fmap.fmap_decode(self.example_blob, 42)
+
def testEncode(self):
encoded = fmap.fmap_encode(_EXAMPLE_BIN_FMAP)
# example.bin contains other binary data besides the fmap