Prepare v2023.10
[platform/kernel/u-boot.git] / tools / binman / fmap_util.py
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
9 import collections
10 import struct
11 import sys
12
13 from u_boot_pylib import tools
14
15 # constants imported from lib/fmap.h
16 FMAP_SIGNATURE = b'__FMAP__'
17 FMAP_VER_MAJOR = 1
18 FMAP_VER_MINOR = 0
19 FMAP_STRLEN = 32
20
21 FMAP_AREA_STATIC = 1 << 0
22 FMAP_AREA_COMPRESSED = 1 << 1
23 FMAP_AREA_RO = 1 << 2
24
25 FMAP_HEADER_LEN = 56
26 FMAP_AREA_LEN = 42
27
28 FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
29 FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)
30
31 FMAP_HEADER_NAMES = (
32     'signature',
33     'ver_major',
34     'ver_minor',
35     'base',
36     'image_size',
37     'name',
38     'nareas',
39 )
40
41 FMAP_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)
49 FMAP_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)
54 FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
55 FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)
56
57
58 def NameToFmap(name):
59     if type(name) == bytes:
60         name = name.decode('utf-8')
61     return name.replace('\0', '').replace('-', '_').upper()
62
63 def 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
78 def 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
101 def 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