X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=disassembler%2Fmain.c;h=76295fd5eb2a836b91c1692827c7fb50405e8033;hb=28703f82744ed3eb122ae33474e9c53f4fe528d3;hp=64e997533f6bc7222dab837dfc77fbf961698602;hpb=b9e19faa25ad9e1b24f08dc77267760df1b98504;p=b43-tools.git diff --git a/disassembler/main.c b/disassembler/main.c index 64e9975..76295fd 100644 --- a/disassembler/main.c +++ b/disassembler/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Michael Buesch + * Copyright (C) 2006-2010 Michael Buesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -11,28 +11,16 @@ * GNU General Public License for more details. */ +#include "main.h" #include "list.h" #include "util.h" +#include "args.h" #include #include #include #include -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) - -/* The header that fwcutter puts in to every .fw file */ -struct fw_header { - /* Type of the firmware data */ - uint8_t type; - /* Version number of the firmware data format */ - uint8_t ver; - uint8_t __padding[2]; - /* Size of the data. For ucode and PCM this is in bytes. - * For IV this is in number-of-ivs. */ - uint32_t size; -} __attribute__ ((__packed__)); - struct bin_instruction { unsigned int opcode; @@ -64,6 +52,9 @@ struct statement { }; struct disassembler_context { + /* The architecture of the input file. */ + unsigned int arch; + struct bin_instruction *code; size_t nr_insns; @@ -71,7 +62,9 @@ struct disassembler_context { }; -static FILE *infile; +FILE *infile; +FILE *outfile; +const char *infile_name; const char *outfile_name; @@ -79,8 +72,8 @@ static const char * gen_raw_code(unsigned int operand) { char *ret; - ret = xmalloc(5); - snprintf(ret, 5, "@%03X", operand); + ret = xmalloc(6); + snprintf(ret, 6, "@%X", operand); return ret; } @@ -89,8 +82,8 @@ static const char * disasm_mem_operand(unsigned int operand) { char *ret; - ret = xmalloc(8); - snprintf(ret, 8, "[0x%03X]", operand); + ret = xmalloc(9); + snprintf(ret, 9, "[0x%X]", operand); return ret; } @@ -98,10 +91,23 @@ static const char * disasm_mem_operand(unsigned int operand) static const char * disasm_indirect_mem_operand(unsigned int operand) { char *ret; + unsigned int offset, reg; + switch (cmdargs.arch) { + case 5: + offset = (operand & 0x3F); + reg = ((operand >> 6) & 0x7); + break; + case 15: + offset = (operand & 0x7F); + reg = ((operand >> 7) & 0x7); + break; + default: + fprintf(stderr, "Internal error: disasm_indirect_mem_operand invalid arch\n"); + exit(1); + } ret = xmalloc(12); - snprintf(ret, 12, "[0x%02X,off%u]", - (operand & 0x3F), ((operand >> 6) & 0x7)); + snprintf(ret, 12, "[0x%02X,off%u]", offset, reg); return ret; } @@ -109,14 +115,29 @@ static const char * disasm_indirect_mem_operand(unsigned int operand) static const char * disasm_imm_operand(unsigned int operand) { char *ret; + unsigned int signmask; + unsigned int mask; - operand &= ~0xC00; + switch (cmdargs.arch) { + case 5: + signmask = (1 << 9); + mask = 0x3FF; + break; + case 15: + signmask = (1 << 10); + mask = 0x7FF; + break; + default: + fprintf(stderr, "Internal error: disasm_imm_operand invalid arch\n"); + exit(1); + } + + operand &= mask; ret = xmalloc(7); - if (operand & (1 << 9)) - snprintf(ret, 7, "0x%04X", (operand | 0xFC00)); - else - snprintf(ret, 7, "0x%03X", operand); + if (operand & signmask) + operand = (operand | (~mask & 0xFFFF)); + snprintf(ret, 7, "0x%X", operand); return ret; } @@ -124,9 +145,22 @@ static const char * disasm_imm_operand(unsigned int operand) static const char * disasm_spr_operand(unsigned int operand) { char *ret; + unsigned int mask; - ret = xmalloc(7); - snprintf(ret, 7, "spr%03X", (operand & 0x1FF)); + switch (cmdargs.arch) { + case 5: + mask = 0x1FF; + break; + case 15: + mask = 0x7FF; + break; + default: + fprintf(stderr, "Internal error: disasm_spr_operand invalid arch\n"); + exit(1); + } + + ret = xmalloc(8); + snprintf(ret, 8, "spr%X", (operand & mask)); return ret; } @@ -134,19 +168,22 @@ static const char * disasm_spr_operand(unsigned int operand) static const char * disasm_gpr_operand(unsigned int operand) { char *ret; + unsigned int mask; - ret = xmalloc(4); - snprintf(ret, 4, "r%u", (operand & 0x3F)); - - return ret; -} - -static const char * disasm_offr_operand(unsigned int operand) -{ - char *ret; + switch (cmdargs.arch) { + case 5: + mask = 0x3F; + break; + case 15: + mask = 0x7F; + break; + default: + fprintf(stderr, "Internal error: disasm_gpr_operand invalid arch\n"); + exit(1); + } ret = xmalloc(5); - snprintf(ret, 5, "off%u", (operand & 0x7)); + snprintf(ret, 5, "r%u", (operand & mask)); return ret; } @@ -161,29 +198,60 @@ static void disasm_std_operand(struct statement *stmt, if (forceraw) goto raw; - if (!(operand & 0x800)) { - stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand); - return; - } else if ((operand & 0xC00) == 0xC00) { - stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand); - return; - } else if ((operand & 0xFC0) == 0xBC0) { - stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand); - return; - } else if ((operand & 0xE00) == 0x800) { - stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand); - return; - } else if ((operand & 0xFF8) == 0x860) { - stmt->u.insn.operands[out_idx] = disasm_offr_operand(operand); - return; - } else if ((operand & 0xE00) == 0xA00) { - stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand); - return; + switch (cmdargs.arch) { + case 5: + if (!(operand & 0x800)) { + stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand); + return; + } else if ((operand & 0xC00) == 0xC00) { + stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand); + return; + } else if ((operand & 0xFC0) == 0xBC0) { + stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand); + return; + } else if ((operand & 0xE00) == 0x800) { + stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand); + return; + } else if ((operand & 0xE00) == 0xA00) { + stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand); + return; + } + break; + case 15: + if (!(operand & 0x1000)) { + stmt->u.insn.operands[out_idx] = disasm_mem_operand(operand); + return; + } else if ((operand & 0x1800) == 0x1800) { + stmt->u.insn.operands[out_idx] = disasm_imm_operand(operand); + return; + } else if ((operand & 0x1F80) == 0x1780) { + stmt->u.insn.operands[out_idx] = disasm_gpr_operand(operand); + return; + } else if ((operand & 0x1C00) == 0x1000) { + stmt->u.insn.operands[out_idx] = disasm_spr_operand(operand); + return; + } else if ((operand & 0x1C00) == 0x1400) { + stmt->u.insn.operands[out_idx] = disasm_indirect_mem_operand(operand); + return; + } + break; + default: + fprintf(stderr, "Internal error: disasm_std_operand invalid arch\n"); + exit(1); } raw: stmt->u.insn.operands[out_idx] = gen_raw_code(operand); } +static void disasm_opcode_raw(struct disassembler_context *ctx, + struct statement *stmt) +{ + stmt->u.insn.name = gen_raw_code(stmt->u.insn.bin->opcode); + disasm_std_operand(stmt, 0, 0, 1); + disasm_std_operand(stmt, 1, 1, 1); + disasm_std_operand(stmt, 2, 2, 1); +} + static void disasm_constant_opcodes(struct disassembler_context *ctx, struct statement *stmt) { @@ -393,12 +461,20 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx, case 0x002: { char *str; - stmt->u.insn.name = "call"; - stmt->u.insn.is_labelref = 1; - stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; - str = xmalloc(4); - snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]); - stmt->u.insn.operands[0] = str; + switch (cmdargs.arch) { + case 5: + stmt->u.insn.name = "call"; + stmt->u.insn.is_labelref = 1; + stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; + str = xmalloc(4); + snprintf(str, 4, "lr%u", stmt->u.insn.bin->operands[0]); + stmt->u.insn.operands[0] = str; + break; + case 15: + //FIXME: This opcode is different on r15. Decode raw for now. + disasm_opcode_raw(ctx, stmt); + break; + } break; } case 0x003: { @@ -414,10 +490,22 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx, break; } case 0x1E0: { - unsigned int flags; + unsigned int flags, mask; + + switch (cmdargs.arch) { + case 5: + mask = 0x3FF; + break; + case 15: + mask = 0x7FF; + break; + default: + fprintf(stderr, "Internal error: TKIP invalid arch\n"); + exit(1); + } flags = stmt->u.insn.bin->operands[1]; - switch (flags & ~0xC00) { + switch (flags & mask) { case 0x1: stmt->u.insn.name = "tkiph"; break; @@ -440,26 +528,36 @@ static void disasm_constant_opcodes(struct disassembler_context *ctx, break; } case 0x001: { + unsigned int mask; + stmt->u.insn.name = "nap"; - if (stmt->u.insn.bin->operands[0] != 0xBC0) { - fprintf(stderr, "NAP: invalid first argument 0x%03X\n", + switch (cmdargs.arch) { + case 5: + mask = 0xBC0; + break; + case 15: + mask = 0x1780; + break; + default: + fprintf(stderr, "Internal error: NAP invalid arch\n"); + exit(1); + } + if (stmt->u.insn.bin->operands[0] != mask) { + fprintf(stderr, "NAP: invalid first argument 0x%04X\n", stmt->u.insn.bin->operands[0]); } - if (stmt->u.insn.bin->operands[1] != 0xBC0) { - fprintf(stderr, "NAP: invalid second argument 0x%03X\n", + if (stmt->u.insn.bin->operands[1] != mask) { + fprintf(stderr, "NAP: invalid second argument 0x%04X\n", stmt->u.insn.bin->operands[1]); } - if (stmt->u.insn.bin->operands[2] != 0x000) { - fprintf(stderr, "NAP: invalid third argument 0x%03X\n", + if (stmt->u.insn.bin->operands[2] != 0) { + fprintf(stderr, "NAP: invalid third argument 0x%04X\n", stmt->u.insn.bin->operands[2]); } break; } default: - stmt->u.insn.name = gen_raw_code(bin->opcode); - disasm_std_operand(stmt, 0, 0, 1); - disasm_std_operand(stmt, 1, 1, 1); - disasm_std_operand(stmt, 2, 2, 1); + disasm_opcode_raw(ctx, stmt); break; } } @@ -546,9 +644,13 @@ static void disasm_opcodes(struct disassembler_context *ctx) snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF)); stmt->u.insn.operands[0] = str; - disasm_std_operand(stmt, 0, 1, 0); - disasm_std_operand(stmt, 1, 2, 0); - stmt->u.insn.is_labelref = 3; + /* We don't disassemble the first and second operand, as + * that always is a dummy r0 operand. + * disasm_std_operand(stmt, 0, 1, 0); + * disasm_std_operand(stmt, 1, 2, 0); + * stmt->u.insn.is_labelref = 3; + */ + stmt->u.insn.is_labelref = 1; stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; break; case 0x700: @@ -558,9 +660,13 @@ static void disasm_opcodes(struct disassembler_context *ctx) snprintf(str, 5, "0x%02X", (bin->opcode & 0x0FF)); stmt->u.insn.operands[0] = str; - disasm_std_operand(stmt, 0, 1, 0); - disasm_std_operand(stmt, 1, 2, 0); - stmt->u.insn.is_labelref = 3; + /* We don't disassemble the first and second operand, as + * that always is a dummy r0 operand. + * disasm_std_operand(stmt, 0, 1, 0); + * disasm_std_operand(stmt, 1, 2, 0); + * stmt->u.insn.is_labelref = 3; + */ + stmt->u.insn.is_labelref = 1; stmt->u.insn.labeladdr = stmt->u.insn.bin->operands[2]; break; default: @@ -635,47 +741,52 @@ static void resolve_labels(struct disassembler_context *ctx) static void emit_asm(struct disassembler_context *ctx) { struct statement *stmt; - FILE *fd; int first, i; + int err; + unsigned int addr = 0; - fd = fopen(outfile_name, "w+"); - if (!fd) { - fprintf(stderr, "Could not open output file \"%s\"\n", - outfile_name); + err = open_output_file(); + if (err) exit(1); - } - fprintf(fd, "%%arch bcm43xx_newworld\n\n"); + + fprintf(outfile, "%%arch %u\n", ctx->arch); + fprintf(outfile, "%%start entry\n\n"); + fprintf(outfile, "entry:\n"); list_for_each_entry(stmt, &ctx->stmt_list, list) { switch (stmt->type) { case STMT_INSN: - fprintf(fd, "\t%s", stmt->u.insn.name); + if (cmdargs.print_addresses) + fprintf(outfile, "/* %03X */", addr); + fprintf(outfile, "\t%s", stmt->u.insn.name); first = 1; for (i = 0; i < ARRAY_SIZE(stmt->u.insn.operands); i++) { if (stmt->u.insn.is_labelref == i) { - fprintf(fd, ",%s", + fprintf(outfile, ", %s", stmt->u.insn.labelref->u.label.name); } if (!stmt->u.insn.operands[i]) continue; if (first) - fprintf(fd, "\t"); + fprintf(outfile, "\t"); if (!first) - fprintf(fd, ","); + fprintf(outfile, ", "); first = 0; - fprintf(fd, "%s", + fprintf(outfile, "%s", stmt->u.insn.operands[i]); } - fprintf(fd, "\n"); + fprintf(outfile, "\n"); + addr++; break; case STMT_LABEL: - fprintf(fd, "%s:\n", stmt->u.label.name); + fprintf(outfile, "%s:\n", stmt->u.label.name); break; } } - fclose(fd); + + close_output_file(); } -static void read_input(struct disassembler_context *ctx) +static int read_input(struct disassembler_context *ctx) { size_t size = 0, pos = 0; size_t ret; @@ -683,21 +794,32 @@ static void read_input(struct disassembler_context *ctx) unsigned char tmp[sizeof(uint64_t)]; uint64_t codeword; struct fw_header hdr; - - ret = fread(&hdr, 1, sizeof(hdr), infile); - if (!ret || ret != sizeof(hdr)) { - fprintf(stderr, "Corrupt input file (not fwcutter output)\n"); - exit(1); - } - - if (hdr.ver != 1) { - fprintf(stderr, "Invalid fwcutter header version\n"); - exit(1); - } - - if (hdr.type != 'u') { - fprintf(stderr, "Not a microcode image\n"); - exit(1); + int err; + + err = open_input_file(); + if (err) + goto error; + + switch (cmdargs.informat) { + case FMT_RAW_LE32: + case FMT_RAW_BE32: + /* Nothing */ + break; + case FMT_B43: + ret = fread(&hdr, 1, sizeof(hdr), infile); + if (ret != sizeof(hdr)) { + fprintf(stderr, "Corrupt input file (not fwcutter output)\n"); + goto err_close; + } + if (hdr.type != FW_TYPE_UCODE) { + fprintf(stderr, "Corrupt input file. Not a microcode image.\n"); + goto err_close; + } + if (hdr.ver != FW_HDR_VER) { + fprintf(stderr, "Invalid input file header version.\n"); + goto err_close; + } + break; } while (1) { @@ -710,65 +832,115 @@ static void read_input(struct disassembler_context *ctx) break; if (ret != sizeof(uint64_t)) { fprintf(stderr, "Corrupt input file (not 8 byte aligned)\n"); - exit(1); + goto err_free_code; } - codeword = 0; - codeword |= ((uint64_t)tmp[0]) << 56; - codeword |= ((uint64_t)tmp[1]) << 48; - codeword |= ((uint64_t)tmp[2]) << 40; - codeword |= ((uint64_t)tmp[3]) << 32; - codeword |= ((uint64_t)tmp[4]) << 24; - codeword |= ((uint64_t)tmp[5]) << 16; - codeword |= ((uint64_t)tmp[6]) << 8; - codeword |= ((uint64_t)tmp[7]); - - code[pos].opcode = (codeword >> 4) & 0xFFF; - code[pos].operands[0] = (codeword & 0xF) << 8; - code[pos].operands[0] |= (codeword >> 56) & 0xFF; - code[pos].operands[1] = (codeword >> 44) & 0xFFF; - code[pos].operands[2] = (codeword >> 32) & 0xFFF; + switch (cmdargs.informat) { + case FMT_B43: + case FMT_RAW_BE32: + codeword = 0; + codeword |= ((uint64_t)tmp[0]) << 56; + codeword |= ((uint64_t)tmp[1]) << 48; + codeword |= ((uint64_t)tmp[2]) << 40; + codeword |= ((uint64_t)tmp[3]) << 32; + codeword |= ((uint64_t)tmp[4]) << 24; + codeword |= ((uint64_t)tmp[5]) << 16; + codeword |= ((uint64_t)tmp[6]) << 8; + codeword |= ((uint64_t)tmp[7]); + codeword = ((codeword & (uint64_t)0xFFFFFFFF00000000ULL) >> 32) | + ((codeword & (uint64_t)0x00000000FFFFFFFFULL) << 32); + break; + case FMT_RAW_LE32: + codeword = 0; + codeword |= ((uint64_t)tmp[7]) << 56; + codeword |= ((uint64_t)tmp[6]) << 48; + codeword |= ((uint64_t)tmp[5]) << 40; + codeword |= ((uint64_t)tmp[4]) << 32; + codeword |= ((uint64_t)tmp[3]) << 24; + codeword |= ((uint64_t)tmp[2]) << 16; + codeword |= ((uint64_t)tmp[1]) << 8; + codeword |= ((uint64_t)tmp[0]); + break; + } + + switch (cmdargs.arch) { + case 5: + if (codeword >> 48) { + fprintf(stderr, "Instruction format error at 0x%X (upper not clear). " + "Wrong input format or architecture?\n", (unsigned int)pos); + goto err_free_code; + } + code[pos].opcode = (codeword >> 36) & 0xFFF; + code[pos].operands[2] = codeword & 0xFFF; + code[pos].operands[1] = (codeword >> 12) & 0xFFF; + code[pos].operands[0] = (codeword >> 24) & 0xFFF; + break; + case 15: + if (codeword >> 51) { + fprintf(stderr, "Instruction format error at 0x%X (upper not clear). " + "Wrong input format or architecture?\n", (unsigned int)pos); + goto err_free_code; + } + code[pos].opcode = (codeword >> 39) & 0xFFF; + code[pos].operands[2] = codeword & 0x1FFF; + code[pos].operands[1] = (codeword >> 13) & 0x1FFF; + code[pos].operands[0] = (codeword >> 26) & 0x1FFF; + break; + default: + fprintf(stderr, "Internal error: read_input unknown arch %u\n", + cmdargs.arch); + goto err_free_code; + } pos++; } ctx->code = code; ctx->nr_insns = pos; + + close_input_file(); + + return 0; + +err_free_code: + free(code); +err_close: + close_input_file(); +error: + return -1; } static void disassemble(void) { struct disassembler_context ctx; + int err; memset(&ctx, 0, sizeof(ctx)); INIT_LIST_HEAD(&ctx.stmt_list); + ctx.arch = cmdargs.arch; - read_input(&ctx); + err = read_input(&ctx); + if (err) + exit(1); disasm_opcodes(&ctx); resolve_labels(&ctx); emit_asm(&ctx); } -static void usage(int argc, char **argv) -{ - fprintf(stderr, "Usage: %s output_file\n", argv[0]); -} - -static void parse_args(int argc, char **argv) -{ - if (argc != 2) { - usage(argc, argv); - exit(1); - } - outfile_name = argv[1]; -} - int main(int argc, char **argv) { - infile = stdin; - parse_args(argc, argv); + int err, res = 1; + + err = parse_args(argc, argv); + if (err < 0) + goto out; + if (err > 0) { + res = 0; + goto out; + } disassemble(); - + res = 0; +out: /* Lazyman simply leaks all allocated memory. */ - return 0; + return res; }