Add b43 firmware assembly beautifier
authorMichael Buesch <mb@bu3sch.de>
Tue, 22 Jul 2008 23:46:14 +0000 (01:46 +0200)
committerMichael Buesch <mb@bu3sch.de>
Tue, 22 Jul 2008 23:46:14 +0000 (01:46 +0200)
This tool converts numeric identifiers to symbolic names.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
debug/b43-beautifier [new file with mode: 0755]
debug/install.py
debug/libb43.py

diff --git a/debug/b43-beautifier b/debug/b43-beautifier
new file mode 100755 (executable)
index 0000000..0b3a09c
--- /dev/null
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+"""
+#  b43 firmware assembly code beautifier
+#
+#  Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
+#
+#  This program is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License version 3
+#  as published by the Free Software Foundation.
+#
+#  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import getopt
+import sys
+from libb43 import *
+
+
+def usage():
+       print "b43 firmware assembly code beautifier"
+       print ""
+       print "Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>"
+       print "Licensed under the GNU/GPL version 3"
+       print ""
+       print "Usage: b43-beautifier [OPTIONS]"
+       print ""
+       print "-h|--help            Print this help text"
+       print "-a|--asmfile [FILE]  Assembly code source file"
+       print "-b|--binfile [FILE]  Binary code source file"
+       print "-d|--defs [DIR]      Directory containing the defs files"
+       print ""
+       print "The options  -d AND (-a XOR -b)  are essential"
+
+def parseArgs():
+       global opt_asmfile
+       global opt_binfile
+       global opt_defsfiles
+
+       opt_asmfile = None
+       opt_binfile = None
+       opt_defsfiles = None
+
+       try:
+               (opts, args) = getopt.getopt(sys.argv[1:],
+                       "ha:b:d:",
+                       [ "help", "asmfile=", "binfile=", "defs=" ])
+       except getopt.GetoptError:
+               usage()
+               sys.exit(1)
+
+       for (o, v) in opts:
+               if o in ("-h", "--help"):
+                       usage()
+                       sys.exit(0)
+               if o in ("-a", "--asmfile"):
+                       if opt_binfile:
+                               print "Must not set both of --asmfile and --binfile"
+                               sys.exit(1)
+                       opt_asmfile = v
+               if o in ("-b", "--binfile"):
+                       if opt_asmfile:
+                               print "Must not set both of --asmfile and --binfile"
+                               sys.exit(1)
+                       opt_binfile = v
+               if o in ("-d", "--defs"):
+                       opt_defsfiles = v
+
+       if not opt_asmfile and not opt_binfile:
+               print "Must set either --asmfile or --binfile"
+               sys.exit(1)
+       if not opt_defsfiles:
+               print "Must set --defs"
+               sys.exit(1)
+
+def main():
+       parseArgs()
+
+       # Get the assembly
+       if opt_asmfile:
+               try:
+                       asm = file(opt_asmfile).read()
+               except IOError, e:
+                       print "Could not read asmfile %s: %s" % (e.filename, e.strerror)
+                       sys.exit(1)
+       else:
+               try:
+                       bin = file(opt_binfile).read()
+               except IOError, e:
+                       print "Could not read binfile %s: %s" % (e.filename, e.strerror)
+                       sys.exit(1)
+               asm = Disassembler(bin, "").getAsm()
+
+       b = B43Beautifier(asm, opt_defsfiles)
+       sys.stdout.write(b.getAsm())
+
+try:
+       main()
+except B43Exception:
+       sys.exit(1)
index 489859b22f5f98db83476bbdc94fd76b11afb8d5..f6d576c04dbb2143f3a5c40667a0593dda18c242 100755 (executable)
@@ -12,5 +12,5 @@ if len(sys.argv) == 1:
 setup(
        name="B43-debug-tools",
        py_modules=["libb43"],
-       scripts=["b43-fwdump"]
+       scripts=["b43-fwdump", "b43-beautifier"]
      )
index 00da13bfced95788004f3820cb057f533f9bdf3f..671ad2c6f173a141bb2bbffa984c6200f5c929e4 100644 (file)
@@ -60,6 +60,8 @@ class B43PsmDebug:
 
 
 class B43:
+       """Hardware access layer. This accesses the hardware through the debugfs interface."""
+
        def __init__(self, phy):
                debugfs_path = self.__debugfs_find()
 
@@ -388,3 +390,271 @@ class TextPatcher:
                        self.lines.insert(index, TextPatcher.TextLine(-1, l))
                        index += 1
 
+class B43SymbolicSpr:
+       """This class converts numeric SPR names into symbolic SPR names."""
+
+       def __init__(self, header_file):
+               """The passed header_file parameter is a file path to the
+               assembly file containing the symbolic SPR definitions."""
+               try:
+                       defs = file(header_file).readlines()
+               except IOError, e:
+                       print "B43SymbolicSpr: Could not read %s: %s" % (e.filename, e.strerror)
+                       B43Exception
+               # Parse the definitions
+               self.spr_names = { }
+               r = re.compile(r"#define\s+(\w+)\s+(spr[a-fA-F0-9]+)")
+               for line in defs:
+                       m = r.match(line)
+                       if not m:
+                               continue # unknown line
+                       name = m.group(1)
+                       offset = m.group(2)
+                       self.spr_names[offset.lower()] = name
+
+       def get(self, spr):
+               """Get the symbolic name for an SPR. The spr parameter
+               must be a string like "sprXXX", where XXX is a hex number."""
+               try:
+                       spr = self.spr_names[spr.lower()]
+               except KeyError:
+                       pass # Symbol not found. Return numeric name.
+               return spr
+
+       def getRaw(self, spr_hexnumber):
+               """Get the symbolic name for an SPR. The spr_hexnumber
+               parameter is the hexadecimal number for the SPR."""
+               return self.get("spr%03X" % spr_hexnumber)
+
+class B43SymbolicShm:
+       """This class converts numeric SHM offsets into symbolic SHM names."""
+
+       def __init__(self, header_file):
+               """The passed header_file parameter is a file path to the
+               assembly file containing the symbolic SHM definitions."""
+               try:
+                       defs = file(header_file).readlines()
+               except IOError, e:
+                       print "B43SymbolicShm: Could not read %s: %s" % (e.filename, e.strerror)
+                       raise B43Exception
+               # Parse the definitions
+               self.shm_names = { }
+               in_abi_section = False
+               r = re.compile(r"#define\s+(\w+)\s+SHM\((\w+)\).*")
+               for line in defs:
+                       if line.startswith("/* BEGIN ABI"):
+                               in_abi_section = True
+                       if line.startswith("/* END ABI"):
+                               in_abi_section = False
+                       if not in_abi_section:
+                               continue # Only parse ABI definitions
+                       m = r.match(line)
+                       if not m:
+                               continue # unknown line
+                       name = m.group(1)
+                       offset = int(m.group(2), 16)
+                       offset /= 2
+                       self.shm_names[offset] = name
+
+       def get(self, shm_wordoffset):
+               """Get the symbolic name for an SHM offset."""
+               try:
+                       sym = self.shm_names[shm_wordoffset]
+               except KeyError:
+                       # Symbol not found. Return numeric name.
+                       sym = "0x%03X" % shm_wordoffset
+               return sym
+
+class B43SymbolicCondition:
+       """This class converts numeric External Conditions into symbolic names."""
+
+       def __init__(self, header_file):
+               """The passed header_file parameter is a file path to the
+               assembly file containing the symbolic condition definitions."""
+               try:
+                       defs = file(header_file).readlines()
+               except IOError, e:
+                       print "B43SymbolicCondition: Could not read %s: %s" % (e.filename, e.strerror)
+                       raise B43Exception
+               # Parse the definitions
+               self.cond_names = { }
+               r = re.compile(r"#define\s+(\w+)\s+EXTCOND\(\s*(\w+),\s*(\d+)\s*\).*")
+               for line in defs:
+                       m = r.match(line)
+                       if not m:
+                               continue # unknown line
+                       name = m.group(1)
+                       register = m.group(2)
+                       bit = int(m.group(3))
+                       if register == "CONDREG_RX":
+                               register = 0
+                       elif register == "CONDREG_TX":
+                               register = 2
+                       elif register == "CONDREG_PHY":
+                               register = 3
+                       elif register == "CONDREG_4":
+                               register = 4
+                       elif register == "CONDREG_PSM":
+                               continue # No lookup table for this one
+                       elif register == "CONDREG_RCM":
+                               register = 6
+                       elif register == "CONDREG_7":
+                               register = 7
+                       else:
+                               continue # unknown register
+                       cond_number = bit | (register << 4)
+                       self.cond_names[cond_number] = name
+
+       def get(self, cond_number):
+               """Get the symbolic name for an External Condition."""
+               register = (cond_number >> 4) & 0x7
+               bit = cond_number & 0xF
+               eoi = ((cond_number & 0x80) != 0)
+               cond_number &= ~0x80
+               if register == 5: # PSM register
+                       return "COND_PSM(%d)" % bit
+               try:
+                       sym = self.cond_names[cond_number]
+               except KeyError:
+                       # Symbol not found. Return numeric name.
+                       sym = "0x%02X" % cond_number
+               if eoi:
+                       sym = "EOI(%s)" % sym
+               return sym
+
+class B43AsmLine:
+       def __init__(self, text):
+               self.text = text
+
+       def getLine(self):
+               return self.text
+
+       def isInstruction(self):
+               return False
+
+class B43AsmInstruction(B43AsmLine):
+       def __init__(self, opcode):
+               self.opcode = opcode
+               self.operands = []
+
+       def getOpcode(self):
+               return self.opcode
+
+       def addOperand(self, operand):
+               self.operands.append(operand)
+
+       def getOperands(self):
+               return self.operands
+
+       def getLine(self):
+               ret = "\t" + self.opcode
+               if self.operands:
+                       ret += "\t"
+               for op in self.operands:
+                       ret += op + ", "
+               if self.operands:
+                       ret = ret[:-2]
+               return ret
+
+       def isInstruction(self):
+               return True
+
+class B43AsmParser:
+       """A simple B43 assembly code parser."""
+
+       def __init__(self, asm_code):
+               self.__parse_code(asm_code)
+
+       def __parse_code(self, asm_code):
+               self.codelines = []
+               label = re.compile(r"^\s*\w+:\s*$")
+               insn_0 = re.compile(r"^\s*([@\.\w]+)\s*$")
+               insn_2 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
+               insn_3 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
+               insn_5 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
+               for line in asm_code.splitlines():
+                       m = label.match(line)
+                       if m: # Label:
+                               l = B43AsmLine(line)
+                               self.codelines.append(l)
+                               continue
+                       m = insn_0.match(line)
+                       if m: # No operands
+                               insn = B43AsmInstruction(m.group(1))
+                               self.codelines.append(insn)
+                               continue
+                       m = insn_2.match(line)
+                       if m: # Two operands
+                               insn = B43AsmInstruction(m.group(1))
+                               insn.addOperand(m.group(2))
+                               insn.addOperand(m.group(3))
+                               self.codelines.append(insn)
+                               continue
+                       m = insn_3.match(line)
+                       if m: # Three operands
+                               insn = B43AsmInstruction(m.group(1))
+                               insn.addOperand(m.group(2))
+                               insn.addOperand(m.group(3))
+                               insn.addOperand(m.group(4))
+                               self.codelines.append(insn)
+                               continue
+                       m = insn_5.match(line)
+                       if m: # Three operands
+                               insn = B43AsmInstruction(m.group(1))
+                               insn.addOperand(m.group(2))
+                               insn.addOperand(m.group(3))
+                               insn.addOperand(m.group(4))
+                               insn.addOperand(m.group(5))
+                               insn.addOperand(m.group(6))
+                               self.codelines.append(insn)
+                               continue
+                       # Unknown line
+                       l = B43AsmLine(line)
+                       self.codelines.append(l)
+
+class B43Beautifier(B43AsmParser):
+       """A B43 assembly code beautifier."""
+
+       def __init__(self, asm_code, headers_dir):
+               """asm_code is the assembly code. headers_dir is a full
+               path to the directory containing the symbolic SPR,SHM,etc... definitions"""
+               B43AsmParser.__init__(self, asm_code)
+               self.symSpr = B43SymbolicSpr(headers_dir + "/spr.inc")
+               self.symShm = B43SymbolicShm(headers_dir + "/shm.inc")
+               self.symCond = B43SymbolicCondition(headers_dir + "/cond.inc")
+               self.preamble = "#include <%s/spr.inc>\n" % headers_dir
+               self.preamble += "#include <%s/shm.inc>\n" % headers_dir
+               self.preamble += "#include <%s/cond.inc>\n" % headers_dir
+               self.preamble += "\n"
+               self.__process_code()
+
+       def __process_code(self):
+               spr_re = re.compile(r"^spr\w\w\w$")
+               shm_re = re.compile(r"^\[(0x\w+)\]$")
+               code = self.codelines
+               for line in code:
+                       if not line.isInstruction():
+                               continue
+                       opcode = line.getOpcode()
+                       operands = line.getOperands()
+                       if opcode == "jext" or opcode == "jnext":
+                               operands[0] = self.symCond.get(int(operands[0], 16))
+                               continue
+                       for i in range(0, len(operands)):
+                               o = operands[i]
+                               m = spr_re.match(o)
+                               if m:
+                                       operands[i] = self.symSpr.get(o)
+                                       continue
+                               m = shm_re.match(o)
+                               if m:
+                                       offset = int(m.group(1), 16)
+                                       operands[i] = "[" + self.symShm.get(offset) + "]"
+                                       continue
+
+       def getAsm(self):
+               """Returns the beautified asm code."""
+               ret = self.preamble
+               for line in self.codelines:
+                       ret += line.getLine() + "\n"
+               return ret