diff options
Diffstat (limited to 'kexec/kexec-xen.c')
-rw-r--r-- | kexec/kexec-xen.c | 297 |
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 |