diff options
author | Varad Gautam <vrd@amazon.de> | 2020-04-01 18:57:16 +0200 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2020-04-07 13:53:32 +0200 |
commit | b13984c6f9ec7fdd322e8d981defc2b846717bbc (patch) | |
tree | dd60edfd304ee13eb933ce07344dab222a742b88 /kexec/arch | |
parent | 41a9b98fff4d3a45ae5c6ffe6a10999fafd3e4e6 (diff) |
kexec: Introduce --load-live-update for xen
Support loading a live update image for xen from kexec userspace.
For a multiboot2 Elf on a xen setup, this will:
- load the Elf into KEXEC_RANGE_MA_XEN
- load purgatory and modules into KEXEC_RANGE_MA_LIVEUPDATE
- append the Elf cmdline with " liveupdate=<size>@<addr>
v2: define xen related symbols outside of HAVE_LIBXENCTRL
Signed-off-by: Varad Gautam <vrd@amazon.de>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'kexec/arch')
-rw-r--r-- | kexec/arch/i386/kexec-mb2-x86.c | 74 |
1 files changed, 69 insertions, 5 deletions
diff --git a/kexec/arch/i386/kexec-mb2-x86.c b/kexec/arch/i386/kexec-mb2-x86.c index b839d59..1ad8aed 100644 --- a/kexec/arch/i386/kexec-mb2-x86.c +++ b/kexec/arch/i386/kexec-mb2-x86.c @@ -42,6 +42,8 @@ #include "../../kexec.h" #include "../../kexec-elf.h" #include "kexec-x86.h" +#include "../../kexec-syscall.h" +#include "../../kexec-xen.h" #include <arch/options.h> /* From GNU GRUB */ @@ -388,6 +390,15 @@ static uint64_t multiboot2_mbi_end(void *mbi_buf, uint64_t mbi_ptr) return mbi_ptr; } +static inline int multiboot2_rel_valid(struct multiboot_header_tag_relocatable *rel_tag, + uint64_t rel_start, uint64_t rel_end) +{ + if (rel_start >= rel_tag->min_addr && rel_end <= rel_tag->max_addr) + return 1; + + return 0; +} + int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { @@ -413,6 +424,7 @@ int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + uint64_t rel_min, rel_max; /* Probe for the MB header if it's not already found */ if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) @@ -459,19 +471,59 @@ int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, if (tmp_cmdline) { free(tmp_cmdline); } + + if (xen_present() && info->kexec_flags & KEXEC_LIVE_UPDATE ) { + if (!mhi.rel_tag) { + fprintf(stderr, "Multiboot2 image must be relocatable" + "for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + cmdline_add_liveupdate(&command_line); + } + command_line_len = strlen(command_line) + 1; /* Load the ELF executable */ - if (mhi.rel_tag) + if (mhi.rel_tag) { + rel_min = mhi.rel_tag->min_addr; + rel_max = mhi.rel_tag->max_addr; + + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + /* TODO also check if elf is xen */ + /* On a live update, load target xen over the current xen image. */ + uint64_t xen_start, xen_end; + + xen_get_kexec_range(KEXEC_RANGE_MA_XEN, &xen_start, &xen_end); + if (multiboot2_rel_valid(mhi.rel_tag, xen_start, xen_end)) { + rel_min = xen_start; + } else { + fprintf(stderr, "Cannot place Elf into " + "KEXEC_RANGE_MA_XEN for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + } + elf_exec_build_load_relocatable(info, &ehdr, buf, len, 0, - mhi.rel_tag->min_addr, mhi.rel_tag->max_addr, - mhi.rel_tag->align); - else + rel_min, rel_max, mhi.rel_tag->align); + } else elf_exec_build_load(info, &ehdr, buf, len, 0); + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + uint64_t lu_start, lu_end; + + xen_get_kexec_range(7 /* KEXEC_RANGE_MA_LIVEUPDATE */, &lu_start, &lu_end); + /* Fit everything else into lu_start-lu_end. First page after lu_start is + * reserved for LU breadcrumb. */ + rel_min = lu_start + 4096; + rel_max = lu_end; + } else { + rel_min = 0; + rel_max = ULONG_MAX; + } + /* Load the setup code */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, - 0, ULONG_MAX, 1, 0); + rel_min, rel_max, 1, 0); /* Construct information tags. */ mbi_bytes = multiboot2_get_mbi_size(info->memory_ranges, command_line_len, modules, mod_command_line_space); @@ -480,6 +532,18 @@ int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, mbi_ptr = multiboot2_make_mbi(info, command_line, command_line_len, info->rhdr.rel_addr, mbi_buf, mbi_bytes); free(command_line); + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + if (multiboot2_rel_valid(mhi.rel_tag, rel_min, rel_max)) { + /* Shrink the reloc range to fit into LU region for xen. */ + mhi.rel_tag->min_addr = rel_min; + mhi.rel_tag->max_addr = rel_max; + } else { + fprintf(stderr, "Multiboot2 image cannot be relocated into " + "KEXEC_RANGE_MA_LIVEUPDATE for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + } + /* Load modules */ if (modules) { char *mod_filename, *mod_command_line, *mod_clp, *buf; |