summaryrefslogtreecommitdiff
path: root/kexec/kexec.c
diff options
context:
space:
mode:
authorVivek Goyal <vgoyal@redhat.com>2014-08-18 11:22:32 -0400
committerSimon Horman <horms@verge.net.au>2014-08-27 16:59:04 +0900
commit046d1755d2bd723a11a180c265e61a884990712e (patch)
treeaf5fab084733aeada0dc6f067dd240fb35144e75 /kexec/kexec.c
parent943ba35f8143408d8ada9a24d0986663cc612df9 (diff)
kexec: Provide an option to use new kexec system call
Hi, This is v2 of the patch. Since v1, I moved syscall implemented check littler earlier in the function as per the feedback. Now a new kexec syscall (kexec_file_load()) has been merged in upstream kernel. This system call takes file descriptors of kernel and initramfs as input (as opposed to list of segments to be loaded). This new system call allows for signature verification of the kernel being loaded. One use of signature verification of kernel is secureboot systems where we want to allow kexec into a kernel only if it is validly signed by a key system trusts. This patch provides and option --kexec-file-syscall (-s), to force use of new system call for kexec. Default is to continue to use old syscall. Currently only bzImage64 on x86_64 can be loaded using this system call. As kernel adds support for more arches and for more image types, kexec-tools can be modified accordingly. Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Acked-by: Baoquan He <bhe@redhat.com> Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'kexec/kexec.c')
-rw-r--r--kexec/kexec.c132
1 files changed, 129 insertions, 3 deletions
diff --git a/kexec/kexec.c b/kexec/kexec.c
index 133e622..7e7b604 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -51,6 +51,8 @@
unsigned long long mem_min = 0;
unsigned long long mem_max = ULONG_MAX;
static unsigned long kexec_flags = 0;
+/* Flags for kexec file (fd) based syscall */
+static unsigned long kexec_file_flags = 0;
int kexec_debug = 0;
void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr)
@@ -787,6 +789,19 @@ static int my_load(const char *type, int fileind, int argc, char **argv,
return result;
}
+static int kexec_file_unload(unsigned long kexec_file_flags)
+{
+ int ret = 0;
+
+ ret = kexec_file_load(-1, -1, 0, NULL, kexec_file_flags);
+ if (ret != 0) {
+ /* The unload failed, print some debugging information */
+ fprintf(stderr, "kexec_file_load(unload) failed\n: %s\n",
+ strerror(errno));
+ }
+ return ret;
+}
+
static int k_unload (unsigned long kexec_flags)
{
int result;
@@ -925,6 +940,7 @@ void usage(void)
" (0 means it's not jump back or\n"
" preserve context)\n"
" to original kernel.\n"
+ " -s, --kexec-file-syscall Use file based syscall for kexec operation\n"
" -d, --debug Enable debugging to help spot a failure.\n"
"\n"
"Supported kernel file types and options: \n");
@@ -1072,6 +1088,82 @@ char *concat_cmdline(const char *base, const char *append)
return cmdline;
}
+/* New file based kexec system call related code */
+static int do_kexec_file_load(int fileind, int argc, char **argv,
+ unsigned long flags) {
+
+ char *kernel;
+ int kernel_fd, i;
+ struct kexec_info info;
+ int ret = 0;
+ char *kernel_buf;
+ off_t kernel_size;
+
+ memset(&info, 0, sizeof(info));
+ info.segment = NULL;
+ info.nr_segments = 0;
+ info.entry = NULL;
+ info.backup_start = 0;
+ info.kexec_flags = flags;
+
+ info.file_mode = 1;
+ info.initrd_fd = -1;
+
+ if (!is_kexec_file_load_implemented()) {
+ fprintf(stderr, "syscall kexec_file_load not available.\n");
+ return -1;
+ }
+
+ if (argc - fileind <= 0) {
+ fprintf(stderr, "No kernel specified\n");
+ usage();
+ return -1;
+ }
+
+ kernel = argv[fileind];
+
+ kernel_fd = open(kernel, O_RDONLY);
+ if (kernel_fd == -1) {
+ fprintf(stderr, "Failed to open file %s:%s\n", kernel,
+ strerror(errno));
+ return -1;
+ }
+
+ /* slurp in the input kernel */
+ kernel_buf = slurp_decompress_file(kernel, &kernel_size);
+
+ for (i = 0; i < file_types; i++) {
+ if (file_type[i].probe(kernel_buf, kernel_size) >= 0)
+ break;
+ }
+
+ if (i == file_types) {
+ fprintf(stderr, "Cannot determine the file type " "of %s\n",
+ kernel);
+ return -1;
+ }
+
+ ret = file_type[i].load(argc, argv, kernel_buf, kernel_size, &info);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot load %s\n", kernel);
+ return ret;
+ }
+
+ /*
+ * If there is no initramfs, set KEXEC_FILE_NO_INITRAMFS flag so that
+ * kernel does not return error with negative initrd_fd.
+ */
+ if (info.initrd_fd == -1)
+ info.kexec_flags |= KEXEC_FILE_NO_INITRAMFS;
+
+ ret = kexec_file_load(kernel_fd, info.initrd_fd, info.command_line_len,
+ info.command_line, info.kexec_flags);
+ if (ret != 0)
+ fprintf(stderr, "kexec_file_load failed: %s\n",
+ strerror(errno));
+ return ret;
+}
+
int main(int argc, char *argv[])
{
@@ -1083,6 +1175,7 @@ int main(int argc, char *argv[])
int do_ifdown = 0;
int do_unload = 0;
int do_reuse_initrd = 0;
+ int do_kexec_file_syscall = 0;
void *entry = 0;
char *type = 0;
char *endptr;
@@ -1095,6 +1188,23 @@ 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;
+
while ((opt = getopt_long(argc, argv, short_options,
options, 0)) != -1) {
switch(opt) {
@@ -1127,6 +1237,8 @@ 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;
break;
case OPT_EXEC:
do_load = 0;
@@ -1169,7 +1281,10 @@ int main(int argc, char *argv[])
do_exec = 0;
do_shutdown = 0;
do_sync = 0;
- kexec_flags = KEXEC_ON_CRASH;
+ if (do_kexec_file_syscall)
+ kexec_file_flags |= KEXEC_FILE_ON_CRASH;
+ else
+ kexec_flags = KEXEC_ON_CRASH;
break;
case OPT_MEM_MIN:
mem_min = strtoul(optarg, &endptr, 0);
@@ -1194,6 +1309,9 @@ int main(int argc, char *argv[])
case OPT_REUSE_INITRD:
do_reuse_initrd = 1;
break;
+ case OPT_KEXEC_FILE_SYSCALL:
+ /* We already parsed it. Nothing to do. */
+ break;
default:
break;
}
@@ -1238,10 +1356,18 @@ int main(int argc, char *argv[])
}
if (do_unload) {
- result = k_unload(kexec_flags);
+ if (do_kexec_file_syscall)
+ result = kexec_file_unload(kexec_file_flags);
+ else
+ result = k_unload(kexec_flags);
}
if (do_load && (result == 0)) {
- result = my_load(type, fileind, argc, argv, kexec_flags, entry);
+ if (do_kexec_file_syscall)
+ result = do_kexec_file_load(fileind, argc, argv,
+ kexec_file_flags);
+ else
+ result = my_load(type, fileind, argc, argv,
+ kexec_flags, entry);
}
/* Don't shutdown unless there is something to reboot to! */
if ((result == 0) && (do_shutdown || do_exec) && !kexec_loaded()) {