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 NameToFmap(name): 53 return name.replace('\0', '').replace('-', '_').upper() 54 55def ConvertName(field_names, fields): 56 """Convert a name to something flashrom likes 57 58 Flashrom requires upper case, underscores instead of hyphens. We remove any 59 null characters as well. This updates the 'name' value in fields. 60 61 Args: 62 field_names: List of field names for this struct 63 fields: Dict: 64 key: Field name 65 value: value of that field (string for the ones we support) 66 """ 67 name_index = field_names.index('name') 68 fields[name_index] = NameToFmap(fields[name_index]) 69 70def DecodeFmap(data): 71 """Decode a flashmap into a header and list of areas 72 73 Args: 74 data: Data block containing the FMAP 75 76 Returns: 77 Tuple: 78 header: FmapHeader object 79 List of FmapArea objects 80 """ 81 fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN])) 82 ConvertName(FMAP_HEADER_NAMES, fields) 83 header = FmapHeader(*fields) 84 areas = [] 85 data = data[FMAP_HEADER_LEN:] 86 for area in range(header.nareas): 87 fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN])) 88 ConvertName(FMAP_AREA_NAMES, fields) 89 areas.append(FmapArea(*fields)) 90 data = data[FMAP_AREA_LEN:] 91 return header, areas 92 93def EncodeFmap(image_size, name, areas): 94 """Create a new FMAP from a list of areas 95 96 Args: 97 image_size: Size of image, to put in the header 98 name: Name of image, to put in the header 99 areas: List of FmapArea objects 100 101 Returns: 102 String containing the FMAP created 103 """ 104 def _FormatBlob(fmt, names, obj): 105 params = [getattr(obj, name) for name in names] 106 ConvertName(names, params) 107 return struct.pack(fmt, *params) 108 109 values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas)) 110 blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values) 111 for area in areas: 112 blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area) 113 return blob 114