From patchwork Mon Jun 12 17:39:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Philipp Rudo X-Patchwork-Id: 20971 Received: (qmail 42068 invoked by alias); 12 Jun 2017 17:39:10 -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 42048 invoked by uid 89); 12 Jun 2017 17:39:10 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KHOP_DYNAMIC, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 spammy=respectfully 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 Jun 2017 17:39:05 +0000 Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.20/8.16.0.20) with SMTP id v5CHcY7m075803 for ; Mon, 12 Jun 2017 13:39:08 -0400 Received: from e06smtp13.uk.ibm.com (e06smtp13.uk.ibm.com [195.75.94.109]) by mx0b-001b2d01.pphosted.com with ESMTP id 2b1v8ja0cd-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 12 Jun 2017 13:39:07 -0400 Received: from localhost by e06smtp13.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 12 Jun 2017 18:39:05 +0100 Received: from b06cxnps4075.portsmouth.uk.ibm.com (9.149.109.197) by e06smtp13.uk.ibm.com (192.168.101.143) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 12 Jun 2017 18:39:02 +0100 Received: from d06av26.portsmouth.uk.ibm.com (d06av26.portsmouth.uk.ibm.com [9.149.105.62]) by b06cxnps4075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v5CHd2NN35061948; Mon, 12 Jun 2017 17:39:02 GMT Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5EF16AE045; Mon, 12 Jun 2017 18:36:24 +0100 (BST) Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 211F1AE057; Mon, 12 Jun 2017 18:36:24 +0100 (BST) Received: from ThinkPad (unknown [9.152.212.148]) by d06av26.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 12 Jun 2017 18:36:24 +0100 (BST) Date: Mon, 12 Jun 2017 19:39:00 +0200 From: Philipp Rudo To: gdb-patches@sourceware.org Cc: omair.javaid@linaro.org, yao.qi@linaro.org, peter.griffin@linaro.org, arnez@linux.vnet.ibm.com Subject: [RFC v4 9/9] Add S390 support for linux-kernel target In-Reply-To: <20170612170836.25174-1-prudo@linux.vnet.ibm.com> References: <20170612170836.25174-1-prudo@linux.vnet.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 17061217-0012-0000-0000-0000054B008F X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17061217-0013-0000-0000-000018BAD71E Message-Id: <20170612193900.234bc540@ThinkPad> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-06-12_11:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=43 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1703280000 definitions=main-1706120307 X-IsSubscribed: yes Hi apparently the last mail got lost... So let's try it this way. --- After implementing the new linux-kernel target and preparing s390-tdep. It is now time to get everything to work. Thus implement the hooks required by the linux-kernel target and enable s390's privileged registers. 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. * s390-tdep.c (s390-lk-tdep.h): New include. (s390_iterate_over_regset_sections): Enable privileged registers. (s390_core_read_description): Enable privileged registers. (s390_gdbarch_init): Enable privileged registers and adjust. --- gdb/Makefile.in | 3 + gdb/configure.tgt | 5 +- gdb/s390-lk-tdep.c | 887 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/s390-lk-tdep.h | 54 ++++ gdb/s390-tdep.c | 57 +++- 5 files changed, 1001 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 9819b70f3d..cb31b65b5e 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -873,6 +873,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 \ @@ -1448,6 +1449,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 \ @@ -2655,6 +2657,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 b79a0bac4c..2788368c17 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -483,8 +483,9 @@ powerpc*-*-*) s390*-*-linux*) # Target: S390 running Linux - gdb_target_obs="s390-tdep.o s390-linux-tdep.o solib-svr4.o \ - linux-tdep.o linux-record.o ${lk_target_obs}" + gdb_target_obs="s390-tdep.o s390-linux-tdep.o s390-lk-tdep.o \ + solib-svr4.o linux-tdep.o linux-record.o \ + ${lk_target_obs}" build_gdbserver=yes ;; diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c new file mode 100644 index 0000000000..06da7795f0 --- /dev/null +++ b/gdb/s390-lk-tdep.c @@ -0,0 +1,887 @@ +/* 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 "dwarf2-frame.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 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 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, 4 }, + { 0 } + }; + +static const struct regcache_map_entry s390x_regmap_prefix [] = + { + { 1, S390_PREFIX_REGNUM, 4 }, + { 0 } + }; + +const struct regset s390x_gregset_lk = { + s390x_gregmap_lk, + regcache_supply_regset, + regcache_collect_regset +}; + +const struct regset s390x_cr_regset = { + s390x_regmap_cr, + regcache_supply_regset, + regcache_collect_regset +}; + +const struct regset s390x_timer_regset = { + s390x_regmap_timer, + regcache_supply_regset, + regcache_collect_regset +}; + +const struct regset s390x_todcmp_regset = { + s390x_regmap_todcmp, + regcache_supply_regset, + regcache_collect_regset +}; + +const struct regset s390x_todpreg_regset = { + s390x_regmap_todpreg, + regcache_supply_regset, + regcache_collect_regset +}; + +const struct regset s390x_prefix_regset = { + s390x_regmap_prefix, + regcache_supply_regset, + regcache_collect_regset +}; + + +/* General Helper functions. */ + +/* Get the lowcore of CPU. */ + +CORE_ADDR +s390_lk_get_lowcore (unsigned int cpu) +{ + CORE_ADDR lowcore_ptr, lowcore; + size_t ptr_len = lk_builtin_type_size (unsigned_long); + + lowcore_ptr = LK_ADDR (lowcore_ptr) + (ptr_len * cpu); + return lk_read_addr (lowcore_ptr); +} + + +/* Linux Kernel 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 (except the panic stack with only 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 = S390_LK_ROUNDUP (_##_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) +{ + struct s390_lk_unwind_cache *cache; + CORE_ADDR backchain, backchain_addr, prev_gpr15; + + if (*this_cache) + return (struct s390_lk_unwind_cache *) *this_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_STRUCT (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; + + pt_regs = cache->sp + TYPE_LENGTH (LK_STRUCT (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_ADDR (init_task) + && stack == S390_LK_STACK_KERNEL) + pt_regs += (TYPE_LENGTH (LK_STRUCT (stack_frame)) + + TYPE_LENGTH (LK_STRUCT (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; +} + +/* Function for frame_unwind->this_id hook for NORMAL_FRAMEs. */ + +static void +s390_lk_frame_this_id (struct frame_info *this_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct 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); +} + +/* Function for frame_unwind->prev_register hook for NORMAL_ and + SIGTRAMP_FRAMEs. */ + +static struct value * +s390_lk_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) +{ + struct 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); +} + +/* 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, + default_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) +{ + struct frame_info *next_frame; + struct s390_lk_unwind_cache *cache; + CORE_ADDR psw; + + if (*this_cache) + return (struct s390_lk_unwind_cache *) *this_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_STRUCT (pt_regs)); + + psw = cache->sp + LK_OFFSET (pt_regs, psw); + cache->pswm = psw + LK_OFFSET (psw_t, mask); + cache->pswa = psw + LK_OFFSET (psw_t, 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; +} + +/* Function for frame_unwind->this_id hook for SIGTRAMP_FRAMEs. */ + +static void +s390_lk_sigtramp_this_id (struct frame_info *this_frame, + void **this_cache, + struct frame_id *this_id) +{ + struct 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_ADDR (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)); + } + } +} + +/* Function for frame_unwind->sniffer hook for SIGTRAMP_FRAMEs. */ + +static int +s390_lk_sigtramp_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + + if (!gdbarch_lk_init_private_p (gdbarch)) + return 0; + + CORE_ADDR pc = get_frame_pc (this_frame); + + return pc == 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 +}; + + +/* Hooks for linux-kernel target. */ + +/* Function for Linux kernel target get_registers hook. Supplies gprs for + task TASK to REGCACHE. Uses r14 (back jump address) as current pswa. */ + +void +s390_lk_get_registers (CORE_ADDR task, struct target_ops *target, + struct regcache *regcache, int regnum) +{ + const struct regset *regset; + CORE_ADDR ksp, gprs, pswa; + gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */ + size_t size; + + regset = &s390x_gregset_lk; + + ksp = lk_read_addr (task + LK_OFFSET (task_struct, thread) + + LK_OFFSET (thread_struct, ksp)); + gprs = ksp + LK_OFFSET (stack_frame, gprs); + size = FIELD_SIZE (LK_FIELD (stack_frame, gprs)); + gdb_assert (size <= sizeof (buf)); + + read_memory (gprs, buf, size); + regset->supply_regset (regset, regcache, -1, buf, size); +} + +/* Function for Linux kernel target get_percpu_offset hook. Returns the + percpu_offset from lowcore for cpu CPU. */ + +CORE_ADDR +s390_lk_get_percpu_offset (unsigned int cpu) +{ + CORE_ADDR lowcore = s390_lk_get_lowcore (cpu); + return lk_read_addr (lowcore + LK_OFFSET (lowcore, percpu_offset)); +} + +/* Function for Linux kernel target map_running_task_to_cpu hook. */ + +unsigned int +s390_lk_map_running_task_to_cpu (struct thread_info *ti) +{ + struct regcache *regcache; + enum register_status reg_status; + CORE_ADDR lowcore; + unsigned int cpu; + + regcache = get_thread_regcache (ti->ptid); + reg_status = regcache_raw_read_unsigned (regcache, 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 ()); + + cpu = lk_read_uint (lowcore + LK_OFFSET (lowcore, cpu_nr)); + + return cpu; +} + +/* Function for Linux kernel target is_kvaddr hook. */ + +int +s390_lk_is_kvaddr (CORE_ADDR addr) +{ + return addr >= LK_ADDR (high_memory); +} + +/* Read table entry from TABLE at offset OFFSET. Helper for s390_lk_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)); +} + +/* Function for Linux kernel target vtop hook. Assume 64 bit addresses. */ + +CORE_ADDR +s390_lk_vtop (CORE_ADDR table, CORE_ADDR vaddr) +{ + ULONGEST entry, offset; + CORE_ADDR paddr; + unsigned int table_type; + size_t addr_size = lk_builtin_type_size (unsigned_long); + + /* 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) + { + paddr = ((entry & S390_LK_RTTE_RFAA) + + (vaddr & ~S390_LK_RTTE_RFAA)); + return paddr; + } + + 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) + { + paddr = ((entry & S390_LK_STE_SFAA) + + (vaddr & ~S390_LK_STE_SFAA)); + return paddr; + } + + 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)); + + paddr = ((entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA)); + + return paddr; +} + +/* Function for Linux kernel target adjust_module_layout hook. */ + +void +s390_lk_adjust_module_layout (CORE_ADDR mod, lk_module *module) +{ + CORE_ADDR mod_arch; + + mod_arch = mod + LK_OFFSET (module, arch); + + module->core->text.base + += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_offset)); + module->core->text.base + += lk_read_ulong (mod_arch + LK_OFFSET (mod_arch_specific, plt_size)); +} + +/* Initialize s390 dependent private data for linux kernel target. */ + +static void +s390_lk_init_private (struct gdbarch *gdbarch) +{ + LK_DECLARE_FIELD (stack_frame, gprs); + LK_DECLARE_FIELD (stack_frame, back_chain); + + LK_DECLARE_FIELD (pt_regs, psw); + LK_DECLARE_FIELD (pt_regs, gprs); + + LK_DECLARE_FIELD (task_struct, stack); + + LK_DECLARE_FIELD (thread_struct, ksp); + + LK_DECLARE_STRUCT_ALIAS (_lowcore, lowcore); /* linux -4.4 */ + LK_DECLARE_STRUCT_ALIAS (lowcore, lowcore); /* linux 4.5+ */ + if (LK_STRUCT (lowcore) == NULL) + error (_("Could not find struct lowcore. Aborting.")); + LK_DECLARE_FIELD (lowcore, percpu_offset); + LK_DECLARE_FIELD (lowcore, cpu_nr); + LK_DECLARE_FIELD (lowcore, async_stack); + LK_DECLARE_FIELD (lowcore, panic_stack); + LK_DECLARE_FIELD (lowcore, restart_stack); + + LK_DECLARE_FIELD (psw_t, mask); + LK_DECLARE_FIELD (psw_t, addr); + + LK_DECLARE_FIELD (mod_arch_specific, plt_offset); + LK_DECLARE_FIELD (mod_arch_specific, plt_size); + + LK_DECLARE_ADDR (lowcore_ptr); + LK_DECLARE_ADDR (high_memory); + + LK_HOOK->get_registers = s390_lk_get_registers; + LK_HOOK->is_kvaddr = s390_lk_is_kvaddr; + LK_HOOK->vtop = s390_lk_vtop; + LK_HOOK->adjust_module_layout = s390_lk_adjust_module_layout; + LK_HOOK->get_percpu_offset = s390_lk_get_percpu_offset; + LK_HOOK->map_running_task_to_cpu = s390_lk_map_running_task_to_cpu; +} + +/* Initialize Linux kernel specific gdbarch hooks. */ + +void +s390_lk_gdbarch_init (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + /* 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_lk_init_private (gdbarch, s390_lk_init_private); +} + +extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */ + +void +_initialize_s390_lk_tdep (void) +{ + /* 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..4e54e7d236 --- /dev/null +++ b/gdb/s390-lk-tdep.h @@ -0,0 +1,54 @@ +/* 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) +#define S390_LK_ROUNDUP(val, base) ((val) + (base) - ((val) % (base))) + +/* 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))) \ + +extern void s390_lk_gdbarch_init (struct gdbarch_info info, + struct gdbarch *gdbarch); + +/* Core file privileged register sets, defined in s390-lk-tdep.c. */ +extern const struct regset s390x_gregset_lk; +extern const struct regset s390x_cr_regset; +extern const struct regset s390x_timer_regset; +extern const struct regset s390x_todcmp_regset; +extern const struct regset s390x_todpreg_regset; +extern const struct regset s390x_prefix_regset; + +extern struct target_desc *tdesc_s390x_cr_linux64; +extern struct target_desc *tdesc_s390x_vxcr_linux64; + +#endif /* S390_LK_TDEP_H */ diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index 7d2906f294..e40fea120a 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -35,6 +35,7 @@ #include "record-full.h" #include "reggroups.h" #include "s390-linux-tdep.h" +#include "s390-lk-tdep.h" #include "s390-tdep.h" #include "target-descriptions.h" #include "value.h" @@ -962,7 +963,7 @@ s390_core_read_description (struct gdbarch *gdbarch, { asection *section = bfd_get_section_by_name (abfd, ".reg"); CORE_ADDR hwcap = 0; - int high_gprs, v1, v2, te, vx; + int high_gprs, v1, v2, te, vx, cr; target_auxv_search (target, AT_HWCAP, &hwcap); if (!section) @@ -974,6 +975,7 @@ s390_core_read_description (struct gdbarch *gdbarch, v2 = (bfd_get_section_by_name (abfd, ".reg-s390-system-call") != NULL); vx = (hwcap & HWCAP_S390_VX); te = (hwcap & HWCAP_S390_TE); + cr = (bfd_get_section_by_name (abfd, ".reg-s390-ctrs") != NULL); switch (bfd_section_size (abfd, section)) { @@ -990,6 +992,8 @@ s390_core_read_description (struct gdbarch *gdbarch, case s390x_sizeof_gregset: return (te && vx ? tdesc_s390x_tevx_linux64 : + vx && cr ? tdesc_s390x_vxcr_linux64 : + cr ? tdesc_s390x_cr_linux64 : vx ? tdesc_s390x_vx_linux64 : te ? tdesc_s390x_te_linux64 : v2 ? tdesc_s390x_linux64v2 : @@ -1046,6 +1050,22 @@ s390_iterate_over_regset_sections (struct gdbarch *gdbarch, 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", 4, &s390x_todpreg_regset, + "s390 TOD programable register", cb_data); + if (bfd_get_section_by_name (core_bfd, ".reg-s390-ctrs")) + cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset, + "s390 control registers", 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); } @@ -2302,6 +2322,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) int have_linux_v2 = 0; int have_tdb = 0; int have_vx = 0; + int have_privileged = 0; int first_pseudo_reg, last_pseudo_reg; static const char *const stap_register_prefixes[] = { "%", NULL }; static const char *const stap_register_indirection_prefixes[] = { "(", @@ -2476,6 +2497,30 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) have_vx = 1; } + /* Control registers. */ + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr"); + if (feature) + { + for (i = 0; i < 16; i++) + valid_p &= tdesc_numbered_register (feature, tdesc_data, + S390_CR0_REGNUM + i, cr[i]); + } + + /* Privileged registers. */ + feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged"); + if (feature) + { + valid_p &= tdesc_numbered_register (feature, tdesc_data, + S390_TIMER_REGNUM, "timer"); + valid_p &= tdesc_numbered_register (feature, tdesc_data, + S390_TODCMP_REGNUM, "todcmp"); + valid_p &= tdesc_numbered_register (feature, tdesc_data, + S390_TODPREG_REGNUM, "todpreg"); + valid_p &= tdesc_numbered_register (feature, tdesc_data, + S390_PREFIX_REGNUM, "prefix"); + have_privileged = 1; + } + if (!valid_p) { tdesc_data_cleanup (tdesc_data); @@ -2648,8 +2693,14 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_valid_disassembler_options (gdbarch, disassembler_options_s390 ()); - /* Note that GNU/Linux is the only OS supported on this platform. */ - s390_linux_gdbarch_init (info, gdbarch); + /* Note that GNU/Linux is the only OS supported on this platform. + Nevertheless we need to distinguish between a kernel- and user-space + gdbarch. GDBs osabi support is not sufficient for our case as for both + cases the ELF bits are for Linux. Thus fallback to this method. */ + if (have_privileged) + s390_lk_gdbarch_init (info, gdbarch); + else + s390_linux_gdbarch_init (info, gdbarch); return gdbarch; }