aboutsummaryrefslogtreecommitdiff
path: root/gdb/aarch64-linux-nat.c
diff options
context:
space:
mode:
authorLuis Machado <luis.machado@arm.com>2023-02-07 09:36:23 +0000
committerLuis Machado <luis.machado@arm.com>2023-10-04 16:23:39 +0100
commitca65640ff724f330e90e63ae0b14a195be79b4f6 (patch)
tree1ad86912de1ab8a0a62166e4b8922626a732a1b5 /gdb/aarch64-linux-nat.c
parent89c4ee8398e3915c6685bb74057eb5644cf36959 (diff)
downloadgdb-ca65640ff724f330e90e63ae0b14a195be79b4f6.zip
gdb-ca65640ff724f330e90e63ae0b14a195be79b4f6.tar.gz
gdb-ca65640ff724f330e90e63ae0b14a195be79b4f6.tar.bz2
sme: Enable SME registers and pseudo-registers
The SME (Scalable Matrix Extension) [1] exposes a new matrix register ZA with variable sizes. It also exposes a new mode called streaming mode. Similarly to SVE, the ZA register size is dictated by a vector length, but the SME vector length is called streaming vetor length. The total size for ZA in a given moment is svl x svl. In streaming mode, the SVE registers have their sizes based on svl rather than the regular vector length (vl). The feature detection is controlled by the HWCAP2_SME bit, but actual support should be validated by attempting a ptrace call for one of the new register sets: NT_ARM_ZA and NT_ARM_SSVE. Due to its large size, the ZA register is exposed as a vector of bytes, but we introduce a number of pseudo-registers that gives various different views into the ZA contents. These can be arranged in a couple categories: tiles and tile slices. Tiles are matrices the same size or smaller than ZA. Tile slices are vectors which map to ZA's rows/columns in different ways. A new dynamic target description is provided containing the ZA register, the SVG register and the SVCR register. The size of ZA, like the SVE vector registers, is based on the vector length register SVG (VG for SVE). This patch enables SME register support for gdb. [1] https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/scalable-matrix-extension-armv9-a-architecture Co-Authored-By: Ezra Sitorus <ezra.sitorus@arm.com> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Diffstat (limited to 'gdb/aarch64-linux-nat.c')
-rw-r--r--gdb/aarch64-linux-nat.c98
1 files changed, 85 insertions, 13 deletions
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 267a1ca..d7fcef5 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -55,6 +55,7 @@
#include "arch/aarch64-mte-linux.h"
#include "nat/aarch64-mte-linux-ptrace.h"
+#include "arch/aarch64-scalable-linux.h"
#include <string.h>
@@ -313,8 +314,11 @@ store_fpregs_to_thread (const struct regcache *regcache)
}
}
-/* Fill GDB's register array with the sve register values
- from the current thread. */
+/* Fill GDB's REGCACHE with the valid SVE register values from the thread
+ associated with REGCACHE.
+
+ This function handles reading data from SVE or SSVE states, depending
+ on which state is active at the moment. */
static void
fetch_sveregs_from_thread (struct regcache *regcache)
@@ -323,8 +327,11 @@ fetch_sveregs_from_thread (struct regcache *regcache)
aarch64_sve_regs_copy_to_reg_buf (regcache->ptid ().lwp (), regcache);
}
-/* Store to the current thread the valid sve register
- values in the GDB's register array. */
+/* Store the valid SVE register values from GDB's REGCACHE to the thread
+ associated with REGCACHE.
+
+ This function handles writing data to SVE or SSVE states, depending
+ on which state is active at the moment. */
static void
store_sveregs_to_thread (struct regcache *regcache)
@@ -334,6 +341,41 @@ store_sveregs_to_thread (struct regcache *regcache)
aarch64_sve_regs_copy_from_reg_buf (regcache->ptid ().lwp (), regcache);
}
+/* Fill GDB's REGCACHE with the ZA register set contents from the
+ thread associated with REGCACHE. If there is no active ZA register state,
+ make the ZA register contents zero. */
+
+static void
+fetch_za_from_thread (struct regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ /* Read ZA state from the thread to the register cache. */
+ aarch64_za_regs_copy_to_reg_buf (regcache->ptid ().lwp (),
+ regcache,
+ tdep->sme_za_regnum,
+ tdep->sme_svg_regnum,
+ tdep->sme_svcr_regnum);
+}
+
+/* Store the NT_ARM_ZA register set contents from GDB's REGCACHE to the thread
+ associated with REGCACHE. */
+
+static void
+store_za_to_thread (struct regcache *regcache)
+{
+ aarch64_gdbarch_tdep *tdep
+ = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+ /* Write ZA state from the register cache to the thread. */
+ aarch64_za_regs_copy_from_reg_buf (regcache->ptid ().lwp (),
+ regcache,
+ tdep->sme_za_regnum,
+ tdep->sme_svg_regnum,
+ tdep->sme_svcr_regnum);
+}
+
/* Fill GDB's register array with the pointer authentication mask values from
the current thread. */
@@ -488,7 +530,10 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
if (regno == -1)
{
fetch_gregs_from_thread (regcache);
- if (tdep->has_sve ())
+
+ /* We attempt to fetch SVE registers if there is support for either
+ SVE or SME (due to the SSVE state of SME). */
+ if (tdep->has_sve () || tdep->has_sme ())
fetch_sveregs_from_thread (regcache);
else
fetch_fpregs_from_thread (regcache);
@@ -501,12 +546,16 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
if (tdep->has_tls ())
fetch_tlsregs_from_thread (regcache);
+
+ if (tdep->has_sme ())
+ fetch_za_from_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
fetch_gregs_from_thread (regcache);
/* SVE register? */
- else if (tdep->has_sve () && regno <= AARCH64_SVE_VG_REGNUM)
+ else if ((tdep->has_sve () || tdep->has_sme ())
+ && regno <= AARCH64_SVE_VG_REGNUM)
fetch_sveregs_from_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
@@ -516,6 +565,10 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
&& (regno == AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base)
|| regno == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base)))
fetch_pauth_masks_from_thread (regcache);
+ /* SME register? */
+ else if (tdep->has_sme () && regno >= tdep->sme_reg_base
+ && regno < tdep->sme_reg_base + 3)
+ fetch_za_from_thread (regcache);
/* MTE register? */
else if (tdep->has_mte ()
&& (regno == tdep->mte_reg_base))
@@ -577,7 +630,10 @@ aarch64_store_registers (struct regcache *regcache, int regno)
if (regno == -1)
{
store_gregs_to_thread (regcache);
- if (tdep->has_sve ())
+
+ /* We attempt to store SVE registers if there is support for either
+ SVE or SME (due to the SSVE state of SME). */
+ if (tdep->has_sve () || tdep->has_sme ())
store_sveregs_to_thread (regcache);
else
store_fpregs_to_thread (regcache);
@@ -587,16 +643,24 @@ aarch64_store_registers (struct regcache *regcache, int regno)
if (tdep->has_tls ())
store_tlsregs_to_thread (regcache);
+
+ if (tdep->has_sme ())
+ store_za_to_thread (regcache);
}
/* General purpose register? */
else if (regno < AARCH64_V0_REGNUM)
store_gregs_to_thread (regcache);
/* SVE register? */
- else if (tdep->has_sve () && regno <= AARCH64_SVE_VG_REGNUM)
+ else if ((tdep->has_sve () || tdep->has_sme ())
+ && regno <= AARCH64_SVE_VG_REGNUM)
store_sveregs_to_thread (regcache);
/* FPSIMD register? */
else if (regno <= AARCH64_FPCR_REGNUM)
store_fpregs_to_thread (regcache);
+ /* SME register? */
+ else if (tdep->has_sme () && regno >= tdep->sme_reg_base
+ && regno < tdep->sme_reg_base + 3)
+ store_za_to_thread (regcache);
/* MTE register? */
else if (tdep->has_mte ()
&& (regno == tdep->mte_reg_base))
@@ -787,10 +851,15 @@ aarch64_linux_nat_target::read_description ()
CORE_ADDR hwcap2 = linux_get_hwcap2 ();
aarch64_features features;
+ /* SVE/SSVE check. Reading VQ may return either the regular vector length
+ or the streaming vector length, depending on whether streaming mode is
+ active or not. */
features.vq = aarch64_sve_get_vq (tid);
features.pauth = hwcap & AARCH64_HWCAP_PACA;
features.mte = hwcap2 & HWCAP2_MTE;
features.tls = aarch64_tls_register_count (tid);
+ /* SME feature check. */
+ features.svq = aarch64_za_get_svq (tid);
return aarch64_read_description (features);
}
@@ -893,21 +962,24 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
if (gdbarch_bfd_arch_info (inf->gdbarch)->bits_per_word == 32)
return inf->gdbarch;
- /* Only return it if the current vector length matches the one in the tdep. */
+ /* Only return the inferior's gdbarch if both vq and svq match the ones in
+ the tdep. */
aarch64_gdbarch_tdep *tdep
= gdbarch_tdep<aarch64_gdbarch_tdep> (inf->gdbarch);
uint64_t vq = aarch64_sve_get_vq (ptid.lwp ());
- if (vq == tdep->vq)
+ uint64_t svq = aarch64_za_get_svq (ptid.lwp ());
+ if (vq == tdep->vq && svq == tdep->sme_svq)
return inf->gdbarch;
- /* We reach here if the vector length for the thread is different from its
+ /* We reach here if any vector length for the thread is different from its
value at process start. Lookup gdbarch via info (potentially creating a
- new one) by using a target description that corresponds to the new vq value
- and the current architecture features. */
+ new one) by using a target description that corresponds to the new vq/svq
+ value and the current architecture features. */
const struct target_desc *tdesc = gdbarch_target_desc (inf->gdbarch);
aarch64_features features = aarch64_features_from_target_desc (tdesc);
features.vq = vq;
+ features.svq = svq;
struct gdbarch_info info;
info.bfd_arch_info = bfd_lookup_arch (bfd_arch_aarch64, bfd_mach_aarch64);