summaryrefslogtreecommitdiffstats
path: root/kexec/kexec-xen.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--kexec/kexec-xen.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/kexec/kexec-xen.c b/kexec/kexec-xen.c
new file mode 100644
index 0000000..a7c8933
--- /dev/null
+++ b/kexec/kexec-xen.c
@@ -0,0 +1,297 @@
+#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 "kexec-xen.h"
+
+#include "crashdump.h"
+
+#ifdef CONFIG_LIBXENCTRL_DL
+#include <dlfcn.h>
+
+/* The handle from dlopen(), needed by dlsym(), dlclose() */
+static void *xc_dlhandle;
+xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL);
+
+void *__xc_dlsym(const char *symbol)
+{
+ return dlsym(xc_dlhandle, symbol);
+}
+
+xc_interface *__xc_interface_open(xentoollog_logger *logger,
+ xentoollog_logger *dombuild_logger,
+ unsigned open_flags)
+{
+ xc_interface *xch = NULL;
+
+ if (!xc_dlhandle)
+ xc_dlhandle = dlopen("libxenctrl.so", RTLD_NOW | RTLD_NODELETE);
+
+ if (xc_dlhandle) {
+ typedef xc_interface *(*func_t)(xentoollog_logger *logger,
+ xentoollog_logger *dombuild_logger,
+ unsigned open_flags);
+
+ func_t func = (func_t)dlsym(xc_dlhandle, "xc_interface_open");
+ xch = func(logger, dombuild_logger, open_flags);
+ }
+
+ return xch;
+}
+
+int __xc_interface_close(xc_interface *xch)
+{
+ int rc = -1;
+
+ if (xc_dlhandle) {
+ typedef int (*func_t)(xc_interface *xch);
+
+ func_t func = (func_t)dlsym(xc_dlhandle, "xc_interface_close");
+ rc = func(xch);
+ dlclose(xc_dlhandle);
+ xc_dlhandle = NULL;
+ }
+
+ return rc;
+}
+#endif /* CONFIG_LIBXENCTRL_DL */
+
+int xen_get_kexec_range(int range, 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, range, 0, &size, start);
+ if (rc < 0) {
+ fprintf(stderr, "failed to get range=%d from hypervisor.\n", range);
+ goto out_close;
+ }
+
+ *end = *start + size - 1;
+
+out_close:
+ xc_interface_close(xc);
+
+out:
+ return rc;
+}
+
+static uint8_t xen_get_kexec_type(unsigned long kexec_flags)
+{
+ if (kexec_flags & KEXEC_ON_CRASH)
+ return KEXEC_TYPE_CRASH;
+
+ if (kexec_flags & KEXEC_LIVE_UPDATE)
+ return KEXEC_TYPE_LIVE_UPDATE;
+
+ return KEXEC_TYPE_DEFAULT;
+}
+
+#define IDENTMAP_1MiB (1024 * 1024)
+
+int xen_kexec_load(struct kexec_info *info)
+{
+ uint32_t nr_segments = info->nr_segments, nr_low_segments = 0;
+ struct kexec_segment *segments = info->segment;
+ uint64_t low_watermark = 0;
+ xc_interface *xch;
+ xc_hypercall_buffer_array_t *array = NULL;
+ uint8_t type;
+ uint8_t arch;
+ xen_kexec_segment_t *xen_segs, *seg;
+ int s;
+ int ret = -1;
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return -1;
+
+ /*
+ * 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.
+ *
+ * First, count the number of additional segments which will
+ * need to be added in between the ones in segments[].
+ *
+ * The segments are already sorted.
+ */
+ for (s = 0; s < nr_segments && (uint64_t)segments[s].mem <= IDENTMAP_1MiB; s++) {
+ if ((uint64_t)segments[s].mem > low_watermark)
+ nr_low_segments++;
+
+ low_watermark = (uint64_t)segments[s].mem + segments[s].memsz;
+ }
+ if (low_watermark < IDENTMAP_1MiB)
+ nr_low_segments++;
+
+ low_watermark = 0;
+
+ xen_segs = calloc(nr_segments + nr_low_segments, sizeof(*xen_segs));
+ if (!xen_segs)
+ goto out;
+
+ array = xc_hypercall_buffer_array_create(xch, nr_segments);
+ if (array == NULL)
+ goto out;
+
+ seg = xen_segs;
+ for (s = 0; s < nr_segments; s++) {
+ DECLARE_HYPERCALL_BUFFER(void, seg_buf);
+
+ if (low_watermark < IDENTMAP_1MiB && (uint64_t)segments[s].mem > low_watermark) {
+ set_xen_guest_handle(seg->buf.h, HYPERCALL_BUFFER_NULL);
+ seg->buf_size = 0;
+ seg->dest_maddr = low_watermark;
+ low_watermark = (uint64_t)segments[s].mem;
+ if (low_watermark > IDENTMAP_1MiB)
+ low_watermark = IDENTMAP_1MiB;
+ seg->dest_size = low_watermark - seg->dest_maddr;
+ seg++;
+ }
+
+ 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(seg->buf.h, seg_buf);
+ seg->buf_size = segments[s].bufsz;
+ seg->dest_maddr = (uint64_t)segments[s].mem;
+ seg->dest_size = segments[s].memsz;
+ seg++;
+
+ low_watermark = (uint64_t)segments[s].mem + segments[s].memsz;
+ }
+
+ if ((uint64_t)low_watermark < IDENTMAP_1MiB) {
+ set_xen_guest_handle(seg->buf.h, HYPERCALL_BUFFER_NULL);
+ seg->buf_size = 0;
+ seg->dest_maddr = low_watermark;
+ seg->dest_size = IDENTMAP_1MiB - low_watermark;
+ seg++;
+ }
+
+ type = xen_get_kexec_type(info->kexec_flags);
+
+ 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 + nr_low_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 = xen_get_kexec_type(kexec_flags);
+
+ ret = xc_kexec_unload(xch, type);
+
+ xc_interface_close(xch);
+
+ return ret;
+}
+
+int xen_kexec_status(uint64_t kexec_flags)
+{
+ xc_interface *xch;
+ uint8_t type;
+ int ret = -1;
+
+#ifdef HAVE_KEXEC_CMD_STATUS
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return -1;
+
+ type = xen_get_kexec_type(kexec_flags);
+
+ ret = xc_kexec_status(xch, type);
+
+ xc_interface_close(xch);
+#endif
+
+ return ret;
+}
+
+int xen_kexec_exec(uint64_t kexec_flags)
+{
+ xc_interface *xch;
+ uint8_t type = KEXEC_TYPE_DEFAULT;
+ int ret;
+
+ xch = xc_interface_open(NULL, NULL, 0);
+ if (!xch)
+ return -1;
+
+ if (kexec_flags & KEXEC_LIVE_UPDATE)
+ type = KEXEC_TYPE_LIVE_UPDATE;
+
+ ret = xc_kexec_exec(xch, type);
+
+ xc_interface_close(xch);
+
+ return ret;
+}
+
+#else /* ! HAVE_LIBXENCTRL */
+
+int xen_get_kexec_range(int range, uint64_t *start, uint64_t *end)
+{
+ return -1;
+}
+
+int xen_kexec_load(struct kexec_info *UNUSED(info))
+{
+ return -1;
+}
+
+int xen_kexec_unload(uint64_t kexec_flags)
+{
+ return -1;
+}
+
+int xen_kexec_status(uint64_t kexec_flags)
+{
+ return -1;
+}
+
+int xen_kexec_exec(uint64_t kexec_flags)
+{
+ return -1;
+}
+
+#endif