diff options
Diffstat (limited to 'kexec')
-rw-r--r-- | kexec/arch/ppc64/kexec-elf-ppc64.c | 84 | ||||
-rw-r--r-- | kexec/kexec-elf-exec.c | 6 | ||||
-rw-r--r-- | kexec/kexec-syscall.h | 3 | ||||
-rw-r--r-- | kexec/kexec.8 | 20 | ||||
-rw-r--r-- | kexec/kexec.c | 104 | ||||
-rw-r--r-- | kexec/kexec.h | 6 |
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, ...) |