nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 """
2 # b43 debugging library
3 #
4 # Copyright (C) 2008-2010 Michael Buesch <m@bues.ch>
5 #
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.
9 #
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.
14 #
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/>.
17 """
18  
19 import sys
20 import os
21 import re
22 import hashlib
23 from tempfile import *
24  
25  
26 # SHM routing values
27 B43_SHM_UCODE = 0
28 B43_SHM_SHARED = 1
29 B43_SHM_REGS = 2
30 B43_SHM_IHR = 3
31 B43_SHM_RCMTA = 4
32  
33  
34 class B43Exception(Exception):
35 pass
36  
37  
38 B43_MMIO_MACCTL = 0x120
39 B43_MMIO_PSMDEBUG = 0x154
40  
41 B43_MACCTL_PSM_MACEN = 0x00000001
42 B43_MACCTL_PSM_RUN = 0x00000002
43 B43_MACCTL_PSM_JMP0 = 0x00000004
44 B43_MACCTL_PSM_DEBUG = 0x00002000
45  
46  
47 class B43PsmDebug:
48 """Parse the contents of the PSM-debug register"""
49 def __init__(self, reg_content):
50 self.raw = reg_content
51 return
52  
53 def getRaw(self):
54 """Get the raw PSM-debug register value"""
55 return self.raw
56  
57 def getPc(self):
58 """Get the microcode program counter"""
59 return self.raw & 0xFFF
60  
61  
62 class B43:
63 """Hardware access layer. This accesses the hardware through the debugfs interface."""
64  
65 def __init__(self, phy=None):
66 debugfs_path = self.__debugfs_find()
67  
68 # Construct the debugfs b43 path to the device
69 b43_path = debugfs_path + "/b43/"
70 if phy:
71 b43_path += phy
72 else:
73 # Get the PHY.
74 try:
75 phys = os.listdir(b43_path)
76 except OSError:
77 print "Could not find B43's debugfs directory: %s" % b43_path
78 raise B43Exception
79 if not phys:
80 print "Could not find any b43 device"
81 raise B43Exception
82 if len(phys) != 1:
83 print "Found multiple b43 devices."
84 print "You must call this tool with a phyX parameter to specify a device"
85 raise B43Exception
86 phy = phys[0]
87 b43_path += phy;
88  
89 # Open the debugfs files
90 try:
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")
99 except IOError, e:
100 print "Could not open debugfs file %s: %s" % (e.filename, e.strerror)
101 raise B43Exception
102  
103 self.b43_path = b43_path
104 return
105  
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")
110 path = None
111 for line in mtab:
112 m = regexp.match(line)
113 if m:
114 path = m.group(1)
115 break
116 if not path:
117 print "Could not find debugfs in /etc/mtab"
118 raise B43Exception
119 return path
120  
121 def read16(self, reg):
122 """Do a 16bit MMIO read"""
123 try:
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()
129 except IOError, e:
130 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
131 raise B43Exception
132 return int(val, 16)
133  
134 def read32(self, reg):
135 """Do a 32bit MMIO read"""
136 try:
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()
142 except IOError, e:
143 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
144 raise B43Exception
145 return int(val, 16)
146  
147 def maskSet16(self, reg, mask, set):
148 """Do a 16bit MMIO mask-and-set operation"""
149 try:
150 mask &= 0xFFFF
151 set &= 0xFFFF
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()
155 except IOError, e:
156 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
157 raise B43Exception
158 return
159  
160 def write16(self, reg, value):
161 """Do a 16bit MMIO write"""
162 self.maskSet16(reg, 0, value)
163 return
164  
165 def maskSet32(self, reg, mask, set):
166 """Do a 32bit MMIO mask-and-set operation"""
167 try:
168 mask &= 0xFFFFFFFF
169 set &= 0xFFFFFFFF
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()
173 except IOError, e:
174 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
175 raise B43Exception
176 return
177  
178 def write32(self, reg, value):
179 """Do a 32bit MMIO write"""
180 self.maskSet32(reg, 0, value)
181 return
182  
183 def shmRead16(self, routing, offset):
184 """Do a 16bit SHM read"""
185 try:
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()
191 except IOError, e:
192 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
193 raise B43Exception
194 return int(val, 16)
195  
196 def shmMaskSet16(self, routing, offset, mask, set):
197 """Do a 16bit SHM mask-and-set operation"""
198 try:
199 mask &= 0xFFFF
200 set &= 0xFFFF
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()
204 except IOError, e:
205 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
206 raise B43Exception
207 return
208  
209 def shmWrite16(self, routing, offset, value):
210 """Do a 16bit SHM write"""
211 self.shmMaskSet16(routing, offset, 0, value)
212 return
213  
214 def shmRead32(self, routing, offset):
215 """Do a 32bit SHM read"""
216 try:
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()
222 except IOError, e:
223 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
224 raise B43Exception
225 return int(val, 16)
226  
227 def shmMaskSet32(self, routing, offset, mask, set):
228 """Do a 32bit SHM mask-and-set operation"""
229 try:
230 mask &= 0xFFFFFFFF
231 set &= 0xFFFFFFFF
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()
235 except IOError, e:
236 print "Could not access debugfs file %s: %s" % (e.filename, e.strerror)
237 raise B43Exception
238 return
239  
240 def shmWrite32(self, routing, offset, value):
241 """Do a 32bit SHM write"""
242 self.shmMaskSet32(routing, offset, 0, value)
243 return
244  
245 def getGprs(self):
246 """Returns an array of 64 ints. One for each General Purpose register."""
247 ret = []
248 for i in range(0, 64):
249 val = self.shmRead16(B43_SHM_REGS, i)
250 ret.append(val)
251 return ret
252  
253 def getLinkRegs(self):
254 """Returns an array of 4 ints. One for each Link Register."""
255 ret = []
256 for i in range(0, 4):
257 val = self.read16(0x4D0 + (i * 2))
258 ret.append(val)
259 return ret
260  
261 def getOffsetRegs(self):
262 """Returns an array of 7 ints. One for each Offset Register."""
263 ret = []
264 for i in range(0, 7):
265 val = self.read16(0x4C0 + (i * 2))
266 ret.append(val)
267 return ret
268  
269 def shmSharedRead(self):
270 """Returns a string containing the SHM contents."""
271 ret = ""
272 for i in range(0, 4096, 4):
273 val = self.shmRead32(B43_SHM_SHARED, i)
274 ret += "%c%c%c%c" % (val & 0xFF,
275 (val >> 8) & 0xFF,
276 (val >> 16) & 0xFF,
277 (val >> 24) & 0xFF)
278 return ret
279  
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)
284  
285 def getPsmConditions(self):
286 """This returns the contents of the programmable-PSM-conditions register."""
287 return self.read16(0x4D8)
288  
289 def ucodeStop(self):
290 """Unconditionally stop the microcode PSM. """
291 self.maskSet32(B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN, 0)
292 return
293  
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)
299 return
300  
301 class Disassembler:
302 """Disassembler for b43 firmware."""
303 def __init__(self, binaryText, b43DasmOpts):
304 input = NamedTemporaryFile()
305 output = NamedTemporaryFile()
306  
307 input.write(binaryText)
308 input.flush()
309 #FIXME check b43-dasm errors
310 os.system("b43-dasm %s %s %s" % (input.name, output.name, b43DasmOpts))
311  
312 self.asmText = output.read()
313  
314 def getAsm(self):
315 """Returns the assembly code."""
316 return self.asmText
317  
318 class Assembler:
319 """Assembler for b43 firmware."""
320 def __init__(self, assemblyText, b43AsmOpts):
321 input = NamedTemporaryFile()
322 output = NamedTemporaryFile()
323  
324 input.write(assemblyText)
325 input.flush()
326 #FIXME check b43-asm errors
327 os.system("b43-asm %s %s %s" % (input.name, output.name, b43AsmOpts))
328  
329 self.binaryText = output.read()
330  
331 def getBinary(self):
332 """Returns the binary code."""
333 return self.binaryText
334  
335 class TextPatcher:
336 """A textfile patcher that does not include any target context.
337 This can be used to patch b43 firmware files."""
338  
339 class TextLine:
340 def __init__(self, index, line):
341 self.index = index
342 self.line = line
343 self.deleted = False
344  
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
351 raise B43Exception
352 text = text.splitlines()
353 self.lines = []
354 i = 0
355 for line in text:
356 self.lines.append(TextPatcher.TextLine(i, line))
357 i += 1
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)
362  
363 def getText(self):
364 """This returns the current text."""
365 textLines = []
366 for l in self.lines:
367 if not l.deleted:
368 textLines.append(l.line)
369 return "\n".join(textLines)
370  
371 def delLine(self, linenumber):
372 """Delete a line of text. The linenumber corresponds to the
373 original unmodified text."""
374 for l in self.lines:
375 if l.index == linenumber:
376 l.deleted = True
377 return
378 print "Patcher deleteLine: Did not find the line!"
379 raise B43Exception
380  
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()
385 index = 0
386 for l in self.lines:
387 if l.index == beforeLineNumber:
388 break
389 index += 1
390 if index >= len(self.lines):
391 print "Patcher addText: Did not find the line!"
392 raise B43Exception
393 for l in text:
394 self.lines.insert(index, TextPatcher.TextLine(-1, l))
395 index += 1
396  
397 class B43SymbolicSpr:
398 """This class converts numeric SPR names into symbolic SPR names."""
399  
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."""
403 try:
404 defs = file(header_file).readlines()
405 except IOError, e:
406 print "B43SymbolicSpr: Could not read %s: %s" % (e.filename, e.strerror)
407 B43Exception
408 # Parse the definitions
409 self.spr_names = { }
410 r = re.compile(r"#define\s+(\w+)\s+(spr[a-fA-F0-9]+)")
411 for line in defs:
412 m = r.match(line)
413 if not m:
414 continue # unknown line
415 name = m.group(1)
416 offset = m.group(2)
417 self.spr_names[offset.lower()] = name
418  
419 def get(self, spr):
420 """Get the symbolic name for an SPR. The spr parameter
421 must be a string like "sprXXX", where XXX is a hex number."""
422 try:
423 spr = self.spr_names[spr.lower()]
424 except KeyError:
425 pass # Symbol not found. Return numeric name.
426 return spr
427  
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)
432  
433 class B43SymbolicShm:
434 """This class converts numeric SHM offsets into symbolic SHM names."""
435  
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."""
439 try:
440 defs = file(header_file).readlines()
441 except IOError, e:
442 print "B43SymbolicShm: Could not read %s: %s" % (e.filename, e.strerror)
443 raise B43Exception
444 # Parse the definitions
445 self.shm_names = { }
446 in_abi_section = False
447 r = re.compile(r"#define\s+(\w+)\s+SHM\((\w+)\).*")
448 for line in defs:
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
455 m = r.match(line)
456 if not m:
457 continue # unknown line
458 name = m.group(1)
459 offset = int(m.group(2), 16)
460 offset /= 2
461 self.shm_names[offset] = name
462  
463 def get(self, shm_wordoffset):
464 """Get the symbolic name for an SHM offset."""
465 try:
466 sym = self.shm_names[shm_wordoffset]
467 except KeyError:
468 # Symbol not found. Return numeric name.
469 sym = "0x%03X" % shm_wordoffset
470 return sym
471  
472 class B43SymbolicCondition:
473 """This class converts numeric External Conditions into symbolic names."""
474  
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."""
478 try:
479 defs = file(header_file).readlines()
480 except IOError, e:
481 print "B43SymbolicCondition: Could not read %s: %s" % (e.filename, e.strerror)
482 raise B43Exception
483 # Parse the definitions
484 self.cond_names = { }
485 r = re.compile(r"#define\s+(\w+)\s+EXTCOND\(\s*(\w+),\s*(\d+)\s*\).*")
486 for line in defs:
487 m = r.match(line)
488 if not m:
489 continue # unknown line
490 name = m.group(1)
491 register = m.group(2)
492 bit = int(m.group(3))
493 if register == "CONDREG_RX":
494 register = 0
495 elif register == "CONDREG_TX":
496 register = 2
497 elif register == "CONDREG_PHY":
498 register = 3
499 elif register == "CONDREG_4":
500 register = 4
501 elif register == "CONDREG_PSM":
502 continue # No lookup table for this one
503 elif register == "CONDREG_RCM":
504 register = 6
505 elif register == "CONDREG_7":
506 register = 7
507 else:
508 continue # unknown register
509 cond_number = bit | (register << 4)
510 self.cond_names[cond_number] = name
511  
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)
517 cond_number &= ~0x80
518 if register == 5: # PSM register
519 return "COND_PSM(%d)" % bit
520 try:
521 sym = self.cond_names[cond_number]
522 except KeyError:
523 # Symbol not found. Return numeric name.
524 sym = "0x%02X" % cond_number
525 if eoi:
526 sym = "EOI(%s)" % sym
527 return sym
528  
529 class B43AsmLine:
530 def __init__(self, text):
531 self.text = text
532  
533 def getLine(self):
534 return self.text
535  
536 def __repr__(self):
537 return self.getLine()
538  
539 def isInstruction(self):
540 return False
541  
542 class B43AsmInstruction(B43AsmLine):
543 def __init__(self, opcode):
544 self.setOpcode(opcode)
545 self.clearOperands()
546  
547 def getOpcode(self):
548 return self.opcode
549  
550 def setOpcode(self, opcode):
551 self.opcode = opcode
552  
553 def clearOperands(self):
554 self.operands = []
555  
556 def addOperand(self, operand):
557 self.operands.append(operand)
558  
559 def getOperands(self):
560 return self.operands
561  
562 def getLine(self):
563 ret = "\t" + self.opcode
564 if self.operands:
565 ret += "\t"
566 for op in self.operands:
567 ret += op + ", "
568 if self.operands:
569 ret = ret[:-2]
570 return ret
571  
572 def isInstruction(self):
573 return True
574  
575 class B43AsmParser:
576 """A simple B43 assembly code parser."""
577  
578 def __init__(self, asm_code):
579 self.__parse_code(asm_code)
580  
581 def __parse_code(self, asm_code):
582 self.codelines = []
583 label = re.compile(r"^\s*\w+:\s*$")
584 insn_0 = re.compile(r"^\s*([@\.\w]+)\s*$")
585 insn_2 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
586 insn_3 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
587 insn_5 = re.compile(r"^\s*([@\.\w]+)\s+([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+),\s*([@\[\],\w]+)\s*$")
588 for line in asm_code.splitlines():
589 m = label.match(line)
590 if m: # Label:
591 l = B43AsmLine(line)
592 self.codelines.append(l)
593 continue
594 m = insn_0.match(line)
595 if m: # No operands
596 insn = B43AsmInstruction(m.group(1))
597 self.codelines.append(insn)
598 continue
599 m = insn_2.match(line)
600 if m: # Two operands
601 insn = B43AsmInstruction(m.group(1))
602 insn.addOperand(m.group(2))
603 insn.addOperand(m.group(3))
604 self.codelines.append(insn)
605 continue
606 m = insn_3.match(line)
607 if m: # Three operands
608 insn = B43AsmInstruction(m.group(1))
609 insn.addOperand(m.group(2))
610 insn.addOperand(m.group(3))
611 insn.addOperand(m.group(4))
612 self.codelines.append(insn)
613 continue
614 m = insn_5.match(line)
615 if m: # Three operands
616 insn = B43AsmInstruction(m.group(1))
617 insn.addOperand(m.group(2))
618 insn.addOperand(m.group(3))
619 insn.addOperand(m.group(4))
620 insn.addOperand(m.group(5))
621 insn.addOperand(m.group(6))
622 self.codelines.append(insn)
623 continue
624 # Unknown line
625 l = B43AsmLine(line)
626 self.codelines.append(l)
627  
628 class B43Beautifier(B43AsmParser):
629 """A B43 assembly code beautifier."""
630  
631 def __init__(self, asm_code, headers_dir):
632 """asm_code is the assembly code. headers_dir is a full
633 path to the directory containing the symbolic SPR,SHM,etc... definitions"""
634 if headers_dir.endswith("/"):
635 headers_dir = headers_dir[:-1]
636 B43AsmParser.__init__(self, asm_code)
637 self.symSpr = B43SymbolicSpr(headers_dir + "/spr.inc")
638 self.symShm = B43SymbolicShm(headers_dir + "/shm.inc")
639 self.symCond = B43SymbolicCondition(headers_dir + "/cond.inc")
640 self.preamble = "#include \"%s/spr.inc\"\n" % headers_dir
641 self.preamble += "#include \"%s/shm.inc\"\n" % headers_dir
642 self.preamble += "#include \"%s/cond.inc\"\n" % headers_dir
643 self.preamble += "\n"
644 self.__process_code()
645  
646 def __process_code(self):
647 spr_re = re.compile(r"^spr\w\w\w$")
648 shm_re = re.compile(r"^\[(0x\w+)\]$")
649 code = self.codelines
650 for line in code:
651 if not line.isInstruction():
652 continue
653 opcode = line.getOpcode()
654 operands = line.getOperands()
655 # Transform unconditional jump
656 if opcode == "jext" and int(operands[0], 16) == 0x7F:
657 label = operands[1]
658 line.setOpcode("jmp")
659 line.clearOperands()
660 line.addOperand(label)
661 continue
662 # Transform external conditions
663 if opcode == "jext" or opcode == "jnext":
664 operands[0] = self.symCond.get(int(operands[0], 16))
665 continue
666 # Transform orx 7,8,imm,imm,target to mov
667 if opcode == "orx" and \
668 int(operands[0], 16) == 7 and int(operands[1], 16) == 8 and\
669 operands[2].startswith("0x") and operands[3].startswith("0x"):
670 value = int(operands[3], 16) & 0xFF
671 value |= (int(operands[2], 16) & 0xFF) << 8
672 target = operands[4]
673 line.setOpcode("mov")
674 line.clearOperands()
675 line.addOperand("0x%X" % value)
676 line.addOperand(target)
677 opcode = line.getOpcode()
678 operands = line.getOperands()
679 for i in range(0, len(operands)):
680 o = operands[i]
681 # Transform SPR operands
682 m = spr_re.match(o)
683 if m:
684 operands[i] = self.symSpr.get(o)
685 continue
686 # Transform SHM operands
687 m = shm_re.match(o)
688 if m:
689 offset = int(m.group(1), 16)
690 operands[i] = "[" + self.symShm.get(offset) + "]"
691 continue
692  
693 def getAsm(self):
694 """Returns the beautified asm code."""
695 ret = [ self.preamble ]
696 for line in self.codelines:
697 ret.append(str(line))
698 return "\n".join(ret)