From 699248a2fc4b9334f5042e1657116ac6b67b7321 Mon Sep 17 00:00:00 2001
From: Mahesh Bodapati <mbodapat@xilinx.com>
Date: Mon, 23 Jan 2017 19:07:44 +0530
Subject: [PATCH 01/10] Add initial port of linux gdbserver add
 gdb_proc_service_h to gdbserver microblaze-linux

gdbserver needs to initialise the microblaze registers

other archs use this step to run a *_arch_setup() to carry out all
architecture specific setup - may need to add in future

 * add linux-ptrace.o to gdbserver configure
 * Update breakpoint opcode
 * fix segfault on connecting gdbserver
 * add microblaze_linux_memory_remove_breakpoint
 * add set_solib_svr4_fetch_link_map_offsets
 * add set_gdbarch_fetch_tls_load_module_address
 * Force reading of r0 as 0, prevent stores

Signed-off-by: David Holsgrove <david.holsgrove@petalogix.com>
Signed-off-by: Nathan Rossi <nathan.rossi@petalogix.com>

Conflicts:
	gdbserver/Makefile.in
---
 gdb/configure.host                   |   3 +
 gdb/features/microblaze-linux.xml    |  12 ++
 gdb/gdbserver/linux-microblaze-low.c | 189 +++++++++++++++++++++++++++
 gdb/microblaze-linux-tdep.c          |  29 +++-
 gdb/microblaze-tdep.c                |  35 ++++-
 gdb/microblaze-tdep.h                |   4 +-
 gdb/regformats/reg-microblaze.dat    |  41 ++++++
 gdbserver/Makefile.in                |   6 +-
 gdbserver/configure.srv              |   8 ++
 9 files changed, 323 insertions(+), 4 deletions(-)
 create mode 100644 gdb/features/microblaze-linux.xml
 create mode 100644 gdb/gdbserver/linux-microblaze-low.c
 create mode 100644 gdb/regformats/reg-microblaze.dat

diff --git a/gdb/configure.host b/gdb/configure.host
index ce528237291..cf1a08e8b28 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -65,6 +65,7 @@ hppa*)			gdb_host_cpu=pa ;;
 i[34567]86*)		gdb_host_cpu=i386 ;;
 m68*)			gdb_host_cpu=m68k ;;
 mips*)			gdb_host_cpu=mips ;;
+microblaze*)		gdb_host_cpu=microblaze ;;
 powerpc* | rs6000)	gdb_host_cpu=powerpc ;;
 sparcv9 | sparc64)	gdb_host_cpu=sparc ;;
 s390*)			gdb_host_cpu=s390 ;;
@@ -133,6 +134,8 @@ mips*-*-netbsd* | mips*-*-knetbsd*-gnu)
 mips*-*-freebsd*)	gdb_host=fbsd ;;
 mips64*-*-openbsd*)	gdb_host=obsd64 ;;
 
+microblaze*-*linux*)	gdb_host=linux ;;
+
 powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*)
 			gdb_host=aix ;;
 powerpc*-*-freebsd*)	gdb_host=fbsd ;;
diff --git a/gdb/features/microblaze-linux.xml b/gdb/features/microblaze-linux.xml
new file mode 100644
index 00000000000..8983e66eb3d
--- /dev/null
+++ b/gdb/features/microblaze-linux.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2014-2018 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <osabi>GNU/Linux</osabi>
+  <xi:include href="microblaze-core.xml"/>
+</target>
diff --git a/gdb/gdbserver/linux-microblaze-low.c b/gdb/gdbserver/linux-microblaze-low.c
new file mode 100644
index 00000000000..cba5d6fc585
--- /dev/null
+++ b/gdb/gdbserver/linux-microblaze-low.c
@@ -0,0 +1,189 @@
+/* GNU/Linux/Microblaze specific low level interface, for the remote server for
+   GDB.
+   Copyright (C) 1995-2013 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program 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 3 of the License, or
+   (at your option) any later version.
+
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include <asm/ptrace.h>
+#include <sys/procfs.h>
+#include <sys/ptrace.h>
+
+#include "gdb_proc_service.h"
+
+static int microblaze_regmap[] =
+ {PT_GPR(0),     PT_GPR(1),     PT_GPR(2),     PT_GPR(3),
+  PT_GPR(4),     PT_GPR(5),     PT_GPR(6),     PT_GPR(7),
+  PT_GPR(8),     PT_GPR(9),     PT_GPR(10),    PT_GPR(11),
+  PT_GPR(12),    PT_GPR(13),    PT_GPR(14),    PT_GPR(15),
+  PT_GPR(16),    PT_GPR(17),    PT_GPR(18),    PT_GPR(19),
+  PT_GPR(20),    PT_GPR(21),    PT_GPR(22),    PT_GPR(23),
+  PT_GPR(24),    PT_GPR(25),    PT_GPR(26),    PT_GPR(27),
+  PT_GPR(28),    PT_GPR(29),    PT_GPR(30),    PT_GPR(31),
+  PT_PC,         PT_MSR,        PT_EAR,        PT_ESR,
+  PT_FSR
+  };
+
+#define microblaze_num_regs (sizeof microblaze_regmap / sizeof microblaze_regmap[0])
+
+/* Defined in auto-generated file microblaze-linux.c.  */
+void init_registers_microblaze (void);
+
+static int
+microblaze_cannot_store_register (int regno)
+{
+  if (microblaze_regmap[regno] == -1 || regno == 0)
+    return 1;
+
+  return 0;
+}
+
+static int
+microblaze_cannot_fetch_register (int regno)
+{
+  return 0;
+}
+
+static CORE_ADDR
+microblaze_get_pc (struct regcache *regcache)
+{
+  unsigned long pc;
+
+  collect_register_by_name (regcache, "pc", &pc);
+  return (CORE_ADDR) pc;
+}
+
+static void
+microblaze_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  unsigned long newpc = pc;
+
+  supply_register_by_name (regcache, "pc", &newpc);
+}
+
+/* dbtrap insn */
+/* brki r16, 0x18; */
+static const unsigned long microblaze_breakpoint = 0xba0c0018;
+#define microblaze_breakpoint_len 4
+
+static int
+microblaze_breakpoint_at (CORE_ADDR where)
+{
+  unsigned long insn;
+
+  (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
+  if (insn == microblaze_breakpoint)
+    return 1;
+  /* If necessary, recognize more trap instructions here.  GDB only uses the
+     one.  */
+  return 0;
+}
+
+static CORE_ADDR
+microblaze_reinsert_addr (struct regcache *regcache)
+{
+  unsigned long pc;
+  collect_register_by_name (regcache, "r15", &pc);
+  return pc;
+}
+
+#ifdef HAVE_PTRACE_GETREGS
+
+static void
+microblaze_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
+{
+  int size = register_size (regno);
+
+  memset (buf, 0, sizeof (long));
+
+  if (size < sizeof (long))
+    collect_register (regcache, regno, buf + sizeof (long) - size);
+  else
+    collect_register (regcache, regno, buf);
+}
+
+static void
+microblaze_supply_ptrace_register (struct regcache *regcache,
+			    int regno, const char *buf)
+{
+  int size = register_size (regno);
+
+  if (regno == 0) {
+    unsigned long regbuf_0 = 0;
+    /* clobbering r0 so that it is always 0 as enforced by hardware */
+    supply_register (regcache, regno, (const char*)&regbuf_0);
+  } else {
+      if (size < sizeof (long))
+        supply_register (regcache, regno, buf + sizeof (long) - size);
+      else
+        supply_register (regcache, regno, buf);
+  }
+}
+
+/* Provide only a fill function for the general register set.  ps_lgetregs
+   will use this for NPTL support.  */
+
+static void microblaze_fill_gregset (struct regcache *regcache, void *buf)
+{
+  int i;
+
+  for (i = 0; i < 32; i++)
+    microblaze_collect_ptrace_register (regcache, i, (char *) buf + microblaze_regmap[i]);
+}
+
+static void
+microblaze_store_gregset (struct regcache *regcache, const void *buf)
+{
+  int i;
+
+  for (i = 0; i < 32; i++)
+    supply_register (regcache, i, (char *) buf + microblaze_regmap[i]);
+}
+
+#endif /* HAVE_PTRACE_GETREGS */
+
+struct regset_info target_regsets[] = {
+#ifdef HAVE_PTRACE_GETREGS
+  { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t), GENERAL_REGS, microblaze_fill_gregset, microblaze_store_gregset },
+  { 0, 0, 0, -1, -1, NULL, NULL },
+#endif /* HAVE_PTRACE_GETREGS */
+  { 0, 0, 0, -1, -1, NULL, NULL }
+};
+
+struct linux_target_ops the_low_target = {
+  init_registers_microblaze,
+  microblaze_num_regs,
+  microblaze_regmap,
+  NULL,
+  microblaze_cannot_fetch_register,
+  microblaze_cannot_store_register,
+  NULL, /* fetch_register */
+  microblaze_get_pc,
+  microblaze_set_pc,
+  (const unsigned char *) &microblaze_breakpoint,
+  microblaze_breakpoint_len,
+  microblaze_reinsert_addr,
+  0,
+  microblaze_breakpoint_at,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  microblaze_collect_ptrace_register,
+  microblaze_supply_ptrace_register,
+};
diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
index 3fc5e768120..0322b4ea813 100644
--- a/gdb/microblaze-linux-tdep.c
+++ b/gdb/microblaze-linux-tdep.c
@@ -37,6 +37,22 @@
 #include "tramp-frame.h"
 #include "linux-tdep.h"
 
+static int microblaze_debug_flag = 0;
+
+static void
+microblaze_debug (const char *fmt, ...)
+{
+  if (microblaze_debug_flag)
+    {
+       va_list args;
+
+       va_start (args, fmt);
+       printf_unfiltered ("MICROBLAZE LINUX: ");
+       vprintf_unfiltered (fmt, args);
+       va_end (args);
+    }
+}
+
 static int
 microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, 
 					   struct bp_target_info *bp_tgt)
@@ -50,13 +66,20 @@ microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
   /* Determine appropriate breakpoint contents and size for this address.  */
   bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen);
 
+  /* Make sure we see the memory breakpoints.  */
+  scoped_restore restore_memory
+    = make_scoped_restore_show_memory_breakpoints (1);
+
   val = target_read_memory (addr, old_contents, bplen);
 
   /* If our breakpoint is no longer at the address, this means that the
      program modified the code on us, so it is wrong to put back the
      old value.  */
   if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
-    val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
+  {
+      val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
+      microblaze_debug ("microblaze_linux_memory_remove_breakpoint writing back to memory at addr 0x%lx\n", addr);
+  }
 
   return val;
 }
@@ -129,6 +152,10 @@ microblaze_linux_init_abi (struct gdbarch_info info,
   /* Trampolines.  */
   tramp_frame_prepend_unwinder (gdbarch,
 				&microblaze_linux_sighandler_tramp_frame);
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+                                             svr4_fetch_objfile_link_map);
 }
 
 void _initialize_microblaze_linux_tdep ();
diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c
index c263228856f..28c2ed9a74c 100644
--- a/gdb/microblaze-tdep.c
+++ b/gdb/microblaze-tdep.c
@@ -137,7 +137,38 @@ microblaze_fetch_instruction (CORE_ADDR pc)
 constexpr gdb_byte microblaze_break_insn[] = MICROBLAZE_BREAKPOINT;
 
 typedef BP_MANIPULATION (microblaze_break_insn) microblaze_breakpoint;
-
+static int
+microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
+				    struct bp_target_info *bp_tgt)
+{
+  CORE_ADDR addr = bp_tgt->placed_address;
+  const unsigned char *bp;
+  int val;
+  int bplen;
+  gdb_byte old_contents[BREAKPOINT_MAX];
+
+  /* Determine appropriate breakpoint contents and size for this address.  */
+  bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen);
+  if (bp == NULL)
+    error (_("Software breakpoints not implemented for this target."));
+
+  /* Make sure we see the memory breakpoints.  */
+  scoped_restore restore_memory
+    = make_scoped_restore_show_memory_breakpoints (1);
+
+  val = target_read_memory (addr, old_contents, bplen);
+
+  /* If our breakpoint is no longer at the address, this means that the
+     program modified the code on us, so it is wrong to put back the
+     old value.  */
+  if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
+  {
+    val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
+    microblaze_debug ("microblaze_linux_memory_remove_breakpoint writing back to memory at addr 0x%lx\n", addr);
+  }
+
+  return val;
+}
 
 /* Allocate and initialize a frame cache.  */
 
@@ -731,6 +762,7 @@ microblaze_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 				       microblaze_breakpoint::kind_from_pc);
   set_gdbarch_sw_breakpoint_from_kind (gdbarch,
 				       microblaze_breakpoint::bp_from_kind);
+  set_gdbarch_memory_remove_breakpoint (gdbarch, microblaze_linux_memory_remove_breakpoint);
 
   set_gdbarch_frame_args_skip (gdbarch, 8);
 
@@ -771,4 +803,5 @@ When non-zero, microblaze specific debugging is enabled."),
 			     NULL,
 			     &setdebuglist, &showdebuglist);
 
+
 }
diff --git a/gdb/microblaze-tdep.h b/gdb/microblaze-tdep.h
index 08af0d191c5..8a429cbf001 100644
--- a/gdb/microblaze-tdep.h
+++ b/gdb/microblaze-tdep.h
@@ -117,6 +117,8 @@ struct microblaze_frame_cache
 
 /* MICROBLAZE_BREAKPOINT defines the breakpoint that should be used.
    Only used for native debugging.  */
-#define MICROBLAZE_BREAKPOINT {0xb9, 0xcc, 0x00, 0x60}
+#define MICROBLAZE_BREAKPOINT {0xba, 0x0c, 0x00, 0x18}
+#define MICROBLAZE_BREAKPOINT_LE {0x18, 0x00, 0x0c, 0xba}
+
 
 #endif /* microblaze-tdep.h */
diff --git a/gdb/regformats/reg-microblaze.dat b/gdb/regformats/reg-microblaze.dat
new file mode 100644
index 00000000000..bd8a4384424
--- /dev/null
+++ b/gdb/regformats/reg-microblaze.dat
@@ -0,0 +1,41 @@
+name:microblaze
+expedite:r1,pc
+32:r0
+32:r1
+32:r2
+32:r3
+32:r4
+32:r5
+32:r6
+32:r7
+32:r8
+32:r9
+32:r10
+32:r11
+32:r12
+32:r13
+32:r14
+32:r15
+32:r16
+32:r17
+32:r18
+32:r19
+32:r20
+32:r21
+32:r22
+32:r23
+32:r24
+32:r25
+32:r26
+32:r27
+32:r28
+32:r29
+32:r30
+32:r31
+32:pc
+32:msr
+32:ear
+32:esr
+32:fsr
+32:slr
+32:shr
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 2bd3a578932..7bee1f1894e 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -184,7 +184,8 @@ SFILES = \
 	$(srcdir)/linux-ia64-low.cc \
 	$(srcdir)/linux-low.cc \
 	$(srcdir)/linux-m68k-low.cc \
-	$(srcdir)/linux-mips-low.cc \
+        $(srcdir)/linux-microblaze-low.c \
+ 	$(srcdir)/linux-mips-low.cc \
 	$(srcdir)/linux-nios2-low.cc \
 	$(srcdir)/linux-ppc-low.cc \
 	$(srcdir)/linux-riscv-low.cc \
@@ -221,6 +222,7 @@ SFILES = \
 	$(srcdir)/../gdb/nat/linux-namespaces.c \
 	$(srcdir)/../gdb/nat/linux-osdata.c \
 	$(srcdir)/../gdb/nat/linux-personality.c \
+        $(srcdir)/../gdb/nat/microblaze-linux.c \        
 	$(srcdir)/../gdb/nat/mips-linux-watch.c \
 	$(srcdir)/../gdb/nat/ppc-linux.c \
 	$(srcdir)/../gdb/nat/riscv-linux-tdesc.c \
@@ -562,6 +564,8 @@ target/%.o: ../gdb/target/%.c
 
 %-generated.cc: ../gdb/regformats/rs6000/%.dat $(regdat_sh)
 	$(ECHO_REGDAT) $(SHELL) $(regdat_sh) $< $@
+microblaze-linux.c : $(srcdir)/../regformats/reg-microblaze.dat $(regdat_sh)
+	$(ECHO_REGDAT) $(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-microblaze.dat microblaze-linux.c
 
 #
 # Dependency tracking.
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 0cb5072c8ab..9d68c24a92d 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -166,6 +166,14 @@ case "${gdbserver_host}" in
 			srv_linux_usrregs=yes
 			srv_linux_thread_db=yes
 			;;
+  microblaze*-*-linux*)	srv_regobj="microblaze-linux.o"
+			srv_tgtobj="linux-low.o linux-osdata.o linux-microblaze-low.o "
+			srv_tgtobj="${srv_tgtobj} linux-procfs.o linux-ptrace.o"
+			srv_xmlfiles="microblaze-linux.xml"
+			srv_linux_regsets=yes
+			srv_linux_usrregs=yes
+			srv_linux_thread_db=yes
+			;;
   powerpc*-*-linux*)	srv_regobj="powerpc-32l.o"
 			srv_regobj="${srv_regobj} powerpc-altivec32l.o"
 			srv_regobj="${srv_regobj} powerpc-vsx32l.o"
-- 
2.17.1

