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