diff mbox series

[RFC,v4,06/15] gdbserver: Implement p and P packets

Message ID 20241102025635.586759-7-thiago.bauermann@linaro.org
State New
Headers show
Series gdbserver improvements for AArch64 SVE support | expand

Commit Message

Thiago Jung Bauermann Nov. 2, 2024, 2:56 a.m. UTC
GDB will need the p packet to individually request "load early" registers
before using the g packet.

Not sure if P is necessary, but if p is supported, why not implement P?

Alternatively, to be more efficient there could be a packet where GDB can
specify a list of registers it wants do load or set.  Or there could be a
register to request/set the expedited registers.
---
 gdbserver/regcache.cc | 44 +++++++++++++++++++++++++
 gdbserver/regcache.h  |  9 ++++++
 gdbserver/server.cc   | 75 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 128 insertions(+)
diff mbox series

Patch

diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 6a1526246867..ec19864bd690 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -208,6 +208,50 @@  find_register_by_number (const struct target_desc *tdesc, int n)
 
 #ifndef IN_PROCESS_AGENT
 
+static gdb::array_view<gdb_byte> register_data (const struct regcache *regcache,
+						int n);
+
+/* See regcache.h.  */
+
+void
+register_to_string (struct regcache *regcache, int regnum, char *buf)
+{
+  if (regcache->register_status[regnum] == REG_VALID)
+    {
+      gdb::array_view<gdb_byte> value = register_data (regcache, regnum);
+
+      bin2hex (value.data (), buf, value.size ());
+      buf[value.size () * 2] = '\0';
+    }
+  else
+    {
+      int len = regcache->register_size (regnum) * 2;
+
+      memset (buf, 'x', len);
+      buf[len] = '\0';
+    }
+}
+
+/* See regcache.h.  */
+
+void
+register_from_string (struct regcache *regcache, int regnum, char *buf)
+{
+  int len = strlen (buf);
+  gdb::array_view<gdb_byte> value = register_data (regcache, regnum);
+  int expected_len = value.size () * 2;
+
+  if (len != expected_len)
+    {
+      warning ("Wrong sized packet for register %d (expected %d bytes, got %d)",
+	       regnum, expected_len, len);
+      if (len > expected_len)
+	len = expected_len;
+    }
+
+  hex2bin (buf, value.data (), len / 2);
+}
+
 void
 registers_to_string (struct regcache *regcache, char *buf)
 {
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index df0feca102e0..5de8fd3d127e 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -96,6 +96,15 @@  void regcache_invalidate (void);
 
 void regcache_release (void);
 
+/* Save contents of register REGNUM to BUF as an hexadecimal string.  */
+
+void register_to_string (struct regcache *regcache, int regnum, char *buf);
+
+/* Set contents of register REGNUM from BUF, interpreted as an hexadecimal
+   string.  */
+
+void register_from_string (struct regcache *regcache, int regnum, char *buf);
+
 /* Convert all registers to a string in the currently specified remote
    format.  */
 
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 5190df4aed5f..4d075fce2359 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -4712,6 +4712,81 @@  process_serial_event (void)
 	    }
 	}
       break;
+    case 'p':
+      {
+	require_running_or_break (cs.own_buf);
+	if (cs.current_traceframe >= 0)
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+	if (!set_desired_thread ())
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+
+	int i = 1, regnum = 0;
+	char c;
+	while ((c = cs.own_buf[i++]) != '\0')
+	  {
+	    regnum = regnum << 4;
+	    regnum |= fromhex (c) & 0x0f;
+	  }
+
+	struct regcache *regcache = get_thread_regcache (current_thread, true);
+
+	if (regnum < 0 || regnum >= regcache->tdesc->reg_defs.size ())
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+
+	fetch_inferior_registers (regcache, regnum);
+	register_to_string (regcache, regnum, cs.own_buf);
+      }
+      break;
+    case 'P':
+      {
+	require_running_or_break (cs.own_buf);
+	if (cs.current_traceframe >= 0)
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+	if (!set_desired_thread ())
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+	if (strchr (cs.own_buf, '=') == nullptr)
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+
+	int i = 1, regnum = 0;
+	char c;
+	while ((c = cs.own_buf[i++]) != '=')
+	  {
+	    regnum = regnum << 4;
+	    regnum |= fromhex (c) & 0x0f;
+	  }
+
+	struct regcache *regcache = get_thread_regcache (current_thread, true);
+
+	if (regnum < 0 || regnum >= regcache->tdesc->reg_defs.size ())
+	  {
+	    write_enn (cs.own_buf);
+	    break;
+	  }
+
+	register_from_string (regcache, regnum, &cs.own_buf[i]);
+	/* FIXME: Why doesn't the G packet need this as well?  */
+	store_inferior_registers (regcache, regnum);
+	write_ok (cs.own_buf);
+      }
+      break;
     case 'm':
       {
 	require_running_or_break (cs.own_buf);