📄 step.cpp
字号:
/* * linux/arch/arm/kernel/ptrace.c * * By Ross Biro 1/23/92 * edited by Linus Torvalds * ARM modifications Copyright (C) 2000 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. *//* Modified for use in jelie by cipix@iv.ro (2003) */#include "jtagpxa250.h"#include "stdio.h"extern JTAGpxa250 *pxa250Ptr;#define REG_PC 15#define REG_PSR 16//#define BREAKINST_ARM 0xef9f0001#define BREAKINST_ARM 0xe1200070 /*bkpt #0*//* fill this in later */#define BREAKINST_THUMB 0xdf00#define predicate(x) (x & 0xf0000000)#define PREDICATE_ALWAYS 0xe0000000#define PCMASK (0x3)#define pc_pointer(v) ((v) & ~PCMASK)#define CC_C_BIT (1 << 29)typedef unsigned int u32;typedef unsigned short u16;union debug_insn { u32 arm; u16 thumb;};struct debug_entry { u32 address; union debug_insn insn;};struct debug_info { int nsaved; struct debug_entry bp[2];};static inline unsigned int hweight16(unsigned int w){unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555); res = (res & 0x3333) + ((res >> 2) & 0x3333); res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); return (res & 0x00FF) + ((res >> 8) & 0x00FF);}/* * this routine will get a word off of the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. * this routine assumes that all the privileged stacks are in our * data space. */static inline long get_user_reg(int offset){ return pxa250Ptr->getSavePlace(offset);}/* * this routine will put a word on the processes privileged stack. * the offset is how far from the base addr as stored in the THREAD. * this routine assumes that all the privileged stacks are in our * data space. */static inline intput_user_reg(int offset, long data){ pxa250Ptr->setSavePlace(offset,data); return 0;}static inline intread_u32(unsigned long addr, u32 *res){ int ret; if (addr & 0x3) { printf("FIXME: unalligned access not performed - returning crap\n"); } else { pxa250Ptr->getData(addr, 1, res); } return 0;}static inline intread_instr(unsigned long addr, u32 *res){ int ret; u32 val; pxa250Ptr->getData(addr, 1, res); return 0;}/* * Get value of register `rn' (in the instruction) */static unsigned longjelie_getrn(unsigned long insn){ unsigned int reg = (insn >> 16) & 15; unsigned long val; val = get_user_reg(reg); if (reg == 15) val = pc_pointer(val + 8); return val;}/* * Get value of operand 2 (in an ALU instruction) */static unsigned longjelie_getaluop2(unsigned long insn){ unsigned long val; int shift; int type; if (insn & 1 << 25) { val = insn & 255; shift = (insn >> 8) & 15; type = 3; } else { val = get_user_reg (insn & 15); if (insn & (1 << 4)) shift = (int)get_user_reg ((insn >> 8) & 15); else shift = (insn >> 7) & 31; type = (insn >> 5) & 3; } switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; case 2: val = (((signed long)val) >> shift); break; case 3: val = (val >> shift) | (val << (32 - shift)); break; } return val;}/* * Get value of operand 2 (in a LDR instruction) */static unsigned longjelie_getldrop2(unsigned long insn){ unsigned long val; int shift; int type; val = get_user_reg(insn & 15); shift = (insn >> 7) & 31; type = (insn >> 5) & 3; switch (type) { case 0: val <<= shift; break; case 1: val >>= shift; break; case 2: val = (((signed long)val) >> shift); break; case 3: val = (val >> shift) | (val << (32 - shift)); break; } return val;}#define OP_MASK 0x01e00000#define OP_AND 0x00000000#define OP_EOR 0x00200000#define OP_SUB 0x00400000#define OP_RSB 0x00600000#define OP_ADD 0x00800000#define OP_ADC 0x00a00000#define OP_SBC 0x00c00000#define OP_RSC 0x00e00000#define OP_ORR 0x01800000#define OP_MOV 0x01a00000#define OP_BIC 0x01c00000#define OP_MVN 0x01e00000static unsigned longget_branch_address(unsigned long pc, unsigned long insn){ u32 alt = 0; switch (insn & 0x0e000000) { case 0x00000000: case 0x02000000: { /* * data processing */ long aluop1, aluop2, ccbit; if ((insn & 0xf000) != 0xf000) break; aluop1 = jelie_getrn(insn); aluop2 = jelie_getaluop2(insn); ccbit = get_user_reg(REG_PSR) & CC_C_BIT ? 1 : 0; switch (insn & OP_MASK) { case OP_AND: alt = aluop1 & aluop2; break; case OP_EOR: alt = aluop1 ^ aluop2; break; case OP_SUB: alt = aluop1 - aluop2; break; case OP_RSB: alt = aluop2 - aluop1; break; case OP_ADD: alt = aluop1 + aluop2; break; case OP_ADC: alt = aluop1 + aluop2 + ccbit; break; case OP_SBC: alt = aluop1 - aluop2 + ccbit; break; case OP_RSC: alt = aluop2 - aluop1 + ccbit; break; case OP_ORR: alt = aluop1 | aluop2; break; case OP_MOV: alt = aluop2; break; case OP_BIC: alt = aluop1 & ~aluop2; break; case OP_MVN: alt = ~aluop2; break; } break; } case 0x04000000: case 0x06000000: /* * ldr */ if ((insn & 0x0010f000) == 0x0010f000) { unsigned long base; base = jelie_getrn(insn); if (insn & 1 << 24) { long aluop2; if (insn & 0x02000000) aluop2 = jelie_getldrop2( insn); else aluop2 = insn & 0xfff; if (insn & 1 << 23) base += aluop2; else base -= aluop2; } if (read_u32(base, &alt) == 0) alt = pc_pointer(alt); } break; case 0x08000000: /* * ldm */ if ((insn & 0x00108000) == 0x00108000) { unsigned long base; int nr_regs; if (insn & (1 << 23)) { nr_regs = hweight16(insn & 65535) << 2; if (!(insn & (1 << 24))) nr_regs -= 4; } else { if (insn & (1 << 24)) nr_regs = -4; else nr_regs = 0; } base = jelie_getrn(insn); if (read_u32(base + nr_regs, &alt) == 0) alt = pc_pointer(alt); break; } break; case 0x0a000000: { /* * bl or b */ signed long displ; /* It's a branch/branch link: instead of trying to * figure out whether the branch will be taken or not, * we'll put a breakpoint at both locations. This is * simpler, more reliable, and probably not a whole lot * slower than the alternative approach of emulating the * branch. */ displ = (insn & 0x00ffffff) << 8; displ = (displ >> 6) + 8; if (displ != 0 && displ != 4) alt = pc + displ; } break; } return alt;}static intswap_insn(unsigned long addr, void *old_insn, void *new_insn, int size){ pxa250Ptr->getData(addr,1,(unsigned int *)old_insn); pxa250Ptr->putData(addr,1,(unsigned int *)new_insn); return 4;}static voidadd_breakpoint(struct debug_info *dbg, unsigned long addr){ int nr = dbg->nsaved; if (nr < 2) { u32 new_insn = BREAKINST_ARM; int res; res = swap_insn(addr, &dbg->bp[nr].insn, &new_insn, 4); if (res == 4) { dbg->bp[nr].address = addr; dbg->nsaved += 1; printf("software breakpoint on at %x\n",addr); } } else printf("too many breakpoints\n");}/* * Clear one breakpoint in the user program. We copy what the hardware * does and use bit 0 of the address to indicate whether this is a Thumb * breakpoint or an ARM breakpoint. */static void clear_breakpoint(struct debug_entry *bp){ unsigned long addr = bp->address; union debug_insn old_insn; int ret; printf("software breakpoint off at %x\n",addr); ret = swap_insn(addr, &old_insn.arm, &bp->insn.arm, 4); if (ret != 4 || old_insn.arm != BREAKINST_ARM) { printf("corrupted ARM breakpoint at " "0x%08lx (0x%08x)\n",addr, old_insn.arm); }}struct debug_info dbg_s, *dbg=&dbg_s;void jelie_set_bpt(){ struct pt_regs *regs; unsigned long pc; u32 insn; int res; pc=get_user_reg(REG_PC); res = read_instr(pc, &insn); if (!res) { unsigned long alt; dbg->nsaved = 0; alt = get_branch_address(pc, insn); if (alt) add_breakpoint(dbg, alt); /* * Note that we ignore the result of setting the above * breakpoint since it may fail. When it does, this is * not so much an error, but a forewarning that we may * be receiving a prefetch abort shortly. * * If we don't set this breakpoint here, then we can * loose control of the thread during single stepping. */ if (!alt || predicate(insn) != PREDICATE_ALWAYS) add_breakpoint(dbg, pc + 4); }}/* * Ensure no single-step breakpoint is pending. Returns non-zero * value if child was being single-stepped. */void jelie_cancel_bpt(){ int i, nsaved = dbg->nsaved; dbg->nsaved = 0; if (nsaved > 2) { printf("jelie_cancel_bpt: bogus nsaved: %d!\n", nsaved); nsaved = 2; } for (i = 0; i < nsaved; i++) clear_breakpoint(dbg->bp+i);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -