#define _GNU_SOURCE #include #include #include #include #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 /* 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