diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 93b6d416a3..919873b816 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -47,6 +47,7 @@
 #include "linux-record.h"
 #include "auxv.h"
 #include "elf/common.h"
+#include "nat/aarch64-sve-linux-ptrace.h"
 
 /* Signal frame handling.
 
@@ -169,6 +170,100 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
   trad_frame_set_id (this_cache, frame_id_build (sp, func));
 }
 
+
+/* Get VG value from SVE section in the core dump.  */
+
+static uint64_t
+aarch64_linux_core_read_vq (bfd *abfd)
+{
+  struct user_sve_header header;
+  asection *sve_section = bfd_get_section_by_name (abfd, ".reg-aarch-sve");
+
+  if (!sve_section)
+    {
+      /* No SVE state.  */
+      return 0;
+    }
+
+  size_t size = bfd_section_size (abfd, sve_section);
+
+  /* Check extended state size.  */
+  if (size < sizeof (struct user_sve_header))
+    {
+      warning (_("'.reg-aarch-sve' section in core file too small."));
+      return 0;
+    }
+
+  if (! bfd_get_section_contents (abfd, sve_section, &header, 0,
+				  sizeof (struct user_sve_header)))
+    {
+      warning (_("Couldn't read sve header from "
+		 "'.reg-aarch-sve' section in core file."));
+      return 0;
+    }
+
+  if (!sve_vl_valid (header.vl))
+    {
+      warning (_("sve header invalid in"
+		 "'.reg-aarch-sve' section in core file."));
+      return 0;
+    }
+
+  return sve_vq_from_vl (header.vl);
+}
+
+
+/* Supply register REGNUM from BUF to REGCACHE, ignoring the register map
+   in REGSET.  */
+
+static void
+aarch64_linux_supply_sve_regset (const struct regset *regset,
+				 struct regcache *regcache,
+				 int regnum, const void *buf, size_t size)
+{
+  /* Only called via aarch64_linux_iterate_over_regset_sections loop, so no
+     need to support anything other than a full regset, and no need to support
+     null buffers.  */
+  gdb_assert (buf != NULL);
+  gdb_assert (regnum == -1);
+
+  aarch64_sve_regs_copy_to_reg_buf (regcache, buf);
+}
+
+/* Collect register REGNUM from REGCACHE to BUF, ignoring the register
+   map in REGSET.  If REGNUM is -1, do this for all registers in
+   REGSET.  */
+
+static void
+aarch64_linux_collect_sve_regset (const struct regset *regset,
+				  const struct regcache *regcache,
+				  int regnum, void *buf, size_t size)
+{
+  /* Only called via aarch64_linux_iterate_over_regset_sections loop, so no
+     need to support anything other than a full regset, and no need to support
+     null buffers.  */
+  gdb_assert (buf != NULL);
+  gdb_assert (regnum == -1);
+
+  /* Read vector size from regcache.  */
+  uint64_t vg;
+  regcache->raw_collect_integer (AARCH64_SVE_VG_REGNUM, (gdb_byte *)&vg,
+				 sizeof (uint64_t), false);
+  uint64_t vq = sve_vq_from_vg (vg);
+
+  /* Initialize a valid SVE header.  */
+  struct user_sve_header *header = (struct user_sve_header *)buf;
+  header->flags = SVE_PT_REGS_SVE;
+  header->size = SVE_PT_SIZE (vq, header->flags);
+  header->max_size = size;
+  header->vl = sve_vl_from_vg (vg);
+  header->max_vl = SVE_VL_MAX;
+
+  gdb_assert (size >= header->size);
+
+  aarch64_sve_regs_copy_from_reg_buf (regcache, buf);
+}
+
 static const struct tramp_frame aarch64_linux_rt_sigframe =
 {
   SIGTRAMP_FRAME,
@@ -219,6 +314,12 @@ const struct regset aarch64_linux_fpregset =
     regcache_supply_regset, regcache_collect_regset
   };
 
+const struct regset aarch64_linux_sve_regset =
+  {
+    NULL, aarch64_linux_supply_sve_regset, aarch64_linux_collect_sve_regset,
+    REGSET_VARIABLE_SIZE
+  };
+
 /* Implement the "regset_from_core_section" gdbarch method.  */
 
 static void
@@ -227,10 +328,17 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
 					    void *cb_data,
 					    const struct regcache *regcache)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
   cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, &aarch64_linux_gregset,
       NULL, cb_data);
-  cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, &aarch64_linux_fpregset,
-      NULL, cb_data);
+
+  if (tdep->has_sve ())
+    cb (".reg-aarch-sve", SVE_PT_SIZE (tdep->vq, 0),
+	&aarch64_linux_sve_regset, "SVE registers", cb_data);
+  else
+    cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, &aarch64_linux_fpregset, NULL,
+	cb_data);
 }
 
 /* Implement the "core_read_description" gdbarch method.  SVE not yet
@@ -245,7 +353,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
   if (target_auxv_search (target, AT_HWCAP, &aarch64_hwcap) != 1)
     return NULL;
 
-  return aarch64_read_description (0);
+  return aarch64_read_description (aarch64_linux_core_read_vq (abfd));
 }
 
 /* Implementation of `gdbarch_stap_is_single_operand', as defined in
diff --git a/gdb/nat/aarch64-sve-linux-sigcontext.h b/gdb/nat/aarch64-sve-linux-sigcontext.h
index bdece8e17d..ad6e111e71 100644
--- a/gdb/nat/aarch64-sve-linux-sigcontext.h
+++ b/gdb/nat/aarch64-sve-linux-sigcontext.h
@@ -19,6 +19,20 @@
 #ifndef AARCH64_SVE_LINUX_SIGCONTEXT_H
 #define AARCH64_SVE_LINUX_SIGCONTEXT_H
 
+#ifndef _aarch64_ctx
+struct _aarch64_ctx {
+	__u32 magic;
+	__u32 size;
+};
+
+struct user_fpsimd_state {
+	__uint128_t	vregs[32];
+	__u32		fpsr;
+	__u32		fpcr;
+	__u32		__reserved[2];
+};
+#endif
+
 #define SVE_MAGIC	0x53564501
 
 struct sve_context {
