| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- #!/usr/bin/env python
- """
- This file is part of PyCortexMDebug
- PyCortexMDebug is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- PyCortexMDebug is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
- """
- import gdb
- import re
- import math
- import sys
- import struct
- import pkg_resources
- from .svd import SVDFile
- BITS_TO_UNPACK_FORMAT = {
- 8: "B",
- 16: "H",
- 32: "I",
- }
- class LoadSVD(gdb.Command):
- """A command to load an SVD file and to create the command for inspecting
- that object
- """
- def __init__(self):
- self.vendors = {}
- try:
- vendor_names = pkg_resources.resource_listdir("cmsis_svd", "data")
- for vendor in vendor_names:
- fnames = pkg_resources.resource_listdir(
- "cmsis_svd", "data/{}".format(vendor)
- )
- self.vendors[vendor] = [
- fname for fname in fnames if fname.lower().endswith(".svd")
- ]
- except:
- pass
- if len(self.vendors) > 0:
- gdb.Command.__init__(self, "svd_load", gdb.COMMAND_USER)
- else:
- gdb.Command.__init__(
- self, "svd_load", gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME
- )
- def complete(self, text, word):
- args = gdb.string_to_argv(text)
- num_args = len(args)
- if text.endswith(" "):
- num_args += 1
- if not text:
- num_args = 1
- # "svd_load <tab>" or "svd_load ST<tab>"
- if num_args == 1:
- prefix = word.lower()
- return [
- vendor for vendor in self.vendors if vendor.lower().startswith(prefix)
- ]
- # "svd_load STMicro<tab>" or "svd_load STMicro STM32F1<tab>"
- elif num_args == 2 and args[0] in self.vendors:
- prefix = word.lower()
- filenames = self.vendors[args[0]]
- return [fname for fname in filenames if fname.lower().startswith(prefix)]
- return gdb.COMPLETE_NONE
- @staticmethod
- def invoke(args, from_tty):
- args = gdb.string_to_argv(args)
- argc = len(args)
- if argc == 1:
- gdb.write("Loading SVD file {}...\n".format(args[0]))
- f = args[0]
- elif argc == 2:
- gdb.write("Loading SVD file {}/{}...\n".format(args[0], args[1]))
- f = pkg_resources.resource_filename(
- "cmsis_svd", "data/{}/{}".format(args[0], args[1])
- )
- else:
- raise gdb.GdbError(
- "Usage: svd_load <vendor> <device.svd> or svd_load <path/to/filename.svd>\n"
- )
- try:
- SVD(SVDFile(f))
- except Exception as e:
- raise gdb.GdbError("Could not load SVD file {} : {}...\n".format(f, e))
- if __name__ == "__main__":
- # This will also get executed by GDB
- # Create just the svd_load command
- LoadSVD()
- class SVD(gdb.Command):
- """The CMSIS SVD (System View Description) inspector command
- This allows easy access to all peripheral registers supported by the system
- in the GDB debug environment
- """
- def __init__(self, svd_file):
- gdb.Command.__init__(self, "svd", gdb.COMMAND_DATA)
- self.svd_file = svd_file
- def _print_registers(self, container_name, form, registers):
- if len(registers) == 0:
- return
- try:
- regs_iter = registers.itervalues()
- except AttributeError:
- regs_iter = registers.values()
- gdb.write("Registers in %s:\n" % container_name)
- reg_list = []
- for r in regs_iter:
- if r.readable():
- try:
- data = self.read(r.address(), r.size)
- data = self.format(data, form, r.size)
- if form == "a":
- data += (
- " <"
- + re.sub(
- r"\s+",
- " ",
- gdb.execute(
- "info symbol {}".format(data), True, True
- ).strip(),
- )
- + ">"
- )
- except gdb.MemoryError:
- data = "(error reading)"
- else:
- data = "(not readable)"
- desc = re.sub(r"\s+", " ", r.description)
- reg_list.append((r.name, data, desc))
- column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
- column2_width = max(len(reg[1]) for reg in reg_list)
- for reg in reg_list:
- gdb.write(
- "\t{}:{}{}".format(
- reg[0],
- "".ljust(column1_width - len(reg[0])),
- reg[1].rjust(column2_width),
- )
- )
- if reg[2] != reg[0]:
- gdb.write(" {}".format(reg[2]))
- gdb.write("\n")
- def _print_register_fields(self, container_name, form, register):
- gdb.write("Fields in {}:\n".format(container_name))
- fields = register.fields
- if not register.readable():
- data = 0
- else:
- data = self.read(register.address(), register.size)
- field_list = []
- try:
- fields_iter = fields.itervalues()
- except AttributeError:
- fields_iter = fields.values()
- for f in fields_iter:
- desc = re.sub(r"\s+", " ", f.description)
- if register.readable():
- val = data >> f.offset
- val &= (1 << f.width) - 1
- if f.enum:
- if val in f.enum:
- desc = f.enum[val][1] + " - " + desc
- val = f.enum[val][0]
- else:
- val = "Invalid enum value: " + self.format(val, form, f.width)
- else:
- val = self.format(val, form, f.width)
- else:
- val = "(not readable)"
- field_list.append((f.name, val, desc))
- column1_width = max(len(field[0]) for field in field_list) + 2 # padding
- column2_width = max(len(field[1]) for field in field_list) # padding
- for field in field_list:
- gdb.write(
- "\t{}:{}{}".format(
- field[0],
- "".ljust(column1_width - len(field[0])),
- field[1].rjust(column2_width),
- )
- )
- if field[2] != field[0]:
- gdb.write(" {}".format(field[2]))
- gdb.write("\n")
- def invoke(self, args, from_tty):
- s = str(args).split(" ")
- form = ""
- if s[0] and s[0][0] == "/":
- if len(s[0]) == 1:
- gdb.write("Incorrect format\n")
- return
- else:
- form = s[0][1:]
- if len(s) == 1:
- return
- s = s[1:]
- if s[0].lower() == "help":
- gdb.write("Usage:\n")
- gdb.write("=========\n")
- gdb.write("svd:\n")
- gdb.write("\tList available peripherals\n")
- gdb.write("svd [peripheral_name]:\n")
- gdb.write("\tDisplay all registers pertaining to that peripheral\n")
- gdb.write("svd [peripheral_name] [register_name]:\n")
- gdb.write("\tDisplay the fields in that register\n")
- gdb.write("svd/[format_character] ...\n")
- gdb.write("\tFormat values using that character\n")
- gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n")
- gdb.write("\n")
- gdb.write(
- "Both prefix matching and case-insensitive matching is supported for peripherals, registers, clusters and fields.\n"
- )
- return
- if not len(s[0]):
- gdb.write("Available Peripherals:\n")
- try:
- peripherals = self.svd_file.peripherals.itervalues()
- except AttributeError:
- peripherals = self.svd_file.peripherals.values()
- column_width = max(len(p.name) for p in peripherals) + 2 # padding
- try:
- peripherals = self.svd_file.peripherals.itervalues()
- except AttributeError:
- peripherals = self.svd_file.peripherals.values()
- for p in peripherals:
- desc = re.sub(r"\s+", " ", p.description)
- gdb.write(
- "\t{}:{}{}\n".format(
- p.name, "".ljust(column_width - len(p.name)), desc
- )
- )
- return
- def warn_if_ambiguous(smart_dict, key):
- if smart_dict.is_ambiguous(key):
- gdb.write(
- "Warning: {} could prefix match any of: {}\n".format(
- key, ", ".join(smart_dict.prefix_match_iter(key))
- )
- )
- registers = None
- if len(s) >= 1:
- peripheral_name = s[0]
- if peripheral_name not in self.svd_file.peripherals:
- gdb.write("Peripheral {} does not exist!\n".format(s[0]))
- return
- warn_if_ambiguous(self.svd_file.peripherals, peripheral_name)
- peripheral = self.svd_file.peripherals[peripheral_name]
- if len(s) == 1:
- self._print_registers(peripheral.name, form, peripheral.registers)
- if len(peripheral.clusters) > 0:
- try:
- clusters_iter = peripheral.clusters.itervalues()
- except AttributeError:
- clusters_iter = peripheral.clusters.values()
- gdb.write("Clusters in %s:\n" % peripheral.name)
- reg_list = []
- for r in clusters_iter:
- desc = re.sub(r"\s+", " ", r.description)
- reg_list.append((r.name, "", desc))
- column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
- column2_width = max(len(reg[1]) for reg in reg_list)
- for reg in reg_list:
- gdb.write(
- "\t{}:{}{}".format(
- reg[0],
- "".ljust(column1_width - len(reg[0])),
- reg[1].rjust(column2_width),
- )
- )
- if reg[2] != reg[0]:
- gdb.write(" {}".format(reg[2]))
- gdb.write("\n")
- return
- cluster = None
- if len(s) == 2:
- if s[1] in peripheral.clusters:
- warn_if_ambiguous(peripheral.clusters, s[1])
- cluster = peripheral.clusters[s[1]]
- container = peripheral.name + " > " + cluster.name
- self._print_registers(container, form, cluster.registers)
- elif s[1] in peripheral.registers:
- warn_if_ambiguous(peripheral.registers, s[1])
- register = peripheral.registers[s[1]]
- container = peripheral.name + " > " + register.name
- self._print_register_fields(container, form, register)
- else:
- gdb.write(
- "Register/cluster {} in peripheral {} does not exist!\n".format(
- s[1], peripheral.name
- )
- )
- return
- if len(s) == 3:
- if s[1] not in peripheral.clusters:
- gdb.write(
- "Cluster {} in peripheral {} does not exist!\n".format(
- s[1], peripheral.name
- )
- )
- return
- warn_if_ambiguous(peripheral.clusters, s[1])
- cluster = peripheral.clusters[s[1]]
- if s[2] not in cluster.registers:
- gdb.write(
- "Register {} in cluster {} in peripheral {} does not exist!\n".format(
- s[2], cluster.name, peripheral.name
- )
- )
- return
- warn_if_ambiguous(cluster.registers, s[2])
- register = cluster.registers[s[2]]
- container = " > ".join([peripheral.name, cluster.name, register.name])
- self._print_register_fields(container, form, register)
- return
- if len(s) == 4:
- if s[1] not in peripheral.registers:
- gdb.write(
- "Register {} in peripheral {} does not exist!\n".format(
- s[1], peripheral.name
- )
- )
- return
- warn_if_ambiguous(peripheral.registers, s[1])
- reg = peripheral.registers[s[1]]
- if s[2] not in reg.fields:
- gdb.write(
- "Field {} in register {} in peripheral {} does not exist!\n".format(
- s[2], reg.name, peripheral.name
- )
- )
- return
- warn_if_ambiguous(reg.fields, s[2])
- field = reg.fields[s[2]]
- if not field.writable() or not reg.writable():
- gdb.write(
- "Field {} in register {} in peripheral {} is read-only!\n".format(
- field.name, reg.name, peripheral.name
- )
- )
- return
- try:
- val = int(s[3], 0)
- except ValueError:
- gdb.write(
- "{} is not a valid number! You can prefix numbers with 0x for hex, 0b for binary, or any python "
- "int literal\n".format(s[3])
- )
- return
- if val >= 1 << field.width or val < 0:
- gdb.write(
- "{} not a valid number for a field with width {}!\n".format(
- val, field.width
- )
- )
- return
- if not reg.readable():
- data = 0
- else:
- data = self.read(reg.address(), reg.size)
- data &= ~(((1 << field.width) - 1) << field.offset)
- data |= val << field.offset
- self.write(reg.address(), data, reg.size)
- return
- gdb.write("Unknown input\n")
- def complete(self, text, word):
- """Perform tab-completion for the command"""
- text = str(text)
- s = text.split(" ")
- # Deal with the possibility of a '/' parameter
- if s[0] and s[0][0] == "/":
- if len(s) > 1:
- s = s[1:]
- else:
- return [] # completion after e.g. "svd/x" but before trailing space
- if len(s) == 1:
- return list(self.svd_file.peripherals.prefix_match_iter(s[0]))
- if len(s) == 2:
- reg = s[1].upper()
- if len(reg) and reg[0] == "&":
- reg = reg[1:]
- if s[0] not in self.svd_file.peripherals:
- return []
- per = self.svd_file.peripherals[s[0]]
- return list(per.registers.prefix_match_iter(s[1]))
- return []
- @staticmethod
- def read(address, bits=32):
- """Read from memory and return an integer"""
- value = gdb.selected_inferior().read_memory(address, bits / 8)
- unpack_format = "I"
- if bits in BITS_TO_UNPACK_FORMAT:
- unpack_format = BITS_TO_UNPACK_FORMAT[bits]
- # gdb.write("{:x} {}\n".format(address, binascii.hexlify(value)))
- return struct.unpack_from("<" + unpack_format, value)[0]
- @staticmethod
- def write(address, data, bits=32):
- """Write data to memory"""
- gdb.selected_inferior().write_memory(address, bytes(data), bits / 8)
- @staticmethod
- def format(value, form, length=32):
- """Format a number based on a format character and length"""
- # get current gdb radix setting
- radix = int(
- re.search(r"\d+", gdb.execute("show output-radix", True, True)).group(0)
- )
- # override it if asked to
- if form == "x" or form == "a":
- radix = 16
- elif form == "o":
- radix = 8
- elif form == "b" or form == "t":
- radix = 2
- # format the output
- if radix == 16:
- # For addresses, probably best in hex too
- l = int(math.ceil(length / 4.0))
- return "0x" + "{:X}".format(value).zfill(l)
- if radix == 8:
- l = int(math.ceil(length / 3.0))
- return "0" + "{:o}".format(value).zfill(l)
- if radix == 2:
- return "0b" + "{:b}".format(value).zfill(length)
- # Default: Just return in decimal
- return str(value)
- def peripheral_list(self):
- try:
- keys = self.svd_file.peripherals.iterkeys()
- except AttributeError:
- keys = self.svd_file.peripherals.keys()
- return list(keys)
- def register_list(self, peripheral):
- try:
- try:
- keys = self.svd_file.peripherals[peripheral].registers.iterkeys()
- except AttributeError:
- keys = self.svd_file.peripherals[peripheral].registers.keys()
- return list(keys)
- except:
- gdb.write("Peripheral {} doesn't exist\n".format(peripheral))
- return []
- def field_list(self, peripheral, register):
- try:
- periph = self.svd_file.peripherals[peripheral]
- reg = periph.registers[register]
- try:
- regs = reg.fields.iterkeys()
- except AttributeError:
- regs = reg.fields.keys()
- return list(regs)
- except:
- gdb.write("Register {} doesn't exist on {}\n".format(register, peripheral))
- return []
|