public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [commit] Add Xtensa Window Exception handlers to frame analysis
@ 2011-03-09  3:27 Maxim Grigoriev
  2011-03-09  4:18 ` Joel Brobecker
  0 siblings, 1 reply; 2+ messages in thread
From: Maxim Grigoriev @ 2011-03-09  3:27 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 73 bytes --]

Improve Xtensa frame analysis to understand Window Exception handlers.



[-- Attachment #2: WEH.diff --]
[-- Type: text/plain, Size: 17242 bytes --]

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.


Index: gdb/xtensa-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/xtensa-tdep.c,v
retrieving revision 1.48
diff -u -r1.48 xtensa-tdep.c
--- gdb/xtensa-tdep.c	9 Mar 2011 00:55:09 -0000	1.48
+++ gdb/xtensa-tdep.c	9 Mar 2011 01:56:34 -0000
@@ -161,6 +161,21 @@
   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
    have just returned.
 
@@ -210,6 +225,22 @@
 
 /* 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.  */
 static const char *
 xtensa_register_name (struct gdbarch *gdbarch, int regnum)
@@ -907,14 +938,13 @@
 {
   int wb;		/* WINDOWBASE of the previous frame.  */
   int callsize;		/* Call size of this frame.  */
-  int ws;		/* WINDOWSTART of the previous frame.  It
-			   keeps track of life windows only.  If there
-			   is no bit set for the window, that means it
-			   had been already spilled because of window
-			   overflow.  */
+  int ws;		/* WINDOWSTART of the previous frame.  It keeps track of
+			   life windows only.  If there is no bit set for the
+			   window,  that means it had been already spilled
+			   because of window overflow.  */
 
-  /* Spilled A-registers from the previous frame.
-     AREGS[i] == -1, if corresponding AR is alive.  */
+   /* Addresses of spilled A-registers.
+      AREGS[i] == -1, if corresponding AR is alive.  */
   CORE_ADDR aregs[XTENSA_NUM_SAVED_AREGS];
 } xtensa_windowed_frame_cache_t;
 
@@ -968,10 +998,10 @@
 typedef struct xtensa_frame_cache
 {
   CORE_ADDR base;	/* Stack pointer of this frame.  */
-  CORE_ADDR pc;		/* PC at the entry point to the function.  */
-  CORE_ADDR ra;		/* The raw return address (without CALLINC).  */
-  CORE_ADDR ps;		/* The PS register of this frame.  */
-  CORE_ADDR prev_sp;	/* Stack Pointer of the previous frame.  */
+  CORE_ADDR pc;		/* PC of this frame at the function entry point.  */
+  CORE_ADDR ra;		/* The raw return address of this frame.  */
+  CORE_ADDR ps;		/* The PS register of the previous (older) frame.  */
+  CORE_ADDR prev_sp;	/* Stack Pointer of the previous (older) frame.  */
   int call0;		/* It's a call0 framework (else windowed).  */
   union
     {
@@ -1064,6 +1094,38 @@
   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
    for the function containing CURRENT_PC.  */
 
@@ -1195,6 +1257,11 @@
 		   xtensa_frame_cache_t *cache,
 		   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 *
 xtensa_frame_cache (struct frame_info *this_frame, void **this_cache)
 {
@@ -1302,9 +1369,8 @@
 	}
 
       if ((cache->prev_sp == 0) && ( ra != 0 ))
-	/* If RA is equal to 0 this frame is an outermost frame.
-	   Leave cache->prev_sp unchanged marking the boundary of the
-	   frame stack.  */
+	/* If RA is equal to 0 this frame is an outermost frame.  Leave
+	   cache->prev_sp unchanged marking the boundary of the frame stack.  */
 	{
 	  if ((cache->wd.ws & (1 << cache->wd.wb)) == 0)
 	    {
@@ -1321,11 +1387,18 @@
 			     (gdbarch, gdbarch_tdep (gdbarch)->a0_base + 1,
 			      cache->wd.wb);
 
-	      cache->prev_sp = get_frame_register_unsigned (this_frame,
-							    regnum);
+	      cache->prev_sp = xtensa_read_register (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.  */
     {
       unsigned int litbase_regnum = gdbarch_tdep (gdbarch)->litbase_regnum;
@@ -1941,11 +2014,33 @@
   c0opc_mov,	       /* Moving a register to a register.  */
   c0opc_movi,	       /* Moving an immediate to a register.  */
   c0opc_l32r,	       /* Loading a literal.  */
-  c0opc_s32i,	       /* Storing word at fixed offset from a base
-			  register.  */
+  c0opc_s32i,	       /* Storing word at fixed offset from a base 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.  */
 } 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.  */
 
@@ -1970,6 +2065,10 @@
      opclass = c0opc_break;
   else if (strcasecmp (opcname, "entry") == 0)
     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
 	   || xtensa_opcode_is_jump   (isa, opc) > 0
 	   || xtensa_opcode_is_loop   (isa, opc) > 0
@@ -1999,6 +2098,12 @@
   else if (strcasecmp (opcname, "s32i") == 0 
 	   || strcasecmp (opcname, "s32i.n") == 0)
     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;
 }
@@ -2028,7 +2133,7 @@
       break;
     case c0opc_add:
       /* 3 operands: dst, src1, src2.  */
-      gdb_assert (nods == 3);
+      gdb_assert (nods == 3); 
       if      (src[odv[1]].fr_reg == C0_CONST)
         {
 	  dst[odv[0]].fr_reg = src[odv[2]].fr_reg;
@@ -2458,6 +2563,258 @@
   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.
 

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2011-03-09  3:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-09  3:27 [commit] Add Xtensa Window Exception handlers to frame analysis Maxim Grigoriev
2011-03-09  4:18 ` Joel Brobecker

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).