123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- """
- # b43 debugging library
- #
- # Copyright (C) 2008-2010 Michael Buesch <m@bues.ch>
- #
- # 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 sys
- import os
- import re
- import hashlib
- from tempfile import *
- # SHM routing values
- B43_SHM_UCODE = 0
- B43_SHM_SHARED = 1
- B43_SHM_REGS = 2
- B43_SHM_IHR = 3
- B43_SHM_RCMTA = 4
- class B43Exception(Exception):
- pass
- B43_MMIO_MACCTL = 0x120
- B43_MMIO_PSMDEBUG = 0x154
- B43_MACCTL_PSM_MACEN = 0x00000001
- B43_MACCTL_PSM_RUN = 0x00000002
- B43_MACCTL_PSM_JMP0 = 0x00000004
- B43_MACCTL_PSM_DEBUG = 0x00002000
- class B43PsmDebug:
- """Parse the contents of the PSM-debug register"""
- def __init__(self, reg_content):
- self.raw = reg_content
- return
- def getRaw(self):
- """Get the raw PSM-debug register value"""
- return self.raw
- def getPc(self):
- """Get the microcode program counter"""
- return self.raw & 0xFFF
- class B43:
- """Hardware access layer. This accesses the hardware through the debugfs interface."""
- def __init__(self, phy=None):
- debugfs_path = self.__debugfs_find()
- # Construct the debugfs b43 path to the device
- b43_path = debugfs_path + "/b43/"
- if phy:
- b43_path += phy
- else:
- # Get the PHY.
- try:
- phys = os.listdir(b43_path)
- except OSError:
- print "Could not find B43's debugfs directory: %s" % b43_path
- raise B43Exception
- if not phys:
- print "Could not find any b43 device"
- raise B43Exception
- if len(phys) != 1:
- print "Found multiple b43 devices."
- print "You must call this tool with a phyX parameter to specify a device"
- raise B43Exception
- phy = phys[0]
- b43_path += phy;
- # Open the debugfs files
- try:
- self.f_mmio16read = file(b43_path + "/mmio16read", "r+")
- self.f_mmio16write = file(b43_path + "/mmio16write", "w")
- self.f_mmio32read = file(b43_path + "/mmio32read", "r+")
- self.f_mmio32write = file(b43_path + "/mmio32write", "w")
- self.f_shm16read = file(b43_path + "/shm16read", "r+")
- self.f_shm16write = file(b43_path + "/shm16write", "w")
- self.f_shm32read = file(b43_path + "/shm32read", "r+")
- self.f_shm32write = file(b43_path + "/shm32write", "w")
- except IOError, e:
- print "Could not open debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- self.b43_path = b43_path
- return
- # Get the debugfs mountpoint.
- def __debugfs_find(self):
- mtab = file("/etc/mtab").read().splitlines()
- regexp = re.compile(r"^[\w\-_]+\s+([\w/\-_]+)\s+debugfs")
- path = None
- for line in mtab:
- m = regexp.match(line)
- if m:
- path = m.group(1)
- break
- if not path:
- print "Could not find debugfs in /etc/mtab"
- raise B43Exception
- return path
- def read16(self, reg):
- """Do a 16bit MMIO read"""
- try:
- self.f_mmio16read.seek(0)
- self.f_mmio16read.write("0x%X" % reg)
- self.f_mmio16read.flush()
- self.f_mmio16read.seek(0)
- val = self.f_mmio16read.read()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return int(val, 16)
- def read32(self, reg):
- """Do a 32bit MMIO read"""
- try:
- self.f_mmio32read.seek(0)
- self.f_mmio32read.write("0x%X" % reg)
- self.f_mmio32read.flush()
- self.f_mmio32read.seek(0)
- val = self.f_mmio32read.read()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return int(val, 16)
- def maskSet16(self, reg, mask, set):
- """Do a 16bit MMIO mask-and-set operation"""
- try:
- mask &= 0xFFFF
- set &= 0xFFFF
- self.f_mmio16write.seek(0)
- self.f_mmio16write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
- self.f_mmio16write.flush()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return
-
- def write16(self, reg, value):
- """Do a 16bit MMIO write"""
- self.maskSet16(reg, 0, value)
- return
- def maskSet32(self, reg, mask, set):
- """Do a 32bit MMIO mask-and-set operation"""
- try:
- mask &= 0xFFFFFFFF
- set &= 0xFFFFFFFF
- self.f_mmio32write.seek(0)
- self.f_mmio32write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
- self.f_mmio32write.flush()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return
- def write32(self, reg, value):
- """Do a 32bit MMIO write"""
- self.maskSet32(reg, 0, value)
- return
- def shmRead16(self, routing, offset):
- """Do a 16bit SHM read"""
- try:
- self.f_shm16read.seek(0)
- self.f_shm16read.write("0x%X 0x%X" % (routing, offset))
- self.f_shm16read.flush()
- self.f_shm16read.seek(0)
- val = self.f_shm16read.read()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return int(val, 16)
- def shmMaskSet16(self, routing, offset, mask, set):
- """Do a 16bit SHM mask-and-set operation"""
- try:
- mask &= 0xFFFF
- set &= 0xFFFF
- self.f_shm16write.seek(0)
- self.f_shm16write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
- self.f_shm16write.flush()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return
- def shmWrite16(self, routing, offset, value):
- """Do a 16bit SHM write"""
- self.shmMaskSet16(routing, offset, 0, value)
- return
- def shmRead32(self, routing, offset):
- """Do a 32bit SHM read"""
- try:
- self.f_shm32read.seek(0)
- self.f_shm32read.write("0x%X 0x%X" % (routing, offset))
- self.f_shm32read.flush()
- self.f_shm32read.seek(0)
- val = self.f_shm32read.read()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return int(val, 16)
- def shmMaskSet32(self, routing, offset, mask, set):
- """Do a 32bit SHM mask-and-set operation"""
- try:
- mask &= 0xFFFFFFFF
- set &= 0xFFFFFFFF
- self.f_shm32write.seek(0)
- self.f_shm32write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
- self.f_shm32write.flush()
- except IOError, e:
- print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
- raise B43Exception
- return
- def shmWrite32(self, routing, offset, value):
- """Do a 32bit SHM write"""
- self.shmMaskSet32(routing, offset, 0, value)
- return
- def getGprs(self):
- """Returns an array of 64 ints. One for each General Purpose register."""
- ret = []
- for i in range(0, 64):
- val = self.shmRead16(B43_SHM_REGS, i)
- ret.append(val)
- return ret
- def getLinkRegs(self):
- """Returns an array of 4 ints. One for each Link Register."""
- ret = []
- for i in range(0, 4):
- val = self.read16(0x4D0 + (i * 2))
- ret.append(val)
- return ret
- def getOffsetRegs(self):
- """Returns an array of 7 ints. One for each Offset Register."""
- ret = []
- for i in range(0, 7):
- val = self.read16(0x4C0 + (i * 2))
- ret.append(val)
- return ret
- def shmSharedRead(self):
- """Returns a string containing the SHM contents."""
- ret = ""
- for i in range(0, 4096, 4):
- val = self.shmRead32(B43_SHM_SHARED, i)
- ret += "%c%c%c%c" % (val & 0xFF,
- (val >> 8) & 0xFF,
- (val >> 16) & 0xFF,
- (val >> 24) & 0xFF)
- return ret
- def getPsmDebug(self):
- """Read the PSM-debug register and return an instance of B43PsmDebug."""
- val = self.read32(B43_MMIO_PSMDEBUG)
- return B43PsmDebug(val)
- def getPsmConditions(self):
- """This returns the contents of the programmable-PSM-conditions register."""
- return self.read16(0x4D8)
- def ucodeStop(self):
- """Unconditionally stop the microcode PSM. """
- self.maskSet32(B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, 0)
- return
- def ucodeStart(self):
- """Unconditionally start the microcode PSM. This will restart the
- microcode on the current PC. It will not jump to 0. Warning: This will
- unconditionally restart the PSM and ignore any driver-state!"""
- self.maskSet32(B43_MMIO_MACCTL, ~0, B43_MACCTL_PSM_RUN)
- return
- class Disassembler:
- """Disassembler for b43 firmware."""
- def __init__(self, binaryText, b43DasmOpts):
- input = NamedTemporaryFile()
- output = NamedTemporaryFile()
- input.write(binaryText)
- input.flush()
- #FIXME check b43-dasm errors
- os.system("b43-dasm %s %s %s" % (input.name, output.name, b43DasmOpts))
- self.asmText = output.read()
- def getAsm(self):
- """Returns the assembly code."""
- return self.asmText
- class Assembler:
- """Assembler for b43 firmware."""
- def __init__(self, assemblyText, b43AsmOpts):
- input = NamedTemporaryFile()
- output = NamedTemporaryFile()
- input.write(assemblyText)
- input.flush()
- #FIXME check b43-asm errors
- os.system("b43-asm %s %s %s" % (input.name, output.name, b43AsmOpts))
- self.binaryText = output.read()
- def getBinary(self):
- """Returns the binary code."""
- return self.binaryText
- class TextPatcher:
- """A textfile patcher that does not include any target context.
- This can be used to patch b43 firmware files."""
- class TextLine:
- def __init__(self, index, line):
- self.index = index
- self.line = line
- self.deleted = False
- def __init__(self, text, expected_md5sum):
- sum = hashlib.md5(text).hexdigest()
- if sum != expected_md5sum:
- print "Patcher: The text does not match the expected MD5 sum"
- print "Expected: " + expected_md5sum
- print "Calculated: " + sum
- raise B43Exception
- text = text.splitlines()
- self.lines = []
- i = 0
- for line in text:
- self.lines.append(TextPatcher.TextLine(i, line))
- i += 1
- # Add an after-last dummy. Needed for the add-before logic
- lastDummy = TextPatcher.TextLine(i, "")
- lastDummy.deleted = True
- self.lines.append(lastDummy)
- def getText(self):
- """This returns the current text."""
- textLines = []
- for l in self.lines:
- if not l.deleted:
- textLines.append(l.line)
- return "\n".join(textLines)
- def delLine(self, linenumber):
- """Delete a line of text. The linenumber corresponds to the
- original unmodified text."""
- for l in self.lines:
- if l.index == linenumber:
- l.deleted = True
- return
- print "Patcher deleteLine: Did not find the line!"
- raise B43Exception
- def addText(self, beforeLineNumber, text):
- """Add a text before the specified linenumber. The linenumber
- corresponds to the original unmodified text."""
- text = text.splitlines()
- index = 0
- for l in self.lines:
- if l.index == beforeLineNumber:
- break
- index += 1
- if index >= len(self.lines):
- print "Patcher addText: Did not find the line!"
- raise B43Exception
- for l in text:
- 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 __repr__(self):
- return self.getLine()
- def isInstruction(self):
- return False
- class B43AsmInstruction(B43AsmLine):
- def __init__(self, opcode):
- self.setOpcode(opcode)
- self.clearOperands()
- def getOpcode(self):
- return self.opcode
- def setOpcode(self, opcode):
- self.opcode = opcode
- def clearOperands(self):
- self.operands = []
- 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"""
- if headers_dir.endswith("/"):
- headers_dir = headers_dir[:-1]
- 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()
- # Transform unconditional jump
- if opcode == "jext" and int(operands[0], 16) == 0x7F:
- label = operands[1]
- line.setOpcode("jmp")
- line.clearOperands()
- line.addOperand(label)
- continue
- # Transform external conditions
- if opcode == "jext" or opcode == "jnext":
- operands[0] = self.symCond.get(int(operands[0], 16))
- continue
- # Transform orx 7,8,imm,imm,target to mov
- if opcode == "orx" and \
- int(operands[0], 16) == 7 and int(operands[1], 16) == 8 and\
- operands[2].startswith("0x") and operands[3].startswith("0x"):
- value = int(operands[3], 16) & 0xFF
- value |= (int(operands[2], 16) & 0xFF) << 8
- target = operands[4]
- line.setOpcode("mov")
- line.clearOperands()
- line.addOperand("0x%X" % value)
- line.addOperand(target)
- opcode = line.getOpcode()
- operands = line.getOperands()
- for i in range(0, len(operands)):
- o = operands[i]
- # Transform SPR operands
- m = spr_re.match(o)
- if m:
- operands[i] = self.symSpr.get(o)
- continue
- # Transform SHM operands
- 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.append(str(line))
- return "\n".join(ret)
|