aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat
diff options
context:
space:
mode:
authorLuis Machado <luis.machado@arm.com>2023-04-04 17:20:46 +0100
committerLuis Machado <luis.machado@arm.com>2023-10-04 16:23:40 +0100
commit42019af621005a274d1a658f4c752a98fe5bab8e (patch)
treee1ffbb608ef942726edb6a813fde7c2b5f4ac384 /gdb/nat
parent6762e153a9e4e450b1a9904f9c96ec9f9b4cbc31 (diff)
downloadbinutils-42019af621005a274d1a658f4c752a98fe5bab8e.zip
binutils-42019af621005a274d1a658f4c752a98fe5bab8e.tar.gz
binutils-42019af621005a274d1a658f4c752a98fe5bab8e.tar.bz2
sme2: Enable SME2 for AArch64 gdb on Linux
SME2 defines a new 512-bit register named ZT0, and it is only available if SME is also supported. The ZT0 state is valid only if the SVCR ZA bit is enabled. Otherwise its contents are empty (0). The target description is dynamic and gets generated at runtime based on the availability of the feature. Validated under Fast Models. Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Diffstat (limited to 'gdb/nat')
-rw-r--r--gdb/nat/aarch64-scalable-linux-ptrace.c134
-rw-r--r--gdb/nat/aarch64-scalable-linux-ptrace.h26
2 files changed, 160 insertions, 0 deletions
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 */