From: Petr Tesarik <ptesarik@suse.cz>
Subject: Add '-X' to makedumpfile when dumping a Xen host
References: bnc#864910, bnc#829646
Patch-mainline: v0.8.10

When dumping a Xen virtualization host, kdump should dump only Dom0 and
hypervisor pages, i.e. it should exclude all DomU pages.

A kdumptool flag is introduced to override this default behaviour.

Signed-off-by: Petr Tesarik <ptesarik@suse.cz>

---
 doc/man/kdump.5.txt.in |    7 +-
 kdumptool/savedump.cc  |    9 ++
 kdumptool/util.cc      |  166 +++++++++++++++++++++++++++++++++++++++++++++++++
 kdumptool/util.h       |   20 +++++
 sysconfig.kdump.in     |    3 
 5 files changed, 202 insertions(+), 3 deletions(-)

--- a/doc/man/kdump.5.txt.in
+++ b/doc/man/kdump.5.txt.in
@@ -481,7 +481,7 @@ KDUMPTOOL_FLAGS
 ~~~~~~~~~~~~~~~
 
 This is a space-separated list of flags to tweak the run-time behaviour of
-*kdumptool*(8). Currently, only one flag is supported:
+*kdumptool*(8). These flags are recognized:
 
 *NOSPARSE*::
   Disable the creation of sparse-files. This flag is for debugging purposes,
@@ -495,6 +495,11 @@ This is a space-separated list of flags
   You can specify this flag to force the use of only one dumping process,
   regardless of the value of KDUMP_CPUS.
 
+*XENALLDOMAINS*::
+  When dumping a Xen virtualization host, *makedumpfile*(8) is normally
+  invoked with the _-X_ option to exclude DomU pages. This flag can be
+  used to include all pages in the dump.
+
 Default: ""
 
 KDUMP_NETCONFIG
--- a/kdumptool/savedump.cc
+++ b/kdumptool/savedump.cc
@@ -306,7 +306,12 @@ void SaveDump::saveDump(const RootDirURL
 	    cerr << "Splitting ELF dumps is not supported." << endl;
     }
 
-    if (useElf && dumplevel == 0) {
+    bool excludeDomU = false;
+    if (!config->kdumptoolContainsFlag("XENALLDOMAINS") &&
+	Util::isXenCoreDump(m_dump.c_str()))
+      excludeDomU = true;
+
+    if (useElf && dumplevel == 0 && !excludeDomU) {
         // use file source?
         provider = new FileDataProvider(m_dump.c_str());
         m_useMakedumpfile = false;
@@ -319,6 +324,8 @@ void SaveDump::saveDump(const RootDirURL
         cmdline << config->getStringValue(Configuration::MAKEDUMPFILE_OPTIONS) << " ";
         cmdline << "-d "
 		<< config->getIntValue(Configuration::KDUMP_DUMPLEVEL) << " ";
+	if (excludeDomU)
+	    cmdline << "-X ";
         if (useElf)
             cmdline << "-E ";
         if (useCompressed)
--- a/kdumptool/util.cc
+++ b/kdumptool/util.cc
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <sys/utsname.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 
 #include <libelf.h>
 #include <gelf.h>
@@ -162,6 +163,171 @@ bool Util::isElfFile(const string &file)
     } catch (...) {
         close(fd);
         throw;
+    }
+
+    return ret;
+}
+
+// -----------------------------------------------------------------------------
+static off_t FindElfNoteByName(int fd, off_t offset, size_t sz,
+                               const char *name)
+    throw (KError)
+{
+    char *buf, *p;
+    size_t to_read;
+    size_t nlen;
+    off_t ret = (off_t)-1;
+
+    Debug::debug()->trace("FindElfNoteByName(%d, %lu, %lu, %s)",
+                          fd, (unsigned long)offset, (unsigned long)sz, name);
+
+    if (lseek(fd, offset, SEEK_SET) == (off_t)-1)
+        throw KSystemError("Cannot seek to ELF notes", errno);
+
+    buf = new char[sz];
+    try {
+        to_read = sz;
+        p = buf;
+        while (to_read) {
+            ssize_t bytes_read = read(fd, p, to_read);
+            if (bytes_read < 0)
+                throw KSystemError("Cannot read ELF note", errno);
+            else if (!bytes_read)
+                throw KError("Unexpected EOF while reading ELF note");
+
+            to_read -= bytes_read;
+            p += bytes_read;
+        }
+
+        nlen = strlen(name);
+        to_read = sz;
+        p = buf;
+        while (to_read) {
+            Elf64_Nhdr *hdr = reinterpret_cast<Elf64_Nhdr*>(p);
+            if (to_read < sizeof(*hdr))
+                break;
+            to_read -= sizeof(*hdr);
+            p += sizeof(*hdr);
+
+            size_t notesz =
+                ((hdr->n_namesz + 3) & (-(Elf64_Word)4)) +
+                ((hdr->n_descsz + 3) & (-(Elf64_Word)4));
+            if (to_read < notesz)
+                break;
+
+            if ((hdr->n_namesz == nlen && !memcmp(p, name, nlen)) ||
+                (hdr->n_namesz == nlen+1 && !memcmp(p, name, nlen+1))) {
+                ret = offset + (p - buf) - sizeof(*hdr);
+                break;
+            }
+
+            to_read -= notesz;
+            p += notesz;
+        }
+    } catch (...) {
+        delete buf;
+        throw;
+    }
+
+    return ret;
+}
+
+// -----------------------------------------------------------------------------
+#define ELF_HEADER_MAPSIZE          (128*1024)
+
+bool Util::isXenCoreDump(int fd)
+    throw (KError)
+{
+    void *map = MAP_FAILED;
+    Elf *elf = (Elf*)0;
+    bool ret = false;
+
+    Debug::debug()->trace("isXenCoreDump(%d)", fd);
+
+    if (elf_version(EV_CURRENT) == EV_NONE)
+        throw KError("libelf is out of date.");
+
+    try {
+        map = mmap(NULL, ELF_HEADER_MAPSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+        if (map == MAP_FAILED) {
+            map = mmap(NULL, ELF_HEADER_MAPSIZE, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+            if (map == MAP_FAILED)
+                throw KSystemError("Cannot allocate ELF headers", errno);
+
+            size_t to_read = ELF_HEADER_MAPSIZE;
+            char *p = reinterpret_cast<char *>(map);
+            while (to_read) {
+                ssize_t bytes_read = read(fd, p, to_read);
+                if (bytes_read < 0)
+                    throw KSystemError("ELF read error", errno);
+                else if (!bytes_read)
+                    break;
+
+                to_read -= bytes_read;
+                p += bytes_read;
+            }
+        }
+        elf = elf_memory(reinterpret_cast<char *>(map), ELF_HEADER_MAPSIZE);
+        if (!elf)
+            throw KELFError("elf_memory() failed", elf_errno());
+
+        if (elf_kind(elf) != ELF_K_ELF)
+            return false;
+
+        switch (gelf_getclass(elf)) {
+        case ELFCLASS32:
+        case ELFCLASS64:
+            break;
+        default:
+            throw KError("Unrecognized ELF class");
+        }
+
+        size_t phnum, i;
+        if (elf_getphdrnum(elf, &phnum))
+            throw KELFError("Cannot count ELF program headers", elf_errno());
+
+        for (i = 0; i < phnum; ++i) {
+            GElf_Phdr phdr;
+
+            if (gelf_getphdr(elf, i, &phdr) != &phdr)
+                throw KELFError("getphdr() failed.", elf_errno());
+
+            if (phdr.p_type == PT_NOTE &&
+                FindElfNoteByName(fd, phdr.p_offset, phdr.p_filesz, "Xen")
+                != (off_t)-1) {
+                ret = true;
+                break;
+            }
+        }
+    } catch (...) {
+        if (elf)
+            elf_end(elf);
+        if (map != MAP_FAILED)
+            munmap(map, ELF_HEADER_MAPSIZE);
+        throw;
+    }
+
+    munmap(map, ELF_HEADER_MAPSIZE);
+    elf_end(elf);
+    return ret;
+}
+
+// -----------------------------------------------------------------------------
+bool Util::isXenCoreDump(const string &file)
+    throw (KError)
+{
+    int fd = open(file.c_str(), O_RDONLY);
+    if (fd < 0) {
+        throw KSystemError("Opening of " + file + " failed.", errno);
+    }
+
+    bool ret;
+    try {
+         ret = isXenCoreDump(fd);
+    } catch (...) {
+        close(fd);
+        throw;
     }
 
     return ret;
--- a/kdumptool/util.h
+++ b/kdumptool/util.h
@@ -94,6 +94,26 @@ class Util {
         throw (KError);
 
         /**
+         * Checks if @p filename is a Xen core dump.
+         *
+         * @param[in] filename the file to check
+         * @return @c true if it's a Xen core dump, @c false otherwise
+         * @exception KError if opening the file failed
+         */
+        static bool isXenCoreDump(const std::string &filename)
+        throw (KError);
+
+        /**
+         * Checks if @p filename is a Xen core dump.
+         *
+         * @param[in] fd a file descriptor
+         * @return @c true if it's a Xen core dump, @c false otherwise
+         * @exception KError if opening the file failed
+         */
+        static bool isXenCoreDump(int fd)
+        throw (KError);
+
+        /**
          * Frees a vector.
          */
         template <typename T>
--- a/sysconfig.kdump.in
+++ b/sysconfig.kdump.in
@@ -260,7 +260,7 @@ KDUMP_POSTSCRIPT=""
 #
 KDUMP_COPY_KERNEL="yes"
 
-## Type:        string(NOSPARSE,NOSPLIT)
+## Type:        string(NOSPARSE,NOSPLIT,XENALLDOMAINS)
 ## Default:     ""
 ## ServiceRestart:	kdump
 #
@@ -268,6 +268,7 @@ KDUMP_COPY_KERNEL="yes"
 #
 #   NOSPARSE disable creation of sparse files.
 #   NOSPLIT  do not pass "--split" to makedumpfile even if KDUMP_CPUS > 1
+#   XENALLDOMAINS do not filter out Xen DomU pages
 #
 # See also: kdump(5).
 #
