summaryrefslogtreecommitdiff
path: root/kexec/arch
diff options
context:
space:
mode:
authorVarad Gautam <vrd@amazon.de>2020-04-01 18:57:16 +0200
committerSimon Horman <horms@verge.net.au>2020-04-07 13:53:32 +0200
commitb13984c6f9ec7fdd322e8d981defc2b846717bbc (patch)
treedd60edfd304ee13eb933ce07344dab222a742b88 /kexec/arch
parent41a9b98fff4d3a45ae5c6ffe6a10999fafd3e4e6 (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.c74
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;