svd_gdb.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. #!/usr/bin/env python
  2. """
  3. This file is part of PyCortexMDebug
  4. PyCortexMDebug is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. PyCortexMDebug is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
  14. """
  15. import gdb
  16. import re
  17. import math
  18. import sys
  19. import struct
  20. import pkg_resources
  21. import fnmatch
  22. import traceback
  23. from .svd import SVDFile
  24. BITS_TO_UNPACK_FORMAT = {
  25. 8: "B",
  26. 16: "H",
  27. 32: "I",
  28. }
  29. class LoadSVD(gdb.Command):
  30. """A command to load an SVD file and to create the command for inspecting
  31. that object
  32. """
  33. def __init__(self):
  34. self.vendors = {}
  35. try:
  36. vendor_names = pkg_resources.resource_listdir("cmsis_svd", "data")
  37. for vendor in vendor_names:
  38. fnames = pkg_resources.resource_listdir(
  39. "cmsis_svd", "data/{}".format(vendor)
  40. )
  41. self.vendors[vendor] = [
  42. fname for fname in fnames if fname.lower().endswith(".svd")
  43. ]
  44. except:
  45. pass
  46. if len(self.vendors) > 0:
  47. gdb.Command.__init__(self, "svd_load", gdb.COMMAND_USER)
  48. else:
  49. gdb.Command.__init__(
  50. self, "svd_load", gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME
  51. )
  52. def complete(self, text, word):
  53. args = gdb.string_to_argv(text)
  54. num_args = len(args)
  55. if text.endswith(" "):
  56. num_args += 1
  57. if not text:
  58. num_args = 1
  59. # "svd_load <tab>" or "svd_load ST<tab>"
  60. if num_args == 1:
  61. prefix = word.lower()
  62. return [
  63. vendor for vendor in self.vendors if vendor.lower().startswith(prefix)
  64. ]
  65. # "svd_load STMicro<tab>" or "svd_load STMicro STM32F1<tab>"
  66. elif num_args == 2 and args[0] in self.vendors:
  67. prefix = word.lower()
  68. filenames = self.vendors[args[0]]
  69. return [fname for fname in filenames if fname.lower().startswith(prefix)]
  70. return gdb.COMPLETE_NONE
  71. @staticmethod
  72. def invoke(args, from_tty):
  73. args = gdb.string_to_argv(args)
  74. argc = len(args)
  75. if argc == 1:
  76. gdb.write("Loading SVD file {}...\n".format(args[0]))
  77. f = args[0]
  78. elif argc == 2:
  79. gdb.write("Loading SVD file {}/{}...\n".format(args[0], args[1]))
  80. f = pkg_resources.resource_filename(
  81. "cmsis_svd", "data/{}/{}".format(args[0], args[1])
  82. )
  83. else:
  84. raise gdb.GdbError(
  85. "Usage: svd_load <vendor> <device.svd> or svd_load <path/to/filename.svd>\n"
  86. )
  87. try:
  88. SVD(SVDFile(f))
  89. except Exception as e:
  90. traceback.print_exc()
  91. raise gdb.GdbError("Could not load SVD file {} : {}...\n".format(f, e))
  92. class SVD(gdb.Command):
  93. """The CMSIS SVD (System View Description) inspector command
  94. This allows easy access to all peripheral registers supported by the system
  95. in the GDB debug environment
  96. """
  97. def __init__(self, svd_file):
  98. gdb.Command.__init__(self, "svd", gdb.COMMAND_DATA)
  99. self.svd_file = svd_file
  100. def _print_registers(self, container_name, form, registers):
  101. if len(registers) == 0:
  102. return
  103. try:
  104. regs_iter = registers.itervalues()
  105. except AttributeError:
  106. regs_iter = registers.values()
  107. gdb.write("Registers in %s:\n" % container_name)
  108. reg_list = []
  109. for r in regs_iter:
  110. if r.readable():
  111. try:
  112. data = self.read(r.address(), r.size)
  113. data = self.format(data, form, r.size)
  114. if form == "a":
  115. data += (
  116. " <"
  117. + re.sub(
  118. r"\s+",
  119. " ",
  120. gdb.execute(
  121. "info symbol {}".format(data), True, True
  122. ).strip(),
  123. )
  124. + ">"
  125. )
  126. except gdb.MemoryError:
  127. data = "(error reading)"
  128. else:
  129. data = "(not readable)"
  130. desc = re.sub(r"\s+", " ", r.description)
  131. reg_list.append((r.name, data, desc))
  132. column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
  133. column2_width = max(len(reg[1]) for reg in reg_list)
  134. for reg in reg_list:
  135. gdb.write(
  136. "\t{}:{}{}".format(
  137. reg[0],
  138. "".ljust(column1_width - len(reg[0])),
  139. reg[1].rjust(column2_width),
  140. )
  141. )
  142. if reg[2] != reg[0]:
  143. gdb.write(" {}".format(reg[2]))
  144. gdb.write("\n")
  145. def _print_register_fields(self, container_name, form, register):
  146. gdb.write("Fields in {}:\n".format(container_name))
  147. fields = register.fields
  148. if not register.readable():
  149. data = 0
  150. else:
  151. data = self.read(register.address(), register.size)
  152. field_list = []
  153. try:
  154. fields_iter = fields.itervalues()
  155. except AttributeError:
  156. fields_iter = fields.values()
  157. for f in fields_iter:
  158. desc = re.sub(r"\s+", " ", f.description)
  159. if register.readable():
  160. val = data >> f.offset
  161. val &= (1 << f.width) - 1
  162. if f.enum:
  163. if val in f.enum:
  164. desc = f.enum[val][1] + " - " + desc
  165. val = f.enum[val][0]
  166. else:
  167. val = "Invalid enum value: " + self.format(val, form, f.width)
  168. else:
  169. val = self.format(val, form, f.width)
  170. else:
  171. val = "(not readable)"
  172. field_list.append((f.name, val, desc))
  173. column1_width = max(len(field[0]) for field in field_list) + 2 # padding
  174. column2_width = max(len(field[1]) for field in field_list) # padding
  175. for field in field_list:
  176. gdb.write(
  177. "\t{}:{}{}".format(
  178. field[0],
  179. "".ljust(column1_width - len(field[0])),
  180. field[1].rjust(column2_width),
  181. )
  182. )
  183. if field[2] != field[0]:
  184. gdb.write(" {}".format(field[2]))
  185. gdb.write("\n")
  186. def invoke(self, args, from_tty):
  187. s = str(args).split(" ")
  188. form = ""
  189. if s[0] and s[0][0] == "/":
  190. if len(s[0]) == 1:
  191. gdb.write("Incorrect format\n")
  192. return
  193. else:
  194. form = s[0][1:]
  195. if len(s) == 1:
  196. return
  197. s = s[1:]
  198. if s[0].lower() == "help":
  199. gdb.write("Usage:\n")
  200. gdb.write("=========\n")
  201. gdb.write("svd:\n")
  202. gdb.write("\tList available peripherals\n")
  203. gdb.write("svd [peripheral_name]:\n")
  204. gdb.write("\tDisplay all registers pertaining to that peripheral\n")
  205. gdb.write("svd [peripheral_name] [register_name]:\n")
  206. gdb.write("\tDisplay the fields in that register\n")
  207. gdb.write("svd/[format_character] ...\n")
  208. gdb.write("\tFormat values using that character\n")
  209. gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n")
  210. gdb.write("\n")
  211. gdb.write(
  212. "Both prefix matching and case-insensitive matching is supported for peripherals, registers, clusters and fields.\n"
  213. )
  214. return
  215. if not len(s[0]):
  216. gdb.write("Available Peripherals:\n")
  217. try:
  218. peripherals = self.svd_file.peripherals.itervalues()
  219. except AttributeError:
  220. peripherals = self.svd_file.peripherals.values()
  221. column_width = max(len(p.name) for p in peripherals) + 2 # padding
  222. try:
  223. peripherals = self.svd_file.peripherals.itervalues()
  224. except AttributeError:
  225. peripherals = self.svd_file.peripherals.values()
  226. for p in peripherals:
  227. desc = re.sub(r"\s+", " ", p.description)
  228. gdb.write(
  229. "\t{}:{}{}\n".format(
  230. p.name, "".ljust(column_width - len(p.name)), desc
  231. )
  232. )
  233. return
  234. def warn_if_ambiguous(smart_dict, key):
  235. if smart_dict.is_ambiguous(key):
  236. gdb.write(
  237. "Warning: {} could prefix match any of: {}\n".format(
  238. key, ", ".join(smart_dict.prefix_match_iter(key))
  239. )
  240. )
  241. registers = None
  242. if len(s) >= 1:
  243. peripheral_name = s[0]
  244. if peripheral_name not in self.svd_file.peripherals:
  245. gdb.write("Peripheral {} does not exist!\n".format(s[0]))
  246. return
  247. warn_if_ambiguous(self.svd_file.peripherals, peripheral_name)
  248. peripheral = self.svd_file.peripherals[peripheral_name]
  249. if len(s) == 1:
  250. self._print_registers(peripheral.name, form, peripheral.registers)
  251. if len(peripheral.clusters) > 0:
  252. try:
  253. clusters_iter = peripheral.clusters.itervalues()
  254. except AttributeError:
  255. clusters_iter = peripheral.clusters.values()
  256. gdb.write("Clusters in %s:\n" % peripheral.name)
  257. reg_list = []
  258. for r in clusters_iter:
  259. desc = re.sub(r"\s+", " ", r.description)
  260. reg_list.append((r.name, "", desc))
  261. column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
  262. column2_width = max(len(reg[1]) for reg in reg_list)
  263. for reg in reg_list:
  264. gdb.write(
  265. "\t{}:{}{}".format(
  266. reg[0],
  267. "".ljust(column1_width - len(reg[0])),
  268. reg[1].rjust(column2_width),
  269. )
  270. )
  271. if reg[2] != reg[0]:
  272. gdb.write(" {}".format(reg[2]))
  273. gdb.write("\n")
  274. return
  275. cluster = None
  276. if len(s) == 2:
  277. if s[1] in peripheral.clusters:
  278. warn_if_ambiguous(peripheral.clusters, s[1])
  279. cluster = peripheral.clusters[s[1]]
  280. container = peripheral.name + " > " + cluster.name
  281. self._print_registers(container, form, cluster.registers)
  282. elif s[1] in peripheral.registers:
  283. warn_if_ambiguous(peripheral.registers, s[1])
  284. register = peripheral.registers[s[1]]
  285. container = peripheral.name + " > " + register.name
  286. self._print_register_fields(container, form, register)
  287. else:
  288. found = False
  289. for key in fnmatch.filter(peripheral.registers.keys(), s[1]):
  290. register = peripheral.registers[key]
  291. container = peripheral.name + " > " + register.name
  292. self._print_register_fields(container, form, register)
  293. found = True
  294. if not found:
  295. gdb.write(
  296. "Register/cluster {} in peripheral {} does not exist!\n".format(
  297. s[1], peripheral.name
  298. )
  299. )
  300. return
  301. if len(s) == 3:
  302. if s[1] not in peripheral.clusters:
  303. gdb.write(
  304. "Cluster {} in peripheral {} does not exist!\n".format(
  305. s[1], peripheral.name
  306. )
  307. )
  308. return
  309. warn_if_ambiguous(peripheral.clusters, s[1])
  310. cluster = peripheral.clusters[s[1]]
  311. if s[2] not in cluster.registers:
  312. gdb.write(
  313. "Register {} in cluster {} in peripheral {} does not exist!\n".format(
  314. s[2], cluster.name, peripheral.name
  315. )
  316. )
  317. return
  318. warn_if_ambiguous(cluster.registers, s[2])
  319. register = cluster.registers[s[2]]
  320. container = " > ".join([peripheral.name, cluster.name, register.name])
  321. self._print_register_fields(container, form, register)
  322. return
  323. if len(s) == 4:
  324. if s[1] not in peripheral.registers:
  325. gdb.write(
  326. "Register {} in peripheral {} does not exist!\n".format(
  327. s[1], peripheral.name
  328. )
  329. )
  330. return
  331. warn_if_ambiguous(peripheral.registers, s[1])
  332. reg = peripheral.registers[s[1]]
  333. if s[2] not in reg.fields:
  334. gdb.write(
  335. "Field {} in register {} in peripheral {} does not exist!\n".format(
  336. s[2], reg.name, peripheral.name
  337. )
  338. )
  339. return
  340. warn_if_ambiguous(reg.fields, s[2])
  341. field = reg.fields[s[2]]
  342. if not field.writable() or not reg.writable():
  343. gdb.write(
  344. "Field {} in register {} in peripheral {} is read-only!\n".format(
  345. field.name, reg.name, peripheral.name
  346. )
  347. )
  348. return
  349. try:
  350. val = int(s[3], 0)
  351. except ValueError:
  352. gdb.write(
  353. "{} is not a valid number! You can prefix numbers with 0x for hex, 0b for binary, or any python "
  354. "int literal\n".format(s[3])
  355. )
  356. return
  357. if val >= 1 << field.width or val < 0:
  358. gdb.write(
  359. "{} not a valid number for a field with width {}!\n".format(
  360. val, field.width
  361. )
  362. )
  363. return
  364. if not reg.readable():
  365. data = 0
  366. else:
  367. data = self.read(reg.address(), reg.size)
  368. data &= ~(((1 << field.width) - 1) << field.offset)
  369. data |= val << field.offset
  370. self.write(reg.address(), data, reg.size)
  371. return
  372. gdb.write("Unknown input\n")
  373. def complete(self, text, word):
  374. """Perform tab-completion for the command"""
  375. text = str(text)
  376. s = text.split(" ")
  377. # Deal with the possibility of a '/' parameter
  378. if s[0] and s[0][0] == "/":
  379. if len(s) > 1:
  380. s = s[1:]
  381. else:
  382. return [] # completion after e.g. "svd/x" but before trailing space
  383. if len(s) == 1:
  384. return list(self.svd_file.peripherals.prefix_match_iter(s[0]))
  385. if len(s) == 2:
  386. reg = s[1].upper()
  387. if len(reg) and reg[0] == "&":
  388. reg = reg[1:]
  389. if s[0] not in self.svd_file.peripherals:
  390. return []
  391. per = self.svd_file.peripherals[s[0]]
  392. return list(per.registers.prefix_match_iter(s[1]))
  393. return []
  394. @staticmethod
  395. def read(address, bits=32):
  396. """Read from memory and return an integer"""
  397. value = gdb.selected_inferior().read_memory(address, bits / 8)
  398. unpack_format = "I"
  399. if bits in BITS_TO_UNPACK_FORMAT:
  400. unpack_format = BITS_TO_UNPACK_FORMAT[bits]
  401. # gdb.write("{:x} {}\n".format(address, binascii.hexlify(value)))
  402. return struct.unpack_from("<" + unpack_format, value)[0]
  403. @staticmethod
  404. def write(address, data, bits=32):
  405. """Write data to memory"""
  406. gdb.selected_inferior().write_memory(address, bytes(data), bits / 8)
  407. @staticmethod
  408. def format(value, form, length=32):
  409. """Format a number based on a format character and length"""
  410. # get current gdb radix setting
  411. radix = int(
  412. re.search(r"\d+", gdb.execute("show output-radix", True, True)).group(0)
  413. )
  414. # override it if asked to
  415. if form == "x" or form == "a":
  416. radix = 16
  417. elif form == "o":
  418. radix = 8
  419. elif form == "b" or form == "t":
  420. radix = 2
  421. # format the output
  422. if radix == 16:
  423. # For addresses, probably best in hex too
  424. l = int(math.ceil(length / 4.0))
  425. return "0x" + "{:X}".format(value).zfill(l)
  426. if radix == 8:
  427. l = int(math.ceil(length / 3.0))
  428. return "0" + "{:o}".format(value).zfill(l)
  429. if radix == 2:
  430. return "0b" + "{:b}".format(value).zfill(length)
  431. # Default: Just return in decimal
  432. return str(value)
  433. def peripheral_list(self):
  434. try:
  435. keys = self.svd_file.peripherals.iterkeys()
  436. except AttributeError:
  437. keys = self.svd_file.peripherals.keys()
  438. return list(keys)
  439. def register_list(self, peripheral):
  440. try:
  441. try:
  442. keys = self.svd_file.peripherals[peripheral].registers.iterkeys()
  443. except AttributeError:
  444. keys = self.svd_file.peripherals[peripheral].registers.keys()
  445. return list(keys)
  446. except:
  447. gdb.write("Peripheral {} doesn't exist\n".format(peripheral))
  448. return []
  449. def field_list(self, peripheral, register):
  450. try:
  451. periph = self.svd_file.peripherals[peripheral]
  452. reg = periph.registers[register]
  453. try:
  454. regs = reg.fields.iterkeys()
  455. except AttributeError:
  456. regs = reg.fields.keys()
  457. return list(regs)
  458. except:
  459. gdb.write("Register {} doesn't exist on {}\n".format(register, peripheral))
  460. return []