📄 arm_stub.c
字号:
// Immediate offset
if (ins & 0x00800000) {
// Add offset
Rn += (ins & 0x00000FFF);
} else {
// Subtract offset
Rn -= (ins & 0x00000FFF);
}
} else {
// Offset is in a register
if (ins & 0x00800000) {
// Add offset
Rn += RmShifted(ins & 0x00000FFF);
} else {
// Subtract offset
Rn -= RmShifted(ins & 0x00000FFF);
}
}
}
return ((unsigned long *)*(unsigned long *)Rn);
}
}
}
return (pc+1);
case 0x2: // Branch, LDM/STM
if ((ins & 0x02000000) == 0) {
// LDM/STM
if ((ins & 0x00100000) == 0) {
// STM
return (pc+1);
} else {
// LDM
if ((ins & 0x00008000) == 0) {
// PC not in list
return (pc+1);
} else {
Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
offset = ins & 0x0000FFFF;
reg_count = 0;
for (i = 0; i < 15; i++) {
if (offset & (1<<i)) reg_count++;
}
if (ins & 0x00800000) {
// Add offset
Rn += reg_count*4;
} else {
// Subtract offset
Rn -= 4;
}
return ((unsigned long *)*(unsigned long *)Rn);
}
}
} else {
// Branch
if (ins_will_execute(ins)) {
offset = (ins & 0x00FFFFFF) << 2;
if (ins & 0x00800000) offset |= 0xFC000000; // sign extend
new_pc = (unsigned long)(pc+2) + offset;
// If its BLX, make new_pc a thumb address.
if ((ins & 0xFE000000) == 0xFA000000) {
if ((ins & 0x01000000) == 0x01000000)
new_pc |= 2;
new_pc = MAKE_THUMB_ADDR(new_pc);
}
return ((unsigned long *)new_pc);
} else {
// Falls through
return (pc+1);
}
}
case 0x3: // Coprocessor & SWI
if (((ins & 0x03000000) == 0x03000000) && ins_will_execute(ins)) {
// SWI
return (CYGNUM_HAL_VECTOR_SOFTWARE_INTERRUPT * 4);
} else {
return (pc+1);
}
default:
// Never reached - but fixes compiler warning.
return 0;
}
}
// FIXME: target_ins also needs to check for CPSR/THUMB being set and
// set the thumb bit accordingly.
static unsigned long
target_thumb_ins(unsigned long pc, unsigned short ins)
{
unsigned long new_pc = MAKE_THUMB_ADDR(pc+2); // default is fall-through
// to next thumb instruction
unsigned long offset, arm_ins, sp;
int i;
switch ((ins & 0xf000) >> 12) {
case 0x4:
// Check for BX or BLX
if ((ins & 0xff07) == 0x4700)
new_pc = (unsigned long)get_register((ins & 0x00078) >> 3);
break;
case 0xb:
// push/pop
// Look for "pop {...,pc}"
if ((ins & 0xf00) == 0xd00) {
// find PC
sp = (unsigned long)get_register(SP);
for (offset = i = 0; i < 8; i++)
if (ins & (1 << i))
offset += 4;
new_pc = *(cyg_uint32 *)(sp + offset);
if (!v5T_semantics())
new_pc = MAKE_THUMB_ADDR(new_pc);
}
break;
case 0xd:
// Bcc | SWI
// Use ARM function to check condition
arm_ins = ((unsigned long)(ins & 0x0f00)) << 20;
if ((arm_ins & 0xF0000000) == 0xF0000000) {
// SWI
new_pc = CYGNUM_HAL_VECTOR_SOFTWARE_INTERRUPT * 4;
} else if (ins_will_execute(arm_ins)) {
offset = (ins & 0x00FF) << 1;
if (ins & 0x0080) offset |= 0xFFFFFE00; // sign extend
new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset);
}
break;
case 0xe:
// check for B
if ((ins & 0x0800) == 0) {
offset = (ins & 0x07FF) << 1;
if (ins & 0x0400) offset |= 0xFFFFF800; // sign extend
new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset);
}
break;
case 0xf:
// BL/BLX (4byte instruction!)
// First instruction (bit 11 == 0) holds top-part of offset
if ((ins & 0x0800) == 0) {
offset = (ins & 0x07FF) << 12;
if (ins & 0x0400) offset |= 0xFF800000; // sign extend
// Get second instruction
// Second instruction (bit 11 == 1) holds bottom-part of offset
ins = *(unsigned short*)(pc+2);
// Check for BL/BLX
if ((ins & 0xE800) == 0xE800) {
offset |= (ins & 0x07ff) << 1;
new_pc = (unsigned long)(pc+4) + offset;
// If its BLX, force a full word alignment
// Otherwise, its a thumb address.
if (!(ins & 0x1000))
new_pc &= ~3;
else
new_pc = MAKE_THUMB_ADDR(new_pc);
}
}
break;
}
return new_pc;
}
void __single_step (void)
{
unsigned long pc = get_register(PC);
unsigned long cpsr = get_register(PS);
// Calculate address of next instruction to be executed
if (cpsr & CPSR_THUMB_ENABLE) {
// thumb
ss_saved_pc = target_thumb_ins(pc, *(unsigned short*)pc);
} else {
// ARM
unsigned long curins = *(unsigned long*)pc;
if (ins_will_execute(curins)) {
// Decode instruction to decide what the next PC will be
ss_saved_pc = (unsigned long) target_ins((unsigned long*)pc,
curins);
} else {
// The current instruction will not execute (the conditions
// don't hold)
ss_saved_pc = pc+4;
}
}
// Set breakpoint according to type
if (IS_THUMB_ADDR(ss_saved_pc)) {
// Thumb instruction
unsigned long t_pc = UNMAKE_THUMB_ADDR(ss_saved_pc);
ss_saved_thumb_instr = *(unsigned short*)t_pc;
*(unsigned short*)t_pc = HAL_BREAKINST_THUMB;
} else {
// ARM instruction
ss_saved_instr = *(unsigned long*)ss_saved_pc;
*(unsigned long*)ss_saved_pc = HAL_BREAKINST_ARM;
}
}
/* Clear the single-step state. */
void __clear_single_step (void)
{
if (ss_saved_pc != 0) {
// Restore instruction according to type
if (IS_THUMB_ADDR(ss_saved_pc)) {
// Thumb instruction
unsigned long t_pc = UNMAKE_THUMB_ADDR(ss_saved_pc);
*(unsigned short*)t_pc = ss_saved_thumb_instr;
} else {
// ARM instruction
*(unsigned long*)ss_saved_pc = ss_saved_instr;
}
ss_saved_pc = 0;
}
}
void __install_breakpoints (void)
{
#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
/* Install the breakpoints in the breakpoint list */
__install_breakpoint_list();
#endif
}
void __clear_breakpoints (void)
{
#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
__clear_breakpoint_list();
#endif
}
/* If the breakpoint we hit is in the breakpoint() instruction, return a
non-zero value. */
int
__is_breakpoint_function ()
{
return get_register (PC) == (target_register_t)&_breakinst;
}
/* Skip the current instruction. Since this is only called by the
stub when the PC points to a breakpoint or trap instruction,
we can safely just skip 4. */
void __skipinst (void)
{
unsigned long pc = get_register(PC);
unsigned long cpsr = get_register(PS);
if (cpsr & CPSR_THUMB_ENABLE)
pc += 2;
else
pc += 4;
put_register(PC, pc);
}
//-----------------------------------------------------------------------
// Thumb-aware GDB interrupt handler.
// This is a brute-force replacement of the ones in hal_stub.c. Need to
// find a better way of handling it... Maybe... Probably only ARM/thumb
// that is this weird.
typedef struct
{
cyg_uint32 targetAddr;
union {
cyg_uint32 arm_instr;
cyg_uint16 thumb_instr;
} savedInstr;
} instrBuffer;
static instrBuffer break_buffer;
volatile int cyg_hal_gdb_running_step = 0;
// This function is passed thumb/arm information about the PC address
// in bit 0. This information is passed on to the break_buffer.
void
cyg_hal_gdb_place_break (target_register_t pc)
{
// Clear flag that we Continued instead of Stepping
cyg_hal_gdb_running_step = 0;
if (0 == break_buffer.targetAddr) {
// Setting a breakpoint in Thumb or ARM code?
if (IS_THUMB_ADDR(pc)) {
break_buffer.targetAddr = (cyg_uint32)pc;
pc = UNMAKE_THUMB_ADDR(pc);
break_buffer.savedInstr.thumb_instr = *(cyg_uint16*)pc;
*(cyg_uint16*)pc = HAL_BREAKINST_THUMB;
} else {
break_buffer.targetAddr = (cyg_uint32)pc;
break_buffer.savedInstr.arm_instr = *(cyg_uint32*)pc;
*(cyg_uint32*)pc = HAL_BREAKINST_ARM;
}
__data_cache(CACHE_FLUSH);
__instruction_cache(CACHE_FLUSH);
}
}
int
cyg_hal_gdb_remove_break (target_register_t pc)
{
if ( cyg_hal_gdb_running_step )
return 0; // Do not remove the break: we must hit it!
if (pc == UNMAKE_THUMB_ADDR(break_buffer.targetAddr)) {
if (IS_THUMB_ADDR(break_buffer.targetAddr)) {
*(cyg_uint16*)pc = break_buffer.savedInstr.thumb_instr;
} else {
*(cyg_uint32*)pc = break_buffer.savedInstr.arm_instr;
}
break_buffer.targetAddr = 0;
__data_cache(CACHE_FLUSH);
__instruction_cache(CACHE_FLUSH);
return 1;
}
return 0;
}
void
cyg_hal_gdb_interrupt (target_register_t pc)
{
// Clear flag that we Continued instead of Stepping
cyg_hal_gdb_running_step = 0;
// and override existing break? So that a ^C takes effect...
if (0 != break_buffer.targetAddr)
cyg_hal_gdb_remove_break( break_buffer.targetAddr );
if (0 == break_buffer.targetAddr) {
cyg_uint32 cpsr = get_register(PS);
if (cpsr & CPSR_THUMB_ENABLE) {
break_buffer.targetAddr = MAKE_THUMB_ADDR((cyg_uint32)pc);
break_buffer.savedInstr.thumb_instr = *(cyg_uint16*)pc;
*(cyg_uint16*)pc = HAL_BREAKINST_THUMB;
} else {
break_buffer.targetAddr = (cyg_uint32)pc;
break_buffer.savedInstr.arm_instr = *(cyg_uint32*)pc;
*(cyg_uint32*)pc = HAL_BREAKINST_ARM;
}
__data_cache(CACHE_FLUSH);
__instruction_cache(CACHE_FLUSH);
}
}
int
cyg_hal_gdb_break_is_set (void)
{
if (0 != break_buffer.targetAddr) {
return 1;
}
return 0;
}
#ifdef CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT
#define ICE_THREAD_KEY0 0xDEAD0001
#define ICE_THREAD_KEY1 0xDEAD0002
#define ICE_THREAD_INBUFSIZ 2048
#define ICE_THREAD_OUTBUFSIZ 2048
#define ICE_THREAD_STACKSIZE 4096
static cyg_uint8 ice_thread_inbuf[ICE_THREAD_INBUFSIZ];
static cyg_uint8 ice_thread_outbuf[ICE_THREAD_OUTBUFSIZ];
static cyg_uint8 ice_thread_stack[ICE_THREAD_STACKSIZE];
static void ice_thread_proc(void);
struct {
cyg_uint32 _key0; // Must be ICE_KEY0
cyg_uint8 *in_buffer;
cyg_int32 in_buffer_size;
cyg_uint8 *out_buffer;
cyg_int32 out_buffer_size;
cyg_uint8 *stack;
cyg_int32 stack_size;
void (*fun)(void);
cyg_uint32 _key1; // Must be ICE_KEY1
} hal_arm_ice_thread_handler = {
ICE_THREAD_KEY0,
ice_thread_inbuf,
ICE_THREAD_INBUFSIZ,
ice_thread_outbuf,
ICE_THREAD_OUTBUFSIZ,
ice_thread_stack,
ICE_THREAD_STACKSIZE,
ice_thread_proc,
ICE_THREAD_KEY1,
};
static int
ice_thread_query(void)
{
switch (ice_thread_inbuf[1]) {
case 'L': // get thread list
stub_pkt_getthreadlist(&ice_thread_inbuf[2], ice_thread_outbuf, sizeof(ice_thread_outbuf));
break;
case 'P': // thread or process information
stub_pkt_getthreadinfo(&ice_thread_inbuf[2], ice_thread_outbuf, sizeof(ice_thread_outbuf));
break;
case 'C': // current thread
stub_pkt_currthread(&ice_thread_inbuf[2], ice_thread_outbuf, sizeof(ice_thread_outbuf));
break;
default:
return 0;
}
return 1;
}
static int
ice_thread_set(void)
{
return 0;
}
static void
ice_thread_proc(void)
{
switch (ice_thread_inbuf[0]) {
case 'g': // Fetch thread registers
stub_format_registers(ice_thread_outbuf);
return;
case 'P': // Update a single register
case 'G': // Update all registers
stub_update_registers(ice_thread_inbuf, ice_thread_outbuf);
return;
case 'H': // Thread set/query
stub_pkt_changethread(&ice_thread_inbuf[1], ice_thread_outbuf, sizeof(ice_thread_outbuf));
return;
case 'q': // Thread queries
if (ice_thread_query()) return;
break;
case 'Q': // Thread set operations
if (ice_thread_set()) return;
break;
case 'T': // Thread alive?
stub_pkt_thread_alive(&ice_thread_inbuf[1], ice_thread_outbuf, sizeof(ice_thread_outbuf));
return;
default:
}
strcpy(ice_thread_outbuf, "ENN"); // Dunno
}
#endif // CYGHWR_HAL_ARM_ICE_THREAD_SUPPORT
#endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -