[BACK]Return to kdba_id.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / arch / ia64 / kdb

File: [Development] / linux-2.6-xfs / arch / ia64 / kdb / kdba_id.c (download)

Revision 1.14, Mon Feb 19 14:47:03 2007 UTC (10 years, 7 months ago) by tes.longdrop.melbourne.sgi.com
Branch: MAIN
Changes since 1.13: +264 -2 lines

Merge up to 2.6.20
Merge of 2.6.x-xfs-melb:linux:28132a by kenmcd.

/*
 * Kernel Debugger Architecture Dependent Instruction Disassembly
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (c) 1999-2004 Silicon Graphics, Inc.  All Rights Reserved.
 */

#include <stdarg.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/kdb.h>
#include <linux/kdbprivate.h>
#include <asm/patch.h>

#define KDBA_PRINTBUF_LEN	64	/* buffer len to print a single instr */
#define KDBA_READBUFFER_LEN	256	/* buffer for BFD disassembler */

#define BUNDLE_MULTIPLIER	3	/* how many instr/bundle */
#define BUNDLE_SIZE		16	/* how many bytes/bundle */
#define KDBA_DEFAULT_IDLEN	3	/* default number of bundles to disassemble */

/*
 * kdba_dis_getsym
 *
 *	Get a symbol for the disassembler.
 *
 * Parameters:
 *	addr	Address for which to get symbol
 *	dip	Pointer to disassemble_info
 * Returns:
 *	0
 * Locking:
 * Remarks:
 *	Not used for kdb.
 */

/* ARGSUSED */
static int
kdba_dis_getsym(bfd_vma addr, disassemble_info *dip)
{

	return 0;
}

/*
 * kdba_printaddress
 *
 *	Print (symbolically) an address.
 *
 * Parameters:
 *	addr	Address for which to get symbol
 *	dip	Pointer to disassemble_info
 *	flag	True if a ":<tab>" sequence should follow the address
 * Returns:
 *	0
 * Locking:
 * Remarks:
 *
 */

/* ARGSUSED */
static void
kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag)
{
	kdb_symtab_t symtab;
	int spaces = 5;
	unsigned int offset;
	int slot;

	/* Some code prints slot number, some prints "byte" offset
	 * from start of bundle.  Standardise on "byte" offset.
	 */
	slot = addr & 0x0f;
	if (slot < 3)
		slot *= 6;
	addr = (addr & ~0x0f) + slot;

	/*
	 * Print a symbol name or address as necessary.
	 */
	dip->fprintf_func(dip->stream, "0x%0*lx ", (int)(2*sizeof(addr)), addr);
	kdbnearsym(addr, &symtab);
	if (symtab.sym_name) {
		/* Do not use kdb_symbol_print here, it always does
		 * kdb_printf but we want dip->fprintf_func.
		 */
		dip->fprintf_func(dip->stream, "%s", symtab.sym_name);
		if ((offset = addr - symtab.sym_start) == 0) {
			spaces += 4;
		}
		else {
			unsigned int o = offset;
			while (o >>= 4)
				--spaces;
			dip->fprintf_func(dip->stream, "+0x%x", offset);
		}
	}

	if (flag) {
		if (spaces < 1) {
			spaces = 1;
		}
		dip->fprintf_func(dip->stream, ":%*s", spaces, " ");
	}
}

/* Calls outside the current kernel module use a PLT */

static int addr_maybe_plt;

/* The templates below were extracted from arch/ia64/kernel/module.c.  The
 * masks were generated by this quick and dirty program:
 */

#if 0	/* mask program */
#include <stdio.h>

#define u64 unsigned long

/* routines copied from arch/ia64/kernel/patch.c */

static void
ia64_patch (u64 insn_addr, u64 mask, u64 val)
{
	u64 m0, m1, v0, v1, b0, b1, *b = (u64 *) (insn_addr & -16);
#	define insn_mask ((1UL << 41) - 1)
	unsigned long shift;

	b0 = b[0]; b1 = b[1];
	shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */
	if (shift >= 64) {
		m1 = mask << (shift - 64);
		v1 = val << (shift - 64);
	} else {
		m0 = mask << shift; m1 = mask >> (64 - shift);
		v0 = val  << shift; v1 = val >> (64 - shift);
		b[0] = (b0 & ~m0) | (v0 & m0);
	}
	b[1] = (b1 & ~m1) | (v1 & m1);
}

static void
ia64_patch_imm64 (u64 insn_addr, u64 val)
{
	/* The assembler may generate offset pointing to either slot 1
	   or slot 2 for a long (2-slot) instruction, occupying slots 1
	   and 2.  */
  	insn_addr &= -16UL;
	ia64_patch(insn_addr + 2,
		   0x01fffefe000UL, (  ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */
				     | ((val & 0x0000000000200000UL) <<  0) /* bit 21 -> 21 */
				     | ((val & 0x00000000001f0000UL) <<  6) /* bit 16 -> 22 */
				     | ((val & 0x000000000000ff80UL) << 20) /* bit  7 -> 27 */
				     | ((val & 0x000000000000007fUL) << 13) /* bit  0 -> 13 */));
	ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22);
}

static void
ia64_patch_imm60 (u64 insn_addr, u64 val)
{
	/* The assembler may generate offset pointing to either slot 1
	   or slot 2 for a long (2-slot) instruction, occupying slots 1
	   and 2.  */
  	insn_addr &= -16UL;
	ia64_patch(insn_addr + 2,
		   0x011ffffe000UL, (  ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */
				     | ((val & 0x00000000000fffffUL) << 13) /* bit  0 -> 13 */));
	ia64_patch(insn_addr + 1, 0x1fffffffffcUL, val >> 18);
}

struct plt_entry {
	unsigned char bundle[3][16];
};
static struct plt_entry ia64_plt_mask;

int main(void)
{
	int i, j;
	printf("2 bundle\n");
	for (i = 0; i < 2; ++i)
		for (j = 0; j < 16; ++j)
			ia64_plt_mask.bundle[i][j] = 0xff;
	ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 0), 0);
	ia64_patch_imm60((u64) (ia64_plt_mask.bundle + 1), 0);
	for (i = 0; i < 2; ++i) {
		for (j = 0; j < 16; ++j) {
			printf("0x%02x", ia64_plt_mask.bundle[i][j]);
			if (j != 15)
				printf(", ");
			if (j % 6 == 5 || j == 15)
				printf("\n");
		}
	}
	printf("\n3 bundle\n");
	for (i = 0; i < 3; ++i)
		for (j = 0; j < 16; ++j)
			ia64_plt_mask.bundle[i][j] = 0xff;
	ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 0), 0);
	ia64_patch_imm64((u64) (ia64_plt_mask.bundle + 1), 0);
	for (i = 0; i < 3; ++i) {
		for (j = 0; j < 16; ++j) {
			printf("0x%02x", ia64_plt_mask.bundle[i][j]);
			if (j != 15)
				printf(", ");
			if (j % 6 == 5 || j == 15)
				printf("\n");
		}
	}
	return 0;
}
#endif	/* mask program */

#ifdef CONFIG_IA64_BRL_EMU
#define PLT_BUNDLES 3
struct plt_entry {
	unsigned char bundle[PLT_BUNDLES][16];
};
static const struct plt_entry ia64_plt_template = {
	{
		{
			0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*	     movl r16=TARGET_IP */
			0x02, 0x00, 0x00, 0x60
		},
		{
			0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /*	     movl gp=TARGET_GP */
			0x00, 0x00, 0x00, 0x60
		},
		{
			0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0 */
			0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /*	     mov b6=r16 */
			0x60, 0x00, 0x80, 0x00		    /*	     br.few b6 */
		}
	}
};
static const struct plt_entry ia64_plt_mask = {
	{
		{
			0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /*       movl r16=TARGET_IP */
			0x0f, 0x08, 0x00, 0xf0
		},
		{
			0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /*       movl gp=TARGET_GP */
			0x0f, 0x08, 0x00, 0xf0
		},
		{
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* [MIB] nop.m 0 */
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /*       mov b6=r16 */
			0xff, 0xff, 0xff, 0xff              /*       br.few b6 */
		}
};

#else /* !CONFIG_IA64_BRL_EMU */

#define PLT_BUNDLES 2
struct plt_entry {
	unsigned char bundle[PLT_BUNDLES][16];
};
static const struct plt_entry ia64_plt_template = {
	{
		{
			0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /*	     movl gp=TARGET_GP */
			0x00, 0x00, 0x00, 0x60
		},
		{
			0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*	     brl.many TARGET_IP */
			0x08, 0x00, 0x00, 0xc0
		}
	}
};
static const struct plt_entry ia64_plt_mask = {
	{
		{
			0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /*       movl gp=TARGET_GP */
			0x0f, 0x08, 0x00, 0xf0
		},
		{
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* [MLX] nop.m 0 */
			0x00, 0x00, 0x00, 0x00, 0x80, 0xff, /*       brl.many TARGET_IP */
			0x0f, 0x00, 0x00, 0xf7
		}
	}
};
#endif /* CONFIG_IA64_BRL_EMU */

static inline u64
get_slot(u64 bundle, int slot)
{
	switch (slot) {
	case 0:
		return (((u64 *)bundle)[0] >> 5) & 0x1ffffffffffLL;
	case 1:
		return ((((u64 *)bundle)[0] >> 46) & 0x3ffff) |
		       ((((u64 *)bundle)[1] & 0x7fffff) << 18);
	default:
		return (((u64 *)bundle)[1] >> 23) & 0x1ffffffffffLL;
	}
}

static inline u64
get_movl (u64 addr)
{
	/* X2 format */
	u64 slot1 = get_slot(addr, 1), slot2 = get_slot(addr, 2);
	u64 i, imm9d, imm5c, ic, imm7b, imm41;
	i = (slot2 >> 36) & 0x1UL;
	imm9d = (slot2 >> 27) & 0xfffUL;
	imm5c = (slot2 >> 22) & 0x1fUL;
	ic = (slot2 >> 21) & 0x1UL;
	imm7b = (slot2 >> 13) & 0x7fUL;
	imm41 = slot1;
	return (i << 63) | (imm41 << 22) | (ic << 21) | (imm5c << 16) |
	       (imm9d << 7) | imm7b;
}

static inline u64
get_brl (u64 addr)
{
	/* X3 format */
	u64 slot1 = get_slot(addr, 1), slot2 = get_slot(addr, 2);
	u64 i, imm20b, imm39;
	i = (slot2 >> 36) & 0x1UL;
	imm20b = (slot2 >> 13) & 0xfffffUL;
	imm39 = slot1 >> 2;
	return ((i << 59) | (imm39 << 20) | imm20b) << 4;
}

static bfd_vma
is_plt(bfd_vma addr) {
	int i, j;
	u64 target;
	struct plt_entry plt;
	if (kdb_getarea_size(&plt, addr, sizeof(plt)))
		return 0;
	for (i = 0; i < PLT_BUNDLES; ++i) {
		for (j = 0; j < 16; ++j) {
			if ((plt.bundle[i][j] & ia64_plt_mask.bundle[i][j]) !=
			    ia64_plt_template.bundle[i][j])
				return 0;
		}
	}
	if (PLT_BUNDLES == 2) {
		/* brl is IP relative, in second bundle */
		target = get_brl(addr + 16) + addr + 16;
	} else {
		/* movl is absolute, in first bundle */
		target = get_movl(addr);
	}
	return target;
}

/*
 * kdba_dis_printaddr
 *
 *	Print (symbolically) an address.  Called by GNU disassembly
 *	code via disassemble_info structure.
 *
 * Parameters:
 *	addr	Address for which to get symbol
 *	dip	Pointer to disassemble_info
 * Returns:
 *	0
 * Locking:
 * Remarks:
 *	This function will never append ":<tab>" to the printed
 *	symbolic address.  If the address may be a PLT entry then try to decode
 *	the PLT information.
 */

static void
kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip)
{
	bfd_vma target;
	kdba_printaddress(addr, dip, 0);
	if (!addr_maybe_plt)
		return;
	if (!(target = is_plt(addr)))
		return;
	kdb_printf(" PLT --> ");
	kdba_printaddress(target, dip, 0);
}

/*
 * kdba_dis_getmem
 *
 *	Fetch 'length' bytes from 'addr' into 'buf'.
 *
 * Parameters:
 *	addr	Address for which to get symbol
 *	buf	Address of buffer to fill with bytes from 'addr'
 *	length	Number of bytes to fetch
 *	dip	Pointer to disassemble_info
 * Returns:
 *	0
 * Locking:
 * Remarks:
 *
 */

/* ARGSUSED */
static int
kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip)
{
	return kdb_getarea_size(buf, addr, length);
}

/*
 * kdba_id_parsemode
 *
 * 	Parse IDMODE environment variable string and
 *	set appropriate value into "disassemble_info" structure.
 *
 * Parameters:
 *	mode	Mode string
 *	dip	Disassemble_info structure pointer
 * Returns:
 * Locking:
 * Remarks:
 *	No mode supported yet.
 */

int
kdba_id_parsemode(const char *mode, disassemble_info *dip)
{
	if (mode && strcmp(mode, "ia64"))
		return KDB_BADMODE;
	return 0;
}

/*
 * kdba_check_pc
 *
 * 	Check that the pc is satisfactory.
 *
 * Parameters:
 *	pc	Program Counter Value.
 * Returns:
 *	None
 * Locking:
 *	None.
 * Remarks:
 *	Can change pc.
 */

void
kdba_check_pc(kdb_machreg_t *pc)
{
	(*pc) &= ~0xf;	/* pc must be 16 byte aligned */
}

/*
 * kdba_id_printinsn
 *
 * 	Format and print a single bundle at 'pc'. Return the
 *	length of the bundle.
 *
 * Parameters:
 *	pc	Program Counter Value.
 *	dip	Disassemble_info structure pointer
 * Returns:
 *	Length of instruction, -1 for error.
 * Locking:
 *	None.
 * Remarks:
 *	None.
 */

int
kdba_id_printinsn(kdb_machreg_t pc, disassemble_info *dip)
{
	int ret;
	int byte = 0;

	kdba_check_pc(&pc);
	while (byte < 16) {
		kdba_dis_printaddr(pc+byte, dip);
		addr_maybe_plt = 1;
		ret = print_insn_ia64((kdb_machreg_t)(pc+byte), dip);
		addr_maybe_plt = 0;
		dip->fprintf_func(dip->stream, "\n");
		if (ret < 0)
			break;
		byte += ret;
	}
	return(byte);
}

/*
 * kdba_id_init
 *
 * 	Initialize the architecture dependent elements of
 *	the disassembly information structure
 *	for the GNU disassembler.
 *
 * Parameters:
 *	None.
 * Outputs:
 *	None.
 * Returns:
 *	None.
 * Locking:
 *	None.
 * Remarks:
 */

void
kdba_id_init(disassemble_info *dip)
{
	dip->read_memory_func       = kdba_dis_getmem;
	dip->print_address_func     = kdba_dis_printaddr;
	dip->symbol_at_address_func = kdba_dis_getsym;

	dip->flavour                = bfd_target_elf_flavour;
	dip->arch		    = bfd_arch_ia64;
	dip->endian	    	    = BFD_ENDIAN_LITTLE;

	dip->display_endian         = BFD_ENDIAN_LITTLE;
}