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/"
75 phys = os.listdir(b43_path)
77 print "Could not find B43's debugfs directory: %s" % b43_path
80 print "Could not find any b43 device"
83 print "Found multiple b43 devices."
84 print "You must call this tool with a phyX parameter to specify a device"
89 # Open the debugfs files
91 self.f_mmio16read = file(b43_path + "/mmio16read", "r+")
92 self.f_mmio16write = file(b43_path + "/mmio16write", "w")
93 self.f_mmio32read = file(b43_path + "/mmio32read", "r+")
94 self.f_mmio32write = file(b43_path + "/mmio32write", "w")
95 self.f_shm16read = file(b43_path + "/shm16read", "r+")
96 self.f_shm16write = file(b43_path + "/shm16write", "w")
97 self.f_shm32read = file(b43_path + "/shm32read", "r+")
98 self.f_shm32write = file(b43_path + "/shm32write", "w")
100 print "Could not open debugfs file %s: %s" % (e.filename, e.strerror)
103 self.b43_path = b43_path
106 # Get the debugfs mountpoint.
107 def __debugfs_find(self):
108 mtab = file("/etc/mtab").read().splitlines()
109 regexp = re.compile(r"^[\w\-_]+\s+([\w/\-_]+)\s+debugfs")
112 m = regexp.match(line)
117 print "Could not find debugfs in /etc/mtab"
121 def read16(self, reg):
122 """Do a 16bit MMIO read"""
124 self.f_mmio16read.seek(0)
125 self.f_mmio16read.write("0x%X" % reg)
126 self.f_mmio16read.flush()
127 self.f_mmio16read.seek(0)
128 val = self.f_mmio16read.read()
130 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
134 def read32(self, reg):
135 """Do a 32bit MMIO read"""
137 self.f_mmio32read.seek(0)
138 self.f_mmio32read.write("0x%X" % reg)
139 self.f_mmio32read.flush()
140 self.f_mmio32read.seek(0)
141 val = self.f_mmio32read.read()
143 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
147 def maskSet16(self, reg, mask, set):
148 """Do a 16bit MMIO mask-and-set operation"""
152 self.f_mmio16write.seek(0)
153 self.f_mmio16write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
154 self.f_mmio16write.flush()
156 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
160 def write16(self, reg, value):
161 """Do a 16bit MMIO write"""
162 self.maskSet16(reg, 0, value)
165 def maskSet32(self, reg, mask, set):
166 """Do a 32bit MMIO mask-and-set operation"""
170 self.f_mmio32write.seek(0)
171 self.f_mmio32write.write("0x%X 0x%X 0x%X" % (reg, mask, set))
172 self.f_mmio32write.flush()
174 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
178 def write32(self, reg, value):
179 """Do a 32bit MMIO write"""
180 self.maskSet32(reg, 0, value)
183 def shmRead16(self, routing, offset):
184 """Do a 16bit SHM read"""
186 self.f_shm16read.seek(0)
187 self.f_shm16read.write("0x%X 0x%X" % (routing, offset))
188 self.f_shm16read.flush()
189 self.f_shm16read.seek(0)
190 val = self.f_shm16read.read()
192 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
196 def shmMaskSet16(self, routing, offset, mask, set):
197 """Do a 16bit SHM mask-and-set operation"""
201 self.f_shm16write.seek(0)
202 self.f_shm16write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
203 self.f_shm16write.flush()
205 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
209 def shmWrite16(self, routing, offset, value):
210 """Do a 16bit SHM write"""
211 self.shmMaskSet16(routing, offset, 0, value)
214 def shmRead32(self, routing, offset):
215 """Do a 32bit SHM read"""
217 self.f_shm32read.seek(0)
218 self.f_shm32read.write("0x%X 0x%X" % (routing, offset))
219 self.f_shm32read.flush()
220 self.f_shm32read.seek(0)
221 val = self.f_shm32read.read()
223 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
227 def shmMaskSet32(self, routing, offset, mask, set):
228 """Do a 32bit SHM mask-and-set operation"""
232 self.f_shm32write.seek(0)
233 self.f_shm32write.write("0x%X 0x%X 0x%X 0x%X" % (routing, offset, mask, set))
234 self.f_shm32write.flush()
236 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
240 def shmWrite32(self, routing, offset, value):
241 """Do a 32bit SHM write"""
242 self.shmMaskSet32(routing, offset, 0, value)
246 """Returns an array of 64 ints. One for each General Purpose register."""
248 for i in range(0, 64):
249 val = self.shmRead16(B43_SHM_REGS, i)
253 def getLinkRegs(self):
254 """Returns an array of 4 ints. One for each Link Register."""
256 for i in range(0, 4):
257 val = self.read16(0x4D0 + (i * 2))
261 def getOffsetRegs(self):
262 """Returns an array of 7 ints. One for each Offset Register."""
264 for i in range(0, 7):
265 val = self.read16(0x4C0 + (i * 2))
269 def shmSharedRead(self):
270 """Returns a string containing the SHM contents."""
272 for i in range(0, 4096, 4):
273 val = self.shmRead32(B43_SHM_SHARED, i)
274 ret += "%c%c%c%c" % (val & 0xFF,
280 def getPsmDebug(self):
281 """Read the PSM-debug register and return an instance of B43PsmDebug."""
282 val = self.read32(B43_MMIO_PSMDEBUG)
283 return B43PsmDebug(val)
285 def getPsmConditions(self):
286 """This returns the contents of the programmable-PSM-conditions register."""
287 return self.read16(0x4D8)
290 """Unconditionally stop the microcode PSM. """
291 self.maskSet32(B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, 0)
294 def ucodeStart(self):
295 """Unconditionally start the microcode PSM. This will restart the
296 microcode on the current PC. It will not jump to 0. Warning: This will
297 unconditionally restart the PSM and ignore any driver-state!"""
298 self.maskSet32(B43_MMIO_MACCTL, ~0, B43_MACCTL_PSM_RUN)
302 """Disassembler for b43 firmware."""
303 def __init__(self, binaryText, b43DasmOpts):
304 input = NamedTemporaryFile()
305 output = NamedTemporaryFile()
307 input.write(binaryText)
309 #FIXME check b43-dasm errors
310 os.system("b43-dasm %s %s %s" % (input.name, output.name, b43DasmOpts))
312 self.asmText = output.read()
315 """Returns the assembly code."""
319 """Assembler for b43 firmware."""
320 def __init__(self, assemblyText, b43AsmOpts):
321 input = NamedTemporaryFile()
322 output = NamedTemporaryFile()
324 input.write(assemblyText)
326 #FIXME check b43-asm errors
327 os.system("b43-asm %s %s %s" % (input.name, output.name, b43AsmOpts))
329 self.binaryText = output.read()
332 """Returns the binary code."""
333 return self.binaryText
336 """A textfile patcher that does not include any target context.
337 This can be used to patch b43 firmware files."""
340 def __init__(self, index, line):
345 def __init__(self, text, expected_md5sum):
346 sum = hashlib.md5(text).hexdigest()
347 if sum != expected_md5sum:
348 print "Patcher: The text does not match the expected MD5 sum"
349 print "Expected: " + expected_md5sum
350 print "Calculated: " + sum
352 text = text.splitlines()
356 self.lines.append(TextPatcher.TextLine(i, line))
358 # Add an after-last dummy. Needed for the add-before logic
359 lastDummy = TextPatcher.TextLine(i, "")
360 lastDummy.deleted = True
361 self.lines.append(lastDummy)
364 """This returns the current text."""
368 textLines.append(l.line)
369 return "\n".join(textLines)
371 def delLine(self, linenumber):
372 """Delete a line of text. The linenumber corresponds to the
373 original unmodified text."""
375 if l.index == linenumber:
378 print "Patcher deleteLine: Did not find the line!"
381 def addText(self, beforeLineNumber, text):
382 """Add a text before the specified linenumber. The linenumber
383 corresponds to the original unmodified text."""
384 text = text.splitlines()
387 if l.index == beforeLineNumber:
390 if index >= len(self.lines):
391 print "Patcher addText: Did not find the line!"
394 self.lines.insert(index, TextPatcher.TextLine(-1, l))
397 class B43SymbolicSpr:
398 """This class converts numeric SPR names into symbolic SPR names."""
400 def __init__(self, header_file):
401 """The passed header_file parameter is a file path to the
402 assembly file containing the symbolic SPR definitions."""
404 defs = file(header_file).readlines()
406 print "B43SymbolicSpr: Could not read %s: %s" % (e.filename, e.strerror)
408 # Parse the definitions
410 r = re.compile(r"#define\s+(\w+)\s+(spr[a-fA-F0-9]+)")
414 continue # unknown line
417 self.spr_names[offset.lower()] = name
420 """Get the symbolic name for an SPR. The spr parameter
421 must be a string like "sprXXX", where XXX is a hex number."""
423 spr = self.spr_names[spr.lower()]
425 pass # Symbol not found. Return numeric name.
428 def getRaw(self, spr_hexnumber):
429 """Get the symbolic name for an SPR. The spr_hexnumber
430 parameter is the hexadecimal number for the SPR."""
431 return self.get("spr%03X" % spr_hexnumber)
433 class B43SymbolicShm:
434 """This class converts numeric SHM offsets into symbolic SHM names."""
436 def __init__(self, header_file):
437 """The passed header_file parameter is a file path to the
438 assembly file containing the symbolic SHM definitions."""
440 defs = file(header_file).readlines()
442 print "B43SymbolicShm: Could not read %s: %s" % (e.filename, e.strerror)
444 # Parse the definitions
446 in_abi_section = False
447 r = re.compile(r"#define\s+(\w+)\s+SHM\((\w+)\).*")
449 if line.startswith("/* BEGIN ABI"):
450 in_abi_section = True
451 if line.startswith("/* END ABI"):
452 in_abi_section = False
453 if not in_abi_section:
454 continue # Only parse ABI definitions
457 continue # unknown line
459 offset = int(m.group(2), 16)
461 self.shm_names[offset] = name
463 def get(self, shm_wordoffset):
464 """Get the symbolic name for an SHM offset."""
466 sym = self.shm_names[shm_wordoffset]
468 # Symbol not found. Return numeric name.
469 sym = "0x%03X" % shm_wordoffset
472 class B43SymbolicCondition:
473 """This class converts numeric External Conditions into symbolic names."""
475 def __init__(self, header_file):
476 """The passed header_file parameter is a file path to the
477 assembly file containing the symbolic condition definitions."""
479 defs = file(header_file).readlines()
481 print "B43SymbolicCondition: Could not read %s: %s" % (e.filename, e.strerror)
483 # Parse the definitions
484 self.cond_names = { }
485 r = re.compile(r"#define\s+(\w+)\s+EXTCOND\(\s*(\w+),\s*(\d+)\s*\).*")
489 continue # unknown line
491 register = m.group(2)
492 bit = int(m.group(3))
493 if register == "CONDREG_RX":
495 elif register == "CONDREG_TX":
497 elif register == "CONDREG_PHY":
499 elif register == "CONDREG_4":
501 elif register == "CONDREG_PSM":
502 continue # No lookup table for this one
503 elif register == "CONDREG_RCM":
505 elif register == "CONDREG_7":
508 continue # unknown register
509 cond_number = bit | (register << 4)
510 self.cond_names[cond_number] = name
512 def get(self, cond_number):
513 """Get the symbolic name for an External Condition."""
514 register = (cond_number >> 4) & 0x7
515 bit = cond_number & 0xF
516 eoi = ((cond_number & 0x80) != 0)
518 if register == 5: # PSM register
519 return "COND_PSM(%d)" % bit
521 sym = self.cond_names[cond_number]
523 # Symbol not found. Return numeric name.
524 sym = "0x%02X" % cond_number
526 sym = "EOI(%s)" % sym
530 def __init__(self, text):
536 def isInstruction(self):
539 class B43AsmInstruction(B43AsmLine):
540 def __init__(self, opcode):
547 def addOperand(self, operand):
548 self.operands.append(operand)
550 def getOperands(self):
554 ret = "\t" + self.opcode
557 for op in self.operands:
563 def isInstruction(self):
567 """A simple B43 assembly code parser."""
569 def __init__(self, asm_code):
570 self.__parse_code(asm_code)
572 def __parse_code(self, asm_code):
574 label = re.compile(r"^\s*\w+:\s*$")
575 insn_0 = re.compile(r"^\s*([@\.\w]+)\s*$")
576 insn_2 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
577 insn_3 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
578 insn_5 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
579 for line in asm_code.splitlines():
580 m = label.match(line)
583 self.codelines.append(l)
585 m = insn_0.match(line)
587 insn = B43AsmInstruction(m.group(1))
588 self.codelines.append(insn)
590 m = insn_2.match(line)
592 insn = B43AsmInstruction(m.group(1))
593 insn.addOperand(m.group(2))
594 insn.addOperand(m.group(3))
595 self.codelines.append(insn)
597 m = insn_3.match(line)
598 if m: # Three operands
599 insn = B43AsmInstruction(m.group(1))
600 insn.addOperand(m.group(2))
601 insn.addOperand(m.group(3))
602 insn.addOperand(m.group(4))
603 self.codelines.append(insn)
605 m = insn_5.match(line)
606 if m: # Three operands
607 insn = B43AsmInstruction(m.group(1))
608 insn.addOperand(m.group(2))
609 insn.addOperand(m.group(3))
610 insn.addOperand(m.group(4))
611 insn.addOperand(m.group(5))
612 insn.addOperand(m.group(6))
613 self.codelines.append(insn)
617 self.codelines.append(l)
619 class B43Beautifier(B43AsmParser):
620 """A B43 assembly code beautifier."""
622 def __init__(self, asm_code, headers_dir):
623 """asm_code is the assembly code. headers_dir is a full
624 path to the directory containing the symbolic SPR,SHM,etc... definitions"""
625 B43AsmParser.__init__(self, asm_code)
626 self.symSpr = B43SymbolicSpr(headers_dir + "/spr.inc")
627 self.symShm = B43SymbolicShm(headers_dir + "/shm.inc")
628 self.symCond = B43SymbolicCondition(headers_dir + "/cond.inc")
629 self.preamble = "#include <%s/spr.inc>\n" % headers_dir
630 self.preamble += "#include <%s/shm.inc>\n" % headers_dir
631 self.preamble += "#include <%s/cond.inc>\n" % headers_dir
632 self.preamble += "\n"
633 self.__process_code()
635 def __process_code(self):
636 spr_re = re.compile(r"^spr\w\w\w$")
637 shm_re = re.compile(r"^\[(0x\w+)\]$")
638 code = self.codelines
640 if not line.isInstruction():
642 opcode = line.getOpcode()
643 operands = line.getOperands()
644 if opcode == "jext" or opcode == "jnext":
645 operands[0] = self.symCond.get(int(operands[0], 16))
647 for i in range(0, len(operands)):
651 operands[i] = self.symSpr.get(o)
655 offset = int(m.group(1), 16)
656 operands[i] = "[" + self.symShm.get(offset) + "]"
660 """Returns the beautified asm code."""
662 for line in self.codelines:
663 ret += line.getLine() + "\n"