📄 arm_stub.c
字号:
//========================================================================
//
// arm_stub.c
//
// Helper functions for stub, generic to all ARM processors
//
//========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 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): 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);
}
// Calculate byte offset a given register from start of register save area.
static int
reg_offset(regnames_t reg)
{
int base_offset;
if (reg < F0)
return reg * 4;
base_offset = 16 * 4;
if (reg < FPS)
return base_offset + ((reg - F0) * 12);
base_offset += (8 * 12);
if (reg <= PS)
return base_offset + ((reg - FPS) * 4);
return -1; // Should never happen!
}
// Return the currently-saved value corresponding to register REG of
// the exception context.
target_register_t
get_register (regnames_t reg)
{
target_register_t val;
int offset = reg_offset(reg);
if (REGSIZE(reg) > sizeof(target_register_t) || offset == -1)
return -1;
val = _registers[offset/sizeof(target_register_t)];
return val;
}
// Store VALUE in the register corresponding to WHICH in the exception
// context.
void
put_register (regnames_t which, target_register_t value)
{
int offset = reg_offset(which);
if (REGSIZE(which) > sizeof(target_register_t) || offset == -1)
return;
_registers[offset/sizeof(target_register_t)] = value;
}
// Write the contents of register WHICH into VALUE as raw bytes. This
// is only used for registers larger than sizeof(target_register_t).
// Return non-zero if it is a valid register.
int
get_register_as_bytes (regnames_t which, char *value)
{
int offset = reg_offset(which);
if (offset != -1) {
memcpy (value, (char *)_registers + offset, REGSIZE(which));
return 1;
}
return 0;
}
// Alter the contents of saved register WHICH to contain VALUE. This
// is only used for registers larger than sizeof(target_register_t).
// Return non-zero if it is a valid register.
int
put_register_as_bytes (regnames_t which, char *value)
{
int offset = reg_offset(which);
if (offset != -1) {
memcpy ((char *)_registers + offset, value, REGSIZE(which));
return 1;
}
return 0;
}
/*----------------------------------------------------------------------
* 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__); }
// return non-zero for v5 and later
static int
v5T_semantics(void)
{
unsigned id;
asm volatile ("mrc p15,0,%0,c0,c0,0\n"
: "=r" (id) : /* no inputs */);
return ((id >> 16) & 0xff) >= 5;
}
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 & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -