summaryrefslogtreecommitdiff
path: root/kexec
diff options
context:
space:
mode:
Diffstat (limited to 'kexec')
-rw-r--r--kexec/arch/ppc64/kexec-elf-ppc64.c84
-rw-r--r--kexec/kexec-elf-exec.c6
-rw-r--r--kexec/kexec-syscall.h3
-rw-r--r--kexec/kexec.820
-rw-r--r--kexec/kexec.c104
-rw-r--r--kexec/kexec.h6
6 files changed, 195 insertions, 28 deletions
diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c
index ddd3de8..3510b70 100644
--- a/kexec/arch/ppc64/kexec-elf-ppc64.c
+++ b/kexec/arch/ppc64/kexec-elf-ppc64.c
@@ -93,6 +93,85 @@ static int read_prop(char *name, void *value, size_t len)
return 0;
}
+static int elf_ppc64_load_file(int argc, char **argv, struct kexec_info *info)
+{
+ int ret = 0;
+ char *cmdline, *dtb;
+ int opt, cmdline_len = 0;
+
+ /* See options.h -- add any more there, too. */
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { "command-line", 1, NULL, OPT_APPEND },
+ { "append", 1, NULL, OPT_APPEND },
+ { "ramdisk", 1, NULL, OPT_RAMDISK },
+ { "initrd", 1, NULL, OPT_RAMDISK },
+ { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB },
+ { "dtb", 1, NULL, OPT_DEVICETREEBLOB },
+ { "args-linux", 0, NULL, OPT_ARGS_IGNORE },
+ { 0, 0, NULL, 0 },
+ };
+
+ static const char short_options[] = KEXEC_OPT_STR "";
+
+ /* Parse command line arguments */
+ cmdline = 0;
+ dtb = 0;
+ ramdisk = 0;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ options, 0)) != -1) {
+ switch (opt) {
+ default:
+ /* Ignore core options */
+ if (opt < OPT_ARCH_MAX)
+ break;
+ case OPT_APPEND:
+ cmdline = optarg;
+ break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
+ case OPT_DEVICETREEBLOB:
+ dtb = optarg;
+ break;
+ case OPT_ARGS_IGNORE:
+ break;
+ }
+ }
+
+ if (dtb)
+ die("--dtb not supported while using --kexec-file-syscall.\n");
+
+ if (reuse_initrd)
+ die("--reuseinitrd not supported with --kexec-file-syscall.\n");
+
+ if (cmdline) {
+ cmdline_len = strlen(cmdline) + 1;
+ } else {
+ cmdline = strdup("\0");
+ cmdline_len = 1;
+ }
+
+ if (ramdisk) {
+ info->initrd_fd = open(ramdisk, O_RDONLY);
+ if (info->initrd_fd == -1) {
+ fprintf(stderr, "Could not open initrd file %s:%s\n",
+ ramdisk, strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ }
+
+ info->command_line = cmdline;
+ info->command_line_len = cmdline_len;
+ return ret;
+out:
+ if (cmdline_len == 1)
+ free(cmdline);
+ return ret;
+}
+
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
@@ -132,6 +211,9 @@ int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
static const char short_options[] = KEXEC_OPT_STR "";
+ if (info->file_mode)
+ return elf_ppc64_load_file(argc, argv, info);
+
/* Parse command line arguments */
initrd_base = 0;
initrd_size = 0;
@@ -387,6 +469,8 @@ void elf_ppc64_usage(void)
fprintf(stderr, " --ramdisk=<filename> Initial RAM disk.\n");
fprintf(stderr, " --initrd=<filename> same as --ramdisk.\n");
fprintf(stderr, " --devicetreeblob=<filename> Specify device tree blob file.\n");
+ fprintf(stderr, " ");
+ fprintf(stderr, "Not applicable while using --kexec-file-syscall.\n");
fprintf(stderr, " --dtb=<filename> same as --devicetreeblob.\n");
fprintf(stderr, "elf support is still broken\n");
diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c
index cb62d04..a9329ac 100644
--- a/kexec/kexec-elf-exec.c
+++ b/kexec/kexec-elf-exec.c
@@ -63,9 +63,13 @@ int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
/* If I have a dynamic executable find it's size
* and then find a location for it in memory.
+ * Note on arm64:
+ * arm64's vmlinux has virtual address in physical address
+ * field of PT_LOAD segments. So the following validity check
+ * and relocation makes no sense on arm64.
*/
base = 0;
- if (ehdr->e_type == ET_DYN) {
+ if ((ehdr->e_machine != EM_AARCH64) && (ehdr->e_type == ET_DYN)) {
unsigned long first, last, align;
first = ULONG_MAX;
last = 0;
diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
index 3b5c528..33638c2 100644
--- a/kexec/kexec-syscall.h
+++ b/kexec/kexec-syscall.h
@@ -61,6 +61,9 @@
#ifdef __x86_64__
#define __NR_kexec_file_load 320
#endif
+#ifdef __powerpc64__
+#define __NR_kexec_file_load 382
+#endif
#ifndef __NR_kexec_file_load
/* system call not available for the arch */
diff --git a/kexec/kexec.8 b/kexec/kexec.8
index e0131b4..fb8a4c9 100644
--- a/kexec/kexec.8
+++ b/kexec/kexec.8
@@ -144,6 +144,26 @@ Load the new kernel for use on panic.
Specify that the new kernel is of this
.I type.
.TP
+.BI \-s\ (\-\-kexec-file-syscall)
+Specify that the new KEXEC_FILE_LOAD syscall should be used exclusively.
+.TP
+.BI \-c\ (\-\-kexec-syscall)
+Specify that the old KEXEC_LOAD syscall should be used exclusively (the default).
+.TP
+.BI \-a\ (\-\-kexec-syscall-auto)
+Try the new KEXEC_FILE_LOAD syscall first and when it is not supported or the
+kernel does not understand the supplied image fall back to the old KEXEC_LOAD
+interface.
+
+There is no one single interface that always works.
+
+KEXEC_FILE_LOAD is required on systems that use locked-down secure boot to
+verify the kernel signature. KEXEC_LOAD may be also disabled in the kernel
+configuration.
+
+KEXEC_LOAD is required for some kernel image formats and on architectures that
+do not implement KEXEC_FILE_LOAD.
+.TP
.B \-u\ (\-\-unload)
Unload the current
.B kexec
diff --git a/kexec/kexec.c b/kexec/kexec.c
index cfd837c..313d9fe 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -1005,6 +1005,12 @@ void usage(void)
" preserve context)\n"
" to original kernel.\n"
" -s, --kexec-file-syscall Use file based syscall for kexec operation\n"
+ " -c, --kexec-syscall Use the kexec_load syscall for for compatibility\n"
+ " with systems that don't support -s (default)\n"
+ " -a, --kexec-syscall-auto Use file based syscall for kexec and fall\n"
+ " back to the compatibility syscall when file based\n"
+ " syscall is not supported or the kernel did not\n"
+ " understand the image\n"
" -d, --debug Enable debugging to help spot a failure.\n"
" -S, --status Return 0 if the type (by default crash) is loaded.\n"
"\n"
@@ -1166,7 +1172,7 @@ static int do_kexec_file_load(int fileind, int argc, char **argv,
if (!is_kexec_file_load_implemented()) {
fprintf(stderr, "syscall kexec_file_load not available.\n");
- return -1;
+ return -ENOSYS;
}
if (argc - fileind <= 0) {
@@ -1243,6 +1249,7 @@ int main(int argc, char *argv[])
int do_unload = 0;
int do_reuse_initrd = 0;
int do_kexec_file_syscall = 0;
+ int do_kexec_fallback = 0;
int do_status = 0;
void *entry = 0;
char *type = 0;
@@ -1256,19 +1263,6 @@ int main(int argc, char *argv[])
};
static const char short_options[] = KEXEC_ALL_OPT_STR;
- /*
- * First check if --use-kexec-file-syscall is set. That changes lot of
- * things
- */
- while ((opt = getopt_long(argc, argv, short_options,
- options, 0)) != -1) {
- switch(opt) {
- case OPT_KEXEC_FILE_SYSCALL:
- do_kexec_file_syscall = 1;
- break;
- }
- }
-
/* Reset getopt for the next pass. */
opterr = 1;
optind = 1;
@@ -1310,8 +1304,7 @@ int main(int argc, char *argv[])
do_shutdown = 0;
do_sync = 0;
do_unload = 1;
- if (do_kexec_file_syscall)
- kexec_file_flags |= KEXEC_FILE_UNLOAD;
+ kexec_file_flags |= KEXEC_FILE_UNLOAD;
break;
case OPT_EXEC:
do_load = 0;
@@ -1354,10 +1347,8 @@ int main(int argc, char *argv[])
do_exec = 0;
do_shutdown = 0;
do_sync = 0;
- if (do_kexec_file_syscall)
- kexec_file_flags |= KEXEC_FILE_ON_CRASH;
- else
- kexec_flags = KEXEC_ON_CRASH;
+ kexec_file_flags |= KEXEC_FILE_ON_CRASH;
+ kexec_flags = KEXEC_ON_CRASH;
break;
case OPT_MEM_MIN:
mem_min = strtoul(optarg, &endptr, 0);
@@ -1383,7 +1374,16 @@ int main(int argc, char *argv[])
do_reuse_initrd = 1;
break;
case OPT_KEXEC_FILE_SYSCALL:
- /* We already parsed it. Nothing to do. */
+ do_kexec_file_syscall = 1;
+ do_kexec_fallback = 0;
+ break;
+ case OPT_KEXEC_SYSCALL:
+ do_kexec_file_syscall = 0;
+ do_kexec_fallback = 0;
+ break;
+ case OPT_KEXEC_SYSCALL_AUTO:
+ do_kexec_file_syscall = 1;
+ do_kexec_fallback = 1;
break;
case OPT_STATUS:
do_status = 1;
@@ -1415,7 +1415,9 @@ int main(int argc, char *argv[])
do_load_jump_back_helper = 0;
}
- if (do_load && (kexec_flags & KEXEC_ON_CRASH) &&
+ if (do_load &&
+ ((kexec_flags & KEXEC_ON_CRASH) ||
+ (kexec_file_flags & KEXEC_FILE_ON_CRASH)) &&
!is_crashkernel_mem_reserved()) {
die("Memory for crashkernel is not reserved\n"
"Please reserve memory by passing"
@@ -1447,6 +1449,12 @@ int main(int argc, char *argv[])
}
}
}
+ if (do_kexec_file_syscall) {
+ if (do_load_jump_back_helper && !do_kexec_fallback)
+ die("--load-jump-back-helper not supported with kexec_file_load\n");
+ if (kexec_flags & KEXEC_PRESERVE_CONTEXT)
+ die("--load-preserve-context not supported with kexec_file_load\n");
+ }
if (do_reuse_initrd){
check_reuse_initrd();
@@ -1456,16 +1464,60 @@ int main(int argc, char *argv[])
result = k_status(kexec_flags);
}
if (do_unload) {
- if (do_kexec_file_syscall)
+ if (do_kexec_file_syscall) {
result = kexec_file_unload(kexec_file_flags);
- else
+ if ((result == -ENOSYS) && do_kexec_fallback)
+ do_kexec_file_syscall = 0;
+ }
+ if (!do_kexec_file_syscall)
result = k_unload(kexec_flags);
}
if (do_load && (result == 0)) {
- if (do_kexec_file_syscall)
+ if (do_kexec_file_syscall) {
result = do_kexec_file_load(fileind, argc, argv,
kexec_file_flags);
- else
+ if (do_kexec_fallback) switch (result) {
+ /*
+ * Something failed with signature verification.
+ * Reject the image.
+ */
+ case -ELIBBAD:
+ case -EKEYREJECTED:
+ case -ENOPKG:
+ case -ENOKEY:
+ case -EBADMSG:
+ case -EMSGSIZE:
+ /*
+ * By default reject or do nothing if
+ * succeded
+ */
+ default: break;
+ case -ENOSYS: /* not implemented */
+ /*
+ * Parsing image or other options failed
+ * The image may be invalid or image
+ * type may not supported by kernel so
+ * retry parsing in kexec-tools.
+ */
+ case -EINVAL:
+ case -ENOEXEC:
+ /*
+ * ENOTSUP can be unsupported image
+ * type or unsupported PE signature
+ * wrapper type, duh
+ *
+ * The kernel sometimes wrongly
+ * returns ENOTSUPP (524) - ignore
+ * that. It is not supposed to be seen
+ * by userspace so seeing it is a
+ * kernel bug
+ */
+ case -ENOTSUP:
+ do_kexec_file_syscall = 0;
+ break;
+ }
+ }
+ if (!do_kexec_file_syscall)
result = my_load(type, fileind, argc, argv,
kexec_flags, entry);
}
diff --git a/kexec/kexec.h b/kexec/kexec.h
index 26225d2..d445fbe 100644
--- a/kexec/kexec.h
+++ b/kexec/kexec.h
@@ -219,6 +219,8 @@ extern int file_types;
#define OPT_TYPE 't'
#define OPT_PANIC 'p'
#define OPT_KEXEC_FILE_SYSCALL 's'
+#define OPT_KEXEC_SYSCALL 'c'
+#define OPT_KEXEC_SYSCALL_AUTO 'a'
#define OPT_STATUS 'S'
#define OPT_MEM_MIN 256
#define OPT_MEM_MAX 257
@@ -246,11 +248,13 @@ extern int file_types;
{ "mem-max", 1, 0, OPT_MEM_MAX }, \
{ "reuseinitrd", 0, 0, OPT_REUSE_INITRD }, \
{ "kexec-file-syscall", 0, 0, OPT_KEXEC_FILE_SYSCALL }, \
+ { "kexec-syscall", 0, 0, OPT_KEXEC_SYSCALL }, \
+ { "kexec-syscall-auto", 0, 0, OPT_KEXEC_SYSCALL_AUTO }, \
{ "debug", 0, 0, OPT_DEBUG }, \
{ "status", 0, 0, OPT_STATUS }, \
{ "print-ckr-size", 0, 0, OPT_PRINT_CKR_SIZE }, \
-#define KEXEC_OPT_STR "h?vdfxyluet:psS"
+#define KEXEC_OPT_STR "h?vdfxyluet:pscaS"
extern void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr);
extern void die(const char *fmt, ...)