summaryrefslogtreecommitdiff
path: root/tools/binman/fmap_util.py
blob: 25fe60a9cc3b3d9557731c709452633741acfcc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Support for flashrom's FMAP format. This supports a header followed by a
# number of 'areas', describing regions of a firmware storage device,
# generally SPI flash.

import collections
import struct
import sys

from patman import tools

# constants imported from lib/fmap.h
FMAP_SIGNATURE = b'__FMAP__'
FMAP_VER_MAJOR = 1
FMAP_VER_MINOR = 0
FMAP_STRLEN = 32

FMAP_AREA_STATIC = 1 << 0
FMAP_AREA_COMPRESSED = 1 << 1
FMAP_AREA_RO = 1 << 2

FMAP_HEADER_LEN = 56
FMAP_AREA_LEN = 42

FMAP_HEADER_FORMAT = '<8sBBQI%dsH'% (FMAP_STRLEN)
FMAP_AREA_FORMAT = '<II%dsH' % (FMAP_STRLEN)

FMAP_HEADER_NAMES = (
    'signature',
    'ver_major',
    'ver_minor',
    'base',
    'image_size',
    'name',
    'nareas',
)

FMAP_AREA_NAMES = (
    'offset',
    'size',
    'name',
    'flags',
)

# These are the two data structures supported by flashrom, a header (which
# appears once at the start) and an area (which is repeated until the end of
# the list of areas)
FmapHeader = collections.namedtuple('FmapHeader', FMAP_HEADER_NAMES)
FmapArea = collections.namedtuple('FmapArea', FMAP_AREA_NAMES)


def NameToFmap(name):
    if type(name) == bytes and sys.version_info[0] >= 3:
        name = name.decode('utf-8')  # pragma: no cover (for Python 2)
    return name.replace('\0', '').replace('-', '_').upper()

def ConvertName(field_names, fields):
    """Convert a name to something flashrom likes

    Flashrom requires upper case, underscores instead of hyphens. We remove any
    null characters as well. This updates the 'name' value in fields.

    Args:
        field_names: List of field names for this struct
        fields: Dict:
            key: Field name
            value: value of that field (string for the ones we support)
    """
    name_index = field_names.index('name')
    fields[name_index] = tools.ToBytes(NameToFmap(fields[name_index]))

def DecodeFmap(data):
    """Decode a flashmap into a header and list of areas

    Args:
        data: Data block containing the FMAP

    Returns:
        Tuple:
            header: FmapHeader object
            List of FmapArea objects
    """
    fields = list(struct.unpack(FMAP_HEADER_FORMAT, data[:FMAP_HEADER_LEN]))
    ConvertName(FMAP_HEADER_NAMES, fields)
    header = FmapHeader(*fields)
    areas = []
    data = data[FMAP_HEADER_LEN:]
    for area in range(header.nareas):
        fields = list(struct.unpack(FMAP_AREA_FORMAT, data[:FMAP_AREA_LEN]))
        ConvertName(FMAP_AREA_NAMES, fields)
        areas.append(FmapArea(*fields))
        data = data[FMAP_AREA_LEN:]
    return header, areas

def EncodeFmap(image_size, name, areas):
    """Create a new FMAP from a list of areas

    Args:
        image_size: Size of image, to put in the header
        name: Name of image, to put in the header
        areas: List of FmapArea objects

    Returns:
        String containing the FMAP created
    """
    def _FormatBlob(fmt, names, obj):
        params = [getattr(obj, name) for name in names]
        ConvertName(names, params)
        return struct.pack(fmt, *params)

    values = FmapHeader(FMAP_SIGNATURE, 1, 0, 0, image_size,
                        tools.FromUnicode(name), len(areas))
    blob = _FormatBlob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, values)
    for area in areas:
        blob += _FormatBlob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
    return blob