otp.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. class Main(App):
  33. def init(self):
  34. # SubParsers
  35. self.subparsers = self.parser.add_subparsers(help="sub-command help")
  36. # Generate All
  37. self.parser_generate_all = self.subparsers.add_parser(
  38. "generate", help="Generate OTP binary"
  39. )
  40. self._addFirstArgs(self.parser_generate_all)
  41. self._addSecondArgs(self.parser_generate_all)
  42. self.parser_generate_all.add_argument("file", help="Output file")
  43. self.parser_generate_all.set_defaults(func=self.generate_all)
  44. # Flash First
  45. self.parser_flash_first = self.subparsers.add_parser(
  46. "flash_first", help="Flash first block of OTP to device"
  47. )
  48. self._addArgsSWD(self.parser_flash_first)
  49. self._addFirstArgs(self.parser_flash_first)
  50. self.parser_flash_first.set_defaults(func=self.flash_first)
  51. # Flash Second
  52. self.parser_flash_second = self.subparsers.add_parser(
  53. "flash_second", help="Flash second block of OTP to device"
  54. )
  55. self._addArgsSWD(self.parser_flash_second)
  56. self._addSecondArgs(self.parser_flash_second)
  57. self.parser_flash_second.set_defaults(func=self.flash_second)
  58. # Flash All
  59. self.parser_flash_all = self.subparsers.add_parser(
  60. "flash_all", help="Flash OTP to device"
  61. )
  62. self._addArgsSWD(self.parser_flash_all)
  63. self._addFirstArgs(self.parser_flash_all)
  64. self._addSecondArgs(self.parser_flash_all)
  65. self.parser_flash_all.set_defaults(func=self.flash_all)
  66. # logging
  67. self.logger = logging.getLogger()
  68. self.timestamp = datetime.datetime.now().timestamp()
  69. def _addArgsSWD(self, parser):
  70. parser.add_argument(
  71. "--port", type=str, help="Port to connect: swd or usb1", default="swd"
  72. )
  73. parser.add_argument("--serial", type=str, help="ST-Link Serial Number")
  74. def _getCubeParams(self):
  75. return {
  76. "port": self.args.port,
  77. "serial": self.args.serial,
  78. }
  79. def _addFirstArgs(self, parser):
  80. parser.add_argument("--version", type=int, help="Version", required=True)
  81. parser.add_argument("--firmware", type=int, help="Firmware", required=True)
  82. parser.add_argument("--body", type=int, help="Body", required=True)
  83. parser.add_argument("--connect", type=int, help="Connect", required=True)
  84. parser.add_argument("--display", type=str, help="Display", required=True)
  85. def _addSecondArgs(self, parser):
  86. parser.add_argument("--color", type=str, help="Color", required=True)
  87. parser.add_argument("--region", type=str, help="Region", required=True)
  88. parser.add_argument("--name", type=str, help="Name", required=True)
  89. def _processFirstArgs(self):
  90. if self.args.display not in OTP_DISPLAYS:
  91. self.parser.error(f"Invalid display. Use one of {OTP_DISPLAYS.keys()}")
  92. self.args.display = OTP_DISPLAYS[self.args.display]
  93. def _processSecondArgs(self):
  94. if self.args.color not in OTP_COLORS:
  95. self.parser.error(f"Invalid color. Use one of {OTP_COLORS.keys()}")
  96. self.args.color = OTP_COLORS[self.args.color]
  97. if self.args.region not in OTP_REGIONS:
  98. self.parser.error(f"Invalid region. Use one of {OTP_REGIONS.keys()}")
  99. self.args.region = OTP_REGIONS[self.args.region]
  100. if len(self.args.name) > 8:
  101. self.parser.error("Name is too long. Max 8 symbols.")
  102. if re.match(r"^[a-zA-Z0-9.]+$", self.args.name) is None:
  103. self.parser.error(
  104. "Name contains incorrect symbols. Only a-zA-Z0-9 allowed."
  105. )
  106. def _packFirst(self):
  107. return struct.pack(
  108. "<" "HBBL" "BBBBBBH",
  109. OTP_MAGIC,
  110. OTP_VERSION,
  111. OTP_RESERVED,
  112. int(self.timestamp),
  113. self.args.version,
  114. self.args.firmware,
  115. self.args.body,
  116. self.args.connect,
  117. self.args.display,
  118. OTP_RESERVED,
  119. OTP_RESERVED,
  120. )
  121. def _packSecond(self):
  122. return struct.pack(
  123. "<" "BBHL" "8s",
  124. self.args.color,
  125. self.args.region,
  126. OTP_RESERVED,
  127. OTP_RESERVED,
  128. self.args.name.encode("ascii"),
  129. )
  130. def generate_all(self):
  131. self.logger.info(f"Generating OTP")
  132. self._processFirstArgs()
  133. self._processSecondArgs()
  134. with open(f"{self.args.file}_first.bin", "wb") as file:
  135. file.write(self._packFirst())
  136. with open(f"{self.args.file}_second.bin", "wb") as file:
  137. file.write(self._packSecond())
  138. self.logger.info(
  139. f"Generated files: {self.args.file}_first.bin and {self.args.file}_second.bin"
  140. )
  141. return 0
  142. def flash_first(self):
  143. self.logger.info(f"Flashing first block of OTP")
  144. self._processFirstArgs()
  145. filename = f"otp_unknown_first_{self.timestamp}.bin"
  146. try:
  147. self.logger.info(f"Packing binary data")
  148. with open(filename, "wb") as file:
  149. file.write(self._packFirst())
  150. self.logger.info(f"Flashing OTP")
  151. cp = CubeProgrammer(self._getCubeParams())
  152. cp.flashBin("0x1FFF7000", filename)
  153. cp.resetTarget()
  154. self.logger.info(f"Flashed Successfully")
  155. os.remove(filename)
  156. except Exception as e:
  157. self.logger.exception(e)
  158. return 1
  159. return 0
  160. def flash_second(self):
  161. self.logger.info(f"Flashing second block of OTP")
  162. self._processSecondArgs()
  163. filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
  164. try:
  165. self.logger.info(f"Packing binary data")
  166. with open(filename, "wb") as file:
  167. file.write(self._packSecond())
  168. self.logger.info(f"Flashing OTP")
  169. cp = CubeProgrammer(self._getCubeParams())
  170. cp.flashBin("0x1FFF7010", filename)
  171. cp.resetTarget()
  172. self.logger.info(f"Flashed Successfully")
  173. os.remove(filename)
  174. except Exception as e:
  175. self.logger.exception(e)
  176. return 1
  177. return 0
  178. def flash_all(self):
  179. self.logger.info(f"Flashing OTP")
  180. self._processFirstArgs()
  181. self._processSecondArgs()
  182. filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
  183. try:
  184. self.logger.info(f"Packing binary data")
  185. with open(filename, "wb") as file:
  186. file.write(self._packFirst())
  187. file.write(self._packSecond())
  188. self.logger.info(f"Flashing OTP")
  189. cp = CubeProgrammer(self._getCubeParams())
  190. cp.flashBin("0x1FFF7000", filename)
  191. cp.resetTarget()
  192. self.logger.info(f"Flashed Successfully")
  193. os.remove(filename)
  194. except Exception as e:
  195. self.logger.exception(e)
  196. return 1
  197. return 0
  198. if __name__ == "__main__":
  199. Main()()