summaryrefslogtreecommitdiff
path: root/kexec/kexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexec/kexec.c')
-rw-r--r--kexec/kexec.c264
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()) {