diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 7c633c212f3..95688480c3d 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -41,6 +41,7 @@
 #include "gdb_proc_service.h"
 #include "arch/aarch64.h"
 #include "arch/aarch64-mte-linux.h"
+#include "arch/aarch64-scalable-linux.h"
 #include "linux-aarch32-tdesc.h"
 #include "linux-aarch64-tdesc.h"
 #include "nat/aarch64-mte-linux-ptrace.h"
@@ -750,6 +751,66 @@ aarch64_sve_regs_copy_from_regcache (struct regcache *regcache, void *buf)
   memcpy (buf, sve_state.data (), sve_state.size ());
 }
 
+/* Wrapper for aarch64_za_regs_copy_to_reg_buf, to help copying NT_ARM_ZA
+   state from the thread (BUF) to the register cache.  */
+
+static void
+aarch64_za_regs_copy_to_regcache (struct regcache *regcache,
+				  ATTRIBUTE_UNUSED const void *buf)
+{
+  /* BUF is unused here since we collect the data straight from a ptrace
+     request, therefore bypassing gdbserver's own call to ptrace.  */
+  int tid = lwpid_of (current_thread);
+
+  gdb::optional<int> za_regnum
+    = find_regno_no_throw (regcache->tdesc, "za");
+  gdb::optional<int> svg_regnum
+    = find_regno_no_throw (regcache->tdesc, "svg");
+  gdb::optional<int> svcr_regnum
+    = find_regno_no_throw (regcache->tdesc, "svcr");
+
+  gdb_assert (za_regnum.has_value ());
+  gdb_assert (svg_regnum.has_value ());
+  gdb_assert (svcr_regnum.has_value ());
+
+  /* Update the register cache.  aarch64_za_regs_copy_to_reg_buf handles
+     fetching the NT_ARM_ZA state from thread TID.  */
+  aarch64_za_regs_copy_to_reg_buf (tid, regcache, *za_regnum, *svg_regnum,
+				   *svcr_regnum);
+}
+
+/* Wrapper for aarch64_za_regs_copy_from_reg_buf, to help copying NT_ARM_ZA
+   state from the register cache to the thread (BUF).  */
+
+static void
+aarch64_za_regs_copy_from_regcache (struct regcache *regcache, void *buf)
+{
+  int tid = lwpid_of (current_thread);
+
+  gdb::optional<int> za_regnum
+    = find_regno_no_throw (regcache->tdesc, "za");
+  gdb::optional<int> svg_regnum
+    = find_regno_no_throw (regcache->tdesc, "svg");
+  gdb::optional<int> svcr_regnum
+    = find_regno_no_throw (regcache->tdesc, "svcr");
+
+  gdb_assert (za_regnum.has_value ());
+  gdb_assert (svg_regnum.has_value ());
+  gdb_assert (svcr_regnum.has_value ());
+
+  /* Update the thread NT_ARM_ZA state.  aarch64_za_regs_copy_from_reg_buf
+     handles writing the ZA state back to thread TID.  */
+  aarch64_za_regs_copy_from_reg_buf (tid, regcache, *za_regnum, *svg_regnum,
+				     *svcr_regnum);
+
+  /* We need to return the expected data in BUF, so copy whatever the kernel
+     already has to BUF.  */
+
+  /* Obtain a dump of ZA from ptrace.  */
+  gdb::byte_vector za_state = aarch64_fetch_za_regset (tid);
+  memcpy (buf, za_state.data (), za_state.size ());
+}
+
 /* Array containing all the possible register sets for AArch64/Linux.  During
    architecture setup, these will be checked against the HWCAP/HWCAP2 bits for
    validity and enabled/disabled accordingly.
@@ -772,6 +833,11 @@ static struct regset_info aarch64_regsets[] =
     0, EXTENDED_REGS,
     aarch64_sve_regs_copy_from_regcache, aarch64_sve_regs_copy_to_regcache
   },
+  /* Scalable Matrix Extension (SME) ZA register.  */
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_ZA,
+    0, EXTENDED_REGS,
+    aarch64_za_regs_copy_from_regcache, aarch64_za_regs_copy_to_regcache
+  },
   /* PAC registers.  */
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
     0, OPTIONAL_REGS,
@@ -839,6 +905,10 @@ aarch64_adjust_register_sets (const struct aarch64_features &features)
 	  if (features.tls > 0)
 	    regset->size = AARCH64_TLS_REGISTER_SIZE * features.tls;
 	  break;
+	case NT_ARM_ZA:
+	  if (features.svq > 0)
+	    regset->size = ZA_PT_SIZE (features.svq);
+	  break;
 	default:
 	  gdb_assert_not_reached ("Unknown register set found.");
 	}
@@ -873,6 +943,10 @@ aarch64_target::low_arch_setup ()
       features.mte = linux_get_hwcap2 (pid, 8) & HWCAP2_MTE;
       features.tls = aarch64_tls_register_count (tid);
 
+      /* Scalable Matrix Extension feature and size check.  */
+      if (linux_get_hwcap2 (pid, 8) & HWCAP2_SME)
+	features.svq = aarch64_za_get_svq (tid);
+
       current_process ()->tdesc = aarch64_linux_read_description (features);
 
       /* Adjust the register sets we should use for this particular set of
diff --git a/gdbserver/linux-aarch64-tdesc.cc b/gdbserver/linux-aarch64-tdesc.cc
index 3c60e1a4db0..b6b622ba4f2 100644
--- a/gdbserver/linux-aarch64-tdesc.cc
+++ b/gdbserver/linux-aarch64-tdesc.cc
@@ -41,6 +41,11 @@ aarch64_linux_read_description (const aarch64_features &features)
     error (_("VQ is %" PRIu64 ", maximum supported value is %d"), features.vq,
 	   AARCH64_MAX_SVE_VQ);
 
+  if (features.svq > AARCH64_MAX_SVE_VQ)
+    error (_("Streaming svq is %" PRIu8 ", maximum supported value is %d"),
+	   features.svq,
+	   AARCH64_MAX_SVE_VQ);
+
   struct target_desc *tdesc = tdesc_aarch64_map[features];
 
   if (tdesc == NULL)
@@ -56,6 +61,8 @@ aarch64_linux_read_description (const aarch64_features &features)
 
       if (features.vq > 0)
 	expedited_registers.push_back ("vg");
+      if (features.svq > 0)
+	expedited_registers.push_back ("svg");
 
       expedited_registers.push_back (nullptr);
 
diff --git a/gdbserver/server.h b/gdbserver/server.h
index fde7dfe7060..306d75d4e9b 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -104,7 +104,7 @@ extern int in_queued_stop_replies (ptid_t ptid);
 /* Buffer sizes for transferring memory, registers, etc.   Set to a constant
    value to accommodate multiple register formats.  This value must be at least
    as large as the largest register set supported by gdbserver.  */
-#define PBUFSIZ 18432
+#define PBUFSIZ 131104
 
 /* Definition for an unknown syscall, used basically in error-cases.  */
 #define UNKNOWN_SYSCALL (-1)
