/* $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.
*/