xref: /arm-trusted-firmware/tools/sptool/sptool.py (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu#!/usr/bin/python3
2*91f16700Schasinglulu# Copyright (c) 2022, Arm Limited. All rights reserved.
3*91f16700Schasinglulu#
4*91f16700Schasinglulu# SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu
6*91f16700Schasinglulu#
7*91f16700Schasinglulu# Copyright 2022 The Hafnium Authors.
8*91f16700Schasinglulu#
9*91f16700Schasinglulu# Use of this source code is governed by a BSD-style
10*91f16700Schasinglulu# license that can be found in the LICENSE file or at
11*91f16700Schasinglulu# https://opensource.org/licenses/BSD-3-Clause.
12*91f16700Schasinglulu
13*91f16700Schasinglulu"""
14*91f16700SchasingluluScript which generates a Secure Partition package.
15*91f16700Schasingluluhttps://trustedfirmware-a.readthedocs.io/en/latest/components/secure-partition-manager.html#secure-partition-packages
16*91f16700Schasinglulu"""
17*91f16700Schasinglulu
18*91f16700Schasingluluimport argparse
19*91f16700Schasinglulufrom collections import namedtuple
20*91f16700Schasingluluimport sys
21*91f16700Schasinglulufrom shutil import copyfileobj
22*91f16700Schasingluluimport os
23*91f16700Schasinglulu
24*91f16700SchasingluluHF_PAGE_SIZE = 0x1000 # bytes
25*91f16700SchasingluluHEADER_ELEMENT_BYTES = 4 # bytes
26*91f16700SchasingluluMANIFEST_IMAGE_SPLITTER=':'
27*91f16700SchasingluluPM_OFFSET_DEFAULT = "0x1000"
28*91f16700SchasingluluIMG_OFFSET_DEFAULT = "0x4000"
29*91f16700Schasinglulu
30*91f16700Schasingluludef split_dtb_bin(i : str):
31*91f16700Schasinglulu    return i.split(MANIFEST_IMAGE_SPLITTER)
32*91f16700Schasinglulu
33*91f16700Schasingluludef align_to_page(n):
34*91f16700Schasinglulu    return HF_PAGE_SIZE * \
35*91f16700Schasinglulu          (round(n / HF_PAGE_SIZE) + \
36*91f16700Schasinglulu           (1 if n % HF_PAGE_SIZE else 0))
37*91f16700Schasinglulu
38*91f16700Schasingluludef to_bytes(value):
39*91f16700Schasinglulu    return int(value).to_bytes(HEADER_ELEMENT_BYTES, 'little')
40*91f16700Schasinglulu
41*91f16700Schasingluluclass SpPkg:
42*91f16700Schasinglulu    def __init__(self, pm_path : str, img_path : str, pm_offset: int,
43*91f16700Schasinglulu                 img_offset: int):
44*91f16700Schasinglulu        if not os.path.isfile(pm_path) or not os.path.isfile(img_path):
45*91f16700Schasinglulu            raise Exception(f"Parameters should be path.  \
46*91f16700Schasinglulu                              manifest: {pm_path}; img: {img_path}")
47*91f16700Schasinglulu        self.pm_path = pm_path
48*91f16700Schasinglulu        self.img_path = img_path
49*91f16700Schasinglulu        self._SpPkgHeader = namedtuple("SpPkgHeader",
50*91f16700Schasinglulu                             ("magic", "version",
51*91f16700Schasinglulu                              "pm_offset", "pm_size",
52*91f16700Schasinglulu                              "img_offset", "img_size"))
53*91f16700Schasinglulu
54*91f16700Schasinglulu        if pm_offset >= img_offset:
55*91f16700Schasinglulu            raise ValueError("pm_offset must be smaller than img_offset")
56*91f16700Schasinglulu
57*91f16700Schasinglulu        is_hfpage_aligned = lambda val : val % HF_PAGE_SIZE == 0
58*91f16700Schasinglulu        if not is_hfpage_aligned(pm_offset) or not is_hfpage_aligned(img_offset):
59*91f16700Schasinglulu           raise ValueError(f"Offsets provided need to be page aligned: pm-{pm_offset}, img-{img_offset}")
60*91f16700Schasinglulu
61*91f16700Schasinglulu        if img_offset - pm_offset < self.pm_size:
62*91f16700Schasinglulu            raise ValueError(f"pm_offset and img_offset do not fit the specified file:{pm_path})")
63*91f16700Schasinglulu
64*91f16700Schasinglulu        self.pm_offset = pm_offset
65*91f16700Schasinglulu        self.img_offset = img_offset
66*91f16700Schasinglulu
67*91f16700Schasinglulu    def __str__(self):
68*91f16700Schasinglulu        return \
69*91f16700Schasinglulu        f'''--SP package Info--
70*91f16700Schasinglulu        header:{self.header}
71*91f16700Schasinglulu        pm: {self.pm_path}
72*91f16700Schasinglulu        img: {self.img_path}
73*91f16700Schasinglulu        '''
74*91f16700Schasinglulu
75*91f16700Schasinglulu    @property
76*91f16700Schasinglulu    def magic(self):
77*91f16700Schasinglulu        return "SPKG".encode()
78*91f16700Schasinglulu
79*91f16700Schasinglulu    @property
80*91f16700Schasinglulu    def version(self):
81*91f16700Schasinglulu        return 0x2
82*91f16700Schasinglulu
83*91f16700Schasinglulu    @property
84*91f16700Schasinglulu    def pm_size(self):
85*91f16700Schasinglulu        return os.path.getsize(self.pm_path)
86*91f16700Schasinglulu
87*91f16700Schasinglulu    @property
88*91f16700Schasinglulu    def img_size(self):
89*91f16700Schasinglulu        return os.path.getsize(self.img_path)
90*91f16700Schasinglulu
91*91f16700Schasinglulu    @property
92*91f16700Schasinglulu    def header(self):
93*91f16700Schasinglulu        return self._SpPkgHeader(
94*91f16700Schasinglulu                self.magic,
95*91f16700Schasinglulu                self.version,
96*91f16700Schasinglulu                self.pm_offset,
97*91f16700Schasinglulu                self.pm_size,
98*91f16700Schasinglulu                self.img_offset,
99*91f16700Schasinglulu                self.img_size)
100*91f16700Schasinglulu
101*91f16700Schasinglulu    @property
102*91f16700Schasinglulu    def header_size(self):
103*91f16700Schasinglulu        return len(self._SpPkgHeader._fields)
104*91f16700Schasinglulu
105*91f16700Schasinglulu    def generate(self, f_out : str):
106*91f16700Schasinglulu        with open(f_out, "wb+") as output:
107*91f16700Schasinglulu            for h in self.header:
108*91f16700Schasinglulu                to_write = h if type(h) is bytes else to_bytes(h)
109*91f16700Schasinglulu                output.write(to_write)
110*91f16700Schasinglulu            output.seek(self.pm_offset)
111*91f16700Schasinglulu            with open(self.pm_path, "rb") as pm:
112*91f16700Schasinglulu                copyfileobj(pm, output)
113*91f16700Schasinglulu            output.seek(self.img_offset)
114*91f16700Schasinglulu            with open(self.img_path, "rb") as img:
115*91f16700Schasinglulu                copyfileobj(img, output)
116*91f16700Schasinglulu
117*91f16700Schasingluludef Main():
118*91f16700Schasinglulu    parser = argparse.ArgumentParser()
119*91f16700Schasinglulu    parser.add_argument("-i", required=True,
120*91f16700Schasinglulu                        help="path to partition's image and manifest separated by a colon.")
121*91f16700Schasinglulu    parser.add_argument("--pm-offset", required=False, default=PM_OFFSET_DEFAULT,
122*91f16700Schasinglulu                        help="set partitition manifest offset.")
123*91f16700Schasinglulu    parser.add_argument("--img-offset", required=False, default=IMG_OFFSET_DEFAULT,
124*91f16700Schasinglulu                        help="set partition image offset.")
125*91f16700Schasinglulu    parser.add_argument("-o", required=True, help="set output file path.")
126*91f16700Schasinglulu    parser.add_argument("-v", required=False, action="store_true",
127*91f16700Schasinglulu                        help="print package information.")
128*91f16700Schasinglulu    args = parser.parse_args()
129*91f16700Schasinglulu
130*91f16700Schasinglulu    if not os.path.exists(os.path.dirname(args.o)):
131*91f16700Schasinglulu        raise Exception("Provide a valid output file path!\n")
132*91f16700Schasinglulu
133*91f16700Schasinglulu    image_path, manifest_path = split_dtb_bin(args.i)
134*91f16700Schasinglulu    pm_offset = int(args.pm_offset, 0)
135*91f16700Schasinglulu    img_offset = int(args.img_offset, 0)
136*91f16700Schasinglulu    pkg = SpPkg(manifest_path, image_path, pm_offset, img_offset)
137*91f16700Schasinglulu    pkg.generate(args.o)
138*91f16700Schasinglulu
139*91f16700Schasinglulu    if args.v is True:
140*91f16700Schasinglulu        print(pkg)
141*91f16700Schasinglulu
142*91f16700Schasinglulu    return 0
143*91f16700Schasinglulu
144*91f16700Schasingluluif __name__ == "__main__":
145*91f16700Schasinglulu    sys.exit(Main())
146