From: Luis Machado Date: Mon, 11 Jan 2021 17:18:36 +0000 (-0300) Subject: Enable Morello register set writes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=39934dda8041b707b0678e796b20fad116077a75;p=thirdparty%2Fbinutils-gdb.git Enable Morello register set writes Make the Morello register set writable, so GDB can write to the C registers and forge capabilities. To enable write access to the registers, the cheri.ptrace_forge_cap sysctl option must be toggled. gdb/ChangeLog: 2021-01-15 Luis Machado * aarch64-linux-nat.c (store_cregs_to_thread): Implement. (aarch64_linux_nat_target::store_registers): Don't fetch the C registers before writing to them. Fetch GPR's after modifying the C registers. * aarch64-tdep.c (aarch64_cannot_store_register): Remove C registers write restriction. * regcache.c (regcache_write_pc): Handle partial writes to PCC. gdbserver/ChangeLog: 2021-01-15 Luis Machado * linux-aarch64-low.cc (aarch64_fill_cregset): New function. (aarch64_regsets, aarch64_sve_regsets): Add C registers fill hook. --- diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index cffd4145567..0d9e16b2349 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -536,7 +536,42 @@ fetch_cregs_from_thread (struct regcache *regcache) static void store_cregs_to_thread (const struct regcache *regcache) { - /* Can't modify capability registers, do nothing. */ + struct gdbarch *gdbarch = regcache->arch (); + gdb_assert (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 64); + + int tid = regcache->ptid ().lwp (); + + struct user_morello_state cregset; + struct iovec iovec; + iovec.iov_base = &cregset; + iovec.iov_len = sizeof (cregset); + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_MORELLO, &iovec) < 0) + perror_with_name (_("Unable to fetch capability registers.")); + + struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ()); + + /* Stored the C registers. */ + int regno, i; + for (regno = tdep->cap_reg_base, i = 0; + regno < tdep->cap_reg_base + AARCH64_C_REGS_NUM; + regno++, i++) + regcache->raw_collect (regno, &cregset.cregs[i]); + + /* Store the other registers. */ + regcache->raw_collect (regno++, &cregset.csp); + regcache->raw_collect (regno++, &cregset.pcc); + regcache->raw_collect (regno++, &cregset.ddc); + regcache->raw_collect (regno++, &cregset.ctpidr); + regcache->raw_collect (regno++, &cregset.rcsp); + regcache->raw_collect (regno++, &cregset.rddc); + regcache->raw_collect (regno++, &cregset.rctpidr); + regcache->raw_collect (regno++, &cregset.cid); + regcache->raw_collect (regno++, &cregset.tag_map); + regcache->raw_collect (regno++, &cregset.cctlr); + + if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_MORELLO, &iovec) < 0) + perror_with_name (_("Unable to store capability registers.")); } /* Implement the "fetch_registers" target_ops method. */ @@ -618,18 +653,14 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache, store_tlsregs_to_thread (regcache); if (tdep->has_capability ()) - { - /* Due to the aliasing of X/C registers and due to register merging - by the kernel (see documentation in the kernel), we should force - a read of the C registers whenever the X registers are written - to. */ - fetch_cregs_from_thread (regcache); - store_cregs_to_thread (regcache); - } + store_cregs_to_thread (regcache); } else if (tdep->has_capability () && regno >= tdep->cap_reg_base && regno < tdep->cap_reg_base + AARCH64_MORELLO_REGS_NUM) - store_cregs_to_thread (regcache); + { + store_cregs_to_thread (regcache); + fetch_gregs_from_thread (regcache); + } else if (regno < AARCH64_V0_REGNUM) { store_gregs_to_thread (regcache); diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index c805c50ac08..e656cc562d1 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -3802,12 +3802,6 @@ aarch64_cannot_store_register (struct gdbarch *gdbarch, int regnum) || regnum == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base)); } - if (tdep->has_capability ()) - { - /* Capability register set is read-only for now. */ - return (regnum >= tdep->cap_reg_base && regnum < tdep->cap_reg_last); - } - return 0; } diff --git a/gdb/regcache.c b/gdb/regcache.c index e676ab64d61..12daa98826d 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -1365,12 +1365,23 @@ void regcache_write_pc (struct regcache *regcache, CORE_ADDR pc) { struct gdbarch *gdbarch = regcache->arch (); + int regnum = gdbarch_pc_regnum (gdbarch); + int reg_size = register_size (gdbarch, regnum); if (gdbarch_write_pc_p (gdbarch)) gdbarch_write_pc (gdbarch, regcache, pc); else if (gdbarch_pc_regnum (gdbarch) >= 0) - regcache_cooked_write_unsigned (regcache, - gdbarch_pc_regnum (gdbarch), pc); + { + /* If our PC is bigger than a CORE_ADDR, only modify the required + bits. Leave the unmodified bits alone. */ + if (reg_size > sizeof (pc)) + regcache->cooked_write_part (regnum, 0, sizeof (pc), + (const gdb_byte *) &pc); + else + { + regcache_cooked_write_unsigned (regcache, regnum, pc); + } + } else internal_error (__FILE__, __LINE__, _("regcache_write_pc: Unable to update PC")); diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index cd68eaecc35..c9c05c06550 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -315,6 +315,36 @@ aarch64_store_tlsregset (struct regcache *regcache, const void *buf) supply_register (regcache, tls_regnum, buf); } +/* Capability registers fill hook implementation. */ + +static void +aarch64_fill_cregset (struct regcache *regcache, void *buf) +{ + struct user_morello_state *cregset + = (struct user_morello_state *) buf; + + int cregs_base = find_regno (regcache->tdesc, "c0"); + + /* Store the C registers to the buffer. */ + int i, regno; + for (regno = cregs_base, i = 0; + regno < cregs_base + AARCH64_C_REGS_NUM; + regno++, i++) + collect_register (regcache, regno, &cregset->cregs[i]); + + /* Store the other registers to the buffer. */ + collect_register (regcache, regno++, &cregset->csp); + collect_register (regcache, regno++, &cregset->pcc); + collect_register (regcache, regno++, &cregset->ddc); + collect_register (regcache, regno++, &cregset->ctpidr); + collect_register (regcache, regno++, &cregset->rcsp); + collect_register (regcache, regno++, &cregset->rddc); + collect_register (regcache, regno++, &cregset->rctpidr); + collect_register (regcache, regno++, &cregset->cid); + collect_register (regcache, regno++, &cregset->tag_map); + collect_register (regcache, regno++, &cregset->cctlr); +} + /* Capability registers store hook implementation. */ static void @@ -784,7 +814,7 @@ static struct regset_info aarch64_regsets[] = /* FIXME-Morello: Fixup the register set size. */ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MORELLO, 0, OPTIONAL_REGS, - nullptr, aarch64_store_cregset }, + aarch64_fill_cregset, aarch64_store_cregset }, NULL_REGSET };