These changes work in conjunction with the signature verification support for Xen I published recently. Prior to this change, kexec supported the following three modes of operation: kexec_load: - unverified loading of kernel into Linux (original mode) - unverified loading of kernel into Xen kexec_file_load (the -s option to kexec): - verified loading of kernel into Linux With the verified loading of a kernel into Linux, the scope of kexec changed drastically as the kernel performs most of the work that kexec previously did; the kernel does so so as to reduce the risk of compromise. For example, the unverified loading of a kernel into Linux involves locating memory within the system to load the various pieces of data (kernel, initramdisk, command line) as well as reserving additional memory such as the first 1MB on x86 for legacy reasons as well as something known as 'purgatory', a trampoline that checks the integrity of the contents of loaded pieces of data, before invoking that loaded kernel. The management of purgatory involves manipulating an embedded ELF purgatory object file to insert a memory hash value, and rewrite a few run-time switches based on kexec command line parameters. By contrast, the verified loading essentially just passes file handles for the kernel, initramdisk, and command line pointer, and the kernel takes care of the rest, by performing all the work that the unverified kexec load would do, but inside the kernel using trusted kernel code. This changeset adds a fourth mode to kexec: - verified loading of kernel into Xen In general, Xen performs the signature verification on the loaded kernel, much as Linux does, but that is where the similarities end. In the current Xen implementation, no infrastructure is present to support reading from [Linux dom0] file handles, or for manipulating ELF objects. As such, without Xen support for these actions, Xen relies upon kexec to provide these services, which is what this mode does. To achieve this, this mode of operation essentially vectors the verified load for Xen through the non-verified path, which performs all the needed actions for kexec to work, but then makes an adjustment to pass the entire kernel file, not just the loadable portion of the kernel file, to Xen in order to provide the proper image for signature verification. The loading of kexec images for signature verification for Xen is indicated with the -s switch, just like for Linux. Changes to configure.ac are for detecting whether or not the Xen version supports this kexec_file_load hypercall op. Changes to kexec-bzImage64.c are for recording what the change to the kernel image entry needs to be (the entire kernel file, not just the loadable portion), as well as vectoring kexec_file_load through kexec_load for Xen. Changes to kexec-xen.c are to invoke the new Xen kexec_file_load hypercall op, from kexec_load. Changes to kexec.c are to vector kexec_file_load for Xen throgh kexec_load for Xen, as well as make the correction for passing the complete kernel file to Xen. Signed-off-by: Eric DeVolder <eric.devolder@xxxxxxxxxx> --- configure.ac | 8 ++++++++ kexec/arch/x86_64/kexec-bzImage64.c | 18 ++++++++++++++++++ kexec/kexec-xen.c | 7 +++++++ kexec/kexec.c | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/configure.ac b/configure.ac index e05d601..a11787b 100644 --- a/configure.ac +++ b/configure.ac @@ -190,6 +190,14 @@ if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then AC_MSG_NOTICE([The kexec_status call is not available])) fi +dnl Check for the Xen kexec_status hypercall - reachable from --with-xen=yes|dl +if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then + AC_CHECK_LIB(xenctrl, xc_kexec_file_load, + AC_DEFINE(HAVE_XEN_KEXEC_FILE_LOAD, 1, + [The Xen kexec_file_load call is available]), + AC_MSG_NOTICE([The Xen kexec_file_load call is not available])) +fi + dnl ---Sanity checks if test "$CC" = "no"; then AC_MSG_ERROR([cc not found]); fi if test "$CPP" = "no"; then AC_MSG_ERROR([cpp not found]); fi diff --git a/kexec/arch/x86_64/kexec-bzImage64.c b/kexec/arch/x86_64/kexec-bzImage64.c index 8edb3e4..98e9d50 100644 --- a/kexec/arch/x86_64/kexec-bzImage64.c +++ b/kexec/arch/x86_64/kexec-bzImage64.c @@ -207,6 +207,20 @@ static int do_bzImage64_load(struct kexec_info *info, align = real_mode->kernel_alignment; addr = add_buffer(info, kernel + kern16_size, k_size, size, align, 0x100000, -1, -1); +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + if (xen_present() && info->file_mode) + { + /* Record info for post-purgatory hash computation replacement with kernel file */ + extern char *original_kernel; + extern off_t original_kernel_len; + extern char *replacement_kernel; + extern off_t replacement_kernel_len; + original_kernel = kernel + kern16_size; + original_kernel_len = k_size; + replacement_kernel = kernel; + replacement_kernel_len = kernel_len; + } +#endif if (addr == ULONG_MAX) die("can not load bzImage64"); dbgprintf("Loaded 64bit kernel at 0x%lx\n", addr); @@ -330,7 +344,11 @@ int bzImage64_load(int argc, char **argv, const char *buf, off_t len, int opt; int result; +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + if (info->file_mode && !xen_present()) +#else if (info->file_mode) +#endif return bzImage64_load_file(argc, argv, info); /* See options.h -- add any more there, too. */ diff --git a/kexec/kexec-xen.c b/kexec/kexec-xen.c index 1887390..2a59d62 100644 --- a/kexec/kexec-xen.c +++ b/kexec/kexec-xen.c @@ -124,8 +124,15 @@ int xen_kexec_load(struct kexec_info *info) arch = EM_386; #endif +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + if (info->file_mode) + ret = xc_kexec_file_load(xch, type, arch, (uint64_t)info->entry, + nr_segments, xen_segs); + else +#else ret = xc_kexec_load(xch, type, arch, (uint64_t)info->entry, nr_segments, xen_segs); +#endif out: xc_hypercall_buffer_array_destroy(xch, array); diff --git a/kexec/kexec.c b/kexec/kexec.c index 32ae56c..80a4905 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -61,6 +61,13 @@ static unsigned long kexec_flags = 0; /* Flags for kexec file (fd) based syscall */ static unsigned long kexec_file_flags = 0; int kexec_debug = 0; +#ifdef HAVE_XEN_KEXEC_FILE_LOAD +static int do_kexec_file_syscall = 0; +char *original_kernel = NULL; +off_t original_kernel_len = 0; +char *replacement_kernel = NULL; +off_t replacement_kernel_len = 0; +#endif void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr) { @@ -690,6 +697,24 @@ static void update_purgatory(struct kexec_info *info) sizeof(region)); elf_rel_set_symbol(&info->rhdr, "sha256_digest", &digest, sizeof(digest)); +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + /* Now that purgatory hash computed, replace kernel loadable segment with entire kernel file for Xen to process */ + if (xen_present() && info->file_mode) + { + if (original_kernel && original_kernel_len) + { + for(i = 0; i < info->nr_segments; i++) { + if ((info->segment[i].buf == original_kernel) && + (info->segment[i].bufsz == original_kernel_len)) + { + info->segment[i].buf = replacement_kernel; + info->segment[i].bufsz = replacement_kernel_len; + break; + } + } + } + } +#endif } /* @@ -710,6 +735,9 @@ static int my_load(const char *type, int fileind, int argc, char **argv, memset(&info, 0, sizeof(info)); info.kexec_flags = kexec_flags; info.skip_checks = skip_checks; +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + info.file_mode = do_kexec_file_syscall; +#endif result = 0; if (argc - fileind <= 0) { @@ -1260,7 +1288,9 @@ int main(int argc, char *argv[]) int do_ifdown = 0, skip_ifdown = 0; int do_unload = 0; int do_reuse_initrd = 0; +#ifndef HAVE_XEN_KEXEC_FILE_LOAD int do_kexec_file_syscall = 0; +#endif int do_kexec_fallback = 0; int skip_checks = 0; int do_status = 0; @@ -1489,7 +1519,11 @@ int main(int argc, char *argv[]) result = k_unload(kexec_flags); } if (do_load && (result == 0)) { +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + if (do_kexec_file_syscall && !xen_present()) { +#else if (do_kexec_file_syscall) { +#endif result = do_kexec_file_load(fileind, argc, argv, kexec_file_flags); if (do_kexec_fallback) switch (result) { @@ -1533,7 +1567,11 @@ int main(int argc, char *argv[]) break; } } +#ifdef HAVE_XEN_KEXEC_FILE_LOAD + if (!do_kexec_file_syscall || xen_present()) +#else if (!do_kexec_file_syscall) +#endif result = my_load(type, fileind, argc, argv, kexec_flags, skip_checks, entry); } -- 2.7.4 _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec