flipperversion.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. from dataclasses import dataclass, field
  2. from typing import Dict, Optional
  3. import gdb
  4. # Must match FuriHalRtcRegisterVersion index in FuriHalRtcRegister enum
  5. RTC_BACKUP_VERSION_REGISTER_IDX = 0x2
  6. RTC_BASE = 0x40002800
  7. RTC_BACKUP_BASE = RTC_BASE + 0x50
  8. VERSION_REGISTER_ADDRESS = RTC_BACKUP_BASE + RTC_BACKUP_VERSION_REGISTER_IDX * 4
  9. VERSION_STRUCT_MAGIC = 0xBE40
  10. @dataclass
  11. class VersionData:
  12. git_hash: str
  13. git_branch: str
  14. build_date: str
  15. version: str
  16. target: int
  17. build_is_dirty: bool
  18. extra: Optional[Dict[str, str]] = field(default_factory=dict)
  19. class VersionLoader:
  20. def __init__(self, version_ptr):
  21. self.version_ptr = version_ptr
  22. self._cstr_type = gdb.lookup_type("char").pointer()
  23. self._uint_type = gdb.lookup_type("unsigned int")
  24. version_signature = version_ptr.dereference().cast(self._uint_type)
  25. is_versioned = (version_signature & (0xFFFF)) == VERSION_STRUCT_MAGIC
  26. if is_versioned:
  27. self._version_data = self.load_versioned(
  28. major=version_signature >> 16 & 0xFF,
  29. minor=version_signature >> 24 & 0xFF,
  30. )
  31. else:
  32. self._version_data = self.load_unversioned()
  33. @property
  34. def version(self) -> VersionData:
  35. return self._version_data
  36. def load_versioned(self, major, minor):
  37. if major != 1:
  38. raise ValueError("Unsupported version struct major version")
  39. # Struct version 1.0
  40. extra_data = int(self.version_ptr[5].cast(self._uint_type))
  41. return VersionData(
  42. git_hash=self.version_ptr[1].cast(self._cstr_type).string(),
  43. git_branch=self.version_ptr[2].cast(self._cstr_type).string(),
  44. build_date=self.version_ptr[3].cast(self._cstr_type).string(),
  45. version=self.version_ptr[4].cast(self._cstr_type).string(),
  46. target=extra_data & 0xF,
  47. build_is_dirty=bool((extra_data >> 8) & 0xF),
  48. )
  49. def load_unversioned(self):
  50. """Parse an early version of the version struct."""
  51. extra_data = int(self.version_ptr[5].cast(self._uint_type))
  52. return VersionData(
  53. git_hash=self.version_ptr[0].cast(self._cstr_type).string(),
  54. git_branch=self.version_ptr[1].cast(self._cstr_type).string(),
  55. # branch number is #2, but we don't care about it
  56. build_date=self.version_ptr[3].cast(self._cstr_type).string(),
  57. version=self.version_ptr[4].cast(self._cstr_type).string(),
  58. target=extra_data & 0xF,
  59. build_is_dirty=bool((extra_data >> 8) & 0xF),
  60. )
  61. class FlipperFwVersion(gdb.Command):
  62. """Print the version of Flipper's firmware."""
  63. def __init__(self):
  64. super(FlipperFwVersion, self).__init__("fw-version", gdb.COMMAND_USER)
  65. def invoke(self, arg, from_tty):
  66. void_ptr_type = gdb.lookup_type("void").pointer().pointer()
  67. version_ptr_ptr = gdb.Value(VERSION_REGISTER_ADDRESS).cast(void_ptr_type)
  68. if not version_ptr_ptr:
  69. print("RTC version register is NULL")
  70. return
  71. version_ptr = version_ptr_ptr.dereference()
  72. if not version_ptr:
  73. print("Pointer to version struct is NULL")
  74. return
  75. version_struct = version_ptr.cast(void_ptr_type)
  76. v = VersionLoader(version_struct)
  77. print("Firmware version on attached Flipper:")
  78. print(f"\tVersion: {v.version.version}")
  79. print(f"\tBuilt on: {v.version.build_date}")
  80. print(f"\tGit branch: {v.version.git_branch}")
  81. print(f"\tGit commit: {v.version.git_hash}")
  82. print(f"\tDirty: {v.version.build_is_dirty}")
  83. print(f"\tHW Target: {v.version.target}")
  84. FlipperFwVersion()