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>
This commit is contained in:
Luis Machado 2023-04-04 17:20:46 +01:00
parent 6762e153a9
commit 42019af621
9 changed files with 304 additions and 1 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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. */

View file

@ -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 ();
}

View file

@ -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 */

View file

@ -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;
}

View file

@ -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. */
}

View file

@ -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 */