| /* $Id$ */ | |
| #include <common.h> | |
| #include <linux/ctype.h> | |
| #include <bedbug/bedbug.h> | |
| #include <bedbug/ppc.h> | |
| #include <bedbug/regs.h> | |
| #include <bedbug/tables.h> | |
| #define Elf32_Word unsigned long | |
| /* USE_SOURCE_CODE enables some symbolic debugging functions of this | |
| code. This is only useful if the program will have access to the | |
| source code for the binary being examined. | |
| */ | |
| /* #define USE_SOURCE_CODE 1 */ | |
| #ifdef USE_SOURCE_CODE | |
| extern int line_info_from_addr __P ((Elf32_Word, char *, char *, int *)); | |
| extern struct symreflist *symByAddr; | |
| extern char *symbol_name_from_addr __P ((Elf32_Word, int, int *)); | |
| #endif /* USE_SOURCE_CODE */ | |
| int print_operands __P ((struct ppc_ctx *)); | |
| int get_operand_value __P ((struct opcode *, unsigned long, | |
| enum OP_FIELD, unsigned long *)); | |
| struct opcode *find_opcode __P ((unsigned long)); | |
| struct opcode *find_opcode_by_name __P ((char *)); | |
| char *spr_name __P ((int)); | |
| int spr_value __P ((char *)); | |
| char *tbr_name __P ((int)); | |
| int tbr_value __P ((char *)); | |
| int parse_operand __P ((unsigned long, struct opcode *, | |
| struct operand *, char *, int *)); | |
| int get_word __P ((char **, char *)); | |
| long read_number __P ((char *)); | |
| int downstring __P ((char *)); | |
| /*====================================================================== | |
| * Entry point for the PPC disassembler. | |
| * | |
| * Arguments: | |
| * memaddr The address to start disassembling from. | |
| * | |
| * virtual If this value is non-zero, then this will be | |
| * used as the base address for the output and | |
| * symbol lookups. If this value is zero then | |
| * memaddr is used as the absolute address. | |
| * | |
| * num_instr The number of instructions to disassemble. Since | |
| * each instruction is 32 bits long, this can be | |
| * computed if you know the total size of the region. | |
| * | |
| * pfunc The address of a function that is called to print | |
| * each line of output. The function should take a | |
| * single character pointer as its parameters a la puts. | |
| * | |
| * flags Sets options for the output. This is a | |
| * bitwise-inclusive-OR of the following | |
| * values. Note that only one of the radix | |
| * options may be set. | |
| * | |
| * F_RADOCTAL - output radix is unsigned base 8. | |
| * F_RADUDECIMAL - output radix is unsigned base 10. | |
| * F_RADSDECIMAL - output radix is signed base 10. | |
| * F_RADHEX - output radix is unsigned base 16. | |
| * F_SIMPLE - use simplified mnemonics. | |
| * F_SYMBOL - lookup symbols for addresses. | |
| * F_INSTR - output raw instruction. | |
| * F_LINENO - show line # info if available. | |
| * | |
| * Returns true if the area was successfully disassembled or false if | |
| * a problem was encountered with accessing the memory. | |
| */ | |
| int disppc (unsigned char *memaddr, unsigned char *virtual, int num_instr, | |
| int (*pfunc) (const char *), unsigned long flags) | |
| { | |
| int i; | |
| struct ppc_ctx ctx; | |
| #ifdef USE_SOURCE_CODE | |
| int line_no = 0; | |
| int last_line_no = 0; | |
| char funcname[128] = { 0 }; | |
| char filename[256] = { 0 }; | |
| char last_funcname[128] = { 0 }; | |
| int symoffset; | |
| char *symname; | |
| char *cursym = (char *) 0; | |
| #endif /* USE_SOURCE_CODE */ | |
| /*------------------------------------------------------------*/ | |
| ctx.flags = flags; | |
| ctx.virtual = virtual; | |
| /* Figure out the output radix before we go any further */ | |
| if (ctx.flags & F_RADOCTAL) { | |
| /* Unsigned octal output */ | |
| strcpy (ctx.radix_fmt, "O%o"); | |
| } else if (ctx.flags & F_RADUDECIMAL) { | |
| /* Unsigned decimal output */ | |
| strcpy (ctx.radix_fmt, "%u"); | |
| } else if (ctx.flags & F_RADSDECIMAL) { | |
| /* Signed decimal output */ | |
| strcpy (ctx.radix_fmt, "%d"); | |
| } else { | |
| /* Unsigned hex output */ | |
| strcpy (ctx.radix_fmt, "0x%x"); | |
| } | |
| if (ctx.virtual == 0) { | |
| ctx.virtual = memaddr; | |
| } | |
| #ifdef USE_SOURCE_CODE | |
| if (ctx.flags & F_SYMBOL) { | |
| if (symByAddr == 0) /* no symbols loaded */ | |
| ctx.flags &= ~F_SYMBOL; | |
| else { | |
| cursym = (char *) 0; | |
| symoffset = 0; | |
| } | |
| } | |
| #endif /* USE_SOURCE_CODE */ | |
| /* format each line as "XXXXXXXX: <symbol> IIIIIIII disassembly" where, | |
| XXXXXXXX is the memory address in hex, | |
| <symbol> is the symbolic location if F_SYMBOL is set. | |
| IIIIIIII is the raw machine code in hex if F_INSTR is set, | |
| and disassembly is the disassembled machine code with numbers | |
| formatted according to the 'radix' parameter */ | |
| for (i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4) { | |
| #ifdef USE_SOURCE_CODE | |
| if (ctx.flags & F_LINENO) { | |
| if ((line_info_from_addr ((Elf32_Word) ctx.virtual, | |
| filename, funcname, &line_no) == true) && | |
| ((line_no != last_line_no) || | |
| (strcmp (last_funcname, funcname) != 0))) { | |
| print_source_line (filename, funcname, line_no, pfunc); | |
| } | |
| last_line_no = line_no; | |
| strcpy (last_funcname, funcname); | |
| } | |
| #endif /* USE_SOURCE_CODE */ | |
| sprintf (ctx.data, "%08lx: ", (unsigned long) ctx.virtual); | |
| ctx.datalen = 10; | |
| #ifdef USE_SOURCE_CODE | |
| if (ctx.flags & F_SYMBOL) { | |
| if ((symname = | |
| symbol_name_from_addr((Elf32_Word) ctx.virtual, | |
| true, 0)) != 0) { | |
| cursym = symname; | |
| symoffset = 0; | |
| } else { | |
| if ((cursym == 0) && | |
| ((symname = | |
| symbol_name_from_addr((Elf32_Word) ctx.virtual, | |
| false, &symoffset)) != 0)) { | |
| cursym = symname; | |
| } else { | |
| symoffset += 4; | |
| } | |
| } | |
| if (cursym != 0) { | |
| sprintf (&ctx.data[ctx.datalen], "<%s+", cursym); | |
| ctx.datalen = strlen (ctx.data); | |
| sprintf (&ctx.data[ctx.datalen], ctx.radix_fmt, symoffset); | |
| strcat (ctx.data, ">"); | |
| ctx.datalen = strlen (ctx.data); | |
| } | |
| } | |
| #endif /* USE_SOURCE_CODE */ | |
| ctx.instr = INSTRUCTION (memaddr); | |
| if (ctx.flags & F_INSTR) { | |
| /* Find the opcode structure for this opcode. If one is not found | |
| then it must be an illegal instruction */ | |
| sprintf (&ctx.data[ctx.datalen], | |
| " %02lx %02lx %02lx %02lx ", | |
| ((ctx.instr >> 24) & 0xff), | |
| ((ctx.instr >> 16) & 0xff), ((ctx.instr >> 8) & 0xff), | |
| (ctx.instr & 0xff)); | |
| ctx.datalen += 18; | |
| } else { | |
| strcat (ctx.data, " "); | |
| ctx.datalen += 3; | |
| } | |
| if ((ctx.op = find_opcode (ctx.instr)) == 0) { | |
| /* Illegal Opcode */ | |
| sprintf (&ctx.data[ctx.datalen], " .long 0x%08lx", | |
| ctx.instr); | |
| ctx.datalen += 24; | |
| (*pfunc) (ctx.data); | |
| continue; | |
| } | |
| if (((ctx.flags & F_SIMPLE) == 0) || | |
| (ctx.op->hfunc == 0) || | |
| ((*ctx.op->hfunc) (&ctx) == false)) { | |
| sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name); | |
| ctx.datalen += 8; | |
| print_operands (&ctx); | |
| } | |
| (*pfunc) (ctx.data); | |
| } | |
| return true; | |
| } /* disppc */ | |
| /*====================================================================== | |
| * Called by the disassembler to print the operands for an instruction. | |
| * | |
| * Arguments: | |
| * ctx A pointer to the disassembler context record. | |
| * | |
| * always returns 0. | |
| */ | |
| int print_operands (struct ppc_ctx *ctx) | |
| { | |
| int open_parens = 0; | |
| int field; | |
| unsigned long operand; | |
| struct operand *opr; | |
| #ifdef USE_SOURCE_CODE | |
| char *symname; | |
| int offset; | |
| #endif /* USE_SOURCE_CODE */ | |
| /*------------------------------------------------------------*/ | |
| /* Walk through the operands and list each in order */ | |
| for (field = 0; ctx->op->fields[field] != 0; ++field) { | |
| if (ctx->op->fields[field] > n_operands) { | |
| continue; /* bad operand ?! */ | |
| } | |
| opr = &operands[ctx->op->fields[field] - 1]; | |
| if (opr->hint & OH_SILENT) { | |
| continue; | |
| } | |
| if ((field > 0) && !open_parens) { | |
| strcat (ctx->data, ","); | |
| ctx->datalen++; | |
| } | |
| operand = (ctx->instr >> opr->shift) & ((1 << opr->bits) - 1); | |
| if (opr->hint & OH_ADDR) { | |
| if ((operand & (1 << (opr->bits - 1))) != 0) { | |
| operand = operand - (1 << opr->bits); | |
| } | |
| if (ctx->op->hint & H_RELATIVE) | |
| operand = (operand << 2) + (unsigned long) ctx->virtual; | |
| else | |
| operand = (operand << 2); | |
| sprintf (&ctx->data[ctx->datalen], "0x%lx", operand); | |
| ctx->datalen = strlen (ctx->data); | |
| #ifdef USE_SOURCE_CODE | |
| if ((ctx->flags & F_SYMBOL) && | |
| ((symname = | |
| symbol_name_from_addr (operand, 0, &offset)) != 0)) { | |
| sprintf (&ctx->data[ctx->datalen], " <%s", symname); | |
| if (offset != 0) { | |
| strcat (ctx->data, "+"); | |
| ctx->datalen = strlen (ctx->data); | |
| sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, | |
| offset); | |
| } | |
| strcat (ctx->data, ">"); | |
| } | |
| #endif /* USE_SOURCE_CODE */ | |
| } | |
| else if (opr->hint & OH_REG) { | |
| if ((operand == 0) && | |
| (opr->field == O_rA) && (ctx->op->hint & H_RA0_IS_0)) { | |
| strcat (ctx->data, "0"); | |
| } else { | |
| sprintf (&ctx->data[ctx->datalen], "r%d", (short) operand); | |
| } | |
| if (open_parens) { | |
| strcat (ctx->data, ")"); | |
| open_parens--; | |
| } | |
| } | |
| else if (opr->hint & OH_SPR) { | |
| strcat (ctx->data, spr_name (operand)); | |
| } | |
| else if (opr->hint & OH_TBR) { | |
| strcat (ctx->data, tbr_name (operand)); | |
| } | |
| else if (opr->hint & OH_LITERAL) { | |
| switch (opr->field) { | |
| case O_cr2: | |
| strcat (ctx->data, "cr2"); | |
| ctx->datalen += 3; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| else { | |
| sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, | |
| (unsigned short) operand); | |
| if (open_parens) { | |
| strcat (ctx->data, ")"); | |
| open_parens--; | |
| } | |
| else if (opr->hint & OH_OFFSET) { | |
| strcat (ctx->data, "("); | |
| open_parens++; | |
| } | |
| } | |
| ctx->datalen = strlen (ctx->data); | |
| } | |
| return 0; | |
| } /* print_operands */ | |
| /*====================================================================== | |
| * Called to get the value of an arbitrary operand with in an instruction. | |
| * | |
| * Arguments: | |
| * op The pointer to the opcode structure to which | |
| * the operands belong. | |
| * | |
| * instr The instruction (32 bits) containing the opcode | |
| * and the operands to print. By the time that | |
| * this routine is called the operand has already | |
| * been added to the output. | |
| * | |
| * field The field (operand) to get the value of. | |
| * | |
| * value The address of an unsigned long to be filled in | |
| * with the value of the operand if it is found. This | |
| * will only be filled in if the function returns | |
| * true. This may be passed as 0 if the value is | |
| * not required. | |
| * | |
| * Returns true if the operand was found or false if it was not. | |
| */ | |
| int get_operand_value (struct opcode *op, unsigned long instr, | |
| enum OP_FIELD field, unsigned long *value) | |
| { | |
| int i; | |
| struct operand *opr; | |
| /*------------------------------------------------------------*/ | |
| if (field > n_operands) { | |
| return false; /* bad operand ?! */ | |
| } | |
| /* Walk through the operands and list each in order */ | |
| for (i = 0; op->fields[i] != 0; ++i) { | |
| if (op->fields[i] != field) { | |
| continue; | |
| } | |
| opr = &operands[op->fields[i] - 1]; | |
| if (value) { | |
| *value = (instr >> opr->shift) & ((1 << opr->bits) - 1); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } /* operand_value */ | |
| /*====================================================================== | |
| * Called by the disassembler to match an opcode value to an opcode structure. | |
| * | |
| * Arguments: | |
| * instr The instruction (32 bits) to match. This value | |
| * may contain operand values as well as the opcode | |
| * since they will be masked out anyway for this | |
| * search. | |
| * | |
| * Returns the address of an opcode struct (from the opcode table) if the | |
| * operand successfully matched an entry, or 0 if no match was found. | |
| */ | |
| struct opcode *find_opcode (unsigned long instr) | |
| { | |
| struct opcode *ptr; | |
| int top = 0; | |
| int bottom = n_opcodes - 1; | |
| int idx; | |
| /*------------------------------------------------------------*/ | |
| while (top <= bottom) { | |
| idx = (top + bottom) >> 1; | |
| ptr = &opcodes[idx]; | |
| if ((instr & ptr->mask) < ptr->opcode) { | |
| bottom = idx - 1; | |
| } else if ((instr & ptr->mask) > ptr->opcode) { | |
| top = idx + 1; | |
| } else { | |
| return ptr; | |
| } | |
| } | |
| return (struct opcode *) 0; | |
| } /* find_opcode */ | |
| /*====================================================================== | |
| * Called by the assembler to match an opcode name to an opcode structure. | |
| * | |
| * Arguments: | |
| * name The text name of the opcode, e.g. "b", "mtspr", etc. | |
| * | |
| * The opcodes are sorted numerically by their instruction binary code | |
| * so a search for the name cannot use the binary search used by the | |
| * other find routine. | |
| * | |
| * Returns the address of an opcode struct (from the opcode table) if the | |
| * name successfully matched an entry, or 0 if no match was found. | |
| */ | |
| struct opcode *find_opcode_by_name (char *name) | |
| { | |
| int idx; | |
| /*------------------------------------------------------------*/ | |
| downstring (name); | |
| for (idx = 0; idx < n_opcodes; ++idx) { | |
| if (!strcmp (name, opcodes[idx].name)) | |
| return &opcodes[idx]; | |
| } | |
| return (struct opcode *) 0; | |
| } /* find_opcode_by_name */ | |
| /*====================================================================== | |
| * Convert the 'spr' operand from its numeric value to its symbolic name. | |
| * | |
| * Arguments: | |
| * value The value of the 'spr' operand. This value should | |
| * be unmodified from its encoding in the instruction. | |
| * the split-field computations will be performed | |
| * here before the switch. | |
| * | |
| * Returns the address of a character array containing the name of the | |
| * special purpose register defined by the 'value' parameter, or the | |
| * address of a character array containing "???" if no match was found. | |
| */ | |
| char *spr_name (int value) | |
| { | |
| unsigned short spr; | |
| static char other[10]; | |
| int i; | |
| /*------------------------------------------------------------*/ | |
| /* spr is a 10 bit field whose interpretation has the high and low | |
| five-bit fields reversed from their encoding in the operand */ | |
| spr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); | |
| for (i = 0; i < n_sprs; ++i) { | |
| if (spr == spr_map[i].spr_val) | |
| return spr_map[i].spr_name; | |
| } | |
| sprintf (other, "%d", spr); | |
| return other; | |
| } /* spr_name */ | |
| /*====================================================================== | |
| * Convert the 'spr' operand from its symbolic name to its numeric value | |
| * | |
| * Arguments: | |
| * name The symbolic name of the 'spr' operand. The | |
| * split-field encoding will be done by this routine. | |
| * NOTE: name can be a number. | |
| * | |
| * Returns the numeric value for the spr appropriate for encoding a machine | |
| * instruction. Returns 0 if unable to find the SPR. | |
| */ | |
| int spr_value (char *name) | |
| { | |
| struct spr_info *sprp; | |
| int spr; | |
| int i; | |
| /*------------------------------------------------------------*/ | |
| if (!name || !*name) | |
| return 0; | |
| if (isdigit ((int) name[0])) { | |
| i = htonl (read_number (name)); | |
| spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); | |
| return spr; | |
| } | |
| downstring (name); | |
| for (i = 0; i < n_sprs; ++i) { | |
| sprp = &spr_map[i]; | |
| if (strcmp (name, sprp->spr_name) == 0) { | |
| /* spr is a 10 bit field whose interpretation has the high and low | |
| five-bit fields reversed from their encoding in the operand */ | |
| i = htonl (sprp->spr_val); | |
| spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); | |
| return spr; | |
| } | |
| } | |
| return 0; | |
| } /* spr_value */ | |
| /*====================================================================== | |
| * Convert the 'tbr' operand from its numeric value to its symbolic name. | |
| * | |
| * Arguments: | |
| * value The value of the 'tbr' operand. This value should | |
| * be unmodified from its encoding in the instruction. | |
| * the split-field computations will be performed | |
| * here before the switch. | |
| * | |
| * Returns the address of a character array containing the name of the | |
| * time base register defined by the 'value' parameter, or the address | |
| * of a character array containing "???" if no match was found. | |
| */ | |
| char *tbr_name (int value) | |
| { | |
| unsigned short tbr; | |
| /*------------------------------------------------------------*/ | |
| /* tbr is a 10 bit field whose interpretation has the high and low | |
| five-bit fields reversed from their encoding in the operand */ | |
| tbr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); | |
| if (tbr == 268) | |
| return "TBL"; | |
| else if (tbr == 269) | |
| return "TBU"; | |
| return "???"; | |
| } /* tbr_name */ | |
| /*====================================================================== | |
| * Convert the 'tbr' operand from its symbolic name to its numeric value. | |
| * | |
| * Arguments: | |
| * name The symbolic name of the 'tbr' operand. The | |
| * split-field encoding will be done by this routine. | |
| * | |
| * Returns the numeric value for the spr appropriate for encoding a machine | |
| * instruction. Returns 0 if unable to find the TBR. | |
| */ | |
| int tbr_value (char *name) | |
| { | |
| int tbr; | |
| int val; | |
| /*------------------------------------------------------------*/ | |
| if (!name || !*name) | |
| return 0; | |
| downstring (name); | |
| if (isdigit ((int) name[0])) { | |
| val = read_number (name); | |
| if (val != 268 && val != 269) | |
| return 0; | |
| } else if (strcmp (name, "tbl") == 0) | |
| val = 268; | |
| else if (strcmp (name, "tbu") == 0) | |
| val = 269; | |
| else | |
| return 0; | |
| /* tbr is a 10 bit field whose interpretation has the high and low | |
| five-bit fields reversed from their encoding in the operand */ | |
| val = htonl (val); | |
| tbr = ((val >> 5) & 0x1f) | ((val & 0x1f) << 5); | |
| return tbr; | |
| } /* tbr_name */ | |
| /*====================================================================== | |
| * The next several functions (handle_xxx) are the routines that handle | |
| * disassembling the opcodes with simplified mnemonics. | |
| * | |
| * Arguments: | |
| * ctx A pointer to the disassembler context record. | |
| * | |
| * Returns true if the simpler form was printed or false if it was not. | |
| */ | |
| int handle_bc (struct ppc_ctx *ctx) | |
| { | |
| unsigned long bo; | |
| unsigned long bi; | |
| static struct opcode blt = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, | |
| 0, "blt", H_RELATIVE | |
| }; | |
| static struct opcode bne = | |
| { B_OPCODE (16, 0, 0), B_MASK, {O_cr2, O_BD, 0}, | |
| 0, "bne", H_RELATIVE | |
| }; | |
| static struct opcode bdnz = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, | |
| 0, "bdnz", H_RELATIVE | |
| }; | |
| /*------------------------------------------------------------*/ | |
| if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false) | |
| return false; | |
| if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false) | |
| return false; | |
| if ((bo == 12) && (bi == 0)) { | |
| ctx->op = &blt; | |
| sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); | |
| ctx->datalen += 8; | |
| print_operands (ctx); | |
| return true; | |
| } else if ((bo == 4) && (bi == 10)) { | |
| ctx->op = =⃥ | |
| sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); | |
| ctx->datalen += 8; | |
| print_operands (ctx); | |
| return true; | |
| } else if ((bo == 16) && (bi == 0)) { | |
| ctx->op = &bdnz; | |
| sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); | |
| ctx->datalen += 8; | |
| print_operands (ctx); | |
| return true; | |
| } | |
| return false; | |
| } /* handle_blt */ | |
| /*====================================================================== | |
| * Outputs source line information for the disassembler. This should | |
| * be modified in the future to lookup the actual line of source code | |
| * from the file, but for now this will do. | |
| * | |
| * Arguments: | |
| * filename The address of a character array containing the | |
| * absolute path and file name of the source file. | |
| * | |
| * funcname The address of a character array containing the | |
| * name of the function (not C++ demangled (yet)) | |
| * to which this code belongs. | |
| * | |
| * line_no An integer specifying the source line number that | |
| * generated this code. | |
| * | |
| * pfunc The address of a function to call to print the output. | |
| * | |
| * | |
| * Returns true if it was able to output the line info, or false if it was | |
| * not. | |
| */ | |
| int print_source_line (char *filename, char *funcname, | |
| int line_no, int (*pfunc) (const char *)) | |
| { | |
| char out_buf[256]; | |
| /*------------------------------------------------------------*/ | |
| (*pfunc) (""); /* output a newline */ | |
| sprintf (out_buf, "%s %s(): line %d", filename, funcname, line_no); | |
| (*pfunc) (out_buf); | |
| return true; | |
| } /* print_source_line */ | |
| /*====================================================================== | |
| * Entry point for the PPC assembler. | |
| * | |
| * Arguments: | |
| * asm_buf An array of characters containing the assembly opcode | |
| * and operands to convert to a POWERPC machine | |
| * instruction. | |
| * | |
| * Returns the machine instruction or zero. | |
| */ | |
| unsigned long asmppc (unsigned long memaddr, char *asm_buf, int *err) | |
| { | |
| struct opcode *opc; | |
| struct operand *oper[MAX_OPERANDS]; | |
| unsigned long instr; | |
| unsigned long param; | |
| char *ptr = asm_buf; | |
| char scratch[20]; | |
| int i; | |
| int w_operands = 0; /* wanted # of operands */ | |
| int n_operands = 0; /* # of operands read */ | |
| int asm_debug = 0; | |
| /*------------------------------------------------------------*/ | |
| if (err) | |
| *err = 0; | |
| if (get_word (&ptr, scratch) == 0) | |
| return 0; | |
| /* Lookup the opcode structure based on the opcode name */ | |
| if ((opc = find_opcode_by_name (scratch)) == (struct opcode *) 0) { | |
| if (err) | |
| *err = E_ASM_BAD_OPCODE; | |
| return 0; | |
| } | |
| if (asm_debug) { | |
| printf ("asmppc: Opcode = \"%s\"\n", opc->name); | |
| } | |
| for (i = 0; i < 8; ++i) { | |
| if (opc->fields[i] == 0) | |
| break; | |
| ++w_operands; | |
| } | |
| if (asm_debug) { | |
| printf ("asmppc: Expecting %d operands\n", w_operands); | |
| } | |
| instr = opc->opcode; | |
| /* read each operand */ | |
| while (n_operands < w_operands) { | |
| oper[n_operands] = &operands[opc->fields[n_operands] - 1]; | |
| if (oper[n_operands]->hint & OH_SILENT) { | |
| /* Skip silent operands, they are covered in opc->opcode */ | |
| if (asm_debug) { | |
| printf ("asmppc: Operand %d \"%s\" SILENT\n", n_operands, | |
| oper[n_operands]->name); | |
| } | |
| ++n_operands; | |
| continue; | |
| } | |
| if (get_word (&ptr, scratch) == 0) | |
| break; | |
| if (asm_debug) { | |
| printf ("asmppc: Operand %d \"%s\" : \"%s\"\n", n_operands, | |
| oper[n_operands]->name, scratch); | |
| } | |
| if ((param = parse_operand (memaddr, opc, oper[n_operands], | |
| scratch, err)) == -1) | |
| return 0; | |
| instr |= param; | |
| ++n_operands; | |
| } | |
| if (n_operands < w_operands) { | |
| if (err) | |
| *err = E_ASM_NUM_OPERANDS; | |
| return 0; | |
| } | |
| if (asm_debug) { | |
| printf ("asmppc: Instruction = 0x%08lx\n", instr); | |
| } | |
| return instr; | |
| } /* asmppc */ | |
| /*====================================================================== | |
| * Called by the assembler to interpret a single operand | |
| * | |
| * Arguments: | |
| * ctx A pointer to the disassembler context record. | |
| * | |
| * Returns 0 if the operand is ok, or -1 if it is bad. | |
| */ | |
| int parse_operand (unsigned long memaddr, struct opcode *opc, | |
| struct operand *oper, char *txt, int *err) | |
| { | |
| long data; | |
| long mask; | |
| int is_neg = 0; | |
| /*------------------------------------------------------------*/ | |
| mask = (1 << oper->bits) - 1; | |
| if (oper->hint & OH_ADDR) { | |
| data = read_number (txt); | |
| if (opc->hint & H_RELATIVE) | |
| data = data - memaddr; | |
| if (data < 0) | |
| is_neg = 1; | |
| data >>= 2; | |
| data &= (mask >> 1); | |
| if (is_neg) | |
| data |= 1 << (oper->bits - 1); | |
| } | |
| else if (oper->hint & OH_REG) { | |
| if (txt[0] == 'r' || txt[0] == 'R') | |
| txt++; | |
| else if (txt[0] == '%' && (txt[1] == 'r' || txt[1] == 'R')) | |
| txt += 2; | |
| data = read_number (txt); | |
| if (data > 31) { | |
| if (err) | |
| *err = E_ASM_BAD_REGISTER; | |
| return -1; | |
| } | |
| data = htonl (data); | |
| } | |
| else if (oper->hint & OH_SPR) { | |
| if ((data = spr_value (txt)) == 0) { | |
| if (err) | |
| *err = E_ASM_BAD_SPR; | |
| return -1; | |
| } | |
| } | |
| else if (oper->hint & OH_TBR) { | |
| if ((data = tbr_value (txt)) == 0) { | |
| if (err) | |
| *err = E_ASM_BAD_TBR; | |
| return -1; | |
| } | |
| } | |
| else { | |
| data = htonl (read_number (txt)); | |
| } | |
| return (data & mask) << oper->shift; | |
| } /* parse_operand */ | |
| char *asm_error_str (int err) | |
| { | |
| switch (err) { | |
| case E_ASM_BAD_OPCODE: | |
| return "Bad opcode"; | |
| case E_ASM_NUM_OPERANDS: | |
| return "Bad number of operands"; | |
| case E_ASM_BAD_REGISTER: | |
| return "Bad register number"; | |
| case E_ASM_BAD_SPR: | |
| return "Bad SPR name or number"; | |
| case E_ASM_BAD_TBR: | |
| return "Bad TBR name or number"; | |
| } | |
| return ""; | |
| } /* asm_error_str */ | |
| /*====================================================================== | |
| * Copy a word from one buffer to another, ignores leading white spaces. | |
| * | |
| * Arguments: | |
| * src The address of a character pointer to the | |
| * source buffer. | |
| * dest A pointer to a character buffer to write the word | |
| * into. | |
| * | |
| * Returns the number of non-white space characters copied, or zero. | |
| */ | |
| int get_word (char **src, char *dest) | |
| { | |
| char *ptr = *src; | |
| int nchars = 0; | |
| /*------------------------------------------------------------*/ | |
| /* Eat white spaces */ | |
| while (*ptr && isblank (*ptr)) | |
| ptr++; | |
| if (*ptr == 0) { | |
| *src = ptr; | |
| return 0; | |
| } | |
| /* Find the text of the word */ | |
| while (*ptr && !isblank (*ptr) && (*ptr != ',')) | |
| dest[nchars++] = *ptr++; | |
| ptr = (*ptr == ',') ? ptr + 1 : ptr; | |
| dest[nchars] = 0; | |
| *src = ptr; | |
| return nchars; | |
| } /* get_word */ | |
| /*====================================================================== | |
| * Convert a numeric string to a number, be aware of base notations. | |
| * | |
| * Arguments: | |
| * txt The numeric string. | |
| * | |
| * Returns the converted numeric value. | |
| */ | |
| long read_number (char *txt) | |
| { | |
| long val; | |
| int is_neg = 0; | |
| /*------------------------------------------------------------*/ | |
| if (txt == 0 || *txt == 0) | |
| return 0; | |
| if (*txt == '-') { | |
| is_neg = 1; | |
| ++txt; | |
| } | |
| if (txt[0] == '0' && (txt[1] == 'x' || txt[1] == 'X')) /* hex */ | |
| val = simple_strtoul (&txt[2], NULL, 16); | |
| else /* decimal */ | |
| val = simple_strtoul (txt, NULL, 10); | |
| if (is_neg) | |
| val = -val; | |
| return val; | |
| } /* read_number */ | |
| int downstring (char *s) | |
| { | |
| if (!s || !*s) | |
| return 0; | |
| while (*s) { | |
| if (isupper (*s)) | |
| *s = tolower (*s); | |
| s++; | |
| } | |
| return 0; | |
| } /* downstring */ | |
| /*====================================================================== | |
| * Examines the instruction at the current address and determines the | |
| * next address to be executed. This will take into account branches | |
| * of different types so that a "step" and "next" operations can be | |
| * supported. | |
| * | |
| * Arguments: | |
| * nextaddr The address (to be filled in) of the next | |
| * instruction to execute. This will only be a valid | |
| * address if true is returned. | |
| * | |
| * step_over A flag indicating how to compute addresses for | |
| * branch statements: | |
| * true = Step over the branch (next) | |
| * false = step into the branch (step) | |
| * | |
| * Returns true if it was able to compute the address. Returns false if | |
| * it has a problem reading the current instruction or one of the registers. | |
| */ | |
| int find_next_address (unsigned char *nextaddr, int step_over, | |
| struct pt_regs *regs) | |
| { | |
| unsigned long pc; /* SRR0 register from PPC */ | |
| unsigned long ctr; /* CTR register from PPC */ | |
| unsigned long cr; /* CR register from PPC */ | |
| unsigned long lr; /* LR register from PPC */ | |
| unsigned long instr; /* instruction at SRR0 */ | |
| unsigned long next; /* computed instruction for 'next' */ | |
| unsigned long step; /* computed instruction for 'step' */ | |
| unsigned long addr = 0; /* target address operand */ | |
| unsigned long aa = 0; /* AA operand */ | |
| unsigned long lk = 0; /* LK operand */ | |
| unsigned long bo = 0; /* BO operand */ | |
| unsigned long bi = 0; /* BI operand */ | |
| struct opcode *op = 0; /* opcode structure for 'instr' */ | |
| int ctr_ok = 0; | |
| int cond_ok = 0; | |
| int conditional = 0; | |
| int branch = 0; | |
| /*------------------------------------------------------------*/ | |
| if (nextaddr == 0 || regs == 0) { | |
| printf ("find_next_address: bad args"); | |
| return false; | |
| } | |
| pc = regs->nip & 0xfffffffc; | |
| instr = INSTRUCTION (pc); | |
| if ((op = find_opcode (instr)) == (struct opcode *) 0) { | |
| printf ("find_next_address: can't parse opcode 0x%lx", instr); | |
| return false; | |
| } | |
| ctr = regs->ctr; | |
| cr = regs->ccr; | |
| lr = regs->link; | |
| switch (op->opcode) { | |
| case B_OPCODE (16, 0, 0): /* bc */ | |
| case B_OPCODE (16, 0, 1): /* bcl */ | |
| case B_OPCODE (16, 1, 0): /* bca */ | |
| case B_OPCODE (16, 1, 1): /* bcla */ | |
| if (!get_operand_value (op, instr, O_BD, &addr) || | |
| !get_operand_value (op, instr, O_BO, &bo) || | |
| !get_operand_value (op, instr, O_BI, &bi) || | |
| !get_operand_value (op, instr, O_AA, &aa) || | |
| !get_operand_value (op, instr, O_LK, &lk)) | |
| return false; | |
| if ((addr & (1 << 13)) != 0) | |
| addr = addr - (1 << 14); | |
| addr <<= 2; | |
| conditional = 1; | |
| branch = 1; | |
| break; | |
| case I_OPCODE (18, 0, 0): /* b */ | |
| case I_OPCODE (18, 0, 1): /* bl */ | |
| case I_OPCODE (18, 1, 0): /* ba */ | |
| case I_OPCODE (18, 1, 1): /* bla */ | |
| if (!get_operand_value (op, instr, O_LI, &addr) || | |
| !get_operand_value (op, instr, O_AA, &aa) || | |
| !get_operand_value (op, instr, O_LK, &lk)) | |
| return false; | |
| if ((addr & (1 << 23)) != 0) | |
| addr = addr - (1 << 24); | |
| addr <<= 2; | |
| conditional = 0; | |
| branch = 1; | |
| break; | |
| case XL_OPCODE (19, 528, 0): /* bcctr */ | |
| case XL_OPCODE (19, 528, 1): /* bcctrl */ | |
| if (!get_operand_value (op, instr, O_BO, &bo) || | |
| !get_operand_value (op, instr, O_BI, &bi) || | |
| !get_operand_value (op, instr, O_LK, &lk)) | |
| return false; | |
| addr = ctr; | |
| aa = 1; | |
| conditional = 1; | |
| branch = 1; | |
| break; | |
| case XL_OPCODE (19, 16, 0): /* bclr */ | |
| case XL_OPCODE (19, 16, 1): /* bclrl */ | |
| if (!get_operand_value (op, instr, O_BO, &bo) || | |
| !get_operand_value (op, instr, O_BI, &bi) || | |
| !get_operand_value (op, instr, O_LK, &lk)) | |
| return false; | |
| addr = lr; | |
| aa = 1; | |
| conditional = 1; | |
| branch = 1; | |
| break; | |
| default: | |
| conditional = 0; | |
| branch = 0; | |
| break; | |
| } | |
| if (conditional) { | |
| switch ((bo & 0x1e) >> 1) { | |
| case 0: /* 0000y */ | |
| if (--ctr != 0) | |
| ctr_ok = 1; | |
| cond_ok = !(cr & (1 << (31 - bi))); | |
| break; | |
| case 1: /* 0001y */ | |
| if (--ctr == 0) | |
| ctr_ok = 1; | |
| cond_ok = !(cr & (1 << (31 - bi))); | |
| break; | |
| case 2: /* 001zy */ | |
| ctr_ok = 1; | |
| cond_ok = !(cr & (1 << (31 - bi))); | |
| break; | |
| case 4: /* 0100y */ | |
| if (--ctr != 0) | |
| ctr_ok = 1; | |
| cond_ok = cr & (1 << (31 - bi)); | |
| break; | |
| case 5: /* 0101y */ | |
| if (--ctr == 0) | |
| ctr_ok = 1; | |
| cond_ok = cr & (1 << (31 - bi)); | |
| break; | |
| case 6: /* 011zy */ | |
| ctr_ok = 1; | |
| cond_ok = cr & (1 << (31 - bi)); | |
| break; | |
| case 8: /* 1z00y */ | |
| if (--ctr != 0) | |
| ctr_ok = cond_ok = 1; | |
| break; | |
| case 9: /* 1z01y */ | |
| if (--ctr == 0) | |
| ctr_ok = cond_ok = 1; | |
| break; | |
| case 10: /* 1z1zz */ | |
| ctr_ok = cond_ok = 1; | |
| break; | |
| } | |
| } | |
| if (branch && (!conditional || (ctr_ok && cond_ok))) { | |
| if (aa) | |
| step = addr; | |
| else | |
| step = addr + pc; | |
| if (lk) | |
| next = pc + 4; | |
| else | |
| next = step; | |
| } else { | |
| step = next = pc + 4; | |
| } | |
| if (step_over == true) | |
| *(unsigned long *) nextaddr = next; | |
| else | |
| *(unsigned long *) nextaddr = step; | |
| return true; | |
| } /* find_next_address */ | |
| /* | |
| * Copyright (c) 2000 William L. Pitts and W. Gerald Hicks | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms are freely | |
| * permitted provided that the above copyright notice and this | |
| * paragraph and the following disclaimer are duplicated in all | |
| * such forms. | |
| * | |
| * This software is provided "AS IS" and without any express or | |
| * implied warranties, including, without limitation, the implied | |
| * warranties of merchantability and fitness for a particular | |
| * purpose. | |
| */ |