#!/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())