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 u_boot_pylib 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# Flags supported by areas (bits 2:0 are unused so not included here) 49FMAP_AREA_PRESERVE = 1 << 3 # Preserved by any firmware updates 50 51# These are the two data structures supported by flashrom, a header (which 52# appears once at the start) and an area (which is repeated until the end of 53# the list of areas) 54FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES) 55FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES) 56 57 58def NameToFmap(name): 59 if type(name) == bytes: 60 name = name.decode('utf-8') 61 return name.replace('\0', '').replace('-', '_').upper() 62 63def ConvertName(field_names, fields): 64 """Convert a name to something flashrom likes 65 66 Flashrom requires upper case, underscores instead of hyphens. We remove any 67 null characters as well. This updates the 'name' value in fields. 68 69 Args: 70 field_names: List of field names for this struct 71 fields: Dict: 72 key: Field name 73 value: value of that field (string for the ones we support) 74 """ 75 name_index = field_names.index('name') 76 fields[name_index] = tools.to_bytes(NameToFmap(fields[name_index])) 77 78def DecodeFmap(data): 79 """Decode a flashmap into a header and list of areas 80 81 Args: 82 data: Data block containing the FMAP 83 84 Returns: 85 Tuple: 86 header: FmapHeader object 87 List of FmapArea objects 88 """ 89 fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN])) 90 ConvertName(FMAP_HEADER_NAMES, fields) 91 header = FmapHeader(*fields) 92 areas = [] 93 data = data[FMAP_HEADER_LEN:] 94 for area in range(header.nareas): 95 fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN])) 96 ConvertName(FMAP_AREA_NAMES, fields) 97 areas.append(FmapArea(*fields)) 98 data = data[FMAP_AREA_LEN:] 99 return header, areas 100 101def EncodeFmap(image_size, name, areas): 102 """Create a new FMAP from a list of areas 103 104 Args: 105 image_size: Size of image, to put in the header 106 name: Name of image, to put in the header 107 areas: List of FmapArea objects 108 109 Returns: 110 String containing the FMAP created 111 """ 112 def _FormatBlob(fmt, names, obj): 113 params = [getattr(obj, name) for name in names] 114 ConvertName(names, params) 115 return struct.pack(fmt, *params) 116 117 values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size, name, len(areas)) 118 blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values) 119 for area in areas: 120 blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area) 121 return blob 122