📄 arm_stub.c
字号:
//========================================================================
//
// arm_stub.c
//
// Helper functions for stub, generic to all ARM processors
//
//========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Red Hat eCos Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.redhat.com/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is eCos - Embedded Configurable Operating System,
// released September 30, 1998.
//
// The Initial Developer of the Original Code is Red Hat.
// Portions created by Red Hat are
// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): Red Hat, gthomas
// Contributors: Red Hat, gthomas, jskov
// Date: 1998-11-26
// Purpose:
// Description: Helper functions for stub, generic to all ARM processors
// Usage:
//
//####DESCRIPTIONEND####
//
//========================================================================
#include <stddef.h>
#include <pkgconf/hal.h>
#ifdef CYGPKG_REDBOOT
#include <pkgconf/redboot.h>
#endif
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
#ifdef CYGPKG_HAL_ARM_SIM
#error "GDB Stub support not implemented for ARM SIM"
#endif
#include <cyg/hal/hal_stub.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
// Use bit 0 as a thumb-mode flag for next address to be executed.
// Alternative would be to keep track of it using a C variable, but
// since bit 0 is used by the BX instruction, we might as well do the
// same thing and thus avoid checking two different flags.
#define IS_THUMB_ADDR(addr) ((addr) & 1)
#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
#ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
#include <cyg/hal/dbg-threads-api.h> // dbg_currthread_id
#endif
#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
cyg_uint32 __arm_breakinst = HAL_BREAKINST_ARM;
cyg_uint16 __thumb_breakinst = HAL_BREAKINST_THUMB;
#endif
/* Given a trap value TRAP, return the corresponding signal. */
int __computeSignal (unsigned int trap_number)
{
// Check to see if we stopped because of a hw watchpoint/breakpoint.
#ifdef HAL_STUB_IS_STOPPED_BY_HARDWARE
{
void *daddr;
if (HAL_STUB_IS_STOPPED_BY_HARDWARE(daddr))
return SIGTRAP;
}
#endif
// should also catch CYGNUM_HAL_VECTOR_UNDEF_INSTRUCTION here but we
// can't tell the different between a real one and a breakpoint :-(
switch (trap_number) {
case CYGNUM_HAL_VECTOR_ABORT_PREFETCH: // Fall through
case CYGNUM_HAL_VECTOR_ABORT_DATA: // Fall through
case CYGNUM_HAL_VECTOR_reserved:
return SIGBUS;
case CYGNUM_HAL_VECTOR_IRQ:
case CYGNUM_HAL_VECTOR_FIQ:
return SIGINT;
default:
return SIGTRAP;
}
}
/* Return the trap number corresponding to the last-taken trap. */
int __get_trap_number (void)
{
// The vector is not not part of the GDB register set so get it
// directly from the save context.
return _hal_registers->vector;
}
#if defined(CYGSEM_REDBOOT_BSP_SYSCALLS)
int __is_bsp_syscall(void)
{
unsigned long pc = get_register(PC);
unsigned long cpsr = get_register(PS); // condition codes
if (_hal_registers->vector == CYGNUM_HAL_EXCEPTION_INTERRUPT) {
if (cpsr & CPSR_THUMB_ENABLE)
return *(unsigned short *)pc == 0xdf18;
else
return *(unsigned *)pc == 0xef180001;
}
return 0;
}
#endif
/* Set the currently-saved pc register value to PC. */
void set_pc (target_register_t pc)
{
put_register (PC, pc);
}
/*----------------------------------------------------------------------
* Single-step support
*/
/* Set things up so that the next user resume will execute one instruction.
This may be done by setting breakpoints or setting a single step flag
in the saved user registers, for example. */
static unsigned long ss_saved_pc = 0;
static unsigned long ss_saved_instr;
static unsigned short ss_saved_thumb_instr;
#define FIXME() {diag_printf("FIXME - %s\n", __FUNCTION__); }
static int
ins_will_execute(unsigned long ins)
{
unsigned long psr = get_register(PS); // condition codes
int res = 0;
switch ((ins & 0xF0000000) >> 28) {
case 0x0: // EQ
res = (psr & PS_Z) != 0;
break;
case 0x1: // NE
res = (psr & PS_Z) == 0;
break;
case 0x2: // CS
res = (psr & PS_C) != 0;
break;
case 0x3: // CC
res = (psr & PS_C) == 0;
break;
case 0x4: // MI
res = (psr & PS_N) != 0;
break;
case 0x5: // PL
res = (psr & PS_N) == 0;
break;
case 0x6: // VS
res = (psr & PS_V) != 0;
break;
case 0x7: // VC
res = (psr & PS_V) == 0;
break;
case 0x8: // HI
res = ((psr & PS_C) != 0) && ((psr & PS_Z) == 0);
break;
case 0x9: // LS
res = ((psr & PS_C) == 0) || ((psr & PS_Z) != 0);
break;
case 0xA: // GE
res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) ||
((psr & (PS_N|PS_V)) == 0);
break;
case 0xB: // LT
res = ((psr & (PS_N|PS_V)) == PS_N) ||
((psr & (PS_N|PS_V)) == PS_V);
break;
case 0xC: // GT
res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) ||
((psr & (PS_N|PS_V)) == 0);
res = ((psr & PS_Z) == 0) && res;
break;
case 0xD: // LE
res = ((psr & (PS_N|PS_V)) == PS_N) ||
((psr & (PS_N|PS_V)) == PS_V);
res = ((psr & PS_Z) == PS_Z) || res;
break;
case 0xE: // AL
res = TRUE;
break;
case 0xF: // NV
if (((ins & 0x0E000000) >> 24) == 0xA)
res = TRUE;
else
res = FALSE;
break;
}
return res;
}
static unsigned long
RmShifted(int shift)
{
unsigned long Rm = get_register(shift & 0x00F);
int shift_count;
if ((shift & 0x010) == 0) {
shift_count = (shift & 0xF80) >> 7;
} else {
shift_count = get_register((shift & 0xF00) >> 8);
}
switch ((shift & 0x060) >> 5) {
case 0x0: // Logical left
Rm <<= shift_count;
break;
case 0x1: // Logical right
Rm >>= shift_count;
break;
case 0x2: // Arithmetic right
Rm = (unsigned long)((long)Rm >> shift_count);
break;
case 0x3: // Rotate right
if (shift_count == 0) {
// Special case, RORx
Rm >>= 1;
if (get_register(PS) & PS_C) Rm |= 0x80000000;
} else {
Rm = (Rm >> shift_count) | (Rm << (32-shift_count));
}
break;
}
return Rm;
}
// Decide the next instruction to be executed for a given instruction
static unsigned long *
target_ins(unsigned long *pc, unsigned long ins)
{
unsigned long new_pc, offset, op2;
unsigned long Rn;
int i, reg_count, c;
switch ((ins & 0x0C000000) >> 26) {
case 0x0:
// BX or BLX
if ((ins & 0x0FFFFFD0) == 0x012FFF10) {
new_pc = (unsigned long)get_register(ins & 0x0000000F);
return ((unsigned long *)new_pc);
}
// Data processing
new_pc = (unsigned long)(pc+1);
if ((ins & 0x0000F000) == 0x0000F000) {
// Destination register is PC
if ((ins & 0x0FBF0000) != 0x010F0000) {
Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
if ((ins & 0x02000000) == 0) {
op2 = RmShifted(ins & 0x00000FFF);
} else {
op2 = ins & 0x000000FF;
i = (ins & 0x00000F00) >> 8; // Rotate count
op2 = (op2 >> (i*2)) | (op2 << (32-(i*2)));
}
switch ((ins & 0x01E00000) >> 21) {
case 0x0: // AND
new_pc = Rn & op2;
break;
case 0x1: // EOR
new_pc = Rn ^ op2;
break;
case 0x2: // SUB
new_pc = Rn - op2;
break;
case 0x3: // RSB
new_pc = op2 - Rn;
break;
case 0x4: // ADD
new_pc = Rn + op2;
break;
case 0x5: // ADC
c = (get_register(PS) & PS_C) != 0;
new_pc = Rn + op2 + c;
break;
case 0x6: // SBC
c = (get_register(PS) & PS_C) != 0;
new_pc = Rn - op2 + c - 1;
break;
case 0x7: // RSC
c = (get_register(PS) & PS_C) != 0;
new_pc = op2 - Rn +c - 1;
break;
case 0x8: // TST
case 0x9: // TEQ
case 0xA: // CMP
case 0xB: // CMN
break; // PC doesn't change
case 0xC: // ORR
new_pc = Rn | op2;
break;
case 0xD: // MOV
new_pc = op2;
break;
case 0xE: // BIC
new_pc = Rn & ~op2;
break;
case 0xF: // MVN
new_pc = ~op2;
break;
}
}
}
return ((unsigned long *)new_pc);
case 0x1:
if ((ins & 0x02000010) == 0x02000010) {
// Undefined!
return (pc+1);
} else {
if ((ins & 0x00100000) == 0) {
// STR
return (pc+1);
} else {
// LDR
if ((ins & 0x0000F000) != 0x0000F000) {
// Rd not PC
return (pc+1);
} else {
Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
if (ins & 0x01000000) {
// Add/subtract offset before
if ((ins & 0x02000000) == 0) {
// 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -