From 98a28c2cd91e0bc78c9420b6b78be81993fe969a Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Wed, 23 Jul 2008 01:46:14 +0200 Subject: [PATCH] Add b43 firmware assembly beautifier This tool converts numeric identifiers to symbolic names. Signed-off-by: Michael Buesch --- debug/b43-beautifier | 105 +++++++++++++++++ debug/install.py | 2 +- debug/libb43.py | 270 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+), 1 deletion(-) create mode 100755 debug/b43-beautifier diff --git a/debug/b43-beautifier b/debug/b43-beautifier new file mode 100755 index 0000000..0b3a09c --- /dev/null +++ b/debug/b43-beautifier @@ -0,0 +1,105 @@ +#!/usr/bin/env python +""" +# b43 firmware assembly code beautifier +# +# Copyright (C) 2008 Michael Buesch +# +# 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 . +""" + +import getopt +import sys +from libb43 import * + + +def usage(): + print "b43 firmware assembly code beautifier" + print "" + print "Copyright (C) 2008 Michael Buesch " + 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) diff --git a/debug/install.py b/debug/install.py index 489859b..f6d576c 100755 --- a/debug/install.py +++ b/debug/install.py @@ -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"] ) diff --git a/debug/libb43.py b/debug/libb43.py index 00da13b..671ad2c 100644 --- a/debug/libb43.py +++ b/debug/libb43.py @@ -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 -- 2.31.1