* [RFC PATCH v5 4/9] Add basic Linux kernel support
2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
` (4 preceding siblings ...)
2018-03-12 15:31 ` [RFC PATCH v5 6/9] Add commands for linux-kernel target Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
2018-03-13 14:09 ` Kamil Rytarowski
2018-03-19 0:11 ` Simon Marchi
2018-03-12 15:31 ` [RFC PATCH v5 7/9] Add privileged registers for s390x Philipp Rudo
` (2 subsequent siblings)
8 siblings, 2 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez
Implement the basic infrastructure and functionality to allow Linux kernel
debugging with GDB. This contains handling of kernel symbols and data
structures as well as a simple target_ops to hook into GDB. For the code
to work architectures must provide an implementation for the virtual
methods in linux_kernel_ops.
For simplicity this patch only supports static targets, i.e. core dumps.
Support for live debugging will be provided in a separate patch.
gdb/ChangeLog:
* gdbarch.sh (get_new_lk_ops): New hook.
* gdbarch.h: Regenerated.
* gdbarch.c: Regenerated.
* defs.h (gdb_osabi): Add GDB_OSABI_LINUX_KERNEL.
* osabi.c (gdb_osabi_names): Add Linux kernel entry.
* lk-low.h: New file.
* lk-low.c: New file.
* lk-list.h: New file.
* lk-bitmap.h: New file.
* Makefile.in (ALLDEPFILES): Add lk-low.c.
(HFILES_NO_SRCDIR): Add lk-low.h.
(ALL_TARGET_OBS): Add lk-low.o.
* configure.tgt (lk_tobjs): New variable with object files for Linux
kernel support.
(s390*-*-linux*): Add lk_tobjs.
---
gdb/Makefile.in | 3 +
gdb/configure.tgt | 7 +-
gdb/defs.h | 1 +
gdb/gdbarch.c | 32 ++
gdb/gdbarch.h | 9 +
gdb/gdbarch.sh | 4 +
gdb/lk-bitmap.h | 226 ++++++++++++++
gdb/lk-list.h | 201 +++++++++++++
gdb/lk-low.c | 864 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/lk-low.h | 335 +++++++++++++++++++++
gdb/osabi.c | 1 +
11 files changed, 1682 insertions(+), 1 deletion(-)
create mode 100644 gdb/lk-bitmap.h
create mode 100644 gdb/lk-list.h
create mode 100644 gdb/lk-low.c
create mode 100644 gdb/lk-low.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 690653ac04..056333e2cd 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -715,6 +715,7 @@ ALL_TARGET_OBS = \
iq2000-tdep.o \
linux-record.o \
linux-tdep.o \
+ lk-low.o \
lm32-tdep.o \
m32c-tdep.o \
m32r-linux-tdep.o \
@@ -1276,6 +1277,7 @@ HFILES_NO_SRCDIR = \
linux-nat.h \
linux-record.h \
linux-tdep.h \
+ lk-low.h \
location.h \
m2-lang.h \
m32r-tdep.h \
@@ -2256,6 +2258,7 @@ ALLDEPFILES = \
linux-fork.c \
linux-record.c \
linux-tdep.c \
+ lk-low.c \
lm32-tdep.c \
m32r-linux-nat.c \
m32r-linux-tdep.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index ba90411782..be68ac50fc 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -40,6 +40,10 @@ esac
i386_tobjs="i386-tdep.o arch/i386.o i387-tdep.o"
amd64_tobjs="amd64-tdep.o arch/amd64.o"
+# List of objectfiles for Linux kernel support. To be included into *-linux*
+# targets wich support Linux kernel debugging.
+lk_tobjs="lk-low.o"
+
# Here are three sections to get a list of target specific object
# files according to target triplet $TARG.
@@ -516,7 +520,8 @@ powerpc*-*-*)
s390*-*-linux*)
# Target: S390 running Linux
gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
- linux-tdep.o linux-record.o symfile-mem.o"
+ linux-tdep.o linux-record.o symfile-mem.o \
+ ${lk_tobjs}"
build_gdbserver=yes
;;
diff --git a/gdb/defs.h b/gdb/defs.h
index 91988758a3..692a7b8407 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -490,6 +490,7 @@ enum gdb_osabi
GDB_OSABI_HURD,
GDB_OSABI_SOLARIS,
GDB_OSABI_LINUX,
+ GDB_OSABI_LINUX_KERNEL,
GDB_OSABI_FREEBSD,
GDB_OSABI_NETBSD,
GDB_OSABI_OPENBSD,
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index b8703e5a55..fd37c51f6b 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -352,6 +352,7 @@ struct gdbarch
gdbarch_addressable_memory_unit_size_ftype *addressable_memory_unit_size;
char ** disassembler_options;
const disasm_options_t * valid_disassembler_options;
+ gdbarch_get_new_lk_ops_ftype *get_new_lk_ops;
};
/* Create a new ``struct gdbarch'' based on information provided by
@@ -713,6 +714,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of addressable_memory_unit_size, invalid_p == 0 */
/* Skip verify of disassembler_options, invalid_p == 0 */
/* Skip verify of valid_disassembler_options, invalid_p == 0 */
+ /* Skip verify of get_new_lk_ops, has predicate. */
if (!log.empty ())
internal_error (__FILE__, __LINE__,
_("verify_gdbarch: the following are invalid ...%s"),
@@ -1058,6 +1060,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
"gdbarch_dump: get_longjmp_target = <%s>\n",
host_address_to_string (gdbarch->get_longjmp_target));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_get_new_lk_ops_p() = %d\n",
+ gdbarch_get_new_lk_ops_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: get_new_lk_ops = <%s>\n",
+ host_address_to_string (gdbarch->get_new_lk_ops));
+ fprintf_unfiltered (file,
"gdbarch_dump: gdbarch_get_siginfo_type_p() = %d\n",
gdbarch_get_siginfo_type_p (gdbarch));
fprintf_unfiltered (file,
@@ -5077,6 +5085,30 @@ set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch,
gdbarch->valid_disassembler_options = valid_disassembler_options;
}
+int
+gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->get_new_lk_ops != NULL;
+}
+
+linux_kernel_ops *
+gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->get_new_lk_ops != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_get_new_lk_ops called\n");
+ return gdbarch->get_new_lk_ops (gdbarch, target);
+}
+
+void
+set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch,
+ gdbarch_get_new_lk_ops_ftype get_new_lk_ops)
+{
+ gdbarch->get_new_lk_ops = get_new_lk_ops;
+}
+
/* Keep a registry of per-architecture data-pointers required by GDB
modules. */
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 5cb131de1d..d1f54c08c9 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -65,6 +65,7 @@ struct mem_range;
struct syscalls_info;
struct thread_info;
struct ui_out;
+class linux_kernel_ops;
#include "regcache.h"
@@ -1554,6 +1555,14 @@ extern void set_gdbarch_disassembler_options (struct gdbarch *gdbarch, char ** d
extern const disasm_options_t * gdbarch_valid_disassembler_options (struct gdbarch *gdbarch);
extern void set_gdbarch_valid_disassembler_options (struct gdbarch *gdbarch, const disasm_options_t * valid_disassembler_options);
+/* Return a new instance of a class inherited from linux_kernel_ops */
+
+extern int gdbarch_get_new_lk_ops_p (struct gdbarch *gdbarch);
+
+typedef linux_kernel_ops * (gdbarch_get_new_lk_ops_ftype) (struct gdbarch *gdbarch, struct target_ops *target);
+extern linux_kernel_ops * gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target);
+extern void set_gdbarch_get_new_lk_ops (struct gdbarch *gdbarch, gdbarch_get_new_lk_ops_ftype *get_new_lk_ops);
+
/* Definition for an unknown syscall, used basically in error-cases. */
#define UNKNOWN_SYSCALL (-1)
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 33dfa6b349..80167f2dc2 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -1160,6 +1160,9 @@ m;int;addressable_memory_unit_size;void;;;default_addressable_memory_unit_size;;
v;char **;disassembler_options;;;0;0;;0;pstring_ptr (gdbarch->disassembler_options)
v;const disasm_options_t *;valid_disassembler_options;;;0;0;;0;host_address_to_string (gdbarch->valid_disassembler_options)
+# Return a new instance of a class inherited from linux_kernel_ops
+M;linux_kernel_ops *;get_new_lk_ops;struct target_ops *target;target
+
EOF
}
@@ -1285,6 +1288,7 @@ struct mem_range;
struct syscalls_info;
struct thread_info;
struct ui_out;
+class linux_kernel_ops;
#include "regcache.h"
diff --git a/gdb/lk-bitmap.h b/gdb/lk-bitmap.h
new file mode 100644
index 0000000000..1247e7f9fb
--- /dev/null
+++ b/gdb/lk-bitmap.h
@@ -0,0 +1,226 @@
+/* Iterator for bitmaps from the Linux kernel.
+
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __LK_BITMAP_H__
+#define __LK_BITMAP_H__
+
+#include "defs.h"
+
+#include "lk-low.h"
+
+/* Short hand access to frequently used bitmap. */
+#define lk_cpu_online_mask lk_bitmap ("cpu_online_mask", "cpumask->bits")
+
+/* Container class to handle bitmaps declared with DECLARE_BITMAP from
+ <linux>/include/linux/types.h. */
+
+class lk_bitmap
+{
+public:
+
+ template<class T>
+ class base_iterator
+ : public std::iterator<std::bidirectional_iterator_tag, T>
+ {
+ public:
+ base_iterator (const base_iterator<T> &it) = default;
+ base_iterator (std::vector<unsigned long>::const_iterator start,
+ size_t bit, size_t size)
+ : m_start (start), m_bit (bit), m_size (size)
+ { next (); }
+
+ base_iterator<T> &operator++ ()
+ { m_bit++; return next (); }
+
+ base_iterator<T> operator++ (int)
+ { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+ base_iterator<T> &operator-- ()
+ { m_bit--; return prev (); }
+
+ base_iterator<T> operator-- (int)
+ { base_iterator<T> retval = *this; --(*this); return retval; }
+
+ bool operator== (base_iterator<T> other) const
+ { return (m_start == other.m_start && m_bit == other.m_bit
+ && m_size == other.m_size); }
+
+ bool operator!= (base_iterator<T> other) const
+ { return !(*this == other); }
+
+ T operator* () const
+ { return m_bit; }
+
+ private:
+ /* Start of the vector containing the bitmap. */
+ std::vector<unsigned long>::const_iterator m_start;
+
+ /* Last set bit returned. */
+ size_t m_bit;
+
+ /* Size of the bitmap in bit. */
+ size_t m_size;
+
+ /* Get next set bit. */
+ base_iterator<T> &next ();
+
+ /* Get previous set bit. */
+ base_iterator<T> &prev ();
+ }; /* class base_iterator */
+
+ /* Constructor for bitmaps defined as variable NAME. */
+ inline lk_bitmap (const std::string &name);
+
+ /* Constructor for bitmaps defined as field in variable NAME. */
+ inline lk_bitmap (const std::string &name, const std::string &alias);
+
+ typedef base_iterator<size_t> iterator;
+ typedef base_iterator<const size_t> const_iterator;
+
+ iterator begin () { return iterator (m_bitmap.cbegin (), 0, size ()); }
+ iterator end () { return iterator (m_bitmap.cbegin (), size (), size ()); }
+
+ const_iterator cbegin () const
+ { return const_iterator (m_bitmap.cbegin (), 0, size ()); }
+ const_iterator cend () const
+ { return const_iterator (m_bitmap.cbegin (), size (), size ()); }
+
+ const_iterator begin () const
+ { return this->cbegin (); }
+ const_iterator end () const
+ { return this->cend (); }
+
+ /* Returns size of bitmap in bits. */
+ inline size_t size () const;
+
+ /* Returns Hamming weight, i.e. number of set bits, of bitmap. */
+ inline size_t hweight () const;
+
+private:
+ /* Read content of bitmap NAME. */
+ inline void read (const std::string &name);
+
+ /* Returns number of unsigned longs needed to store N bytes. */
+ inline size_t byte_to_ulong (size_t n) const;
+
+ /* Storage for content of bitmap. */
+ std::vector<unsigned long> m_bitmap;
+}; /* class bitmap */
+
+/* see declaration. */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::next ()
+{
+ size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+ auto ulong = m_start + m_bit / ulong_bits;
+ while (m_bit < m_size)
+ {
+ if (*ulong & (1 << m_bit))
+ return *this;
+
+ m_bit++;
+ if ((m_bit % ulong_bits) == 0)
+ ulong++;
+ }
+ return *this;
+}
+
+/* see declaration. */
+
+template<class T>
+lk_bitmap::base_iterator<T> &
+lk_bitmap::base_iterator<T>::prev ()
+{
+ size_t ulong_bits = lk_builtin_type_size (unsigned_long) * LK_BITS_PER_BYTE;
+ auto ulong = m_start + m_bit / ulong_bits;
+ while (m_bit > m_size)
+ {
+ if (*ulong & (1 << m_bit))
+ return *this;
+
+ m_bit--;
+ if ((m_bit % ulong_bits) == 0)
+ ulong--;
+ }
+ return *this;
+}
+
+/* see declaration. */
+
+lk_bitmap::lk_bitmap (const std::string &name)
+{
+ symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+ size_t size = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym)));
+
+ m_bitmap.resize (byte_to_ulong (size));
+ read (name);
+}
+
+/* see declaration. */
+
+lk_bitmap::lk_bitmap (const std::string &name, const std::string &alias)
+{
+ field *field = lk_field (alias);
+ m_bitmap.resize (byte_to_ulong (FIELD_SIZE (field)));
+ read (name);
+}
+
+/* see declaration. */
+
+void
+lk_bitmap::read (const std::string &name)
+{
+ size_t ulong_size = lk_builtin_type_size (unsigned_long);
+ CORE_ADDR addr = lk_address (name);
+
+ for (size_t i = 0; i < m_bitmap.size (); i++)
+ m_bitmap[i] = lk_read_ulong (addr + i * ulong_size);
+}
+
+/* see declaration. */
+size_t
+lk_bitmap::byte_to_ulong (size_t n) const
+{
+ size_t ulong_size = lk_builtin_type_size (unsigned_long);
+ return (n + ulong_size - 1) / ulong_size;
+}
+
+/* see declaration. */
+
+size_t
+lk_bitmap::size () const
+{
+ size_t ulong_size = lk_builtin_type_size (unsigned_long);
+ return (m_bitmap.size () * ulong_size * LK_BITS_PER_BYTE);
+}
+
+/* see declaration. */
+
+size_t
+lk_bitmap::hweight () const
+{
+ size_t ret = 0;
+ for (auto bit : *this)
+ ret++;
+ return ret;
+}
+
+#endif /* __LK_BITMAP_H__ */
diff --git a/gdb/lk-list.h b/gdb/lk-list.h
new file mode 100644
index 0000000000..512a47ba08
--- /dev/null
+++ b/gdb/lk-list.h
@@ -0,0 +1,201 @@
+/* Iterators for internal data structures of the Linux kernel.
+
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __LK_LIST_H__
+#define __LK_LIST_H__
+
+#include "defs.h"
+
+#include "inferior.h"
+#include "lk-low.h"
+
+
+/* Container class to handle doubly linked list using struct list_head from
+ <linux>/include/linux/types.h . */
+
+class lk_list
+{
+ template<class T>
+ class base_iterator
+ : public std::iterator<std::bidirectional_iterator_tag, T>
+ {
+ public:
+ base_iterator (const base_iterator<T> &it) = default;
+ base_iterator (CORE_ADDR start, CORE_ADDR offset, bool embedded)
+ : m_current (start), m_start (start), m_offset (offset)
+ {
+ if (!embedded)
+ next ();
+ }
+
+ base_iterator<T> &operator++ ()
+ { return next (); }
+
+ base_iterator<T> operator++ (int)
+ { base_iterator<T> retval = *this; ++(*this); return retval; }
+
+ base_iterator<T> &operator-- ()
+ { return next (false); }
+
+ base_iterator<T> operator-- (int)
+ { base_iterator<T> retval = *this; --(*this); return retval; }
+
+ bool operator== (base_iterator<T> &other) const
+ { return (m_start == other.m_start && m_current == other.m_current
+ && !m_first); }
+
+ bool operator!= (base_iterator<T> &other) const
+ { return !(*this == other); }
+
+ /* Return container of the list_head. */
+ T operator* () const
+ { return m_current - m_offset; }
+
+ private:
+ /* The list_head we are currently at. */
+ CORE_ADDR m_current;
+
+ /* First element of the list. */
+ CORE_ADDR m_start;
+
+ /* Offset of the list_head in the containing struct. */
+ CORE_ADDR m_offset;
+
+ /* For doubly linked lists start == end. Use m_first to track if we
+ just started. */
+ bool m_first = true;
+
+ /* Go to the next (forward) or prev (!forward) element. */
+ base_iterator<T> &next (bool forward = true);
+
+ /* We must always assume that the data we handle is corrupted. Use
+ curr->next->prev == curr (or ->prev->next if goining back). */
+ bool is_valid_next (CORE_ADDR next, bool forward) const;
+ }; /* class base_iterator */
+
+public:
+ /* Constructor for lists starting at address START. */
+ inline lk_list (CORE_ADDR start, const std::string &alias,
+ bool embedded = true);
+
+ /* Constructor for lists starting at variable NAME. */
+ inline lk_list (const std::string &name, const std::string &alias)
+ : lk_list (lk_address (name), alias, is_embedded (name))
+ {}
+
+ typedef base_iterator<CORE_ADDR> iterator;
+ typedef base_iterator<const CORE_ADDR> const_iterator;
+
+ /* Never advance to next element for end () --> embedded = true. */
+ iterator begin () { return iterator (m_start, m_offset, m_embedded); }
+ iterator end () { return iterator (m_start, m_offset, true); }
+
+ const_iterator cbegin () const
+ { return const_iterator (m_start, m_offset, m_embedded); }
+ const_iterator cend () const
+ { return const_iterator (m_start, m_offset, true); }
+
+ const_iterator begin () const
+ { return this->cbegin (); }
+ const_iterator end () const
+ { return this->cend (); }
+
+private:
+ /* First element of the list. */
+ CORE_ADDR m_start;
+
+ /* Offset of the list_head in the containing struct. */
+ CORE_ADDR m_offset;
+
+ /* Is the first list_head embedded in the containing struct, i.e. do we
+ have to consider m_start as a full element of the list or just an entry
+ point? */
+ bool m_embedded;
+
+ /* Check whether variable name is embeded, i.e. is not a list_head. */
+ inline bool is_embedded (const std::string &name) const;
+}; /* class lk_list */
+
+/* see declaration. */
+
+lk_list::lk_list (CORE_ADDR start, const std::string &alias, bool embedded)
+ : m_offset (lk_offset (alias)), m_embedded (embedded)
+{
+ m_start = start;
+ if (m_embedded)
+ m_start += m_offset;
+}
+
+/* see declaration. */
+
+bool
+lk_list::is_embedded (const std::string &name) const
+{
+ symbol *sym = lookup_symbol (name.c_str (), NULL, VAR_DOMAIN, NULL).symbol;
+ type *type = SYMBOL_TYPE (sym);
+
+ return !(TYPE_CODE (type) == TYPE_CODE_STRUCT
+ && streq ("list_head", TYPE_TAG_NAME (type)));
+}
+
+/* see declaration. */
+
+template<class T>
+bool
+lk_list::base_iterator<T>::is_valid_next (CORE_ADDR next, bool forward) const
+{
+ if (forward)
+ next += lk_offset ("list_head->prev");
+ else
+ next += lk_offset ("list_head->next");
+
+ return m_current == lk_read_addr (next);
+}
+
+/* see declaration. */
+
+template<class T>
+lk_list::base_iterator<T> &
+lk_list::base_iterator<T>::next (bool forward)
+{
+ CORE_ADDR next;
+
+ if (m_current == m_start && !m_first)
+ return *this;
+
+ m_first = false;
+
+ if (forward)
+ next = lk_read_addr (m_current + lk_offset ("list_head->next"));
+ else
+ next = lk_read_addr (m_current + lk_offset ("list_head->prev"));
+
+ if (!is_valid_next (next, forward))
+ {
+ error (_("Memory corruption detected while iterating list_head at "
+ "0x%s: list_head->%s != list_head."),
+ phex (m_current, lk_builtin_type_size (unsigned_long)),
+ forward ? "next->prev" : "prev->next");
+ }
+
+ m_current = next;
+
+ return *this;
+}
+#endif /* __LK_LIST_H__ */
diff --git a/gdb/lk-low.c b/gdb/lk-low.c
new file mode 100644
index 0000000000..193619e6f5
--- /dev/null
+++ b/gdb/lk-low.c
@@ -0,0 +1,864 @@
+/* Basic Linux kernel support, architecture independent.
+
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "block.h"
+#include "exceptions.h"
+#include "frame.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "gdbtypes.h"
+#include "inferior.h"
+#include "lk-bitmap.h"
+#include "lk-list.h"
+#include "lk-low.h"
+#include "objfiles.h"
+#include "observer.h"
+#include "solib.h"
+#include "target.h"
+#include "value.h"
+
+#include <algorithm>
+
+target_ops *lk_target_ops = NULL;
+linux_kernel_ops *lk_ops = NULL;
+
+/* Helper function for declare_address. Returns address of variable NAME on
+ success or -1 on failure. */
+
+static CORE_ADDR
+lk_find_address (const std::string &name)
+{
+ bound_minimal_symbol bmsym = lookup_minimal_symbol (name.c_str (), NULL,
+ NULL);
+ if (bmsym.minsym == NULL)
+ return -1;
+
+ return BMSYMBOL_VALUE_ADDRESS (bmsym);
+}
+
+/* See lk-low.h. */
+
+bool
+linux_kernel_ops::try_declare_address (const std::string &alias,
+ const std::string &name)
+{
+ if (has_address (alias))
+ return true;
+
+ CORE_ADDR addr = lk_find_address (name);
+ if (addr == -1)
+ return false;
+
+ m_symbols[alias].addr = addr;
+ return true;
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+ const std::string &name,
+ const lk_kconfig config)
+{
+ if (!try_declare_address (alias, name))
+ {
+ m_kconfig |= config;
+ warning (_("Missing address: %s"), alias.c_str ());
+ }
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_address (const std::string &alias,
+ const std::initializer_list<const std::string> names,
+ const lk_kconfig config)
+{
+ for (auto &name: names)
+ if (try_declare_address (alias, name))
+ break;
+
+ if (!has_address (alias))
+ {
+ m_kconfig |= config;
+ warning (_("Missing address: %s"), alias.c_str ());
+ }
+}
+
+/* Helper function for try_declare_type. Returns type on success or NULL on
+ failure */
+
+static struct type *
+lk_find_type (const std::string &name)
+{
+ const struct block *global;
+ const struct symbol *sym;
+
+ global = block_global_block(get_selected_block (0));
+ sym = lookup_symbol (name.c_str (), global, STRUCT_DOMAIN, NULL).symbol;
+ if (sym != NULL)
+ return SYMBOL_TYPE (sym);
+
+ /* Chek for "typedef struct { ... } name;"-like definitions. */
+ sym = lookup_symbol (name.c_str (), global, VAR_DOMAIN, NULL).symbol;
+ if (sym == NULL)
+ return NULL;
+
+ struct type *type = check_typedef (SYMBOL_TYPE (sym));
+ if (TYPE_CODE (type) != TYPE_CODE_STRUCT)
+ return NULL;
+
+ return type;
+}
+
+/* See lk-low.h. */
+
+bool
+linux_kernel_ops::try_declare_type (const std::string &alias,
+ const std::string &name)
+{
+ if (has_type (alias))
+ return true;
+
+ struct type *type = lk_find_type (name);
+
+ if (type == NULL)
+ return false;
+
+ m_symbols[unique_type_alias (alias)].type = type;
+
+ /* Also add an entry with the name actually used to m_symbol. Needed to
+ support chained field lookup. */
+ if (alias != name)
+ m_symbols[unique_type_alias (name)].type = type;
+
+ return true;
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+ const std::string &name,
+ const lk_kconfig config)
+{
+ if (!try_declare_type (alias, name))
+ {
+ m_kconfig |= config;
+ warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+ }
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_type (const std::string &alias,
+ const std::initializer_list<const std::string> names,
+ const lk_kconfig config)
+{
+ for (auto &name: names)
+ if (try_declare_type (alias, name))
+ break;
+
+ if (!has_type (alias))
+ {
+ m_kconfig |= config;
+ warning (_("Missing type: %s"), unique_type_alias (alias).c_str ());
+ }
+}
+
+/* Helper function for try_declare_field. Returns lk_symbol with field
+ belonging to TYPE on success or empty on failure. */
+
+static lk_symbol
+lk_find_field (const std::string &f_name, const struct type *type)
+{
+ struct field *field = TYPE_FIELDS (type);
+ struct field *last = field + TYPE_NFIELDS (type);
+
+ while (field != last)
+ {
+ if (streq (field->name, f_name.c_str ()))
+ return lk_symbol (field, FIELD_BYTEPOS (field));
+
+ /* Check if field is defined in anonymous struct within TYPE. */
+ if (streq (field->name, ""))
+ {
+ lk_symbol sym = lk_find_field (f_name, FIELD_TYPE (*field));
+ if (sym.field != NULL)
+ return lk_symbol (sym.field, FIELD_BYTEPOS (field) + sym.offset);
+ }
+ field++;
+ }
+ return lk_symbol ();
+}
+
+/* Helper class to parse C-like field names (type->field1->field2->...) and
+ generate aliases used in lk_ops->m_symbols. */
+
+class lk_field_parser
+{
+public:
+ lk_field_parser (const std::string &alias)
+ : m_alias (alias)
+ {
+ /* The alias must begin with s_name->f_name of the first field. */
+ m_end = m_alias.find (delim);
+ gdb_assert (m_end != std::string::npos);
+ m_end = m_alias.find (delim, m_end + delim.size ());
+ }
+
+ /* Return the struct, i.e. type name of the current field. */
+ std::string s_name () const
+ {
+ if (m_last_type == NULL)
+ return m_alias.substr (0, m_alias.find (delim));
+
+ if (TYPE_CODE (m_last_type) == TYPE_CODE_TYPEDEF)
+ return TYPE_NAME (m_last_type);
+ else
+ return TYPE_TAG_NAME (m_last_type);
+ }
+
+ /* Return the field name of the current field. */
+ std::string f_name () const
+ {
+ size_t start;
+
+ if (m_last_type == NULL)
+ start = m_alias.find (delim) + delim.size ();
+ else
+ start = m_start;
+
+ return m_alias.substr (start, m_end - start);
+ }
+
+ /* Return the full name of the current field. */
+ std::string name () const
+ { return s_name () + delim + f_name (); }
+
+ /* Advance to the next field. */
+ lk_field_parser *next ()
+ {
+ gdb_assert (!empty ());
+
+ m_last_type = FIELD_TYPE (*lk_field (name ()));
+ m_start = m_end + delim.size ();
+ m_end = m_alias.find (delim, m_start);
+
+ return this;
+ }
+
+ /* True when all fiels have been parsed. */
+ bool empty () const
+ { return m_end == std::string::npos; }
+
+ /* Return the depth, i.e. number of fields, in m_alias. */
+ unsigned int depth () const
+ {
+ size_t pos = m_alias.find (delim);
+ unsigned int ret = 0;
+
+ while (pos != std::string::npos)
+ {
+ ret ++;
+ pos = m_alias.find (delim, pos + delim.size ());
+ }
+
+ return ret;
+ }
+
+private:
+ /* Alias originally passed to parser. */
+ std::string m_alias;
+
+ /* First index of current field in m_alias. */
+ size_t m_start = 0;
+
+ /* Last index of current field in m_alias. */
+ size_t m_end = 0;
+
+ /* Type of the last field found. Needed to get s_name of embedded
+ fields. */
+ struct type *m_last_type = NULL;
+
+ /* Delemiter used to separate fields. */
+ const std::string delim = "->";
+};
+
+/* See lk-low.h. */
+
+bool
+linux_kernel_ops::try_declare_field (const std::string &orig_alias,
+ const std::string &orig_name)
+{
+ if (has_field (orig_alias))
+ return true;
+
+ lk_field_parser alias (orig_alias);
+ lk_field_parser name (orig_name);
+
+ /* Only allow declaration of one field at a time. */
+ gdb_assert (alias.depth () == 1);
+ gdb_assert (name.depth () == 1);
+
+ if (!try_declare_type (alias.s_name (), name.s_name ()))
+ return false;
+
+ lk_symbol field = lk_find_field (name.f_name (), type (alias.s_name ()));
+ if (field.field == NULL)
+ return false;
+
+ m_symbols[alias.name ()] = field;
+ return true;
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+ const std::string &name,
+ const lk_kconfig config)
+{
+ if (!try_declare_field (alias, name))
+ {
+ m_kconfig |= config;
+ warning (_("Missing field: %s"), alias.c_str ());
+ }
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::declare_field (const std::string &alias,
+ const std::initializer_list<const std::string> names,
+ const lk_kconfig config)
+{
+ for (auto &name: names)
+ if (try_declare_field (alias, name))
+ break;
+
+ if (!has_field (alias))
+ {
+ m_kconfig |= config;
+ warning (_("Missing field: %s"), alias.c_str ());
+ }
+}
+
+/* See lk-low.h. */
+
+void
+linux_kernel_ops::read_symbols ()
+{
+ if (!m_symbols.empty ())
+ m_symbols.clear ();
+
+ declare_field ("task_struct->tasks", LK_CONFIG_ALWAYS);
+ declare_field ("task_struct->pid", LK_CONFIG_ALWAYS);
+ declare_field ("task_struct->tgid", LK_CONFIG_ALWAYS);
+ declare_field ("task_struct->thread_group", LK_CONFIG_ALWAYS);
+ declare_field ("task_struct->comm", LK_CONFIG_ALWAYS);
+ declare_field ("task_struct->thread", LK_CONFIG_ALWAYS);
+
+ declare_field ("list_head->next", LK_CONFIG_ALWAYS);
+ declare_field ("list_head->prev", LK_CONFIG_ALWAYS);
+
+ declare_field ("rq->curr", LK_CONFIG_ALWAYS);
+
+ declare_field ("cpumask->bits", LK_CONFIG_ALWAYS);
+
+ declare_address ("init_task", LK_CONFIG_ALWAYS);
+ declare_address ("runqueues", LK_CONFIG_ALWAYS);
+ declare_address ("__per_cpu_offset", LK_CONFIG_ALWAYS);
+
+ declare_address ("cpu_online_mask", {"__cpu_online_mask", /* linux 4.5+ */
+ "cpu_online_bits"}, /* linux -4.4 */
+ LK_CONFIG_ALWAYS);
+
+ arch_read_symbols ();
+
+ if (!ifdef (LK_CONFIG_ALWAYS))
+ error (_("Could not find all symbols needed. Aborting."));
+}
+
+/* See lk-low.h. */
+
+CORE_ADDR
+linux_kernel_ops::offset (const std::string &orig_alias) const
+{
+ lk_field_parser alias (orig_alias);
+ CORE_ADDR ret = m_symbols.at (alias.name ()).offset;
+
+ while (!alias.empty ())
+ ret += m_symbols.at (alias.next ()->name ()).offset;
+
+ return ret;
+}
+
+/* Map cpu number CPU to the original PTID from target beneath. */
+
+static ptid_t
+lk_cpu_to_old_ptid (const unsigned int cpu)
+{
+ struct lk_ptid_map *ptid_map;
+
+ for (ptid_map = lk_ops->old_ptid; ptid_map;
+ ptid_map = ptid_map->next)
+ {
+ if (ptid_map->cpu == cpu)
+ return ptid_map->old_ptid;
+ }
+
+ error (_("Could not map CPU %d to original PTID. Aborting."), cpu);
+}
+
+/* Helper functions to read and return basic types at a given ADDRess. */
+
+/* Read and return the integer value at address ADDR. */
+
+int
+lk_read_int (CORE_ADDR addr)
+{
+ size_t int_size = lk_builtin_type_size (int);
+ enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+ return read_memory_integer (addr, int_size, endian);
+}
+
+/* Read and return the unsigned integer value at address ADDR. */
+
+unsigned int
+lk_read_uint (CORE_ADDR addr)
+{
+ size_t uint_size = lk_builtin_type_size (unsigned_int);
+ enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+ return read_memory_unsigned_integer (addr, uint_size, endian);
+}
+
+/* Read and return the long integer value at address ADDR. */
+
+LONGEST
+lk_read_long (CORE_ADDR addr)
+{
+ size_t long_size = lk_builtin_type_size (long);
+ enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+ return read_memory_integer (addr, long_size, endian);
+}
+
+/* Read and return the unsigned long integer value at address ADDR. */
+
+ULONGEST
+lk_read_ulong (CORE_ADDR addr)
+{
+ size_t ulong_size = lk_builtin_type_size (unsigned_long);
+ enum bfd_endian endian = gdbarch_byte_order (current_inferior ()->gdbarch);
+ return read_memory_unsigned_integer (addr, ulong_size, endian);
+}
+
+/* Read and return the address value at address ADDR. */
+
+CORE_ADDR
+lk_read_addr (CORE_ADDR addr)
+{
+ return (CORE_ADDR) lk_read_ulong (addr);
+}
+
+/* See lk-low.h. */
+
+CORE_ADDR
+linux_kernel_ops::percpu_offset (unsigned int cpu)
+{
+ size_t ulong_size = lk_builtin_type_size (unsigned_long);
+ CORE_ADDR percpu_elt = address ("__per_cpu_offset") + (ulong_size * cpu);
+ return lk_read_addr (percpu_elt);
+}
+
+/* See lk-low.h. */
+
+unsigned int
+linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti)
+{
+ for (unsigned int cpu : lk_cpu_online_mask)
+ {
+ CORE_ADDR rq = address ("runqueues") + percpu_offset (cpu);
+ CORE_ADDR curr = lk_read_addr (rq + offset ("rq->curr"));
+ int pid = lk_read_int (curr + offset ("task_struct->pid"));
+
+ if (pid == ti->ptid.lwp ())
+ return cpu;
+ }
+
+ error (_("Could not map thread with pid %d, lwp %lu to a cpu."),
+ ti->ptid.pid (), ti->ptid.lwp ());
+}
+
+/* Test if a given task TASK is running. See comment in lk-low.h for
+ details. */
+
+unsigned int
+lk_task_running (CORE_ADDR task)
+{
+ for (unsigned int cpu : lk_cpu_online_mask)
+ {
+ CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+ CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+
+ if (curr == task)
+ return cpu;
+ }
+
+ return LK_CPU_INVAL;
+}
+
+/* Update running tasks with information from struct rq->curr. */
+
+static void
+lk_update_running_tasks ()
+{
+ for (unsigned int cpu : lk_cpu_online_mask)
+ {
+ CORE_ADDR rq = lk_address ("runqueues") + lk_ops->percpu_offset (cpu);
+ CORE_ADDR curr = lk_read_addr (rq + lk_offset ("rq->curr"));
+ int pid = lk_read_int (curr + lk_offset ("task_struct->pid"));
+ int inf_pid = current_inferior ()->pid;
+
+ ptid_t new_ptid (inf_pid, pid, curr);
+ ptid_t old_ptid = lk_cpu_to_old_ptid (cpu); /* FIXME not suitable for
+ running targets? */
+
+ thread_info *tp = find_thread_ptid (old_ptid);
+ if (tp && tp->state != THREAD_EXITED)
+ thread_change_ptid (old_ptid, new_ptid);
+ }
+}
+
+/* Update sleeping tasks by walking the task_structs starting from
+ init_task. */
+
+static void
+lk_update_sleeping_tasks ()
+{
+ int inf_pid = current_inferior ()->pid;
+
+ for (CORE_ADDR task : lk_list ("init_task", "task_struct->tasks"))
+ {
+ for (CORE_ADDR thread : lk_list (task, "task_struct->thread_group"))
+ {
+ int pid = lk_read_int (thread + lk_offset ("task_struct->pid"));
+ ptid_t ptid (inf_pid, pid, thread);
+
+ thread_info *tp = find_thread_ptid (ptid);
+ if (tp == NULL || tp->state == THREAD_EXITED)
+ add_thread (ptid);
+ }
+ }
+}
+
+/* Function for targets to_update_thread_list hook. */
+
+static void
+lk_update_thread_list (struct target_ops *target)
+{
+ prune_threads ();
+ lk_update_running_tasks ();
+ lk_update_sleeping_tasks ();
+}
+
+/* Function for targets to_fetch_registers hook. */
+
+static void
+lk_fetch_registers (struct target_ops *target,
+ struct regcache *regcache, int regnum)
+{
+ CORE_ADDR task = (CORE_ADDR) regcache->ptid ().tid ();
+
+ /* Are we called during init? */
+ if (task == 0)
+ return target->beneath->to_fetch_registers (target, regcache, regnum);
+
+ unsigned int cpu = lk_task_running (task);
+
+ /* Let the target beneath fetch registers of running tasks. */
+ if (cpu != LK_CPU_INVAL)
+ {
+ scoped_restore_regcache_ptid restore_regcache (regcache);
+ regcache->set_ptid (lk_cpu_to_old_ptid (cpu));
+
+ lk_ops->beneath ()->to_fetch_registers (target, regcache, regnum);
+ }
+ else
+ {
+ lk_ops->get_registers (task, target, regcache, regnum);
+
+ /* Mark all registers not found as unavailable. */
+ for (int i = 0; i < gdbarch_num_regs (regcache->arch ()); i++)
+ {
+ if (regcache->get_register_status (i) != REG_VALID)
+ regcache->invalidate (i);
+ }
+ }
+}
+
+/* Function for targets to_pid_to_str hook. Marks running tasks with an
+ asterisk "*". */
+
+static const char *
+lk_pid_to_str (struct target_ops *target, ptid_t ptid)
+{
+ CORE_ADDR task = (CORE_ADDR) ptid.tid ();
+ static std::string str;
+ const char *fmt;
+
+ if (lk_task_running (task) != LK_CPU_INVAL)
+ fmt = "PID: %5li*, 0x%s";
+ else
+ fmt = "PID: %6li, 0x%s";
+
+ str = string_printf (fmt, ptid.lwp (),
+ phex (task, lk_builtin_type_size (unsigned_long)));
+
+ return str.c_str ();
+}
+
+/* Function for targets to_thread_name hook. */
+
+static const char *
+lk_thread_name (struct target_ops *target, struct thread_info *ti)
+{
+ static std::string str (LK_TASK_COMM_LEN, '\0');
+
+ size_t size = std::min ((unsigned int) LK_TASK_COMM_LEN,
+ LK_ARRAY_LEN(lk_field ("task_struct->comm")));
+
+ CORE_ADDR task = (CORE_ADDR) ti->ptid.tid ();
+ CORE_ADDR comm = task + lk_offset ("task_struct->comm");
+ read_memory (comm, (gdb_byte *) str.data (), size);
+
+ str = string_printf ("%-16s", str.c_str ());
+
+ return str.c_str ();
+}
+
+
+/* Functions to initialize and free target_ops and its private data. As well
+ as functions for targets to_open/close/detach hooks. */
+
+/* Check if OBFFILE is a Linux kernel. */
+
+static bool
+lk_is_linux_kernel (struct objfile *objfile)
+{
+ int ok = 0;
+
+ if (objfile == NULL || !(objfile->flags & OBJF_MAINLINE))
+ return false;
+
+ ok += lookup_minimal_symbol ("linux_banner", NULL, objfile).minsym != NULL;
+ ok += lookup_minimal_symbol ("_stext", NULL, objfile).minsym != NULL;
+ ok += lookup_minimal_symbol ("_etext", NULL, objfile).minsym != NULL;
+
+ return (ok > 2);
+}
+
+/* Frees the cpu to old ptid map. */
+
+static void
+lk_free_ptid_map ()
+{
+ while (lk_ops->old_ptid)
+ {
+ struct lk_ptid_map *tmp;
+
+ tmp = lk_ops->old_ptid;
+ lk_ops->old_ptid = tmp->next;
+ XDELETE (tmp);
+ }
+}
+
+/* Initialize the cpu to old ptid map. */
+
+static void
+lk_init_ptid_map ()
+{
+ struct thread_info *ti;
+
+ if (lk_ops->old_ptid != NULL)
+ lk_free_ptid_map ();
+
+ ALL_THREADS (ti)
+ {
+ struct lk_ptid_map *ptid_map = XCNEW (struct lk_ptid_map);
+
+ ptid_map->cpu = lk_ops->beneath_thread_to_cpu (ti);
+ ptid_map->old_ptid = ti->ptid;
+
+ ptid_map->next = lk_ops->old_ptid;
+ lk_ops->old_ptid = ptid_map;
+ }
+}
+
+/* Initializes all private data and pushes the linux kernel target, if not
+ already done. */
+
+static void
+lk_try_push_target ()
+{
+ struct gdbarch *gdbarch;
+
+ gdbarch = current_inferior ()->gdbarch;
+ if (!(gdbarch && gdbarch_get_new_lk_ops_p (gdbarch)))
+ error (_("Linux kernel debugging not supported on %s."),
+ gdbarch_bfd_arch_info (gdbarch)->printable_name);
+
+ lk_ops = gdbarch_get_new_lk_ops (gdbarch, lk_target_ops);
+ lk_ops->read_symbols ();
+
+ lk_init_ptid_map ();
+ lk_update_thread_list (lk_ops->target ());
+
+ if (!target_is_pushed (lk_target_ops))
+ push_target (lk_ops->target ());
+}
+
+/* Function for targets to_open hook. */
+
+static void
+lk_open (const char *args, int from_tty)
+{
+ struct objfile *objfile;
+
+ if (target_is_pushed (lk_target_ops))
+ {
+ printf_unfiltered (_("Linux kernel target already pushed. Aborting\n"));
+ return;
+ }
+
+ for (objfile = current_program_space->objfiles; objfile;
+ objfile = objfile->next)
+ {
+ if (lk_is_linux_kernel (objfile)
+ && inferior_ptid.pid () != 0)
+ {
+ lk_try_push_target ();
+ return;
+ }
+ }
+ printf_unfiltered (_("Could not find a valid Linux kernel object file. "
+ "Aborting.\n"));
+}
+
+/* Function for targets to_close hook. Deletes all private data. */
+
+static void
+lk_close (struct target_ops *ops)
+{
+ lk_free_ptid_map ();
+
+ delete (lk_ops);
+}
+
+/* Function for targets to_detach hook. */
+
+static void
+lk_detach (struct target_ops *t, inferior *inf, int from_tty)
+{
+ struct target_ops *beneath = lk_ops->beneath ();
+
+ unpush_target (lk_ops->target ());
+ reinit_frame_cache ();
+ if (from_tty)
+ printf_filtered (_("Linux kernel target detached.\n"));
+
+ beneath->to_detach (beneath, inf, from_tty);
+}
+
+/* Function for new objfile observer. */
+
+static void
+lk_observer_new_objfile (struct objfile *objfile)
+{
+ if (lk_is_linux_kernel (objfile) && inferior_ptid.pid () != 0)
+ lk_try_push_target ();
+}
+
+/* Function for inferior created observer. */
+
+static void
+lk_observer_inferior_created (struct target_ops *ops, int from_tty)
+{
+ struct objfile *objfile;
+
+ if (inferior_ptid.pid () == 0)
+ return;
+
+ for (objfile = current_inferior ()->pspace->objfiles; objfile;
+ objfile = objfile->next)
+ {
+ if (lk_is_linux_kernel (objfile))
+ {
+ lk_try_push_target ();
+ return;
+ }
+ }
+}
+
+/* Initialize linux kernel target. */
+
+static void
+init_lk_target_ops (void)
+{
+ struct target_ops *t;
+
+ if (lk_target_ops != NULL)
+ return;
+
+ t = XCNEW (struct target_ops);
+ t->to_shortname = "linux-kernel";
+ t->to_longname = "linux kernel support";
+ t->to_doc = "Adds support to debug the Linux kernel";
+
+ t->to_open = lk_open;
+ t->to_close = lk_close;
+ t->to_detach = lk_detach;
+ t->to_fetch_registers = lk_fetch_registers;
+ t->to_update_thread_list = lk_update_thread_list;
+ t->to_pid_to_str = lk_pid_to_str;
+ t->to_thread_name = lk_thread_name;
+
+ t->to_stratum = thread_stratum;
+ t->to_magic = OPS_MAGIC;
+
+ lk_target_ops = t;
+
+ add_target (t);
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_linux_kernel;
+
+void
+_initialize_linux_kernel (void)
+{
+ init_lk_target_ops ();
+
+ observer_attach_new_objfile (lk_observer_new_objfile);
+ observer_attach_inferior_created (lk_observer_inferior_created);
+}
diff --git a/gdb/lk-low.h b/gdb/lk-low.h
new file mode 100644
index 0000000000..39c0d88f43
--- /dev/null
+++ b/gdb/lk-low.h
@@ -0,0 +1,335 @@
+/* Basic Linux kernel support, architecture independent.
+
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __LK_LOW_H__
+#define __LK_LOW_H__
+
+#include <unordered_map>
+
+#include "gdbtypes.h"
+#include "target.h"
+
+/* Copied constants defined in Linux kernel. */
+#define LK_TASK_COMM_LEN 16
+#define LK_BITS_PER_BYTE 8
+
+/* Definitions used in linux kernel target. */
+#define LK_CPU_INVAL -1U
+
+/* Helper functions to read and return a value at a given ADDRess. */
+extern int lk_read_int (CORE_ADDR addr);
+extern unsigned int lk_read_uint (CORE_ADDR addr);
+extern LONGEST lk_read_long (CORE_ADDR addr);
+extern ULONGEST lk_read_ulong (CORE_ADDR addr);
+extern CORE_ADDR lk_read_addr (CORE_ADDR addr);
+
+/* Enum to track the config options used to build the kernel. Whenever
+ a symbol is declared (in linux_kernel_ops::{arch_}read_symbols) which
+ only exists if the kernel was built with a certain config option an entry
+ has to be added here. */
+enum lk_kconfig_values
+{
+ LK_CONFIG_ALWAYS = 1 << 0,
+ LK_CONFIG_SMT = 1 << 1,
+ LK_CONFIG_MODULES = 1 << 2,
+};
+DEF_ENUM_FLAGS_TYPE (enum lk_kconfig_values, lk_kconfig);
+
+/* We use the following convention for PTIDs:
+
+ ptid->pid = inferiors PID
+ ptid->lwp = PID from task_stuct
+ ptid->tid = address of task_struct
+
+ The task_structs address as TID has two reasons. First, we need it quite
+ often and there is no other reasonable way to pass it down. Second, it
+ helps us to distinguish swapper tasks as they all have PID = 0.
+
+ Furthermore we cannot rely on the target beneath to use the same PID as the
+ task_struct. Thus we need a mapping between our PTID and the PTID of the
+ target beneath. Otherwise it is impossible to pass jobs, e.g. fetching
+ registers of running tasks, to the target beneath. */
+
+/* Private data struct to map between our and the target beneath PTID. */
+
+struct lk_ptid_map
+{
+ struct lk_ptid_map *next;
+ unsigned int cpu;
+ ptid_t old_ptid;
+};
+
+/* Cache for the value of a symbol. Used in linux_kernel_ops->m_symbols. */
+
+union lk_symbol
+{
+ CORE_ADDR addr;
+ struct type *type;
+ struct
+ {
+ struct field *field;
+ CORE_ADDR offset;
+ };
+
+ lk_symbol () {field = NULL; offset = 0;}
+ lk_symbol (struct field *f, CORE_ADDR o) {field = f, offset = o;}
+};
+
+class linux_kernel_ops
+{
+public:
+ linux_kernel_ops (struct target_ops *ops)
+ : m_ops (ops)
+ {}
+
+ virtual ~linux_kernel_ops () = default;
+
+ /* Read registers from the target and supply their content to regcache. */
+ virtual void get_registers (CORE_ADDR task, struct target_ops *target,
+ struct regcache *regcache, int regnum) = 0;
+
+ /* Return the per_cpu_offset of cpu CPU. Default uses __per_cpu_offset
+ array to determine the offset. */
+ virtual CORE_ADDR percpu_offset (unsigned int cpu);
+
+ /* Maps thread of target beneath to a cpu id. Default assumes
+ rq->curr->pid == beneath_ptid.lwp. */
+ virtual unsigned int beneath_thread_to_cpu (thread_info *ti);
+
+ /* Return the linux-kernel target. */
+ struct target_ops *target () const
+ { return m_ops; }
+
+ /* Return the target beneath the linux-kernel target. */
+ struct target_ops *beneath () const
+ { return m_ops->beneath; }
+
+ /* Return a previously declared address with key ALIAS.
+ Throws internal_error if requested symbol was not declared first. */
+ CORE_ADDR address (const std::string &alias) const
+ {
+ gdb_assert (has_address (alias));
+ return m_symbols.at (alias).addr;
+ }
+
+ /* Same like address but for types. */
+ struct type *type (const std::string &alias) const
+ {
+ gdb_assert (has_type (alias));
+ return m_symbols.at (unique_type_alias(alias)).type;
+ }
+
+ /* Same like address but for fields. */
+ struct field *field (const std::string &alias) const
+ {
+ gdb_assert (has_field (alias));
+ return m_symbols.at (alias).field;
+ }
+
+ /* Checks whether address ALIAS exists in m_symbols. */
+ bool has_address (const std::string &alias) const
+ { return has_symbol (alias); }
+
+ /* Same like has_address but for types. */
+ bool has_type (const std::string &alias) const
+ { return has_symbol (unique_type_alias (alias)); }
+
+ /* Same like has_address but for fields. */
+ bool has_field (const std::string &alias) const
+ { return has_symbol (alias); }
+
+ /* Return offset of field ALIAS (in byte). */
+ CORE_ADDR offset (const std::string &alias) const;
+
+ /* Check whether the kernel was build using this config option. */
+ bool ifdef (lk_kconfig config) const
+ { return !(m_kconfig & config); }
+
+ /* Linked list to map between cpu number and original ptid from target
+ beneath. */
+ struct lk_ptid_map *old_ptid = NULL;
+
+ /* Declare and initialize all symbols needed. Must be called _after_
+ symbol tables were initialized. */
+ void read_symbols ();
+
+protected:
+ /* Virtual method to allow architectures to declare their own symbols.
+ Called by read_symbols. */
+ virtual void arch_read_symbols ()
+ {}
+
+ /* Helper function to declare_address. Returns true when symbol NAME
+ using key ALIAS was successfully declared, false otherwise. Try not to
+ use this function directly but use declare_address instead. */
+ bool try_declare_address (const std::string &alias,
+ const std::string &names);
+
+ /* Same like try_declare_address but for types. */
+ bool try_declare_type (const std::string &alias, const std::string &name);
+
+ /* Same like try_declare_address but for fields. */
+ bool try_declare_field (const std::string &alias, const std::string &name);
+
+ /* Same like try_declare_field but with NAME = ALIAS. */
+ bool try_declare_field (const std::string &name)
+ { return try_declare_field (name, name); }
+
+ /* Declare symbol NAME using key ALIAS. If no symbol NAME could be found
+ mark CONFIG as missing. */
+ void declare_address (const std::string &alias, const std::string &name,
+ const lk_kconfig config);
+
+ /* Same like above but with NAME = ALIAS. */
+ void declare_address (const std::string &name, const lk_kconfig config)
+ { declare_address (name, name, config); }
+
+ /* Same like above but only mark CONFIG as missing if none of the symbols
+ in NAMES could be found. */
+ void declare_address (const std::string &alias,
+ const std::initializer_list<const std::string> names,
+ const lk_kconfig config);
+
+ /* See declare_address. */
+ void declare_type (const std::string &alias, const std::string &name,
+ const lk_kconfig config);
+
+ /* See declare_address. */
+ void declare_type (const std::string &name, const lk_kconfig config)
+ { declare_type (name, name, config); }
+
+ /* See declare_address. */
+ void declare_type (const std::string &alias,
+ const std::initializer_list<const std::string> names,
+ const lk_kconfig config);
+
+ /* See declare_address. */
+ void declare_field (const std::string &alias, const std::string &name,
+ const lk_kconfig kconfig);
+
+ /* See declare_address. */
+ void declare_field (const std::string &name, const lk_kconfig kconfig)
+ { declare_field (name, name, kconfig); }
+
+ /* See declare_address. */
+ void declare_field (const std::string &alias,
+ const std::initializer_list <const std::string> names,
+ const lk_kconfig config);
+
+private:
+ /* The target_ops we are working with. */
+ struct target_ops *m_ops;
+
+ /* The configuration used to build the kernel. To make the implementation
+ easier m_kconfig is inverse, i.e. it tracks the _missing_ config options
+ not the set ones. */
+ lk_kconfig m_kconfig = 0;
+
+ /* Collection of all declared symbols (addresses, fields etc.). */
+ std::unordered_map<std::string, union lk_symbol> m_symbols;
+
+ /* Returns unique alias for struct ALIAS. */
+ const std::string unique_type_alias (const std::string &alias) const
+ {
+ std::string prefix ("struct ");
+ if (startswith (alias.c_str (), prefix.c_str ()))
+ return alias;
+ return prefix + alias;
+ }
+
+ /* Check if m_symbols contains ALIAS. */
+ bool has_symbol (const std::string &alias) const
+ { return m_symbols.count (alias) != 0; }
+};
+
+extern target_ops *lk_target_ops;
+extern linux_kernel_ops *lk_ops;
+
+/* Short hand access to frequently used lk_ops methods. */
+
+static inline CORE_ADDR
+lk_address (const std::string &alias)
+{
+ return lk_ops->address (alias);
+}
+
+static inline struct type *
+lk_type (const std::string &alias)
+{
+ return lk_ops->type (alias);
+}
+
+static inline struct field *
+lk_field (const std::string &alias)
+{
+ return lk_ops->field (alias);
+}
+
+static inline CORE_ADDR
+lk_offset (const std::string &alias)
+{
+ return lk_ops->offset (alias);
+}
+
+static inline bool
+lk_ifdef (lk_kconfig config)
+{
+ return lk_ops->ifdef (config);
+}
+
+/* Align VAL to BASE. BASE must be a power of 2. */
+#define LK_ALIGN(val, base) LK_ALIGN_MASK ((val), (typeof(val))(base) - 1)
+
+/* Same as LK_ALIGN, but aligns down. */
+#define LK_ALIGN_DOWN(val, base) ((val) & ~((typeof(val))(base) - 1))
+
+/* Helper for LK_ALIGN. */
+#define LK_ALIGN_MASK(val, mask) (((val) + (mask)) & ~(mask))
+
+/* Short hand access to current gdbarchs builtin types and their
+ size (in byte). For TYPE replace spaces " " by underscore "_", e.g.
+ "unsigned int" => "unsigned_int". */
+#define lk_builtin_type(type) \
+ (builtin_type (current_inferior ()->gdbarch)->builtin_##type)
+#define lk_builtin_type_size(type) \
+ (lk_builtin_type (type)->length)
+
+/* If field FIELD is an array returns its length (in #elements). */
+#define LK_ARRAY_LEN(field) \
+ (FIELD_SIZE (field) / FIELD_TARGET_SIZE (field))
+
+/* Additional access macros to fields in the style of gdbtypes.h */
+/* Returns the size of field FIELD (in bytes). If FIELD is an array returns
+ the size of the whole array. */
+#define FIELD_SIZE(field) \
+ TYPE_LENGTH (check_typedef (FIELD_TYPE ((*field))))
+
+/* Returns the size of the target type of field FIELD (in bytes). If FIELD is
+ an array returns the size of its elements. */
+#define FIELD_TARGET_SIZE(field) \
+ TYPE_LENGTH (check_typedef (TYPE_TARGET_TYPE (FIELD_TYPE ((*field)))))
+
+#define FIELD_BYTEPOS(field) \
+ (FIELD_BITPOS (*field) / LK_BITS_PER_BYTE)
+
+/* Tests if a given task TASK is running. Returns either the cpu-id
+ if running or LK_CPU_INVAL if not. */
+extern unsigned int lk_task_running (CORE_ADDR task);
+
+#endif /* __LK_LOW_H__ */
diff --git a/gdb/osabi.c b/gdb/osabi.c
index fd44deb9fa..14ddd414f3 100644
--- a/gdb/osabi.c
+++ b/gdb/osabi.c
@@ -64,6 +64,7 @@ static const struct osabi_names gdb_osabi_names[] =
{ "GNU/Hurd", NULL },
{ "Solaris", NULL },
{ "GNU/Linux", "linux(-gnu[^-]*)?" },
+ { "Linux kernel", NULL },
{ "FreeBSD", NULL },
{ "NetBSD", NULL },
{ "OpenBSD", NULL },
--
2.13.5
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH v5 7/9] Add privileged registers for s390x
2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
` (5 preceding siblings ...)
2018-03-12 15:31 ` [RFC PATCH v5 4/9] Add basic Linux kernel support Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
2018-03-12 15:31 ` [RFC PATCH v5 9/9] Add S390 support for linux-kernel target Philipp Rudo
8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez
The Linux kernel and thus the linux-kernel target needs access to S390x's
privileged registers. Define new features and prepare s390-tdep.* to use
them in new Linux kernel code.
gdb/ChangeLog:
* features/s390-cr.xml: New file.
* features/s390x-cr-linux64.xml: New file
* features/s390x-vxcr-linux64.xml: New file
* features/Makefile:
(WICH): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
(XMLTOC): Add s390x-cr-linux64.xml and s390x-vxcr-linux64.xml
* features/s390x-cr-linux64.c: Generated.
* features/s390x-vxcr-linux64.c: Generated.
* regformats/s390x-cr-linux64.dat: Generated.
* regformats/s390x-vxcr-linux64.dat: Generated.
* s390-tdep.h: Define regnums for control registers.
(S390_NUM_REGS): Adjust.
* s390-tdep.c: (s390_dwarf_regmat): Add control registers.
---
gdb/features/Makefile | 11 ++-
gdb/features/s390-cr.xml | 26 ++++++
gdb/features/s390x-cr-linux64.c | 99 ++++++++++++++++++++
gdb/features/s390x-cr-linux64.xml | 24 +++++
gdb/features/s390x-vxcr-linux64.c | 170 ++++++++++++++++++++++++++++++++++
gdb/features/s390x-vxcr-linux64.xml | 25 +++++
gdb/regformats/s390x-cr-linux64.dat | 76 +++++++++++++++
gdb/regformats/s390x-vxcr-linux64.dat | 108 +++++++++++++++++++++
gdb/s390-tdep.c | 8 +-
gdb/s390-tdep.h | 24 ++++-
10 files changed, 562 insertions(+), 9 deletions(-)
create mode 100644 gdb/features/s390-cr.xml
create mode 100644 gdb/features/s390x-cr-linux64.c
create mode 100644 gdb/features/s390x-cr-linux64.xml
create mode 100644 gdb/features/s390x-vxcr-linux64.c
create mode 100644 gdb/features/s390x-vxcr-linux64.xml
create mode 100644 gdb/regformats/s390x-cr-linux64.dat
create mode 100644 gdb/regformats/s390x-vxcr-linux64.dat
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 82609f5862..7924a64d1e 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -79,6 +79,7 @@ WHICH = aarch64 \
s390-te-linux64 s390x-te-linux64 s390-vx-linux64 s390x-vx-linux64 \
s390-tevx-linux64 s390x-tevx-linux64 \
s390-gs-linux64 s390x-gs-linux64 \
+ s390x-cr-linux64 s390x-vxcr-linux64 \
tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
# Record which registers should be sent to GDB by default after stop.
@@ -179,16 +180,18 @@ XMLTOC = \
s390-linux64v1.xml \
s390-linux64v2.xml \
s390-te-linux64.xml \
+ s390-vx-linux64.xml \
+ s390-tevx-linux64.xml \
s390x-linux64.xml \
s390x-linux64v1.xml \
s390x-linux64v2.xml \
s390x-te-linux64.xml \
- s390-tevx-linux64.xml \
- s390-vx-linux64.xml \
- s390x-tevx-linux64.xml \
s390x-vx-linux64.xml \
s390-gs-linux64.xml \
- s390x-gs-linux64.xml
+ s390x-gs-linux64.xml \
+ s390x-cr-linux64.xml \
+ s390x-tevx-linux64.xml \
+ s390x-vxcr-linux64.xml
TDESC_CFILES = $(patsubst %.xml,%.c,$(XMLTOC))
GDB = false
diff --git a/gdb/features/s390-cr.xml b/gdb/features/s390-cr.xml
new file mode 100644
index 0000000000..15ae4f9af7
--- /dev/null
+++ b/gdb/features/s390-cr.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 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.cr">
+ <reg name="cr0" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr1" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr2" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr3" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr4" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr5" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr6" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr7" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr8" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr9" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr10" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr11" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr12" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr13" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr14" bitsize="64" type="uint64" group="control"/>
+ <reg name="cr15" bitsize="64" type="uint64" group="control"/>
+</feature>
diff --git a/gdb/features/s390x-cr-linux64.c b/gdb/features/s390x-cr-linux64.c
new file mode 100644
index 0000000000..5f75aefe26
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.c
@@ -0,0 +1,99 @@
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: s390x-cr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_cr_linux64;
+static void
+initialize_tdesc_s390x_cr_linux64 (void)
+{
+ struct target_desc *result = allocate_target_description ();
+ set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+ struct tdesc_feature *feature;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+ tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+ tdesc_create_reg (feature, "pswa", 1, 1, "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");
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+ tdesc_create_reg (feature, "cr0", 51, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr1", 52, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr2", 53, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr3", 54, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr4", 55, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr5", 56, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr6", 57, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr7", 58, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr8", 59, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr9", 60, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr10", 61, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr11", 62, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr12", 63, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr13", 64, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr14", 65, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr15", 66, 1, "control", 64, "uint64");
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+ tdesc_create_reg (feature, "timer", 67, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "todcmp", 68, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "todpreg", 69, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "prefix", 70, 1, "system", 32, "uint32");
+
+ tdesc_s390x_cr_linux64 = result;
+}
diff --git a/gdb/features/s390x-cr-linux64.xml b/gdb/features/s390x-cr-linux64.xml
new file mode 100644
index 0000000000..75c5d611df
--- /dev/null
+++ b/gdb/features/s390x-cr-linux64.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 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 kernel-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"/>
+ <xi:include href="s390-cr.xml"/>
+
+ <feature name="org.gnu.gdb.s390.privileged">
+ <reg name="timer" bitsize="64" type="uint64" group="system"/>
+ <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+ <reg name="todpreg" bitsize="64" type="uint64" group="system"/>
+ <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+ </feature>
+</target>
diff --git a/gdb/features/s390x-vxcr-linux64.c b/gdb/features/s390x-vxcr-linux64.c
new file mode 100644
index 0000000000..0ae2f93629
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.c
@@ -0,0 +1,170 @@
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: s390x-vxcr-linux64.xml */
+
+#include "defs.h"
+#include "osabi.h"
+#include "target-descriptions.h"
+
+struct target_desc *tdesc_s390x_vxcr_linux64;
+static void
+initialize_tdesc_s390x_vxcr_linux64 (void)
+{
+ struct target_desc *result = allocate_target_description ();
+ set_tdesc_architecture (result, bfd_scan_arch ("s390:64-bit"));
+
+ struct tdesc_feature *feature;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.core");
+ tdesc_create_reg (feature, "pswm", 0, 1, "psw", 64, "uint64");
+ tdesc_create_reg (feature, "pswa", 1, 1, "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");
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.vx");
+ tdesc_type *element_type;
+ element_type = tdesc_named_type (feature, "ieee_single");
+ tdesc_create_vector (feature, "v4f", element_type, 4);
+
+ element_type = tdesc_named_type (feature, "ieee_double");
+ tdesc_create_vector (feature, "v2d", element_type, 2);
+
+ element_type = tdesc_named_type (feature, "int8");
+ tdesc_create_vector (feature, "v16i8", element_type, 16);
+
+ element_type = tdesc_named_type (feature, "int16");
+ tdesc_create_vector (feature, "v8i16", element_type, 8);
+
+ element_type = tdesc_named_type (feature, "int32");
+ tdesc_create_vector (feature, "v4i32", element_type, 4);
+
+ element_type = tdesc_named_type (feature, "int64");
+ tdesc_create_vector (feature, "v2i64", element_type, 2);
+
+ tdesc_type_with_fields *type_with_fields;
+ type_with_fields = tdesc_create_union (feature, "vec128");
+ tdesc_type *field_type;
+ field_type = tdesc_named_type (feature, "v4f");
+ tdesc_add_field (type_with_fields, "v4_float", field_type);
+ field_type = tdesc_named_type (feature, "v2d");
+ tdesc_add_field (type_with_fields, "v2_double", field_type);
+ field_type = tdesc_named_type (feature, "v16i8");
+ tdesc_add_field (type_with_fields, "v16_int8", field_type);
+ field_type = tdesc_named_type (feature, "v8i16");
+ tdesc_add_field (type_with_fields, "v8_int16", field_type);
+ field_type = tdesc_named_type (feature, "v4i32");
+ tdesc_add_field (type_with_fields, "v4_int32", field_type);
+ field_type = tdesc_named_type (feature, "v2i64");
+ tdesc_add_field (type_with_fields, "v2_int64", field_type);
+ field_type = tdesc_named_type (feature, "uint128");
+ tdesc_add_field (type_with_fields, "uint128", field_type);
+
+ tdesc_create_reg (feature, "v0l", 51, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v1l", 52, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v2l", 53, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v3l", 54, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v4l", 55, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v5l", 56, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v6l", 57, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v7l", 58, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v8l", 59, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v9l", 60, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v10l", 61, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v11l", 62, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v12l", 63, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v13l", 64, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v14l", 65, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v15l", 66, 1, NULL, 64, "uint64");
+ tdesc_create_reg (feature, "v16", 67, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v17", 68, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v18", 69, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v19", 70, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v20", 71, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v21", 72, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v22", 73, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v23", 74, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v24", 75, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v25", 76, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v26", 77, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v27", 78, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v28", 79, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v29", 80, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v30", 81, 1, NULL, 128, "vec128");
+ tdesc_create_reg (feature, "v31", 82, 1, NULL, 128, "vec128");
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.cr");
+ tdesc_create_reg (feature, "cr0", 83, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr1", 84, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr2", 85, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr3", 86, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr4", 87, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr5", 88, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr6", 89, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr7", 90, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr8", 91, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr9", 92, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr10", 93, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr11", 94, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr12", 95, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr13", 96, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr14", 97, 1, "control", 64, "uint64");
+ tdesc_create_reg (feature, "cr15", 98, 1, "control", 64, "uint64");
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.s390.privileged");
+ tdesc_create_reg (feature, "timer", 99, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "todcmp", 100, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "todpreg", 101, 1, "system", 64, "uint64");
+ tdesc_create_reg (feature, "prefix", 102, 1, "system", 32, "uint32");
+
+ tdesc_s390x_vxcr_linux64 = result;
+}
diff --git a/gdb/features/s390x-vxcr-linux64.xml b/gdb/features/s390x-vxcr-linux64.xml
new file mode 100644
index 0000000000..703b1c360c
--- /dev/null
+++ b/gdb/features/s390x-vxcr-linux64.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2016 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 kernel-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"/>
+ <xi:include href="s390-vx.xml"/>
+ <xi:include href="s390-cr.xml"/>
+
+ <feature name="org.gnu.gdb.s390.privileged">
+ <reg name="timer" bitsize="64" type="uint64" group="system"/>
+ <reg name="todcmp" bitsize="64" type="uint64" group="system"/>
+ <reg name="todpreg" bitsize="64" type="uint64" group="system"/>
+ <reg name="prefix" bitsize="32" type="uint32" group="system"/>
+ </feature>
+</target>
diff --git a/gdb/regformats/s390x-cr-linux64.dat b/gdb/regformats/s390x-cr-linux64.dat
new file mode 100644
index 0000000000..091d55a816
--- /dev/null
+++ b/gdb/regformats/s390x-cr-linux64.dat
@@ -0,0 +1,76 @@
+# THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-cr-linux64.xml
+name:s390x_cr_linux64
+xmltarget:s390x-cr-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
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+64:todpreg
+32:prefix
diff --git a/gdb/regformats/s390x-vxcr-linux64.dat b/gdb/regformats/s390x-vxcr-linux64.dat
new file mode 100644
index 0000000000..4b3bf71adb
--- /dev/null
+++ b/gdb/regformats/s390x-vxcr-linux64.dat
@@ -0,0 +1,108 @@
+# THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi :set ro:
+# Generated from: s390x-vxcr-linux64.xml
+name:s390x_vxcr_linux64
+xmltarget:s390x-vxcr-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
+64:v0l
+64:v1l
+64:v2l
+64:v3l
+64:v4l
+64:v5l
+64:v6l
+64:v7l
+64:v8l
+64:v9l
+64:v10l
+64:v11l
+64:v12l
+64:v13l
+64:v14l
+64:v15l
+128:v16
+128:v17
+128:v18
+128:v19
+128:v20
+128:v21
+128:v22
+128:v23
+128:v24
+128:v25
+128:v26
+128:v27
+128:v28
+128:v29
+128:v30
+128:v31
+64:cr0
+64:cr1
+64:cr2
+64:cr3
+64:cr4
+64:cr5
+64:cr6
+64:cr7
+64:cr8
+64:cr9
+64:cr10
+64:cr11
+64:cr12
+64:cr13
+64:cr14
+64:cr15
+64:timer
+64:todcmp
+64:todpreg
+32:prefix
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 408bb875d2..07b02afe3d 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -1108,9 +1108,11 @@ static const short s390_dwarf_regmap[] =
S390_F8_REGNUM, S390_F10_REGNUM, S390_F12_REGNUM, S390_F14_REGNUM,
S390_F9_REGNUM, S390_F11_REGNUM, S390_F13_REGNUM, S390_F15_REGNUM,
- /* 32-47: Control Registers (not mapped). */
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 32-47: Control Registers. */
+ S390_CR0_REGNUM, S390_CR1_REGNUM, S390_CR2_REGNUM, S390_CR3_REGNUM,
+ S390_CR4_REGNUM, S390_CR5_REGNUM, S390_CR6_REGNUM, S390_CR7_REGNUM,
+ S390_CR8_REGNUM, S390_CR9_REGNUM, S390_CR10_REGNUM, S390_CR11_REGNUM,
+ S390_CR12_REGNUM, S390_CR13_REGNUM, S390_CR14_REGNUM, S390_CR15_REGNUM,
/* 48-63: Access Registers. */
S390_A0_REGNUM, S390_A1_REGNUM, S390_A2_REGNUM, S390_A3_REGNUM,
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
index 14530cef31..a565f72d9c 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -296,9 +296,29 @@ enum
#define S390_BC_GSD_REGNUM 125
#define S390_BC_GSSM_REGNUM 126
#define S390_BC_GSEPLA_REGNUM 127
+/* Control registers. */
+#define S390_TIMER_REGNUM 128
+#define S390_TODCMP_REGNUM 129
+#define S390_TODPREG_REGNUM 130
+#define S390_CR0_REGNUM 131
+#define S390_CR1_REGNUM 132
+#define S390_CR2_REGNUM 133
+#define S390_CR3_REGNUM 134
+#define S390_CR4_REGNUM 135
+#define S390_CR5_REGNUM 136
+#define S390_CR6_REGNUM 137
+#define S390_CR7_REGNUM 138
+#define S390_CR8_REGNUM 139
+#define S390_CR9_REGNUM 140
+#define S390_CR10_REGNUM 141
+#define S390_CR11_REGNUM 142
+#define S390_CR12_REGNUM 143
+#define S390_CR13_REGNUM 144
+#define S390_CR14_REGNUM 145
+#define S390_CR15_REGNUM 146
+#define S390_PREFIX_REGNUM 147
/* Total. */
-#define S390_NUM_REGS 128
-
+#define S390_NUM_REGS 148
#define S390_NUM_GPRS 16
#define S390_NUM_FPRS 16
--
2.13.5
^ permalink raw reply [flat|nested] 18+ messages in thread
* [RFC PATCH v5 9/9] Add S390 support for linux-kernel target
2018-03-12 15:31 [RFC v5 0/9] Add support for Linux kernel debugging Philipp Rudo
` (7 preceding siblings ...)
2018-03-12 15:31 ` [RFC PATCH v5 3/9] Add scoped_restore_regcache_ptid Philipp Rudo
@ 2018-03-12 15:31 ` Philipp Rudo
8 siblings, 0 replies; 18+ messages in thread
From: Philipp Rudo @ 2018-03-12 15:31 UTC (permalink / raw)
To: gdb-patches; +Cc: Omair Javaid, Yao Qi, arnez
Add s390 architecture support for the new linux-kernel target. In
particular implement the required virtual methods, enable priviledged
registers and add a kernel stack unwinder.
gdb/ChangeLog:
* s390-lk-tdep.h: New file.
* s390-lk-tdep.c: New file.
* Makefile.in (ALL_TARGET_OBS): Add s390-lk-tdep.o.
(HFILES_NO_SRCDIR): Add s390-lk-tdep.h.
(ALLDEPFILES): Add s390-lk-tdep.c.
* configure.tgt (s390*-*-linux*): Add s390-lk-tdep.o.
* s390-tdep.h: Define macros for address translation.
(gdbarch_tdep) <have_privileged>: New field.
(s390_unwind_pseudo_register): New export.
* s390-tdep.c (s390_unwind_pseudo_register): Remove static.
(s390_tdesc_valid): Add control registers and check for
priviledeged feature in tdesc.
(s390_gdbarch_tdep_alloc): Initialize new field have_priviledged.
---
gdb/Makefile.in | 3 +
gdb/configure.tgt | 6 +-
gdb/s390-lk-tdep.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/s390-lk-tdep.h | 39 ++
gdb/s390-tdep.c | 36 +-
gdb/s390-tdep.h | 66 ++++
6 files changed, 1160 insertions(+), 5 deletions(-)
create mode 100644 gdb/s390-lk-tdep.c
create mode 100644 gdb/s390-lk-tdep.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 2029c933af..1970b7f734 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -760,6 +760,7 @@ ALL_TARGET_OBS = \
rs6000-tdep.o \
rx-tdep.o \
s390-linux-tdep.o \
+ s390-lk-tdep.o \
s390-tdep.o \
score-tdep.o \
sh-linux-tdep.o \
@@ -1342,6 +1343,7 @@ HFILES_NO_SRCDIR = \
rs6000-aix-tdep.h \
rs6000-tdep.h \
s390-linux-tdep.h \
+ s390-lk-tdep.h \
s390-tdep.h \
score-tdep.h \
selftest-arch.h \
@@ -2321,6 +2323,7 @@ ALLDEPFILES = \
rx-tdep.c \
s390-linux-nat.c \
s390-linux-tdep.c \
+ s390-lk-tdep.c \
s390-tdep.c \
score-tdep.c \
ser-go32.c \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 680b3b2e42..a9116b3b7b 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -519,9 +519,9 @@ powerpc*-*-*)
s390*-*-linux*)
# Target: S390 running Linux
- gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \
- linux-tdep.o linux-record.o symfile-mem.o \
- ${lk_tobjs}"
+ gdb_target_obs="s390-linux-tdep.o s390-tdep.o s390-lk-tdep.o \
+ solib-svr4.o linux-tdep.o linux-record.o \
+ symfile-mem.o ${lk_tobjs}"
build_gdbserver=yes
;;
diff --git a/gdb/s390-lk-tdep.c b/gdb/s390-lk-tdep.c
new file mode 100644
index 0000000000..d1582f2474
--- /dev/null
+++ b/gdb/s390-lk-tdep.c
@@ -0,0 +1,1015 @@
+/* Target-dependent code for linux-kernel target on S390.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+
+#include "auxv.h"
+#include "dwarf2-frame.h"
+#include "elf/common.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
+#include "gdbarch.h"
+#include "gdbcore.h"
+#include "gdbthread.h"
+#include "lk-low.h"
+#include "lk-modules.h"
+#include "osabi.h"
+#include "regcache.h"
+#include "regset.h"
+#include "s390-tdep.h"
+#include "s390-lk-tdep.h"
+
+#include "features/s390x-cr-linux64.c"
+#include "features/s390x-vxcr-linux64.c"
+
+
+/* Register maps and sets. */
+
+static const struct regcache_map_entry s390_gregmap[] =
+ {
+ { 1, S390_PSWM_REGNUM },
+ { 1, S390_PSWA_REGNUM },
+ { 16, S390_R0_REGNUM },
+ { 16, S390_A0_REGNUM },
+ { 1, REGCACHE_MAP_SKIP, 8 }, /* ORIG_R2_REG, exists but unused. */
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_gregmap_lk[] =
+ {
+ { 10, S390_R6_REGNUM }, /* r0-r5 volatile */
+ { -2, REGCACHE_MAP_SKIP, 8 },
+ { 1, S390_PSWA_REGNUM }, /* Use r14 for PSWA. */
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390_fpregmap[] =
+ {
+ { 1, S390_FPC_REGNUM, 8 },
+ { 16, S390_F0_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390_regmap_vxrs_low[] =
+ {
+ { 16, S390_V0_LOWER_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390_regmap_vxrs_high[] =
+ {
+ { 16, S390_V16_REGNUM, 16 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_regmap_cr [] =
+ {
+ { 16, S390_CR0_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_regmap_timer [] =
+ {
+ { 1, S390_TIMER_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_regmap_todcmp [] =
+ {
+ { 1, S390_TODCMP_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_regmap_todpreg [] =
+ {
+ { 1, S390_TODPREG_REGNUM, 8 },
+ { 0 }
+ };
+
+static const struct regcache_map_entry s390x_regmap_prefix [] =
+ {
+ { 1, S390_PREFIX_REGNUM, 4 },
+ { 0 }
+ };
+
+/* Gregset stored in dump section. Used for running tasks. */
+
+#define s390x_sizeof_gregset 0xd8
+static const struct regset s390_gregset = {
+ s390_gregmap,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+/* Gregset set stored in stack_frame. Used for sleeping tasks. */
+
+static const struct regset s390x_gregset_lk = {
+ s390x_gregmap_lk,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+#define s390_sizeof_fpregset 0x88
+static const struct regset s390_fpregset = {
+ s390_fpregmap,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390_vxrs_low_regset = {
+ s390_regmap_vxrs_low,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390_vxrs_high_regset = {
+ s390_regmap_vxrs_high,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390x_cr_regset = {
+ s390x_regmap_cr,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390x_timer_regset = {
+ s390x_regmap_timer,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390x_todcmp_regset = {
+ s390x_regmap_todcmp,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390x_todpreg_regset = {
+ s390x_regmap_todpreg,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+static const struct regset s390x_prefix_regset = {
+ s390x_regmap_prefix,
+ regcache_supply_regset,
+ regcache_collect_regset
+};
+
+/* S390 specific implementation for linux_kernel_ops. */
+
+class s390_linux_kernel_ops : public linux_kernel_ops
+{
+public:
+ s390_linux_kernel_ops (struct target_ops *target)
+ : linux_kernel_ops::linux_kernel_ops (target)
+ {}
+
+ /* See lk-low.h. Uses r14 as current pswa. */
+ void get_registers (CORE_ADDR task, struct target_ops *target,
+ struct regcache *regcache, int regnum) override;
+
+ /* See lk-low.h. */
+ bool is_kvaddr (CORE_ADDR addr) override
+ { return addr >= lk_address ("high_memory"); }
+
+ /* See lk-low.h. */
+ CORE_ADDR vtop (CORE_ADDR vaddr, CORE_ADDR pgd) override;
+
+ /* See lk-low.h. */
+ void adjust_module_layout (CORE_ADDR mod, lk_module *module) override;
+
+ /* See lk-low.h. */
+ CORE_ADDR percpu_offset (unsigned int cpu) override;
+
+ /* See lk-low.h. */
+ unsigned int beneath_thread_to_cpu (thread_info *ti) override;
+
+protected:
+ /* See lk-low.h. */
+ void arch_read_symbols () override;
+};
+
+/* Returns address of lowcore of given cpu. */
+
+static CORE_ADDR
+s390_lk_get_lowcore (unsigned int cpu)
+{
+ size_t ptr_len = lk_builtin_type_size (unsigned_long);
+ CORE_ADDR lowcore_ptr = lk_ops->address ("lowcore_ptr") + (ptr_len * cpu);
+ return lk_read_addr (lowcore_ptr);
+}
+
+/* Implement linux_kernel_ops->arch_read_symbols virtual method. */
+
+void
+s390_linux_kernel_ops::arch_read_symbols ()
+{
+ declare_field ("stack_frame->gprs", LK_CONFIG_ALWAYS);
+ declare_field ("stack_frame->back_chain", LK_CONFIG_ALWAYS);
+
+ declare_field ("pt_regs->psw", LK_CONFIG_ALWAYS);
+ declare_field ("pt_regs->gprs", LK_CONFIG_ALWAYS);
+
+ declare_field ("task_struct->stack", LK_CONFIG_ALWAYS);
+
+ declare_field ("thread_struct->ksp", LK_CONFIG_ALWAYS);
+
+ declare_type ("lowcore", {"lowcore", /* linux 4.5+ */
+ "_lowcore"}, /* linux -4.4 */
+ LK_CONFIG_ALWAYS);
+ declare_field ("lowcore->percpu_offset", LK_CONFIG_ALWAYS);
+ declare_field ("lowcore->cpu_nr", LK_CONFIG_ALWAYS);
+ declare_field ("lowcore->async_stack", LK_CONFIG_ALWAYS);
+ declare_field ("lowcore->panic_stack", LK_CONFIG_ALWAYS);
+ declare_field ("lowcore->restart_stack", LK_CONFIG_ALWAYS);
+
+ declare_field ("psw_t->mask", LK_CONFIG_ALWAYS);
+ declare_field ("psw_t->addr", LK_CONFIG_ALWAYS);
+
+ declare_field ("mod_arch_specific->plt_offset", LK_CONFIG_ALWAYS);
+ declare_field ("mod_arch_specific->plt_size", LK_CONFIG_ALWAYS);
+
+ declare_address ("lowcore_ptr", LK_CONFIG_ALWAYS);
+ declare_address ("high_memory", LK_CONFIG_ALWAYS);
+}
+
+/* Implement linux_kernel_ops->get_registers virtual method. */
+
+void
+s390_linux_kernel_ops::get_registers (CORE_ADDR task,
+ struct target_ops *target,
+ struct regcache *regcache,
+ int regnum)
+{
+ CORE_ADDR ksp, gprs;
+ gdb_byte buf[80]; /* 80 = 10 (#registers saved) * 8 (64 bit width) */
+ size_t size;
+
+ ksp = lk_read_addr (task + offset ("task_struct->thread->ksp"));
+ gprs = ksp + offset ("stack_frame->gprs");
+ size = FIELD_SIZE (field ("stack_frame->gprs"));
+ gdb_assert (size <= sizeof (buf));
+
+ read_memory (gprs, buf, size);
+ regcache->supply_regset (&s390x_gregset_lk, -1, buf, size);
+}
+
+/* Read table entry from TABLE at offset OFFSET. Helper for
+ s390_linux_kernel_ops::vtop. */
+
+static inline ULONGEST
+s390_lk_read_table_entry (CORE_ADDR table, ULONGEST offset)
+{
+ return lk_read_ulong (table + offset * lk_builtin_type_size (unsigned_long));
+}
+
+/* Implement linux_kernel_ops->vtop virtual method. */
+
+CORE_ADDR
+s390_linux_kernel_ops::vtop (CORE_ADDR vaddr, CORE_ADDR table)
+{
+ size_t addr_size = lk_builtin_type_size (unsigned_long);
+ unsigned int table_type;
+ ULONGEST entry, offset;
+
+ /* Read first entry in table to get its type. As the Table-Type bits are
+ the same in every table assume Region1-Table. */
+ entry = s390_lk_read_table_entry (table, 0);
+ table_type = (entry & S390_LK_RFTE_TT) >> 2;
+
+ switch (table_type)
+ {
+ case S390_LK_DAT_TT_REGION1:
+ offset = (vaddr & S390_LK_VADDR_RFX) >> 53;
+ entry = s390_lk_read_table_entry (table, offset);
+
+ /* Do sanity checks. */
+ if (!entry)
+ warning (_("Trying to translate address 0x%s with empty "\
+ "region-first-table entry."), phex (vaddr, addr_size));
+ else if ((entry & S390_LK_RFTE_TT) >> 2 != S390_LK_DAT_TT_REGION1)
+ warning (_("Trying to translate address 0x%s with corrupt "\
+ "table type in region-first-table entry."),
+ phex (vaddr, addr_size));
+ else if (entry & S390_LK_RFTE_I)
+ warning (_("Translating address 0x%s with invalid bit set at "\
+ "region-first-table entry."), phex (vaddr, addr_size));
+
+ table = entry & S390_LK_RFTE_O;
+
+ /* fall through */
+ case S390_LK_DAT_TT_REGION2:
+ offset = (vaddr & S390_LK_VADDR_RSX) >> 42;
+ entry = s390_lk_read_table_entry (table, offset);
+
+ /* Do sanity checks. */
+ if (!entry)
+ warning (_("Trying to translate address 0x%s with empty "\
+ "region-second-table entry."), phex (vaddr, addr_size));
+ else if ((entry & S390_LK_RSTE_TT) >> 2 != S390_LK_DAT_TT_REGION2)
+ warning (_("Trying to translate address 0x%s with corrupt "\
+ "table type in region-second-table entry."),
+ phex (vaddr, addr_size));
+ else if (entry & S390_LK_RSTE_I)
+ warning (_("Translating address 0x%s with invalid bit set at "\
+ "region-second-table entry."), phex (vaddr, addr_size));
+
+ table = entry & S390_LK_RSTE_O;
+
+ /* fall through */
+ case S390_LK_DAT_TT_REGION3:
+ offset = (vaddr & S390_LK_VADDR_RTX) >> 31;
+ entry = s390_lk_read_table_entry (table, offset);
+
+ /* Do sanity checks. */
+ if (!entry)
+ warning (_("Trying to translate address 0x%s with empty "\
+ "region-third-table entry."), phex (vaddr, addr_size));
+ else if ((entry & S390_LK_RTTE_TT) >> 2 != S390_LK_DAT_TT_REGION3)
+ warning (_("Trying to translate address 0x%s with corrupt "\
+ "table type in region-third-table entry."),
+ phex (vaddr, addr_size));
+ else if (entry & S390_LK_RTTE_I)
+ warning (_("Translating address 0x%s with invalid bit set at "\
+ "region-third-table entry."), phex (vaddr, addr_size));
+
+ /* Check for huge page. */
+ if (entry & S390_LK_RTTE_FC)
+ return (entry & S390_LK_RTTE_RFAA) + (vaddr & ~S390_LK_RTTE_RFAA);
+
+ table = entry & S390_LK_RTTE_O;
+
+ /* fall through */
+ case S390_LK_DAT_TT_SEGMENT:
+ offset = (vaddr & S390_LK_VADDR_SX) >> 20;
+ entry = s390_lk_read_table_entry (table, offset);
+
+ /* Do sanity checks. */
+ if (!entry)
+ warning (_("Trying to translate address 0x%s with empty "\
+ "segment-table entry."), phex (vaddr, addr_size));
+ else if ((entry & S390_LK_STE_TT) >> 2 != S390_LK_DAT_TT_SEGMENT)
+ warning (_("Trying to translate address 0x%s with corrupt "\
+ "table type in segment-table entry."),
+ phex (vaddr, addr_size));
+ else if (entry & S390_LK_STE_I)
+ warning (_("Translating address 0x%s with invalid bit set at "\
+ "segment-table entry."), phex (vaddr, addr_size));
+
+ /* Check for large page. */
+ if (entry & S390_LK_STE_FC)
+ return ((entry & S390_LK_STE_SFAA) + (vaddr & ~S390_LK_STE_SFAA));
+
+ table = entry & S390_LK_STE_O;
+ break;
+ } /* switch (table_type) */
+
+ offset = (vaddr & S390_LK_VADDR_PX) >> 12;
+ entry = s390_lk_read_table_entry (table, offset);
+
+ /* Do sanity checks. */
+ if (!entry)
+ warning (_("Trying to translate address 0x%s with empty page-table "\
+ "entry."), phex (vaddr, addr_size));
+ else if (entry & S390_LK_PTE_I)
+ warning (_("Translating address 0x%s with invalid bit set at "\
+ "page-table entry."), phex (vaddr, addr_size));
+
+ return (entry & S390_LK_PTE_PFAA) + (vaddr & ~S390_LK_PTE_PFAA);
+}
+
+/* Implement linux_kernel_ops->adjust_module_layout virtual method. */
+
+void
+s390_linux_kernel_ops::adjust_module_layout (CORE_ADDR mod,
+ lk_module *module)
+{
+ CORE_ADDR mod_arch = mod + offset ("module->arch");
+
+ module->core->text.base
+ += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_offset"));
+ module->core->text.base
+ += lk_read_ulong (mod_arch + offset ("mod_arch_specific->plt_size"));
+}
+
+/* Implement linux_kernel_ops->percpu_offset virtual method. */
+
+CORE_ADDR
+s390_linux_kernel_ops::percpu_offset (unsigned int cpu)
+{
+ CORE_ADDR lowcore = s390_lk_get_lowcore (cpu);
+ return lk_read_addr (lowcore + offset ("lowcore->percpu_offset"));
+}
+
+/* Implement linux_kernel_ops->beneath_thread_to_cpu virtual method. */
+
+unsigned int
+s390_linux_kernel_ops::beneath_thread_to_cpu (thread_info *ti)
+{
+ CORE_ADDR lowcore;
+
+ regcache *regcache = get_thread_regcache (ti->ptid);
+ register_status reg_status = regcache->raw_read (S390_PREFIX_REGNUM,
+ (ULONGEST *) &lowcore);
+ if (reg_status != REG_VALID)
+ error (_("Could not find prefix register for thread with pid %d, lwp %li."),
+ ti->ptid.pid (), ti->ptid.lwp ());
+
+ return lk_read_uint (lowcore + offset ("lowcore->cpu_nr"));
+}
+
+/* Linux kernel stack unwinder. */
+
+/* Helper functions and definitions. */
+
+struct s390_lk_unwind_cache
+{
+ /* The frame this frame_cache belongs to and its ... */
+ struct frame_info *frame;
+
+ /* ... frame base, ... */
+ CORE_ADDR frame_base;
+ /* ... stack pointer, ... */
+ CORE_ADDR sp;
+ /* ... process counter, ... */
+ CORE_ADDR pc;
+ /* ... PSW mask, ... */
+ CORE_ADDR pswm;
+ /* ... PSW address and ... */
+ CORE_ADDR pswa;
+ /* ... saved general purpose registers. */
+ CORE_ADDR gprs[S390_NUM_GPRS];
+
+ /* Mask to store which gprs where saved. */
+ unsigned long gprs_p:S390_NUM_GPRS;
+ /* Does this frame contain a zero backchain? */
+ bool zero_backchain;
+};
+
+/* S390s kernel stack is split up on several memory locations:
+
+ - kernel stack (per task)
+ - async aka. interrupt stack (per cpu)
+ - panic stack (per cpu)
+ - restart stack (unique, global)
+
+ Each memory location is page aligned and has a size of four consecutive
+ pages (exception is the panic stack with size of one page). */
+
+enum s390_lk_stack_location
+{
+ S390_LK_STACK_INVALID = -1,
+ S390_LK_STACK_USER,
+ S390_LK_STACK_KERNEL,
+ S390_LK_STACK_ASYNC,
+ S390_LK_STACK_PANIC,
+ S390_LK_STACK_RESTART
+};
+
+/* Get value of gpr REGNUM stored in CACHE. */
+
+static inline struct value *
+s390_lk_unwind_get_gpr (struct s390_lk_unwind_cache *cache, int regnum)
+{
+ gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+ unsigned int gpr = regnum - S390_R0_REGNUM;
+ if (!(cache->gprs_p & (1 << gpr)))
+ return frame_unwind_got_optimized (cache->frame, regnum);
+
+ /* When we hit a zero_backchain R15 is the (computed) lowest address of
+ pt_regs. */
+ if (regnum == S390_R15_REGNUM && cache->zero_backchain)
+ return frame_unwind_got_address (cache->frame, regnum, cache->gprs[gpr]);
+
+ return frame_unwind_got_memory (cache->frame, regnum, cache->gprs[gpr]);
+}
+
+/* Store ADDR of gpr REGNUM in CACHE. */
+
+static inline void
+s390_lk_unwind_set_gpr (struct s390_lk_unwind_cache *cache, int regnum,
+ CORE_ADDR addr)
+{
+ gdb_assert (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM);
+
+ unsigned int gpr = regnum - S390_R0_REGNUM;
+ cache->gprs[gpr] = addr;
+ cache->gprs_p |= (1 << gpr);
+}
+
+/* Allocate and initialize a new s390_lk_unwind_cache. */
+
+static struct s390_lk_unwind_cache *
+s390_lk_alloc_unwind_cache ()
+{
+ struct s390_lk_unwind_cache *cache;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct s390_lk_unwind_cache);
+
+ cache->frame = NULL;
+ cache->frame_base = -1;
+ cache->zero_backchain = false;
+
+ cache->sp = -1;
+ cache->pc = -1;
+ cache->pswm = -1;
+ cache->pswa = -1;
+ for (CORE_ADDR *gpr = cache->gprs; gpr < cache->gprs + S390_NUM_GPRS; gpr++)
+ *gpr = -1;
+ cache->gprs_p = 0;
+
+ return cache;
+}
+
+/* Helper macro for s390_lk_get_stack_location to check for stacks which
+ locations are stored in the lowcore.
+
+ _addr address to be checked for
+ _lc address of the corresponding lowcore
+ _stack field name of stack in lowcore
+ _type type to be returned if _addr is on _stack
+ _size size of _stack
+*/
+
+#define s390_lk_check_lowcore_stack(_addr, _lc, _stack, _type, _size) \
+ do \
+ { \
+ CORE_ADDR _##_stack, _##_stack##_top, _##_stack##_bottom; \
+ _##_stack = lk_read_addr ((_lc) + lk_offset ("lowcore->"#_stack));\
+ _##_stack##_top = LK_ALIGN (_##_stack, S390_LK_PAGESIZE); \
+ _##_stack##_bottom = _##_stack##_top - (_size); \
+ if ((_addr) <= _##_stack##_top && (_addr) >= _##_stack##_bottom) \
+ return (_type); \
+ } \
+ while (0)
+
+/* Find and return the stack location of ADDR belonging to TASK. */
+
+static enum s390_lk_stack_location
+s390_lk_get_stack_location (CORE_ADDR task, CORE_ADDR addr)
+{
+ CORE_ADDR lowcore, top, bottom;
+ unsigned int cpu = lk_task_running (task);
+
+ /* Kernel stack. */
+ bottom = lk_read_addr (task + lk_offset ("task_struct->stack"));
+ top = bottom + S390_LK_STACKSIZE;
+ if (addr <= top && addr >= bottom)
+ return S390_LK_STACK_KERNEL;
+
+ /* A sleeping task only has the kernel stack. If a sleeping task reaches
+ this point ADDR isn't on the stack. */
+ if (cpu == LK_CPU_INVAL)
+ return S390_LK_STACK_INVALID;
+
+ lowcore = s390_lk_get_lowcore (cpu);
+
+ /* Async aka. interrupt stack. */
+ s390_lk_check_lowcore_stack (addr, lowcore, async_stack,
+ S390_LK_STACK_ASYNC, S390_LK_ASYNCSIZE);
+
+ /* Panic stack.
+ Note: The panic stack only has a size of one page. */
+ s390_lk_check_lowcore_stack (addr, lowcore, panic_stack,
+ S390_LK_STACK_PANIC, S390_LK_PAGESIZE);
+
+ /* Restart stack. */
+ s390_lk_check_lowcore_stack (addr, lowcore, restart_stack,
+ S390_LK_STACK_RESTART, S390_LK_ASYNCSIZE);
+
+ return S390_LK_STACK_INVALID;
+}
+
+/* Unwind a single NORMAL_FRAME, i.e. a struct stack_frame. */
+
+static struct s390_lk_unwind_cache *
+s390_lk_frame_unwind_cache (struct frame_info *this_frame,
+ void **this_cache)
+{
+ CORE_ADDR backchain, backchain_addr, prev_gpr15;
+
+ if (*this_cache)
+ return (struct s390_lk_unwind_cache *) *this_cache;
+
+ s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache ();
+ *this_cache = cache;
+
+ cache->frame = this_frame;
+ /* For NORMAL_FRAMES, cache->sp == lowest address of struct stack_frame. */
+ cache->sp = get_frame_sp (this_frame);
+ cache->pc = get_frame_pc (this_frame);
+ /* Choose the frame's base to be gpr15 + sizeof (stack_frame) to be
+ complient with DWARF's DW_CFA_def_cfa. Otherwise gdbarch_inner_than
+ can trigger false stack corruption asserts, as
+
+ next_dwarf_frame_base = next_backchain_frame_base + sizeof (stack_frame)
+ > this_backchain_frame_base
+ */
+ cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("stack_frame"));
+
+ backchain_addr = cache->sp + lk_offset ("stack_frame->back_chain");
+ backchain = lk_read_addr (backchain_addr);
+
+ /* A zero backchain marks the end of this stack. This can lead to two
+ actions (handled by SIGTRAMP_FRAME unwinder):
+ * stop unwinding (if sp at the end of the kernel stack)
+ * jump to next stack location (otherwise).
+ */
+ if (backchain == 0)
+ {
+ CORE_ADDR pt_regs = cache->sp + TYPE_LENGTH (lk_type ("stack_frame"));
+
+ {
+ /* A bug in the kernel (linux 3.10+) adds the stack overhead twice
+ for swapper tasks (!= swapper/0) with the information written in
+ the first (higher address) pt_regs. Compensate this by updating
+ the stack pointer. */
+ enum s390_lk_stack_location stack;
+
+ CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+ long pid = frame_get_thread (this_frame)->ptid.lwp ();
+ stack = s390_lk_get_stack_location (task, cache->sp);
+ if (pid == 0 && task != lk_address ("init_task")
+ && stack == S390_LK_STACK_KERNEL)
+ pt_regs += (TYPE_LENGTH (lk_type ("stack_frame"))
+ + TYPE_LENGTH (lk_type ("pt_regs")));
+ }
+
+ cache->zero_backchain = true;
+ s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, pt_regs);
+
+ /* Use pc = 0 to mark end of stack. */
+ s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM, backchain_addr);
+ return cache;
+ }
+
+ /* Do sanity check. */
+ prev_gpr15 = lk_read_addr (backchain
+ + s390_lk_stack_frame_gpr (S390_R15_REGNUM));
+ if (backchain != prev_gpr15)
+ {
+ size_t ptr_len = lk_builtin_type_size (unsigned_long);
+ CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+ error (_("Stack corruption for task 0x%s at stack frame 0x%s with "
+ "back_chain 0x%s detected %s. back_chain->gpr15 != back_chain."),
+ phex (task, ptr_len), phex (cache->sp, ptr_len),
+ phex (backchain, ptr_len), phex (prev_gpr15, ptr_len));
+ }
+
+ s390_lk_unwind_set_gpr (cache, S390_R15_REGNUM, backchain_addr);
+ s390_lk_unwind_set_gpr (cache, S390_R14_REGNUM,
+ backchain + s390_lk_stack_frame_gpr (S390_R14_REGNUM));
+
+ return cache;
+}
+
+/* Implement frame_unwind->this_id for NORMAL_FRAMEs. */
+
+static void
+s390_lk_frame_this_id (struct frame_info *this_frame,
+ void **this_cache,
+ struct frame_id *this_id)
+{
+ s390_lk_unwind_cache *cache
+ = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+ /* If we don't have a stack frame... */
+ if (cache->sp == -1)
+ {
+ /* ...but a PC. We have a inline frame. */
+ if (cache->pc != -1)
+ *this_id = frame_id_build_unavailable_stack (cache->pc);
+ return;
+ }
+ *this_id = frame_id_build (cache->frame_base, cache->pc);
+}
+
+/* Implement frame_unwind->prev_register for NORMAL and SIGTRAMP_FRAMEs. */
+
+static struct value *
+s390_lk_frame_prev_register (struct frame_info *this_frame,
+ void **this_cache, int regnum)
+{
+ s390_lk_unwind_cache *cache
+ = s390_lk_frame_unwind_cache (this_frame, this_cache);
+
+ /* Also handles S390_SP_REGNUM and S390_RETADDR_REGNUM as they are mapped to
+ R15 and R14 respectfully in s390-tdep.h. */
+ if (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
+ return s390_lk_unwind_get_gpr (cache, regnum);
+
+ /* Use PSWM stored in pt_regs if available. */
+ if (regnum == S390_PSWM_REGNUM)
+ return (cache->pswm != -1
+ ? frame_unwind_got_memory (cache->frame, regnum, cache->pswm)
+ : frame_unwind_got_optimized (cache->frame, regnum));
+
+ /* Use PSWA stored in pt_regs if available, R14/PC otherwise. */
+ if (regnum == S390_PSWA_REGNUM)
+ return (cache->pswa != -1
+ ? frame_unwind_got_memory (cache->frame, regnum, cache->pswa)
+ : s390_lk_unwind_get_gpr (cache, S390_RETADDR_REGNUM));
+
+ if (regnum >= S390_NUM_REGS)
+ return s390_unwind_pseudo_register (cache->frame, regnum);
+
+ /* Default is not saved. */
+ return frame_unwind_got_optimized (cache->frame, regnum);
+}
+
+/* Implement frame_unwind->sniffer for NORMAL_FRAMEs. */
+
+static int
+s390_lk_frame_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_cache)
+{
+ if (!target_is_pushed (lk_target_ops))
+ return 0;
+
+ return 1;
+}
+
+/* Simple backchain unwinder for Linux kernel on S390. Its main purpose is
+ to handle stack frames created in Assembler code without debug
+ infromation. */
+
+static const struct frame_unwind s390_lk_frame_unwind = {
+ NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
+ s390_lk_frame_this_id,
+ s390_lk_frame_prev_register,
+ NULL,
+ s390_lk_frame_sniffer
+};
+
+/* Unwind a single SIGTRAMP_FRAME, i.e. a struct pt_regs. */
+
+static struct s390_lk_unwind_cache *
+s390_lk_sigtramp_unwind_cache (struct frame_info *this_frame,
+ void **this_cache)
+{
+ if (*this_cache)
+ return (s390_lk_unwind_cache *) *this_cache;
+
+ s390_lk_unwind_cache *cache = s390_lk_alloc_unwind_cache ();
+ *this_cache = cache;
+
+ cache->frame = this_frame;
+ /* For SIGTRAMP_FRAMES, cache->sp = lowest address of struct pt_regs. */
+ cache->sp = get_frame_sp (this_frame);
+ cache->pc = cache->sp + s390_lk_pt_regs_gpr (S390_R14_REGNUM);
+ /* Choose the frame base to be SP + sizeof (pt_regs) aka. the end of this
+ stack. */
+ cache->frame_base = cache->sp + TYPE_LENGTH (lk_type ("pt_regs"));
+
+ cache->pswm = cache->sp + lk_offset ("pt_regs->psw->mask");
+ cache->pswa = cache->sp + lk_offset ("pt_regs->psw->addr");
+
+ for (int i = S390_R0_REGNUM; i <= S390_R15_REGNUM; i++)
+ s390_lk_unwind_set_gpr (cache, i, cache->sp + s390_lk_pt_regs_gpr (i));
+
+ return cache;
+}
+
+/* Implement frame_unwind->this_id for SIGTRAMP_FRAMEs. */
+
+static void
+s390_lk_sigtramp_this_id (struct frame_info *this_frame,
+ void **this_cache,
+ struct frame_id *this_id)
+{
+ s390_lk_unwind_cache *cache
+ = s390_lk_sigtramp_unwind_cache (this_frame, this_cache);
+
+ CORE_ADDR task = frame_get_thread (this_frame)->ptid.tid ();
+
+ switch (s390_lk_get_stack_location (task, cache->sp))
+ {
+ /* If we are on an interrupt stack keep on unwinding. */
+ case S390_LK_STACK_ASYNC:
+ case S390_LK_STACK_PANIC:
+ case S390_LK_STACK_RESTART:
+ *this_id = frame_id_build (cache->frame_base, cache->pc);
+ return;
+
+ /* If we are on the kernel stack check whether we are at the end of
+ the stack. If so stop unwinding otherwise continue. */
+ case S390_LK_STACK_KERNEL:
+ {
+ CORE_ADDR top;
+
+ top = lk_read_addr (task + lk_offset ("task_struct->stack"));
+ top += S390_LK_STACKSIZE;
+
+ /* swapper/0 doesn't have a pt_regs at the top of its stack, so in
+ this case cache->sp pointing at the top of the last stack_frame
+ points at the end of the stack. */
+ if (task == lk_address ("init_task") && top <= cache->sp)
+ *this_id = outer_frame_id;
+
+ /* For all other tasks cache->frame_base pointing at the top of
+ pt_regs points at the end of stack. */
+ else if (top <= cache->frame_base)
+ *this_id = outer_frame_id;
+
+ /* The program check handler appends his stack to the kernel stack.
+ If that is the case simply keep on unwinding. */
+ else
+ *this_id = frame_id_build (cache->frame_base, cache->pc);
+
+ return;
+ }
+
+ /* Otherwise somewhere something went wrong. */
+ default:
+ {
+ size_t ptr_len = lk_builtin_type_size (unsigned_long);
+ error (_("Couldn't find find valid stack for task 0x%s with stack "
+ "pointer 0x%s. Stack corruption?"),
+ phex (task, ptr_len), phex (cache->sp, ptr_len));
+ }
+ }
+}
+
+/* Implement frame_unwind->sniffer for SIGTRAMP_FRAMEs. */
+
+static int
+s390_lk_sigtramp_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_cache)
+{
+ if (!target_is_pushed (lk_target_ops))
+ return 0;
+
+ return get_frame_pc (this_frame) == 0;
+}
+
+/* Sigtramp unwinder for Linux kernel on S390. It handles situations when
+ the end of a stack, i.e. a zero backchain, was detected. */
+
+static const struct frame_unwind s390_lk_sigtramp_unwind = {
+ SIGTRAMP_FRAME,
+ default_frame_unwind_stop_reason,
+ s390_lk_sigtramp_this_id,
+ s390_lk_frame_prev_register,
+ NULL,
+ s390_lk_sigtramp_sniffer
+};
+
+/* Frame base handling. */
+
+static CORE_ADDR
+s390_lk_frame_base_address (struct frame_info *this_frame, void **this_cache)
+{
+ struct s390_lk_unwind_cache *cache
+ = s390_lk_frame_unwind_cache (this_frame, this_cache);
+ return cache->frame_base;
+}
+
+static const struct frame_base s390_lk_frame_base = {
+ &s390_lk_frame_unwind,
+ s390_lk_frame_base_address,
+ s390_lk_frame_base_address,
+ s390_lk_frame_base_address
+};
+
+/* Implement gdbarch get_new_lk_ops hook. */
+
+static linux_kernel_ops *
+s390_get_new_lk_ops (struct gdbarch *gdbarch, struct target_ops *target)
+{
+ return new s390_linux_kernel_ops (target);
+}
+
+/* Implement gdbarch iterate_over_regset_sections hook. */
+
+static void
+s390_lk_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ cb (".reg", s390x_sizeof_gregset, &s390_gregset, NULL, cb_data);
+ cb (".reg2", s390_sizeof_fpregset, &s390_fpregset, NULL, cb_data);
+ cb (".reg-s390-ctrs", 16 * 8, &s390x_cr_regset, "s390 control registers",
+ cb_data);
+
+ if (tdep->v0_full_regnum != -1)
+ {
+ cb (".reg-s390-vxrs-low", 16 * 8, &s390_vxrs_low_regset,
+ "s390 vector registers 0-15 lower half", cb_data);
+ cb (".reg-s390-vxrs-high", 16 * 16, &s390_vxrs_high_regset,
+ "s390 vector registers 16-31", cb_data);
+ }
+
+ /* Privileged registers for kernel debugging. */
+ if (bfd_get_section_by_name (core_bfd, ".reg-s390-timer"))
+ cb (".reg-s390-timer", 8, &s390x_timer_regset, "s390 timer", cb_data);
+ if (bfd_get_section_by_name (core_bfd, ".reg-s390-todcmp"))
+ cb (".reg-s390-todcmp", 8, &s390x_todcmp_regset,
+ "s390 clock comperator", cb_data);
+ if (bfd_get_section_by_name (core_bfd, ".reg-s390-todpreg"))
+ cb (".reg-s390-todpreg", 8, &s390x_todpreg_regset,
+ "s390 TOD programable register", cb_data);
+ if (bfd_get_section_by_name (core_bfd, ".reg-s390-prefix"))
+ cb (".reg-s390-prefix", 4, &s390x_prefix_regset,
+ "s390 prefix area", cb_data);
+}
+
+/* Implement gdbarch core_read_description hook. */
+
+static const struct target_desc *
+s390_lk_core_read_description (struct gdbarch *gdbarch,
+ struct target_ops *target, bfd *abfd)
+{
+ CORE_ADDR hwcap = 0;
+
+ target_auxv_search (target, AT_HWCAP, &hwcap);
+
+ if (hwcap & HWCAP_S390_VX)
+ return tdesc_s390x_vxcr_linux64;
+ else
+ return tdesc_s390x_cr_linux64;
+}
+
+/* Initialize Linux kernel specific gdbarch hooks. */
+
+static void
+s390_lk_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ const struct target_desc *tdesc = info.target_desc;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ tdep->abi = ABI_LINUX_ZSERIES;
+ if (!tdesc_has_registers (tdesc))
+ tdesc = tdesc_s390x_cr_linux64;
+ tdep->tdesc = tdesc;
+
+ /* Register handling. */
+ set_gdbarch_core_read_description (gdbarch, s390_lk_core_read_description);
+ set_gdbarch_iterate_over_regset_sections (gdbarch,
+ s390_lk_iterate_over_regset_sections);
+ /* Frame handling. */
+ frame_unwind_append_unwinder (gdbarch, &s390_lk_sigtramp_unwind);
+ frame_unwind_append_unwinder (gdbarch, &s390_lk_frame_unwind);
+
+ /* Support linux kernel debugging. */
+ set_gdbarch_get_new_lk_ops (gdbarch, s390_get_new_lk_ops);
+}
+
+/* From the ELF-headers point of view Linux kernel- and user-space binaries
+ are the same. So we have to rely on some heuristics to distinguish them.
+
+ For the heuristic we check for:
+ * the prefix register section in dumps.
+ * the __ksymtab section in the vmlinux and modules .ko files.
+ */
+
+static enum gdb_osabi
+s390_lk_osabi_sniffer (bfd *abfd)
+{
+ if (bfd_get_section_by_name (abfd, ".reg-s390-prefix")
+ || bfd_get_section_by_name (abfd, "__ksymtab"))
+ return GDB_OSABI_LINUX_KERNEL;
+
+ return GDB_OSABI_UNKNOWN;
+}
+
+extern initialize_file_ftype _initialize_s390_lk_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_s390_lk_tdep (void)
+{
+ /* Hook us into the OSABI mechanism. */
+ gdbarch_register_osabi (bfd_arch_s390, bfd_mach_s390_64, GDB_OSABI_LINUX_KERNEL,
+ s390_lk_init_abi_64);
+ /* Add OSABI sniffer. */
+ gdbarch_register_osabi_sniffer (bfd_arch_s390, bfd_target_elf_flavour,
+ s390_lk_osabi_sniffer);
+
+ /* Initialize the Linux kernel target descriptions. */
+ initialize_tdesc_s390x_cr_linux64 ();
+ initialize_tdesc_s390x_vxcr_linux64 ();
+}
diff --git a/gdb/s390-lk-tdep.h b/gdb/s390-lk-tdep.h
new file mode 100644
index 0000000000..e972d50167
--- /dev/null
+++ b/gdb/s390-lk-tdep.h
@@ -0,0 +1,39 @@
+/* Target-dependent code for linux-kernel target on S390.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef S390_LK_TDEP_H
+#define S390_LK_TDEP_H
+
+/* Constants copied from kernel sources. */
+#define S390_LK_PAGESIZE (1UL << 12)
+#define S390_LK_STACKSIZE (S390_LK_PAGESIZE << 2)
+#define S390_LK_ASYNCSIZE (S390_LK_STACKSIZE)
+
+/* Short hand access to gprs stored in struct pt_regs. */
+#define s390_lk_pt_regs_gpr(regnum) \
+ (lk_offset ("pt_regs->gprs") \
+ + (FIELD_TARGET_SIZE (lk_field ("pt_regs->gprs")) \
+ * ((regnum) - S390_R0_REGNUM))) \
+
+/* Short hand access to gprs stored in struct stack_frame. */
+#define s390_lk_stack_frame_gpr(regnum) \
+ (lk_offset ("stack_frame->gprs") \
+ + (FIELD_TARGET_SIZE (lk_field ("stack_frame->gprs")) \
+ * ((regnum) - S390_R6_REGNUM))) \
+
+#endif /* S390_LK_TDEP_H */
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 07b02afe3d..fc27a26702 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -2154,7 +2154,7 @@ s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
/* Helper routine to unwind pseudo registers. */
-static struct value *
+struct value *
s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
@@ -2280,7 +2280,6 @@ s390_trad_frame_prev_register (struct frame_info *this_frame,
else
return s390_unwind_pseudo_register (this_frame, regnum);
}
-
/* Normal stack frames. */
struct s390_unwind_cache {
@@ -6790,6 +6789,10 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep,
static const char *const gs_bc[] = {
"bc_gsd", "bc_gssm", "bc_gsepla",
};
+ static const char *const cr[] = {
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+ "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15"
+ };
const struct target_desc *tdesc = tdep->tdesc;
const struct tdesc_feature *feature;
@@ -6891,6 +6894,34 @@ s390_tdesc_valid (struct gdbarch_tdep *tdep,
gs_bc);
}
+ /* Control registers. */
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.cr");
+ if (feature)
+ s390_validate_reg_range (feature, tdesc_data, S390_CR0_REGNUM, cr);
+
+ /* Privileged registers. */
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.privileged");
+ if (feature)
+ {
+ if (!tdesc_numbered_register (feature, tdesc_data,
+ S390_TIMER_REGNUM, "timer"))
+ return false;
+
+ if (!tdesc_numbered_register (feature, tdesc_data,
+ S390_TODCMP_REGNUM, "todcmp"))
+ return false;
+
+ if (!tdesc_numbered_register (feature, tdesc_data,
+ S390_TODPREG_REGNUM, "todpreg"))
+ return false;
+
+ if (!tdesc_numbered_register (feature, tdesc_data,
+ S390_PREFIX_REGNUM, "prefix"))
+ return false;
+
+ tdep->have_privileged = true;
+ }
+
return true;
}
@@ -6918,6 +6949,7 @@ s390_gdbarch_tdep_alloc ()
tdep->have_tdb = false;
tdep->have_vx = false;
tdep->have_gs = false;
+ tdep->have_privileged = false;
tdep->s390_syscall_record = NULL;
diff --git a/gdb/s390-tdep.h b/gdb/s390-tdep.h
index a565f72d9c..63f2481010 100644
--- a/gdb/s390-tdep.h
+++ b/gdb/s390-tdep.h
@@ -60,6 +60,7 @@ struct gdbarch_tdep
bool have_tdb;
bool have_vx;
bool have_gs;
+ bool have_privileged;
/* Hook to record OS specific systemcall. */
int (*s390_syscall_record) (struct regcache *regcache, LONGEST svc_number);
@@ -329,12 +330,77 @@ enum
#define S390_RETADDR_REGNUM S390_R14_REGNUM
#define S390_FRAME_REGNUM S390_R11_REGNUM
+/* Definitions for address translation. */
+/* DAT Table types. */
+#define S390_LK_DAT_TT_REGION1 3
+#define S390_LK_DAT_TT_REGION2 2
+#define S390_LK_DAT_TT_REGION3 1
+#define S390_LK_DAT_TT_SEGMENT 0
+
+/* Region-First-Table */
+#define S390_LK_RFTE_TL 0x3ULL /* Table-Length */
+#define S390_LK_RFTE_TT 0xcULL /* Table-Type */
+#define S390_LK_RFTE_I 0x20ULL /* Region-Invalid Bit */
+#define S390_LK_RFTE_TF 0xc0ULL /* Table Offset */
+#define S390_LK_RFTE_P 0x200ULL /* DAT-Protection Bit */
+#define S390_LK_RFTE_O ~0xfffULL /* Region-Second-Table Origin */
+
+/* Region-Second-Table flags. */
+#define S390_LK_RSTE_TL 0x3ULL /* Table-Length */
+#define S390_LK_RSTE_TT 0xcULL /* Table-Type */
+#define S390_LK_RSTE_I 0x20ULL /* Region-Invalid Bit */
+#define S390_LK_RSTE_TF 0xc0ULL /* Table Offset */
+#define S390_LK_RSTE_P 0x200ULL /* DAT-Protection Bit */
+#define S390_LK_RSTE_O ~0xfffULL /* Region-Third-Table Origin */
+
+/* Region-Third-Table flags. */
+#define S390_LK_RTTE_TL 0x3ULL /* Table-Length */
+#define S390_LK_RTTE_TT 0xcULL /* Table-Type */
+#define S390_LK_RTTE_CR 0x10ULL /* Common-Region Bit */
+#define S390_LK_RTTE_I 0x20ULL /* Region-Invalid Bit */
+#define S390_LK_RTTE_TF 0xc0ULL /* Table Offset */
+#define S390_LK_RTTE_P 0x200ULL /* DAT-Protection Bit */
+#define S390_LK_RTTE_FC 0x400ULL /* Format-Control Bit */
+#define S390_LK_RTTE_F 0x800ULL /* Fetch-Protection Bit */
+#define S390_LK_RTTE_ACC 0xf000ULL /* Access-Control Bits */
+#define S390_LK_RTTE_AV 0x10000ULL /* ACCF-Validity Control */
+#define S390_LK_RTTE_O ~0xfffULL /* Segment-Table Origin */
+#define S390_LK_RTTE_RFAA ~0x7fffffffULL /* Region-Frame Absolute Address */
+
+/* Segment-Table flags. */
+#define S390_LK_STE_TT 0xcULL /* Table-Type */
+#define S390_LK_STE_I 0x20ULL /* Segment-Invalid Bit */
+#define S390_LK_STE_TF 0xc0ULL /* Table Offset */
+#define S390_LK_STE_P 0x200ULL /* DAT-Protection Bit */
+#define S390_LK_STE_FC 0x400ULL /* Format-Control Bit */
+#define S390_LK_STE_F 0x800ULL /* Fetch-Protection Bit */
+#define S390_LK_STE_ACC 0xf000ULL /* Access-Control Bits */
+#define S390_LK_STE_AV 0x10000ULL /* ACCF-Validity Control */
+#define S390_LK_STE_O ~0x7ffULL /* Page-Table Origin */
+#define S390_LK_STE_SFAA ~0xfffffULL /* Segment-Frame Absolute Address */
+
+/* Page-Table flags. */
+#define S390_LK_PTE_P 0x200ULL /* DAT-Protection Bit */
+#define S390_LK_PTE_I 0x400ULL /* Page-Invalid Bit */
+#define S390_LK_PTE_PFAA ~0xfffULL /* Page-Frame Absolute Address */
+
+/* Virtual Address Fields. */
+#define S390_LK_VADDR_RFX 0xffe0000000000000ULL
+#define S390_LK_VADDR_RSX 0x001ffc0000000000ULL
+#define S390_LK_VADDR_RTX 0x000003ff80000000ULL
+#define S390_LK_VADDR_SX 0x000000007ff00000ULL
+#define S390_LK_VADDR_PX 0x00000000000ff000ULL
+#define S390_LK_VADDR_BX 0x0000000000000fffULL
+
/* Frame unwinding. */
extern struct value *s390_trad_frame_prev_register
(struct frame_info *this_frame, struct trad_frame_saved_reg saved_regs[],
int regnum);
+extern struct value *s390_unwind_pseudo_register (struct frame_info *this_frame,
+ int regnum);
+
extern struct target_desc *tdesc_s390_linux32;
extern struct target_desc *tdesc_s390x_linux64;
--
2.13.5
^ permalink raw reply [flat|nested] 18+ messages in thread