1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2018 Google, Inc 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Support for flashrom's FMAP format. This supports a header followed by a 6# number of 'areas', describing regions of a firmware storage device, 7# generally SPI flash. 8 9import collections 10import struct 11 12# constants imported from lib/fmap.h 13FMAP_SIGNATURE = '__FMAP__' 14FMAP_VER_MAJOR = 1 15FMAP_VER_MINOR = 0 16FMAP_STRLEN = 32 17 18FMAP_AREA_STATIC = 1 << 0 19FMAP_AREA_COMPRESSED = 1 << 1 20FMAP_AREA_RO = 1 << 2 21 22FMAP_HEADER_LEN = 56 23FMAP_AREA_LEN = 42 24 25FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN) 26FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN) 27 28FMAP_HEADER_NAMES = ( 29 'signature', 30 'ver_major', 31 'ver_minor', 32 'base', 33 'image_size', 34 'name', 35 'nareas', 36) 37 38FMAP_AREA_NAMES = ( 39 'offset', 40 'size', 41 'name', 42 'flags', 43) 44 45# These are the two data structures supported by flashrom, a header (which 46# appears once at the start) and an area (which is repeated until the end of 47# the list of areas) 48FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES) 49FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES) 50 51 52def ConvertName(field_names, fields): 53 """Convert a name to something flashrom likes 54 55 Flashrom requires upper case, underscores instead of hyphens. We remove any 56 null characters as well. This updates the 'name' value in fields. 57 58 Args: 59 field_names: List of field names for this struct 60 fields: Dict: 61 key: Field name 62 value: value of that field (string for the ones we support) 63 """ 64 name_index = field_names.index('name') 65 fields[name_index] = fields[name_index].replace('\0', '').replace('-', '_').upper() 66 67def DecodeFmap(data): 68 """Decode a flashmap into a header and list of areas 69 70 Args: 71 data: Data block containing the FMAP 72 73 Returns: 74 Tuple: 75 header: FmapHeader object 76 List of FmapArea objects 77 """ 78 fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN])) 79 ConvertName(FMAP_HEADER_NAMES, fields) 80 header = FmapHeader(*fields) 81 areas = [] 82 data = data[FMAP_HEADER_LEN:] 83 for area in range(header.nareas): 84 fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN])) 85 ConvertName(FMAP_AREA_NAMES, fields) 86 areas.append(FmapArea(*fields)) 87 data = data[FMAP_AREA_LEN:] 88 return header, areas 89 90def EncodeFmap(image_size, name, areas): 91 """Create a new FMAP from a list of areas 92 93 Args: 94 image_size: Size of image, to put in the header 95 name: Name of image, to put in the header 96 areas: List of FmapArea objects 97 98 Returns: 99 String containing the FMAP created 100 """ 101 def _FormatBlob(fmt, names, obj): 102 params = [getattr(obj, name) for name in names] 103 return struct.pack(fmt, *params) 104 105 values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas)) 106 blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values) 107 for area in areas: 108 blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area) 109 return blob 110