diff options
Diffstat (limited to 'kexec/kexec.c')
-rw-r--r-- | kexec/kexec.c | 264 |
1 files changed, 206 insertions, 58 deletions
diff --git a/kexec/kexec.c b/kexec/kexec.c index 2ce570f..a5e78c6 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -51,8 +51,20 @@ 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) +{ + int i; + dbgprintf("%s\n", prefix); + for (i = 0; i < nr_mr; i++) { + dbgprintf("%016llx-%016llx (%d)\n", mr[i].start, + mr[i].end, mr[i].type); + } +} + void die(const char *fmt, ...) { va_list args; @@ -275,7 +287,7 @@ unsigned long locate_hole(struct kexec_info *info, hole_base = start; break; } else { - hole_base = _ALIGN_DOWN(end - hole_size, + hole_base = _ALIGN_DOWN(end - hole_size + 1, hole_align); } } @@ -469,14 +481,46 @@ static int add_backup_segments(struct kexec_info *info, return 0; } +static char *slurp_fd(int fd, const char *filename, off_t size, off_t *nread) +{ + char *buf; + off_t progress; + ssize_t result; + + buf = xmalloc(size); + progress = 0; + while (progress < size) { + result = read(fd, buf + progress, size - progress); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + fprintf(stderr, "Read on %s failed: %s\n", filename, + strerror(errno)); + free(buf); + close(fd); + return NULL; + } + if (result == 0) + /* EOF */ + break; + progress += result; + } + result = close(fd); + if (result < 0) + die("Close of %s failed: %s\n", filename, strerror(errno)); + + if (nread) + *nread = progress; + return buf; +} + char *slurp_file(const char *filename, off_t *r_size) { int fd; char *buf; - off_t size, progress, err; + off_t size, err, nread; ssize_t result; struct stat stats; - if (!filename) { *r_size = 0; @@ -512,37 +556,23 @@ char *slurp_file(const char *filename, off_t *r_size) size = stats.st_size; } + buf = slurp_fd(fd, filename, size, &nread); + if (!buf) + die("Cannot read %s", filename); + + if (nread != size) + die("Read on %s ended before stat said it should\n", filename); + *r_size = size; - buf = xmalloc(size); - progress = 0; - while(progress < size) { - result = read(fd, buf + progress, size - progress); - if (result < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - die("read on %s of %ld bytes failed: %s\n", filename, - (size - progress)+ 0UL, strerror(errno)); - } - if (result == 0) - die("read on %s ended before stat said it should\n", filename); - progress += result; - } - result = close(fd); - if (result < 0) { - die("Close of %s failed: %s\n", filename, strerror(errno)); - } return buf; } /* This functions reads either specified number of bytes from the file or lesser if EOF is met. */ -char *slurp_file_len(const char *filename, off_t size) +char *slurp_file_len(const char *filename, off_t size, off_t *nread) { int fd; - char *buf; - off_t progress; - ssize_t result; if (!filename) return 0; @@ -552,30 +582,8 @@ char *slurp_file_len(const char *filename, off_t size) strerror(errno)); return 0; } - buf = xmalloc(size); - progress = 0; - while(progress < size) { - result = read(fd, buf + progress, size - progress); - if (result < 0) { - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - fprintf(stderr, "read on %s of %ld bytes failed: %s\n", - filename, (size - progress)+ 0UL, - strerror(errno)); - free(buf); - return 0; - } - if (result == 0) - /* EOF */ - break; - progress += result; - } - result = close(fd); - if (result < 0) { - die("Close of %s failed: %s\n", - filename, strerror(errno)); - } - return buf; + + return slurp_fd(fd, filename, size, nread); } char *slurp_decompress_file(const char *filename, off_t *r_size) @@ -695,7 +703,7 @@ static int my_load(const char *type, int fileind, int argc, char **argv, } if (!type || guess_only) { for (i = 0; i < file_types; i++) { - if (file_type[i].probe(kernel_buf, kernel_size) >= 0) + if (file_type[i].probe(kernel_buf, kernel_size) == 0) break; } if (i == file_types) { @@ -764,8 +772,12 @@ static int my_load(const char *type, int fileind, int argc, char **argv, if (kexec_debug) print_segments(stderr, &info); - result = kexec_load( - info.entry, info.nr_segments, info.segment, info.kexec_flags); + if (xen_present()) + result = xen_kexec_load(&info); + else + result = kexec_load(info.entry, + info.nr_segments, info.segment, + info.kexec_flags); if (result != 0) { /* The load failed, print some debugging information */ fprintf(stderr, "kexec_load failed: %s\n", @@ -777,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; @@ -789,10 +814,13 @@ static int k_unload (unsigned long kexec_flags) } kexec_flags |= native_arch; - result = kexec_load(NULL, 0, NULL, kexec_flags); + if (xen_present()) + result = xen_kexec_unload(kexec_flags); + else + result = kexec_load(NULL, 0, NULL, kexec_flags); if (result != 0) { /* The unload failed, print some debugging information */ - fprintf(stderr, "kexec_load (0 segments) failed: %s\n", + fprintf(stderr, "kexec unload failed: %s\n", strerror(errno)); } return result; @@ -823,7 +851,10 @@ static int my_shutdown(void) */ static int my_exec(void) { - reboot(LINUX_REBOOT_CMD_KEXEC); + if (xen_present()) + xen_kexec_exec(); + else + reboot(LINUX_REBOOT_CMD_KEXEC); /* I have failed if I make it here */ fprintf(stderr, "kexec failed: %s\n", strerror(errno)); @@ -909,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"); @@ -928,6 +960,10 @@ static int kexec_loaded(void) char *p; char line[3]; + /* No way to tell if an image is loaded under Xen, assume it is. */ + if (xen_present()) + return 1; + fp = fopen("/sys/kernel/kexec_loaded", "r"); if (fp == NULL) return -1; @@ -1052,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[]) { @@ -1063,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; @@ -1075,10 +1188,29 @@ 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) { case '?': + usage(); + return 1; case OPT_HELP: usage(); return 0; @@ -1107,6 +1239,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; @@ -1149,7 +1283,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); @@ -1174,6 +1311,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; } @@ -1218,10 +1358,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()) { |