From patchwork Mon Mar 12 15:31:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Rudo X-Patchwork-Id: 26293 Received: (qmail 6314 invoked by alias); 12 Mar 2018 15:31:45 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 6214 invoked by uid 89); 12 Mar 2018 15:31:44 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_SHORT, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 spammy=1160, 22806, tod X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0b-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.158.5) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 12 Mar 2018 15:31:36 +0000 Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.22/8.16.0.22) with SMTP id w2CFTqZp086571 for ; Mon, 12 Mar 2018 11:31:34 -0400 Received: from e06smtp14.uk.ibm.com (e06smtp14.uk.ibm.com [195.75.94.110]) by mx0b-001b2d01.pphosted.com with ESMTP id 2gnsfkrar4-1 (version=TLSv1.2 cipher=AES256-SHA256 bits=256 verify=NOT) for ; Mon, 12 Mar 2018 11:31:32 -0400 Received: from localhost by e06smtp14.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 12 Mar 2018 15:31:30 -0000 Received: from b06cxnps4074.portsmouth.uk.ibm.com (9.149.109.196) by e06smtp14.uk.ibm.com (192.168.101.144) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 12 Mar 2018 15:31:28 -0000 Received: from d06av21.portsmouth.uk.ibm.com (d06av21.portsmouth.uk.ibm.com [9.149.105.232]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id w2CFVSEX51314698; Mon, 12 Mar 2018 15:31:28 GMT Received: from d06av21.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 8E10452041; Mon, 12 Mar 2018 14:23:00 +0000 (GMT) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.152.85.9]) by d06av21.portsmouth.uk.ibm.com (Postfix) with ESMTPS id 3F5B75204C; Mon, 12 Mar 2018 14:23:00 +0000 (GMT) From: Philipp Rudo To: gdb-patches@sourceware.org Cc: Omair Javaid , Yao Qi , arnez@linux.vnet.ibm.com Subject: [RFC PATCH v5 9/9] Add S390 support for linux-kernel target Date: Mon, 12 Mar 2018 16:31:15 +0100 In-Reply-To: <20180312153115.47321-1-prudo@linux.vnet.ibm.com> References: <20180312153115.47321-1-prudo@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 18031215-0016-0000-0000-00000530819B X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 18031215-0017-0000-0000-0000286DAFA2 Message-Id: <20180312153115.47321-10-prudo@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2018-03-12_09:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=43 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1803120176 X-IsSubscribed: yes Add s390 architecture support for the new linux-kernel target. In particular implement the required virtual methods, enable priviledged registers and add a kernel stack unwinder. gdb/ChangeLog: * s390-lk-tdep.h: New file. * s390-lk-tdep.c: New file. * Makefile.in (ALL_TARGET_OBS): Add s390-lk-tdep.o. (HFILES_NO_SRCDIR): Add s390-lk-tdep.h. (ALLDEPFILES): Add s390-lk-tdep.c. * configure.tgt (s390*-*-linux*): Add s390-lk-tdep.o. * s390-tdep.h: Define macros for address translation. (gdbarch_tdep) : New field. (s390_unwind_pseudo_register): New export. * s390-tdep.c (s390_unwind_pseudo_register): Remove static. (s390_tdesc_valid): Add control registers and check for priviledeged feature in tdesc. (s390_gdbarch_tdep_alloc): Initialize new field have_priviledged. --- gdb/Makefile.in | 3 + gdb/configure.tgt | 6 +- gdb/s390-lk-tdep.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/s390-lk-tdep.h | 39 ++ gdb/s390-tdep.c | 36 +- gdb/s390-tdep.h | 66 ++++ 6 files changed, 1160 insertions(+), 5 deletions(-) create mode 100644 gdb/s390-lk-tdep.c create mode 100644 gdb/s390-lk-tdep.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2029c933af..1970b7f734 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -760,6 +760,7 @@ ALL_TARGET_OBS = \ rs6000-tdep.o \ rx-tdep.o \ s390-linux-tdep.o \ + s390-lk-tdep.o \ s390-tdep.o \ score-tdep.o \ sh-linux-tdep.o \ @@ -1342,6 +1343,7 @@ HFILES_NO_SRCDIR = \ rs6000-aix-tdep.h \ rs6000-tdep.h \ s390-linux-tdep.h \ + s390-lk-tdep.h \ s390-tdep.h \ score-tdep.h \ selftest-arch.h \ @@ -2321,6 +2323,7 @@ ALLDEPFILES = \ rx-tdep.c \ s390-linux-nat.c \ s390-linux-tdep.c \ + s390-lk-tdep.c \ s390-tdep.c \ score-tdep.c \ ser-go32.c \ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 680b3b2e42..a9116b3b7b 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -519,9 +519,9 @@ powerpc*-*-*) s390*-*-linux*) # Target: S390 running Linux - gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \ - linux-tdep.o linux-record.o symfile-mem.o \ - ${lk_tobjs}" + gdb_target_obs="s390-linux-tdep.o s390-tdep.o s390-lk-tdep.o \ + solib-svr4.o linux-tdep.o linux-record.o \ + symfile-mem.o ${lk_tobjs}" build_gdbserver=yes ;; diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c new file mode 100644 index 0000000000..d1582f2474 --- /dev/null +++ b/gdb/s390-lk-tdep.c @@ -0,0 +1,1015 @@ +/* Target-dependent code for linux-kernel target on S390. + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" + +#include "auxv.h" +#include "dwarf2-frame.h" +#include "elf/common.h" +#include "frame-base.h" +#include "frame-unwind.h" +#include "gdbarch.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "lk-low.h" +#include "lk-modules.h" +#include "osabi.h" +#include "regcache.h" +#include "regset.h" +#include "s390-tdep.h" +#include "s390-lk-tdep.h" + +#include "features/s390x-cr-linux64.c" +#include "features/s390x-vxcr-linux64.c" + + +/* Register maps and sets. */ + +static const struct regcache_map_entry s390_gregmap[] = + { + { 1, S390_PSWM_REGNUM }, + { 1, S390_PSWA_REGNUM }, + { 16, S390_R0_REGNUM }, + { 16, S390_A0_REGNUM }, + { 1, REGCACHE_MAP_SKIP, 8 }, /* ORIG_R2_REG, exists but unused. */ + { 0 } + }; + +static const struct regcache_map_entry s390x_gregmap_lk[] = + { + { 10, S390_R6_REGNUM }, /* r0-r5 volatile */ + { -2, REGCACHE_MAP_SKIP, 8 }, + { 1, S390_PSWA_REGNUM }, /* Use r14 for PSWA. */ + { 0 } + }; + +static const struct regcache_map_entry s390_fpregmap[] = + { + { 1, S390_FPC_REGNUM, 8 }, + { 16, S390_F0_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390_regmap_vxrs_low[] = + { + { 16, S390_V0_LOWER_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390_regmap_vxrs_high[] = + { + { 16, S390_V16_REGNUM, 16 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_cr [] = + { + { 16, S390_CR0_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_timer [] = + { + { 1, S390_TIMER_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_todcmp [] = + { + { 1, S390_TODCMP_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_todpreg [] = + { + { 1, S390_TODPREG_REGNUM, 8 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_prefix [] = + { + { 1, S390_PREFIX_REGNUM, 4 }, + { 0 } + }; + +/* Gregset stored in dump section. Used for running tasks. */ + +#define s390x_sizeof_gregset 0xd8 +static const struct regset s390_gregset = { + s390_gregmap, + regcache_supply_regset, + regcache_collect_regset +}; + +/* Gregset set stored in stack_frame. Used for sleeping tasks. */ + +static const struct regset s390x_gregset_lk = { + s390x_gregmap_lk, + regcache_supply_regset, + regcache_collect_regset +}; + +#define s390_sizeof_fpregset 0x88 +static const struct regset s390_fpregset = { + s390_fpregmap, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390_vxrs_low_regset = { + s390_regmap_vxrs_low, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390_vxrs_high_regset = { + s390_regmap_vxrs_high, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390x_cr_regset = { + s390x_regmap_cr, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390x_timer_regset = { + s390x_regmap_timer, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390x_todcmp_regset = { + s390x_regmap_todcmp, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390x_todpreg_regset = { + s390x_regmap_todpreg, + regcache_supply_regset, + regcache_collect_regset +}; + +static const struct regset s390x_prefix_regset = { + s390x_regmap_prefix, + regcache_supply_regset, + regcache_collect_regset +}; + +/* S390 specific implementation for linux_kernel_ops. */ + +class s390_linux_kernel_ops : public linux_kernel_ops +{ +public: + s390_linux_kernel_ops (struct target_ops *target) + : linux_kernel_ops::linux_kernel_ops (target) + {} + + /* See lk-low.h. Uses r14 as current pswa. */ + void get_registers (CORE_ADDR task, struct target_ops *target, + struct regcache *regcache, int regnum) override; + + /* See lk-low.h. */ + bool is_kvaddr (CORE_ADDR addr) override + { return addr >= lk_address ("high_memory"); } + + /* See lk-low.h. */ + CORE_ADDR vtop (CORE_ADDR vaddr, CORE_ADDR pgd) override; + + /* See lk-low.h. */ + void adjust_module_layout (CORE_ADDR mod, lk_module *module) override; + + /* See lk-low.h. */ + CORE_ADDR percpu_offset (unsigned int cpu) override; + + /* See lk-low.h. */ + unsigned int beneath_thread_to_cpu (thread_info *ti) override; + +protected: + /* See lk-low.h. */ + void arch_read_symbols () override; +}; + +/* Returns address of lowcore of given cpu. */ + +static CORE_ADDR +s390_lk_get_lowcore (unsigned int cpu) +{ + size_t ptr_len = lk_builtin_type_size (unsigned_long); + CORE_ADDR lowcore_ptr = lk_ops->address ("lowcore_ptr") + (ptr_len * cpu); + return lk_read_addr (lowcore_ptr); +} + +/* Implement linux_kernel_ops->arch_read_symbols virtual method. */ + +void +s390_linux_kernel_ops::arch_read_symbols () +{ + declare_field ("stack_frame->gprs", LK_CONFIG_ALWAYS); + declare_field ("stack_frame->back_chain", LK_CONFIG_ALWAYS); + + declare_field ("pt_regs->psw", LK_CONFIG_ALWAYS); + declare_field ("pt_regs->gprs", LK_CONFIG_ALWAYS); + + declare_field ("task_struct->stack", LK_CONFIG_ALWAYS); + + declare_field ("thread_struct->ksp", LK_CONFIG_ALWAYS); + + declare_type ("lowcore", {"lowcore", /* linux 4.5+ */ + "_lowcore"}, /* linux -4.4 */ + LK_CONFIG_ALWAYS); + declare_field ("lowcore->percpu_offset", LK_CONFIG_ALWAYS); + declare_field ("lowcore->cpu_nr", LK_CONFIG_ALWAYS); + declare_field ("lowcore->async_stack", LK_CONFIG_ALWAYS); + declare_field ("lowcore->panic_stack", LK_CONFIG_ALWAYS); + declare_field ("lowcore->restart_stack", LK_CONFIG_ALWAYS); + + declare_field ("psw_t->mask", LK_CONFIG_ALWAYS); + declare_field ("psw_t->addr", LK_CONFIG_ALWAYS); + + declare_field ("mod_arch_specific->plt_offset", LK_CONFIG_ALWAYS); + declare_field ("mod_arch_specific->plt_size", LK_CONFIG_ALWAYS); + + declare_address ("lowcore_ptr", LK_CONFIG_ALWAYS); + declare_address ("high_memory", LK_CONFIG_ALWAYS); +} + +/* Implement linux_kernel_ops->get_registers virtual method. */ + +void +s390_linux_kernel_ops::get_registers (CORE_ADDR task, + struct target_ops *target, + struct regcache *regcache, + int regnum) +{ + CORE_ADDR ksp, gprs; + gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */ + size_t size; + + ksp = lk_read_addr (task + offset ("task_struct->thread->ksp")); + gprs = ksp + offset ("stack_frame->gprs"); + size = FIELD_SIZE (field ("stack_frame->gprs")); + gdb_assert (size <= sizeof (buf)); + + read_memory (gprs, buf, size); + regcache->supply_regset (&s390x_gregset_lk, -1, buf, size); +} + +/* Read table entry from TABLE at offset OFFSET. Helper for + s390_linux_kernel_ops::vtop. */ + +static inline ULONGEST +s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset) +{ + return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long)); +} + +/* Implement linux_kernel_ops->vtop virtual method. */ + +CORE_ADDR +s390_linux_kernel_ops::vtop (CORE_ADDR vaddr, CORE_ADDR table) +{ + size_t addr_size = lk_builtin_type_size (unsigned_long); + unsigned int table_type; + ULONGEST entry, offset; + + /* Read first entry in table to get its type. As the Table-Type bits are + the same in every table assume Region1-Table. */ + entry = s390_lk_read_table_entry (table, 0); + table_type = (entry & S390_LK_RFTE_TT) >> 2; + + switch (table_type) + { + case S390_LK_DAT_TT_REGION1: + offset = (vaddr & S390_LK_VADDR_RFX) >> 53; + entry = s390_lk_read_table_entry (table, offset); + + /* Do sanity checks. */ + if (!entry) + warning (_("Trying to translate address 0x%s with empty "\ + "region-first-table entry."), phex (vaddr, addr_size)); + else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1) + warning (_("Trying to translate address 0x%s with corrupt "\ + "table type in region-first-table entry."), + phex (vaddr, addr_size)); + else if (entry & S390_LK_RFTE_I) + warning (_("Translating address 0x%s with invalid bit set at "\ + "region-first-table entry."), phex (vaddr, addr_size)); + + table = entry & S390_LK_RFTE_O; + + /* fall through */ + case S390_LK_DAT_TT_REGION2: + offset = (vaddr & S390_LK_VADDR_RSX) >> 42; + entry = s390_lk_read_table_entry (table, offset); + + /* Do sanity checks. */ + if (!entry) + warning (_("Trying to translate address 0x%s with empty "\ + "region-second-table entry."), phex (vaddr, addr_size)); + else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2) + warning (_("Trying to translate address 0x%s with corrupt "\ + "table type in region-second-table entry."), + phex (vaddr, addr_size)); + else if (entry & S390_LK_RSTE_I) + warning (_("Translating address 0x%s with invalid bit set at "\ + "region-second-table entry."), phex (vaddr, addr_size)); + + table = entry & S390_LK_RSTE_O; + + /* fall through */ + case S390_LK_DAT_TT_REGION3: + offset = (vaddr & S390_LK_VADDR_RTX) >> 31; + entry = s390_lk_read_table_entry (table, offset); + + /* Do sanity checks. */ + if (!entry) + warning (_("Trying to translate address 0x%s with empty "\ + "region-third-table entry."), phex (vaddr, addr_size)); + else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3) + warning (_("Trying to translate address 0x%s with corrupt "\ + "table type in region-third-table entry."), + phex (vaddr, addr_size)); + else if (entry & S390_LK_RTTE_I) + warning (_("Translating address 0x%s with invalid bit set at "\ + "region-third-table entry."), phex (vaddr, addr_size)); + + /* Check for huge page. */ + if (entry & S390_LK_RTTE_FC) + return (entry & S390_LK_RTTE_RFAA) + (vaddr & ~S390_LK_RTTE_RFAA); + + table = entry & S390_LK_RTTE_O; + + /* fall through */ + case S390_LK_DAT_TT_SEGMENT: + offset = (vaddr & S390_LK_VADDR_SX) >> 20; + entry = s390_lk_read_table_entry (table, offset); + + /* Do sanity checks. */ + if (!entry) + warning (_("Trying to translate address 0x%s with empty "\ + "segment-table entry."), phex (vaddr, addr_size)); + else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT) + warning (_("Trying to translate address 0x%s with corrupt "\ + "table type in segment-table entry."), + phex (vaddr, addr_size)); + else if (entry & S390_LK_STE_I) + warning (_("Translating address 0x%s with invalid bit set at "\ + "segment-table entry."), phex (vaddr, addr_size)); + + /* Check for large page. */ + if (entry & S390_LK_STE_FC) + return ((entry & S390_LK_STE_SFAA) + (vaddr & ~S390_LK_STE_SFAA)); + + table = entry & S390_LK_STE_O; + break; + } /* switch (table_type) */ + + offset = (vaddr & S390_LK_VADDR_PX) >> 12; + entry = s390_lk_read_table_entry (table, offset); + + /* Do sanity checks. */ + if (!entry) + warning (_("Trying to translate address 0x%s with empty page-table "\ + "entry."), phex (vaddr, addr_size)); + else if (entry & S390_LK_PTE_I) + warning (_("Translating address 0x%s with invalid bit set at "\ + "page-table entry."), phex (vaddr, addr_size)); + + return (entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA); +} + +/* Implement linux_kernel_ops->adjust_module_layout virtual method. */ + +void +s390_linux_kernel_ops::adjust_module_layout (CORE_ADDR mod, + lk_module *module) +{ + CORE_ADDR mod_arch = mod + offset ("module->arch"); + + module->core->text.base + += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_offset")); + module->core->text.base + += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_size")); +} + +/* Implement linux_kernel_ops->percpu_offset virtual method. */ + +CORE_ADDR +s390_linux_kernel_ops::percpu_offset (unsigned int cpu) +{ + CORE_ADDR lowcore = s390_lk_get_lowcore (cpu); + return lk_read_addr (lowcore + offset ("lowcore->percpu_offset")); +} + +/* Implement linux_kernel_ops->beneath_thread_to_cpu virtual method. */ + +unsigned int +s390_linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti) +{ + CORE_ADDR lowcore; + + regcache *regcache = get_thread_regcache (ti->ptid); + register_status reg_status = regcache->raw_read (S390_PREFIX_REGNUM, + (ULONGEST *) &lowcore); + if (reg_status != REG_VALID) + error (_("Could not find prefix register for thread with pid %d, lwp %li."), + ti->ptid.pid (), ti->ptid.lwp ()); + + return lk_read_uint (lowcore + offset ("lowcore->cpu_nr")); +} + +/* Linux kernel stack unwinder. */ + +/* Helper functions and definitions. */ + +struct s390_lk_unwind_cache +{ + /* The frame this frame_cache belongs to and its ... */ + struct frame_info *frame; + + /* ... frame base, ... */ + CORE_ADDR frame_base; + /* ... stack pointer, ... */ + CORE_ADDR sp; + /* ... process counter, ... */ + CORE_ADDR pc; + /* ... PSW mask, ... */ + CORE_ADDR pswm; + /* ... PSW address and ... */ + CORE_ADDR pswa; + /* ... saved general purpose registers. */ + CORE_ADDR gprs[S390_NUM_GPRS]; + + /* Mask to store which gprs where saved. */ + unsigned long gprs_p:S390_NUM_GPRS; + /* Does this frame contain a zero backchain? */ + bool zero_backchain; +}; + +/* S390s kernel stack is split up on several memory locations: + + - kernel stack (per task) + - async aka. interrupt stack (per cpu) + - panic stack (per cpu) + - restart stack (unique, global) + + Each memory location is page aligned and has a size of four consecutive + pages (exception is the panic stack with size of one page). */ + +enum s390_lk_stack_location +{ + S390_LK_STACK_INVALID = -1, + S390_LK_STACK_USER, + S390_LK_STACK_KERNEL, + S390_LK_STACK_ASYNC, + S390_LK_STACK_PANIC, + S390_LK_STACK_RESTART +}; + +/* Get value of gpr REGNUM stored in CACHE. */ + +static inline struct value * +s390_lk_unwind_get_gpr (struct s390_lk_unwind_cache *cache, int regnum) +{ + gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM); + + unsigned int gpr = regnum - S390_R0_REGNUM; + if (!(cache->gprs_p & (1 << gpr))) + return frame_unwind_got_optimized (cache->frame, regnum); + + /* When we hit a zero_backchain R15 is the (computed) lowest address of + pt_regs. */ + if (regnum == S390_R15_REGNUM && cache->zero_backchain) + return frame_unwind_got_address (cache->frame, regnum, cache->gprs[gpr]); + + return frame_unwind_got_memory (cache->frame, regnum, cache->gprs[gpr]); +} + +/* Store ADDR of gpr REGNUM in CACHE. */ + +static inline void +s390_lk_unwind_set_gpr (struct s390_lk_unwind_cache *cache, int regnum, + CORE_ADDR addr) +{ + gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM); + + unsigned int gpr = regnum - S390_R0_REGNUM; + cache->gprs[gpr] = addr; + cache->gprs_p |= (1 << gpr); +} + +/* Allocate and initialize a new s390_lk_unwind_cache. */ + +static struct s390_lk_unwind_cache * +s390_lk_alloc_unwind_cache () +{ + struct s390_lk_unwind_cache *cache; + + cache = FRAME_OBSTACK_ZALLOC (struct s390_lk_unwind_cache); + + cache->frame = NULL; + cache->frame_base = -1; + cache->zero_backchain = false; + + cache->sp = -1; + cache->pc = -1; + cache->pswm = -1; + cache->pswa = -1; + for (CORE_ADDR *gpr = cache->gprs; gpr < cache->gprs + S390_NUM_GPRS; gpr++) + *gpr = -1; + cache->gprs_p = 0; + + return cache; +} + +/* Helper macro for s390_lk_get_stack_location to check for stacks which + locations are stored in the lowcore. + + _addr address to be checked for + _lc address of the corresponding lowcore + _stack field name of stack in lowcore + _type type to be returned if _addr is on _stack + _size size of _stack +*/ + +#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size) \ + do \ + { \ + CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom; \ + _##_stack = lk_read_addr ((_lc) + lk_offset ("lowcore->"#_stack));\ + _##_stack##_top = LK_ALIGN (_##_stack, S390_LK_PAGESIZE); \ + _##_stack##_bottom = _##_stack##_top - (_size); \ + if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom) \ + return (_type); \ + } \ + while (0) + +/* Find and return the stack location of ADDR belonging to TASK. */ + +static enum s390_lk_stack_location +s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr) +{ + CORE_ADDR lowcore, top, bottom; + unsigned int cpu = lk_task_running (task); + + /* Kernel stack. */ + bottom = lk_read_addr (task + lk_offset ("task_struct->stack")); + top = bottom + S390_LK_STACKSIZE; + if (addr <= top && addr >= bottom) + return S390_LK_STACK_KERNEL; + + /* A sleeping task only has the kernel stack. If a sleeping task reaches + this point ADDR isn't on the stack. */ + if (cpu == LK_CPU_INVAL) + return S390_LK_STACK_INVALID; + + lowcore = s390_lk_get_lowcore (cpu); + + /* Async aka. interrupt stack. */ + s390_lk_check_lowcore_stack (addr, lowcore, async_stack, + S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE); + + /* Panic stack. + Note: The panic stack only has a size of one page. */ + s390_lk_check_lowcore_stack (addr, lowcore, panic_stack, + S390_LK_STACK_PANIC, S390_LK_PAGESIZE); + + /* Restart stack. */ + s390_lk_check_lowcore_stack (addr, lowcore, restart_stack, + S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE); + + return S390_LK_STACK_INVALID; +} + +/* Unwind a single NORMAL_FRAME, i.e. a struct stack_frame. */ + +static struct s390_lk_unwind_cache * +s390_lk_frame_unwind_cache (struct frame_info *this_frame, + void **this_cache) +{ + CORE_ADDR backchain, backchain_addr, prev_gpr15; + + if (*this_cache) + return (struct s390_lk_unwind_cache *) *this_cache; + + s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache (); + *this_cache = cache; + + cache->frame = this_frame; + /* For NORMAL_FRAMES, cache->sp == lowest address of struct stack_frame. */ + cache->sp = get_frame_sp (this_frame); + cache->pc = get_frame_pc (this_frame); + /* Choose the frame's base to be gpr15 + sizeof (stack_frame) to be + complient with DWARF's DW_CFA_def_cfa. Otherwise gdbarch_inner_than + can trigger false stack corruption asserts, as + + next_dwarf_frame_base = next_backchain_frame_base + sizeof (stack_frame) + > this_backchain_frame_base + */ + cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("stack_frame")); + + backchain_addr = cache->sp + lk_offset ("stack_frame->back_chain"); + backchain = lk_read_addr (backchain_addr); + + /* A zero backchain marks the end of this stack. This can lead to two + actions (handled by SIGTRAMP_FRAME unwinder): + * stop unwinding (if sp at the end of the kernel stack) + * jump to next stack location (otherwise). + */ + if (backchain == 0) + { + CORE_ADDR pt_regs = cache->sp + TYPE_LENGTH (lk_type ("stack_frame")); + + { + /* A bug in the kernel (linux 3.10+) adds the stack overhead twice + for swapper tasks (!= swapper/0) with the information written in + the first (higher address) pt_regs. Compensate this by updating + the stack pointer. */ + enum s390_lk_stack_location stack; + + CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid (); + long pid = frame_get_thread (this_frame)->ptid.lwp (); + stack = s390_lk_get_stack_location (task, cache->sp); + if (pid == 0 && task != lk_address ("init_task") + && stack == S390_LK_STACK_KERNEL) + pt_regs += (TYPE_LENGTH (lk_type ("stack_frame")) + + TYPE_LENGTH (lk_type ("pt_regs"))); + } + + cache->zero_backchain = true; + s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, pt_regs); + + /* Use pc = 0 to mark end of stack. */ + s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, backchain_addr); + return cache; + } + + /* Do sanity check. */ + prev_gpr15 = lk_read_addr (backchain + + s390_lk_stack_frame_gpr (S390_R15_REGNUM)); + if (backchain != prev_gpr15) + { + size_t ptr_len = lk_builtin_type_size (unsigned_long); + CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid (); + error (_("Stack corruption for task 0x%s at stack frame 0x%s with " + "back_chain 0x%s detected %s. back_chain->gpr15 != back_chain."), + phex (task, ptr_len), phex (cache->sp, ptr_len), + phex (backchain, ptr_len), phex (prev_gpr15, ptr_len)); + } + + s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, backchain_addr); + s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, + backchain + s390_lk_stack_frame_gpr (S390_R14_REGNUM)); + + return cache; +} + +/* Implement frame_unwind->this_id for NORMAL_FRAMEs. */ + +static void +s390_lk_frame_this_id (struct frame_info *this_frame, + void **this_cache, + struct frame_id *this_id) +{ + s390_lk_unwind_cache *cache + = s390_lk_frame_unwind_cache (this_frame, this_cache); + + /* If we don't have a stack frame... */ + if (cache->sp == -1) + { + /* ...but a PC. We have a inline frame. */ + if (cache->pc != -1) + *this_id = frame_id_build_unavailable_stack (cache->pc); + return; + } + *this_id = frame_id_build (cache->frame_base, cache->pc); +} + +/* Implement frame_unwind->prev_register for NORMAL and SIGTRAMP_FRAMEs. */ + +static struct value * +s390_lk_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) +{ + s390_lk_unwind_cache *cache + = s390_lk_frame_unwind_cache (this_frame, this_cache); + + /* Also handles S390_SP_REGNUM and S390_RETADDR_REGNUM as they are mapped to + R15 and R14 respectfully in s390-tdep.h. */ + if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM) + return s390_lk_unwind_get_gpr (cache, regnum); + + /* Use PSWM stored in pt_regs if available. */ + if (regnum == S390_PSWM_REGNUM) + return (cache->pswm != -1 + ? frame_unwind_got_memory (cache->frame, regnum, cache->pswm) + : frame_unwind_got_optimized (cache->frame, regnum)); + + /* Use PSWA stored in pt_regs if available, R14/PC otherwise. */ + if (regnum == S390_PSWA_REGNUM) + return (cache->pswa != -1 + ? frame_unwind_got_memory (cache->frame, regnum, cache->pswa) + : s390_lk_unwind_get_gpr (cache, S390_RETADDR_REGNUM)); + + if (regnum >= S390_NUM_REGS) + return s390_unwind_pseudo_register (cache->frame, regnum); + + /* Default is not saved. */ + return frame_unwind_got_optimized (cache->frame, regnum); +} + +/* Implement frame_unwind->sniffer for NORMAL_FRAMEs. */ + +static int +s390_lk_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + if (!target_is_pushed (lk_target_ops)) + return 0; + + return 1; +} + +/* Simple backchain unwinder for Linux kernel on S390. Its main purpose is + to handle stack frames created in Assembler code without debug + infromation. */ + +static const struct frame_unwind s390_lk_frame_unwind = { + NORMAL_FRAME, + default_frame_unwind_stop_reason, + s390_lk_frame_this_id, + s390_lk_frame_prev_register, + NULL, + s390_lk_frame_sniffer +}; + +/* Unwind a single SIGTRAMP_FRAME, i.e. a struct pt_regs. */ + +static struct s390_lk_unwind_cache * +s390_lk_sigtramp_unwind_cache (struct frame_info *this_frame, + void **this_cache) +{ + if (*this_cache) + return (s390_lk_unwind_cache *) *this_cache; + + s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache (); + *this_cache = cache; + + cache->frame = this_frame; + /* For SIGTRAMP_FRAMES, cache->sp = lowest address of struct pt_regs. */ + cache->sp = get_frame_sp (this_frame); + cache->pc = cache->sp + s390_lk_pt_regs_gpr (S390_R14_REGNUM); + /* Choose the frame base to be SP + sizeof (pt_regs) aka. the end of this + stack. */ + cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("pt_regs")); + + cache->pswm = cache->sp + lk_offset ("pt_regs->psw->mask"); + cache->pswa = cache->sp + lk_offset ("pt_regs->psw->addr"); + + for (int i = S390_R0_REGNUM; i <= S390_R15_REGNUM; i++) + s390_lk_unwind_set_gpr (cache, i, cache->sp + s390_lk_pt_regs_gpr (i)); + + return cache; +} + +/* Implement frame_unwind->this_id for SIGTRAMP_FRAMEs. */ + +static void +s390_lk_sigtramp_this_id (struct frame_info *this_frame, + void **this_cache, + struct frame_id *this_id) +{ + s390_lk_unwind_cache *cache + = s390_lk_sigtramp_unwind_cache (this_frame, this_cache); + + CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid (); + + switch (s390_lk_get_stack_location (task, cache->sp)) + { + /* If we are on an interrupt stack keep on unwinding. */ + case S390_LK_STACK_ASYNC: + case S390_LK_STACK_PANIC: + case S390_LK_STACK_RESTART: + *this_id = frame_id_build (cache->frame_base, cache->pc); + return; + + /* If we are on the kernel stack check whether we are at the end of + the stack. If so stop unwinding otherwise continue. */ + case S390_LK_STACK_KERNEL: + { + CORE_ADDR top; + + top = lk_read_addr (task + lk_offset ("task_struct->stack")); + top += S390_LK_STACKSIZE; + + /* swapper/0 doesn't have a pt_regs at the top of its stack, so in + this case cache->sp pointing at the top of the last stack_frame + points at the end of the stack. */ + if (task == lk_address ("init_task") && top <= cache->sp) + *this_id = outer_frame_id; + + /* For all other tasks cache->frame_base pointing at the top of + pt_regs points at the end of stack. */ + else if (top <= cache->frame_base) + *this_id = outer_frame_id; + + /* The program check handler appends his stack to the kernel stack. + If that is the case simply keep on unwinding. */ + else + *this_id = frame_id_build (cache->frame_base, cache->pc); + + return; + } + + /* Otherwise somewhere something went wrong. */ + default: + { + size_t ptr_len = lk_builtin_type_size (unsigned_long); + error (_("Couldn't find find valid stack for task 0x%s with stack " + "pointer 0x%s. Stack corruption?"), + phex (task, ptr_len), phex (cache->sp, ptr_len)); + } + } +} + +/* Implement frame_unwind->sniffer for SIGTRAMP_FRAMEs. */ + +static int +s390_lk_sigtramp_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + if (!target_is_pushed (lk_target_ops)) + return 0; + + return get_frame_pc (this_frame) == 0; +} + +/* Sigtramp unwinder for Linux kernel on S390. It handles situations when + the end of a stack, i.e. a zero backchain, was detected. */ + +static const struct frame_unwind s390_lk_sigtramp_unwind = { + SIGTRAMP_FRAME, + default_frame_unwind_stop_reason, + s390_lk_sigtramp_this_id, + s390_lk_frame_prev_register, + NULL, + s390_lk_sigtramp_sniffer +}; + +/* Frame base handling. */ + +static CORE_ADDR +s390_lk_frame_base_address (struct frame_info *this_frame, void **this_cache) +{ + struct s390_lk_unwind_cache *cache + = s390_lk_frame_unwind_cache (this_frame, this_cache); + return cache->frame_base; +} + +static const struct frame_base s390_lk_frame_base = { + &s390_lk_frame_unwind, + s390_lk_frame_base_address, + s390_lk_frame_base_address, + s390_lk_frame_base_address +}; + +/* Implement gdbarch get_new_lk_ops hook. */ + +static linux_kernel_ops * +s390_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target) +{ + return new s390_linux_kernel_ops (target); +} + +/* Implement gdbarch iterate_over_regset_sections hook. */ + +static void +s390_lk_iterate_over_regset_sections (struct gdbarch *gdbarch, + iterate_over_regset_sections_cb *cb, + void *cb_data, + const struct regcache *regcache) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + cb (".reg", s390x_sizeof_gregset, &s390_gregset, NULL, cb_data); + cb (".reg2", s390_sizeof_fpregset, &s390_fpregset, NULL, cb_data); + cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset, "s390 control registers", + cb_data); + + if (tdep->v0_full_regnum != -1) + { + cb (".reg-s390-vxrs-low", 16 * 8, &s390_vxrs_low_regset, + "s390 vector registers 0-15 lower half", cb_data); + cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset, + "s390 vector registers 16-31", cb_data); + } + + /* Privileged registers for kernel debugging. */ + if (bfd_get_section_by_name (core_bfd, ".reg-s390-timer")) + cb (".reg-s390-timer", 8, &s390x_timer_regset, "s390 timer", cb_data); + if (bfd_get_section_by_name (core_bfd, ".reg-s390-todcmp")) + cb (".reg-s390-todcmp", 8, &s390x_todcmp_regset, + "s390 clock comperator", cb_data); + if (bfd_get_section_by_name (core_bfd, ".reg-s390-todpreg")) + cb (".reg-s390-todpreg", 8, &s390x_todpreg_regset, + "s390 TOD programable register", cb_data); + if (bfd_get_section_by_name (core_bfd, ".reg-s390-prefix")) + cb (".reg-s390-prefix", 4, &s390x_prefix_regset, + "s390 prefix area", cb_data); +} + +/* Implement gdbarch core_read_description hook. */ + +static const struct target_desc * +s390_lk_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, bfd *abfd) +{ + CORE_ADDR hwcap = 0; + + target_auxv_search (target, AT_HWCAP, &hwcap); + + if (hwcap & HWCAP_S390_VX) + return tdesc_s390x_vxcr_linux64; + else + return tdesc_s390x_cr_linux64; +} + +/* Initialize Linux kernel specific gdbarch hooks. */ + +static void +s390_lk_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + const struct target_desc *tdesc = info.target_desc; + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + tdep->abi = ABI_LINUX_ZSERIES; + if (!tdesc_has_registers (tdesc)) + tdesc = tdesc_s390x_cr_linux64; + tdep->tdesc = tdesc; + + /* Register handling. */ + set_gdbarch_core_read_description (gdbarch, s390_lk_core_read_description); + set_gdbarch_iterate_over_regset_sections (gdbarch, + s390_lk_iterate_over_regset_sections); + /* Frame handling. */ + frame_unwind_append_unwinder (gdbarch, &s390_lk_sigtramp_unwind); + frame_unwind_append_unwinder (gdbarch, &s390_lk_frame_unwind); + + /* Support linux kernel debugging. */ + set_gdbarch_get_new_lk_ops (gdbarch, s390_get_new_lk_ops); +} + +/* From the ELF-headers point of view Linux kernel- and user-space binaries + are the same. So we have to rely on some heuristics to distinguish them. + + For the heuristic we check for: + * the prefix register section in dumps. + * the __ksymtab section in the vmlinux and modules .ko files. + */ + +static enum gdb_osabi +s390_lk_osabi_sniffer (bfd *abfd) +{ + if (bfd_get_section_by_name (abfd, ".reg-s390-prefix") + || bfd_get_section_by_name (abfd, "__ksymtab")) + return GDB_OSABI_LINUX_KERNEL; + + return GDB_OSABI_UNKNOWN; +} + +extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */ + +void +_initialize_s390_lk_tdep (void) +{ + /* Hook us into the OSABI mechanism. */ + gdbarch_register_osabi (bfd_arch_s390, bfd_mach_s390_64, GDB_OSABI_LINUX_KERNEL, + s390_lk_init_abi_64); + /* Add OSABI sniffer. */ + gdbarch_register_osabi_sniffer (bfd_arch_s390, bfd_target_elf_flavour, + s390_lk_osabi_sniffer); + + /* Initialize the Linux kernel target descriptions. */ + initialize_tdesc_s390x_cr_linux64 (); + initialize_tdesc_s390x_vxcr_linux64 (); +} diff --git a/gdb/s390-lk-tdep.h b/gdb/s390-lk-tdep.h new file mode 100644 index 0000000000..e972d50167 --- /dev/null +++ b/gdb/s390-lk-tdep.h @@ -0,0 +1,39 @@ +/* Target-dependent code for linux-kernel target on S390. + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef S390_LK_TDEP_H +#define S390_LK_TDEP_H + +/* Constants copied from kernel sources. */ +#define S390_LK_PAGESIZE (1UL << 12) +#define S390_LK_STACKSIZE (S390_LK_PAGESIZE << 2) +#define S390_LK_ASYNCSIZE (S390_LK_STACKSIZE) + +/* Short hand access to gprs stored in struct pt_regs. */ +#define s390_lk_pt_regs_gpr(regnum) \ + (lk_offset ("pt_regs->gprs") \ + + (FIELD_TARGET_SIZE (lk_field ("pt_regs->gprs")) \ + * ((regnum) - S390_R0_REGNUM))) \ + +/* Short hand access to gprs stored in struct stack_frame. */ +#define s390_lk_stack_frame_gpr(regnum) \ + (lk_offset ("stack_frame->gprs") \ + + (FIELD_TARGET_SIZE (lk_field ("stack_frame->gprs")) \ + * ((regnum) - S390_R6_REGNUM))) \ + +#endif /* S390_LK_TDEP_H */ diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index 07b02afe3d..fc27a26702 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -2154,7 +2154,7 @@ s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) /* Helper routine to unwind pseudo registers. */ -static struct value * +struct value * s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum) { struct gdbarch *gdbarch = get_frame_arch (this_frame); @@ -2280,7 +2280,6 @@ s390_trad_frame_prev_register (struct frame_info *this_frame, else return s390_unwind_pseudo_register (this_frame, regnum); } - /* Normal stack frames. */ struct s390_unwind_cache { @@ -6790,6 +6789,10 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep, static const char *const gs_bc[] = { "bc_gsd", "bc_gssm", "bc_gsepla", }; + static const char *const cr[] = { + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", + "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15" + }; const struct target_desc *tdesc = tdep->tdesc; const struct tdesc_feature *feature; @@ -6891,6 +6894,34 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep, gs_bc); } + /* Control registers. */ + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr"); + if (feature) + s390_validate_reg_range (feature, tdesc_data, S390_CR0_REGNUM, cr); + + /* Privileged registers. */ + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged"); + if (feature) + { + if (!tdesc_numbered_register (feature, tdesc_data, + S390_TIMER_REGNUM, "timer")) + return false; + + if (!tdesc_numbered_register (feature, tdesc_data, + S390_TODCMP_REGNUM, "todcmp")) + return false; + + if (!tdesc_numbered_register (feature, tdesc_data, + S390_TODPREG_REGNUM, "todpreg")) + return false; + + if (!tdesc_numbered_register (feature, tdesc_data, + S390_PREFIX_REGNUM, "prefix")) + return false; + + tdep->have_privileged = true; + } + return true; } @@ -6918,6 +6949,7 @@ s390_gdbarch_tdep_alloc () tdep->have_tdb = false; tdep->have_vx = false; tdep->have_gs = false; + tdep->have_privileged = false; tdep->s390_syscall_record = NULL; diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h index a565f72d9c..63f2481010 100644 --- a/gdb/s390-tdep.h +++ b/gdb/s390-tdep.h @@ -60,6 +60,7 @@ struct gdbarch_tdep bool have_tdb; bool have_vx; bool have_gs; + bool have_privileged; /* Hook to record OS specific systemcall. */ int (*s390_syscall_record) (struct regcache *regcache, LONGEST svc_number); @@ -329,12 +330,77 @@ enum #define S390_RETADDR_REGNUM S390_R14_REGNUM #define S390_FRAME_REGNUM S390_R11_REGNUM +/* Definitions for address translation. */ +/* DAT Table types. */ +#define S390_LK_DAT_TT_REGION1 3 +#define S390_LK_DAT_TT_REGION2 2 +#define S390_LK_DAT_TT_REGION3 1 +#define S390_LK_DAT_TT_SEGMENT 0 + +/* Region-First-Table */ +#define S390_LK_RFTE_TL 0x3ULL /* Table-Length */ +#define S390_LK_RFTE_TT 0xcULL /* Table-Type */ +#define S390_LK_RFTE_I 0x20ULL /* Region-Invalid Bit */ +#define S390_LK_RFTE_TF 0xc0ULL /* Table Offset */ +#define S390_LK_RFTE_P 0x200ULL /* DAT-Protection Bit */ +#define S390_LK_RFTE_O ~0xfffULL /* Region-Second-Table Origin */ + +/* Region-Second-Table flags. */ +#define S390_LK_RSTE_TL 0x3ULL /* Table-Length */ +#define S390_LK_RSTE_TT 0xcULL /* Table-Type */ +#define S390_LK_RSTE_I 0x20ULL /* Region-Invalid Bit */ +#define S390_LK_RSTE_TF 0xc0ULL /* Table Offset */ +#define S390_LK_RSTE_P 0x200ULL /* DAT-Protection Bit */ +#define S390_LK_RSTE_O ~0xfffULL /* Region-Third-Table Origin */ + +/* Region-Third-Table flags. */ +#define S390_LK_RTTE_TL 0x3ULL /* Table-Length */ +#define S390_LK_RTTE_TT 0xcULL /* Table-Type */ +#define S390_LK_RTTE_CR 0x10ULL /* Common-Region Bit */ +#define S390_LK_RTTE_I 0x20ULL /* Region-Invalid Bit */ +#define S390_LK_RTTE_TF 0xc0ULL /* Table Offset */ +#define S390_LK_RTTE_P 0x200ULL /* DAT-Protection Bit */ +#define S390_LK_RTTE_FC 0x400ULL /* Format-Control Bit */ +#define S390_LK_RTTE_F 0x800ULL /* Fetch-Protection Bit */ +#define S390_LK_RTTE_ACC 0xf000ULL /* Access-Control Bits */ +#define S390_LK_RTTE_AV 0x10000ULL /* ACCF-Validity Control */ +#define S390_LK_RTTE_O ~0xfffULL /* Segment-Table Origin */ +#define S390_LK_RTTE_RFAA ~0x7fffffffULL /* Region-Frame Absolute Address */ + +/* Segment-Table flags. */ +#define S390_LK_STE_TT 0xcULL /* Table-Type */ +#define S390_LK_STE_I 0x20ULL /* Segment-Invalid Bit */ +#define S390_LK_STE_TF 0xc0ULL /* Table Offset */ +#define S390_LK_STE_P 0x200ULL /* DAT-Protection Bit */ +#define S390_LK_STE_FC 0x400ULL /* Format-Control Bit */ +#define S390_LK_STE_F 0x800ULL /* Fetch-Protection Bit */ +#define S390_LK_STE_ACC 0xf000ULL /* Access-Control Bits */ +#define S390_LK_STE_AV 0x10000ULL /* ACCF-Validity Control */ +#define S390_LK_STE_O ~0x7ffULL /* Page-Table Origin */ +#define S390_LK_STE_SFAA ~0xfffffULL /* Segment-Frame Absolute Address */ + +/* Page-Table flags. */ +#define S390_LK_PTE_P 0x200ULL /* DAT-Protection Bit */ +#define S390_LK_PTE_I 0x400ULL /* Page-Invalid Bit */ +#define S390_LK_PTE_PFAA ~0xfffULL /* Page-Frame Absolute Address */ + +/* Virtual Address Fields. */ +#define S390_LK_VADDR_RFX 0xffe0000000000000ULL +#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL +#define S390_LK_VADDR_RTX 0x000003ff80000000ULL +#define S390_LK_VADDR_SX 0x000000007ff00000ULL +#define S390_LK_VADDR_PX 0x00000000000ff000ULL +#define S390_LK_VADDR_BX 0x0000000000000fffULL + /* Frame unwinding. */ extern struct value *s390_trad_frame_prev_register (struct frame_info *this_frame, struct trad_frame_saved_reg saved_regs[], int regnum); +extern struct value *s390_unwind_pseudo_register (struct frame_info *this_frame, + int regnum); + extern struct target_desc *tdesc_s390_linux32; extern struct target_desc *tdesc_s390x_linux64;