]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[Morello] Add capability register set support
authorLuis Machado <luis.machado@arm.com>
Thu, 5 Mar 2020 17:02:22 +0000 (14:02 -0300)
committerJohn Baldwin <jhb@FreeBSD.org>
Thu, 1 Sep 2022 22:53:22 +0000 (15:53 -0700)
This patch adds capability register set support to both GDB and GDBserver,
allowing the use of ptrace.

gdb/ChangeLog

2020-10-20  Luis Machado  <luis.machado@arm.com>

* aarch64-linux-nat.c: Include arch/aarch64-cap-linux.h.
(fetch_cregs_from_thread)
(store_cregs_to_thread): New functions.
(aarch64_linux_nat_target::fetch_registers): Modify to check for
capability registers.
* aarch64-linux-tdep.c: Include arch/aarch64-cap-linux.h.
* aarch64-tdep.c (aarch64_cannot_store_register): Check for capability
registers.
(aarch64_gdbarch_init): Also save the last capability register number.
* aarch64-tdep.h (struct gdbarch_tdep) <cap_reg_last>: New field.
* arch/aarch64-cap-linux.h (AARCH64_LINUX_CREGS_SIZE,
AARCH64_MORELLO_REGS_NUM, AARCH64_C_REGS_NUM): New constants.
* arch/aarch64.c: Remove FIXME comment.
* nat/aarch64-linux.h (user_morello_state): New struct.

gdbserver/ChangeLog

2020-10-20  Luis Machado  <luis.machado@arm.com>

* linux-aarch64-low.c: arch/aarch64-cap-linux.h.
(aarch64_store_cregset): New function.
(aarch64_regsets): Add capability register set.
(aarch64_sve_regsets): Likewise.

include/ChangeLog

2020-10-20  Luis Machado  <luis.machado@arm.com>

* elf/common.h (NT_ARM_MORELLO): Define.

gdb/aarch64-linux-nat.c
gdb/aarch64-linux-tdep.c
gdb/aarch64-tdep.c
gdb/aarch64-tdep.h
gdb/arch/aarch64-cap-linux.h
gdb/arch/aarch64.c
gdb/nat/aarch64-linux.h
gdbserver/linux-aarch64-low.cc
include/ChangeLog
include/elf/common.h

index b429fa31a03b3b1484b9b5b8692ca8676cdc5f3e..3b62a8fd503ae643bbb060ea54196d9f81ff8fad 100644 (file)
@@ -33,6 +33,7 @@
 #include "aarch32-linux-nat.h"
 #include "aarch32-tdep.h"
 #include "arch/arm.h"
+#include "arch/aarch64-cap-linux.h"
 #include "nat/aarch64-linux.h"
 #include "nat/aarch64-linux-hw-point.h"
 #include "nat/aarch64-sve-linux-ptrace.h"
@@ -485,6 +486,56 @@ store_tlsregs_to_thread (struct regcache *regcache)
     perror_with_name (_("unable to store TLS register."));
 }
 
+/* Fill GDB's register array with the capability register values
+   from the current thread.  */
+
+static void
+fetch_cregs_from_thread (struct regcache *regcache)
+{
+  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 ());
+
+  /* Fetch 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_supply (regno, &cregset.cregs[i]);
+
+  /* Fetch the other registers.  */
+  regcache->raw_supply (regno++, &cregset.pcc);
+  regcache->raw_supply (regno++, &cregset.csp);
+  regcache->raw_supply (regno++, &cregset.ddc);
+  regcache->raw_supply (regno++, &cregset.ctpidr);
+  regcache->raw_supply (regno++, &cregset.rcsp);
+  regcache->raw_supply (regno++, &cregset.rddc);
+  regcache->raw_supply (regno++, &cregset.rctpidr);
+  regcache->raw_supply (regno++, &cregset.cid);
+  regcache->raw_supply (regno++, &cregset.tag_map);
+  regcache->raw_supply (regno++, &cregset.cctlr);
+}
+
+/* Store to the current thread the valid capability register
+   values in the GDB's register array.  */
+
+static void
+store_cregs_to_thread (const struct regcache *regcache)
+{
+  /* Can't modify capability registers, do nothing.  */
+}
+
 /* Implement the "fetch_registers" target_ops method.  */
 
 void
@@ -510,7 +561,13 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache,
 
       if (tdep->has_tls ())
        fetch_tlsregs_from_thread (regcache);
+
+      if (tdep->has_capability ())
+       fetch_cregs_from_thread (regcache);
     }
+  else if (tdep->has_capability () && regno >= tdep->cap_reg_base
+          && regno < tdep->cap_reg_base + AARCH64_MORELLO_REGS_NUM)
+    fetch_cregs_from_thread (regcache);
   else if (regno < AARCH64_V0_REGNUM)
     fetch_gregs_from_thread (regcache);
   else if (tdep->has_sve ())
@@ -556,9 +613,25 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache,
 
       if (tdep->has_tls ())
        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);
+       }
     }
+  else if (tdep->has_capability () && regno >= tdep->cap_reg_base
+          && regno < tdep->cap_reg_base + AARCH64_MORELLO_REGS_NUM)
+    store_cregs_to_thread (regcache);
   else if (regno < AARCH64_V0_REGNUM)
-    store_gregs_to_thread (regcache);
+    {
+      store_gregs_to_thread (regcache);
+      fetch_cregs_from_thread (regcache);
+    }
   else if (tdep->has_sve ())
     store_sveregs_to_thread (regcache);
   else
index f63759f98c6011e92fc0f80df650df3c22acba7d..10f0bf7291631d3030175a604a89a3a6ff8d1ce8 100644 (file)
@@ -25,6 +25,7 @@
 #include "linux-tdep.h"
 #include "aarch64-tdep.h"
 #include "aarch64-linux-tdep.h"
+#include "arch/aarch64-cap-linux.h"
 #include "osabi.h"
 #include "solib-svr4.h"
 #include "symtab.h"
index 14974230f0ed04bfc1c45677754d4a4893299d05..a9a4f538a71f35ca631178e53a1f1baac8cce468 100644 (file)
@@ -3406,12 +3406,20 @@ aarch64_cannot_store_register (struct gdbarch *gdbarch, int regnum)
 {
   aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
 
-  if (!tdep->has_pauth ())
-    return 0;
+  if (tdep->has_pauth ())
+    {
+      /* Pointer authentication registers are read-only.  */
+      return (regnum == AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base)
+             || regnum == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base));
+    }
 
-  /* Pointer authentication registers are read-only.  */
-  return (regnum == AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base)
-         || 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;
 }
 
 /* Implement the stack_frame_destroyed_p gdbarch method.  */
@@ -3600,6 +3608,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   const struct tdesc_feature *feature_capability
       = tdesc_find_feature (tdesc,"org.gnu.gdb.aarch64.capability");
   int first_cap_regnum = -1;
+  int last_cap_regnum = -1;
 
   if (feature_capability != nullptr)
     {
@@ -3608,10 +3617,11 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       for (i = 0; i < ARRAY_SIZE (aarch64_c_register_names); i++)
        valid_p &= tdesc_numbered_register (feature_capability,
                                            tdesc_data.get (),
-                                           AARCH64_C0_REGNUM + i,
+                                           first_cap_regnum + i,
                                            aarch64_c_register_names[i]);
 
-      num_regs = AARCH64_C0_REGNUM + i;
+      last_cap_regnum = first_cap_regnum + i - 1;
+      num_regs += i;
     }
 
   if (!valid_p)
@@ -3635,6 +3645,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->tls_regnum = tls_regnum;
 
   tdep->cap_reg_base = first_cap_regnum;
+  tdep->cap_reg_last = last_cap_regnum;
 
   set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
index 06b2b674f6a77874638aaaaaaca74bb60911e692..e847a42cbe60afb048852d121defbf15e9cb6254 100644 (file)
@@ -127,7 +127,10 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep
     return tls_regnum != -1;
   }
 
+  /* First register from the capability set.  */
   int cap_reg_base;
+  /* Last register from the capability set.  */
+  int cap_reg_last;
 
   /* Returns true if the target supports capabilities.  */
   bool has_capability () const
index 1445d4f0ce9e448cf5c8459c46d61bfe955b8e83..f62bc7f2f8dc6c855a78b5f0ceb58149192d1d54 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifndef ARCH_AARCH64_LINUX_H
-#define ARCH_AARCH64_LINUX_H
+#ifndef ARCH_AARCH64_CAP_LINUX_H
+#define ARCH_AARCH64_CAP_LINUX_H
 
 /* Morello HWCAP bit.  */
 #define HWCAP2_MORELLO (1 << 19)
 
-#endif /*ARCH_AARCH64_LINUX_H */
+/* Size of the Capability register set.  */
+#define AARCH64_LINUX_CREGS_SIZE ((39 * 16) + (2 * 8))
+
+/* 39 128-bit C registers plus 2 64-bit registers.  */
+#define AARCH64_MORELLO_REGS_NUM 41
+#define AARCH64_C_REGS_NUM 31
+
+#endif /*ARCH_AARCH64_CAP_LINUX_H */
index edfd8f5710fbdba36895696a6b878ce5e6771593..ae51ec36299f1f66a3cccf910c00099fa277d3a7 100644 (file)
@@ -57,10 +57,6 @@ aarch64_create_target_description (const aarch64_features &features)
   if (features.tls)
     regnum = create_feature_aarch64_tls (tdesc.get (), regnum);
 
-  /* FIXME-Morello: We need to append the capability registers to
-     the existing target description.  Figure out how to do that.
-     Maybe replace the general purpose register description with
-     the capability registers.  */
   if (features.capability)
     regnum = create_feature_aarch64_capability (tdesc.get (), regnum);
 
index 1777ce3252231f4eebfa3fbe7e17cd61a0458a41..6104dfa8ef2f57aafd63c4f32a1ab3400dda179d 100644 (file)
 /* Defines ps_err_e, struct ps_prochandle.  */
 #include "gdb_proc_service.h"
 
+/* Struct defining the layout of the capability register set.  */
+struct user_morello_state {
+       /* General capability registers.  */
+       unsigned __int128 cregs[31];
+       /* Capability program counter.  */
+       unsigned __int128 pcc;
+       /* Capability stack pointer.  */
+       unsigned __int128 csp;
+       /* Default data capability.  */
+       unsigned __int128 ddc;
+       unsigned __int128 ctpidr;
+       unsigned __int128 rcsp;
+       unsigned __int128 rddc;
+       unsigned __int128 rctpidr;
+       /* Compartment ID register.  */
+       unsigned __int128 cid;
+       /* Bitmap storing the tags of all the capability registers.
+          The tag for register <reg> is stored at bit index
+          MORELLO_PT_TAG_MAP_REG_BIT(<reg>) in tag_map.  */
+       uint64_t        tag_map;
+       /* Capability control register.  */
+       uint64_t        cctlr;
+};
+
 typedef int compat_int_t;
 typedef unsigned int compat_uptr_t;
 
index 9f7a636fc425c2e942425311185a21ffe38fb979..5d5dc817b0f682c4a4b84af117fc2de578e65ee9 100644 (file)
@@ -23,6 +23,7 @@
 #include "linux-low.h"
 #include "nat/aarch64-linux.h"
 #include "nat/aarch64-linux-hw-point.h"
+#include "arch/aarch64-cap-linux.h"
 #include "arch/aarch64-insn.h"
 #include "linux-aarch32-low.h"
 #include "elf/common.h"
@@ -307,13 +308,43 @@ aarch64_store_tlsregset (struct regcache *regcache, const void *buf)
   supply_register (regcache, tls_regnum, buf);
 }
 
+/* Capability registers store hook implementation.  */
+
+static void
+aarch64_store_cregset (struct regcache *regcache, const void *buf)
+{
+  const struct user_morello_state *cregset
+      = (const struct user_morello_state *) buf;
+
+  int cregs_base = find_regno (regcache->tdesc, "c0");
+
+  /* Fetch the C registers.  */
+  int i, regno;
+  for (regno = cregs_base, i = 0;
+       regno < cregs_base + AARCH64_C_REGS_NUM;
+       regno++, i++)
+    supply_register (regcache, regno, &cregset->cregs[i]);
+
+  /* Fetch the other registers.  */
+  supply_register (regcache, regno++, &cregset->pcc);
+  supply_register (regcache, regno++, &cregset->csp);
+  supply_register (regcache, regno++, &cregset->ddc);
+  supply_register (regcache, regno++, &cregset->ctpidr);
+  supply_register (regcache, regno++, &cregset->rcsp);
+  supply_register (regcache, regno++, &cregset->rddc);
+  supply_register (regcache, regno++, &cregset->rctpidr);
+  supply_register (regcache, regno++, &cregset->cid);
+  supply_register (regcache, regno++, &cregset->tag_map);
+  supply_register (regcache, regno++, &cregset->cctlr);
+}
+
 bool
 aarch64_target::low_supports_breakpoints ()
 {
   return true;
 }
 
-/* Implementation of linux target ops method "low_get_pc".  */
+/* Implementation of linux_target_ops method "get_pc".  */
 
 CORE_ADDR
 aarch64_target::low_get_pc (regcache *regcache)
@@ -743,6 +774,10 @@ static struct regset_info aarch64_regsets[] =
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TLS,
     0, OPTIONAL_REGS,
     aarch64_fill_tlsregset, aarch64_store_tlsregset },
+  /* FIXME-Morello: Fixup the register set size.  */
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MORELLO,
+    0, OPTIONAL_REGS,
+    nullptr, aarch64_store_cregset },
   NULL_REGSET
 };
 
@@ -798,6 +833,10 @@ aarch64_adjust_register_sets (const struct aarch64_features &features)
          if (features.tls)
            regset->size = AARCH64_TLS_REGS_SIZE;
          break;
+       case NT_ARM_MORELLO:
+         if (features.capability)
+           regset->size = AARCH64_LINUX_CREGS_SIZE;
+         break;
        default:
          gdb_assert_not_reached ("Unknown register set found.");
        }
index 9b58eefcfbfa8b965d95283d74810c741a6c79d3..a28667ae61fe812c831248d349005d5f9d63518c 100644 (file)
@@ -1,3 +1,7 @@
+2020-10-20  Luis Machado  <luis.machado@arm.com>
+
+       * elf/common.h (NT_ARM_MORELLO): Define.
+
 2020-10-20  Luis Machado  <luis.machado@arm.com>
 
        * dwarf2.def (DW_ATE_CHERI_signed_intcap)
index ad62a7d85236f71a9eebc7ad694bb84cff3cbd83..bb449409257ec42d4fe078b538aa61de10bc5bbf 100644 (file)
 #define NT_ARM_PAC_ENABLED_KEYS        0x40a   /* AArch64 pointer authentication
                                           enabled keys (prctl()) */
                                        /*   note name must be "LINUX".  */
+#define NT_ARM_MORELLO 0x410           /* AArch capability registers */
+                                       /* Note name must be "LINUX".  */
+
 #define NT_ARC_V2      0x600           /* ARC HS accumulator/extra registers.  */
                                        /*   note name must be "LINUX".  */
 #define NT_LARCH_CPUCFG 0xa00          /* LoongArch CPU config registers */