This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] Support 32-bit applications using 64-bit register set on s390-linux


Hello,

System z hardware allows use of the full 64-bit general purpose
register set (and corresponding instructions) even to 32-bit
applications running under a 64-bit kernel.  Recent Linux kernels
have added support for this mode of operation.  In particular,
this affects the core file format: when such an application
crashes, we need not just the usual 32-bit register set, but
the full 64-bit register contents in order to enable debugging.
Also, the kernel needs to save and restore the full register
contents when calling a signal handler, as that handler might
itself be using the full 64-bit register set.

To preserve compatibility, the kernel does not change the existing
layout of the core register notes or the saved signal register
buffer.  Instead, these structure continue to contain the 32-bit
lower halves of the general purpose registers; in addition the
upper halves are stores separately -- in new core file notes and
an extension of the signal frame register buffer.

The following patch adds support for debugging applications making
use of this mode of operation.  GDB will consider such inferiors
to use the "s390:31-bit" architecture, but with a 64-bit general
purpose register set.

To achieve this, the s390 port is first switched to support XML
target descriptions providing extra register sets.  (Amongst other
changes, this requires that GDB pseudo registers no longer use
fixed register numbers.)  Three such sets are pre-defined; one each
corresponding to the current s390-linux and s390x-linux architectures,
and a new set implementing the 64-bit register set on the s390-linux
architecture.

This new set has two GDB registers for each GPR: one for the lower
half and one for the upper half.  The s390 port then defines a new
peseudo for each GPR representing the concatenation of the two.
The ABI defines the upper GPR halves to be all call-clobbered.
The various unwinders are changed to unwind full GPRs where possible
(in the signal frame case), and unwind GPRs to just the lower halves
where the upper halves are clobbered.  DWARF-2 register numbers are
mapped to the full GPR pseudos (except for CFI, where GPRs are again
mapped to simply the lower halves).

Support for detecting this mode of operation (e.g. by checking the
appropriate kernel hwcaps bit) and accessing the upper register
halves is added to Linux native, remote, and core file targets 
(including *generating* core files in this mode).

In addition to this patch, a number of additional patches are
required:

- binutils patch to support the new core file register notes:
  http://sourceware.org/ml/binutils/2009-12/msg00246.html
  (already committed)

- patch to handle DWARF-2 value pieces in register parts
- patch to use gdbarch_core_regset_sections in get_core_registers
- patch to re-parse display expressions if the architecture changed
  (posted in conjunction with this patch)

The full patch set was tested on s390x-linux in 31-bit and 64-bit
mode, both on a machine with kernel supporting the feature and on
a kernel that does not, and on a s390-linux machine.  All tests
where performed both natively and in local-gdbserver mode.

Any comments?  I'm planning on committing this next week.

Bye,
Ulrich


ChangeLog:

        * features/Makefile (WHICH): Add s390-linux32, s390-linux64,
	and s390x-linux64.
	(s390-linux32-expedite): Define.
	(s390-linux64-expedite): Define.
	(s390x-linux64-expedite): Define.
	* features/s390-acr.xml: New file.
	* features/s390-fpr.xml: New file.
	* features/s390-core32.xml: New file.
	* features/s390-core64.xml: New file.
	* features/s390x-core64.xml: New file.
	* features/s390-linux32.xml: New file.
	* features/s390-linux64.xml: New file.
	* features/s390x-linux64.xml: New file.
	* features/s390-linux32.c: New generated file.
	* features/s390-linux64.c: New generated file.
	* features/s390x-linux64.c: New generated file.

	* regformats/s390-linux32.dat: New generated file.
	* regformats/s390-linux64.dat: New generated file.
	* regformats/s390x-linux64.dat: New generated file.
	* regformats/reg-s390.dat: Remove.
	* regformats/reg-s390x.dat: Remove.

	* s390-nat.c: Include "auxv.h" and <elf.h>.
	(HWCAP_S390_HIGH_GPRS): Define if undefined.
	(s390_target_wordsize): New function.
	(s390_auxv_parse): Likewise.
	(s390_get_hwcap): Likewise.
	(s390_read_description): Likewise.
	(_initialize_s390_nat): Install s390_auxv_parse and
	s390_read_description.

	* s390-tdep.c: Include "features/s390-linux32.c",
	"features/s390-linux64.c", and "features/s390x-linux64.c".
	(struct gdbarch_tdep): Add gpr_full_regnum, pc_regnum, and cc_regnum.
	(s390_register_call_saved): New function.
	(s390_register_name): Remove.
	(s390_register_type): Remove.
	(s390_dwarf_regmap): Add lower half GPR pseudo DWARF CFI regnums.
	(s390_dwarf_reg_to_regnum): Remap GPR regnums to full GPRs.
	(s390_adjust_frame_regnum): Remap GPR regnums to lower halves for CFI.
	(s390_pseudo_register_name): New function.
	(s390_pseudo_register_type): New function.
	(s390_pseudo_register_read): Handle both 32-bit and 64-bit cases.
	Handle full GPR pesudos and varying pseudo register numbers.
	(s390_pseudo_register_write): Likewise
	(s390x_pseudo_register_read): Remove.
	(s390x_pseudo_register_write): Likewise.
	(s390_register_group): Remove.
	(s390_pseudo_register_group): New function.
	(s390_regmap_gregset): Add GPR upper halves.
	(s390x_regmap_gregset): Likewise.
	(s390_regmap_fpregset): Likewise.
	(s390_regmap_upper): New global variable.
	(s390_upper_regset): New global variable.
	(s390_upper_regset_sections): New global variable.
	(s390_regset_from_core_section): Handle GPR upper halves.
	(s390_core_read_description): New function.
	(s390_prologue_frame_unwind_cache): Set up ABI call-saved/clobbered
	register information.  Handle varying pseudo register numbers.
	(s390_backchain_frame_unwind_cache): Likewise.
	(s390_frame_prev_register): Unwind full GPRs to show lower halves.
	(s390_stub_frame_unwind_cache): Handle varying pseudo register numbers.
	(s390_sigtramp_frame_unwind_cache): Unwind PSWM and PSWA as well as
	PC and CC pseudos.  Unwind upper halves and full GPRs as appropriate.
	Handle varying pseudo register numbers.
	(s390_unwind_pc): Handle varying pseudo register numbers.
	(s390_dwarf2_prev_register): New function.
	(s390_dwarf2_frame_init_reg): Set up ABI call-saved/clobbered
	register information.  Handle varying pseudo register numbers.
	Install s390_dwarf2_prev_register to unwind full GPRs.
	(s390_gdbarch_init): Handle target descriptions.  Assign varying
	pseudo register numbers.  Install s390_adjust_frame_regnum.
	(_initialize_s390_tdep): Initialize target descriptions.

	* s390-tdep.h (S390_R0_UPPER_REGNUM .. S390_R15_UPPER_REGNUM): Define.
	(S390_NUM_REGS): Redefine to include upper half registers.
	(S390_PC_REGNUM, S390_CC_REGNUM): Remove.
	(S390_NUM_PSEUDO_REGS, S390_NUM_TOTAL_REGS): Likewise.
	(tdesc_s390_linux32): Add declaration.
	(tdesc_s390_linux64): Likewise.
	(tdesc_s390x_linux64): Likewise.

gdb/testsuite/
	* gdb.xml/tdesc-regs.exp: Support s390*-*-* targets.

gdbserver/
	* Makefile.in (clean): Remove new generated files.
	(reg-s390.o, reg-s390.c): Remove rules.
	(reg-s390x.o, reg-s390x.c): Likewise.
	(s390-linux32.o, s390-linux32.c): Add rules.
	(s390-linux64.o, s390-linux64.c): Likewise.
	(s390x-linux64.o, s390x-linux64.c): Likewise.
	* configure.srv (s390*-*-linux*): Update srv_regobj and srv_xmlfiles.
	* linux-s390-low.c: Include <elf.h>.
	(HWCAP_S390_HIGH_GPRS): Define if undefined.
	(init_registers_s390): Remove prototype.
	(init_registers_s390x): Likewise.
	(init_registers_s390_linux32): Add prototype.
	(init_registers_s390_linux64): Likewise.
	(init_registers_s390x_linux64): Likewise.
	(s390_num_regs_3264): New define.
	(s390_regmap_3264): New global variable.
	(s390_cannot_fetch_register): Remove obsolete check.
	(s390_cannot_store_register): Likewise.
	(s390_collect_ptrace_register): Handle upper/lower register halves.
	(s390_supply_ptrace_register): Likewise.
	(s390_fill_gregset): Update to register number changes.
	(s390_get_hwcap): New routine.
	(s390_arch_setup): Detect 32-bit process running on 64-bit system.
	Install appropriate regmap and register set.


diff -urNp gdb-orig/gdb/features/Makefile gdb-head/gdb/features/Makefile
--- gdb-orig/gdb/features/Makefile	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/features/Makefile	2009-12-18 17:42:44.000000000 +0100
@@ -35,7 +35,8 @@ WHICH = arm-with-iwmmxt arm-with-vfpv2 a
 	mips-linux mips64-linux \
 	rs6000/powerpc-32l rs6000/powerpc-altivec32l rs6000/powerpc-e500l \
 	rs6000/powerpc-64l rs6000/powerpc-altivec64l rs6000/powerpc-vsx32l \
-	rs6000/powerpc-vsx64l rs6000/powerpc-cell32l rs6000/powerpc-cell64l
+	rs6000/powerpc-vsx64l rs6000/powerpc-cell32l rs6000/powerpc-cell64l \
+	s390-linux32 s390-linux64 s390x-linux64
 
 # Record which registers should be sent to GDB by default after stop.
 arm-expedite = r11,sp,pc
@@ -44,6 +45,9 @@ mips64-expedite = r29,pc
 powerpc-expedite = r1,pc
 rs6000/powerpc-cell32l-expedite = r1,pc,r0,orig_r3,r4
 rs6000/powerpc-cell64l-expedite = r1,pc,r0,orig_r3,r4
+s390-linux32-expedite = r14,r15,pswa
+s390-linux64-expedite = r14l,r15l,pswa
+s390x-linux64-expedite = r14,r15,pswa
 
 
 XSLTPROC = xsltproc
diff -urNp gdb-orig/gdb/features/s390-acr.xml gdb-head/gdb/features/s390-acr.xml
--- gdb-orig/gdb/features/s390-acr.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-acr.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.acr">
+  <reg name="acr0" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr1" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr2" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr3" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr4" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr5" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr6" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr7" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr8" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr9" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr10" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr11" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr12" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr13" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr14" bitsize="32" type="uint32" group="access"/>
+  <reg name="acr15" bitsize="32" type="uint32" group="access"/>
+</feature>
diff -urNp gdb-orig/gdb/features/s390-core32.xml gdb-head/gdb/features/s390-core32.xml
--- gdb-orig/gdb/features/s390-core32.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-core32.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.core">
+  <reg name="pswm" bitsize="32" type="uint32" group="psw" save-restore="no"/>
+  <reg name="pswa" bitsize="32" type="uint32" group="psw" save-restore="no"/>
+  <reg name="r0" bitsize="32" type="uint32" group="general"/>
+  <reg name="r1" bitsize="32" type="uint32" group="general"/>
+  <reg name="r2" bitsize="32" type="uint32" group="general"/>
+  <reg name="r3" bitsize="32" type="uint32" group="general"/>
+  <reg name="r4" bitsize="32" type="uint32" group="general"/>
+  <reg name="r5" bitsize="32" type="uint32" group="general"/>
+  <reg name="r6" bitsize="32" type="uint32" group="general"/>
+  <reg name="r7" bitsize="32" type="uint32" group="general"/>
+  <reg name="r8" bitsize="32" type="uint32" group="general"/>
+  <reg name="r9" bitsize="32" type="uint32" group="general"/>
+  <reg name="r10" bitsize="32" type="uint32" group="general"/>
+  <reg name="r11" bitsize="32" type="uint32" group="general"/>
+  <reg name="r12" bitsize="32" type="uint32" group="general"/>
+  <reg name="r13" bitsize="32" type="uint32" group="general"/>
+  <reg name="r14" bitsize="32" type="uint32" group="general"/>
+  <reg name="r15" bitsize="32" type="uint32" group="general"/>
+</feature>
diff -urNp gdb-orig/gdb/features/s390-core64.xml gdb-head/gdb/features/s390-core64.xml
--- gdb-orig/gdb/features/s390-core64.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-core64.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.core">
+  <reg name="pswm" bitsize="32" type="uint32" group="psw" save-restore="no"/>
+  <reg name="pswa" bitsize="32" type="uint32" group="psw" save-restore="no"/>
+  <reg name="r0h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r0l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r1h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r1l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r2h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r2l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r3h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r3l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r4h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r4l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r5h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r5l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r6h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r6l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r7h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r7l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r8h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r8l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r9h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r9l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r10h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r10l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r11h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r11l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r12h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r12l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r13h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r13l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r14h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r14l" bitsize="32" type="uint32" group="lower"/>
+  <reg name="r15h" bitsize="32" type="uint32" group="upper"/>
+  <reg name="r15l" bitsize="32" type="uint32" group="lower"/>
+</feature>
+
diff -urNp gdb-orig/gdb/features/s390-fpr.xml gdb-head/gdb/features/s390-fpr.xml
--- gdb-orig/gdb/features/s390-fpr.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-fpr.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.fpr">
+  <reg name="fpc" bitsize="32" type="uint32" group="float"/>
+  <reg name="f0" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f1" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f2" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f3" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f4" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f5" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f6" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f7" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f8" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f9" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f10" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f11" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f12" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f13" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f14" bitsize="64" type="ieee_double" group="float"/>
+  <reg name="f15" bitsize="64" type="ieee_double" group="float"/>
+</feature>
diff -urNp gdb-orig/gdb/features/s390-linux32.c gdb-head/gdb/features/s390-linux32.c
--- gdb-orig/gdb/features/s390-linux32.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-linux32.c	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,74 @@
+/* THIS FILE IS GENERATED.  Original: s390-linux32.xml */
+
+#include "defs.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390_linux32;
+static void
+initialize_tdesc_s390_linux32 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+  struct tdesc_type *field_type, *type;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:31-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 0, "psw", 32, "uint32");
+  tdesc_create_reg (feature, "pswa", 1, 0, "psw", 32, "uint32");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  tdesc_s390_linux32 = result;
+}
diff -urNp gdb-orig/gdb/features/s390-linux32.xml gdb-head/gdb/features/s390-linux32.xml
--- gdb-orig/gdb/features/s390-linux32.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-linux32.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 31-bit user-level code on a machine operating
+     in ESA/390 architecture mode.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:31-bit</architecture>
+  <xi:include href="s390-core32.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+</target>
diff -urNp gdb-orig/gdb/features/s390-linux64.c gdb-head/gdb/features/s390-linux64.c
--- gdb-orig/gdb/features/s390-linux64.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-linux64.c	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,90 @@
+/* THIS FILE IS GENERATED.  Original: s390-linux64.xml */
+
+#include "defs.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390_linux64;
+static void
+initialize_tdesc_s390_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+  struct tdesc_type *field_type, *type;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:31-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 0, "psw", 32, "uint32");
+  tdesc_create_reg (feature, "pswa", 1, 0, "psw", 32, "uint32");
+  tdesc_create_reg (feature, "r0h", 2, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r0l", 3, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r1h", 4, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r1l", 5, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r2h", 6, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r2l", 7, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r3h", 8, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r3l", 9, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r4h", 10, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r4l", 11, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r5h", 12, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r5l", 13, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r6h", 14, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r6l", 15, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r7h", 16, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r7l", 17, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r8h", 18, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r8l", 19, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r9h", 20, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r9l", 21, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r10h", 22, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r10l", 23, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r11h", 24, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r11l", 25, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r12h", 26, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r12l", 27, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r13h", 28, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r13l", 29, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r14h", 30, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r14l", 31, 1, "lower", 32, "uint32");
+  tdesc_create_reg (feature, "r15h", 32, 1, "upper", 32, "uint32");
+  tdesc_create_reg (feature, "r15l", 33, 1, "lower", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 34, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 35, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 36, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 37, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 38, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 39, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 40, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 41, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 42, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 43, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 44, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 45, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 46, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 47, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 48, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 49, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 50, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 51, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 52, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 53, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 54, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 55, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 56, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 57, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 58, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 59, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 60, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 61, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 62, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 63, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 64, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 65, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 66, 1, "float", 64, "ieee_double");
+
+  tdesc_s390_linux64 = result;
+}
diff -urNp gdb-orig/gdb/features/s390-linux64.xml gdb-head/gdb/features/s390-linux64.xml
--- gdb-orig/gdb/features/s390-linux64.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390-linux64.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 31-bit user-level code on a machine operating
+     in z/Architecture mode.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:31-bit</architecture>
+  <xi:include href="s390-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+</target>
diff -urNp gdb-orig/gdb/features/s390x-core64.xml gdb-head/gdb/features/s390x-core64.xml
--- gdb-orig/gdb/features/s390x-core64.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390x-core64.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.s390.core">
+  <reg name="pswm" bitsize="64" type="uint64" group="psw" save-restore="no"/>
+  <reg name="pswa" bitsize="64" type="uint64" group="psw" save-restore="no"/>
+  <reg name="r0" bitsize="64" type="uint64" group="general"/>
+  <reg name="r1" bitsize="64" type="uint64" group="general"/>
+  <reg name="r2" bitsize="64" type="uint64" group="general"/>
+  <reg name="r3" bitsize="64" type="uint64" group="general"/>
+  <reg name="r4" bitsize="64" type="uint64" group="general"/>
+  <reg name="r5" bitsize="64" type="uint64" group="general"/>
+  <reg name="r6" bitsize="64" type="uint64" group="general"/>
+  <reg name="r7" bitsize="64" type="uint64" group="general"/>
+  <reg name="r8" bitsize="64" type="uint64" group="general"/>
+  <reg name="r9" bitsize="64" type="uint64" group="general"/>
+  <reg name="r10" bitsize="64" type="uint64" group="general"/>
+  <reg name="r11" bitsize="64" type="uint64" group="general"/>
+  <reg name="r12" bitsize="64" type="uint64" group="general"/>
+  <reg name="r13" bitsize="64" type="uint64" group="general"/>
+  <reg name="r14" bitsize="64" type="uint64" group="general"/>
+  <reg name="r15" bitsize="64" type="uint64" group="general"/>
+</feature>
diff -urNp gdb-orig/gdb/features/s390x-linux64.c gdb-head/gdb/features/s390x-linux64.c
--- gdb-orig/gdb/features/s390x-linux64.c	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390x-linux64.c	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,74 @@
+/* THIS FILE IS GENERATED.  Original: s390x-linux64.xml */
+
+#include "defs.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_linux64;
+static void
+initialize_tdesc_s390x_linux64 (void)
+{
+  struct target_desc *result = allocate_target_description ();
+  struct tdesc_feature *feature;
+  struct tdesc_type *field_type, *type;
+
+  set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+  tdesc_create_reg (feature, "pswm", 0, 0, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "pswa", 1, 0, "psw", 64, "uint64");
+  tdesc_create_reg (feature, "r0", 2, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", 3, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", 4, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", 5, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", 6, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", 7, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", 8, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", 9, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", 10, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", 11, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", 12, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", 13, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", 14, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", 15, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", 16, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", 17, 1, "general", 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.acr");
+  tdesc_create_reg (feature, "acr0", 18, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr1", 19, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr2", 20, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr3", 21, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr4", 22, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr5", 23, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr6", 24, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr7", 25, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr8", 26, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr9", 27, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr10", 28, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr11", 29, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr12", 30, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr13", 31, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr14", 32, 1, "access", 32, "uint32");
+  tdesc_create_reg (feature, "acr15", 33, 1, "access", 32, "uint32");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.s390.fpr");
+  tdesc_create_reg (feature, "fpc", 34, 1, "float", 32, "uint32");
+  tdesc_create_reg (feature, "f0", 35, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f1", 36, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f2", 37, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f3", 38, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f4", 39, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f5", 40, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f6", 41, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f7", 42, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f8", 43, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f9", 44, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f10", 45, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f11", 46, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f12", 47, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f13", 48, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f14", 49, 1, "float", 64, "ieee_double");
+  tdesc_create_reg (feature, "f15", 50, 1, "float", 64, "ieee_double");
+
+  tdesc_s390x_linux64 = result;
+}
diff -urNp gdb-orig/gdb/features/s390x-linux64.xml gdb-head/gdb/features/s390x-linux64.xml
--- gdb-orig/gdb/features/s390x-linux64.xml	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/features/s390x-linux64.xml	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- S/390 64-bit user-level code.  -->
+
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target>
+  <architecture>s390:64-bit</architecture>
+  <xi:include href="s390x-core64.xml"/>
+  <xi:include href="s390-acr.xml"/>
+  <xi:include href="s390-fpr.xml"/>
+</target>
diff -urNp gdb-orig/gdb/gdbserver/Makefile.in gdb-head/gdb/gdbserver/Makefile.in
--- gdb-orig/gdb/gdbserver/Makefile.in	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/gdbserver/Makefile.in	2009-12-18 17:42:44.000000000 +0100
@@ -215,6 +215,7 @@ clean:
 	rm -f powerpc-isa205-32l.c powerpc-isa205-64l.c
 	rm -f powerpc-isa205-altivec32l.c powerpc-isa205-vsx32l.c powerpc-isa205-altivec64l.c
 	rm -f powerpc-isa205-vsx64l.c
+	rm -f s390-linux32.c s390-linux64.c s390x-linux64.c
 	rm -f xml-builtin.c stamp-xml
 
 maintainer-clean realclean distclean: clean
@@ -413,12 +414,15 @@ powerpc-isa205-altivec64l.c : $(srcdir)/
 powerpc-isa205-vsx64l.o : powerpc-isa205-vsx64l.c $(regdef_h)
 powerpc-isa205-vsx64l.c : $(srcdir)/../regformats/rs6000/powerpc-isa205-vsx64l.dat $(regdat_sh)
 	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/rs6000/powerpc-isa205-vsx64l.dat powerpc-isa205-vsx64l.c
-reg-s390.o : reg-s390.c $(regdef_h)
-reg-s390.c : $(srcdir)/../regformats/reg-s390.dat $(regdat_sh)
-	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-s390.dat reg-s390.c
-reg-s390x.o : reg-s390x.c $(regdef_h)
-reg-s390x.c : $(srcdir)/../regformats/reg-s390x.dat $(regdat_sh)
-	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-s390x.dat reg-s390x.c
+s390-linux32.o : s390-linux32.c $(regdef_h)
+s390-linux32.c : $(srcdir)/../regformats/s390-linux32.dat $(regdat_sh)
+	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/s390-linux32.dat s390-linux32.c
+s390-linux64.o : s390-linux64.c $(regdef_h)
+s390-linux64.c : $(srcdir)/../regformats/s390-linux64.dat $(regdat_sh)
+	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/s390-linux64.dat s390-linux64.c
+s390x-linux64.o : s390x-linux64.c $(regdef_h)
+s390x-linux64.c : $(srcdir)/../regformats/s390x-linux64.dat $(regdat_sh)
+	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/s390x-linux64.dat s390x-linux64.c
 reg-sh.o : reg-sh.c $(regdef_h)
 reg-sh.c : $(srcdir)/../regformats/reg-sh.dat $(regdat_sh)
 	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/reg-sh.dat reg-sh.c
diff -urNp gdb-orig/gdb/gdbserver/configure.srv gdb-head/gdb/gdbserver/configure.srv
--- gdb-orig/gdb/gdbserver/configure.srv	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/gdbserver/configure.srv	2009-12-18 17:42:44.000000000 +0100
@@ -176,8 +176,18 @@ case "${target}" in
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
-  s390*-*-linux*)	srv_regobj="reg-s390.o reg-s390x.o"
+  s390*-*-linux*)	srv_regobj="s390-linux32.o"
+			srv_regobj="${srv_regobj} s390-linux64.o"
+			srv_regobj="${srv_regobj} s390x-linux64.o"
 			srv_tgtobj="linux-low.o linux-s390-low.o"
+			srv_xmlfiles="s390-linux32.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390-linux64.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390x-linux64.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390-core32.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390-core64.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390x-core64.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390-acr.xml"
+			srv_xmlfiles="${srv_xmlfiles} s390-fpr.xml"
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
diff -urNp gdb-orig/gdb/gdbserver/linux-s390-low.c gdb-head/gdb/gdbserver/linux-s390-low.c
--- gdb-orig/gdb/gdbserver/linux-s390-low.c	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/gdbserver/linux-s390-low.c	2009-12-18 17:42:44.000000000 +0100
@@ -24,12 +24,18 @@
 #include "linux-low.h"
 
 #include <asm/ptrace.h>
+#include <elf.h>
 
-/* Defined in auto-generated file reg-s390.c.  */
-void init_registers_s390 (void);
-/* Defined in auto-generated file reg-s390x.c.  */
-void init_registers_s390x (void);
+#ifndef HWCAP_S390_HIGH_GPRS
+#define HWCAP_S390_HIGH_GPRS 512
+#endif
 
+/* Defined in auto-generated file s390-linux32.c.  */
+void init_registers_s390_linux32 (void);
+/* Defined in auto-generated file s390-linux64.c.  */
+void init_registers_s390_linux64 (void);
+/* Defined in auto-generated file s390x-linux64.c.  */
+void init_registers_s390x_linux64 (void);
 
 #define s390_num_regs 51
 
@@ -61,21 +67,41 @@ static int s390_regmap[] = {
 #endif
 };
 
+#ifdef __s390x__
+#define s390_num_regs_3264 67
+
+static int s390_regmap_3264[] = {
+  PT_PSWMASK, PT_PSWADDR,
+
+  PT_GPR0, PT_GPR0, PT_GPR1, PT_GPR1, PT_GPR2, PT_GPR2, PT_GPR3, PT_GPR3,
+  PT_GPR4, PT_GPR4, PT_GPR5, PT_GPR5, PT_GPR6, PT_GPR6, PT_GPR7, PT_GPR7,
+  PT_GPR8, PT_GPR8, PT_GPR9, PT_GPR9, PT_GPR10, PT_GPR10, PT_GPR11, PT_GPR11,
+  PT_GPR12, PT_GPR12, PT_GPR13, PT_GPR13, PT_GPR14, PT_GPR14, PT_GPR15, PT_GPR15,
+
+  PT_ACR0, PT_ACR1, PT_ACR2, PT_ACR3,
+  PT_ACR4, PT_ACR5, PT_ACR6, PT_ACR7,
+  PT_ACR8, PT_ACR9, PT_ACR10, PT_ACR11,
+  PT_ACR12, PT_ACR13, PT_ACR14, PT_ACR15,
+
+  PT_FPC,
+
+  PT_FPR0, PT_FPR1, PT_FPR2, PT_FPR3,
+  PT_FPR4, PT_FPR5, PT_FPR6, PT_FPR7,
+  PT_FPR8, PT_FPR9, PT_FPR10, PT_FPR11,
+  PT_FPR12, PT_FPR13, PT_FPR14, PT_FPR15,
+};
+#endif
+
+
 static int
 s390_cannot_fetch_register (int regno)
 {
-  if (s390_regmap[regno] == -1)
-    return 1;
-
   return 0;
 }
 
 static int
 s390_cannot_store_register (int regno)
 {
-  if (s390_regmap[regno] == -1)
-    return 1;
-
   return 0;
 }
 
@@ -85,17 +111,25 @@ s390_collect_ptrace_register (int regno,
   int size = register_size (regno);
   if (size < sizeof (long))
     {
+      int regaddr = the_low_target.regmap[regno];
+
       memset (buf, 0, sizeof (long));
 
-      if (regno == find_regno ("pswa")
-	  || (regno >= find_regno ("r0") && regno <= find_regno ("r15")))
+      if ((regno ^ 1) < the_low_target.num_regs
+	  && the_low_target.regmap[regno ^ 1] == regaddr)
+	{
+	  collect_register (regno & ~1, buf);
+	  collect_register ((regno & ~1) + 1, buf + sizeof (long) - size);
+	}
+      else if (regaddr == PT_PSWADDR
+	       || (regaddr >= PT_GPR0 && regaddr <= PT_GPR15))
 	collect_register (regno, buf + sizeof (long) - size);
       else
 	collect_register (regno, buf);
 
       /* When debugging a 32-bit inferior on a 64-bit host, make sure
 	 the 31-bit addressing mode bit is set in the PSW mask.  */
-      if (regno == find_regno ("pswm"))
+      if (regaddr == PT_PSWMASK)
 	buf[size] |= 0x80;
     }
   else
@@ -108,8 +142,16 @@ s390_supply_ptrace_register (int regno, 
   int size = register_size (regno);
   if (size < sizeof (long))
     {
-      if (regno == find_regno ("pswa")
-	  || (regno >= find_regno ("r0") && regno <= find_regno ("r15")))
+      int regaddr = the_low_target.regmap[regno];
+
+      if ((regno ^ 1) < the_low_target.num_regs
+	  && the_low_target.regmap[regno ^ 1] == regaddr)
+	{
+	  supply_register (regno & ~1, buf);
+	  supply_register ((regno & ~1) + 1, buf + sizeof (long) - size);
+	}
+      else if (regaddr == PT_PSWADDR
+	       || (regaddr >= PT_GPR0 && regaddr <= PT_GPR15))
 	supply_register (regno, buf + sizeof (long) - size);
       else
 	supply_register (regno, buf);
@@ -125,8 +167,15 @@ static void s390_fill_gregset (void *buf
 {
   int i;
 
-  for (i = 0; i < 34; i++)
-    s390_collect_ptrace_register (i, (char *) buf + s390_regmap[i]);
+  for (i = 0; i < the_low_target.num_regs; i++)
+    {
+      if (the_low_target.regmap[i] < PT_PSWMASK
+	  || the_low_target.regmap[i] > PT_ACR15)
+	continue;
+
+      s390_collect_ptrace_register (i, (char *) buf
+				       + the_low_target.regmap[i]);
+    }
 }
 
 struct regset_info target_regsets[] = {
@@ -176,12 +225,43 @@ s390_set_pc (CORE_ADDR newpc)
     }
 }
 
+#ifdef __s390x__
+static unsigned long
+s390_get_hwcap (void)
+{
+  int wordsize = register_size (0);
+  unsigned char *data = alloca (2 * wordsize);
+  int offset = 0;
+
+  while ((*the_target->read_auxv) (offset, data, 2 * wordsize) == 2 * wordsize)
+    {
+      if (wordsize == 4)
+        {
+          unsigned int *data_p = (unsigned int *)data;
+          if (data_p[0] == AT_HWCAP)
+	    return data_p[1];
+        }
+      else
+        {
+          unsigned long *data_p = (unsigned long *)data;
+          if (data_p[0] == AT_HWCAP)
+	    return data_p[1];
+        }
+
+      offset += 2 * wordsize;
+    }
+
+  return 0;
+}
+#endif
 
 static void
 s390_arch_setup (void)
 {
   /* Assume 31-bit inferior process.  */
-  init_registers_s390 ();
+  init_registers_s390_linux32 ();
+  the_low_target.num_regs = s390_num_regs;
+  the_low_target.regmap = s390_regmap;
 
   /* On a 64-bit host, check the low bit of the (31-bit) PSWM
      -- if this is one, we actually have a 64-bit inferior.  */
@@ -190,7 +270,16 @@ s390_arch_setup (void)
     unsigned int pswm;
     collect_register_by_name ("pswm", &pswm);
     if (pswm & 1)
-      init_registers_s390x ();
+      init_registers_s390x_linux64 ();
+
+    /* For a 31-bit inferior, check whether the kernel supports
+       using the full 64-bit GPRs.  */
+    else if (s390_get_hwcap () & HWCAP_S390_HIGH_GPRS)
+      {
+        init_registers_s390_linux64 ();
+	the_low_target.num_regs = s390_num_regs_3264;
+	the_low_target.regmap = s390_regmap_3264;
+      }
   }
 #endif
 }
diff -urNp gdb-orig/gdb/regformats/reg-s390.dat gdb-head/gdb/regformats/reg-s390.dat
--- gdb-orig/gdb/regformats/reg-s390.dat	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/regformats/reg-s390.dat	1970-01-01 01:00:00.000000000 +0100
@@ -1,53 +0,0 @@
-name:s390
-expedite:r14,r15,pswa
-32:pswm
-32:pswa
-32:r0
-32:r1
-32:r2
-32:r3
-32:r4
-32:r5
-32:r6
-32:r7
-32:r8
-32:r9
-32:r10
-32:r11
-32:r12
-32:r13
-32:r14
-32:r15
-32:acr0
-32:acr1
-32:acr2
-32:acr3
-32:acr4
-32:acr5
-32:acr6
-32:acr7
-32:acr8
-32:acr9
-32:acr10
-32:acr11
-32:acr12
-32:acr13
-32:acr14
-32:acr15
-32:fpc
-64:f0
-64:f1
-64:f2
-64:f3
-64:f4
-64:f5
-64:f6
-64:f7
-64:f8
-64:f9
-64:f10
-64:f11
-64:f12
-64:f13
-64:f14
-64:f15
diff -urNp gdb-orig/gdb/regformats/reg-s390x.dat gdb-head/gdb/regformats/reg-s390x.dat
--- gdb-orig/gdb/regformats/reg-s390x.dat	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/regformats/reg-s390x.dat	1970-01-01 01:00:00.000000000 +0100
@@ -1,53 +0,0 @@
-name:s390x
-expedite:r14,r15,pswa
-64:pswm
-64:pswa
-64:r0
-64:r1
-64:r2
-64:r3
-64:r4
-64:r5
-64:r6
-64:r7
-64:r8
-64:r9
-64:r10
-64:r11
-64:r12
-64:r13
-64:r14
-64:r15
-32:acr0
-32:acr1
-32:acr2
-32:acr3
-32:acr4
-32:acr5
-32:acr6
-32:acr7
-32:acr8
-32:acr9
-32:acr10
-32:acr11
-32:acr12
-32:acr13
-32:acr14
-32:acr15
-32:fpc
-64:f0
-64:f1
-64:f2
-64:f3
-64:f4
-64:f5
-64:f6
-64:f7
-64:f8
-64:f9
-64:f10
-64:f11
-64:f12
-64:f13
-64:f14
-64:f15
diff -urNp gdb-orig/gdb/regformats/s390-linux32.dat gdb-head/gdb/regformats/s390-linux32.dat
--- gdb-orig/gdb/regformats/s390-linux32.dat	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/regformats/s390-linux32.dat	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,55 @@
+# DO NOT EDIT: generated from s390-linux32.xml
+name:s390_linux32
+xmltarget:s390-linux32.xml
+expedite:r14,r15,pswa
+32:pswm
+32:pswa
+32:r0
+32:r1
+32:r2
+32:r3
+32:r4
+32:r5
+32:r6
+32:r7
+32:r8
+32:r9
+32:r10
+32:r11
+32:r12
+32:r13
+32:r14
+32:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
diff -urNp gdb-orig/gdb/regformats/s390-linux64.dat gdb-head/gdb/regformats/s390-linux64.dat
--- gdb-orig/gdb/regformats/s390-linux64.dat	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/regformats/s390-linux64.dat	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,71 @@
+# DO NOT EDIT: generated from s390-linux64.xml
+name:s390_linux64
+xmltarget:s390-linux64.xml
+expedite:r14l,r15l,pswa
+32:pswm
+32:pswa
+32:r0h
+32:r0l
+32:r1h
+32:r1l
+32:r2h
+32:r2l
+32:r3h
+32:r3l
+32:r4h
+32:r4l
+32:r5h
+32:r5l
+32:r6h
+32:r6l
+32:r7h
+32:r7l
+32:r8h
+32:r8l
+32:r9h
+32:r9l
+32:r10h
+32:r10l
+32:r11h
+32:r11l
+32:r12h
+32:r12l
+32:r13h
+32:r13l
+32:r14h
+32:r14l
+32:r15h
+32:r15l
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
diff -urNp gdb-orig/gdb/regformats/s390x-linux64.dat gdb-head/gdb/regformats/s390x-linux64.dat
--- gdb-orig/gdb/regformats/s390x-linux64.dat	1970-01-01 01:00:00.000000000 +0100
+++ gdb-head/gdb/regformats/s390x-linux64.dat	2009-12-18 17:42:44.000000000 +0100
@@ -0,0 +1,55 @@
+# DO NOT EDIT: generated from s390x-linux64.xml
+name:s390x_linux64
+xmltarget:s390x-linux64.xml
+expedite:r14,r15,pswa
+64:pswm
+64:pswa
+64:r0
+64:r1
+64:r2
+64:r3
+64:r4
+64:r5
+64:r6
+64:r7
+64:r8
+64:r9
+64:r10
+64:r11
+64:r12
+64:r13
+64:r14
+64:r15
+32:acr0
+32:acr1
+32:acr2
+32:acr3
+32:acr4
+32:acr5
+32:acr6
+32:acr7
+32:acr8
+32:acr9
+32:acr10
+32:acr11
+32:acr12
+32:acr13
+32:acr14
+32:acr15
+32:fpc
+64:f0
+64:f1
+64:f2
+64:f3
+64:f4
+64:f5
+64:f6
+64:f7
+64:f8
+64:f9
+64:f10
+64:f11
+64:f12
+64:f13
+64:f14
+64:f15
diff -urNp gdb-orig/gdb/s390-nat.c gdb-head/gdb/s390-nat.c
--- gdb-orig/gdb/s390-nat.c	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/s390-nat.c	2009-12-18 17:42:44.000000000 +0100
@@ -25,6 +25,7 @@
 #include "inferior.h"
 #include "target.h"
 #include "linux-nat.h"
+#include "auxv.h"
 
 #include "s390-tdep.h"
 
@@ -33,6 +34,11 @@
 #include <asm/types.h>
 #include <sys/procfs.h>
 #include <sys/ucontext.h>
+#include <elf.h>
+
+#ifndef HWCAP_S390_HIGH_GPRS
+#define HWCAP_S390_HIGH_GPRS 512
+#endif
 
 
 /* Map registers to gregset/ptrace offsets.
@@ -389,6 +395,83 @@ s390_region_ok_for_hw_watchpoint (CORE_A
   return 1;
 }
 
+static int
+s390_target_wordsize (void)
+{
+  int wordsize = 4;
+
+  /* Check for 64-bit inferior process.  This is the case when the host is
+     64-bit, and in addition bit 32 of the PSW mask is set.  */
+#ifdef __s390x__
+  long pswm;
+
+  errno = 0;
+  pswm = (long) ptrace (PTRACE_PEEKUSER, s390_inferior_tid (), PT_PSWMASK, 0);
+  if (errno == 0 && (pswm & 0x100000000ul) != 0)
+    wordsize = 8;
+#endif
+
+  return wordsize;
+}
+
+static int
+s390_auxv_parse (struct target_ops *ops, gdb_byte **readptr,
+		 gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
+{
+  int sizeof_auxv_field = s390_target_wordsize ();
+  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+  gdb_byte *ptr = *readptr;
+
+  if (endptr == ptr)
+    return 0;
+
+  if (endptr - ptr < sizeof_auxv_field * 2)
+    return -1;
+
+  *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+  ptr += sizeof_auxv_field;
+  *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+  ptr += sizeof_auxv_field;
+
+  *readptr = ptr;
+  return 1;
+}
+
+#ifdef __s390x__
+static unsigned long
+s390_get_hwcap (void)
+{
+  CORE_ADDR field;
+
+  if (target_auxv_search (&current_target, AT_HWCAP, &field))
+    return (unsigned long) field;
+
+  return 0;
+}
+#endif
+
+static const struct target_desc *
+s390_read_description (struct target_ops *ops)
+{
+#ifdef __s390x__
+  /* If GDB itself is compiled as 64-bit, we are running on a machine in
+     z/Architecture mode.  If the target is running in 64-bit addressing
+     mode, report s390x architecture.  If the target is running in 31-bit
+     addressing mode, but the kernel supports using 64-bit registers in
+     that mode, report s390 architecture with 64-bit GPRs.  */
+
+  if (s390_target_wordsize () == 8)
+    return tdesc_s390x_linux64;
+
+  if (s390_get_hwcap () & HWCAP_S390_HIGH_GPRS)
+    return tdesc_s390_linux64;
+#endif
+
+  /* If GDB itself is compiled as 31-bit, or if we're running a 31-bit inferior
+     on a 64-bit kernel that does not support using 64-bit registers in 31-bit
+     mode, report s390 architecture with 32-bit GPRs.  */
+  return tdesc_s390_linux32;
+}
 
 void _initialize_s390_nat (void);
 
@@ -412,6 +495,10 @@ _initialize_s390_nat (void)
   t->to_insert_watchpoint = s390_insert_watchpoint;
   t->to_remove_watchpoint = s390_remove_watchpoint;
 
+  /* Detect target architecture.  */
+  t->to_read_description = s390_read_description;
+  t->to_auxv_parse = s390_auxv_parse;
+
   /* Register the target.  */
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, s390_fix_watch_points);
diff -urNp gdb-orig/gdb/s390-tdep.c gdb-head/gdb/s390-tdep.c
--- gdb-orig/gdb/s390-tdep.c	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/s390-tdep.c	2009-12-18 17:44:21.000000000 +0100
@@ -46,6 +46,10 @@
 
 #include "s390-tdep.h"
 
+#include "features/s390-linux32.c"
+#include "features/s390-linux64.c"
+#include "features/s390x-linux64.c"
+
 
 /* The tdep structure.  */
 
@@ -54,6 +58,11 @@ struct gdbarch_tdep
   /* ABI version.  */
   enum { ABI_LINUX_S390, ABI_LINUX_ZSERIES } abi;
 
+  /* Pseudo register numbers.  */
+  int gpr_full_regnum;
+  int pc_regnum;
+  int cc_regnum;
+
   /* Core file register sets.  */
   const struct regset *gregset;
   int sizeof_gregset;
@@ -63,56 +72,36 @@ struct gdbarch_tdep
 };
 
 
-/* Return the name of register REGNUM.  */
-static const char *
-s390_register_name (struct gdbarch *gdbarch, int regnum)
+/* ABI call-saved register information.  */
+
+static int
+s390_register_call_saved (struct gdbarch *gdbarch, int regnum)
 {
-  static const char *register_names[S390_NUM_TOTAL_REGS] =
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  switch (tdep->abi)
     {
-      /* Program Status Word.  */
-      "pswm", "pswa",
-      /* General Purpose Registers.  */
-      "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
-      "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
-      /* Access Registers.  */
-      "acr0", "acr1", "acr2", "acr3", "acr4", "acr5", "acr6", "acr7",
-      "acr8", "acr9", "acr10", "acr11", "acr12", "acr13", "acr14", "acr15",
-      /* Floating Point Control Word.  */
-      "fpc",
-      /* Floating Point Registers.  */
-      "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
-      "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
-      /* Pseudo registers.  */
-      "pc", "cc",
-    };
+    case ABI_LINUX_S390:
+      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
+	  || regnum == S390_F4_REGNUM || regnum == S390_F6_REGNUM
+	  || regnum == S390_A0_REGNUM)
+	return 1;
 
-  gdb_assert (regnum >= 0 && regnum < S390_NUM_TOTAL_REGS);
-  return register_names[regnum];
-}
+      break;
 
-/* Return the GDB type object for the "standard" data type of data in
-   register REGNUM.  */
-static struct type *
-s390_register_type (struct gdbarch *gdbarch, int regnum)
-{
-  if (regnum == S390_PSWM_REGNUM || regnum == S390_PSWA_REGNUM)
-    return builtin_type (gdbarch)->builtin_long;
-  if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
-    return builtin_type (gdbarch)->builtin_long;
-  if (regnum >= S390_A0_REGNUM && regnum <= S390_A15_REGNUM)
-    return builtin_type (gdbarch)->builtin_int;
-  if (regnum == S390_FPC_REGNUM)
-    return builtin_type (gdbarch)->builtin_int;
-  if (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM)
-    return builtin_type (gdbarch)->builtin_double;
-  if (regnum == S390_PC_REGNUM)
-    return builtin_type (gdbarch)->builtin_func_ptr;
-  if (regnum == S390_CC_REGNUM)
-    return builtin_type (gdbarch)->builtin_int;
+    case ABI_LINUX_ZSERIES:
+      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
+	  || (regnum >= S390_F8_REGNUM && regnum <= S390_F15_REGNUM)
+	  || (regnum >= S390_A0_REGNUM && regnum <= S390_A1_REGNUM))
+	return 1;
 
-  internal_error (__FILE__, __LINE__, _("invalid regnum"));
+      break;
+    }
+
+  return 0;
 }
 
+
 /* DWARF Register Mapping.  */
 
 static int s390_dwarf_regmap[] =
@@ -141,7 +130,13 @@ static int s390_dwarf_regmap[] =
 
   /* Program Status Word.  */
   S390_PSWM_REGNUM,
-  S390_PSWA_REGNUM
+  S390_PSWA_REGNUM,
+
+  /* GPR Lower Half Access.  */
+  S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
+  S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
+  S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
+  S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
 };
 
 /* Convert DWARF register number REG to the appropriate register
@@ -149,117 +144,172 @@ static int s390_dwarf_regmap[] =
 static int
 s390_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
 {
-  int regnum = -1;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  /* In a 32-on-64 debug scenario, debug info refers to the full 64-bit
+     GPRs.  Note that call frame information still refers to the 32-bit
+     lower halves, because s390_adjust_frame_regnum uses register numbers
+     66 .. 81 to access GPRs.  */
+  if (tdep->gpr_full_regnum != -1 && reg >= 0 && reg < 16)
+    return tdep->gpr_full_regnum + reg;
 
   if (reg >= 0 && reg < ARRAY_SIZE (s390_dwarf_regmap))
-    regnum = s390_dwarf_regmap[reg];
+    return s390_dwarf_regmap[reg];
 
-  if (regnum == -1)
-    warning (_("Unmapped DWARF Register #%d encountered."), reg);
+  warning (_("Unmapped DWARF Register #%d encountered."), reg);
+  return -1;
+}
 
-  return regnum;
+/* Translate a .eh_frame register to DWARF register, or adjust a
+   .debug_frame register.  */
+static int
+s390_adjust_frame_regnum (struct gdbarch *gdbarch, int num, int eh_frame_p)
+{
+  /* See s390_dwarf_reg_to_regnum for comments.  */
+  return (num >= 0 && num < 16)? num + 66 : num;
 }
 
-/* Pseudo registers - PC and condition code.  */
 
-static void
-s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
-			   int regnum, gdb_byte *buf)
+/* Pseudo registers.  */
+
+static const char *
+s390_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  ULONGEST val;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  switch (regnum)
-    {
-    case S390_PC_REGNUM:
-      regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
-      store_unsigned_integer (buf, 4, byte_order, val & 0x7fffffff);
-      break;
+  if (regnum == tdep->pc_regnum)
+    return "pc";
 
-    case S390_CC_REGNUM:
-      regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
-      store_unsigned_integer (buf, 4, byte_order, (val >> 12) & 3);
-      break;
+  if (regnum == tdep->cc_regnum)
+    return "cc";
 
-    default:
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+  if (tdep->gpr_full_regnum != -1
+      && regnum >= tdep->gpr_full_regnum
+      && regnum < tdep->gpr_full_regnum + 16)
+    {
+      static const char *full_name[] = {
+	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+      };
+      return full_name[regnum - tdep->gpr_full_regnum];
     }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
 }
 
-static void
-s390_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
-			    int regnum, const gdb_byte *buf)
+static struct type *
+s390_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  ULONGEST val, psw;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  switch (regnum)
-    {
-    case S390_PC_REGNUM:
-      val = extract_unsigned_integer (buf, 4, byte_order);
-      regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &psw);
-      psw = (psw & 0x80000000) | (val & 0x7fffffff);
-      regcache_raw_write_unsigned (regcache, S390_PSWA_REGNUM, psw);
-      break;
+  if (regnum == tdep->pc_regnum)
+    return builtin_type (gdbarch)->builtin_func_ptr;
 
-    case S390_CC_REGNUM:
-      val = extract_unsigned_integer (buf, 4, byte_order);
-      regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
-      psw = (psw & ~((ULONGEST)3 << 12)) | ((val & 3) << 12);
-      regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, psw);
-      break;
+  if (regnum == tdep->cc_regnum)
+    return builtin_type (gdbarch)->builtin_int;
 
-    default:
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
-    }
+  if (tdep->gpr_full_regnum != -1
+      && regnum >= tdep->gpr_full_regnum
+      && regnum < tdep->gpr_full_regnum + 16)
+    return builtin_type (gdbarch)->builtin_uint64;
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
 }
 
 static void
-s390x_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
-			    int regnum, gdb_byte *buf)
+s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
+			   int regnum, gdb_byte *buf)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int regsize = register_size (gdbarch, regnum);
   ULONGEST val;
 
-  switch (regnum)
+  if (regnum == tdep->pc_regnum)
     {
-    case S390_PC_REGNUM:
-      regcache_raw_read (regcache, S390_PSWA_REGNUM, buf);
-      break;
+      regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	val &= 0x7fffffff;
+      store_unsigned_integer (buf, regsize, byte_order, val);
+      return;
+    }
 
-    case S390_CC_REGNUM:
+  if (regnum == tdep->cc_regnum)
+    {
       regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
-      store_unsigned_integer (buf, 4, byte_order, (val >> 44) & 3);
-      break;
-
-    default:
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	val = (val >> 12) & 3;
+      else
+	val = (val >> 44) & 3;
+      store_unsigned_integer (buf, regsize, byte_order, val);
+      return;
+    }
+
+  if (tdep->gpr_full_regnum != -1
+      && regnum >= tdep->gpr_full_regnum
+      && regnum < tdep->gpr_full_regnum + 16)
+    {
+      ULONGEST val_upper;
+      regnum -= tdep->gpr_full_regnum;
+
+      regcache_raw_read_unsigned (regcache, S390_R0_REGNUM + regnum, &val);
+      regcache_raw_read_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
+				  &val_upper);
+      val |= val_upper << 32;
+      store_unsigned_integer (buf, regsize, byte_order, val);
+      return;
     }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
 }
 
 static void
-s390x_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
-			     int regnum, const gdb_byte *buf)
+s390_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
+			    int regnum, const gdb_byte *buf)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int regsize = register_size (gdbarch, regnum);
   ULONGEST val, psw;
 
-  switch (regnum)
+  if (regnum == tdep->pc_regnum)
     {
-    case S390_PC_REGNUM:
-      regcache_raw_write (regcache, S390_PSWA_REGNUM, buf);
-      break;
+      val = extract_unsigned_integer (buf, regsize, byte_order);
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	{
+	  regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &psw);
+	  val = (psw & 0x80000000) | (val & 0x7fffffff);
+	}
+      regcache_raw_write_unsigned (regcache, S390_PSWA_REGNUM, val);
+      return;
+    }
 
-    case S390_CC_REGNUM:
-      val = extract_unsigned_integer (buf, 4, byte_order);
+  if (regnum == tdep->cc_regnum)
+    {
+      val = extract_unsigned_integer (buf, regsize, byte_order);
       regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
-      psw = (psw & ~((ULONGEST)3 << 44)) | ((val & 3) << 44);
-      regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, psw);
-      break;
-
-    default:
-      internal_error (__FILE__, __LINE__, _("invalid regnum"));
+      if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+	val = (psw & ~((ULONGEST)3 << 12)) | ((val & 3) << 12);
+      else
+	val = (psw & ~((ULONGEST)3 << 44)) | ((val & 3) << 44);
+      regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, val);
+      return;
+    }
+
+  if (tdep->gpr_full_regnum != -1
+      && regnum >= tdep->gpr_full_regnum
+      && regnum < tdep->gpr_full_regnum + 16)
+    {
+      regnum -= tdep->gpr_full_regnum;
+      val = extract_unsigned_integer (buf, regsize, byte_order);
+      regcache_raw_write_unsigned (regcache, S390_R0_REGNUM + regnum,
+				   val & 0xffffffff);
+      regcache_raw_write_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
+				   val >> 32);
+      return;
     }
+
+  internal_error (__FILE__, __LINE__, _("invalid regnum"));
 }
 
 /* 'float' values are stored in the upper half of floating-point
@@ -281,26 +331,15 @@ s390_value_from_register (struct type *t
 /* Register groups.  */
 
 static int
-s390_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
-			  struct reggroup *group)
+s390_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+				 struct reggroup *group)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* Registers displayed via 'info regs'.  */
-  if (group == general_reggroup)
-    return (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
-	   || regnum == S390_PC_REGNUM
-	   || regnum == S390_CC_REGNUM;
-
-  /* Registers displayed via 'info float'.  */
-  if (group == float_reggroup)
-    return (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM)
-	   || regnum == S390_FPC_REGNUM;
-
-  /* Registers that need to be saved/restored in order to
+  /* PC and CC pseudo registers need to be saved/restored in order to
      push or pop frames.  */
   if (group == save_reggroup || group == restore_reggroup)
-    return regnum != S390_PSWM_REGNUM && regnum != S390_PSWA_REGNUM;
+    return regnum == tdep->pc_regnum || regnum == tdep->cc_regnum;
 
   return default_register_reggroup_p (gdbarch, regnum, group);
 }
@@ -327,10 +366,14 @@ int s390_regmap_gregset[S390_NUM_REGS] =
   /* Floating Point Registers.  */
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
+  /* GPR Uppper Halves.  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
 };
 
 int s390x_regmap_gregset[S390_NUM_REGS] =
 {
+  /* Program Status Word.  */
   0x00, 0x08,
   /* General Purpose Registers.  */
   0x10, 0x18, 0x20, 0x28,
@@ -347,6 +390,11 @@ int s390x_regmap_gregset[S390_NUM_REGS] 
   /* Floating Point Registers.  */
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
+  /* GPR Uppper Halves.  */
+  0x10, 0x18, 0x20, 0x28,
+  0x30, 0x38, 0x40, 0x48,
+  0x50, 0x58, 0x60, 0x68,
+  0x70, 0x78, 0x80, 0x88,
 };
 
 int s390_regmap_fpregset[S390_NUM_REGS] =
@@ -366,6 +414,31 @@ int s390_regmap_fpregset[S390_NUM_REGS] 
   0x28, 0x30, 0x38, 0x40,
   0x48, 0x50, 0x58, 0x60,
   0x68, 0x70, 0x78, 0x80,
+  /* GPR Uppper Halves.  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+int s390_regmap_upper[S390_NUM_REGS] =
+{
+  /* Program Status Word.  */
+  -1, -1,
+  /* General Purpose Registers.  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  /* Access Registers.  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  /* Floating Point Control Word.  */
+  -1,
+  /* Floating Point Registers.  */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  /* GPR Uppper Halves.  */
+  0x00, 0x04, 0x08, 0x0c,
+  0x10, 0x14, 0x18, 0x1c,
+  0x20, 0x24, 0x28, 0x2c,
+  0x30, 0x34, 0x38, 0x3c,
 };
 
 /* Supply register REGNUM from the register set REGSET to register cache 
@@ -421,6 +494,20 @@ static const struct regset s390_fpregset
   s390_collect_regset
 };
 
+static const struct regset s390_upper_regset = {
+  s390_regmap_upper, 
+  s390_supply_regset,
+  s390_collect_regset
+};
+
+static struct core_regset_section s390_upper_regset_sections[] =
+{
+  { ".reg", s390_sizeof_gregset, "general-purpose" },
+  { ".reg2", s390_sizeof_fpregset, "floating-point" },
+  { ".reg-s390-high-gprs", 16*4, "s390 GPR upper halves" },
+  { NULL, 0}
+};
+
 /* Return the appropriate register set for the core section identified
    by SECT_NAME and SECT_SIZE.  */
 static const struct regset *
@@ -435,9 +522,34 @@ s390_regset_from_core_section (struct gd
   if (strcmp (sect_name, ".reg2") == 0 && sect_size >= tdep->sizeof_fpregset)
     return tdep->fpregset;
 
+  if (strcmp (sect_name, ".reg-s390-high-gprs") == 0 && sect_size >= 16*4)
+    return &s390_upper_regset;
+
   return NULL;
 }
 
+static const struct target_desc *
+s390_core_read_description (struct gdbarch *gdbarch,
+			    struct target_ops *target, bfd *abfd)
+{
+  asection *high_gprs = bfd_get_section_by_name (abfd, ".reg-s390-high-gprs");
+  asection *section = bfd_get_section_by_name (abfd, ".reg");
+  if (!section)
+    return NULL;
+
+  switch (bfd_section_size (abfd, section))
+    {
+    case s390_sizeof_gregset:
+      return high_gprs? tdesc_s390_linux64 : tdesc_s390_linux32;
+
+    case s390x_sizeof_gregset:
+      return tdesc_s390x_linux64;
+
+    default:
+      return NULL;
+    }
+}
+
 
 /* Decoding S/390 instructions.  */
 
@@ -1448,42 +1560,40 @@ s390_prologue_frame_unwind_cache (struct
   prev_sp = get_frame_register_unsigned (this_frame, frame_pointer) + size;
   cfa = prev_sp + 16*word_size + 32;
 
+  /* Set up ABI call-saved/call-clobbered registers.  */
+  for (i = 0; i < S390_NUM_REGS; i++)
+    if (!s390_register_call_saved (gdbarch, i))
+      trad_frame_set_unknown (info->saved_regs, i);
+
+  /* CC is always call-clobbered.  */
+  trad_frame_set_unknown (info->saved_regs, tdep->cc_regnum);
+
   /* Record the addresses of all register spill slots the prologue parser
      has recognized.  Consider only registers defined as call-saved by the
      ABI; for call-clobbered registers the parser may have recognized
      spurious stores.  */
 
-  for (i = 6; i <= 15; i++)
-    if (data.gpr_slot[i] != 0)
+  for (i = 0; i < 16; i++)
+    if (s390_register_call_saved (gdbarch, S390_R0_REGNUM + i)
+	&& data.gpr_slot[i] != 0)
       info->saved_regs[S390_R0_REGNUM + i].addr = cfa - data.gpr_slot[i];
 
-  switch (tdep->abi)
-    {
-    case ABI_LINUX_S390:
-      if (data.fpr_slot[4] != 0)
-        info->saved_regs[S390_F4_REGNUM].addr = cfa - data.fpr_slot[4];
-      if (data.fpr_slot[6] != 0)
-        info->saved_regs[S390_F6_REGNUM].addr = cfa - data.fpr_slot[6];
-      break;
-
-    case ABI_LINUX_ZSERIES:
-      for (i = 8; i <= 15; i++)
-	if (data.fpr_slot[i] != 0)
-	  info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
-      break;
-    }
+  for (i = 0; i < 16; i++)
+    if (s390_register_call_saved (gdbarch, S390_F0_REGNUM + i)
+	&& data.fpr_slot[i] != 0)
+      info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
 
   /* Function return will set PC to %r14.  */
-  info->saved_regs[S390_PC_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
+  info->saved_regs[tdep->pc_regnum] = info->saved_regs[S390_RETADDR_REGNUM];
 
   /* In frameless functions, we unwind simply by moving the return
      address to the PC.  However, if we actually stored to the
      save area, use that -- we might only think the function frameless
      because we're in the middle of the prologue ...  */
   if (size == 0
-      && !trad_frame_addr_p (info->saved_regs, S390_PC_REGNUM))
+      && !trad_frame_addr_p (info->saved_regs, tdep->pc_regnum))
     {
-      info->saved_regs[S390_PC_REGNUM].realreg = S390_RETADDR_REGNUM;
+      info->saved_regs[tdep->pc_regnum].realreg = S390_RETADDR_REGNUM;
     }
 
   /* Another sanity check: unless this is a frameless function,
@@ -1493,7 +1603,7 @@ s390_prologue_frame_unwind_cache (struct
   if (size > 0)
     {
       if (!trad_frame_addr_p (info->saved_regs, S390_SP_REGNUM)
-	  || !trad_frame_addr_p (info->saved_regs, S390_PC_REGNUM))
+	  || !trad_frame_addr_p (info->saved_regs, tdep->pc_regnum))
 	prev_sp = -1;
     }
 
@@ -1514,11 +1624,21 @@ s390_backchain_frame_unwind_cache (struc
 				   struct s390_unwind_cache *info)
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   int word_size = gdbarch_ptr_bit (gdbarch) / 8;
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   CORE_ADDR backchain;
   ULONGEST reg;
   LONGEST sp;
+  int i;
+
+  /* Set up ABI call-saved/call-clobbered registers.  */
+  for (i = 0; i < S390_NUM_REGS; i++)
+    if (!s390_register_call_saved (gdbarch, i))
+      trad_frame_set_unknown (info->saved_regs, i);
+
+  /* CC is always call-clobbered.  */
+  trad_frame_set_unknown (info->saved_regs, tdep->cc_regnum);
 
   /* Get the backchain.  */
   reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
@@ -1540,7 +1660,8 @@ s390_backchain_frame_unwind_cache (struc
       info->saved_regs[S390_RETADDR_REGNUM].addr = backchain + 14*word_size;
 
       /* Function return will set PC to %r14.  */
-      info->saved_regs[S390_PC_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
+      info->saved_regs[tdep->pc_regnum]
+	= info->saved_regs[S390_RETADDR_REGNUM];
 
       /* We use the current value of the frame register as local_base,
          and the top of the register save area as frame_base.  */
@@ -1592,8 +1713,28 @@ static struct value *
 s390_frame_prev_register (struct frame_info *this_frame,
 			  void **this_prologue_cache, int regnum)
 {
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   struct s390_unwind_cache *info
     = s390_frame_unwind_cache (this_frame, this_prologue_cache);
+
+  /* Unwind full GPRs to show at least the lower halves (as the
+     upper halves are undefined).  */
+  if (tdep->gpr_full_regnum != -1
+      && regnum >= tdep->gpr_full_regnum
+      && regnum < tdep->gpr_full_regnum + 16)
+    {
+      int reg = regnum - tdep->gpr_full_regnum + S390_R0_REGNUM;
+      struct value *val, *newval;
+
+      val = trad_frame_get_prev_register (this_frame, info->saved_regs, reg);
+      newval = value_cast (register_type (gdbarch, regnum), val);
+      if (value_optimized_out (val))
+	set_value_optimized_out (newval, 1);
+
+      return newval;
+    }
+
   return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
 }
 
@@ -1621,6 +1762,7 @@ s390_stub_frame_unwind_cache (struct fra
 			      void **this_prologue_cache)
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   int word_size = gdbarch_ptr_bit (gdbarch) / 8;
   struct s390_stub_unwind_cache *info;
   ULONGEST reg;
@@ -1633,7 +1775,7 @@ s390_stub_frame_unwind_cache (struct fra
   info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
 
   /* The return address is in register %r14.  */
-  info->saved_regs[S390_PC_REGNUM].realreg = S390_RETADDR_REGNUM;
+  info->saved_regs[tdep->pc_regnum].realreg = S390_RETADDR_REGNUM;
 
   /* Retrieve stack pointer and determine our frame base.  */
   reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
@@ -1700,11 +1842,13 @@ s390_sigtramp_frame_unwind_cache (struct
 				  void **this_prologue_cache)
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   int word_size = gdbarch_ptr_bit (gdbarch) / 8;
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   struct s390_sigtramp_unwind_cache *info;
   ULONGEST this_sp, prev_sp;
-  CORE_ADDR next_ra, next_cfa, sigreg_ptr;
+  CORE_ADDR next_ra, next_cfa, sigreg_ptr, sigreg_high_off;
+  ULONGEST pswm;
   int i;
 
   if (*this_prologue_cache)
@@ -1725,6 +1869,9 @@ s390_sigtramp_frame_unwind_cache (struct
   if (next_ra == next_cfa)
     {
       sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8);
+      /* sigregs are followed by uc_sigmask (8 bytes), then by the
+	 upper GPR halves if present.  */
+      sigreg_high_off = 8;
     }
 
   /* Old-style RT frame and all non-RT frames:
@@ -1734,6 +1881,9 @@ s390_sigtramp_frame_unwind_cache (struct
     {
       sigreg_ptr = read_memory_unsigned_integer (next_cfa + 8,
 						 word_size, byte_order);
+      /* sigregs are followed by signo (4 bytes), then by the
+	 upper GPR halves if present.  */
+      sigreg_high_off = 4;
     }
 
   /* The sigregs structure looks like this:
@@ -1745,13 +1895,22 @@ s390_sigtramp_frame_unwind_cache (struct
             int    __pad;
             double fprs[16];  */
 
-  /* Let's ignore the PSW mask, it will not be restored anyway.  */
+  /* PSW mask and address.  */
+  info->saved_regs[S390_PSWM_REGNUM].addr = sigreg_ptr;
   sigreg_ptr += word_size;
-
-  /* Next comes the PSW address.  */
-  info->saved_regs[S390_PC_REGNUM].addr = sigreg_ptr;
+  info->saved_regs[S390_PSWA_REGNUM].addr = sigreg_ptr;
   sigreg_ptr += word_size;
 
+  /* Point PC to PSWA as well.  */
+  info->saved_regs[tdep->pc_regnum] = info->saved_regs[S390_PSWA_REGNUM];
+
+  /* Extract CC from PSWM.  */
+  pswm = read_memory_unsigned_integer (
+			info->saved_regs[S390_PSWM_REGNUM].addr,
+			word_size, byte_order);
+  trad_frame_set_value (info->saved_regs, tdep->cc_regnum,
+			(pswm >> (8 * word_size - 20)) & 3);
+
   /* Then the GPRs.  */
   for (i = 0; i < 16; i++)
     {
@@ -1777,6 +1936,31 @@ s390_sigtramp_frame_unwind_cache (struct
       sigreg_ptr += 8;
     }
 
+  /* If we have them, the GPR upper halves are appended at the end.  */
+  sigreg_ptr += sigreg_high_off;
+  if (tdep->gpr_full_regnum != -1)
+    for (i = 0; i < 16; i++)
+      {
+        info->saved_regs[S390_R0_UPPER_REGNUM + i].addr = sigreg_ptr;
+	sigreg_ptr += 4;
+      }
+
+  /* Provide read-only copies of the full registers.  */
+  if (tdep->gpr_full_regnum != -1)
+    for (i = 0; i < 16; i++)
+      {
+	ULONGEST low, high;
+	low = read_memory_unsigned_integer (
+			info->saved_regs[S390_R0_REGNUM + i].addr,
+			4, byte_order);
+	high = read_memory_unsigned_integer (
+			info->saved_regs[S390_R0_UPPER_REGNUM + i].addr,
+			4, byte_order);
+	
+	trad_frame_set_value (info->saved_regs, tdep->gpr_full_regnum + i,
+			      (high << 32) | low);
+      }
+
   /* Restore the previous frame's SP.  */
   prev_sp = read_memory_unsigned_integer (
 			info->saved_regs[S390_SP_REGNUM].addr,
@@ -1865,8 +2049,9 @@ static const struct frame_base s390_fram
 static CORE_ADDR
 s390_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
 {
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   ULONGEST pc;
-  pc = frame_unwind_register_unsigned (next_frame, S390_PC_REGNUM);
+  pc = frame_unwind_register_unsigned (next_frame, tdep->pc_regnum);
   return gdbarch_addr_bits_remove (gdbarch, pc);
 }
 
@@ -1881,6 +2066,23 @@ s390_unwind_sp (struct gdbarch *gdbarch,
 
 /* DWARF-2 frame support.  */
 
+static struct value *
+s390_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
+			   int regnum)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int reg = regnum - tdep->gpr_full_regnum;
+  struct value *val, *newval;
+
+  val = frame_unwind_register_value (this_frame, S390_R0_REGNUM + reg);
+  newval = value_cast (register_type (gdbarch, regnum), val);
+  if (value_optimized_out (val))
+    set_value_optimized_out (newval, 1);
+
+  return newval;
+}
+
 static void
 s390_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
                             struct dwarf2_frame_state_reg *reg,
@@ -1888,41 +2090,32 @@ s390_dwarf2_frame_init_reg (struct gdbar
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  switch (tdep->abi)
+  /* Fixed registers are call-saved or call-clobbered
+     depending on the ABI in use.  */
+  if (regnum >= 0 && regnum < S390_NUM_REGS)
     {
-    case ABI_LINUX_S390:
-      /* Call-saved registers.  */
-      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
-	  || regnum == S390_F4_REGNUM
-	  || regnum == S390_F6_REGNUM)
+      if (s390_register_call_saved (gdbarch, regnum))
 	reg->how = DWARF2_FRAME_REG_SAME_VALUE;
-
-      /* Call-clobbered registers.  */
-      else if ((regnum >= S390_R0_REGNUM && regnum <= S390_R5_REGNUM)
-	       || (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM
-		   && regnum != S390_F4_REGNUM && regnum != S390_F6_REGNUM))
-	reg->how = DWARF2_FRAME_REG_UNDEFINED;
-
-      /* The return address column.  */
-      else if (regnum == S390_PC_REGNUM)
-	reg->how = DWARF2_FRAME_REG_RA;
-      break;
-
-    case ABI_LINUX_ZSERIES:
-      /* Call-saved registers.  */
-      if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
-	  || (regnum >= S390_F8_REGNUM && regnum <= S390_F15_REGNUM))
-	reg->how = DWARF2_FRAME_REG_SAME_VALUE;
-
-      /* Call-clobbered registers.  */
-      else if ((regnum >= S390_R0_REGNUM && regnum <= S390_R5_REGNUM)
-	       || (regnum >= S390_F0_REGNUM && regnum <= S390_F7_REGNUM))
+      else
 	reg->how = DWARF2_FRAME_REG_UNDEFINED;
+    }
 
-      /* The return address column.  */
-      else if (regnum == S390_PC_REGNUM)
-	reg->how = DWARF2_FRAME_REG_RA;
-      break;
+  /* The CC pseudo register is call-clobbered.  */
+  else if (regnum == tdep->cc_regnum)
+    reg->how = DWARF2_FRAME_REG_UNDEFINED;
+
+  /* The PC register unwinds to the return address.  */
+  else if (regnum == tdep->pc_regnum)
+    reg->how = DWARF2_FRAME_REG_RA;
+
+  /* We install a special function to unwind full GPRs to show at
+     least the lower halves (as the upper halves are undefined).  */
+  else if (tdep->gpr_full_regnum != -1
+	   && regnum >= tdep->gpr_full_regnum
+	   && regnum < tdep->gpr_full_regnum + 16)
+    {
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = s390_dwarf2_prev_register;
     }
 }
 
@@ -2484,20 +2677,146 @@ s390_address_class_name_to_type_flags (s
 static struct gdbarch *
 s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 {
+  const struct target_desc *tdesc = info.target_desc;
+  struct tdesc_arch_data *tdesc_data = NULL;
   struct gdbarch *gdbarch;
   struct gdbarch_tdep *tdep;
+  int tdep_abi;
+  int have_upper = 0;
+  int first_pseudo_reg, last_pseudo_reg;
+
+  /* Default ABI and register size.  */
+  switch (info.bfd_arch_info->mach)
+    {
+    case bfd_mach_s390_31:
+      tdep_abi = ABI_LINUX_S390;
+      break;
+
+    case bfd_mach_s390_64:
+      tdep_abi = ABI_LINUX_ZSERIES;
+      break;
+
+    default:
+      return NULL;
+    }
+
+  /* Use default target description if none provided by the target.  */
+  if (!tdesc_has_registers (tdesc))
+    {
+      if (tdep_abi == ABI_LINUX_S390)
+	tdesc = tdesc_s390_linux32;
+      else
+	tdesc = tdesc_s390x_linux64;
+    }
+
+  /* Check any target description for validity.  */
+  if (tdesc_has_registers (tdesc))
+    {
+      static const char *const gprs[] = {
+	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+	"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+      };
+      static const char *const fprs[] = {
+	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15"
+      };
+      static const char *const acrs[] = {
+	"acr0", "acr1", "acr2", "acr3", "acr4", "acr5", "acr6", "acr7",
+	"acr8", "acr9", "acr10", "acr11", "acr12", "acr13", "acr14", "acr15"
+      };
+      static const char *const gprs_lower[] = {
+	"r0l", "r1l", "r2l", "r3l", "r4l", "r5l", "r6l", "r7l",
+	"r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
+      };
+      static const char *const gprs_upper[] = {
+	"r0h", "r1h", "r2h", "r3h", "r4h", "r5h", "r6h", "r7h",
+	"r8h", "r9h", "r10h", "r11h", "r12h", "r13h", "r14h", "r15h"
+      };
+      const struct tdesc_feature *feature;
+      int i, valid_p = 1;
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.core");
+      if (feature == NULL)
+	return NULL;
+
+      tdesc_data = tdesc_data_alloc ();
+
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_PSWM_REGNUM, "pswm");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_PSWA_REGNUM, "pswa");
+
+      if (tdesc_unnumbered_register (feature, "r0"))
+	{
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_REGNUM + i, gprs[i]);
+	}
+      else
+	{
+	  have_upper = 1;
+
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_REGNUM + i,
+						gprs_lower[i]);
+	  for (i = 0; i < 16; i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						S390_R0_UPPER_REGNUM + i,
+						gprs_upper[i]);
+	}
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.fpr");
+      if (feature == NULL)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
 
-  /* First see if there is already a gdbarch that can satisfy the request.  */
-  arches = gdbarch_list_lookup_by_info (arches, &info);
-  if (arches != NULL)
-    return arches->gdbarch;
-
-  /* None found: is the request for a s390 architecture? */
-  if (info.bfd_arch_info->arch != bfd_arch_s390)
-    return NULL;		/* No; then it's not for us.  */
+      valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					  S390_FPC_REGNUM, "fpc");
+      for (i = 0; i < 16; i++)
+	valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					    S390_F0_REGNUM + i, fprs[i]);
+
+      feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.acr");
+      if (feature == NULL)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
+
+      for (i = 0; i < 16; i++)
+	valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					    S390_A0_REGNUM + i, acrs[i]);
+
+      if (!valid_p)
+	{
+	  tdesc_data_cleanup (tdesc_data);
+	  return NULL;
+	}
+    }
 
-  /* Yes: create a new gdbarch for the specified machine type.  */
+  /* Find a candidate among extant architectures.  */
+  for (arches = gdbarch_list_lookup_by_info (arches, &info);
+       arches != NULL;
+       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+    {
+      tdep = gdbarch_tdep (arches->gdbarch);
+      if (!tdep)
+	continue;
+      if (tdep->abi != tdep_abi)
+	continue;
+      if ((tdep->gpr_full_regnum != -1) != have_upper)
+	continue;
+      if (tdesc_data != NULL)
+	tdesc_data_cleanup (tdesc_data);
+      return arches->gdbarch;
+    }
+
+  /* Otherwise create a new gdbarch for the specified machine type.  */
   tdep = XCALLOC (1, struct gdbarch_tdep);
+  tdep->abi = tdep_abi;
   gdbarch = gdbarch_alloc (&info, tdep);
 
   set_gdbarch_believe_pcc_promotion (gdbarch, 0);
@@ -2519,19 +2838,38 @@ s390_gdbarch_init (struct gdbarch_info i
   set_gdbarch_skip_prologue (gdbarch, s390_skip_prologue);
   set_gdbarch_in_function_epilogue_p (gdbarch, s390_in_function_epilogue_p);
 
-  set_gdbarch_pc_regnum (gdbarch, S390_PC_REGNUM);
+  set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
   set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
   set_gdbarch_fp0_regnum (gdbarch, S390_F0_REGNUM);
-  set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
-  set_gdbarch_num_pseudo_regs (gdbarch, S390_NUM_PSEUDO_REGS);
-  set_gdbarch_register_name (gdbarch, s390_register_name);
-  set_gdbarch_register_type (gdbarch, s390_register_type);
   set_gdbarch_stab_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
   set_gdbarch_value_from_register (gdbarch, s390_value_from_register);
-  set_gdbarch_register_reggroup_p (gdbarch, s390_register_reggroup_p);
   set_gdbarch_regset_from_core_section (gdbarch,
                                         s390_regset_from_core_section);
+  set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
+  if (have_upper)
+    set_gdbarch_core_regset_sections (gdbarch, s390_upper_regset_sections);
+  set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
+  set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
+  set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
+  set_tdesc_pseudo_register_type (gdbarch, s390_pseudo_register_type);
+  set_tdesc_pseudo_register_reggroup_p (gdbarch,
+                                        s390_pseudo_register_reggroup_p);
+  tdesc_use_registers (gdbarch, tdesc, tdesc_data);
+
+  /* Assign pseudo register numbers.  */
+  first_pseudo_reg = gdbarch_num_regs (gdbarch);
+  last_pseudo_reg = first_pseudo_reg;
+  tdep->gpr_full_regnum = -1;
+  if (have_upper)
+    {
+      tdep->gpr_full_regnum = last_pseudo_reg;
+      last_pseudo_reg += 16;
+    }
+  tdep->pc_regnum = last_pseudo_reg++;
+  tdep->cc_regnum = last_pseudo_reg++;
+  set_gdbarch_pc_regnum (gdbarch, tdep->pc_regnum);
+  set_gdbarch_num_pseudo_regs (gdbarch, last_pseudo_reg - first_pseudo_reg);
 
   /* Inferior function calls.  */
   set_gdbarch_push_dummy_call (gdbarch, s390_push_dummy_call);
@@ -2541,6 +2879,7 @@ s390_gdbarch_init (struct gdbarch_info i
 
   /* Frame handling.  */
   dwarf2_frame_set_init_reg (gdbarch, s390_dwarf2_frame_init_reg);
+  dwarf2_frame_set_adjust_regnum (gdbarch, s390_adjust_frame_regnum);
   dwarf2_append_unwinders (gdbarch);
   frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
   frame_unwind_append_unwinder (gdbarch, &s390_stub_frame_unwind);
@@ -2560,26 +2899,20 @@ s390_gdbarch_init (struct gdbarch_info i
                                        displaced_step_at_entry_point);
   set_gdbarch_max_insn_length (gdbarch, S390_MAX_INSTR_SIZE);
 
-  switch (info.bfd_arch_info->mach)
+  switch (tdep->abi)
     {
-    case bfd_mach_s390_31:
-      tdep->abi = ABI_LINUX_S390;
-
+    case ABI_LINUX_S390:
       tdep->gregset = &s390_gregset;
       tdep->sizeof_gregset = s390_sizeof_gregset;
       tdep->fpregset = &s390_fpregset;
       tdep->sizeof_fpregset = s390_sizeof_fpregset;
 
       set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
-      set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
       set_solib_svr4_fetch_link_map_offsets
 	(gdbarch, svr4_ilp32_fetch_link_map_offsets);
-
       break;
-    case bfd_mach_s390_64:
-      tdep->abi = ABI_LINUX_ZSERIES;
 
+    case ABI_LINUX_ZSERIES:
       tdep->gregset = &s390x_gregset;
       tdep->sizeof_gregset = s390x_sizeof_gregset;
       tdep->fpregset = &s390_fpregset;
@@ -2588,8 +2921,6 @@ s390_gdbarch_init (struct gdbarch_info i
       set_gdbarch_long_bit (gdbarch, 64);
       set_gdbarch_long_long_bit (gdbarch, 64);
       set_gdbarch_ptr_bit (gdbarch, 64);
-      set_gdbarch_pseudo_register_read (gdbarch, s390x_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, s390x_pseudo_register_write);
       set_solib_svr4_fetch_link_map_offsets
 	(gdbarch, svr4_lp64_fetch_link_map_offsets);
       set_gdbarch_address_class_type_flags (gdbarch,
@@ -2613,13 +2944,16 @@ s390_gdbarch_init (struct gdbarch_info i
 }
 
 
-
 extern initialize_file_ftype _initialize_s390_tdep; /* -Wmissing-prototypes */
 
 void
 _initialize_s390_tdep (void)
 {
-
   /* Hook us into the gdbarch mechanism.  */
   register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
+
+  /* Initialize the Linux target descriptions.  */
+  initialize_tdesc_s390_linux32 ();
+  initialize_tdesc_s390_linux64 ();
+  initialize_tdesc_s390x_linux64 ();
 }
diff -urNp gdb-orig/gdb/s390-tdep.h gdb-head/gdb/s390-tdep.h
--- gdb-orig/gdb/s390-tdep.h	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/s390-tdep.h	2009-12-18 17:42:44.000000000 +0100
@@ -77,14 +77,25 @@
 #define S390_F13_REGNUM 48
 #define S390_F14_REGNUM 49
 #define S390_F15_REGNUM 50
+/* General Purpose Register Upper Halves.  */
+#define S390_R0_UPPER_REGNUM 51
+#define S390_R1_UPPER_REGNUM 52
+#define S390_R2_UPPER_REGNUM 53
+#define S390_R3_UPPER_REGNUM 54
+#define S390_R4_UPPER_REGNUM 55
+#define S390_R5_UPPER_REGNUM 56
+#define S390_R6_UPPER_REGNUM 57
+#define S390_R7_UPPER_REGNUM 58
+#define S390_R8_UPPER_REGNUM 59
+#define S390_R9_UPPER_REGNUM 60
+#define S390_R10_UPPER_REGNUM 61
+#define S390_R11_UPPER_REGNUM 62
+#define S390_R12_UPPER_REGNUM 63
+#define S390_R13_UPPER_REGNUM 64
+#define S390_R14_UPPER_REGNUM 65
+#define S390_R15_UPPER_REGNUM 66
 /* Total.  */
-#define S390_NUM_REGS 51
-
-/* Pseudo registers -- PC and condition code.  */
-#define S390_PC_REGNUM S390_NUM_REGS
-#define S390_CC_REGNUM (S390_NUM_REGS+1)
-#define S390_NUM_PSEUDO_REGS 2
-#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+#define S390_NUM_REGS 67
 
 /* Special register usage.  */
 #define S390_SP_REGNUM S390_R15_REGNUM
@@ -99,5 +110,10 @@ extern int s390x_regmap_gregset[S390_NUM
 #define s390_sizeof_fpregset 0x88
 extern int s390_regmap_fpregset[S390_NUM_REGS];
 
+/* Linux target descriptions.  */
+extern struct target_desc *tdesc_s390_linux32;
+extern struct target_desc *tdesc_s390_linux64;
+extern struct target_desc *tdesc_s390x_linux64;
+
 #endif
 
diff -urNp gdb-orig/gdb/testsuite/gdb.xml/tdesc-regs.exp gdb-head/gdb/testsuite/gdb.xml/tdesc-regs.exp
--- gdb-orig/gdb/testsuite/gdb.xml/tdesc-regs.exp	2009-12-18 17:27:13.000000000 +0100
+++ gdb-head/gdb/testsuite/gdb.xml/tdesc-regs.exp	2009-12-18 17:42:44.000000000 +0100
@@ -42,6 +42,9 @@ switch -glob -- [istarget] {
 	set regdir "rs6000/"
 	set core-regs {power-core.xml}
     }
+    "s390*-*-*" {
+	set core-regs {s390-core32.xml s390-acr.xml s390-fpr.xml}
+    }
     "spu*-*-*" {
 	# This may be either the spu-linux-nat target, or the Cell/B.E.
 	# multi-architecture debugger in SPU standalone executable mode.
-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]