diff mbox

[4/4] lkd: Add an Architecture Layer for ARMv7 targets

Message ID 1456427706-30077-5-git-send-email-kieran.bingham@linaro.org
State New
Headers show

Commit Message

Kieran Bingham Feb. 25, 2016, 7:15 p.m. UTC
This layer provides the architecture specific code for thread awarenes
on an ARMv7 or compatible target

There is still more that could be stripped out, but it serves to highlight
the implementation detail
---
 gdb/Makefile.in   |   5 +-
 gdb/configure.tgt |   4 +
 gdb/lkd/lkd-arm.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 320 insertions(+), 1 deletion(-)
 create mode 100644 gdb/lkd/lkd-arm.c

-- 
2.5.0
diff mbox

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ff324c393f23..7b368d7b8a22 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -722,7 +722,8 @@  ALL_TARGET_OBS = \
 	windows-tdep.o \
 	linux-record.o \
 	ravenscar-thread.o \
-	lkd-main.o lkd-process.o
+	lkd-main.o lkd-process.o \
+	lkd-arm.o
 
 # Host-dependent makefile fragment comes in here.
 @host_makefile_frag@
@@ -2728,6 +2729,8 @@  py-varobj.o: $(srcdir)/python/py-varobj.c
 lkd-main.o: $(srcdir)/lkd/lkd-main.c
 lkd-process.o: $(srcdir)/lkd/lkd-process.c
 
+lkd-arm.o: $(srcdir)/lkd/lkd-arm.c
+
 # Generalised lkd-compile rule
 lkd-%:
 	$(COMPILE) $<
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 8a685afe5c1e..ea0d0d2a540c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -730,6 +730,10 @@  done
 # Add linux kernel awareness obs and set no default OS ABI
 if test x"$enable_linux_kernel_aware" = xyes; then
   gdb_target_obs="$gdb_target_obs lkd-main.o lkd-process.o"
+  case "${targ}" in
+  arm*-*-linux*)
+	  gdb_target_obs="$gdb_target_obs lkd-arm.o" ;;
+  esac
   gdb_osabi=
 fi
 
diff --git a/gdb/lkd/lkd-arm.c b/gdb/lkd/lkd-arm.c
new file mode 100644
index 000000000000..f734b3cb89df
--- /dev/null
+++ b/gdb/lkd/lkd-arm.c
@@ -0,0 +1,312 @@ 
+/*
+  Copyright 2011-2013 STMicroelectronics.
+
+  This file contains the ARM specific part of the Linux
+  Awareness layer for GDB.
+
+  The Linux Awareness layer allows GDB boot or attach-to and debug
+  a Linux kernel through a H/W link (presumably JTAG).
+
+  see file linux-awareness.c for more details.
+*/
+
+#include "defs.h"
+#include "block.h"
+#include "command.h"
+#include "frame.h"
+#include "frame-unwind.h"
+#include "gdb_assert.h"
+#include "gdbarch.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+#include "gdbtypes.h"
+#include "gdb_obstack.h"
+#include "inferior.h"
+#include "objfiles.h"
+#include "regcache.h"
+#include "user-regs.h"
+#include "symtab.h"
+#include "target.h"
+#include "top.h"
+#include "value.h"
+#include "gdbthread.h"
+
+#include "arm-tdep.h"
+
+#include "lkd.h"
+#include "lkd-process.h"
+
+/* Addresses used by the ARM specific linux awareness. */
+
+DECLARE_ADDR (ret_from_fork);
+DECLARE_ADDR (ret_fast_syscall);
+
+/* Fields used by the ARM specific linux awareness. */
+DECLARE_FIELD (mm_struct, pgd);
+DECLARE_FIELD (task_struct, mm);
+DECLARE_FIELD (task_struct, thread);
+DECLARE_FIELD (task_struct, stack);
+DECLARE_FIELD (thread_info, cpu_context);
+
+
+struct pt_regs
+{
+  uint32_t uregs[18];
+};
+
+static int
+arch_can_write (CORE_ADDR addr, CORE_ADDR task_struct)
+{
+  return 0;
+}
+
+static inline int
+is_special_addr (CORE_ADDR addr)
+{
+  /* ret_fast_syscall is a special address (both sp _and_ pc)
+     used to link user and kernel spaces */
+  return HAS_ADDR (ret_fast_syscall) && addr == ADDR (ret_fast_syscall);
+}
+
+static int
+arch_is_user_address (CORE_ADDR addr)
+{
+  return (addr < linux_awareness_ops->kernel_offset)
+    || is_special_addr (addr);
+}
+
+static int
+arch_is_kernel_address (CORE_ADDR addr)
+{
+  return !arch_is_user_address (addr) || is_special_addr (addr);
+}
+
+
+/*****************************************************************************/
+/*                              TASK AWARENESS                               */
+/*****************************************************************************/
+
+/* This function gets the register values that the schedule() routine
+ * has stored away to be able to restart an asleep task later.
+ * this must match the layout in arm/include/asm/thread_info.h
+ *
+ **/
+static CORE_ADDR
+fetch_context_register_real (CORE_ADDR task_struct)
+{
+  struct regcache *regcache;
+  int offset = 0, val, i;
+  uint32_t thread_info_addr;
+  uint32_t cpsr;
+  struct cpu_context_save
+  {
+    uint32_t r4;
+    uint32_t r5;
+    uint32_t r6;
+    uint32_t r7;
+    uint32_t r8;
+    uint32_t r9;
+    uint32_t sl;
+    uint32_t fp;
+    uint32_t sp;
+    uint32_t pc;
+  } cpu_cxt;
+
+  /*get thread_info address */
+  thread_info_addr = read_unsigned_field (task_struct, task_struct, stack);
+
+  /*get cpu_context as saved by scheduled */
+  read_memory ((CORE_ADDR) thread_info_addr +
+	       F_OFFSET (thread_info, cpu_context),
+	       (gdb_byte *) & cpu_cxt, sizeof (struct cpu_context_save));
+
+  DEBUG (TASK, 4, "fetch_context_register_real (%x)\n", thread_info_addr);
+
+  regcache = get_current_regcache ();
+
+  regcache_raw_supply (regcache, ARM_PC_REGNUM, &cpu_cxt.pc);
+  regcache_raw_supply (regcache, ARM_SP_REGNUM, &cpu_cxt.sp);
+  regcache_raw_supply (regcache, ARM_FP_REGNUM, &cpu_cxt.fp);
+
+  /*general purpose registers */
+  regcache_raw_supply (regcache, 10, &cpu_cxt.sl);
+  regcache_raw_supply (regcache, 9, &cpu_cxt.r9);
+  regcache_raw_supply (regcache, 8, &cpu_cxt.r8);
+  regcache_raw_supply (regcache, 7, &cpu_cxt.r7);
+  regcache_raw_supply (regcache, 6, &cpu_cxt.r6);
+  regcache_raw_supply (regcache, 5, &cpu_cxt.r5);
+  regcache_raw_supply (regcache, 4, &cpu_cxt.r4);
+
+  /* Fake a value for cpsr:T bit.  */
+#define IS_THUMB_ADDR(addr)	((addr) & 1)
+  cpsr = IS_THUMB_ADDR(cpu_cxt.pc) ? arm_psr_thumb_bit (target_gdbarch ()) : 0;
+  regcache_raw_supply (regcache, ARM_PS_REGNUM, &cpsr);
+
+  for (i = 0; i < gdbarch_num_regs (target_gdbarch ()); i++)
+    if (REG_VALID != regcache_register_status (regcache, i))
+      /* Mark other registers as unavailable.  */
+      regcache_invalidate (regcache, i);
+
+  return cpu_cxt.pc;
+}
+
+static int
+arch_fetch_context_register (int regno, CORE_ADDR task_struct)
+{
+  CORE_ADDR pc;
+  DEBUG (TASK, 4, "fetch_context_register(%i,%x)\n",
+	 regno, (unsigned int) task_struct);
+
+  if (REG_VALID != regcache_register_status (get_current_regcache (),
+					     ARM_PC_REGNUM))
+    {
+
+      pc = fetch_context_register_real (task_struct);
+
+      if (lkd_params.skip_schedule_frame
+	  && !(HAS_ADDR (ret_from_fork) && pc == ADDR (ret_from_fork)))
+	{
+	  int arm_num_regs = gdbarch_num_regs (target_gdbarch ());
+	  struct frame_info *f = get_current_frame ();
+	  int level = frame_relative_level(f);
+
+	   /* skip this frame, unwind to the next
+	    **/
+	  while (f && (level++ < lkd_params.skip_schedule_frame))
+		  f = get_prev_frame (f);
+
+	  if (f != NULL)
+	    {
+	      int i;
+	      gdb_byte buf[12];
+	      int optimized, unavailable;
+	      CORE_ADDR addr;
+	      int realnum;
+	      enum lval_type lval;
+	      unsigned long *new_registers = xcalloc (arm_num_regs,
+						      sizeof (unsigned long));
+	      char *new_registers_valid = xcalloc (arm_num_regs, 1);
+
+	      for (i = 0; i < arm_num_regs; ++i)
+		{
+		  frame_register_unwind (f, i, &optimized, &unavailable,
+					 &lval, &addr, &realnum, buf);
+		  if (!optimized && !unavailable)
+		    {
+		      memcpy (&new_registers[i], buf, 4);
+		      new_registers_valid[i] = 1;
+		    }
+		}
+
+	      reinit_frame_cache ();
+
+	      for (i = 0; i < arm_num_regs; ++i)
+		{
+		  if (new_registers_valid[i])
+		    regcache_raw_supply
+		      (get_current_regcache (), i, &new_registers[i]);
+		  else
+		    /* Mark other registers as unavailable. */
+		    regcache_invalidate (get_current_regcache (), i);
+		}
+
+	      xfree (new_registers);
+	      xfree (new_registers_valid);
+	    }
+	}
+    }
+
+  return 1;
+}
+
+// FIXME
+static int
+arch_store_context_register (int regno, CORE_ADDR task_struct)
+{
+  DEBUG (TASK, 3, "store_context_register(%i,%x)\n",
+	 regno, (unsigned int) task_struct);
+  warning ("ARM/store_context_register not implemented yet");
+  return 1;
+}
+
+static void
+arch_clear_cache (void)
+{
+}
+
+
+static void
+arch_close (void)
+{
+  arch_clear_cache ();
+}
+
+
+/*
+ * default op handler for STGDI (non-remote)
+ **/
+static void
+arch_post_load (char *prog, int fromtty)
+{
+  CORE_ADDR addr_phys_offset;
+
+  DEBUG (D_INIT, 1, "ARM: arch_post_load\n");
+
+
+  DEBUG (TARGET, 1, "Assuming 0x%08x as MODULES_VADDR.\n",
+	 linux_awareness_ops->kernel_offset);
+
+  /* this installs what ever LKD specific handling is required
+   * in the gdb arch data. */
+
+  /* lkd_params.loaded will be set later */
+}
+
+static int
+arch_init (void)
+{
+  DEBUG (D_INIT, 1, "ARM: arch_init.\n");
+
+  return 1;
+}
+
+static int
+arch_check_kernel (void)
+{
+  int res = HAS_FIELD (mm_struct, pgd)
+    && HAS_FIELD (task_struct, stack)
+    && HAS_FIELD (task_struct, thread)
+    && HAS_FIELD (task_struct, mm) && HAS_FIELD (thread_info, cpu_context);
+  return res;
+}
+
+//#define ARMv7_PAGESHIFT       12
+
+struct linux_awareness_ops arm_linux_awareness_ops = {
+  .name = "armv7",
+  .lo_check_kernel = arch_check_kernel,
+  .lo_init = arch_init,
+  .lo_close = arch_close,
+  .lo_post_load = arch_post_load,
+  .lo_can_write = arch_can_write,
+  .lo_is_user_address = arch_is_user_address,
+  .lo_is_kernel_address = arch_is_kernel_address,
+  .lo_single_step_destination = NULL,
+  .lo_clear_cache = arch_clear_cache,
+  .lo_fetch_context_register = arch_fetch_context_register,
+  .lo_store_context_register = arch_store_context_register,
+  .kernel_offset = 0x0,
+};
+
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_armv7_lkd;
+
+/* _initialize_xxx routines are added to the ugly autogenerates init code
+ * see gdb makefile.
+ **/
+void
+_initialize_armv7_lkd (void)
+{
+  linux_awareness_ops = &arm_linux_awareness_ops;
+}