diff options
-rw-r--r-- | gdb/aarch64-linux-nat.c | 49 | ||||
-rw-r--r-- | gdb/aarch64-tdep.c | 21 | ||||
-rw-r--r-- | gdb/aarch64-tdep.h | 10 | ||||
-rw-r--r-- | gdb/arch/aarch64-scalable-linux.h | 6 | ||||
-rw-r--r-- | gdb/arch/aarch64.c | 4 | ||||
-rw-r--r-- | gdb/arch/aarch64.h | 12 | ||||
-rw-r--r-- | gdb/features/aarch64-sme2.c | 43 | ||||
-rw-r--r-- | gdb/nat/aarch64-scalable-linux-ptrace.c | 134 | ||||
-rw-r--r-- | gdb/nat/aarch64-scalable-linux-ptrace.h | 26 |
9 files changed, 304 insertions, 1 deletions
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index d7fcef5..c1d59b5 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -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); diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 8e7259a..eaae2d9 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -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); diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 9297487..9e4d1a5 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -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); diff --git a/gdb/arch/aarch64-scalable-linux.h b/gdb/arch/aarch64-scalable-linux.h index cb9d85a..0f59ddb 100644 --- a/gdb/arch/aarch64-scalable-linux.h +++ b/gdb/arch/aarch64-scalable-linux.h @@ -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. */ diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c index e1f4948..c93d602 100644 --- a/gdb/arch/aarch64.c +++ b/gdb/arch/aarch64.c @@ -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 (); } diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h index c1cd233..65c6205 100644 --- a/gdb/arch/aarch64.h +++ b/gdb/arch/aarch64.h @@ -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 */ diff --git a/gdb/features/aarch64-sme2.c b/gdb/features/aarch64-sme2.c new file mode 100644 index 0000000..a14b98f --- /dev/null +++ b/gdb/features/aarch64-sme2.c @@ -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; +} diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c index d3a50ed..dc0e45f 100644 --- a/gdb/nat/aarch64-scalable-linux-ptrace.c +++ b/gdb/nat/aarch64-scalable-linux-ptrace.c @@ -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. */ +} diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.h b/gdb/nat/aarch64-scalable-linux-ptrace.h index d609933..69c9982 100644 --- a/gdb/nat/aarch64-scalable-linux-ptrace.h +++ b/gdb/nat/aarch64-scalable-linux-ptrace.h @@ -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 */ |