2 # b43 debugging library
4 # Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 3
8 # as published by the Free Software Foundation.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 from tempfile import *
34 class B43Exception(Exception):
38 B43_MMIO_MACCTL = 0x120
39 B43_MMIO_PSMDEBUG = 0x154
41 B43_MACCTL_PSM_MACEN = 0x00000001
42 B43_MACCTL_PSM_RUN = 0x00000002
43 B43_MACCTL_PSM_JMP0 = 0x00000004
44 B43_MACCTL_PSM_DEBUG = 0x00002000
48 """Parse the contents of the PSM-debug register"""
49 def __init__(self, reg_content):
50 self.raw = reg_content
54 """Get the raw PSM-debug register value"""
58 """Get the microcode program counter"""
59 return self.raw & 0xFFF
63 """Hardware access layer. This accesses the hardware through the debugfs interface."""
65 def __init__(self, phy):
66 debugfs_path = self.__debugfs_find()
68 # Construct the debugfs b43 path to the device
69 b43_path = debugfs_path + "/b43/"
74 phys = os.listdir(b43_path)
76 print "Could not find any b43 device"
79 print "Found multiple b43 devices."
80 print "You must call this tool with a phyX parameter to specify a device"
85 # Open the debugfs files
87 self.f_mmio16read = file(b43_path + "/mmio16read", "r+")
88 self.f_mmio16write = file(b43_path + "/mmio16write", "w")
89 self.f_mmio32read = file(b43_path + "/mmio32read", "r+")
90 self.f_mmio32write = file(b43_path + "/mmio32write", "w")
91 self.f_shm16read = file(b43_path + "/shm16read", "r+")
92 self.f_shm16write = file(b43_path + "/shm16write", "w")
93 self.f_shm32read = file(b43_path + "/shm32read", "r+")
94 self.f_shm32write = file(b43_path + "/shm32write", "w")
96 print "Could not open debugfs file %s: %s" % (e.filename, e.strerror)
99 self.b43_path = b43_path
102 # Get the debugfs mountpoint.
103 def __debugfs_find(self):
104 mtab = file("/etc/mtab").read().splitlines()
105 regexp = re.compile(r"^[\w\-_]+\s+([\w/\-_]+)\s+debugfs")
108 m = regexp.match(line)
113 print "Could not find debugfs in /etc/mtab"
117 def read16(self, reg):
118 """Do a 16bit MMIO read"""
120 self.f_mmio16read.seek(0)
121 self.f_mmio16read.write("0x%X" % reg)
122 self.f_mmio16read.flush()
123 self.f_mmio16read.seek(0)
124 val = self.f_mmio16read.read()
126 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
130 def read32(self, reg):
131 """Do a 32bit MMIO read"""
133 self.f_mmio32read.seek(0)
134 self.f_mmio32read.write("0x%X" % reg)
135 self.f_mmio32read.flush()
136 self.f_mmio32read.seek(0)
137 val = self.f_mmio32read.read()
139 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
143 def maskSet16(self, reg, mask, set):
144 """Do a 16bit MMIO mask-and-set operation"""
148 self.f_mmio16write.seek(0)
149 self.f_mmio16write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
150 self.f_mmio16write.flush()
152 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
156 def write16(self, reg, value):
157 """Do a 16bit MMIO write"""
158 self.maskSet16(reg, 0, value)
161 def maskSet32(self, reg, mask, set):
162 """Do a 32bit MMIO mask-and-set operation"""
166 self.f_mmio32write.seek(0)
167 self.f_mmio32write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
168 self.f_mmio32write.flush()
170 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
174 def write32(self, reg, value):
175 """Do a 32bit MMIO write"""
176 self.maskSet32(reg, 0, value)
179 def shmRead16(self, routing, offset):
180 """Do a 16bit SHM read"""
182 self.f_shm16read.seek(0)
183 self.f_shm16read.write("0x%X 0x%X" % (routing, offset))
184 self.f_shm16read.flush()
185 self.f_shm16read.seek(0)
186 val = self.f_shm16read.read()
188 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
192 def shmMaskSet16(self, routing, offset, mask, set):
193 """Do a 16bit SHM mask-and-set operation"""
197 self.f_shm16write.seek(0)
198 self.f_shm16write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
199 self.f_shm16write.flush()
201 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
205 def shmWrite16(self, routing, offset, value):
206 """Do a 16bit SHM write"""
207 self.shmMaskSet16(routing, offset, 0, value)
210 def shmRead32(self, routing, offset):
211 """Do a 32bit SHM read"""
213 self.f_shm32read.seek(0)
214 self.f_shm32read.write("0x%X 0x%X" % (routing, offset))
215 self.f_shm32read.flush()
216 self.f_shm32read.seek(0)
217 val = self.f_shm32read.read()
219 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
223 def shmMaskSet32(self, routing, offset, mask, set):
224 """Do a 32bit SHM mask-and-set operation"""
228 self.f_shm32write.seek(0)
229 self.f_shm32write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
230 self.f_shm32write.flush()
232 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
236 def shmWrite32(self, routing, offset, value):
237 """Do a 32bit SHM write"""
238 self.shmMaskSet32(routing, offset, 0, value)
242 """Returns an array of 64 ints. One for each General Purpose register."""
244 for i in range(0, 64):
245 val = self.shmRead16(B43_SHM_REGS, i)
249 def getLinkRegs(self):
250 """Returns an array of 4 ints. One for each Link Register."""
252 for i in range(0, 4):
253 val = self.read16(0x4D0 + (i * 2))
257 def getOffsetRegs(self):
258 """Returns an array of 7 ints. One for each Offset Register."""
260 for i in range(0, 7):
261 val = self.read16(0x4C0 + (i * 2))
265 def shmSharedRead(self):
266 """Returns a string containing the SHM contents."""
268 for i in range(0, 4096, 4):
269 val = self.shmRead32(B43_SHM_SHARED, i)
270 ret += "%c%c%c%c" % (val & 0xFF,
276 def getPsmDebug(self):
277 """Read the PSM-debug register and return an instance of B43PsmDebug."""
278 val = self.read32(B43_MMIO_PSMDEBUG)
279 return B43PsmDebug(val)
281 def getPsmConditions(self):
282 """This returns the contents of the programmable-PSM-conditions register."""
283 return self.read16(0x4D8)
286 """Unconditionally stop the microcode PSM. """
287 self.maskSet32(B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, 0)
290 def ucodeStart(self):
291 """Unconditionally start the microcode PSM. This will restart the
292 microcode on the current PC. It will not jump to 0. Warning: This will
293 unconditionally restart the PSM and ignore any driver-state!"""
294 self.maskSet32(B43_MMIO_MACCTL, ~0, B43_MACCTL_PSM_RUN)
298 """Disassembler for b43 firmware."""
299 def __init__(self, binaryText, b43DasmOpts):
300 input = NamedTemporaryFile()
301 output = NamedTemporaryFile()
303 input.write(binaryText)
305 #FIXME check b43-dasm errors
306 os.system("b43-dasm %s %s %s" % (input.name, output.name, b43DasmOpts))
308 self.asmText = output.read()
311 """Returns the assembly code."""
315 """Assembler for b43 firmware."""
316 def __init__(self, assemblyText, b43AsmOpts):
317 input = NamedTemporaryFile()
318 output = NamedTemporaryFile()
320 input.write(assemblyText)
322 #FIXME check b43-asm errors
323 os.system("b43-asm %s %s %s" % (input.name, output.name, b43AsmOpts))
325 self.binaryText = output.read()
328 """Returns the binary code."""
329 return self.binaryText
332 """A textfile patcher that does not include any target context.
333 This can be used to patch b43 firmware files."""
336 def __init__(self, index, line):
341 def __init__(self, text, expected_md5sum):
342 sum = md5.md5(text).hexdigest()
343 if sum != expected_md5sum:
344 print "Patcher: The text does not match the expected MD5 sum"
345 print "Expected: " + expected_md5sum
346 print "Calculated: " + sum
348 text = text.splitlines()
352 self.lines.append(TextPatcher.TextLine(i, line))
354 # Add an after-last dummy. Needed for the add-before logic
355 lastDummy = TextPatcher.TextLine(i, "")
356 lastDummy.deleted = True
357 self.lines.append(lastDummy)
360 """This returns the current text."""
364 textLines.append(l.line)
365 return "\n".join(textLines)
367 def delLine(self, linenumber):
368 """Delete a line of text. The linenumber corresponds to the
369 original unmodified text."""
371 if l.index == linenumber:
374 print "Patcher deleteLine: Did not find the line!"
377 def addText(self, beforeLineNumber, text):
378 """Add a text before the specified linenumber. The linenumber
379 corresponds to the original unmodified text."""
380 text = text.splitlines()
383 if l.index == beforeLineNumber:
386 if index >= len(self.lines):
387 print "Patcher addText: Did not find the line!"
390 self.lines.insert(index, TextPatcher.TextLine(-1, l))
393 class B43SymbolicSpr:
394 """This class converts numeric SPR names into symbolic SPR names."""
396 def __init__(self, header_file):
397 """The passed header_file parameter is a file path to the
398 assembly file containing the symbolic SPR definitions."""
400 defs = file(header_file).readlines()
402 print "B43SymbolicSpr: Could not read %s: %s" % (e.filename, e.strerror)
404 # Parse the definitions
406 r = re.compile(r"#define\s+(\w+)\s+(spr[a-fA-F0-9]+)")
410 continue # unknown line
413 self.spr_names[offset.lower()] = name
416 """Get the symbolic name for an SPR. The spr parameter
417 must be a string like "sprXXX", where XXX is a hex number."""
419 spr = self.spr_names[spr.lower()]
421 pass # Symbol not found. Return numeric name.
424 def getRaw(self, spr_hexnumber):
425 """Get the symbolic name for an SPR. The spr_hexnumber
426 parameter is the hexadecimal number for the SPR."""
427 return self.get("spr%03X" % spr_hexnumber)
429 class B43SymbolicShm:
430 """This class converts numeric SHM offsets into symbolic SHM names."""
432 def __init__(self, header_file):
433 """The passed header_file parameter is a file path to the
434 assembly file containing the symbolic SHM definitions."""
436 defs = file(header_file).readlines()
438 print "B43SymbolicShm: Could not read %s: %s" % (e.filename, e.strerror)
440 # Parse the definitions
442 in_abi_section = False
443 r = re.compile(r"#define\s+(\w+)\s+SHM\((\w+)\).*")
445 if line.startswith("/* BEGIN ABI"):
446 in_abi_section = True
447 if line.startswith("/* END ABI"):
448 in_abi_section = False
449 if not in_abi_section:
450 continue # Only parse ABI definitions
453 continue # unknown line
455 offset = int(m.group(2), 16)
457 self.shm_names[offset] = name
459 def get(self, shm_wordoffset):
460 """Get the symbolic name for an SHM offset."""
462 sym = self.shm_names[shm_wordoffset]
464 # Symbol not found. Return numeric name.
465 sym = "0x%03X" % shm_wordoffset
468 class B43SymbolicCondition:
469 """This class converts numeric External Conditions into symbolic names."""
471 def __init__(self, header_file):
472 """The passed header_file parameter is a file path to the
473 assembly file containing the symbolic condition definitions."""
475 defs = file(header_file).readlines()
477 print "B43SymbolicCondition: Could not read %s: %s" % (e.filename, e.strerror)
479 # Parse the definitions
480 self.cond_names = { }
481 r = re.compile(r"#define\s+(\w+)\s+EXTCOND\(\s*(\w+),\s*(\d+)\s*\).*")
485 continue # unknown line
487 register = m.group(2)
488 bit = int(m.group(3))
489 if register == "CONDREG_RX":
491 elif register == "CONDREG_TX":
493 elif register == "CONDREG_PHY":
495 elif register == "CONDREG_4":
497 elif register == "CONDREG_PSM":
498 continue # No lookup table for this one
499 elif register == "CONDREG_RCM":
501 elif register == "CONDREG_7":
504 continue # unknown register
505 cond_number = bit | (register << 4)
506 self.cond_names[cond_number] = name
508 def get(self, cond_number):
509 """Get the symbolic name for an External Condition."""
510 register = (cond_number >> 4) & 0x7
511 bit = cond_number & 0xF
512 eoi = ((cond_number & 0x80) != 0)
514 if register == 5: # PSM register
515 return "COND_PSM(%d)" % bit
517 sym = self.cond_names[cond_number]
519 # Symbol not found. Return numeric name.
520 sym = "0x%02X" % cond_number
522 sym = "EOI(%s)" % sym
526 def __init__(self, text):
532 def isInstruction(self):
535 class B43AsmInstruction(B43AsmLine):
536 def __init__(self, opcode):
543 def addOperand(self, operand):
544 self.operands.append(operand)
546 def getOperands(self):
550 ret = "\t" + self.opcode
553 for op in self.operands:
559 def isInstruction(self):
563 """A simple B43 assembly code parser."""
565 def __init__(self, asm_code):
566 self.__parse_code(asm_code)
568 def __parse_code(self, asm_code):
570 label = re.compile(r"^\s*\w+:\s*$")
571 insn_0 = re.compile(r"^\s*([@\.\w]+)\s*$")
572 insn_2 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
573 insn_3 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
574 insn_5 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
575 for line in asm_code.splitlines():
576 m = label.match(line)
579 self.codelines.append(l)
581 m = insn_0.match(line)
583 insn = B43AsmInstruction(m.group(1))
584 self.codelines.append(insn)
586 m = insn_2.match(line)
588 insn = B43AsmInstruction(m.group(1))
589 insn.addOperand(m.group(2))
590 insn.addOperand(m.group(3))
591 self.codelines.append(insn)
593 m = insn_3.match(line)
594 if m: # Three operands
595 insn = B43AsmInstruction(m.group(1))
596 insn.addOperand(m.group(2))
597 insn.addOperand(m.group(3))
598 insn.addOperand(m.group(4))
599 self.codelines.append(insn)
601 m = insn_5.match(line)
602 if m: # Three operands
603 insn = B43AsmInstruction(m.group(1))
604 insn.addOperand(m.group(2))
605 insn.addOperand(m.group(3))
606 insn.addOperand(m.group(4))
607 insn.addOperand(m.group(5))
608 insn.addOperand(m.group(6))
609 self.codelines.append(insn)
613 self.codelines.append(l)
615 class B43Beautifier(B43AsmParser):
616 """A B43 assembly code beautifier."""
618 def __init__(self, asm_code, headers_dir):
619 """asm_code is the assembly code. headers_dir is a full
620 path to the directory containing the symbolic SPR,SHM,etc... definitions"""
621 B43AsmParser.__init__(self, asm_code)
622 self.symSpr = B43SymbolicSpr(headers_dir + "/spr.inc")
623 self.symShm = B43SymbolicShm(headers_dir + "/shm.inc")
624 self.symCond = B43SymbolicCondition(headers_dir + "/cond.inc")
625 self.preamble = "#include <%s/spr.inc>\n" % headers_dir
626 self.preamble += "#include <%s/shm.inc>\n" % headers_dir
627 self.preamble += "#include <%s/cond.inc>\n" % headers_dir
628 self.preamble += "\n"
629 self.__process_code()
631 def __process_code(self):
632 spr_re = re.compile(r"^spr\w\w\w$")
633 shm_re = re.compile(r"^\[(0x\w+)\]$")
634 code = self.codelines
636 if not line.isInstruction():
638 opcode = line.getOpcode()
639 operands = line.getOperands()
640 if opcode == "jext" or opcode == "jnext":
641 operands[0] = self.symCond.get(int(operands[0], 16))
643 for i in range(0, len(operands)):
647 operands[i] = self.symSpr.get(o)
651 offset = int(m.group(1), 16)
652 operands[i] = "[" + self.symShm.get(offset) + "]"
656 """Returns the beautified asm code."""
658 for line in self.codelines:
659 ret += line.getLine() + "\n"