+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 instruction: 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 instruction: 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 instruction 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;
+ gdb_byte ibuf[XTENSA_ISA_BSZ];
+ CORE_ADDR ia, bt, ba;
+ xtensa_format ifmt;
+ int ilen, islots, is;
+ xtensa_opcode opc;
+ int insn_num = 0;
+ void (*func) (struct gdbarch *, int, int, int, CORE_ADDR);
+
+ uint32_t at, as, offset;
+
+ /* 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);
+}
+