1
0
Fork 0
remarkable-uboot/common/bedbug.c

1254 lines
30 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/* $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 = &bne;
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.
*/