📄 singlestep.c
字号:
//==========================================================================
//
// singlestep.c
//
// ARM(R) specific single-step support.
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):
// Contributors: gthomas
// Date: 1999-10-20
// Purpose: ARM(R) specific single-step support.
// Description: ARM is a Registered Trademark of Advanced RISC Machines Limited.
// Other Brands and Trademarks are the property of their
// respective owners.
//
//####DESCRIPTIONEND####
//
//=========================================================================
#include <stdlib.h>
#include <bsp/bsp.h>
#include <bsp/cpu.h>
#include "insn.h"
#define DEBUG_SINGLESTEP 0
#define DEBUG_SINGLESTEP_VERBOSE 0
/*
* Structure to hold opcodes hoisted when breakpoints are
* set for single-stepping or async interruption.
*/
struct _bp_save {
unsigned long *addr;
unsigned long opcode;
};
#define NUM_BREAKS_SAVED 2
static struct _bp_save _breaks[NUM_BREAKS_SAVED];
/*
* Insert a breakpoint at 'pc' using first available
* _bp_save struct.
*/
static void
insert_ss_break(unsigned long *pc)
{
struct _bp_save *p = _breaks;
union arm_insn inst;
if (p->addr && (++p)->addr)
return;
/*
* We can't set a breakpoint at 0
*/
if (pc == 0)
{
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at <0x%08lx>: Error\n", pc);
#endif /* DEBUG_SINGLESTEP */
return;
}
/*
* Make sure we are on a long word boundary.
*/
if (((unsigned long)pc & 0x3) != 0)
{
/*
* All ARM(R) instructions are on a word boundary.
* This would be invalid. Don't set a bkpt here.
*/
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at <0x%08lx>: Error\n", pc);
#endif /* DEBUG_SINGLESTEP */
return;
}
/*
* What is the current instruction
*/
if (bsp_memory_read(pc, 0, ARM_INST_SIZE * 8, 1, &(inst.word)) == 0)
{
/*
* Unable to read this address, probably an invalid address.
* Don't set a breakpoint here, as it will likely cause a bus error
*/
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at <0x%08lx>: Error\n", pc);
#endif /* DEBUG_SINGLESTEP */
return;
}
if (inst.word != BREAKPOINT_INSN)
{
/*
* Only insert a breakpoint if we haven't done so already
*
* We may try to insert 2 breakpoints if we to a branch to
* the immediately following instruction.
*/
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at <0x%08lx>: inst <0x%08lx>\n", pc, inst.word);
#endif /* DEBUG_SINGLESTEP */
p->addr = pc;
p->opcode = inst.word;
inst.word = BREAKPOINT_INSN;
if (bsp_memory_write(pc, 0, ARM_INST_SIZE * 8, 1, &(inst.word)) == 0)
{
/*
* Unable to write this address, probably an invalid address.
* Don't set a breakpoint here, as it will likely cause a bus error
*/
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at <0x%08lx>: Error\n", pc);
#endif /* DEBUG_SINGLESTEP */
return;
}
/* flush icache and dcache, now */
bsp_flush_dcache((void *)pc, ARM_INST_SIZE);
bsp_flush_icache((void *)pc, ARM_INST_SIZE);
#if DEBUG_SINGLESTEP_VERBOSE
bsp_printf("Done setting BP at <0x%08lx>: inst <0x%08lx>\n", pc, *pc);
#endif /* DEBUG_SINGLESTEP_VERBOSE */
}
}
/*
* Cleanup after a singlestep.
*/
void
bsp_singlestep_cleanup(void *registers)
{
struct _bp_save *p = _breaks;
int i;
for (i = 0; i < NUM_BREAKS_SAVED; i++, p++)
{
if (p->addr)
{
unsigned long *old_addr = p->addr;
#if DEBUG_SINGLESTEP_VERBOSE
bsp_printf("Remove BP at <0x%08lx>: inst <0x%08lx>\n", old_addr, *old_addr);
#endif /* DEBUG_SINGLESTEP */
*(p->addr) = p->opcode;
p->addr = NULL;
/* flush icache and dcache, now */
bsp_flush_dcache((void *)old_addr, ARM_INST_SIZE);
bsp_flush_icache((void *)old_addr, ARM_INST_SIZE);
#if DEBUG_SINGLESTEP_VERBOSE
bsp_printf("Done removing BP at <0x%08lx>: inst <0x%08lx>\n", old_addr, *old_addr);
#endif /* DEBUG_SINGLESTEP_VERBOSE */
}
}
}
/*
* Rotate right a value by count
*/
static unsigned long ror(unsigned long value, unsigned count)
{
while (count-- > 0)
{
if (value & 0x1)
value = (value >> 1) | 0x80000000;
else
value = (value >> 1);
}
return(value);
}
/*
* Rotate right a value by 1 with extend
*/
static unsigned long rrx(union arm_psr sr, unsigned long value)
{
if (sr.psr.c_bit)
value = (value >> 1) | 0x80000000;
else
value = (value >> 1);
return(value);
}
/*
* Logical shift left by count
*/
static unsigned long lsl(unsigned long value, unsigned count)
{
value <<= count;
return(value);
}
/*
* Logical shift right by count
*/
static unsigned long lsr(unsigned long value, unsigned count)
{
value >>= count;
return(value);
}
/*
* Arithmetic shift right by count
*/
static unsigned long asr(unsigned long value, unsigned count)
{
unsigned long sign_ext_mask = 0;
if (value & 0x80000000)
{
if (count >= sizeof(value)*8)
sign_ext_mask = ~0;
else
sign_ext_mask = (~0 << (sizeof(value)*8 - count));
}
value = (value >> count) | sign_ext_mask;
return(value);
}
/*
* Calculate an immediate shift operand based on input shift operand,
* shift value and register address.
*/
static unsigned long immediate_shift_operand(ex_regs_t *regs, unsigned shift_immediate,
unsigned shift, unsigned Rm)
{
unsigned char *regs_array = (unsigned char *)regs;
unsigned char *reg_ptr = ®s_array[bsp_regbyte(Rm)];
unsigned long reg_value = *((unsigned long *)(reg_ptr));
unsigned long rc = 0;
BSP_ASSERT((shift_immediate >= 0) && (shift_immediate <= 0x1f));
BSP_ASSERT((shift >= 0) && (shift <= 0x3));
BSP_ASSERT((Rm >= 0) && (Rm <= 0xf));
BSP_ASSERT(bsp_regsize(Rm) == sizeof(unsigned long));
/*
* According to the ARM(R) Manual, if Rm is PC then,
* the value used is the address of the current instruction
* plus 8
*/
if (Rm == REG_PC)
reg_value += 8;
switch (shift)
{
case SHIFT_LSL:
rc = lsl(reg_value, shift_immediate);
break;
case SHIFT_LSR:
if (shift_immediate == 0)
{
/*
* Special Case: LSR IMM(0) == 0
*/
rc = 0;
} else {
rc = lsr(reg_value, shift_immediate);
}
break;
case SHIFT_ASR:
if (shift_immediate == 0)
{
/*
* Special Case: ASR IMM(0)
*/
if (reg_value & 0x80000000)
{
rc = 0xFFFFFFFF;
} else {
rc = 0;
}
} else {
rc = asr(reg_value, shift_immediate);
}
break;
case SHIFT_ROR:
if (shift_immediate == 0)
{
/*
* SHIFT_RRX
* Special case: ROR(0) implies RRX
*/
rc = rrx((union arm_psr)(unsigned long)regs->_cpsr, reg_value);
} else {
rc = ror(reg_value, shift_immediate);
}
break;
default:
BSP_ASSERT(0);
break;
}
return (rc);
}
/*
* Calculate a register shift operand based on input shift operand,
* and target registers.
*/
static unsigned long register_shift_operand(ex_regs_t *regs, unsigned Rs,
unsigned shift, unsigned Rm)
{
unsigned char *regs_array = (unsigned char *)regs;
unsigned char *Rs_ptr = ®s_array[bsp_regbyte(Rs)];
unsigned char *Rm_ptr = ®s_array[bsp_regbyte(Rm)];
unsigned long Rs_val = *((unsigned long *)(Rs_ptr));
unsigned long Rm_val = *((unsigned long *)(Rm_ptr));
unsigned long rc = 0;
/*
* Use only the least significant byte of Rs
*/
Rs_val &= 0xFF;
BSP_ASSERT((Rs >= 0) && (Rs <= 0xf));
BSP_ASSERT((shift >= 0) && (shift <= 0x3));
BSP_ASSERT((Rm >= 0) && (Rm <= 0xf));
BSP_ASSERT(bsp_regsize(Rs) == sizeof(unsigned long));
BSP_ASSERT(bsp_regsize(Rm) == sizeof(unsigned long));
BSP_ASSERT((Rs_val >=0) && (Rs_val <= 0xff));
/*
* According to the ARM(R) Manual, if Rm is PC then,
* the value used is the address of the current instruction
* plus 8
*/
if (Rm == REG_PC)
Rm_val += 8;
switch (shift)
{
case SHIFT_LSL: rc = lsl(Rm_val, Rs_val); break;
case SHIFT_LSR: rc = lsr(Rm_val, Rs_val); break;
case SHIFT_ASR: rc = asr(Rm_val, Rs_val); break;
case SHIFT_ROR: rc = ror(Rm_val, Rs_val); break;
default: BSP_ASSERT(0); break;
}
return (rc);
}
/*
* Calculate a branch exchange operand based on input destination register
*/
static unsigned long branch_exchange_operand(ex_regs_t *regs, unsigned Rm)
{
unsigned char *regs_array = (unsigned char *)regs;
unsigned char *reg_ptr = ®s_array[bsp_regbyte(Rm)];
unsigned long reg_value = *((unsigned long *)(reg_ptr));
BSP_ASSERT((Rm >= 0) && (Rm <= 0xf));
BSP_ASSERT(bsp_regsize(Rm) == sizeof(unsigned long));
/*
* Clear the low-order bit
*/
return (reg_value & ~0x1);
}
/*
* Handle a load to the PC
*/
static void handle_pc_load(unsigned size, unsigned long operand)
{
unsigned long mem_value = 0;
if (size == LS_SIZE_WORD)
{
if (bsp_memory_read((void*)(operand & ~0x3), 0, 32, 1, &mem_value) == 0)
{
/*
* Unable to read the memory address.
* Don't try any further.
*/
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at *(0x%08lx): Error\n", operand & ~0x3);
#endif /* DEBUG_SINGLESTEP */
return;
} else {
#if DEBUG_SINGLESTEP
bsp_printf("Setting BP at *(0x%08lx): data <0x%08lx>\n", operand & ~0x3, mem_value);
#endif /* DEBUG_SINGLESTEP */
}
/*
* Handle rotations if required
*/
switch (operand & 0x3)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -