diff options
-rw-r--r-- | gdb/ChangeLog | 57 | ||||
-rw-r--r-- | gdb/aarch64-linux-nat.c | 54 | ||||
-rw-r--r-- | gdb/gdbserver/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 1 | ||||
-rw-r--r-- | gdb/nat/aarch64-sve-linux-ptrace.c | 269 | ||||
-rw-r--r-- | gdb/nat/aarch64-sve-linux-ptrace.h | 21 |
6 files changed, 403 insertions, 3 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b0ea280..4217b08 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,62 @@ 2018-06-18 Alan Hayward <alan.hayward@arm.com> + * aarch64-linux-nat.c (fetch_sveregs_from_thread): New function. + (store_sveregs_to_thread): Likewise. + (aarch64_linux_fetch_inferior_registers): Check for SVE. + (aarch64_linux_store_inferior_registers): Likewise. + * nat/aarch64-sve-linux-ptrace.c (aarch64_sve_get_sveregs): New + function. + (aarch64_sve_regs_copy_to_regcache): Likewise. + (aarch64_sve_regs_copy_from_regcache): Likewise. + * nat/aarch64-sve-linux-ptrace.h (aarch64_sve_get_sveregs): New + declaration. + (aarch64_sve_regs_copy_to_regcache): Likewise. + (aarch64_sve_regs_copy_from_regcache): Likewise. + (sve_context): Structure from Linux headers. + (SVE_SIG_ZREGS_SIZE): Define from Linux headers. + (SVE_SIG_ZREG_SIZE): Likewise. + (SVE_SIG_PREG_SIZE): Likewise. + (SVE_SIG_FFR_SIZE): Likewise. + (SVE_SIG_REGS_OFFSET): Likewise. + (SVE_SIG_ZREGS_OFFSET): Likewise. + (SVE_SIG_ZREG_OFFSET): Likewise. + (SVE_SIG_ZREGS_SIZE): Likewise. + (SVE_SIG_PREGS_OFFSET): Likewise. + (SVE_SIG_PREG_OFFSET): Likewise. + (SVE_SIG_PREGS_SIZE): Likewise. + (SVE_SIG_FFR_OFFSET): Likewise. + (SVE_SIG_REGS_SIZE): Likewise. + (SVE_SIG_CONTEXT_SIZE): Likewise. + (SVE_PT_REGS_MASK): Likewise. + (SVE_PT_REGS_FPSIMD): Likewise. + (SVE_PT_REGS_SVE): Likewise. + (SVE_PT_VL_INHERIT): Likewise. + (SVE_PT_VL_ONEXEC): Likewise. + (SVE_PT_REGS_OFFSET): Likewise. + (SVE_PT_FPSIMD_OFFSET): Likewise. + (SVE_PT_FPSIMD_SIZE): Likewise. + (SVE_PT_SVE_ZREG_SIZE): Likewise. + (SVE_PT_SVE_PREG_SIZE): Likewise. + (SVE_PT_SVE_FFR_SIZE): Likewise. + (SVE_PT_SVE_FPSR_SIZE): Likewise. + (SVE_PT_SVE_FPCR_SIZE): Likewise. + (__SVE_SIG_TO_PT): Likewise. + (SVE_PT_SVE_OFFSET): Likewise. + (SVE_PT_SVE_ZREGS_OFFSET): Likewise. + (SVE_PT_SVE_ZREG_OFFSET): Likewise. + (SVE_PT_SVE_ZREGS_SIZE): Likewise. + (SVE_PT_SVE_PREGS_OFFSET): Likewise. + (SVE_PT_SVE_PREG_OFFSET): Likewise. + (SVE_PT_SVE_PREGS_SIZE): Likewise. + (SVE_PT_SVE_FFR_OFFSET): Likewise. + (SVE_PT_SVE_FPSR_OFFSET): Likewise. + (SVE_PT_SVE_FPCR_OFFSET): Likewise. + (SVE_PT_SVE_SIZE): Likewise. + (SVE_PT_SIZE): Likewise. + (HAS_SVE_STATE): New define. + +2018-06-18 Alan Hayward <alan.hayward@arm.com> + * nat/aarch64-sve-linux-sigcontext.h: New file. * nat/aarch64-sve-linux-ptrace.h (SVE_VQ_BYTES): Move to new files. diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 1e4f937..1e7db29 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -384,19 +384,62 @@ store_fpregs_to_thread (const struct regcache *regcache) } } +/* Fill GDB's register array with the sve register values + from the current thread. */ + +static void +fetch_sveregs_from_thread (struct regcache *regcache) +{ + std::unique_ptr<gdb_byte[]> base + = aarch64_sve_get_sveregs (ptid_get_lwp (regcache->ptid ())); + aarch64_sve_regs_copy_to_reg_buf (regcache, base.get ()); +} + +/* Store to the current thread the valid sve register + values in the GDB's register array. */ + +static void +store_sveregs_to_thread (struct regcache *regcache) +{ + int ret; + struct iovec iovec; + int tid = ptid_get_lwp (regcache->ptid ()); + + /* Obtain a dump of SVE registers from ptrace. */ + std::unique_ptr<gdb_byte[]> base = aarch64_sve_get_sveregs (tid); + + /* Overwrite with regcache state. */ + aarch64_sve_regs_copy_from_reg_buf (regcache, base.get ()); + + /* Write back to the kernel. */ + iovec.iov_base = base.get (); + iovec.iov_len = ((struct user_sve_header *) base.get ())->size; + ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec); + + if (ret < 0) + perror_with_name (_("Unable to store sve registers")); +} + /* Implement the "fetch_registers" target_ops method. */ void aarch64_linux_nat_target::fetch_registers (struct regcache *regcache, int regno) { + struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ()); + if (regno == -1) { fetch_gregs_from_thread (regcache); - fetch_fpregs_from_thread (regcache); + if (tdep->has_sve ()) + fetch_sveregs_from_thread (regcache); + else + fetch_fpregs_from_thread (regcache); } else if (regno < AARCH64_V0_REGNUM) fetch_gregs_from_thread (regcache); + else if (tdep->has_sve ()) + fetch_sveregs_from_thread (regcache); else fetch_fpregs_from_thread (regcache); } @@ -407,13 +450,20 @@ void aarch64_linux_nat_target::store_registers (struct regcache *regcache, int regno) { + struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ()); + if (regno == -1) { store_gregs_to_thread (regcache); - store_fpregs_to_thread (regcache); + if (tdep->has_sve ()) + store_sveregs_to_thread (regcache); + else + store_fpregs_to_thread (regcache); } else if (regno < AARCH64_V0_REGNUM) store_gregs_to_thread (regcache); + else if (tdep->has_sve ()) + store_sveregs_to_thread (regcache); else store_fpregs_to_thread (regcache); } diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index ac1eb1c..a1c72b2 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,7 @@ +2018-06-18 Alan Hayward <alan.hayward@arm.com> + + * Makefile.in: Add aarch64-sve-linux-ptrace.c. + 2018-06-11 Alan Hayward <alan.hayward@arm.com> * linux-aarch64-ipa.c (get_ipa_tdesc): Add null VQ param. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index cf04b7d..513f286 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -219,6 +219,7 @@ SFILES = \ $(srcdir)/common/tdesc.c \ $(srcdir)/common/vec.c \ $(srcdir)/common/xml-utils.c \ + $(srcdir)/nat/aarch64-sve-linux-ptrace.c \ $(srcdir)/nat/linux-btrace.c \ $(srcdir)/nat/linux-namespaces.c \ $(srcdir)/nat/linux-osdata.c \ diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c index 119656b..a2f9261 100644 --- a/gdb/nat/aarch64-sve-linux-ptrace.c +++ b/gdb/nat/aarch64-sve-linux-ptrace.c @@ -24,6 +24,10 @@ #include "elf/common.h" #include "aarch64-sve-linux-ptrace.h" #include "arch/aarch64.h" +#include "common-regcache.h" +#include "common/byte-vector.h" + +static bool vq_change_warned = false; /* See nat/aarch64-sve-linux-ptrace.h. */ @@ -46,7 +50,7 @@ aarch64_sve_get_vq (int tid) return 0; } - long vq = sve_vq_from_vl (header.vl); + uint64_t vq = sve_vq_from_vl (header.vl); if (!sve_vl_valid (header.vl)) { @@ -56,3 +60,266 @@ aarch64_sve_get_vq (int tid) return vq; } + +/* See nat/aarch64-sve-linux-ptrace.h. */ + +std::unique_ptr<gdb_byte[]> +aarch64_sve_get_sveregs (int tid) +{ + struct iovec iovec; + struct user_sve_header header; + uint64_t vq = aarch64_sve_get_vq (tid); + + if (vq == 0) + perror_with_name (_("Unable to fetch SVE register header")); + + /* A ptrace call with NT_ARM_SVE will return a header followed by either a + dump of all the SVE and FP registers, or an fpsimd structure (identical to + the one returned by NT_FPREGSET) if the kernel has not yet executed any + SVE code. Make sure we allocate enough space for a full SVE dump. */ + + iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + std::unique_ptr<gdb_byte[]> buf (new gdb_byte[iovec.iov_len]); + iovec.iov_base = buf.get (); + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + perror_with_name (_("Unable to fetch SVE registers")); + + return buf; +} + +/* See nat/aarch64-sve-linux-ptrace.h. */ + +void +aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, + const void *buf) +{ + char *base = (char *) buf; + struct user_sve_header *header = (struct user_sve_header *) buf; + uint64_t vq, vg_reg_buf = 0; + + vq = sve_vq_from_vl (header->vl); + + /* Sanity check the data in the header. */ + if (!sve_vl_valid (header->vl) + || SVE_PT_SIZE (vq, header->flags) != header->size) + error (_("Invalid SVE header from kernel.")); + + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM)) + reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); + + if (vg_reg_buf == 0) + { + /* VG has not been set. */ + vg_reg_buf = sve_vg_from_vl (header->vl); + reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); + } + else if (vg_reg_buf != sve_vg_from_vl (header->vl) && !vq_change_warned) + { + /* Vector length on the running process has changed. GDB currently does + not support this and will result in GDB showing incorrect partially + incorrect data for the vector registers. Warn once and continue. We + do not expect many programs to exhibit this behaviour. To fix this + we need to spot the change earlier and generate a new target + descriptor. */ + warning (_("SVE Vector length has changed (%ld to %d). " + "Vector registers may show incorrect data."), + vg_reg_buf, sve_vg_from_vl (header->vl)); + vq_change_warned = true; + } + + if (HAS_SVE_STATE (*header)) + { + /* The register dump contains a set of SVE registers. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + reg_buf->raw_supply (AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + reg_buf->raw_supply (AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + } + else + { + /* 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)); + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + /* 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); + } + + reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + + /* Clear the SVE only registers. */ + + 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_FFR_REGNUM, zero_reg); + } +} + +/* See nat/aarch64-sve-linux-ptrace.h. */ + +void +aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, + void *buf) +{ + struct user_sve_header *header = (struct user_sve_header *) buf; + char *base = (char *) buf; + uint64_t vq, vg_reg_buf = 0; + + vq = sve_vq_from_vl (header->vl); + + /* Sanity check the data in the header. */ + if (!sve_vl_valid (header->vl) + || SVE_PT_SIZE (vq, header->flags) != header->size) + error (_("Invalid SVE header from kernel.")); + + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM)) + reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); + + if (vg_reg_buf != 0 && vg_reg_buf != sve_vg_from_vl (header->vl)) + { + /* Vector length on the running process has changed. GDB currently does + not support this and will result in GDB writing invalid data back to + the vector registers. Error and exit. We do not expect many programs + to exhibit this behaviour. To fix this we need to spot the change + earlier and generate a new target descriptor. */ + error (_("SVE Vector length has changed (%ld to %d). " + "Cannot write back registers."), + vg_reg_buf, sve_vg_from_vl (header->vl)); + } + + if (!HAS_SVE_STATE (*header)) + { + /* There is no SVE state yet - the register dump contains a fpsimd + structure instead. Where possible we want to write the reg_buf data + back to the kernel using the fpsimd structure. However, if we cannot + then we'll need to reformat the fpsimd into a full SVE structure, + resulting in the initialization of SVE state written back to the + 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)); + struct user_fpsimd_state *fpsimd + = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); + + memset (zero_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. */ + + 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)); + if (has_sve_state) + break; + } + + if (!has_sve_state) + 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); + if (has_sve_state) + break; + } + + if (!has_sve_state) + has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM, + zero_reg, 0); + + /* If no SVE state exists, then use the existing fpsimd structure to + write out state and return. */ + if (!has_sve_state) + { + /* 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 + collecting every register. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + { + 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)); + } + } + + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); + + return; + } + + /* Otherwise, reformat the fpsimd structure into a full SVE set, by + expanding the V registers (working backwards so we don't splat + registers before they are copied) and using null for everything else. + Note that enough space for a full SVE dump was originally allocated + for base. */ + + header->flags |= SVE_PT_REGS_SVE; + header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); + + memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, + sizeof (uint32_t)); + memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, + sizeof (uint32_t)); + + for (int i = AARCH64_SVE_Z_REGS_NUM; i >= 0 ; i--) + { + memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i], + sizeof (__int128_t)); + } + } + + /* Replace the kernel values with those from reg_buf. */ + + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) + reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, + base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); + + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM + i)) + reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i, + base + SVE_PT_SVE_PREG_OFFSET (vq, i)); + + if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM)) + reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM, + base + SVE_PT_SVE_FFR_OFFSET (vq)); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPSR_REGNUM, + base + SVE_PT_SVE_FPSR_OFFSET (vq)); + if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) + reg_buf->raw_collect (AARCH64_FPCR_REGNUM, + base + SVE_PT_SVE_FPCR_OFFSET (vq)); + +} diff --git a/gdb/nat/aarch64-sve-linux-ptrace.h b/gdb/nat/aarch64-sve-linux-ptrace.h index 7f5a81f..5a7186b 100644 --- a/gdb/nat/aarch64-sve-linux-ptrace.h +++ b/gdb/nat/aarch64-sve-linux-ptrace.h @@ -29,9 +29,30 @@ #include "aarch64-sve-linux-sigcontext.h" #endif +/* Indicates whether a SVE ptrace header is followed by SVE registers or a + fpsimd structure. */ + +#define HAS_SVE_STATE(header) ((header).flags && SVE_PT_REGS_SVE) + /* Read VQ for the given tid using ptrace. If SVE is not supported then zero is returned (on a system that supports SVE, then VQ cannot be zero). */ uint64_t aarch64_sve_get_vq (int tid); +/* Read the current SVE register set using ptrace, allocating space as + required. */ + +extern std::unique_ptr<gdb_byte[]> aarch64_sve_get_sveregs (int tid); + +/* Put the registers from linux structure buf into register buffer. */ + +extern void aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, + const void *buf); + +/* Put the registers from register buffer into linux structure buf. */ + +extern void +aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, + void *buf); + #endif /* aarch64-sve-linux-ptrace.h */ |