⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 arm_stub.c

📁 eCos/RedBoot for勤研ARM AnywhereII(4510) 含全部源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
                            // 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 + -