summaryrefslogtreecommitdiffstats
path: root/kexec/crashdump-xen.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/crashdump-xen.c')
-rw-r--r--kexec/crashdump-xen.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/kexec/crashdump-xen.c b/kexec/crashdump-xen.c
new file mode 100644
index 0000000..3f59a0d
--- /dev/null
+++ b/kexec/crashdump-xen.c
@@ -0,0 +1,256 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <elf.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include "kexec.h"
+#include "crashdump.h"
+#include "kexec-syscall.h"
+#include "config.h"
+#include "kexec-xen.h"
+
+struct crash_note_info {
+ unsigned long base;
+ unsigned long length;
+};
+
+static int xen_phys_cpus;
+static struct crash_note_info *xen_phys_notes;
+
+/* based on code from xen-detect.c */
+static int is_dom0;
+#if defined(__i386__) || defined(__x86_64__)
+static jmp_buf xen_sigill_jmp;
+void xen_sigill_handler(int sig)
+{
+ longjmp(xen_sigill_jmp, 1);
+}
+
+static void xen_cpuid(uint32_t idx, uint32_t *regs, int pv_context)
+{
+#ifdef __i386__
+ /* Use the stack to avoid reg constraint failures with some gcc flags */
+ asm volatile (
+ "push %%eax; push %%ebx; push %%ecx; push %%edx\n\t"
+ "test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
+ "mov %%eax,(%2); mov %%ebx,4(%2)\n\t"
+ "mov %%ecx,8(%2); mov %%edx,12(%2)\n\t"
+ "pop %%edx; pop %%ecx; pop %%ebx; pop %%eax\n\t"
+ : : "a" (idx), "c" (pv_context), "S" (regs) : "memory" );
+#else
+ asm volatile (
+ "test %5,%5 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t"
+ : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
+ : "0" (idx), "1" (pv_context), "2" (0) );
+#endif
+}
+
+static int check_for_xen(int pv_context)
+{
+ uint32_t regs[4];
+ char signature[13];
+ uint32_t base;
+
+ for (base = 0x40000000; base < 0x40010000; base += 0x100)
+ {
+ xen_cpuid(base, regs, pv_context);
+
+ *(uint32_t *)(signature + 0) = regs[1];
+ *(uint32_t *)(signature + 4) = regs[2];
+ *(uint32_t *)(signature + 8) = regs[3];
+ signature[12] = '\0';
+
+ if (strcmp("XenVMMXenVMM", signature) == 0 && regs[0] >= (base + 2))
+ goto found;
+ }
+
+ return 0;
+
+found:
+ xen_cpuid(base + 1, regs, pv_context);
+ return regs[0];
+}
+
+static int xen_detect_pv_guest(void)
+{
+ struct sigaction act, oldact;
+ int is_pv = -1;
+
+ if (setjmp(xen_sigill_jmp))
+ return is_pv;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = xen_sigill_handler;
+ sigemptyset (&act.sa_mask);
+ if (sigaction(SIGILL, &act, &oldact))
+ return is_pv;
+ if (check_for_xen(1))
+ is_pv = 1;
+ sigaction(SIGILL, &oldact, NULL);
+ return is_pv;
+}
+#else
+static int xen_detect_pv_guest(void)
+{
+ return 1;
+}
+#endif
+
+/*
+ * Return 1 if its a PV guest.
+ * This includes dom0, which is the only PV guest where kexec/kdump works.
+ * HVM guests have to be handled as native hardware.
+ */
+int xen_present(void)
+{
+ if (!is_dom0) {
+ if (access("/proc/xen", F_OK) == 0)
+ is_dom0 = xen_detect_pv_guest();
+ else
+ is_dom0 = -1;
+ }
+ return is_dom0 > 0;
+}
+
+unsigned long xen_architecture(struct crash_elf_info *elf_info)
+{
+ unsigned long machine = elf_info->machine;
+#ifdef HAVE_LIBXENCTRL
+ int rc;
+ xen_capabilities_info_t capabilities;
+ xc_interface *xc;
+
+ if (!xen_present())
+ goto out;
+
+ memset(capabilities, '0', XEN_CAPABILITIES_INFO_LEN);
+
+ xc = xc_interface_open(NULL, NULL, 0);
+ if ( !xc ) {
+ fprintf(stderr, "failed to open xen control interface.\n");
+ goto out;
+ }
+
+ rc = xc_version(xc, XENVER_capabilities, &capabilities[0]);
+ if ( rc == -1 ) {
+ fprintf(stderr, "failed to make Xen version hypercall.\n");
+ goto out_close;
+ }
+
+ if (strstr(capabilities, "xen-3.0-x86_64"))
+ machine = EM_X86_64;
+ else if (strstr(capabilities, "xen-3.0-x86_32"))
+ machine = EM_386;
+
+ out_close:
+ xc_interface_close(xc);
+
+ out:
+#endif
+ return machine;
+}
+
+#ifdef HAVE_LIBXENCTRL
+int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len)
+{
+ uint64_t end;
+ int ret = 0;
+
+ ret = xen_get_kexec_range(KEXEC_RANGE_MA_VMCOREINFO, addr, &end);
+ if (ret < 0)
+ return -1;
+
+ *len = end - *addr + 1;
+
+ return 0;
+}
+
+int xen_get_nr_phys_cpus(void)
+{
+ xc_interface *xc;
+ int max_cpus;
+ int cpu = -1;
+
+ if (xen_phys_cpus)
+ return xen_phys_cpus;
+
+ xc = xc_interface_open(NULL, NULL, 0);
+ if (!xc) {
+ fprintf(stderr, "failed to open xen control interface.\n");
+ return -1;
+ }
+
+ max_cpus = xc_get_max_cpus(xc);
+ if (max_cpus <= 0)
+ goto out;
+
+ xen_phys_notes = calloc(max_cpus, sizeof(*xen_phys_notes));
+ if (xen_phys_notes == NULL)
+ goto out;
+
+ for (cpu = 0; cpu < max_cpus; cpu++) {
+ uint64_t size, start;
+ int ret;
+
+ ret = xc_kexec_get_range(xc, KEXEC_RANGE_MA_CPU, cpu, &size, &start);
+ if (ret < 0)
+ break;
+
+ xen_phys_notes[cpu].base = start;
+ xen_phys_notes[cpu].length = size;
+ }
+
+ xen_phys_cpus = cpu;
+
+out:
+ xc_interface_close(xc);
+ return cpu;
+}
+#else
+int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len)
+{
+ return -1;
+}
+
+int xen_get_nr_phys_cpus(void)
+{
+ return -1;
+}
+#endif
+
+
+int xen_get_note(int cpu, uint64_t *addr, uint64_t *len)
+{
+ struct crash_note_info *note;
+
+ if (xen_phys_cpus <= 0)
+ return -1;
+
+ note = xen_phys_notes + cpu;
+
+ *addr = note->base;
+ *len = note->length;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBXENCTRL
+int xen_get_crashkernel_region(uint64_t *start, uint64_t *end)
+{
+ return xen_get_kexec_range(KEXEC_RANGE_MA_CRASH, start, end);
+}
+#else
+int xen_get_crashkernel_region(uint64_t *start, uint64_t *end)
+{
+ return -1;
+}
+#endif