coprobin.py 5.6 KB

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