otp.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. #!/usr/bin/env python3
  2. import logging
  3. import argparse
  4. import subprocess
  5. import os
  6. import sys
  7. import re
  8. import struct
  9. import datetime
  10. OTP_MAGIC = 0xBABE
  11. OTP_VERSION = 0x02
  12. OTP_RESERVED = 0x00
  13. OTP_COLORS = {
  14. "unknown": 0x00,
  15. "black": 0x01,
  16. "white": 0x02,
  17. }
  18. OTP_REGIONS = {
  19. "unknown": 0x00,
  20. "eu_ru": 0x01,
  21. "us_ca_au": 0x02,
  22. "jp": 0x03,
  23. "world": 0x04,
  24. }
  25. OTP_DISPLAYS = {
  26. "unknown": 0x00,
  27. "erc": 0x01,
  28. "mgg": 0x02,
  29. }
  30. from flipper.app import App
  31. from flipper.cube import CubeProgrammer
  32. from flipper.utils.programmer_openocd import OpenOCDProgrammer
  33. class Main(App):
  34. def init(self):
  35. # SubParsers
  36. self.subparsers = self.parser.add_subparsers(help="sub-command help")
  37. # Generate All
  38. self.parser_generate_all = self.subparsers.add_parser(
  39. "generate", help="Generate OTP binary"
  40. )
  41. self._addFirstArgs(self.parser_generate_all)
  42. self._addSecondArgs(self.parser_generate_all)
  43. self.parser_generate_all.add_argument("file", help="Output file")
  44. self.parser_generate_all.set_defaults(func=self.generate_all)
  45. # Flash First
  46. self.parser_flash_first = self.subparsers.add_parser(
  47. "flash_first", help="Flash first block of OTP to device"
  48. )
  49. self._addArgsOpenOCD(self.parser_flash_first)
  50. self._addFirstArgs(self.parser_flash_first)
  51. self.parser_flash_first.set_defaults(func=self.flash_first)
  52. # Flash Second
  53. self.parser_flash_second = self.subparsers.add_parser(
  54. "flash_second", help="Flash second block of OTP to device"
  55. )
  56. self._addArgsOpenOCD(self.parser_flash_second)
  57. self._addSecondArgs(self.parser_flash_second)
  58. self.parser_flash_second.set_defaults(func=self.flash_second)
  59. # Flash All
  60. self.parser_flash_all = self.subparsers.add_parser(
  61. "flash_all", help="Flash OTP to device"
  62. )
  63. self._addArgsOpenOCD(self.parser_flash_all)
  64. self._addFirstArgs(self.parser_flash_all)
  65. self._addSecondArgs(self.parser_flash_all)
  66. self.parser_flash_all.set_defaults(func=self.flash_all)
  67. # logging
  68. self.logger = logging.getLogger()
  69. self.timestamp = datetime.datetime.now().timestamp()
  70. def _addArgsOpenOCD(self, parser):
  71. parser.add_argument(
  72. "--port-base", type=int, help="OpenOCD port base", default=3333
  73. )
  74. parser.add_argument(
  75. "--interface",
  76. type=str,
  77. help="OpenOCD interface",
  78. default="interface/cmsis-dap.cfg",
  79. )
  80. parser.add_argument(
  81. "--serial", type=str, help="OpenOCD interface serial number"
  82. )
  83. def _addFirstArgs(self, parser):
  84. parser.add_argument("--version", type=int, help="Version", required=True)
  85. parser.add_argument("--firmware", type=int, help="Firmware", required=True)
  86. parser.add_argument("--body", type=int, help="Body", required=True)
  87. parser.add_argument("--connect", type=int, help="Connect", required=True)
  88. parser.add_argument("--display", type=str, help="Display", required=True)
  89. def _addSecondArgs(self, parser):
  90. parser.add_argument("--color", type=str, help="Color", required=True)
  91. parser.add_argument("--region", type=str, help="Region", required=True)
  92. parser.add_argument("--name", type=str, help="Name", required=True)
  93. def _processFirstArgs(self):
  94. if self.args.display not in OTP_DISPLAYS:
  95. self.parser.error(f"Invalid display. Use one of {OTP_DISPLAYS.keys()}")
  96. self.args.display = OTP_DISPLAYS[self.args.display]
  97. def _processSecondArgs(self):
  98. if self.args.color not in OTP_COLORS:
  99. self.parser.error(f"Invalid color. Use one of {OTP_COLORS.keys()}")
  100. self.args.color = OTP_COLORS[self.args.color]
  101. if self.args.region not in OTP_REGIONS:
  102. self.parser.error(f"Invalid region. Use one of {OTP_REGIONS.keys()}")
  103. self.args.region = OTP_REGIONS[self.args.region]
  104. if len(self.args.name) > 8:
  105. self.parser.error("Name is too long. Max 8 symbols.")
  106. if re.match(r"^[a-zA-Z0-9.]+$", self.args.name) is None:
  107. self.parser.error(
  108. "Name contains incorrect symbols. Only a-zA-Z0-9 allowed."
  109. )
  110. def _packFirst(self):
  111. return struct.pack(
  112. "<" "HBBL" "BBBBBBH",
  113. OTP_MAGIC,
  114. OTP_VERSION,
  115. OTP_RESERVED,
  116. int(self.timestamp),
  117. self.args.version,
  118. self.args.firmware,
  119. self.args.body,
  120. self.args.connect,
  121. self.args.display,
  122. OTP_RESERVED,
  123. OTP_RESERVED,
  124. )
  125. def _packSecond(self):
  126. return struct.pack(
  127. "<" "BBHL" "8s",
  128. self.args.color,
  129. self.args.region,
  130. OTP_RESERVED,
  131. OTP_RESERVED,
  132. self.args.name.encode("ascii"),
  133. )
  134. def generate_all(self):
  135. self.logger.info(f"Generating OTP")
  136. self._processFirstArgs()
  137. self._processSecondArgs()
  138. with open(f"{self.args.file}_first.bin", "wb") as file:
  139. file.write(self._packFirst())
  140. with open(f"{self.args.file}_second.bin", "wb") as file:
  141. file.write(self._packSecond())
  142. self.logger.info(
  143. f"Generated files: {self.args.file}_first.bin and {self.args.file}_second.bin"
  144. )
  145. return 0
  146. def flash_first(self):
  147. self.logger.info(f"Flashing first block of OTP")
  148. self._processFirstArgs()
  149. filename = f"otp_unknown_first_{self.timestamp}.bin"
  150. try:
  151. self.logger.info(f"Packing binary data")
  152. with open(filename, "wb") as file:
  153. file.write(self._packFirst())
  154. self.logger.info(f"Flashing OTP")
  155. openocd = OpenOCDProgrammer(
  156. self.args.interface,
  157. self.args.port_base,
  158. self.args.serial,
  159. )
  160. if not openocd.otp_write(0x1FFF7000, filename):
  161. raise Exception("Failed to flash OTP")
  162. self.logger.info(f"Flashed Successfully")
  163. except Exception as e:
  164. self.logger.exception(e)
  165. return 1
  166. finally:
  167. os.remove(filename)
  168. return 0
  169. def flash_second(self):
  170. self.logger.info(f"Flashing second block of OTP")
  171. self._processSecondArgs()
  172. filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
  173. try:
  174. self.logger.info(f"Packing binary data")
  175. with open(filename, "wb") as file:
  176. file.write(self._packSecond())
  177. self.logger.info(f"Flashing OTP")
  178. openocd = OpenOCDProgrammer(
  179. self.args.interface,
  180. self.args.port_base,
  181. self.args.serial,
  182. )
  183. if not openocd.otp_write(0x1FFF7010, filename):
  184. raise Exception("Failed to flash OTP")
  185. self.logger.info(f"Flashed Successfully")
  186. except Exception as e:
  187. self.logger.exception(e)
  188. return 1
  189. finally:
  190. os.remove(filename)
  191. return 0
  192. def flash_all(self):
  193. self.logger.info(f"Flashing OTP")
  194. self._processFirstArgs()
  195. self._processSecondArgs()
  196. filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
  197. try:
  198. self.logger.info(f"Packing binary data")
  199. with open(filename, "wb") as file:
  200. file.write(self._packFirst())
  201. file.write(self._packSecond())
  202. self.logger.info(f"Flashing OTP")
  203. openocd = OpenOCDProgrammer(
  204. self.args.interface,
  205. self.args.port_base,
  206. self.args.serial,
  207. )
  208. if not openocd.otp_write(0x1FFF7000, filename):
  209. raise Exception("Failed to flash OTP")
  210. self.logger.info(f"Flashed Successfully")
  211. except Exception as e:
  212. self.logger.exception(e)
  213. return 1
  214. finally:
  215. os.remove(filename)
  216. return 0
  217. if __name__ == "__main__":
  218. Main()()