diff options
author | wdenk <wdenk> | 2002-09-05 16:50:46 +0000 |
---|---|---|
committer | wdenk <wdenk> | 2002-09-05 16:50:46 +0000 |
commit | 7666a9041cf8a8e768769f1186c9b9c663bd90d3 (patch) | |
tree | 43c66fd9c63f07825cb60dad3f4a40ca9d7fe621 /common | |
parent | 5afd1307c7434d47dda541ceab583920e3286863 (diff) | |
download | u-boot-imx-7666a9041cf8a8e768769f1186c9b9c663bd90d3.zip u-boot-imx-7666a9041cf8a8e768769f1186c9b9c663bd90d3.tar.gz u-boot-imx-7666a9041cf8a8e768769f1186c9b9c663bd90d3.tar.bz2 |
Initial revision
Diffstat (limited to 'common')
-rw-r--r-- | common/bedbug.c | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/common/bedbug.c b/common/bedbug.c new file mode 100644 index 0000000..fe54d17 --- /dev/null +++ b/common/bedbug.c @@ -0,0 +1,1256 @@ +/* $Id$ */ + +#include <common.h> + +#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG) + +#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. + */ + +#endif /* CONFIG_COMMANDS & CFG_CMD_BEDBUG */ |