/*
- * Copyright (C) 2006 Michael Buesch <mb@bu3sch.de>
+ * Copyright (C) 2006-2010 Michael Buesch <mb@bu3sch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* GNU General Public License for more details.
*/
+#include "main.h"
#include "list.h"
#include "util.h"
+#include "args.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#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;
};
struct disassembler_context {
+ /* The architecture of the input file. */
+ unsigned int arch;
+
struct bin_instruction *code;
size_t nr_insns;
};
-static FILE *infile;
+FILE *infile;
+FILE *outfile;
+const char *infile_name;
const char *outfile_name;
{
char *ret;
- ret = xmalloc(5);
- snprintf(ret, 5, "@%03X", operand);
+ ret = xmalloc(6);
+ snprintf(ret, 6, "@%X", operand);
return ret;
}
{
char *ret;
- ret = xmalloc(8);
- snprintf(ret, 8, "[0x%03X]", operand);
+ ret = xmalloc(9);
+ snprintf(ret, 9, "[0x%X]", operand);
return ret;
}
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;
}
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;
}
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;
}
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;
}
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)
{
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: {
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;
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;
}
}
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:
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:
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);
- }
- /* FIXME: We currently only support v5 architecture. */
- fprintf(fd, "%%arch 5\n\n");
+
+ fprintf(outfile, "%%arch %u\n\n", ctx->arch);
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;
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) {
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;
}