Patchwork [RFC,v5,9/9] Add S390 support for linux-kernel target

login
register
mail settings
Submitter Philipp Rudo
Date March 12, 2018, 3:31 p.m.
Message ID <20180312153115.47321-10-prudo@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/26293/
State New
Headers show

Comments

Philipp Rudo - March 12, 2018, 3:31 p.m.
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) <have_privileged>: 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

Patch

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 <http://www.gnu.org/licenses/>.  */
+
+#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 <http://www.gnu.org/licenses/>.  */
+
+#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;