| /* Common native Linux code for the AArch64 scalable extensions: SVE and SME. | 
 |  | 
 |    Copyright (C) 2018-2025 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 <sys/utsname.h> | 
 | #include <sys/uio.h> | 
 | #include "elf/external.h" | 
 | #include "elf/common.h" | 
 | #include "aarch64-scalable-linux-ptrace.h" | 
 | #include "arch/aarch64.h" | 
 | #include "gdbsupport/common-regcache.h" | 
 | #include "gdbsupport/byte-vector.h" | 
 | #include <endian.h> | 
 | #include "arch/aarch64-scalable-linux.h" | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_has_sve_state (int tid) | 
 | { | 
 |   struct user_sve_header header; | 
 |  | 
 |   if (!read_sve_header (tid, header)) | 
 |     return false; | 
 |  | 
 |   if ((header.flags & SVE_PT_REGS_SVE) == 0) | 
 |     return false; | 
 |  | 
 |   if (sizeof (header) == header.size) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_has_ssve_state (int tid) | 
 | { | 
 |   struct user_sve_header header; | 
 |  | 
 |   if (!read_ssve_header (tid, header)) | 
 |     return false; | 
 |  | 
 |   if ((header.flags & SVE_PT_REGS_SVE) == 0) | 
 |     return false; | 
 |  | 
 |   if (sizeof (header) == header.size) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_has_za_state (int tid) | 
 | { | 
 |   struct user_za_header header; | 
 |  | 
 |   if (!read_za_header (tid, header)) | 
 |     return false; | 
 |  | 
 |   if (sizeof (header) == header.size) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | read_sve_header (int tid, struct user_sve_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | 
 |     { | 
 |       /* SVE is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | write_sve_header (int tid, const struct user_sve_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = (void *) &header; | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | 
 |     { | 
 |       /* SVE is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | read_ssve_header (int tid, struct user_sve_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SSVE, &iovec) < 0) | 
 |     { | 
 |       /* SSVE is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | write_ssve_header (int tid, const struct user_sve_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = (void *) &header; | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SSVE, &iovec) < 0) | 
 |     { | 
 |       /* SSVE is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | read_za_header (int tid, struct user_za_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     { | 
 |       /* ZA is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | write_za_header (int tid, const struct user_za_header &header) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = (void *) &header; | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     { | 
 |       /* ZA is not supported.  */ | 
 |       return false; | 
 |     } | 
 |   return true; | 
 | } | 
 |  | 
 | /* Given VL, the streaming vector length for SME, return true if it is valid | 
 |    and false otherwise.  */ | 
 |  | 
 | static bool | 
 | aarch64_sme_vl_valid (size_t vl) | 
 | { | 
 |   return (vl == 16 || vl == 32 || vl == 64 || vl == 128 || vl == 256); | 
 | } | 
 |  | 
 | /* Given VL, the vector length for SVE, return true if it is valid and false | 
 |    otherwise.  SVE_state is true when the check is for the SVE register set. | 
 |    Otherwise the check is for the SSVE register set.  */ | 
 |  | 
 | static bool | 
 | aarch64_sve_vl_valid (const bool sve_state, size_t vl) | 
 | { | 
 |   if (sve_state) | 
 |     return sve_vl_valid (vl); | 
 |  | 
 |   /* We have an active SSVE state, where the valid vector length values are | 
 |      more restrictive.  */ | 
 |   return aarch64_sme_vl_valid (vl); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | uint64_t | 
 | aarch64_sve_get_vq (int tid) | 
 | { | 
 |   struct iovec iovec; | 
 |   struct user_sve_header header; | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   /* Figure out which register set to use for the request.  The vector length | 
 |      for SVE can be different from the vector length for SSVE.  */ | 
 |   bool has_sve_state = !aarch64_has_ssve_state (tid); | 
 |   if (ptrace (PTRACE_GETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE, | 
 | 	      &iovec) < 0) | 
 |     { | 
 |       /* SVE is not supported.  */ | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Ptrace gives the vector length in bytes.  Convert it to VQ, the number of | 
 |      128bit chunks in a Z register.  We use VQ because 128 bits is the minimum | 
 |      a Z register can increase in size.  */ | 
 |   uint64_t vq = sve_vq_from_vl (header.vl); | 
 |  | 
 |   if (!aarch64_sve_vl_valid (has_sve_state, header.vl)) | 
 |     { | 
 |       warning (_("Invalid SVE state from kernel; SVE disabled.")); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   return vq; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_sve_set_vq (int tid, uint64_t vq) | 
 | { | 
 |   struct iovec iovec; | 
 |   struct user_sve_header header; | 
 |  | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   /* Figure out which register set to use for the request.  The vector length | 
 |      for SVE can be different from the vector length for SSVE.  */ | 
 |   bool has_sve_state = !aarch64_has_ssve_state (tid); | 
 |   if (ptrace (PTRACE_GETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE, | 
 | 	      &iovec) < 0) | 
 |     { | 
 |       /* SVE/SSVE is not supported.  */ | 
 |       return false; | 
 |     } | 
 |  | 
 |   header.vl = sve_vl_from_vq (vq); | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE, | 
 | 	      &iovec) < 0) | 
 |     { | 
 |       /* Vector length change failed.  */ | 
 |       return false; | 
 |     } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf) | 
 | { | 
 |   uint64_t reg_vg = 0; | 
 |  | 
 |   /* The VG register may not be valid if we've not collected any value yet. | 
 |      This can happen, for example,  if we're restoring the regcache after an | 
 |      inferior function call, and the VG register comes after the Z | 
 |      registers.  */ | 
 |   if (reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM) != REG_VALID) | 
 |     { | 
 |       /* If vg is not available yet, fetch it from ptrace.  The VG value from | 
 | 	 ptrace is likely the correct one.  */ | 
 |       uint64_t vq = aarch64_sve_get_vq (tid); | 
 |  | 
 |       /* If something went wrong, just bail out.  */ | 
 |       if (vq == 0) | 
 | 	return false; | 
 |  | 
 |       reg_vg = sve_vg_from_vq (vq); | 
 |     } | 
 |   else | 
 |     reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg); | 
 |  | 
 |   return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg)); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | uint64_t | 
 | aarch64_za_get_svq (int tid) | 
 | { | 
 |   struct user_za_header header; | 
 |   if (!read_za_header (tid, header)) | 
 |     return 0; | 
 |  | 
 |   uint64_t vq = sve_vq_from_vl (header.vl); | 
 |  | 
 |   if (!aarch64_sve_vl_valid (false, header.vl)) | 
 |     { | 
 |       warning (_("Invalid ZA state from kernel; ZA disabled.")); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   return vq; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_za_set_svq (int tid, uint64_t vq) | 
 | { | 
 |   struct iovec iovec; | 
 |  | 
 |   /* Read the NT_ARM_ZA header.  */ | 
 |   struct user_za_header header; | 
 |   if (!read_za_header (tid, header)) | 
 |     { | 
 |       /* ZA is not supported.  */ | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* If the size is the correct one already, don't update it.  If we do | 
 |      update the streaming vector length, we will invalidate the register | 
 |      state for ZA, and we do not want that.  */ | 
 |   if (header.vl == sve_vl_from_vq (vq)) | 
 |     return true; | 
 |  | 
 |   /* The streaming vector length is about to get updated.  Set the new value | 
 |      in the NT_ARM_ZA header and adjust the size as well.  */ | 
 |  | 
 |   header.vl = sve_vl_from_vq (vq); | 
 |   header.size = sizeof (struct user_za_header); | 
 |  | 
 |   /* Update the NT_ARM_ZA register set with the new streaming vector | 
 |      length.  */ | 
 |   iovec.iov_len = sizeof (header); | 
 |   iovec.iov_base = &header; | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     { | 
 |       /* Streaming vector length change failed.  */ | 
 |       return false; | 
 |     } | 
 |  | 
 |   /* At this point we have successfully adjusted the streaming vector length | 
 |      for the NT_ARM_ZA register set, and it should have no payload | 
 |      (no ZA state).  */ | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | aarch64_za_set_svq (int tid, const struct reg_buffer_common *reg_buf, | 
 | 		    int svg_regnum) | 
 | { | 
 |   uint64_t reg_svg = 0; | 
 |  | 
 |   /* The svg register may not be valid if we've not collected any value yet. | 
 |      This can happen, for example,  if we're restoring the regcache after an | 
 |      inferior function call, and the svg register comes after the Z | 
 |      registers.  */ | 
 |   if (reg_buf->get_register_status (svg_regnum) != REG_VALID) | 
 |     { | 
 |       /* If svg is not available yet, fetch it from ptrace.  The svg value from | 
 | 	 ptrace is likely the correct one.  */ | 
 |       uint64_t svq = aarch64_za_get_svq (tid); | 
 |  | 
 |       /* If something went wrong, just bail out.  */ | 
 |       if (svq == 0) | 
 | 	return false; | 
 |  | 
 |       reg_svg = sve_vg_from_vq (svq); | 
 |     } | 
 |   else | 
 |     reg_buf->raw_collect (svg_regnum, ®_svg); | 
 |  | 
 |   return aarch64_za_set_svq (tid, sve_vq_from_vg (reg_svg)); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | gdb::byte_vector | 
 | aarch64_fetch_sve_regset (int tid) | 
 | { | 
 |   uint64_t vq = aarch64_sve_get_vq (tid); | 
 |  | 
 |   if (vq == 0) | 
 |     perror_with_name (_("Unable to fetch SVE/SSVE vector length")); | 
 |  | 
 |   /* A ptrace call with NT_ARM_SVE will return a header followed by either a | 
 |      dump of all the SVE and FP registers, or an fpsimd structure (identical to | 
 |      the one returned by NT_FPREGSET) if the kernel has not yet executed any | 
 |      SVE code.  Make sure we allocate enough space for a full SVE dump.  */ | 
 |  | 
 |   gdb::byte_vector sve_state (SVE_PT_SIZE (vq, SVE_PT_REGS_SVE), 0); | 
 |  | 
 |   struct iovec iovec; | 
 |   iovec.iov_base = sve_state.data (); | 
 |   iovec.iov_len = sve_state.size (); | 
 |  | 
 |   bool has_sve_state = !aarch64_has_ssve_state (tid); | 
 |   if (ptrace (PTRACE_GETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE, | 
 | 	      &iovec) < 0) | 
 |     perror_with_name (_("Unable to fetch SVE/SSVE registers")); | 
 |  | 
 |   return sve_state; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_store_sve_regset (int tid, const gdb::byte_vector &sve_state) | 
 | { | 
 |   struct iovec iovec; | 
 |   /* We need to cast from (const void *) here.  */ | 
 |   iovec.iov_base = (void *) sve_state.data (); | 
 |   iovec.iov_len = sve_state.size (); | 
 |  | 
 |   bool has_sve_state = !aarch64_has_ssve_state (tid); | 
 |   if (ptrace (PTRACE_SETREGSET, tid, has_sve_state? NT_ARM_SVE : NT_ARM_SSVE, | 
 | 	      &iovec) < 0) | 
 |     perror_with_name (_("Unable to store SVE/SSVE registers")); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | gdb::byte_vector | 
 | aarch64_fetch_za_regset (int tid) | 
 | { | 
 |   struct user_za_header header; | 
 |   if (!read_za_header (tid, header)) | 
 |     error (_("Failed to read NT_ARM_ZA header.")); | 
 |  | 
 |   if (!aarch64_sme_vl_valid (header.vl)) | 
 |     error (_("Found invalid vector length for NT_ARM_ZA.")); | 
 |  | 
 |   struct iovec iovec; | 
 |   iovec.iov_len = header.size; | 
 |   gdb::byte_vector za_state (header.size); | 
 |   iovec.iov_base = za_state.data (); | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     perror_with_name (_("Failed to fetch NT_ARM_ZA register set.")); | 
 |  | 
 |   return za_state; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_store_za_regset (int tid, const gdb::byte_vector &za_state) | 
 | { | 
 |   struct iovec iovec; | 
 |   /* We need to cast from (const void *) here.  */ | 
 |   iovec.iov_base = (void *) za_state.data (); | 
 |   iovec.iov_len = za_state.size (); | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     perror_with_name (_("Failed to write to the NT_ARM_ZA register set.")); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_initialize_za_regset (int tid) | 
 | { | 
 |   /* First fetch the NT_ARM_ZA header so we can fetch the streaming vector | 
 |      length.  */ | 
 |   struct user_za_header header; | 
 |   if (!read_za_header (tid, header)) | 
 |     error (_("Failed to read NT_ARM_ZA header.")); | 
 |  | 
 |   /* The vector should be default-initialized to zero, and we should account | 
 |      for the payload as well.  */ | 
 |   std::vector<gdb_byte> za_new_state (ZA_PT_SIZE (sve_vq_from_vl (header.vl))); | 
 |  | 
 |   /* Adjust the header size since we are adding the initialized ZA | 
 |      payload.  */ | 
 |   header.size = ZA_PT_SIZE (sve_vq_from_vl (header.vl)); | 
 |  | 
 |   /* Overlay the modified header onto the new ZA state.  */ | 
 |   const gdb_byte *base = (gdb_byte *) &header; | 
 |   memcpy (za_new_state.data (), base, sizeof (user_za_header)); | 
 |  | 
 |   /* Set the ptrace request up and update the NT_ARM_ZA register set.  */ | 
 |   struct iovec iovec; | 
 |   iovec.iov_len = za_new_state.size (); | 
 |   iovec.iov_base = za_new_state.data (); | 
 |  | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_ZA, &iovec) < 0) | 
 |     perror_with_name (_("Failed to initialize the NT_ARM_ZA register set.")); | 
 |  | 
 |   if (supports_zt_registers (tid)) | 
 |     { | 
 |       /* If this target supports SME2, upon initializing ZA, we also need to | 
 | 	 initialize the ZT registers with 0 values.  Do so now.  */ | 
 |       gdb::byte_vector zt_new_state (AARCH64_SME2_ZT0_SIZE, 0); | 
 |       aarch64_store_zt_regset (tid, zt_new_state); | 
 |     } | 
 |  | 
 |   /* The NT_ARM_ZA register set should now contain a zero-initialized ZA | 
 |      payload.  */ | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | gdb::byte_vector | 
 | aarch64_fetch_zt_regset (int tid) | 
 | { | 
 |   /* Read NT_ARM_ZT.  This register set is only available if | 
 |      the ZA bit is non-zero.  */ | 
 |   gdb::byte_vector zt_state (AARCH64_SME2_ZT0_SIZE); | 
 |  | 
 |   struct iovec iovec; | 
 |   iovec.iov_len = AARCH64_SME2_ZT0_SIZE; | 
 |   iovec.iov_base = zt_state.data (); | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_ZT, &iovec) < 0) | 
 |     perror_with_name (_("Failed to fetch NT_ARM_ZT register set.")); | 
 |  | 
 |   return zt_state; | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_store_zt_regset (int tid, const gdb::byte_vector &zt_state) | 
 | { | 
 |   gdb_assert (zt_state.size () == AARCH64_SME2_ZT0_SIZE | 
 | 	      || zt_state.size () == 0); | 
 |  | 
 |   /* We need to be mindful of writing data to NT_ARM_ZT.  If the ZA bit | 
 |      is 0 and we write something to ZT, it will flip the ZA bit. | 
 |  | 
 |      Right now this is taken care of by callers of this function.  */ | 
 |   struct iovec iovec; | 
 |   iovec.iov_len = zt_state.size (); | 
 |   iovec.iov_base = (void *) zt_state.data (); | 
 |  | 
 |   /* Write the contents of ZT_STATE to the NT_ARM_ZT register set.  */ | 
 |   if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_ZT, &iovec) < 0) | 
 |     perror_with_name (_("Failed to write to the NT_ARM_ZT register set.")); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | bool | 
 | supports_zt_registers (int tid) | 
 | { | 
 |   gdb_byte zt_state[AARCH64_SME2_ZT0_SIZE]; | 
 |  | 
 |   struct iovec iovec; | 
 |   iovec.iov_len = AARCH64_SME2_ZT0_SIZE; | 
 |   iovec.iov_base = (void *) zt_state; | 
 |  | 
 |   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_ZT, &iovec) < 0) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | /* If we are running in BE mode, byteswap the contents | 
 |    of SRC to DST for SIZE bytes.  Other, just copy the contents | 
 |    from SRC to DST.  */ | 
 |  | 
 | static void | 
 | aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size) | 
 | { | 
 |   gdb_assert (src != nullptr && dst != nullptr); | 
 |   gdb_assert (size > 1); | 
 |  | 
 | #if (__BYTE_ORDER == __BIG_ENDIAN) | 
 |   for (int i = 0; i < size - 1; i++) | 
 |     dst[i] = src[size - i]; | 
 | #else | 
 |   memcpy (dst, src, size); | 
 | #endif | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf) | 
 | { | 
 |   gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid); | 
 |  | 
 |   gdb_byte *base = sve_state.data (); | 
 |   struct user_sve_header *header | 
 |     = (struct user_sve_header *) sve_state.data (); | 
 |  | 
 |   uint64_t vq = sve_vq_from_vl (header->vl); | 
 |   uint64_t vg = sve_vg_from_vl (header->vl); | 
 |  | 
 |   /* Sanity check the data in the header.  */ | 
 |   if (!sve_vl_valid (header->vl) | 
 |       || SVE_PT_SIZE (vq, header->flags) != header->size) | 
 |     error (_("Invalid SVE header from kernel.")); | 
 |  | 
 |   /* Update VG.  Note, the registers in the regcache will already be of the | 
 |      correct length.  */ | 
 |   reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg); | 
 |  | 
 |   if (HAS_SVE_STATE (*header)) | 
 |     { | 
 |       /* The register dump contains a set of SVE registers.  */ | 
 |  | 
 |       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | 
 | 	reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, | 
 | 			     base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); | 
 |  | 
 |       for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | 
 | 	reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, | 
 | 			     base + SVE_PT_SVE_PREG_OFFSET (vq, i)); | 
 |  | 
 |       reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, | 
 | 			   base + SVE_PT_SVE_FFR_OFFSET (vq)); | 
 |       reg_buf->raw_supply (AARCH64_FPSR_REGNUM, | 
 | 			   base + SVE_PT_SVE_FPSR_OFFSET (vq)); | 
 |       reg_buf->raw_supply (AARCH64_FPCR_REGNUM, | 
 | 			   base + SVE_PT_SVE_FPCR_OFFSET (vq)); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* WARNING: SIMD state is laid out in memory in target-endian format, | 
 | 	 while SVE state is laid out in an endianness-independent format (LE). | 
 |  | 
 | 	 So we have a couple cases to consider: | 
 |  | 
 | 	 1 - If the target is big endian, then SIMD state is big endian, | 
 | 	 requiring a byteswap. | 
 |  | 
 | 	 2 - If the target is little endian, then SIMD state is little endian, | 
 | 	 which matches the SVE format, so no byteswap is needed. */ | 
 |  | 
 |       /* There is no SVE state yet - the register dump contains a fpsimd | 
 | 	 structure instead.  These registers still exist in the hardware, but | 
 | 	 the kernel has not yet initialised them, and so they will be null.  */ | 
 |  | 
 |       gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); | 
 |       struct user_fpsimd_state *fpsimd | 
 | 	= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); | 
 |  | 
 |       /* Make sure we have a zeroed register buffer.  We will need the zero | 
 | 	 padding below.  */ | 
 |       memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); | 
 |  | 
 |       /* Copy across the V registers from fpsimd structure to the Z registers, | 
 | 	 ensuring the non overlapping state is set to null.  */ | 
 |  | 
 |       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | 
 | 	{ | 
 | 	  /* Handle big endian/little endian SIMD/SVE conversion.  */ | 
 | 	  aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i], | 
 | 				 V_REGISTER_SIZE); | 
 | 	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg); | 
 | 	} | 
 |  | 
 |       reg_buf->raw_supply (AARCH64_FPSR_REGNUM, | 
 | 			   (const gdb_byte *) &fpsimd->fpsr); | 
 |       reg_buf->raw_supply (AARCH64_FPCR_REGNUM, | 
 | 			   (const gdb_byte *) &fpsimd->fpcr); | 
 |  | 
 |       /* Clear the SVE only registers.  */ | 
 |       memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); | 
 |  | 
 |       for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | 
 | 	reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg); | 
 |  | 
 |       reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg); | 
 |     } | 
 |  | 
 |   /* At this point we have updated the register cache with the contents of | 
 |      the NT_ARM_SVE register set.  */ | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_sve_regs_copy_from_reg_buf (int tid, | 
 | 				    struct reg_buffer_common *reg_buf) | 
 | { | 
 |   /* First store the vector length to the thread.  This is done first to | 
 |      ensure the ptrace buffers read from the kernel are the correct size.  */ | 
 |   if (!aarch64_sve_set_vq (tid, reg_buf)) | 
 |     perror_with_name (_("Unable to set VG register")); | 
 |  | 
 |   /* Obtain a dump of SVE registers from ptrace.  */ | 
 |   gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid); | 
 |  | 
 |   struct user_sve_header *header = (struct user_sve_header *) sve_state.data (); | 
 |   uint64_t vq = sve_vq_from_vl (header->vl); | 
 |  | 
 |   gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0); | 
 |   memcpy (new_state.data (), sve_state.data (), sve_state.size ()); | 
 |   header = (struct user_sve_header *) new_state.data (); | 
 |   gdb_byte *base = new_state.data (); | 
 |  | 
 |   /* Sanity check the data in the header.  */ | 
 |   if (!sve_vl_valid (header->vl) | 
 |       || SVE_PT_SIZE (vq, header->flags) != header->size) | 
 |     error (_("Invalid SVE header from kernel.")); | 
 |  | 
 |   if (!HAS_SVE_STATE (*header)) | 
 |     { | 
 |       /* There is no SVE state yet - the register dump contains a fpsimd | 
 | 	 structure instead.  Where possible we want to write the reg_buf data | 
 | 	 back to the kernel using the fpsimd structure.  However, if we cannot | 
 | 	 then we'll need to reformat the fpsimd into a full SVE structure, | 
 | 	 resulting in the initialization of SVE state written back to the | 
 | 	 kernel, which is why we try to avoid it.  */ | 
 |  | 
 |       /* Buffer (using the maximum size a Z register) used to look for zeroed | 
 | 	 out sve state.  */ | 
 |       gdb_byte reg[256]; | 
 |       memset (reg, 0, sizeof (reg)); | 
 |  | 
 |       /* Check in the reg_buf if any of the Z registers are set after the | 
 | 	 first 128 bits, or if any of the other SVE registers are set.  */ | 
 |       bool has_sve_state = false; | 
 |       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | 
 | 	{ | 
 | 	  if (!reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, reg, | 
 | 				     V_REGISTER_SIZE)) | 
 | 	    { | 
 | 	      has_sve_state = true; | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       if (!has_sve_state) | 
 | 	for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | 
 | 	  { | 
 | 	    if (!reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i, reg, 0)) | 
 | 	      { | 
 | 		has_sve_state = true; | 
 | 		break; | 
 | 	      } | 
 | 	  } | 
 |  | 
 |       if (!has_sve_state) | 
 | 	  has_sve_state | 
 | 	    = !reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM, reg, 0); | 
 |  | 
 |       struct user_fpsimd_state *fpsimd | 
 | 	= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); | 
 |  | 
 |       /* If no SVE state exists, then use the existing fpsimd structure to | 
 | 	 write out state and return.  */ | 
 |       if (!has_sve_state) | 
 | 	{ | 
 | 	  /* WARNING: SIMD state is laid out in memory in target-endian format, | 
 | 	     while SVE state is laid out in an endianness-independent format | 
 | 	     (LE). | 
 |  | 
 | 	     So we have a couple cases to consider: | 
 |  | 
 | 	     1 - If the target is big endian, then SIMD state is big endian, | 
 | 	     requiring a byteswap. | 
 |  | 
 | 	     2 - If the target is little endian, then SIMD state is little | 
 | 	     endian, which matches the SVE format, so no byteswap is needed. */ | 
 |  | 
 | 	  /* The collects of the Z registers will overflow the size of a vreg. | 
 | 	     There is enough space in the structure to allow for this, but we | 
 | 	     cannot overflow into the next register as we might not be | 
 | 	     collecting every register.  */ | 
 |  | 
 | 	  for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | 
 | 	    { | 
 | 	      if (REG_VALID | 
 | 		  == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) | 
 | 		{ | 
 | 		  reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg); | 
 | 		  /* Handle big endian/little endian SIMD/SVE conversion.  */ | 
 | 		  aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg, | 
 | 					 V_REGISTER_SIZE); | 
 | 		} | 
 | 	    } | 
 |  | 
 | 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) | 
 | 	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, | 
 | 				  (gdb_byte *) &fpsimd->fpsr); | 
 | 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) | 
 | 	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, | 
 | 				  (gdb_byte *) &fpsimd->fpcr); | 
 |  | 
 | 	  /* At this point we have collected all the data from the register | 
 | 	     cache and we are ready to update the FPSIMD register content | 
 | 	     of the thread.  */ | 
 |  | 
 | 	  /* Fall through so we can update the thread's contents with the | 
 | 	     FPSIMD register cache values.  */ | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* Otherwise, reformat the fpsimd structure into a full SVE set, by | 
 | 	     expanding the V registers (working backwards so we don't splat | 
 | 	     registers before they are copied) and using zero for everything | 
 | 	     else. | 
 | 	     Note that enough space for a full SVE dump was originally allocated | 
 | 	     for base.  */ | 
 |  | 
 | 	  header->flags |= SVE_PT_REGS_SVE; | 
 | 	  header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); | 
 |  | 
 | 	  memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, | 
 | 		  sizeof (uint32_t)); | 
 | 	  memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, | 
 | 		  sizeof (uint32_t)); | 
 |  | 
 | 	  for (int i = AARCH64_SVE_Z_REGS_NUM - 1; i >= 0 ; i--) | 
 | 	    { | 
 | 	      memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i], | 
 | 		      sizeof (__int128_t)); | 
 | 	    } | 
 |  | 
 | 	  /* At this point we have converted the FPSIMD layout to an SVE | 
 | 	     layout and copied the register data. | 
 |  | 
 | 	     Fall through so we can update the thread's contents with the SVE | 
 | 	     register cache values.  */ | 
 | 	} | 
 |     } | 
 |   else | 
 |     { | 
 |       /* We already have SVE state for this thread, so we just need to update | 
 | 	 the values of the registers.  */ | 
 |       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | 
 | 	if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM | 
 | 						       + i)) | 
 | 	  reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, | 
 | 				base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); | 
 |  | 
 |       for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | 
 | 	if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM | 
 | 						       + i)) | 
 | 	  reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i, | 
 | 				base + SVE_PT_SVE_PREG_OFFSET (vq, i)); | 
 |  | 
 |       if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM)) | 
 | 	reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM, | 
 | 			      base + SVE_PT_SVE_FFR_OFFSET (vq)); | 
 |       if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) | 
 | 	reg_buf->raw_collect (AARCH64_FPSR_REGNUM, | 
 | 			      base + SVE_PT_SVE_FPSR_OFFSET (vq)); | 
 |       if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) | 
 | 	reg_buf->raw_collect (AARCH64_FPCR_REGNUM, | 
 | 			      base + SVE_PT_SVE_FPCR_OFFSET (vq)); | 
 |     } | 
 |  | 
 |   /* At this point we have collected all the data from the register cache and | 
 |      we are ready to update the SVE/FPSIMD register contents of the thread. | 
 |  | 
 |      sve_state should contain all the data in the correct format, ready to be | 
 |      passed on to ptrace.  */ | 
 |   aarch64_store_sve_regset (tid, new_state); | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_za_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf, | 
 | 				 int za_regnum, int svg_regnum, | 
 | 				 int svcr_regnum) | 
 | { | 
 |   /* Fetch the current ZA state from the thread.  */ | 
 |   gdb::byte_vector za_state = aarch64_fetch_za_regset (tid); | 
 |  | 
 |   /* Sanity check.  */ | 
 |   gdb_assert (!za_state.empty ()); | 
 |  | 
 |   gdb_byte *base = za_state.data (); | 
 |   struct user_za_header *header = (struct user_za_header *) base; | 
 |  | 
 |   /* If we have ZA state, read it.  Otherwise, make the contents of ZA | 
 |      in the register cache all zeroes.  This is how we present the ZA | 
 |      state when it is not initialized.  */ | 
 |   uint64_t svcr_value = 0; | 
 |   if (aarch64_has_za_state (tid)) | 
 |     { | 
 |       /* Sanity check the data in the header.  */ | 
 |       if (!sve_vl_valid (header->vl) | 
 | 	  || ZA_PT_SIZE (sve_vq_from_vl (header->vl)) != header->size) | 
 | 	{ | 
 | 	  error (_("Found invalid streaming vector length in NT_ARM_ZA" | 
 | 		   " register set")); | 
 | 	} | 
 |  | 
 |       reg_buf->raw_supply (za_regnum, base + ZA_PT_ZA_OFFSET); | 
 |       svcr_value |= SVCR_ZA_BIT; | 
 |     } | 
 |   else | 
 |     { | 
 |       size_t za_bytes = header->vl * header->vl; | 
 |       reg_buf->raw_supply_part_zeroed (za_regnum, 0, za_bytes); | 
 |     } | 
 |  | 
 |   /* Handle the svg and svcr registers separately.  We need to calculate | 
 |      their values manually, as the Linux Kernel doesn't expose those | 
 |      explicitly.  */ | 
 |   svcr_value |= aarch64_has_ssve_state (tid)? SVCR_SM_BIT : 0; | 
 |   uint64_t svg_value = sve_vg_from_vl (header->vl); | 
 |  | 
 |   /* Update the contents of svg and svcr registers.  */ | 
 |   reg_buf->raw_supply (svg_regnum, &svg_value); | 
 |   reg_buf->raw_supply (svcr_regnum, &svcr_value); | 
 |  | 
 |   /* The register buffer should now contain the updated copy of the NT_ARM_ZA | 
 |      state.  */ | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_za_regs_copy_from_reg_buf (int tid, | 
 | 				   struct reg_buffer_common *reg_buf, | 
 | 				   int za_regnum, int svg_regnum, | 
 | 				   int svcr_regnum) | 
 | { | 
 |   /* REG_BUF contains the updated ZA state.  We need to extract that state | 
 |      and write it to the thread TID.  */ | 
 |  | 
 |  | 
 |   /* First check if there is a change to the streaming vector length.  Two | 
 |      outcomes are possible here: | 
 |  | 
 |      1 - The streaming vector length in the register cache differs from the | 
 | 	 one currently on the thread state.  This means that we will need to | 
 | 	 update the NT_ARM_ZA register set to reflect the new streaming vector | 
 | 	 length. | 
 |  | 
 |      2 - The streaming vector length in the register cache is the same as in | 
 | 	 the thread state.  This means we do not need to update the NT_ARM_ZA | 
 | 	 register set for a new streaming vector length, and we only need to | 
 | 	 deal with changes to za, svg and svcr. | 
 |  | 
 |      None of the two possibilities above imply that the ZA state actually | 
 |      exists.  They only determine what needs to be done with any ZA content | 
 |      based on the state of the streaming vector length.  */ | 
 |  | 
 |   /* First fetch the NT_ARM_ZA header so we can fetch the streaming vector | 
 |      length.  */ | 
 |   struct user_za_header header; | 
 |   if (!read_za_header (tid, header)) | 
 |     error (_("Failed to read NT_ARM_ZA header.")); | 
 |  | 
 |   /* Fetch the current streaming vector length.  */ | 
 |   uint64_t old_svg = sve_vg_from_vl (header.vl); | 
 |  | 
 |   /* Fetch the (potentially) new streaming vector length.  */ | 
 |   uint64_t new_svg; | 
 |   reg_buf->raw_collect (svg_regnum, &new_svg); | 
 |  | 
 |   /* Did the streaming vector length change?  */ | 
 |   bool svg_changed = new_svg != old_svg; | 
 |  | 
 |   /* First store the streaming vector length to the thread.  This is done | 
 |      first to ensure the ptrace buffers read from the kernel are the correct | 
 |      size.  If the streaming vector length is the same as the current one, it | 
 |      won't be updated.  */ | 
 |   if (!aarch64_za_set_svq (tid, reg_buf, svg_regnum)) | 
 |     error (_("Unable to set svg register")); | 
 |  | 
 |   bool has_za_state = aarch64_has_za_state (tid); | 
 |  | 
 |   size_t za_bytes = sve_vl_from_vg (old_svg) * sve_vl_from_vg (old_svg); | 
 |   gdb::byte_vector za_zeroed (za_bytes, 0); | 
 |  | 
 |   /* If the streaming vector length changed, zero out the contents of ZA in | 
 |      the register cache.  Otherwise, we will need to update the ZA contents | 
 |      in the thread with the ZA contents from the register cache, and they will | 
 |      differ in size.  */ | 
 |   if (svg_changed) | 
 |     reg_buf->raw_supply_part_zeroed (za_regnum, 0, za_bytes); | 
 |  | 
 |   /* When we update svg, we don't automatically initialize the ZA buffer.  If | 
 |      we have no ZA state and the ZA register contents in the register cache are | 
 |      zero, just return and leave the ZA register cache contents as zero.  */ | 
 |   if (!has_za_state && reg_buf->raw_compare (za_regnum, za_zeroed.data (), 0)) | 
 |     { | 
 |       /* No ZA state in the thread or in the register cache.  This was likely | 
 | 	 just an adjustment of the streaming vector length.  Let this fall | 
 | 	 through and update svcr and svg in the register cache.  */ | 
 |     } | 
 |   else | 
 |     { | 
 |       /* If there is no ZA state but the register cache contains ZA data, we | 
 | 	 need to initialize the ZA data through ptrace.  First we initialize | 
 | 	 all the bytes of ZA to zero.  */ | 
 |       if (!has_za_state | 
 | 	  && !reg_buf->raw_compare (za_regnum, za_zeroed.data (), 0)) | 
 | 	aarch64_initialize_za_regset (tid); | 
 |  | 
 |       /* From this point onwards, it is assumed we have a ZA payload in | 
 | 	 the NT_ARM_ZA register set for this thread, and we need to update | 
 | 	 such state based on the contents of the register cache.  */ | 
 |  | 
 |       /* Fetch the current ZA state from the thread.  */ | 
 |       gdb::byte_vector za_state = aarch64_fetch_za_regset (tid); | 
 |  | 
 |       gdb_byte *base = za_state.data (); | 
 |       struct user_za_header *za_header = (struct user_za_header *) base; | 
 |       uint64_t svq = sve_vq_from_vl (za_header->vl); | 
 |  | 
 |       /* Sanity check the data in the header.  */ | 
 |       if (!sve_vl_valid (za_header->vl) | 
 | 	  || ZA_PT_SIZE (svq) != za_header->size) | 
 | 	error (_("Invalid vector length or payload size when reading ZA.")); | 
 |  | 
 |       /* Overwrite the ZA state contained in the thread with the ZA state from | 
 | 	 the register cache.  */ | 
 |       if (REG_VALID == reg_buf->get_register_status (za_regnum)) | 
 | 	reg_buf->raw_collect (za_regnum, base + ZA_PT_ZA_OFFSET); | 
 |  | 
 |       /* Write back the ZA state to the thread's NT_ARM_ZA register set.  */ | 
 |       aarch64_store_za_regset (tid, za_state); | 
 |     } | 
 |  | 
 |   /* Update svcr and svg accordingly.  */ | 
 |   uint64_t svcr_value = 0; | 
 |   svcr_value |= aarch64_has_ssve_state (tid)? SVCR_SM_BIT : 0; | 
 |   svcr_value |= aarch64_has_za_state (tid)? SVCR_ZA_BIT : 0; | 
 |   reg_buf->raw_supply (svcr_regnum, &svcr_value); | 
 |  | 
 |   /* At this point we have written the data contained in the register cache to | 
 |      the thread's NT_ARM_ZA register set.  */ | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_zt_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf, | 
 | 				 int zt_regnum) | 
 | { | 
 |   /* If we have ZA state, read the ZT state.  Otherwise, make the contents of | 
 |      ZT in the register cache all zeroes.  This is how we present the ZT | 
 |      state when it is not initialized (ZA not active).  */ | 
 |   if (aarch64_has_za_state (tid)) | 
 |     { | 
 |       /* Fetch the current ZT state from the thread.  */ | 
 |       gdb::byte_vector zt_state = aarch64_fetch_zt_regset (tid); | 
 |  | 
 |       /* Sanity check.  */ | 
 |       gdb_assert (!zt_state.empty ()); | 
 |  | 
 |       /* Copy the ZT data to the register buffer.  */ | 
 |       reg_buf->raw_supply (zt_regnum, zt_state.data ()); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Zero out ZT.  */ | 
 |       reg_buf->raw_supply_part_zeroed (zt_regnum, 0, AARCH64_SME2_ZT0_SIZE); | 
 |     } | 
 |  | 
 |   /* The register buffer should now contain the updated copy of the NT_ARM_ZT | 
 |      state.  */ | 
 | } | 
 |  | 
 | /* See nat/aarch64-scalable-linux-ptrace.h.  */ | 
 |  | 
 | void | 
 | aarch64_zt_regs_copy_from_reg_buf (int tid, | 
 | 				   struct reg_buffer_common *reg_buf, | 
 | 				   int zt_regnum) | 
 | { | 
 |   /* Do we have a valid ZA state?  */ | 
 |   bool valid_za = aarch64_has_za_state (tid); | 
 |  | 
 |   /* Is the register buffer contents for ZT all zeroes?  */ | 
 |   gdb::byte_vector zt_bytes (AARCH64_SME2_ZT0_SIZE, 0); | 
 |   bool zt_is_all_zeroes | 
 |     = reg_buf->raw_compare (zt_regnum, zt_bytes.data (), 0); | 
 |  | 
 |   /* If ZA state is valid or if we have non-zero data for ZT in the register | 
 |      buffer, we will invoke ptrace to write the ZT state.  Otherwise we don't | 
 |      have to do anything here.  */ | 
 |   if (valid_za || !zt_is_all_zeroes) | 
 |     { | 
 |       if (!valid_za) | 
 | 	{ | 
 | 	  /* ZA state is not valid.  That means we need to initialize the ZA | 
 | 	     state prior to writing the ZT state.  */ | 
 | 	  aarch64_initialize_za_regset (tid); | 
 | 	} | 
 |  | 
 |       /* Extract the ZT data from the register buffer.  */ | 
 |       reg_buf->raw_collect (zt_regnum, zt_bytes.data ()); | 
 |  | 
 |       /* Write the ZT data to thread TID.  */ | 
 |       aarch64_store_zt_regset (tid, zt_bytes); | 
 |     } | 
 |  | 
 |   /* At this point we have (potentially) written the data contained in the | 
 |      register cache to the thread's NT_ARM_ZT register set.  */ | 
 | } |