#!/usr/bin/python # # IEEE 1212/ IEEE 1394 Configuration ROM pretty printer # # Copyright 2010 Stefan Richter # You can use, modify, and/or redistribute this program at your leisure. # # Reads Configuration ROM data from stdin (binary data, i.e. a big endian # or little endian quadlet array, or a firewire-ohci debug log) and # writes a human-readable annotated representation to stdout. # # 2010-02-16: initial release # import sys def read_le32_data(s, l, rom): for i in xrange(0, l / 4): j = i * 4 rom[i] = (ord(s[j ]) ) + \ (ord(s[j + 1]) << 8) + \ (ord(s[j + 2]) << 16) + \ (ord(s[j + 3]) << 24) def read_be32_data(s, l, rom): for i in xrange(0, l / 4): j = i * 4 rom[i] = (ord(s[j ]) << 24) + \ (ord(s[j + 1]) << 16) + \ (ord(s[j + 2]) << 8) + \ (ord(s[j + 3]) ) def read_log_data(lines, rom): i = 0 for line in lines: if line.find("firewire_ohci: AT spd ") >= 0 and \ line.find(", ack_pending , QR req, fffff0000") >= 0: j = int(line[-12:], 16) if j < 0xfffff0000400 or j >= 0xfffff0000800: continue i = (j - 0xfffff0000400) / 4 if i == 0: rom[:] = [0] * 256 continue if line.find("firewire_ohci: AR spd ") >= 0 and \ line.find(", ack_complete, QR resp = ") >= 0: rom[i] = int(line[-8:], 16) def u8_to_char(n): # separator/ terminator if n == 0: return "" # printable character from minimal ASCII if (n >= 0x20 and n <= 0x22) or (n >= 0x25 and n <= 0x5a) or \ n == 0x5f or (n >= 0x61 and n <= 0x7a): return chr(n) # other character return "~" def u32_to_string(n): if n == 0: return "" s = "\"" for i in 24, 16, 8, 0: s += u8_to_char((n >> i) & 0xff) # add separator in c0c0, c0cc, c00c, or 0c0c if n & 0xff000000 and n & 0x0000ffff and not n & 0x00ff0000 or \ n & 0x00ff0000 and n & 0x000000ff and not n & 0xff00ff00: s = s[:2] + "\", \"" + s[2:] # add separator in cc0c if n & 0xff000000 and n & 0x00ff0000 and n & 0xff and not n & 0xff00: s = s[:3] + "\", \"" + s[3] return s + "\"" def language(n): n &= 0xffff if n == 0: return "en" s = "" for i in 10, 5, 0: c = ((n >> i) & 0x1f) - 1 s += char(ord("a") + c) if c >= 0 and c <= 25 else " " return s ieee1212_types = { 0: "immediate", 1: "csr offset", 2: "leaf", 3: "directory", } ieee1212_key_ids = { 0x01: "descriptor", 0x02: "bus dependent info", 0x03: "vendor", 0x04: "hardware version", 0x07: "module", 0x0c: "node capabilities", 0x0d: "eui-64", 0x11: "unit", 0x12: "specifier id", 0x13: "version", 0x14: "dependent info", 0x15: "unit location", 0x17: "model", 0x18: "instance", 0x19: "keyword", 0x1a: "feature", 0x1b: "extended rom", 0x1c: "extended key specifier id", 0x1d: "extended key", 0x1e: "extended data", 0x1f: "modifiable descriptor", 0x20: "directory id", 0x21: "revision", } def write_directory(f, rom, blocks, o, i, end): while i <= end: r = rom[i] t = r >> 30 k = r >> 24 & 0x3f v = r & 0xffffff if t == 0: headline = ieee1212_key_ids[k] if k in ieee1212_key_ids \ else "(immediate value)" elif t == 1: headline = "CSR at %012x" % (0xfffff0000000 + 4 * v) else: headline = "%s%s at %x" % \ (ieee1212_key_ids[k] + " " if k in ieee1212_key_ids else "", ieee1212_types[t], o + 4 * v) blocks[i + v] = (headline, t, k) f.write("%x %08x %s%s\n" % (o, r, "--> " if t else "", headline)) i += 1 o += 4 return i def write_eui64_hi(f, rom, o, i): hi = rom[i] f.write("%x %08x company_id %06x |\n" % (o, hi, hi >> 8)) def write_eui64_lo(f, rom, o, i): hi = rom[i - 1] lo = rom[i] f.write("%x %08x device_id %02x%08x | EUI-64 %08x%08x\n" % (o, lo, hi & 0xff, lo, hi, lo)) def write_leaf(f, rom, o, i, end, key_id): j = 0 descriptor_type = None while i <= end: r = rom[i] if key_id == 0x01 and j == 0: # descriptor leaf, general header descriptor_type = r >> 24 if descriptor_type == 0: f.write("%x %08x textual descriptor\n" % (o, r)) elif descriptor_type == 1: f.write("%x %08x icon descriptor\n" % (o, r)) else: f.write("%x %08x descriptor_type %d, specifier_ID %x\n" % (o, r, r >> 24, r & 0xffffff)) elif key_id == 0x1f: # modifiable descriptor if j == 0: f.write("%x %08x max_descriptor_size %d, " "descriptor_address_hi %d\n" % (o, r, r >> 16, r & 0xffff)) elif j == 1: f.write("%x %08x descriptor_address_lo %d\n" % (o, r, r)) elif (key_id == 0x07 or # primary node unique ID leaf key_id == 0x0d) and j < 2: # eui-64 leaf (write_eui64_hi, write_eui64_lo)[j](f, rom, o, i) elif key_id == 0x19: # keyword leaf f.write("%x %08x %s\n" % (o, r, u32_to_string(r))) elif descriptor_type == 0: # textual descriptor leaf if j == 1: is_minimal_ascii = r >> 16 == 0 if is_minimal_ascii: f.write("%x %08x minimal ASCII\n" % (o, r)) else: f.write("%x %08x width %d, character_set %d, " "language %s\n" % (o, r, r >> 28, (r >> 16) & 0xfff, language(r))) elif j > 1 and is_minimal_ascii: f.write("%x %08x %s\n" % (o, r, u32_to_string(r))) else: f.write("%x %08x\n" % (o, r)) i += 1 j += 1 o += 4 return i def write_block(f, rom, blocks, i, context): headline, t, k = context r = rom[i] o = 0x400 + i * 4 l = r >> 16 f.write(" %s\n" " ---------------------------------------------------------\n" "%x %08x %s_length %d, crc %d\n" % (headline, o, r, ieee1212_types[t], l, r & 0xffff)) end = i + l if i + l < 255 else 255 i += 1 o += 4 if t == 3: i = write_directory(f, rom, blocks, o, i, end) else: i = write_leaf(f, rom, o, i, end, k) f.write("\n") return i def write_bus_info_block(f, rom, blocks): f.write(" ROM header and bus information block\n" " ---------------------------------------------------------\n") r = rom[0] bib_len = r >> 24 f.write("400 %08x bus_info_length %d, crc_length %d, crc %d\n" % (r, bib_len, (r >> 16) & 0xff, r & 0xffff)) r = rom[1] f.write("404 %08x bus_name %s\n" % (r, u32_to_string(r))) if r == 0x31333934: r = rom[2] f.write("408 %08x irmc %d, cmc %d, isc %d, bmc %d, pmc %d, " "cyc_clk_acc %d,\n max_rec %d (%d), " "max_rom %d, gen %d, spd %d (S%d00)\n" % (r, r >> 31, r >> 30 & 1, r >> 29 & 1, r >> 28 & 1, r >> 27 & 1, r >> 16 & 0xff, r >> 12 & 0xf, 1 << (r >> 12 & 0xf) + 1, r >> 8 & 3, r >> 4 & 0xf, r & 7, 1 << (r & 7))) else: f.write("408 %08x bus-dependent information\n" % (rom[2])) write_eui64_hi(f, rom, 0x40c, 3) write_eui64_lo(f, rom, 0x410, 4) i = 5 while i <= bib_len: f.write("%x %08x bus-dependent information\n" % (0x400 + i * 4, rom[i])) i += 1 f.write("\n") blocks[i] = ("root directory", 3, None) return i config_rom = [0] * 256 config_rom_blocks = {} s = sys.stdin.read() l = len(s) may_be_binary = l > 20 and l <= 1024 and l & 3 == 0 if may_be_binary and s[4:8] == "4931": read_le32_data(s, l, config_rom) elif may_be_binary and s[4:8] == "1394": read_be32_data(s, l, config_rom) else: read_log_data(s.splitlines(), config_rom) if config_rom == [0] * 256: sys.exit("Nothing read.") f = sys.stdout i = write_bus_info_block(f, config_rom, config_rom_blocks) need_linefeed = False while i < 256: if i in config_rom_blocks: if need_linefeed: f.write("\n") i = write_block(f, config_rom, config_rom_blocks, i, config_rom_blocks[i]) need_linefeed = False elif config_rom[i]: f.write("%x %08x (unreferenced data)\n" % (0x400 + i * 4, config_rom[i])) need_linefeed = True i += 1 else: if need_linefeed: f.write("\n") need_linefeed = False i += 1 if need_linefeed: f.write("\n")