diff options
Diffstat (limited to 'common/bedbug.c')
| -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 000000000..fe54d17f8 --- /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 */ |