sptool.py 4.6 KB

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