From: Luis Machado Date: Thu, 5 Mar 2020 17:02:22 +0000 (-0300) Subject: [Morello] Add capability register set support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57676e189376008cc404d03fa62841e6b87d8e54;p=thirdparty%2Fbinutils-gdb.git [Morello] Add capability register set support This patch adds capability register set support to both GDB and GDBserver, allowing the use of ptrace. gdb/ChangeLog 2020-10-20 Luis Machado * 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) : 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 * 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 * elf/common.h (NT_ARM_MORELLO): Define. --- diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index b429fa31a03..3b62a8fd503 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -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 diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index f63759f98c6..10f0bf72916 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -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" diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 14974230f0e..a9a4f538a71 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -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); diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 06b2b674f6a..e847a42cbe6 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -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 diff --git a/gdb/arch/aarch64-cap-linux.h b/gdb/arch/aarch64-cap-linux.h index 1445d4f0ce9..f62bc7f2f8d 100644 --- a/gdb/arch/aarch64-cap-linux.h +++ b/gdb/arch/aarch64-cap-linux.h @@ -17,10 +17,17 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#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 */ diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c index edfd8f5710f..ae51ec36299 100644 --- a/gdb/arch/aarch64.c +++ b/gdb/arch/aarch64.c @@ -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); diff --git a/gdb/nat/aarch64-linux.h b/gdb/nat/aarch64-linux.h index 1777ce32522..6104dfa8ef2 100644 --- a/gdb/nat/aarch64-linux.h +++ b/gdb/nat/aarch64-linux.h @@ -24,6 +24,30 @@ /* 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 is stored at bit index + MORELLO_PT_TAG_MAP_REG_BIT() in tag_map. */ + uint64_t tag_map; + /* Capability control register. */ + uint64_t cctlr; +}; + typedef int compat_int_t; typedef unsigned int compat_uptr_t; diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index 9f7a636fc42..5d5dc817b0f 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -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."); } diff --git a/include/ChangeLog b/include/ChangeLog index 9b58eefcfbf..a28667ae61f 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2020-10-20 Luis Machado + + * elf/common.h (NT_ARM_MORELLO): Define. + 2020-10-20 Luis Machado * dwarf2.def (DW_ATE_CHERI_signed_intcap) diff --git a/include/elf/common.h b/include/elf/common.h index ad62a7d8523..bb449409257 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -691,6 +691,9 @@ #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 */