From baac387700a72407b3994bfd0a03825112c9745f Mon Sep 17 00:00:00 2001
From: Mahesh Bodapati <mbodapat@xilinx.com>
Date: Mon, 10 Oct 2022 15:07:22 +0530
Subject: [PATCH 1/8] 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>
Signed-off-by: Mahesh Bodapati <mbodapat@xilinx.com>
---
 gdb/configure.host                  |   2 +
 gdb/features/Makefile               |   1 +
 gdb/features/microblaze-linux.xml   |  13 ++
 gdb/microblaze-linux-tdep.c         |  29 ++-
 gdb/microblaze-tdep.c               |  35 +++-
 gdb/microblaze-tdep.h               |   4 +-
 gdb/regformats/microblaze-linux.dat |  64 +++++++
 gdb/regformats/reg-microblaze.dat   |  41 +++++
 gdbserver/Makefile.in               |   1 +
 gdbserver/configure.srv             |  10 ++
 gdbserver/linux-microblaze-low.cc   | 269 ++++++++++++++++++++++++++++
 11 files changed, 466 insertions(+), 3 deletions(-)
 create mode 100644 gdb/features/microblaze-linux.xml
 create mode 100644 gdb/regformats/microblaze-linux.dat
 create mode 100644 gdb/regformats/reg-microblaze.dat
 create mode 100644 gdbserver/linux-microblaze-low.cc

diff --git a/gdb/configure.host b/gdb/configure.host
index da71675b201..877537d06ef 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -61,6 +61,7 @@ i[34567]86*)		gdb_host_cpu=i386 ;;
 loongarch*)		gdb_host_cpu=loongarch ;;
 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 ;;
@@ -127,6 +128,7 @@ m68*-*-openbsd*)	gdb_host=obsd ;;
 
 m88*-*-openbsd*)	gdb_host=obsd ;;
 
+microblaze*-*linux*)   gdb_host=linux ;;
 mips*-*-linux*)		gdb_host=linux ;;
 mips*-*-netbsdaout* | mips*-*-knetbsd*-gnu)
 			gdb_host=nbsd ;;
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 68e17d0085d..fc3196864c9 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -46,6 +46,7 @@
 # List of .dat files to create in ../regformats/
 WHICH = mips-linux mips-dsp-linux \
 	mips64-linux mips64-dsp-linux \
+	microblaze-linux \
 	nios2-linux \
 	or1k-linux \
 	rs6000/powerpc-32 \
diff --git a/gdb/features/microblaze-linux.xml b/gdb/features/microblaze-linux.xml
new file mode 100644
index 00000000000..688a3f83d1e
--- /dev/null
+++ b/gdb/features/microblaze-linux.xml
@@ -0,0 +1,13 @@
+<?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>
+  <architecture>microblaze</architecture>
+  <osabi>GNU/Linux</osabi>
+  <xi:include href="microblaze-core.xml"/>
+</target>
diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
index daa7ddf7e4d..5748556a556 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 3d5dd669341..3e8e8fe35b9 100644
--- a/gdb/microblaze-tdep.c
+++ b/gdb/microblaze-tdep.c
@@ -128,7 +128,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.  */
 
@@ -716,6 +747,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);
 
@@ -756,4 +788,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 4d90e8785dc..53fcb2297e6 100644
--- a/gdb/microblaze-tdep.h
+++ b/gdb/microblaze-tdep.h
@@ -118,6 +118,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/microblaze-linux.dat b/gdb/regformats/microblaze-linux.dat
new file mode 100644
index 00000000000..b5b49f485cd
--- /dev/null
+++ b/gdb/regformats/microblaze-linux.dat
@@ -0,0 +1,64 @@
+# THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: microblaze-linux.xml
+name:microblaze_linux
+xmltarget:microblaze-linux.xml
+expedite:r1,rpc
+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:rpc
+32:rmsr
+32:rear
+32:resr
+32:rfsr
+32:rbtr
+32:rpvr0
+32:rpvr1
+32:rpvr2
+32:rpvr3
+32:rpvr4
+32:rpvr5
+32:rpvr6
+32:rpvr7
+32:rpvr8
+32:rpvr9
+32:rpvr10
+32:rpvr11
+32:redr
+32:rpid
+32:rzpr
+32:rtlbx
+32:rtlbsx
+32:rtlblo
+32:rtlbhi
+32:slr
+32:shr
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 47648b8d962..55a5f5b81ae 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -178,6 +178,7 @@ SFILES = \
 	$(srcdir)/linux-ia64-low.cc \
 	$(srcdir)/linux-low.cc \
 	$(srcdir)/linux-m68k-low.cc \
+	$(srcdir)/linux-microblaze-low.cc \
 	$(srcdir)/linux-mips-low.cc \
 	$(srcdir)/linux-nios2-low.cc \
 	$(srcdir)/linux-or1k-low.cc \
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 6e09b0eeb79..1817f1f04fb 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -145,6 +145,16 @@ case "${gdbserver_host}" in
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
+
+microblaze*-*-linux*)  srv_regobj="microblaze-linux.o"
+                       srv_tgtobj="$srv_linux_obj linux-microblaze-low.o"      
+                       srv_xmlfiles="microblaze-linux.xml"
+                       srv_xmlfiles="${srv_xmlfiles} microblaze-core.xml"
+                       srv_linux_usrregs=yes
+                       srv_linux_regsets=yes
+                       srv_linux_thread_db=yes
+                       ;;
+
   mips*-*-linux*)	srv_regobj="mips-linux.o"
 			srv_regobj="${srv_regobj} mips-dsp-linux.o"
 			srv_regobj="${srv_regobj} mips64-linux.o"
diff --git a/gdbserver/linux-microblaze-low.cc b/gdbserver/linux-microblaze-low.cc
new file mode 100644
index 00000000000..bf9eecc41ab
--- /dev/null
+++ b/gdbserver/linux-microblaze-low.cc
@@ -0,0 +1,269 @@
+/* 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 "elf/common.h"
+#include "nat/gdb_ptrace.h"
+#include <endian.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
+  };
+
+
+
+class microblaze_target : public linux_process_target
+{
+public:
+
+  const regs_info *get_regs_info () override;
+
+  const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
+ // CORE_ADDR microblaze_reinsert_addr (regcache *regcache);
+
+protected:
+
+  void low_arch_setup () override;
+
+  bool low_cannot_fetch_register (int regno) override;
+
+  bool low_cannot_store_register (int regno) override;
+
+ // bool low_supports_breakpoints () override;
+
+  CORE_ADDR low_get_pc (regcache *regcache) override;
+
+  void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
+
+  bool low_breakpoint_at (CORE_ADDR pc) override;
+};
+
+/* The singleton target ops object.  */
+
+static microblaze_target the_microblaze_target;
+
+#define microblaze_num_regs (sizeof (microblaze_regmap) / sizeof (microblaze_regmap[0]))
+
+/* Defined in auto-generated file microblaze-linux.c.  */
+void init_registers_microblaze_linux (void);
+extern const struct target_desc *tdesc_microblaze_linux;
+
+bool
+microblaze_target::low_cannot_store_register (int regno)
+{
+  if (microblaze_regmap[regno] == -1 || regno == 0)
+    return 1;
+
+  return 0;
+}
+
+bool
+microblaze_target::low_cannot_fetch_register (int regno)
+{
+  return 0;
+}
+
+CORE_ADDR
+microblaze_target::low_get_pc (struct regcache *regcache)
+{
+  unsigned long pc;
+
+  collect_register_by_name (regcache, "pc", &pc);
+  return (CORE_ADDR) pc;
+}
+
+void
+microblaze_target::low_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
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind".  */
+
+const gdb_byte *
+microblaze_target::sw_breakpoint_from_kind (int kind, int *size)
+{
+  *size = microblaze_breakpoint_len;
+  return (const gdb_byte *) &microblaze_breakpoint;
+}
+
+bool
+microblaze_target::low_breakpoint_at (CORE_ADDR where)
+{
+  unsigned long insn;
+
+  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;
+}
+#if 0
+CORE_ADDR
+microblaze_target::microblaze_reinsert_addr (struct regcache *regcache)
+{
+  unsigned long pc;
+  collect_register_by_name (regcache, "r15", &pc);
+  return pc;
+}
+#endif
+#if 0
+#ifdef HAVE_PTRACE_GETREGS
+
+static void
+microblaze_collect_ptrace_register (struct regcache *regcache, int regno, char *buf)
+{
+  int size = register_size (regcache->tdesc, 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 (regcache->tdesc, 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 */
+#endif
+
+static struct regset_info microblaze_regsets[] = {
+#if 0
+#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, GENERAL_REGS, NULL, NULL },
+#endif /* HAVE_PTRACE_GETREGS */
+#endif
+  { 0, 0, 0, -1, GENERAL_REGS, NULL, NULL },
+  NULL_REGSET
+};
+
+static struct usrregs_info microblaze_usrregs_info =
+  {
+    microblaze_num_regs,
+    microblaze_regmap,
+  };
+
+static struct regsets_info microblaze_regsets_info =
+  {
+    microblaze_regsets, /* regsets */
+    0, /* num_regsets */
+    NULL, /* disabled_regsets */
+  };
+
+static struct regs_info microblaze_regs_info =
+  {
+    NULL, /* regset_bitmap */
+    &microblaze_usrregs_info,
+    &microblaze_regsets_info
+  };
+
+const regs_info *
+microblaze_target::get_regs_info (void)
+{
+  return &microblaze_regs_info;
+}
+
+/* Support for hardware single step.  */
+
+static int
+microblaze_supports_hardware_single_step (void)
+{
+  return 1;
+}
+
+
+void
+microblaze_target::low_arch_setup (void)
+{
+  current_process ()->tdesc = tdesc_microblaze_linux;
+}
+
+linux_process_target *the_linux_target = &the_microblaze_target;
+
+void
+initialize_low_arch (void)
+{
+  init_registers_microblaze_linux ();
+  initialize_regsets_info (&microblaze_regsets_info);
+}
+
-- 
2.37.1 (Apple Git-137.1)

