]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Enable Morello register set writes
authorLuis Machado <luis.machado@linaro.org>
Mon, 11 Jan 2021 17:18:36 +0000 (14:18 -0300)
committerJohn Baldwin <jhb@FreeBSD.org>
Thu, 1 Sep 2022 22:57:21 +0000 (15:57 -0700)
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  <luis.machado@arm.com>

* 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  <luis.machado@arm.com>

* linux-aarch64-low.cc (aarch64_fill_cregset): New function.
(aarch64_regsets, aarch64_sve_regsets): Add C registers fill hook.

gdb/aarch64-linux-nat.c
gdb/aarch64-tdep.c
gdb/regcache.c
gdbserver/linux-aarch64-low.cc

index cffd41455678d0a667291047a688edaa9ba1a762..0d9e16b23493f5369425a95702425bb3b4783e5e 100644 (file)
@@ -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);
index c805c50ac083266f29a73e0f3c93109ff2b0cbc9..e656cc562d1b3722425f6dfbd0ab0a9b6290960f 100644 (file)
@@ -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;
 }
 
index e676ab64d615145417798b4b235dba5aac548dbd..12daa98826de858355d0a44e95f307f8387e1b90 100644 (file)
@@ -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"));
index cd68eaecc354e8a0f25e48685c12d2ea48cb3e7c..c9c05c065500c31da9051417fbbc4da47259b0b1 100644 (file)
@@ -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
 };