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/?a=commitdiff_plain;h=698c89836e269c33f61897d00479982670d2193a;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/ChangeLog b/gdb/ChangeLog index 34b102220a6..8c634d9fa9d 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +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. + 2020-10-20 Luis Machado * aarch64-linux-nat.c (aarch64_linux_nat_target::read_description): diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 7adc6075b7a..7e1763e8eac 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -32,6 +32,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" @@ -459,6 +460,56 @@ fetch_pauth_masks_from_thread (struct regcache *regcache) &pauth_regset[1]); } +/* 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 @@ -477,7 +528,13 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache, if (tdep->has_pauth ()) fetch_pauth_masks_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 ()) @@ -508,9 +565,25 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache, store_sveregs_to_thread (regcache); else store_fpregs_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 3819b7ab09d..46a0a25f953 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 0f25e97e4cc..c9c9e262dd4 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -3354,12 +3354,20 @@ aarch64_cannot_store_register (struct gdbarch *gdbarch, int regnum) { struct gdbarch_tdep *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; } /* Initialize the current architecture based on INFO. If possible, @@ -3492,6 +3500,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) { @@ -3500,10 +3509,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) @@ -3525,6 +3535,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) : pauth_ra_state_offset + num_regs; 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 d0d85f4ce7b..3a2c30c47f3 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -108,7 +108,10 @@ struct gdbarch_tdep return pauth_reg_base != -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..7fd0ec9aaf9 100644 --- a/gdb/arch/aarch64-cap-linux.h +++ b/gdb/arch/aarch64-cap-linux.h @@ -23,4 +23,11 @@ /* Morello HWCAP bit. */ #define HWCAP2_MORELLO (1 << 19) +/* 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_LINUX_H */ diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c index a2a571f2b23..38cf34d2094 100644 --- a/gdb/arch/aarch64.c +++ b/gdb/arch/aarch64.c @@ -49,10 +49,6 @@ aarch64_create_target_description (uint64_t vq, bool pauth_p, if (pauth_p) regnum = create_feature_aarch64_pauth (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 (capability_p) regnum = create_feature_aarch64_capability (tdesc.get (), regnum); diff --git a/gdb/nat/aarch64-linux.h b/gdb/nat/aarch64-linux.h index 3c4481a978d..3ac3ac350f4 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/ChangeLog b/gdbserver/ChangeLog index d4f6c68e4a7..7a4aa4eed27 100644 --- a/gdbserver/ChangeLog +++ b/gdbserver/ChangeLog @@ -1,3 +1,10 @@ +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. + 2020-10-20 Luis Machado * linux-aarch64-ipa.cc (get_ipa_tdesc): Update. diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index d9fa43a67e0..940399d381b 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" @@ -260,13 +261,43 @@ aarch64_store_pauthregset (struct regcache *regcache, const void *buf) &pauth_regset[1]); } +/* 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) @@ -682,6 +713,10 @@ static struct regset_info aarch64_regsets[] = { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK, AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS, NULL, aarch64_store_pauthregset }, + /* FIXME-Morello: Fixup the register set size. */ + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MORELLO, + AARCH64_LINUX_CREGS_SIZE, OPTIONAL_REGS, + nullptr, aarch64_store_cregset }, NULL_REGSET }; @@ -711,6 +746,10 @@ static struct regset_info aarch64_sve_regsets[] = { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK, AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS, NULL, aarch64_store_pauthregset }, + /* FIXME-Morello: Fixup the register set size. */ + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MORELLO, + AARCH64_LINUX_CREGS_SIZE, OPTIONAL_REGS, + nullptr, aarch64_store_cregset }, NULL_REGSET }; diff --git a/include/ChangeLog b/include/ChangeLog index 86f78d47141..1093514929c 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 b3c30e0e2f7..3980a0749ff 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -660,6 +660,9 @@ /* note name must be "LINUX". */ #define NT_ARM_PAC_MASK 0x406 /* AArch pointer authentication code masks */ /* 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_SIGINFO 0x53494749 /* Fields of siginfo_t. */