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

login
register
mail settings
Submitter Philipp Rudo
Date June 12, 2017, 5:39 p.m.
Message ID <20170612193900.234bc540@ThinkPad>
Download mbox | patch
Permalink /patch/20971/
State New
Headers show

Comments

Philipp Rudo - June 12, 2017, 5:39 p.m.
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
Yao Qi - June 19, 2017, 1:20 p.m.
Philipp Rudo <prudo@linux.vnet.ibm.com> writes:

> +/* 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
> +};
> +

Did you consider adding different frame unwinders for frames on
different memory locations?  Your patches only adds two, but IMO, we
need more unwinders for different frames,

> +
> +/* 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;
> +}

The unwinders for different frames should be chained in this order.
Philipp Rudo - June 19, 2017, 3:45 p.m.
Hi Yao

On Mon, 19 Jun 2017 14:20:34 +0100
Yao Qi <qiyaoltc@gmail.com> wrote:

> Philipp Rudo <prudo@linux.vnet.ibm.com> writes:
> 
> > +/* 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
> > +};
> > +  
> 
> Did you consider adding different frame unwinders for frames on
> different memory locations?  Your patches only adds two, but IMO, we
> need more unwinders for different frames,

All those stacks (except user space) are working the same way.  That's why it
is sufficient to only add one unwinder.  In theory it is sufficient to only
distinguish three cases (KERNEL, INVALID and "OTHER") but for the sake of
completeness I also added the other possible cases.  This makes the code only a
little longer but makes extending it (e.g. to compensate a bug as been done for
the kernel stack) much easier.

Philipp

> > +
> > +/* 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;
> > +}  
> 
> The unwinders for different frames should be chained in this order.
>

Patch

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