summaryrefslogtreecommitdiff
path: root/kexec
diff options
context:
space:
mode:
Diffstat (limited to 'kexec')
-rw-r--r--kexec/Makefile1
-rw-r--r--kexec/arch/i386/crashdump-x86.c20
-rw-r--r--kexec/crashdump-xen.c34
-rw-r--r--kexec/crashdump.h3
-rw-r--r--kexec/kexec-xen.c139
-rw-r--r--kexec/kexec.c24
-rw-r--r--kexec/kexec.h5
7 files changed, 218 insertions, 8 deletions
diff --git a/kexec/Makefile b/kexec/Makefile
index 8a6138d..dc9dab1 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -25,6 +25,7 @@ KEXEC_SRCS_base += kexec/phys_arch.c
KEXEC_SRCS_base += kexec/kernel_version.c
KEXEC_SRCS_base += kexec/lzma.c
KEXEC_SRCS_base += kexec/zlib.c
+KEXEC_SRCS_base += kexec/kexec-xen.c
KEXEC_GENERATED_SRCS += $(PURGATORY_HEX_C)
diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c
index 7aa5a12..85f3a87 100644
--- a/kexec/arch/i386/crashdump-x86.c
+++ b/kexec/arch/i386/crashdump-x86.c
@@ -990,8 +990,24 @@ static int crashkernel_mem_callback(void *UNUSED(data), int nr,
int is_crashkernel_mem_reserved(void)
{
- crash_reserved_mem_nr = kexec_iomem_for_each_line("Crash kernel\n",
- crashkernel_mem_callback, NULL);
+ int ret;
+
+ if (xen_present()) {
+ uint64_t start, end;
+
+ ret = xen_get_crashkernel_region(&start, &end);
+ if (ret < 0)
+ return 0;
+
+ crash_reserved_mem[0].start = start;
+ crash_reserved_mem[0].end = end;
+ crash_reserved_mem[0].type = RANGE_RAM;
+ crash_reserved_mem_nr = 1;
+ } else {
+ ret = kexec_iomem_for_each_line("Crash kernel\n",
+ crashkernel_mem_callback, NULL);
+ crash_reserved_mem_nr = ret;
+ }
return !!crash_reserved_mem_nr;
}
diff --git a/kexec/crashdump-xen.c b/kexec/crashdump-xen.c
index 79b68e0..60594f6 100644
--- a/kexec/crashdump-xen.c
+++ b/kexec/crashdump-xen.c
@@ -252,3 +252,37 @@ int xen_get_note(int cpu, uint64_t *addr, uint64_t *len)
return 0;
}
+
+#ifdef HAVE_LIBXENCTRL
+int xen_get_crashkernel_region(uint64_t *start, uint64_t *end)
+{
+ uint64_t size;
+ xc_interface *xc;
+ int rc = -1;
+
+ xc = xc_interface_open(NULL, NULL, 0);
+ if (!xc) {
+ fprintf(stderr, "failed to open xen control interface.\n");
+ goto out;
+ }
+
+ rc = xc_kexec_get_range(xc, KEXEC_RANGE_MA_CRASH, 0, &size, start);
+ if (rc < 0) {
+ fprintf(stderr, "failed to get crash region from hypervisor.\n");
+ goto out_close;
+ }
+
+ *end = *start + size - 1;
+
+out_close:
+ xc_interface_close(xc);
+
+out:
+ return rc;
+}
+#else
+int xen_get_crashkernel_region(uint64_t *start, uint64_t *end)
+{
+ return -1;
+}
+#endif
diff --git a/kexec/crashdump.h b/kexec/crashdump.h
index 0f7c2ea..95f1f0c 100644
--- a/kexec/crashdump.h
+++ b/kexec/crashdump.h
@@ -1,6 +1,7 @@
#ifndef CRASHDUMP_H
#define CRASHDUMP_H
+int get_crashkernel_region(uint64_t *start, uint64_t *end);
extern int get_crash_notes_per_cpu(int cpu, uint64_t *addr, uint64_t *len);
extern int get_kernel_vmcoreinfo(uint64_t *addr, uint64_t *len);
extern int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len);
@@ -56,9 +57,9 @@ unsigned long crash_architecture(struct crash_elf_info *elf_info);
unsigned long phys_to_virt(struct crash_elf_info *elf_info,
unsigned long paddr);
-int xen_present(void);
unsigned long xen_architecture(struct crash_elf_info *elf_info);
int xen_get_nr_phys_cpus(void);
int xen_get_note(int cpu, uint64_t *addr, uint64_t *len);
+int xen_get_crashkernel_region(uint64_t *start, uint64_t *end);
#endif /* CRASHDUMP_H */
diff --git a/kexec/kexec-xen.c b/kexec/kexec-xen.c
new file mode 100644
index 0000000..77f65c0
--- /dev/null
+++ b/kexec/kexec-xen.c
@@ -0,0 +1,139 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <elf.h>
+#include "kexec.h"
+#include "kexec-syscall.h"
+#include "crashdump.h"
+
+#include "config.h"
+
+#ifdef HAVE_LIBXENCTRL
+#include <xenctrl.h>
+
+#include "crashdump.h"
+
+int xen_kexec_load(struct kexec_info *info)
+{
+ uint32_t nr_segments = info->nr_segments;
+ struct kexec_segment *segments = info->segment;
+ xc_interface *xch;
+ xc_hypercall_buffer_array_t *array = NULL;
+ uint8_t type;
+ uint8_t arch;
+ xen_kexec_segment_t *xen_segs;
+ int s;
+ int ret = -1;
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return -1;
+
+ xen_segs = calloc(nr_segments + 1, sizeof(*xen_segs));
+ if (!xen_segs)
+ goto out;
+
+ array = xc_hypercall_buffer_array_create(xch, nr_segments);
+ if (array == NULL)
+ goto out;
+
+ for (s = 0; s < nr_segments; s++) {
+ DECLARE_HYPERCALL_BUFFER(void, seg_buf);
+
+ seg_buf = xc_hypercall_buffer_array_alloc(xch, array, s,
+ seg_buf, segments[s].bufsz);
+ if (seg_buf == NULL)
+ goto out;
+ memcpy(seg_buf, segments[s].buf, segments[s].bufsz);
+
+ set_xen_guest_handle(xen_segs[s].buf.h, seg_buf);
+ xen_segs[s].buf_size = segments[s].bufsz;
+ xen_segs[s].dest_maddr = (uint64_t)segments[s].mem;
+ xen_segs[s].dest_size = segments[s].memsz;
+ }
+
+ /*
+ * Ensure 0 - 1 MiB is mapped and accessible by the image.
+ *
+ * This allows access to the VGA memory and the region
+ * purgatory copies in the crash case.
+ */
+ set_xen_guest_handle(xen_segs[s].buf.h, HYPERCALL_BUFFER_NULL);
+ xen_segs[s].buf_size = 0;
+ xen_segs[s].dest_maddr = 0;
+ xen_segs[s].dest_size = 1 * 1024 * 1024;
+ nr_segments++;
+
+ type = (info->kexec_flags & KEXEC_ON_CRASH) ? KEXEC_TYPE_CRASH
+ : KEXEC_TYPE_DEFAULT;
+
+ arch = (info->kexec_flags & KEXEC_ARCH_MASK) >> 16;
+#if defined(_i386__) || defined(__x86_64__)
+ if (!arch)
+ arch = EM_386;
+#endif
+
+ ret = xc_kexec_load(xch, type, arch, (uint64_t)info->entry,
+ nr_segments, xen_segs);
+
+out:
+ xc_hypercall_buffer_array_destroy(xch, array);
+ free(xen_segs);
+ xc_interface_close(xch);
+
+ return ret;
+}
+
+int xen_kexec_unload(uint64_t kexec_flags)
+{
+ xc_interface *xch;
+ uint8_t type;
+ int ret;
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return -1;
+
+ type = (kexec_flags & KEXEC_ON_CRASH) ? KEXEC_TYPE_CRASH
+ : KEXEC_TYPE_DEFAULT;
+
+ ret = xc_kexec_unload(xch, type);
+
+ xc_interface_close(xch);
+
+ return ret;
+}
+
+void xen_kexec_exec(void)
+{
+ xc_interface *xch;
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return;
+
+ xc_kexec_exec(xch, KEXEC_TYPE_DEFAULT);
+
+ xc_interface_close(xch);
+}
+
+#else /* ! HAVE_LIBXENCTRL */
+
+int xen_kexec_load(uint64_t entry,
+ uint32_t nr_segments, struct kexec_segment *segments,
+ uint64_t kexec_flags)
+{
+ return -1;
+}
+
+int xen_kexec_unload(uin64_t kexec_flags);
+{
+ return -1;
+}
+
+void xen_kexec_exec(void)
+{
+}
+
+#endif
diff --git a/kexec/kexec.c b/kexec/kexec.c
index 2ce570f..9dc3fa4 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -764,8 +764,12 @@ static int my_load(const char *type, int fileind, int argc, char **argv,
if (kexec_debug)
print_segments(stderr, &info);
- result = kexec_load(
- info.entry, info.nr_segments, info.segment, info.kexec_flags);
+ if (xen_present())
+ result = xen_kexec_load(&info);
+ else
+ result = kexec_load(info.entry,
+ info.nr_segments, info.segment,
+ info.kexec_flags);
if (result != 0) {
/* The load failed, print some debugging information */
fprintf(stderr, "kexec_load failed: %s\n",
@@ -789,10 +793,13 @@ static int k_unload (unsigned long kexec_flags)
}
kexec_flags |= native_arch;
- result = kexec_load(NULL, 0, NULL, kexec_flags);
+ if (xen_present())
+ result = xen_kexec_unload(kexec_flags);
+ else
+ result = kexec_load(NULL, 0, NULL, kexec_flags);
if (result != 0) {
/* The unload failed, print some debugging information */
- fprintf(stderr, "kexec_load (0 segments) failed: %s\n",
+ fprintf(stderr, "kexec unload failed: %s\n",
strerror(errno));
}
return result;
@@ -823,7 +830,10 @@ static int my_shutdown(void)
*/
static int my_exec(void)
{
- reboot(LINUX_REBOOT_CMD_KEXEC);
+ if (xen_present())
+ xen_kexec_exec();
+ else
+ reboot(LINUX_REBOOT_CMD_KEXEC);
/* I have failed if I make it here */
fprintf(stderr, "kexec failed: %s\n",
strerror(errno));
@@ -928,6 +938,10 @@ static int kexec_loaded(void)
char *p;
char line[3];
+ /* No way to tell if an image is loaded under Xen, assume it is. */
+ if (xen_present())
+ return 1;
+
fp = fopen("/sys/kernel/kexec_loaded", "r");
if (fp == NULL)
return -1;
diff --git a/kexec/kexec.h b/kexec/kexec.h
index 2904e03..c8be4a5 100644
--- a/kexec/kexec.h
+++ b/kexec/kexec.h
@@ -289,4 +289,9 @@ const char * proc_iomem(void);
char *concat_cmdline(const char *base, const char *append);
+int xen_present(void);
+int xen_kexec_load(struct kexec_info *info);
+int xen_kexec_unload(uint64_t kexec_flags);
+void xen_kexec_exec(void);
+
#endif /* KEXEC_H */