mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-24 04:00:07 +08:00
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
* xtensa-tdep.c (xtensa_read_register): New function. (xtensa_write_register): New function. (xtensa_find_register_by_name): New function. (xtensa_windowed_frame_cache): Update comments in type description. (xtensa_frame_cache): Likewise. (xtensa_window_interrupt_insn): New function. (xtensa_frame_cache): Add analysis for Xtensa Window Exception frames. (xtensa_insn_kind): Add new instructions. (rwx_special_register): New function. (call0_classify_opcode): Add new instructions to the analysis. (a0_saved, a7_saved, a11_saved): New variables. (a0_was_saved, a7_was_saved, a11_was_saved): New variables. (execute_l32e): New function. (execute_s32e): New function. (xtensa_exception_handler_t): New type. (execute_code): New function. (xtensa_window_interrupt_frame_cache): New function to conduct frame analysis for Xtensa Window Exception handlers.
This commit is contained in:
@ -1,3 +1,24 @@
|
|||||||
|
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
|
||||||
|
|
||||||
|
* xtensa-tdep.c (xtensa_read_register): New function.
|
||||||
|
(xtensa_write_register): New function.
|
||||||
|
(xtensa_find_register_by_name): New function.
|
||||||
|
(xtensa_windowed_frame_cache): Update comments in type description.
|
||||||
|
(xtensa_frame_cache): Likewise.
|
||||||
|
(xtensa_window_interrupt_insn): New function.
|
||||||
|
(xtensa_frame_cache): Add analysis for Xtensa Window Exception frames.
|
||||||
|
(xtensa_insn_kind): Add new instructions.
|
||||||
|
(rwx_special_register): New function.
|
||||||
|
(call0_classify_opcode): Add new instructions to the analysis.
|
||||||
|
(a0_saved, a7_saved, a11_saved): New variables.
|
||||||
|
(a0_was_saved, a7_was_saved, a11_was_saved): New variables.
|
||||||
|
(execute_l32e): New function.
|
||||||
|
(execute_s32e): New function.
|
||||||
|
(xtensa_exception_handler_t): New type.
|
||||||
|
(execute_code): New function.
|
||||||
|
(xtensa_window_interrupt_frame_cache): New function to conduct frame
|
||||||
|
analysis for Xtensa Window Exception handlers.
|
||||||
|
|
||||||
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
|
2011-03-08 Maxim Grigoriev <maxim2405@gmail.com>
|
||||||
|
|
||||||
* xtensa-tdep.c (TX_PS): New.
|
* xtensa-tdep.c (TX_PS): New.
|
||||||
|
@ -161,6 +161,21 @@ areg_number (struct gdbarch *gdbarch, int ar_regnum, unsigned int wb)
|
|||||||
return (areg > 15) ? -1 : areg;
|
return (areg > 15) ? -1 : areg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
xtensa_read_register (int regnum)
|
||||||
|
{
|
||||||
|
ULONGEST value;
|
||||||
|
|
||||||
|
regcache_raw_read_unsigned (get_current_regcache (), regnum, &value);
|
||||||
|
return (unsigned long) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
xtensa_write_register (int regnum, ULONGEST value)
|
||||||
|
{
|
||||||
|
regcache_raw_write_unsigned (get_current_regcache (), regnum, value);
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the window size of the previous call to the function from which we
|
/* Return the window size of the previous call to the function from which we
|
||||||
have just returned.
|
have just returned.
|
||||||
|
|
||||||
@ -210,6 +225,22 @@ extract_call_winsize (struct gdbarch *gdbarch, CORE_ADDR pc)
|
|||||||
|
|
||||||
/* REGISTER INFORMATION */
|
/* REGISTER INFORMATION */
|
||||||
|
|
||||||
|
/* Find register by name. */
|
||||||
|
static int
|
||||||
|
xtensa_find_register_by_name (struct gdbarch *gdbarch, char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < gdbarch_num_regs (gdbarch)
|
||||||
|
+ gdbarch_num_pseudo_regs (gdbarch);
|
||||||
|
i++)
|
||||||
|
|
||||||
|
if (strcasecmp (gdbarch_tdep (gdbarch)->regmap[i].name, name) == 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the name of a register. */
|
/* Returns the name of a register. */
|
||||||
static const char *
|
static const char *
|
||||||
xtensa_register_name (struct gdbarch *gdbarch, int regnum)
|
xtensa_register_name (struct gdbarch *gdbarch, int regnum)
|
||||||
@ -907,14 +938,13 @@ typedef struct xtensa_windowed_frame_cache
|
|||||||
{
|
{
|
||||||
int wb; /* WINDOWBASE of the previous frame. */
|
int wb; /* WINDOWBASE of the previous frame. */
|
||||||
int callsize; /* Call size of this frame. */
|
int callsize; /* Call size of this frame. */
|
||||||
int ws; /* WINDOWSTART of the previous frame. It
|
int ws; /* WINDOWSTART of the previous frame. It keeps track of
|
||||||
keeps track of life windows only. If there
|
life windows only. If there is no bit set for the
|
||||||
is no bit set for the window, that means it
|
window, that means it had been already spilled
|
||||||
had been already spilled because of window
|
because of window overflow. */
|
||||||
overflow. */
|
|
||||||
|
|
||||||
/* Spilled A-registers from the previous frame.
|
/* Addresses of spilled A-registers.
|
||||||
AREGS[i] == -1, if corresponding AR is alive. */
|
AREGS[i] == -1, if corresponding AR is alive. */
|
||||||
CORE_ADDR aregs[XTENSA_NUM_SAVED_AREGS];
|
CORE_ADDR aregs[XTENSA_NUM_SAVED_AREGS];
|
||||||
} xtensa_windowed_frame_cache_t;
|
} xtensa_windowed_frame_cache_t;
|
||||||
|
|
||||||
@ -968,10 +998,10 @@ typedef struct xtensa_call0_frame_cache
|
|||||||
typedef struct xtensa_frame_cache
|
typedef struct xtensa_frame_cache
|
||||||
{
|
{
|
||||||
CORE_ADDR base; /* Stack pointer of this frame. */
|
CORE_ADDR base; /* Stack pointer of this frame. */
|
||||||
CORE_ADDR pc; /* PC at the entry point to the function. */
|
CORE_ADDR pc; /* PC of this frame at the function entry point. */
|
||||||
CORE_ADDR ra; /* The raw return address (without CALLINC). */
|
CORE_ADDR ra; /* The raw return address of this frame. */
|
||||||
CORE_ADDR ps; /* The PS register of this frame. */
|
CORE_ADDR ps; /* The PS register of the previous (older) frame. */
|
||||||
CORE_ADDR prev_sp; /* Stack Pointer of the previous frame. */
|
CORE_ADDR prev_sp; /* Stack Pointer of the previous (older) frame. */
|
||||||
int call0; /* It's a call0 framework (else windowed). */
|
int call0; /* It's a call0 framework (else windowed). */
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
@ -1064,6 +1094,38 @@ xtensa_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
|
|||||||
return frame_id_build (fp + SP_ALIGNMENT, pc);
|
return frame_id_build (fp + SP_ALIGNMENT, pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns true, if instruction to execute next is unique to Xtensa Window
|
||||||
|
Interrupt Handlers. It can only be one of L32E, S32E, RFWO, or RFWU. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
xtensa_window_interrupt_insn (struct gdbarch *gdbarch, CORE_ADDR pc)
|
||||||
|
{
|
||||||
|
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||||
|
unsigned int insn = read_memory_integer (pc, 4, byte_order);
|
||||||
|
unsigned int code;
|
||||||
|
|
||||||
|
if (byte_order == BFD_ENDIAN_BIG)
|
||||||
|
{
|
||||||
|
/* Check, if this is L32E or S32E. */
|
||||||
|
code = insn & 0xf000ff00;
|
||||||
|
if ((code == 0x00009000) || (code == 0x00009400))
|
||||||
|
return 1;
|
||||||
|
/* Check, if this is RFWU or RFWO. */
|
||||||
|
code = insn & 0xffffff00;
|
||||||
|
return ((code == 0x00430000) || (code == 0x00530000));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Check, if this is L32E or S32E. */
|
||||||
|
code = insn & 0x00ff000f;
|
||||||
|
if ((code == 0x090000) || (code == 0x490000))
|
||||||
|
return 1;
|
||||||
|
/* Check, if this is RFWU or RFWO. */
|
||||||
|
code = insn & 0x00ffffff;
|
||||||
|
return ((code == 0x00003400) || (code == 0x00003500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the best guess about which register is a frame pointer
|
/* Returns the best guess about which register is a frame pointer
|
||||||
for the function containing CURRENT_PC. */
|
for the function containing CURRENT_PC. */
|
||||||
|
|
||||||
@ -1195,6 +1257,11 @@ call0_frame_cache (struct frame_info *this_frame,
|
|||||||
xtensa_frame_cache_t *cache,
|
xtensa_frame_cache_t *cache,
|
||||||
CORE_ADDR pc, CORE_ADDR litbase);
|
CORE_ADDR pc, CORE_ADDR litbase);
|
||||||
|
|
||||||
|
static void
|
||||||
|
xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
|
||||||
|
xtensa_frame_cache_t *cache,
|
||||||
|
CORE_ADDR pc);
|
||||||
|
|
||||||
static struct xtensa_frame_cache *
|
static struct xtensa_frame_cache *
|
||||||
xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
|
xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
|
||||||
{
|
{
|
||||||
@ -1302,9 +1369,8 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((cache->prev_sp == 0) && ( ra != 0 ))
|
if ((cache->prev_sp == 0) && ( ra != 0 ))
|
||||||
/* If RA is equal to 0 this frame is an outermost frame.
|
/* If RA is equal to 0 this frame is an outermost frame. Leave
|
||||||
Leave cache->prev_sp unchanged marking the boundary of the
|
cache->prev_sp unchanged marking the boundary of the frame stack. */
|
||||||
frame stack. */
|
|
||||||
{
|
{
|
||||||
if ((cache->wd.ws & (1 << cache->wd.wb)) == 0)
|
if ((cache->wd.ws & (1 << cache->wd.wb)) == 0)
|
||||||
{
|
{
|
||||||
@ -1321,11 +1387,18 @@ xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
|
|||||||
(gdbarch, gdbarch_tdep (gdbarch)->a0_base + 1,
|
(gdbarch, gdbarch_tdep (gdbarch)->a0_base + 1,
|
||||||
cache->wd.wb);
|
cache->wd.wb);
|
||||||
|
|
||||||
cache->prev_sp = get_frame_register_unsigned (this_frame,
|
cache->prev_sp = xtensa_read_register (regnum);
|
||||||
regnum);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (xtensa_window_interrupt_insn (gdbarch, pc))
|
||||||
|
{
|
||||||
|
/* Execution stopped inside Xtensa Window Interrupt Handler. */
|
||||||
|
|
||||||
|
xtensa_window_interrupt_frame_cache (this_frame, cache, pc);
|
||||||
|
/* Everything was set already, including cache->base. */
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
else /* Call0 framework. */
|
else /* Call0 framework. */
|
||||||
{
|
{
|
||||||
unsigned int litbase_regnum = gdbarch_tdep (gdbarch)->litbase_regnum;
|
unsigned int litbase_regnum = gdbarch_tdep (gdbarch)->litbase_regnum;
|
||||||
@ -1941,11 +2014,33 @@ typedef enum {
|
|||||||
c0opc_mov, /* Moving a register to a register. */
|
c0opc_mov, /* Moving a register to a register. */
|
||||||
c0opc_movi, /* Moving an immediate to a register. */
|
c0opc_movi, /* Moving an immediate to a register. */
|
||||||
c0opc_l32r, /* Loading a literal. */
|
c0opc_l32r, /* Loading a literal. */
|
||||||
c0opc_s32i, /* Storing word at fixed offset from a base
|
c0opc_s32i, /* Storing word at fixed offset from a base register. */
|
||||||
register. */
|
c0opc_rwxsr, /* RSR, WRS, or XSR instructions. */
|
||||||
|
c0opc_l32e, /* L32E instruction. */
|
||||||
|
c0opc_s32e, /* S32E instruction. */
|
||||||
|
c0opc_rfwo, /* RFWO instruction. */
|
||||||
|
c0opc_rfwu, /* RFWU instruction. */
|
||||||
c0opc_NrOf /* Number of opcode classifications. */
|
c0opc_NrOf /* Number of opcode classifications. */
|
||||||
} xtensa_insn_kind;
|
} xtensa_insn_kind;
|
||||||
|
|
||||||
|
/* Return true, if OPCNAME is RSR, WRS, or XSR instruction. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
rwx_special_register (const char *opcname)
|
||||||
|
{
|
||||||
|
char ch = *opcname++;
|
||||||
|
|
||||||
|
if ((ch != 'r') && (ch != 'w') && (ch != 'x'))
|
||||||
|
return 0;
|
||||||
|
if (*opcname++ != 's')
|
||||||
|
return 0;
|
||||||
|
if (*opcname++ != 'r')
|
||||||
|
return 0;
|
||||||
|
if (*opcname++ != '.')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Classify an opcode based on what it means for Call0 prologue analysis. */
|
/* Classify an opcode based on what it means for Call0 prologue analysis. */
|
||||||
|
|
||||||
@ -1970,6 +2065,10 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
|
|||||||
opclass = c0opc_break;
|
opclass = c0opc_break;
|
||||||
else if (strcasecmp (opcname, "entry") == 0)
|
else if (strcasecmp (opcname, "entry") == 0)
|
||||||
opclass = c0opc_entry;
|
opclass = c0opc_entry;
|
||||||
|
else if (strcasecmp (opcname, "rfwo") == 0)
|
||||||
|
opclass = c0opc_rfwo;
|
||||||
|
else if (strcasecmp (opcname, "rfwu") == 0)
|
||||||
|
opclass = c0opc_rfwu;
|
||||||
else if (xtensa_opcode_is_branch (isa, opc) > 0
|
else if (xtensa_opcode_is_branch (isa, opc) > 0
|
||||||
|| xtensa_opcode_is_jump (isa, opc) > 0
|
|| xtensa_opcode_is_jump (isa, opc) > 0
|
||||||
|| xtensa_opcode_is_loop (isa, opc) > 0
|
|| xtensa_opcode_is_loop (isa, opc) > 0
|
||||||
@ -1999,6 +2098,12 @@ call0_classify_opcode (xtensa_isa isa, xtensa_opcode opc)
|
|||||||
else if (strcasecmp (opcname, "s32i") == 0
|
else if (strcasecmp (opcname, "s32i") == 0
|
||||||
|| strcasecmp (opcname, "s32i.n") == 0)
|
|| strcasecmp (opcname, "s32i.n") == 0)
|
||||||
opclass = c0opc_s32i;
|
opclass = c0opc_s32i;
|
||||||
|
else if (strcasecmp (opcname, "l32e") == 0)
|
||||||
|
opclass = c0opc_l32e;
|
||||||
|
else if (strcasecmp (opcname, "s32e") == 0)
|
||||||
|
opclass = c0opc_s32e;
|
||||||
|
else if (rwx_special_register (opcname))
|
||||||
|
opclass = c0opc_rwxsr;
|
||||||
|
|
||||||
return opclass;
|
return opclass;
|
||||||
}
|
}
|
||||||
@ -2458,6 +2563,258 @@ analysis failed in this frame. GDB command execution stopped."));
|
|||||||
cache->c0.c0_fp = fp;
|
cache->c0.c0_fp = fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CORE_ADDR a0_saved;
|
||||||
|
static CORE_ADDR a7_saved;
|
||||||
|
static CORE_ADDR a11_saved;
|
||||||
|
static int a0_was_saved;
|
||||||
|
static int a7_was_saved;
|
||||||
|
static int a11_was_saved;
|
||||||
|
|
||||||
|
/* Simulate L32E insn: AT <-- ref (AS + offset). */
|
||||||
|
static void
|
||||||
|
execute_l32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
|
||||||
|
{
|
||||||
|
int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
|
||||||
|
int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
|
||||||
|
CORE_ADDR addr = xtensa_read_register (asreg) + offset;
|
||||||
|
unsigned int spilled_value
|
||||||
|
= read_memory_unsigned_integer (addr, 4, gdbarch_byte_order (gdbarch));
|
||||||
|
|
||||||
|
if ((at == 0) && !a0_was_saved)
|
||||||
|
{
|
||||||
|
a0_saved = xtensa_read_register (atreg);
|
||||||
|
a0_was_saved = 1;
|
||||||
|
}
|
||||||
|
else if ((at == 7) && !a7_was_saved)
|
||||||
|
{
|
||||||
|
a7_saved = xtensa_read_register (atreg);
|
||||||
|
a7_was_saved = 1;
|
||||||
|
}
|
||||||
|
else if ((at == 11) && !a11_was_saved)
|
||||||
|
{
|
||||||
|
a11_saved = xtensa_read_register (atreg);
|
||||||
|
a11_was_saved = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xtensa_write_register (atreg, spilled_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simulate S32E insn: AT --> ref (AS + offset). */
|
||||||
|
static void
|
||||||
|
execute_s32e (struct gdbarch *gdbarch, int at, int as, int offset, CORE_ADDR wb)
|
||||||
|
{
|
||||||
|
int atreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + at, wb);
|
||||||
|
int asreg = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base + as, wb);
|
||||||
|
CORE_ADDR addr = xtensa_read_register (asreg) + offset;
|
||||||
|
ULONGEST spilled_value = xtensa_read_register (atreg);
|
||||||
|
|
||||||
|
write_memory_unsigned_integer (addr, 4,
|
||||||
|
gdbarch_byte_order (gdbarch),
|
||||||
|
spilled_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN 200
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
xtWindowOverflow,
|
||||||
|
xtWindowUnderflow,
|
||||||
|
xtNoExceptionHandler
|
||||||
|
} xtensa_exception_handler_t;
|
||||||
|
|
||||||
|
/* Execute insn stream from current PC until hitting RFWU or RFWO.
|
||||||
|
Return type of Xtensa Window Interrupt Handler on success. */
|
||||||
|
static xtensa_exception_handler_t
|
||||||
|
execute_code (struct gdbarch *gdbarch, CORE_ADDR current_pc, CORE_ADDR wb)
|
||||||
|
{
|
||||||
|
xtensa_isa isa;
|
||||||
|
xtensa_insnbuf ins, slot;
|
||||||
|
char ibuf[XTENSA_ISA_BSZ];
|
||||||
|
CORE_ADDR ia, bt, ba;
|
||||||
|
xtensa_format ifmt;
|
||||||
|
int ilen, islots, is;
|
||||||
|
xtensa_opcode opc;
|
||||||
|
int insn_num = 0;
|
||||||
|
int fail = 0;
|
||||||
|
void (*func) (struct gdbarch *, int, int, int, CORE_ADDR);
|
||||||
|
|
||||||
|
int at, as, offset;
|
||||||
|
int num_operands;
|
||||||
|
|
||||||
|
/* WindowUnderflow12 = true, when inside _WindowUnderflow12. */
|
||||||
|
int WindowUnderflow12 = (current_pc & 0x1ff) >= 0x140;
|
||||||
|
|
||||||
|
isa = xtensa_default_isa;
|
||||||
|
gdb_assert (XTENSA_ISA_BSZ >= xtensa_isa_maxlength (isa));
|
||||||
|
ins = xtensa_insnbuf_alloc (isa);
|
||||||
|
slot = xtensa_insnbuf_alloc (isa);
|
||||||
|
ba = 0;
|
||||||
|
ia = current_pc;
|
||||||
|
bt = ia;
|
||||||
|
|
||||||
|
a0_was_saved = 0;
|
||||||
|
a7_was_saved = 0;
|
||||||
|
a11_was_saved = 0;
|
||||||
|
|
||||||
|
while (insn_num++ < XTENSA_MAX_WINDOW_INTERRUPT_HANDLER_LEN)
|
||||||
|
{
|
||||||
|
if (ia + xtensa_isa_maxlength (isa) > bt)
|
||||||
|
{
|
||||||
|
ba = ia;
|
||||||
|
bt = (ba + XTENSA_ISA_BSZ);
|
||||||
|
if (target_read_memory (ba, ibuf, bt - ba) != 0)
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
}
|
||||||
|
xtensa_insnbuf_from_chars (isa, ins, &ibuf[ia-ba], 0);
|
||||||
|
ifmt = xtensa_format_decode (isa, ins);
|
||||||
|
if (ifmt == XTENSA_UNDEFINED)
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
ilen = xtensa_format_length (isa, ifmt);
|
||||||
|
if (ilen == XTENSA_UNDEFINED)
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
islots = xtensa_format_num_slots (isa, ifmt);
|
||||||
|
if (islots == XTENSA_UNDEFINED)
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
for (is = 0; is < islots; ++is)
|
||||||
|
{
|
||||||
|
if (xtensa_format_get_slot (isa, ifmt, is, ins, slot))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
opc = xtensa_opcode_decode (isa, ifmt, is, slot);
|
||||||
|
if (opc == XTENSA_UNDEFINED)
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
switch (call0_classify_opcode (isa, opc))
|
||||||
|
{
|
||||||
|
case c0opc_illegal:
|
||||||
|
case c0opc_flow:
|
||||||
|
case c0opc_entry:
|
||||||
|
case c0opc_break:
|
||||||
|
/* We expect none of them here. */
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
case c0opc_l32e:
|
||||||
|
func = execute_l32e;
|
||||||
|
break;
|
||||||
|
case c0opc_s32e:
|
||||||
|
func = execute_s32e;
|
||||||
|
break;
|
||||||
|
case c0opc_rfwo: /* RFWO. */
|
||||||
|
/* Here, we return from WindowOverflow handler and,
|
||||||
|
if we stopped at the very beginning, which means
|
||||||
|
A0 was saved, we have to restore it now. */
|
||||||
|
if (a0_was_saved)
|
||||||
|
{
|
||||||
|
int arreg = arreg_number (gdbarch,
|
||||||
|
gdbarch_tdep (gdbarch)->a0_base,
|
||||||
|
wb);
|
||||||
|
xtensa_write_register (arreg, a0_saved);
|
||||||
|
}
|
||||||
|
return xtWindowOverflow;
|
||||||
|
case c0opc_rfwu: /* RFWU. */
|
||||||
|
/* Here, we return from WindowUnderflow handler.
|
||||||
|
Let's see if either A7 or A11 has to be restored. */
|
||||||
|
if (WindowUnderflow12)
|
||||||
|
{
|
||||||
|
if (a11_was_saved)
|
||||||
|
{
|
||||||
|
int arreg = arreg_number (gdbarch,
|
||||||
|
gdbarch_tdep (gdbarch)->a0_base + 11,
|
||||||
|
wb);
|
||||||
|
xtensa_write_register (arreg, a11_saved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a7_was_saved)
|
||||||
|
{
|
||||||
|
int arreg = arreg_number (gdbarch,
|
||||||
|
gdbarch_tdep (gdbarch)->a0_base + 7,
|
||||||
|
wb);
|
||||||
|
xtensa_write_register (arreg, a7_saved);
|
||||||
|
}
|
||||||
|
return xtWindowUnderflow;
|
||||||
|
default: /* Simply skip this insns. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decode arguments for L32E / S32E and simulate their execution. */
|
||||||
|
if ( xtensa_opcode_num_operands (isa, opc) != 3 )
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_get_field (isa, opc, 0, ifmt, is, slot, &at))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_decode (isa, opc, 0, &at))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_get_field (isa, opc, 1, ifmt, is, slot, &as))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_decode (isa, opc, 1, &as))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_get_field (isa, opc, 2, ifmt, is, slot, &offset))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
if (xtensa_operand_decode (isa, opc, 2, &offset))
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
|
||||||
|
(*func) (gdbarch, at, as, offset, wb);
|
||||||
|
}
|
||||||
|
|
||||||
|
ia += ilen;
|
||||||
|
}
|
||||||
|
return xtNoExceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle Window Overflow / Underflow exception frames. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
xtensa_window_interrupt_frame_cache (struct frame_info *this_frame,
|
||||||
|
xtensa_frame_cache_t *cache,
|
||||||
|
CORE_ADDR pc)
|
||||||
|
{
|
||||||
|
struct gdbarch *gdbarch = get_frame_arch (this_frame);
|
||||||
|
CORE_ADDR ps, wb, ws, ra;
|
||||||
|
int epc1_regnum, i, regnum;
|
||||||
|
xtensa_exception_handler_t eh_type;
|
||||||
|
|
||||||
|
/* Read PS, WB, and WS from the hardware. Note that PS register
|
||||||
|
must be present, if Windowed ABI is supported. */
|
||||||
|
ps = xtensa_read_register (gdbarch_ps_regnum (gdbarch));
|
||||||
|
wb = xtensa_read_register (gdbarch_tdep (gdbarch)->wb_regnum);
|
||||||
|
ws = xtensa_read_register (gdbarch_tdep (gdbarch)->ws_regnum);
|
||||||
|
|
||||||
|
/* Execute all the remaining instructions from Window Interrupt Handler
|
||||||
|
by simulating them on the remote protocol level. On return, set the
|
||||||
|
type of Xtensa Window Interrupt Handler, or report an error. */
|
||||||
|
eh_type = execute_code (gdbarch, pc, wb);
|
||||||
|
if (eh_type == xtNoExceptionHandler)
|
||||||
|
error (_("\
|
||||||
|
Unable to decode Xtensa Window Interrupt Handler's code."));
|
||||||
|
|
||||||
|
cache->ps = ps ^ PS_EXC; /* Clear the exception bit in PS. */
|
||||||
|
cache->call0 = 0; /* It's Windowed ABI. */
|
||||||
|
|
||||||
|
/* All registers for the cached frame will be alive. */
|
||||||
|
for (i = 0; i < XTENSA_NUM_SAVED_AREGS; i++)
|
||||||
|
cache->wd.aregs[i] = -1;
|
||||||
|
|
||||||
|
if (eh_type == xtWindowOverflow)
|
||||||
|
cache->wd.ws = ws ^ (1 << wb);
|
||||||
|
else /* eh_type == xtWindowUnderflow. */
|
||||||
|
cache->wd.ws = ws | (1 << wb);
|
||||||
|
|
||||||
|
cache->wd.wb = (ps & 0xf00) >> 8; /* Set WB to OWB. */
|
||||||
|
regnum = arreg_number (gdbarch, gdbarch_tdep (gdbarch)->a0_base,
|
||||||
|
cache->wd.wb);
|
||||||
|
ra = xtensa_read_register (regnum);
|
||||||
|
cache->wd.callsize = WINSIZE (ra);
|
||||||
|
cache->prev_sp = xtensa_read_register (regnum + 1);
|
||||||
|
/* Set regnum to a frame pointer of the frame being cached. */
|
||||||
|
regnum = xtensa_scan_prologue (gdbarch, pc);
|
||||||
|
regnum = arreg_number (gdbarch,
|
||||||
|
gdbarch_tdep (gdbarch)->a0_base + regnum,
|
||||||
|
cache->wd.wb);
|
||||||
|
cache->base = get_frame_register_unsigned (this_frame, regnum);
|
||||||
|
|
||||||
|
/* Read PC of interrupted function from EPC1 register. */
|
||||||
|
epc1_regnum = xtensa_find_register_by_name (gdbarch,"epc1");
|
||||||
|
if (epc1_regnum < 0)
|
||||||
|
error(_("Unable to read Xtensa register EPC1"));
|
||||||
|
cache->ra = xtensa_read_register (epc1_regnum);
|
||||||
|
cache->pc = get_frame_func (this_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Skip function prologue.
|
/* Skip function prologue.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user