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