X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fs390-linux-nat.c;h=9ec806f5b591e98a2042e6de2761a7da63928ef5;hb=refs%2Fheads%2Fconcurrent-displaced-stepping-2020-04-01;hp=45db7c998cb864409b0458977cb64354837d2cbb;hpb=9b44a3a57d17ea2d35823780007a38daeeaec6a4;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c index 45db7c998c..9ec806f5b5 100644 --- a/gdb/s390-linux-nat.c +++ b/gdb/s390-linux-nat.c @@ -1,5 +1,5 @@ /* S390 native-dependent code for GDB, the GNU debugger. - Copyright (C) 2001-2014 Free Software Foundation, Inc. + Copyright (C) 2001-2020 Free Software Foundation, Inc. Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) for IBM Deutschland Entwicklung GmbH, IBM Corporation. @@ -26,64 +26,125 @@ #include "linux-nat.h" #include "auxv.h" #include "gregset.h" +#include "regset.h" +#include "nat/linux-ptrace.h" +#include "gdbcmd.h" +#include "s390-tdep.h" #include "s390-linux-tdep.h" #include "elf/common.h" #include -#include +#include "nat/gdb_ptrace.h" #include #include #include #include +#include +#include "inf-ptrace.h" +#include "linux-tdep.h" +#include "gdbarch.h" -#ifndef PTRACE_GETREGSET -#define PTRACE_GETREGSET 0x4204 -#endif +/* Per-thread arch-specific data. */ -#ifndef PTRACE_SETREGSET -#define PTRACE_SETREGSET 0x4205 -#endif +struct arch_lwp_info +{ + /* Non-zero if the thread's PER info must be re-written. */ + int per_info_changed; +}; static int have_regset_last_break = 0; static int have_regset_system_call = 0; static int have_regset_tdb = 0; +static int have_regset_vxrs = 0; +static int have_regset_gs = 0; -/* Map registers to gregset/ptrace offsets. - These arrays are defined in s390-tdep.c. */ +/* Register map for 32-bit executables running under a 64-bit + kernel. */ #ifdef __s390x__ -#define regmap_gregset s390x_regmap_gregset -#else -#define regmap_gregset s390_regmap_gregset +static const struct regcache_map_entry s390_64_regmap_gregset[] = + { + /* Skip PSWM and PSWA, since they must be handled specially. */ + { 2, REGCACHE_MAP_SKIP, 8 }, + { 1, S390_R0_UPPER_REGNUM, 4 }, { 1, S390_R0_REGNUM, 4 }, + { 1, S390_R1_UPPER_REGNUM, 4 }, { 1, S390_R1_REGNUM, 4 }, + { 1, S390_R2_UPPER_REGNUM, 4 }, { 1, S390_R2_REGNUM, 4 }, + { 1, S390_R3_UPPER_REGNUM, 4 }, { 1, S390_R3_REGNUM, 4 }, + { 1, S390_R4_UPPER_REGNUM, 4 }, { 1, S390_R4_REGNUM, 4 }, + { 1, S390_R5_UPPER_REGNUM, 4 }, { 1, S390_R5_REGNUM, 4 }, + { 1, S390_R6_UPPER_REGNUM, 4 }, { 1, S390_R6_REGNUM, 4 }, + { 1, S390_R7_UPPER_REGNUM, 4 }, { 1, S390_R7_REGNUM, 4 }, + { 1, S390_R8_UPPER_REGNUM, 4 }, { 1, S390_R8_REGNUM, 4 }, + { 1, S390_R9_UPPER_REGNUM, 4 }, { 1, S390_R9_REGNUM, 4 }, + { 1, S390_R10_UPPER_REGNUM, 4 }, { 1, S390_R10_REGNUM, 4 }, + { 1, S390_R11_UPPER_REGNUM, 4 }, { 1, S390_R11_REGNUM, 4 }, + { 1, S390_R12_UPPER_REGNUM, 4 }, { 1, S390_R12_REGNUM, 4 }, + { 1, S390_R13_UPPER_REGNUM, 4 }, { 1, S390_R13_REGNUM, 4 }, + { 1, S390_R14_UPPER_REGNUM, 4 }, { 1, S390_R14_REGNUM, 4 }, + { 1, S390_R15_UPPER_REGNUM, 4 }, { 1, S390_R15_REGNUM, 4 }, + { 16, S390_A0_REGNUM, 4 }, + { 1, REGCACHE_MAP_SKIP, 4 }, { 1, S390_ORIG_R2_REGNUM, 4 }, + { 0 } + }; + +static const struct regset s390_64_gregset = + { + s390_64_regmap_gregset, + regcache_supply_regset, + regcache_collect_regset + }; + +#define S390_PSWM_OFFSET 0 +#define S390_PSWA_OFFSET 8 #endif -#define regmap_fpregset s390_regmap_fpregset +/* PER-event mask bits and PER control bits (CR9). */ -/* Fill the regset described by MAP into REGCACHE, using the values - from REGP. The MAP array represents each register as a pair - (offset, regno) of short integers and is terminated with -1. */ +#define PER_BIT(n) (1UL << (63 - (n))) +#define PER_EVENT_BRANCH PER_BIT (32) +#define PER_EVENT_IFETCH PER_BIT (33) +#define PER_EVENT_STORE PER_BIT (34) +#define PER_EVENT_NULLIFICATION PER_BIT (39) +#define PER_CONTROL_BRANCH_ADDRESS PER_BIT (40) +#define PER_CONTROL_SUSPENSION PER_BIT (41) +#define PER_CONTROL_ALTERATION PER_BIT (42) -static void -s390_native_supply (struct regcache *regcache, const short *map, - const gdb_byte *regp) +class s390_linux_nat_target final : public linux_nat_target { - for (; map[0] >= 0; map += 2) - regcache_raw_supply (regcache, map[1], regp ? regp + map[0] : NULL); -} +public: + /* Add our register access methods. */ + void fetch_registers (struct regcache *, int) override; + void store_registers (struct regcache *, int) override; -/* Collect the register REGNO out of the regset described by MAP from - REGCACHE into REGP. If REGNO == -1, do this for all registers in - this regset. */ + /* Add our watchpoint methods. */ + int can_use_hw_breakpoint (enum bptype, int, int) override; + int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) + override; + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) + override; + int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + bool stopped_by_watchpoint () override; + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; + int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; -static void -s390_native_collect (const struct regcache *regcache, const short *map, - int regno, gdb_byte *regp) -{ - for (; map[0] >= 0; map += 2) - if (regno == -1 || regno == map[1]) - regcache_raw_collect (regcache, map[1], regp + map[0]); -} + /* Detect target architecture. */ + const struct target_desc *read_description () override; + int auxv_parse (gdb_byte **readptr, + gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp) + override; + + /* Override linux_nat_target low methods. */ + void low_new_thread (struct lwp_info *lp) override; + void low_delete_thread (struct arch_lwp_info *lp) override; + void low_prepare_to_resume (struct lwp_info *lp) override; + void low_new_fork (struct lwp_info *parent, pid_t child_pid) override; + void low_forget_process (pid_t pid) override; +}; + +static s390_linux_nat_target the_s390_linux_nat_target; /* Fill GDB's register array with the general-purpose register values in *REGP. @@ -96,42 +157,30 @@ void supply_gregset (struct regcache *regcache, const gregset_t *regp) { #ifdef __s390x__ - struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch *gdbarch = regcache->arch (); if (gdbarch_ptr_bit (gdbarch) == 32) { enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - ULONGEST pswm = 0, pswa = 0; + ULONGEST pswm, pswa; gdb_byte buf[4]; - const short *map; - - for (map = regmap_gregset; map[0] >= 0; map += 2) - { - const gdb_byte *p = (const gdb_byte *) regp + map[0]; - int regno = map[1]; - - if (regno == S390_PSWM_REGNUM) - pswm = extract_unsigned_integer (p, 8, byte_order); - else if (regno == S390_PSWA_REGNUM) - pswa = extract_unsigned_integer (p, 8, byte_order); - else - { - if ((regno >= S390_R0_REGNUM && regno <= S390_R15_REGNUM) - || regno == S390_ORIG_R2_REGNUM) - p += 4; - regcache_raw_supply (regcache, regno, p); - } - } + regcache_supply_regset (&s390_64_gregset, regcache, -1, + regp, sizeof (gregset_t)); + pswm = extract_unsigned_integer ((const gdb_byte *) regp + + S390_PSWM_OFFSET, 8, byte_order); + pswa = extract_unsigned_integer ((const gdb_byte *) regp + + S390_PSWA_OFFSET, 8, byte_order); store_unsigned_integer (buf, 4, byte_order, (pswm >> 32) | 0x80000); - regcache_raw_supply (regcache, S390_PSWM_REGNUM, buf); + regcache->raw_supply (S390_PSWM_REGNUM, buf); store_unsigned_integer (buf, 4, byte_order, (pswa & 0x7fffffff) | (pswm & 0x80000000)); - regcache_raw_supply (regcache, S390_PSWA_REGNUM, buf); + regcache->raw_supply (S390_PSWA_REGNUM, buf); return; } #endif - s390_native_supply (regcache, regmap_gregset, (const gdb_byte *) regp); + regcache_supply_regset (&s390_gregset, regcache, -1, regp, + sizeof (gregset_t)); } /* Fill register REGNO (if it is a general-purpose register) in @@ -142,31 +191,11 @@ void fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno) { #ifdef __s390x__ - struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch *gdbarch = regcache->arch (); if (gdbarch_ptr_bit (gdbarch) == 32) { - gdb_byte *psw_p[2]; - const short *map; - - for (map = regmap_gregset; map[0] >= 0; map += 2) - { - gdb_byte *p = (gdb_byte *) regp + map[0]; - int reg = map[1]; - - if (reg >= S390_PSWM_REGNUM && reg <= S390_PSWA_REGNUM) - psw_p[reg - S390_PSWM_REGNUM] = p; - - else if (regno == -1 || regno == reg) - { - if ((reg >= S390_R0_REGNUM && reg <= S390_R15_REGNUM) - || reg == S390_ORIG_R2_REGNUM) - { - memset (p, 0, 4); - p += 4; - } - regcache_raw_collect (regcache, reg, p); - } - } + regcache_collect_regset (&s390_64_gregset, regcache, regno, + regp, sizeof (gregset_t)); if (regno == -1 || regno == S390_PSWM_REGNUM || regno == S390_PSWA_REGNUM) @@ -174,25 +203,36 @@ fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno) enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); ULONGEST pswa, pswm; gdb_byte buf[4]; + gdb_byte *pswm_p = (gdb_byte *) regp + S390_PSWM_OFFSET; + gdb_byte *pswa_p = (gdb_byte *) regp + S390_PSWA_OFFSET; - regcache_raw_collect (regcache, S390_PSWM_REGNUM, buf); - pswm = extract_unsigned_integer (buf, 4, byte_order); - regcache_raw_collect (regcache, S390_PSWA_REGNUM, buf); - pswa = extract_unsigned_integer (buf, 4, byte_order); + pswm = extract_unsigned_integer (pswm_p, 8, byte_order); if (regno == -1 || regno == S390_PSWM_REGNUM) - store_unsigned_integer (psw_p[0], 8, byte_order, - ((pswm & 0xfff7ffff) << 32) | - (pswa & 0x80000000)); + { + pswm &= 0x80000000; + regcache->raw_collect (S390_PSWM_REGNUM, buf); + pswm |= (extract_unsigned_integer (buf, 4, byte_order) + & 0xfff7ffff) << 32; + } + if (regno == -1 || regno == S390_PSWA_REGNUM) - store_unsigned_integer (psw_p[1], 8, byte_order, - pswa & 0x7fffffff); + { + regcache->raw_collect (S390_PSWA_REGNUM, buf); + pswa = extract_unsigned_integer (buf, 4, byte_order); + pswm ^= (pswm ^ pswa) & 0x80000000; + pswa &= 0x7fffffff; + store_unsigned_integer (pswa_p, 8, byte_order, pswa); + } + + store_unsigned_integer (pswm_p, 8, byte_order, pswm); } return; } #endif - s390_native_collect (regcache, regmap_gregset, regno, (gdb_byte *) regp); + regcache_collect_regset (&s390_gregset, regcache, regno, regp, + sizeof (gregset_t)); } /* Fill GDB's register array with the floating-point register values @@ -200,7 +240,8 @@ fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno) void supply_fpregset (struct regcache *regcache, const fpregset_t *regp) { - s390_native_supply (regcache, regmap_fpregset, (const gdb_byte *) regp); + regcache_supply_regset (&s390_fpregset, regcache, -1, regp, + sizeof (fpregset_t)); } /* Fill register REGNO (if it is a general-purpose register) in @@ -209,7 +250,8 @@ supply_fpregset (struct regcache *regcache, const fpregset_t *regp) void fill_fpregset (const struct regcache *regcache, fpregset_t *regp, int regno) { - s390_native_collect (regcache, regmap_fpregset, regno, (gdb_byte *) regp); + regcache_collect_regset (&s390_fpregset, regcache, regno, regp, + sizeof (fpregset_t)); } /* Find the TID for the current inferior thread to use with ptrace. */ @@ -217,9 +259,9 @@ static int s390_inferior_tid (void) { /* GNU/Linux LWP ID's are process ID's. */ - int tid = ptid_get_lwp (inferior_ptid); + int tid = inferior_ptid.lwp (); if (tid == 0) - tid = ptid_get_pid (inferior_ptid); /* Not a threaded program. */ + tid = inferior_ptid.pid (); /* Not a threaded program. */ return tid; } @@ -235,7 +277,7 @@ fetch_regs (struct regcache *regcache, int tid) parea.len = sizeof (regs); parea.process_addr = (addr_t) ®s; parea.kernel_addr = offsetof (struct user_regs_struct, psw); - if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't get registers")); supply_gregset (regcache, (const gregset_t *) ®s); @@ -252,12 +294,12 @@ store_regs (const struct regcache *regcache, int tid, int regnum) parea.len = sizeof (regs); parea.process_addr = (addr_t) ®s; parea.kernel_addr = offsetof (struct user_regs_struct, psw); - if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't get registers")); fill_gregset (regcache, ®s, regnum); - if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't write registers")); } @@ -272,7 +314,7 @@ fetch_fpregs (struct regcache *regcache, int tid) parea.len = sizeof (fpregs); parea.process_addr = (addr_t) &fpregs; parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs); - if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't get floating point status")); supply_fpregset (regcache, (const fpregset_t *) &fpregs); @@ -289,58 +331,59 @@ store_fpregs (const struct regcache *regcache, int tid, int regnum) parea.len = sizeof (fpregs); parea.process_addr = (addr_t) &fpregs; parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs); - if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't get floating point status")); fill_fpregset (regcache, &fpregs, regnum); - if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0) + if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea, 0) < 0) perror_with_name (_("Couldn't write floating point status")); } -/* Fetch all registers in the kernel's register set whose number is REGSET, - whose size is REGSIZE, and whose layout is described by REGMAP, from - process/thread TID and store their values in GDB's register cache. */ +/* Fetch all registers in the kernel's register set whose number is + REGSET_ID, whose size is REGSIZE, and whose layout is described by + REGSET, from process/thread TID and store their values in GDB's + register cache. */ static void fetch_regset (struct regcache *regcache, int tid, - int regset, int regsize, const short *regmap) + int regset_id, int regsize, const struct regset *regset) { - gdb_byte *buf = alloca (regsize); + void *buf = alloca (regsize); struct iovec iov; iov.iov_base = buf; iov.iov_len = regsize; - if (ptrace (PTRACE_GETREGSET, tid, (long) regset, (long) &iov) < 0) + if (ptrace (PTRACE_GETREGSET, tid, (long) regset_id, (long) &iov) < 0) { if (errno == ENODATA) - s390_native_supply (regcache, regmap, NULL); + regcache_supply_regset (regset, regcache, -1, NULL, regsize); else perror_with_name (_("Couldn't get register set")); } else - s390_native_supply (regcache, regmap, buf); + regcache_supply_regset (regset, regcache, -1, buf, regsize); } -/* Store all registers in the kernel's register set whose number is REGSET, - whose size is REGSIZE, and whose layout is described by REGMAP, from - GDB's register cache back to process/thread TID. */ +/* Store all registers in the kernel's register set whose number is + REGSET_ID, whose size is REGSIZE, and whose layout is described by + REGSET, from GDB's register cache back to process/thread TID. */ static void store_regset (struct regcache *regcache, int tid, - int regset, int regsize, const short *regmap) + int regset_id, int regsize, const struct regset *regset) { - gdb_byte *buf = alloca (regsize); + void *buf = alloca (regsize); struct iovec iov; iov.iov_base = buf; iov.iov_len = regsize; - if (ptrace (PTRACE_GETREGSET, tid, (long) regset, (long) &iov) < 0) + if (ptrace (PTRACE_GETREGSET, tid, (long) regset_id, (long) &iov) < 0) perror_with_name (_("Couldn't get register set")); - s390_native_collect (regcache, regmap, -1, buf); + regcache_collect_regset (regset, regcache, -1, buf, regsize); - if (ptrace (PTRACE_SETREGSET, tid, (long) regset, (long) &iov) < 0) + if (ptrace (PTRACE_SETREGSET, tid, (long) regset_id, (long) &iov) < 0) perror_with_name (_("Couldn't set register set")); } @@ -349,7 +392,7 @@ store_regset (struct regcache *regcache, int tid, static int check_regset (int tid, int regset, int regsize) { - gdb_byte *buf = alloca (regsize); + void *buf = alloca (regsize); struct iovec iov; iov.iov_base = buf; @@ -363,11 +406,10 @@ check_regset (int tid, int regset, int regsize) /* Fetch register REGNUM from the child process. If REGNUM is -1, do this for all registers. */ -static void -s390_linux_fetch_inferior_registers (struct target_ops *ops, - struct regcache *regcache, int regnum) +void +s390_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum) { - int tid = s390_inferior_tid (); + pid_t tid = get_ptrace_pid (regcache->ptid ()); if (regnum == -1 || S390_IS_GREGSET_REGNUM (regnum)) fetch_regs (regcache, tid); @@ -378,27 +420,50 @@ s390_linux_fetch_inferior_registers (struct target_ops *ops, if (have_regset_last_break) if (regnum == -1 || regnum == S390_LAST_BREAK_REGNUM) fetch_regset (regcache, tid, NT_S390_LAST_BREAK, 8, - (gdbarch_ptr_bit (get_regcache_arch (regcache)) == 32 - ? s390_regmap_last_break : s390x_regmap_last_break)); + (gdbarch_ptr_bit (regcache->arch ()) == 32 + ? &s390_last_break_regset : &s390x_last_break_regset)); if (have_regset_system_call) if (regnum == -1 || regnum == S390_SYSTEM_CALL_REGNUM) fetch_regset (regcache, tid, NT_S390_SYSTEM_CALL, 4, - s390_regmap_system_call); + &s390_system_call_regset); if (have_regset_tdb) if (regnum == -1 || S390_IS_TDBREGSET_REGNUM (regnum)) fetch_regset (regcache, tid, NT_S390_TDB, s390_sizeof_tdbregset, - s390_regmap_tdb); + &s390_tdb_regset); + + if (have_regset_vxrs) + { + if (regnum == -1 || (regnum >= S390_V0_LOWER_REGNUM + && regnum <= S390_V15_LOWER_REGNUM)) + fetch_regset (regcache, tid, NT_S390_VXRS_LOW, 16 * 8, + &s390_vxrs_low_regset); + if (regnum == -1 || (regnum >= S390_V16_REGNUM + && regnum <= S390_V31_REGNUM)) + fetch_regset (regcache, tid, NT_S390_VXRS_HIGH, 16 * 16, + &s390_vxrs_high_regset); + } + + if (have_regset_gs) + { + if (regnum == -1 || (regnum >= S390_GSD_REGNUM + && regnum <= S390_GSEPLA_REGNUM)) + fetch_regset (regcache, tid, NT_S390_GS_CB, 4 * 8, + &s390_gs_regset); + if (regnum == -1 || (regnum >= S390_BC_GSD_REGNUM + && regnum <= S390_BC_GSEPLA_REGNUM)) + fetch_regset (regcache, tid, NT_S390_GS_BC, 4 * 8, + &s390_gsbc_regset); + } } /* Store register REGNUM back into the child process. If REGNUM is -1, do this for all registers. */ -static void -s390_linux_store_inferior_registers (struct target_ops *ops, - struct regcache *regcache, int regnum) +void +s390_linux_nat_target::store_registers (struct regcache *regcache, int regnum) { - int tid = s390_inferior_tid (); + pid_t tid = get_ptrace_pid (regcache->ptid ()); if (regnum == -1 || S390_IS_GREGSET_REGNUM (regnum)) store_regs (regcache, tid, regnum); @@ -411,163 +476,468 @@ s390_linux_store_inferior_registers (struct target_ops *ops, if (have_regset_system_call) if (regnum == -1 || regnum == S390_SYSTEM_CALL_REGNUM) store_regset (regcache, tid, NT_S390_SYSTEM_CALL, 4, - s390_regmap_system_call); + &s390_system_call_regset); + + if (have_regset_vxrs) + { + if (regnum == -1 || (regnum >= S390_V0_LOWER_REGNUM + && regnum <= S390_V15_LOWER_REGNUM)) + store_regset (regcache, tid, NT_S390_VXRS_LOW, 16 * 8, + &s390_vxrs_low_regset); + if (regnum == -1 || (regnum >= S390_V16_REGNUM + && regnum <= S390_V31_REGNUM)) + store_regset (regcache, tid, NT_S390_VXRS_HIGH, 16 * 16, + &s390_vxrs_high_regset); + } } /* Hardware-assisted watchpoint handling. */ -/* We maintain a list of all currently active watchpoints in order - to properly handle watchpoint removal. +/* For each process we maintain a list of all currently active + watchpoints, in order to properly handle watchpoint removal. The only thing we actually need is the total address space area spanned by the watchpoints. */ struct watch_area { - struct watch_area *next; CORE_ADDR lo_addr; CORE_ADDR hi_addr; }; -static struct watch_area *watch_base = NULL; +/* Hardware debug state. */ -static int -s390_stopped_by_watchpoint (struct target_ops *ops) +struct s390_debug_reg_state { + std::vector watch_areas; + std::vector break_areas; +}; + +/* Per-process data. */ + +struct s390_process_info +{ + struct s390_process_info *next = nullptr; + pid_t pid = 0; + struct s390_debug_reg_state state; +}; + +static struct s390_process_info *s390_process_list = NULL; + +/* Find process data for process PID. */ + +static struct s390_process_info * +s390_find_process_pid (pid_t pid) +{ + struct s390_process_info *proc; + + for (proc = s390_process_list; proc; proc = proc->next) + if (proc->pid == pid) + return proc; + + return NULL; +} + +/* Add process data for process PID. Returns newly allocated info + object. */ + +static struct s390_process_info * +s390_add_process (pid_t pid) +{ + struct s390_process_info *proc = new struct s390_process_info; + + proc->pid = pid; + proc->next = s390_process_list; + s390_process_list = proc; + + return proc; +} + +/* Get data specific info for process PID, creating it if necessary. + Never returns NULL. */ + +static struct s390_process_info * +s390_process_info_get (pid_t pid) +{ + struct s390_process_info *proc; + + proc = s390_find_process_pid (pid); + if (proc == NULL) + proc = s390_add_process (pid); + + return proc; +} + +/* Get hardware debug state for process PID. */ + +static struct s390_debug_reg_state * +s390_get_debug_reg_state (pid_t pid) +{ + return &s390_process_info_get (pid)->state; +} + +/* Called whenever GDB is no longer debugging process PID. It deletes + data structures that keep track of hardware debug state. */ + +void +s390_linux_nat_target::low_forget_process (pid_t pid) +{ + struct s390_process_info *proc, **proc_link; + + proc = s390_process_list; + proc_link = &s390_process_list; + + while (proc != NULL) + { + if (proc->pid == pid) + { + *proc_link = proc->next; + delete proc; + return; + } + + proc_link = &proc->next; + proc = *proc_link; + } +} + +/* linux_nat_new_fork hook. */ + +void +s390_linux_nat_target::low_new_fork (struct lwp_info *parent, pid_t child_pid) +{ + pid_t parent_pid; + struct s390_debug_reg_state *parent_state; + struct s390_debug_reg_state *child_state; + + /* NULL means no watchpoint has ever been set in the parent. In + that case, there's nothing to do. */ + if (lwp_arch_private_info (parent) == NULL) + return; + + /* GDB core assumes the child inherits the watchpoints/hw breakpoints of + the parent. So copy the debug state from parent to child. */ + + parent_pid = parent->ptid.pid (); + parent_state = s390_get_debug_reg_state (parent_pid); + child_state = s390_get_debug_reg_state (child_pid); + + child_state->watch_areas = parent_state->watch_areas; + child_state->break_areas = parent_state->break_areas; +} + +/* Dump PER state. */ + +static void +s390_show_debug_regs (int tid, const char *where) +{ + per_struct per_info; + ptrace_area parea; + + parea.len = sizeof (per_info); + parea.process_addr = (addr_t) &per_info; + parea.kernel_addr = offsetof (struct user_regs_struct, per_info); + + if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea, 0) < 0) + perror_with_name (_("Couldn't retrieve debug regs")); + + debug_printf ("PER (debug) state for %d -- %s\n" + " cr9-11: %lx %lx %lx\n" + " start, end: %lx %lx\n" + " code/ATMID: %x address: %lx PAID: %x\n", + tid, + where, + per_info.control_regs.words.cr[0], + per_info.control_regs.words.cr[1], + per_info.control_regs.words.cr[2], + per_info.starting_addr, + per_info.ending_addr, + per_info.lowcore.words.perc_atmid, + per_info.lowcore.words.address, + per_info.lowcore.words.access_id); +} + +bool +s390_linux_nat_target::stopped_by_watchpoint () +{ + struct s390_debug_reg_state *state + = s390_get_debug_reg_state (inferior_ptid.pid ()); per_lowcore_bits per_lowcore; ptrace_area parea; - int result; + + if (show_debug_regs) + s390_show_debug_regs (s390_inferior_tid (), "stop"); /* Speed up common case. */ - if (!watch_base) - return 0; + if (state->watch_areas.empty ()) + return false; parea.len = sizeof (per_lowcore); parea.process_addr = (addr_t) & per_lowcore; parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore); - if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0) + if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea, 0) < 0) perror_with_name (_("Couldn't retrieve watchpoint status")); - result = (per_lowcore.perc_storage_alteration == 1 - && per_lowcore.perc_store_real_address == 0); + bool result = (per_lowcore.perc_storage_alteration == 1 + && per_lowcore.perc_store_real_address == 0); if (result) { /* Do not report this watchpoint again. */ memset (&per_lowcore, 0, sizeof (per_lowcore)); - if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0) + if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea, 0) < 0) perror_with_name (_("Couldn't clear watchpoint status")); } return result; } -static void -s390_fix_watch_points (struct lwp_info *lp) +/* Each time before resuming a thread, update its PER info. */ + +void +s390_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp) { int tid; + pid_t pid = ptid_of_lwp (lp).pid (); per_struct per_info; ptrace_area parea; CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0; - struct watch_area *area; + struct arch_lwp_info *lp_priv = lwp_arch_private_info (lp); + struct s390_debug_reg_state *state = s390_get_debug_reg_state (pid); + int step = lwp_is_stepping (lp); - tid = ptid_get_lwp (lp->ptid); - if (tid == 0) - tid = ptid_get_pid (lp->ptid); + /* Nothing to do if there was never any PER info for this thread. */ + if (lp_priv == NULL) + return; - for (area = watch_base; area; area = area->next) + /* If PER info has changed, update it. When single-stepping, disable + hardware breakpoints (if any). Otherwise we're done. */ + if (!lp_priv->per_info_changed) { - watch_lo_addr = min (watch_lo_addr, area->lo_addr); - watch_hi_addr = max (watch_hi_addr, area->hi_addr); + if (!step || state->break_areas.empty ()) + return; } + lp_priv->per_info_changed = 0; + + tid = ptid_of_lwp (lp).lwp (); + if (tid == 0) + tid = pid; + parea.len = sizeof (per_info); parea.process_addr = (addr_t) & per_info; parea.kernel_addr = offsetof (struct user_regs_struct, per_info); - if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0) - perror_with_name (_("Couldn't retrieve watchpoint status")); - if (watch_base) + /* Clear PER info, but adjust the single_step field (used by older + kernels only). */ + memset (&per_info, 0, sizeof (per_info)); + per_info.single_step = (step != 0); + + if (!state->watch_areas.empty ()) { - per_info.control_regs.bits.em_storage_alteration = 1; - per_info.control_regs.bits.storage_alt_space_ctl = 1; + for (const auto &area : state->watch_areas) + { + watch_lo_addr = std::min (watch_lo_addr, area.lo_addr); + watch_hi_addr = std::max (watch_hi_addr, area.hi_addr); + } + + /* Enable storage-alteration events. */ + per_info.control_regs.words.cr[0] |= (PER_EVENT_STORE + | PER_CONTROL_ALTERATION); } - else + + if (!state->break_areas.empty ()) { - per_info.control_regs.bits.em_storage_alteration = 0; - per_info.control_regs.bits.storage_alt_space_ctl = 0; + /* Don't install hardware breakpoints while single-stepping, since + our PER settings (e.g. the nullification bit) might then conflict + with the kernel's. But re-install them afterwards. */ + if (step) + lp_priv->per_info_changed = 1; + else + { + for (const auto &area : state->break_areas) + { + watch_lo_addr = std::min (watch_lo_addr, area.lo_addr); + watch_hi_addr = std::max (watch_hi_addr, area.hi_addr); + } + + /* If there's just one breakpoint, enable instruction-fetching + nullification events for the breakpoint address (fast). + Otherwise stop after any instruction within the PER area and + after any branch into it (slow). */ + if (watch_hi_addr == watch_lo_addr) + per_info.control_regs.words.cr[0] |= (PER_EVENT_NULLIFICATION + | PER_EVENT_IFETCH); + else + { + /* The PER area must include the instruction before the + first breakpoint address. */ + watch_lo_addr = watch_lo_addr > 6 ? watch_lo_addr - 6 : 0; + per_info.control_regs.words.cr[0] + |= (PER_EVENT_BRANCH + | PER_EVENT_IFETCH + | PER_CONTROL_BRANCH_ADDRESS); + } + } } per_info.starting_addr = watch_lo_addr; per_info.ending_addr = watch_hi_addr; - if (ptrace (PTRACE_POKEUSR_AREA, tid, &parea) < 0) + if (ptrace (PTRACE_POKEUSR_AREA, tid, &parea, 0) < 0) perror_with_name (_("Couldn't modify watchpoint status")); + + if (show_debug_regs) + s390_show_debug_regs (tid, "resume"); } -static int -s390_insert_watchpoint (struct target_ops *self, - CORE_ADDR addr, int len, int type, - struct expression *cond) +/* Mark the PER info as changed, so the next resume will update it. */ + +static void +s390_mark_per_info_changed (struct lwp_info *lp) { - struct lwp_info *lp; - struct watch_area *area = xmalloc (sizeof (struct watch_area)); + if (lwp_arch_private_info (lp) == NULL) + lwp_set_arch_private_info (lp, XCNEW (struct arch_lwp_info)); - if (!area) - return -1; + lwp_arch_private_info (lp)->per_info_changed = 1; +} + +/* When attaching to a new thread, mark its PER info as changed. */ - area->lo_addr = addr; - area->hi_addr = addr + len - 1; +void +s390_linux_nat_target::low_new_thread (struct lwp_info *lp) +{ + s390_mark_per_info_changed (lp); +} + +/* Function to call when a thread is being deleted. */ - area->next = watch_base; - watch_base = area; +void +s390_linux_nat_target::low_delete_thread (struct arch_lwp_info *arch_lwp) +{ + xfree (arch_lwp); +} - ALL_LWPS (lp) - s390_fix_watch_points (lp); +/* Iterator callback for s390_refresh_per_info. */ + +static int +s390_refresh_per_info_cb (struct lwp_info *lp) +{ + s390_mark_per_info_changed (lp); + + if (!lwp_is_stopped (lp)) + linux_stop_lwp (lp); return 0; } +/* Make sure that threads are stopped and mark PER info as changed. */ + static int -s390_remove_watchpoint (struct target_ops *self, - CORE_ADDR addr, int len, int type, - struct expression *cond) +s390_refresh_per_info (void) +{ + ptid_t pid_ptid = ptid_t (current_lwp_ptid ().pid ()); + + iterate_over_lwps (pid_ptid, s390_refresh_per_info_cb); + return 0; +} + +int +s390_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) { - struct lwp_info *lp; - struct watch_area *area, **parea; + watch_area area; + struct s390_debug_reg_state *state + = s390_get_debug_reg_state (inferior_ptid.pid ()); - for (parea = &watch_base; *parea; parea = &(*parea)->next) - if ((*parea)->lo_addr == addr - && (*parea)->hi_addr == addr + len - 1) - break; + area.lo_addr = addr; + area.hi_addr = addr + len - 1; + state->watch_areas.push_back (area); - if (!*parea) + return s390_refresh_per_info (); +} + +int +s390_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) +{ + unsigned ix; + struct s390_debug_reg_state *state + = s390_get_debug_reg_state (inferior_ptid.pid ()); + + for (ix = 0; ix < state->watch_areas.size (); ix++) { - fprintf_unfiltered (gdb_stderr, - "Attempt to remove nonexistent watchpoint.\n"); - return -1; + watch_area &area = state->watch_areas[ix]; + if (area.lo_addr == addr && area.hi_addr == addr + len - 1) + { + unordered_remove (state->watch_areas, ix); + return s390_refresh_per_info (); + } } - area = *parea; - *parea = area->next; - xfree (area); + fprintf_unfiltered (gdb_stderr, + "Attempt to remove nonexistent watchpoint.\n"); + return -1; +} + +/* Implement the "can_use_hw_breakpoint" target_ops method. */ - ALL_LWPS (lp) - s390_fix_watch_points (lp); +int +s390_linux_nat_target::can_use_hw_breakpoint (enum bptype type, + int cnt, int othertype) +{ + if (type == bp_hardware_watchpoint || type == bp_hardware_breakpoint) + return 1; return 0; } -static int -s390_can_use_hw_breakpoint (struct target_ops *self, - int type, int cnt, int othertype) +/* Implement the "insert_hw_breakpoint" target_ops method. */ + +int +s390_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) { - return type == bp_hardware_watchpoint; + watch_area area; + struct s390_debug_reg_state *state; + + area.lo_addr = bp_tgt->placed_address = bp_tgt->reqstd_address; + area.hi_addr = area.lo_addr; + state = s390_get_debug_reg_state (inferior_ptid.pid ()); + state->break_areas.push_back (area); + + return s390_refresh_per_info (); } -static int -s390_region_ok_for_hw_watchpoint (struct target_ops *self, - CORE_ADDR addr, int cnt) +/* Implement the "remove_hw_breakpoint" target_ops method. */ + +int +s390_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) +{ + unsigned ix; + struct s390_debug_reg_state *state; + + state = s390_get_debug_reg_state (inferior_ptid.pid ()); + for (ix = 0; state->break_areas.size (); ix++) + { + watch_area &area = state->break_areas[ix]; + if (area.lo_addr == bp_tgt->placed_address) + { + unordered_remove (state->break_areas, ix); + return s390_refresh_per_info (); + } + } + + fprintf_unfiltered (gdb_stderr, + "Attempt to remove nonexistent breakpoint.\n"); + return -1; +} + +int +s390_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int cnt) { return 1; } @@ -591,9 +961,10 @@ s390_target_wordsize (void) return wordsize; } -static int -s390_auxv_parse (struct target_ops *ops, gdb_byte **readptr, - gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp) +int +s390_linux_nat_target::auxv_parse (gdb_byte **readptr, + gdb_byte *endptr, CORE_ADDR *typep, + CORE_ADDR *valp) { int sizeof_auxv_field = s390_target_wordsize (); enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); @@ -614,21 +985,8 @@ s390_auxv_parse (struct target_ops *ops, gdb_byte **readptr, return 1; } -#ifdef __s390x__ -static unsigned long -s390_get_hwcap (void) -{ - CORE_ADDR field; - - if (target_auxv_search (¤t_target, AT_HWCAP, &field)) - return (unsigned long) field; - - return 0; -} -#endif - -static const struct target_desc * -s390_read_description (struct target_ops *ops) +const struct target_desc * +s390_linux_nat_target::read_description () { int tid = s390_inferior_tid (); @@ -637,27 +995,46 @@ s390_read_description (struct target_ops *ops) have_regset_system_call = check_regset (tid, NT_S390_SYSTEM_CALL, 4); -#ifdef __s390x__ /* If GDB itself is compiled as 64-bit, we are running on a machine in z/Architecture mode. If the target is running in 64-bit addressing mode, report s390x architecture. If the target is running in 31-bit addressing mode, but the kernel supports using 64-bit registers in that mode, report s390 architecture with 64-bit GPRs. */ - - have_regset_tdb = (s390_get_hwcap () & HWCAP_S390_TE) ? - check_regset (tid, NT_S390_TDB, s390_sizeof_tdbregset) : 0; - - if (s390_target_wordsize () == 8) - return (have_regset_tdb ? tdesc_s390x_te_linux64 : - have_regset_system_call? tdesc_s390x_linux64v2 : - have_regset_last_break? tdesc_s390x_linux64v1 : - tdesc_s390x_linux64); - - if (s390_get_hwcap () & HWCAP_S390_HIGH_GPRS) - return (have_regset_tdb ? tdesc_s390_te_linux64 : - have_regset_system_call? tdesc_s390_linux64v2 : - have_regset_last_break? tdesc_s390_linux64v1 : - tdesc_s390_linux64); +#ifdef __s390x__ + { + CORE_ADDR hwcap = linux_get_hwcap (current_top_target ()); + + have_regset_tdb = (hwcap & HWCAP_S390_TE) + && check_regset (tid, NT_S390_TDB, s390_sizeof_tdbregset); + + have_regset_vxrs = (hwcap & HWCAP_S390_VX) + && check_regset (tid, NT_S390_VXRS_LOW, 16 * 8) + && check_regset (tid, NT_S390_VXRS_HIGH, 16 * 16); + + have_regset_gs = (hwcap & HWCAP_S390_GS) + && check_regset (tid, NT_S390_GS_CB, 4 * 8) + && check_regset (tid, NT_S390_GS_BC, 4 * 8); + + if (s390_target_wordsize () == 8) + return (have_regset_gs ? tdesc_s390x_gs_linux64 : + have_regset_vxrs ? + (have_regset_tdb ? tdesc_s390x_tevx_linux64 : + tdesc_s390x_vx_linux64) : + have_regset_tdb ? tdesc_s390x_te_linux64 : + have_regset_system_call ? tdesc_s390x_linux64v2 : + have_regset_last_break ? tdesc_s390x_linux64v1 : + tdesc_s390x_linux64); + + if (hwcap & HWCAP_S390_HIGH_GPRS) + return (have_regset_gs ? tdesc_s390_gs_linux64 : + have_regset_vxrs ? + (have_regset_tdb ? tdesc_s390_tevx_linux64 : + tdesc_s390_vx_linux64) : + have_regset_tdb ? tdesc_s390_te_linux64 : + have_regset_system_call ? tdesc_s390_linux64v2 : + have_regset_last_break ? tdesc_s390_linux64v1 : + tdesc_s390_linux64); + } #endif /* If GDB itself is compiled as 31-bit, or if we're running a 31-bit inferior @@ -668,33 +1045,24 @@ s390_read_description (struct target_ops *ops) tdesc_s390_linux32); } -void _initialize_s390_nat (void); - +void _initialize_s390_nat (); void -_initialize_s390_nat (void) +_initialize_s390_nat () { - struct target_ops *t; - - /* Fill in the generic GNU/Linux methods. */ - t = linux_target (); - - /* Add our register access methods. */ - t->to_fetch_registers = s390_linux_fetch_inferior_registers; - t->to_store_registers = s390_linux_store_inferior_registers; - - /* Add our watchpoint methods. */ - t->to_can_use_hw_breakpoint = s390_can_use_hw_breakpoint; - t->to_region_ok_for_hw_watchpoint = s390_region_ok_for_hw_watchpoint; - t->to_have_continuable_watchpoint = 1; - t->to_stopped_by_watchpoint = s390_stopped_by_watchpoint; - t->to_insert_watchpoint = s390_insert_watchpoint; - t->to_remove_watchpoint = s390_remove_watchpoint; - - /* Detect target architecture. */ - t->to_read_description = s390_read_description; - t->to_auxv_parse = s390_auxv_parse; - /* Register the target. */ - linux_nat_add_target (t); - linux_nat_set_new_thread (t, s390_fix_watch_points); + linux_target = &the_s390_linux_nat_target; + add_inf_child_target (&the_s390_linux_nat_target); + + /* A maintenance command to enable showing the PER state. */ + add_setshow_boolean_cmd ("show-debug-regs", class_maintenance, + &show_debug_regs, _("\ +Set whether to show the PER (debug) hardware state."), _("\ +Show whether to show the PER (debug) hardware state."), _("\ +Use \"on\" to enable, \"off\" to disable.\n\ +If enabled, the PER state is shown after it is changed by GDB,\n\ +and when the inferior triggers a breakpoint or watchpoint."), + NULL, + NULL, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); }