@@ -376,6 +376,37 @@ store_za_to_thread (struct regcache *regcache)
tdep->sme_svcr_regnum);
}
+/* Fill GDB's REGCACHE with the ZT register set contents from the
+ thread associated with REGCACHE. If there is no active ZA register state,
+ make the ZT register contents zero. */
+
+static void
+fetch_zt_from_thread (struct regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ /* Read ZT state from the thread to the register cache. */
+ aarch64_zt_regs_copy_to_reg_buf (regcache->ptid ().lwp (),
+ regcache,
+ tdep->sme2_zt0_regnum);
+}
+
+/* Store the NT_ARM_ZT register set contents from GDB's REGCACHE to the
+ thread associated with REGCACHE. */
+
+static void
+store_zt_to_thread (struct regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ /* Write ZT state from the register cache to the thread. */
+ aarch64_zt_regs_copy_from_reg_buf (regcache->ptid ().lwp (),
+ regcache,
+ tdep->sme2_zt0_regnum);
+}
+
/* Fill GDB's register array with the pointer authentication mask values from
the current thread. */
@@ -549,6 +580,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
if (tdep->has_sme ())
fetch_za_from_thread (regcache);
+
+ if (tdep->has_sme2 ())
+ fetch_zt_from_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
@@ -569,6 +603,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
else if (tdep->has_sme () && regno >= tdep->sme_reg_base
&& regno < tdep->sme_reg_base + 3)
fetch_za_from_thread (regcache);
+ /* SME2 register? */
+ else if (tdep->has_sme2 () && regno == tdep->sme2_zt0_regnum)
+ fetch_zt_from_thread (regcache);
/* MTE register? */
else if (tdep->has_mte ()
&& (regno == tdep->mte_reg_base))
@@ -646,6 +683,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
if (tdep->has_sme ())
store_za_to_thread (regcache);
+
+ if (tdep->has_sme2 ())
+ store_zt_to_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
@@ -661,6 +701,8 @@ aarch64_store_registers (struct regcache *regcache, int regno)
else if (tdep->has_sme () && regno >= tdep->sme_reg_base
&& regno < tdep->sme_reg_base + 3)
store_za_to_thread (regcache);
+ else if (tdep->has_sme2 () && regno == tdep->sme2_zt0_regnum)
+ store_zt_to_thread (regcache);
/* MTE register? */
else if (tdep->has_mte ()
&& (regno == tdep->mte_reg_base))
@@ -861,6 +903,10 @@ aarch64_linux_nat_target::read_description ()
/* SME feature check. */
features.svq = aarch64_za_get_svq (tid);
+ /* Check for SME2 support. */
+ if ((hwcap2 & HWCAP2_SME2) || (hwcap2 & HWCAP2_SME2P1))
+ features.sme2 = supports_zt_registers (tid);
+
return aarch64_read_description (features);
}
@@ -981,6 +1027,9 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
features.vq = vq;
features.svq = svq;
+ /* Check for the SME2 feature. */
+ features.sme2 = supports_zt_registers (ptid.lwp ());
+
struct gdbarch_info info;
info.bfd_arch_info = bfd_lookup_arch (bfd_arch_aarch64, bfd_mach_aarch64);
info.target_desc = aarch64_read_description (features);
@@ -4048,6 +4048,10 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
features.svq = aarch64_get_tdesc_svq (tdesc);
+ /* Check for the SME2 feature. */
+ features.sme2 = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme2")
+ != nullptr);
+
return features;
}
@@ -4310,6 +4314,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
}
int first_sme_regnum = -1;
+ int first_sme2_regnum = -1;
int first_sme_pseudo_regnum = -1;
const struct tdesc_feature *feature_sme
= tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme");
@@ -4336,6 +4341,19 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
/* Add the ZA tile pseudo registers. */
num_pseudo_regs += AARCH64_ZA_TILES_NUM;
+
+ /* Now check for the SME2 feature. SME2 is only available if SME is
+ available. */
+ const struct tdesc_feature *feature_sme2
+ = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sme2");
+ if (feature_sme2 != nullptr)
+ {
+ /* Record the first SME2 register. */
+ first_sme2_regnum = num_regs;
+
+ valid_p &= tdesc_numbered_register (feature_sme2, tdesc_data.get (),
+ num_regs++, "zt0");
+ }
}
/* Add the TLS register. */
@@ -4459,6 +4477,9 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->sme_za_regnum = first_sme_regnum + 2;
tdep->sme_svq = svq;
+ /* Set the SME2 register set details. */
+ tdep->sme2_zt0_regnum = first_sme2_regnum;
+
set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
@@ -172,6 +172,16 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
{
return sme_svq != 0;
}
+
+ /* Index of the SME2 ZT0 register. This is -1 if SME2 is not
+ supported. */
+ int sme2_zt0_regnum = -1;
+
+ /* Return true if the target supports SME2, and false otherwise. */
+ bool has_sme2 () const
+ {
+ return sme2_zt0_regnum > 0;
+ }
};
const target_desc *aarch64_read_description (const aarch64_features &features);
@@ -29,6 +29,12 @@
#define HWCAP2_SME (1 << 23)
#endif
+/* Feature check for Scalable Matrix Extension 2. */
+#ifndef HWCAP2_SME2
+#define HWCAP2_SME2 (1UL << 37)
+#define HWCAP2_SME2P1 (1UL << 38)
+#endif
+
/* Streaming mode enabled/disabled bit. */
#define SVCR_SM_BIT (1 << 0)
/* ZA enabled/disabled bit. */
@@ -25,6 +25,7 @@
#include "../features/aarch64-pauth.c"
#include "../features/aarch64-mte.c"
#include "../features/aarch64-sme.c"
+#include "../features/aarch64-sme2.c"
#include "../features/aarch64-tls.c"
/* See arch/aarch64.h. */
@@ -62,6 +63,9 @@ aarch64_create_target_description (const aarch64_features &features)
regnum = create_feature_aarch64_sme (tdesc.get (), regnum,
sve_vl_from_vq (features.svq));
+ if (features.sme2)
+ regnum = create_feature_aarch64_sme2 (tdesc.get (), regnum);
+
return tdesc.release ();
}
@@ -48,6 +48,9 @@ struct aarch64_features
These use at most 5 bits to represent. */
uint8_t svq = 0;
+
+ /* Whether SME2 is supported. */
+ bool sme2 = false;
};
inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
@@ -56,7 +59,8 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
&& lhs.pauth == rhs.pauth
&& lhs.mte == rhs.mte
&& lhs.tls == rhs.tls
- && lhs.svq == rhs.svq;
+ && lhs.svq == rhs.svq
+ && lhs.sme2 == rhs.sme2;
}
namespace std
@@ -79,6 +83,9 @@ namespace std
gdb_assert (features.svq >= 0);
gdb_assert (features.svq <= 16);
h = h << 5 | (features.svq & 0x5);
+
+ /* SME2 feature. */
+ h = h << 1 | features.sme2;
return h;
}
};
@@ -220,4 +227,7 @@ enum aarch64_regnum
#define AARCH64_SME_MIN_SVL 128
#define AARCH64_SME_MAX_SVL 2048
+/* Size of the SME2 ZT0 register in bytes. */
+#define AARCH64_SME2_ZT0_SIZE 64
+
#endif /* ARCH_AARCH64_H */
new file mode 100644
@@ -0,0 +1,43 @@
+/* Copyright (C) 2023 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 "gdbsupport/tdesc.h"
+
+/* This function is NOT auto generated from xml. Create the AArch64 SME2
+ feature into RESULT.
+
+ The ZT0 register is only available when the SME ZA register is
+ available. */
+
+static int
+create_feature_aarch64_sme2 (struct target_desc *result, long regnum)
+{
+ struct tdesc_feature *feature;
+ tdesc_type *element_type;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.sme2");
+
+ /* Byte type. */
+ element_type = tdesc_named_type (feature, "uint8");
+
+ /* Vector of 64 bytes. */
+ element_type = tdesc_create_vector (feature, "sme2_bv", element_type, 64);
+
+ /* The following is the ZT0 register, with 512 bits (64 bytes). */
+ tdesc_create_reg (feature, "zt0", regnum++, 1, nullptr, 512, "sme2_bv");
+ return regnum;
+}
@@ -519,10 +519,75 @@ aarch64_initialize_za_regset (int tid)
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. */
@@ -989,3 +1054,72 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
/* 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. */
+ gdb::byte_vector zt_zeroed (AARCH64_SME2_ZT0_SIZE, 0);
+ reg_buf->raw_supply (zt_regnum, zt_zeroed.data ());
+ }
+
+ /* 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. */
+}
@@ -120,6 +120,16 @@ extern void aarch64_store_za_regset (int tid, const gdb::byte_vector &za_state);
size. The bytes of the ZA register are initialized to zero. */
extern void aarch64_initialize_za_regset (int tid);
+/* Given TID, return the NT_ARM_ZT register set data as a vector of bytes. */
+extern gdb::byte_vector aarch64_fetch_zt_regset (int tid);
+
+/* Write ZT_STATE for TID. */
+extern void aarch64_store_zt_regset (int tid, const gdb::byte_vector &zt_state);
+
+/* Return TRUE if thread TID supports the NT_ARM_ZT register set.
+ Return FALSE otherwise. */
+extern bool supports_zt_registers (int tid);
+
/* Given a register buffer REG_BUF, update it with SVE/SSVE register data
from SVE_STATE. */
extern void
@@ -151,4 +161,20 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
struct reg_buffer_common *reg_buf,
int za_regnum, int svg_regnum,
int svcr_regnum);
+
+/* Given a thread id TID and a register buffer REG_BUF, update the register
+ buffer with the ZT register set state from thread TID.
+
+ ZT_REGNUM is the register number for ZT0. */
+extern void
+aarch64_zt_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf,
+ int zt_regnum);
+
+/* Given a thread id TID and a register buffer REG_BUF containing the ZT
+ register set state, write the ZT data to thread TID.
+
+ ZT_REGNUM is the register number for ZT0. */
+extern void
+aarch64_zt_regs_copy_from_reg_buf (int tid, struct reg_buffer_common *reg_buf,
+ int zt_regnum);
#endif /* NAT_AARCH64_SCALABLE_LINUX_PTRACE_H */