coprobin.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import struct
  2. import math
  3. import os, os.path
  4. import sys
  5. # From STM32CubeWB\Middlewares\ST\STM32_WPAN\interface\patterns\ble_thread\shci\shci.h
  6. __STACK_TYPE_CODES = {
  7. "BLE_FULL": 0x01,
  8. "BLE_HCI": 0x02,
  9. "BLE_LIGHT": 0x03,
  10. "BLE_BEACON": 0x04,
  11. "BLE_BASIC": 0x05,
  12. "BLE_FULL_EXT_ADV": 0x06,
  13. "BLE_HCI_EXT_ADV": 0x07,
  14. "THREAD_FTD": 0x10,
  15. "THREAD_MTD": 0x11,
  16. "ZIGBEE_FFD": 0x30,
  17. "ZIGBEE_RFD": 0x31,
  18. "MAC": 0x40,
  19. "BLE_THREAD_FTD_STATIC": 0x50,
  20. "BLE_THREAD_FTD_DYAMIC": 0x51,
  21. "802154_LLD_TESTS": 0x60,
  22. "802154_PHY_VALID": 0x61,
  23. "BLE_PHY_VALID": 0x62,
  24. "BLE_LLD_TESTS": 0x63,
  25. "BLE_RLV": 0x64,
  26. "802154_RLV": 0x65,
  27. "BLE_ZIGBEE_FFD_STATIC": 0x70,
  28. "BLE_ZIGBEE_RFD_STATIC": 0x71,
  29. "BLE_ZIGBEE_FFD_DYNAMIC": 0x78,
  30. "BLE_ZIGBEE_RFD_DYNAMIC": 0x79,
  31. "RLV": 0x80,
  32. "BLE_MAC_STATIC": 0x90,
  33. }
  34. class CoproException(ValueError):
  35. pass
  36. # Formats based on AN5185
  37. class CoproFooterBase:
  38. SIG_BIN_SIZE = 5 * 4
  39. _SIG_BIN_COMMON_SIZE = 2 * 4
  40. def get_version(self):
  41. return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})"
  42. def get_details(self):
  43. raise CoproException("Not implemented")
  44. def __init__(self, raw: bytes):
  45. if len(raw) != self.SIG_BIN_SIZE:
  46. raise CoproException("Invalid footer size")
  47. sig_common_part = raw[-self._SIG_BIN_COMMON_SIZE :]
  48. parts = struct.unpack("BBBBI", sig_common_part)
  49. self.version_major = parts[3]
  50. self.version_minor = parts[2]
  51. self.version_sub = parts[1]
  52. # AN5185 mismatch: swapping byte halves
  53. self.version_build = parts[0] & 0x0F
  54. self.version_branch = (parts[0] & 0xF0) >> 4
  55. self.magic = parts[4]
  56. class CoproFusFooter(CoproFooterBase):
  57. FUS_MAGIC_IMG_STACK = 0x23372991
  58. FUS_MAGIC_IMG_FUS = 0x32279221
  59. FUS_MAGIC_IMG_OTHER = 0x42769811
  60. FUS_BASE = 0x80F4000
  61. FLASH_PAGE_SIZE = 4 * 1024
  62. def __init__(self, raw: bytes):
  63. super().__init__(raw)
  64. if self.magic not in (
  65. self.FUS_MAGIC_IMG_OTHER,
  66. self.FUS_MAGIC_IMG_FUS,
  67. self.FUS_MAGIC_IMG_STACK,
  68. ):
  69. raise CoproException(f"Invalid FUS img magic {self.magic:x}")
  70. own_data = raw[: -self._SIG_BIN_COMMON_SIZE]
  71. parts = struct.unpack("IIBBBB", own_data)
  72. self.info1 = parts[0]
  73. self.info2 = parts[1]
  74. self.sram2b_1ks = parts[5]
  75. self.sram2a_1ks = parts[4]
  76. self.flash_4ks = parts[2]
  77. def get_details(self):
  78. return f"SRAM2b={self.sram2b_1ks}k SRAM2a={self.sram2a_1ks}k flash={self.flash_4ks}p"
  79. def is_stack(self):
  80. return self.magic == self.FUS_MAGIC_IMG_STACK
  81. def get_flash_pages(self, fullsize):
  82. return math.ceil(fullsize / self.FLASH_PAGE_SIZE)
  83. def get_flash_base(self, fullsize):
  84. if not self.is_stack():
  85. raise CoproException("Not a stack image")
  86. return self.FUS_BASE - self.get_flash_pages(fullsize) * self.FLASH_PAGE_SIZE
  87. class CoproSigFooter(CoproFooterBase):
  88. SIG_MAGIC_ST = 0xD3A12C5E
  89. SIG_MAGIC_CUSTOMER = 0xE2B51D4A
  90. def __init__(self, raw: bytes):
  91. super().__init__(raw)
  92. if self.magic not in (self.SIG_MAGIC_ST, self.SIG_MAGIC_CUSTOMER):
  93. raise CoproException(f"Invalid FUS img magic {self.magic:x}")
  94. own_data = raw[: -self._SIG_BIN_COMMON_SIZE]
  95. parts = struct.unpack("IIBBH", own_data)
  96. self.reserved_1 = parts[0]
  97. self.reserved_2 = parts[1]
  98. self.size = parts[2]
  99. self.source = parts[3]
  100. self.reserved_34 = parts[4]
  101. def get_details(self):
  102. return f"Signature Src {self.source:x} size {self.size:x}"
  103. class CoproBinary:
  104. def __init__(self, binary_path):
  105. self.binary_path = binary_path
  106. self.img_sig_footer = None
  107. self.img_sig = None
  108. self.binary_size = -1
  109. self._load()
  110. def _load(self):
  111. with open(self.binary_path, "rb") as fin:
  112. whole_file = fin.read()
  113. self.binary_size = len(whole_file)
  114. img_sig_footer_bin = whole_file[-CoproFooterBase.SIG_BIN_SIZE :]
  115. self.img_sig_footer = CoproSigFooter(img_sig_footer_bin)
  116. img_sig_size = self.img_sig_footer.size + CoproSigFooter.SIG_BIN_SIZE
  117. img_sig_bin = whole_file[
  118. -(img_sig_size + CoproFusFooter.SIG_BIN_SIZE) : -img_sig_size
  119. ]
  120. self.img_sig = CoproFusFooter(img_sig_bin)
  121. def is_valid(self):
  122. return self.img_sig_footer is not None and self.img_sig is not None
  123. def is_stack(self):
  124. return self.img_sig and self.img_sig.is_stack()
  125. def get_flash_load_addr(self):
  126. if not self.is_stack():
  127. raise CoproException("Not a stack image")
  128. return self.img_sig.get_flash_base(self.binary_size)
  129. def get_stack_type(typestr: str):
  130. stack_code = __STACK_TYPE_CODES.get(typestr.upper(), None)
  131. if stack_code is None:
  132. raise CoproException(f"Unknown stack type {typestr}. See shci.h")
  133. return stack_code
  134. def _load_bin(binary_path: str):
  135. print(binary_path)
  136. copro_bin = CoproBinary(binary_path)
  137. print(copro_bin.img_sig.get_version())
  138. if copro_bin.img_sig.is_stack():
  139. print(f"\t>> FLASH AT {copro_bin.get_flash_load_addr():X}\n")
  140. def main():
  141. coprodir = (
  142. sys.argv[1]
  143. if len(sys.argv) > 1
  144. else "../../../lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
  145. )
  146. for fn in os.listdir(coprodir):
  147. if not fn.endswith(".bin"):
  148. continue
  149. _load_bin(os.path.join(coprodir, fn))
  150. if __name__ == "__main__":
  151. main()