File: [Development] / linux-2.6-xfs / arch / ia64 / kdb / kdba_id.c (download)
Revision 1.12, Fri Jan 12 15:41:10 2007 UTC (10 years, 9 months ago) by donaldd.longdrop.melbourne.sgi.com
Branch: MAIN
Changes since 1.11: +263 -1
lines
Merge up to 2.6.20-rc4
Merge of 2.6.x-xfs-melb:linux:27915c 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 __init
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;
}