aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/nat')
-rw-r--r--gdb/nat/aarch64-sve-linux-ptrace.c77
1 files changed, 63 insertions, 14 deletions
diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c
index 2ce90cc..5f08995 100644
--- a/gdb/nat/aarch64-sve-linux-ptrace.c
+++ b/gdb/nat/aarch64-sve-linux-ptrace.c
@@ -26,6 +26,7 @@
#include "arch/aarch64.h"
#include "gdbsupport/common-regcache.h"
#include "gdbsupport/byte-vector.h"
+#include <endian.h>
/* See nat/aarch64-sve-linux-ptrace.h. */
@@ -142,6 +143,24 @@ aarch64_sve_get_sveregs (int tid)
return buf;
}
+/* 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. */
+
+static void
+aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size)
+{
+ gdb_assert (src != nullptr && dst != nullptr);
+ gdb_assert (size > 1);
+
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+ for (int i = 0; i < size - 1; i++)
+ dst[i] = src[size - i];
+#else
+ memcpy (dst, src, size);
+#endif
+}
+
/* See nat/aarch64-sve-linux-ptrace.h. */
void
@@ -184,34 +203,50 @@ aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf,
}
else
{
+ /* WARNING: SIMD state is laid out in memory in target-endian format,
+ while SVE state is laid out in an endianness-independent format (LE).
+
+ So we have a couple cases to consider:
+
+ 1 - If the target is big endian, then SIMD state is big endian,
+ requiring a byteswap.
+
+ 2 - If the target is little endian, then SIMD state is little endian,
+ which matches the SVE format, so no byteswap is needed. */
+
/* There is no SVE state yet - the register dump contains a fpsimd
structure instead. These registers still exist in the hardware, but
the kernel has not yet initialised them, and so they will be null. */
- char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
struct user_fpsimd_state *fpsimd
= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
+ /* Make sure we have a zeroed register buffer. We will need the zero
+ padding below. */
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+
/* Copy across the V registers from fpsimd structure to the Z registers,
ensuring the non overlapping state is set to null. */
- memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
-
for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
{
- memcpy (zero_reg, &fpsimd->vregs[i], sizeof (__int128_t));
- reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
+ /* Handle big endian/little endian SIMD/SVE conversion. */
+ aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i],
+ V_REGISTER_SIZE);
+ reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
}
reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
/* Clear the SVE only registers. */
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
- reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, zero_reg);
+ reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg);
- reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, zero_reg);
+ reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg);
}
}
@@ -240,11 +275,11 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
kernel, which is why we try to avoid it. */
bool has_sve_state = false;
- char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
struct user_fpsimd_state *fpsimd
= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
- memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
/* Check in the reg_buf if any of the Z registers are set after the
first 128 bits, or if any of the other SVE registers are set. */
@@ -252,7 +287,7 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
{
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i,
- zero_reg, sizeof (__int128_t));
+ reg, sizeof (__int128_t));
if (has_sve_state)
break;
}
@@ -261,19 +296,31 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
{
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i,
- zero_reg, 0);
+ reg, 0);
if (has_sve_state)
break;
}
if (!has_sve_state)
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM,
- zero_reg, 0);
+ reg, 0);
/* If no SVE state exists, then use the existing fpsimd structure to
write out state and return. */
if (!has_sve_state)
{
+ /* WARNING: SIMD state is laid out in memory in target-endian format,
+ while SVE state is laid out in an endianness-independent format
+ (LE).
+
+ So we have a couple cases to consider:
+
+ 1 - If the target is big endian, then SIMD state is big endian,
+ requiring a byteswap.
+
+ 2 - If the target is little endian, then SIMD state is little
+ endian, which matches the SVE format, so no byteswap is needed. */
+
/* The collects of the Z registers will overflow the size of a vreg.
There is enough space in the structure to allow for this, but we
cannot overflow into the next register as we might not be
@@ -284,8 +331,10 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
if (REG_VALID
== reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i))
{
- reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
- memcpy (&fpsimd->vregs[i], zero_reg, sizeof (__int128_t));
+ reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg);
+ /* Handle big endian/little endian SIMD/SVE conversion. */
+ aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg,
+ V_REGISTER_SIZE);
}
}