summaryrefslogtreecommitdiffstats
path: root/tools/sptool/sptool.py
blob: ae7df92c163b7b81ff42f0964824c6adab791e19 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/python3
# Copyright (c) 2022, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

#
# Copyright 2022 The Hafnium Authors.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/BSD-3-Clause.

"""
Script which generates a Secure Partition package.
https://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager.html#secure-partition-packages
"""

import argparse
from collections import namedtuple
import sys
from shutil import copyfileobj
import os

HF_PAGE_SIZE = 0x1000 # bytes
HEADER_ELEMENT_BYTES = 4 # bytes
MANIFEST_IMAGE_SPLITTER=':'
PM_OFFSET_DEFAULT = "0x1000"
IMG_OFFSET_DEFAULT = "0x4000"

def split_dtb_bin(i : str):
    return i.split(MANIFEST_IMAGE_SPLITTER)

def align_to_page(n):
    return HF_PAGE_SIZE * \
          (round(n / HF_PAGE_SIZE) + \
           (1 if n % HF_PAGE_SIZE else 0))

def to_bytes(value):
    return int(value).to_bytes(HEADER_ELEMENT_BYTES, 'little')

class SpPkg:
    def __init__(self, pm_path : str, img_path : str, pm_offset: int,
                 img_offset: int):
        if not os.path.isfile(pm_path) or not os.path.isfile(img_path):
            raise Exception(f"Parameters should be path.  \
                              manifest: {pm_path}; img: {img_path}")
        self.pm_path = pm_path
        self.img_path = img_path
        self._SpPkgHeader = namedtuple("SpPkgHeader",
                             ("magic", "version",
                              "pm_offset", "pm_size",
                              "img_offset", "img_size"))

        if pm_offset >= img_offset:
            raise ValueError("pm_offset must be smaller than img_offset")

        is_hfpage_aligned = lambda val : val % HF_PAGE_SIZE == 0
        if not is_hfpage_aligned(pm_offset) or not is_hfpage_aligned(img_offset):
           raise ValueError(f"Offsets provided need to be page aligned: pm-{pm_offset}, img-{img_offset}")

        if img_offset - pm_offset < self.pm_size:
            raise ValueError(f"pm_offset and img_offset do not fit the specified file:{pm_path})")

        self.pm_offset = pm_offset
        self.img_offset = img_offset

    def __str__(self):
        return \
        f'''--SP package Info--
        header:{self.header}
        pm: {self.pm_path}
        img: {self.img_path}
        '''

    @property
    def magic(self):
        return "SPKG".encode()

    @property
    def version(self):
        return 0x2

    @property
    def pm_size(self):
        return os.path.getsize(self.pm_path)

    @property
    def img_size(self):
        return os.path.getsize(self.img_path)

    @property
    def header(self):
        return self._SpPkgHeader(
                self.magic,
                self.version,
                self.pm_offset,
                self.pm_size,
                self.img_offset,
                self.img_size)

    @property
    def header_size(self):
        return len(self._SpPkgHeader._fields)

    def generate(self, f_out : str):
        with open(f_out, "wb+") as output:
            for h in self.header:
                to_write = h if type(h) is bytes else to_bytes(h)
                output.write(to_write)
            output.seek(self.pm_offset)
            with open(self.pm_path, "rb") as pm:
                copyfileobj(pm, output)
            output.seek(self.img_offset)
            with open(self.img_path, "rb") as img:
                copyfileobj(img, output)

def Main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", required=True,
                        help="path to partition's image and manifest separated by a colon.")
    parser.add_argument("--pm-offset", required=False, default=PM_OFFSET_DEFAULT,
                        help="set partitition manifest offset.")
    parser.add_argument("--img-offset", required=False, default=IMG_OFFSET_DEFAULT,
                        help="set partition image offset.")
    parser.add_argument("-o", required=True, help="set output file path.")
    parser.add_argument("-v", required=False, action="store_true",
                        help="print package information.")
    args = parser.parse_args()

    if not os.path.exists(os.path.dirname(args.o)):
        raise Exception("Provide a valid output file path!\n")

    image_path, manifest_path = split_dtb_bin(args.i)
    pm_offset = int(args.pm_offset, 0)
    img_offset = int(args.img_offset, 0)
    pkg = SpPkg(manifest_path, image_path, pm_offset, img_offset)
    pkg.generate(args.o)

    if args.v is True:
        print(pkg)

    return 0

if __name__ == "__main__":
    sys.exit(Main())