diff options
Diffstat (limited to '')
-rw-r--r-- | kexec/arch/s390/kexec-image.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/kexec/arch/s390/kexec-image.c b/kexec/arch/s390/kexec-image.c new file mode 100644 index 0000000..69aaf96 --- /dev/null +++ b/kexec/arch/s390/kexec-image.c @@ -0,0 +1,236 @@ +/* + * kexec/arch/s390/kexec-image.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com> + * Heiko Carstens <heiko.carstens@de.ibm.com> + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec/crashdump.h" +#include "kexec-s390.h" +#include <arch/options.h> +#include <fcntl.h> + +static uint64_t crash_base, crash_end; + +static void add_segment_check(struct kexec_info *info, const void *buf, + size_t bufsz, unsigned long base, size_t memsz) +{ + if (info->kexec_flags & KEXEC_ON_CRASH) + if (base + memsz > crash_end - crash_base) + die("Not enough crashkernel memory to load segments\n"); + add_segment(info, buf, bufsz, crash_base + base, memsz); +} + +int command_line_add(struct kexec_info *info, const char *str) +{ + char *tmp = NULL; + + tmp = concat_cmdline(info->command_line, str); + if (!tmp) { + fprintf(stderr, "out of memory\n"); + return -1; + } + + free(info->command_line); + info->command_line = tmp; + return 0; +} + +int image_s390_load_file(int argc, char **argv, struct kexec_info *info) +{ + const char *ramdisk = NULL; + int opt; + + static const struct option options[] = + { + KEXEC_ALL_OPTIONS + {0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_OPT_STR ""; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + case OPT_APPEND: + if (command_line_add(info, optarg)) + return -1; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_REUSE_CMDLINE: + free(info->command_line); + info->command_line = get_command_line(); + break; + } + } + + 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)); + free(info->command_line); + info->command_line = NULL; + return -1; + } + } + + if (info->command_line) + info->command_line_len = strlen(info->command_line) + 1; + else + info->command_line_len = 0; + return 0; +} + +int +image_s390_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info) +{ + void *krnl_buffer; + char *rd_buffer; + const char *ramdisk; + off_t ramdisk_len; + unsigned int ramdisk_origin; + int opt, ret = -1; + + if (info->file_mode) + return image_s390_load_file(argc, argv, info); + + static const struct option options[] = + { + KEXEC_ALL_OPTIONS + {0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_OPT_STR ""; + + ramdisk = NULL; + ramdisk_len = 0; + ramdisk_origin = 0; + + while ((opt = getopt_long(argc,argv,short_options,options,0)) != -1) { + switch(opt) { + case OPT_APPEND: + if (command_line_add(info, optarg)) + return -1; + break; + case OPT_REUSE_CMDLINE: + free(info->command_line); + info->command_line = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + } + } + + if (info->kexec_flags & KEXEC_ON_CRASH) { + if (parse_iomem_single("Crash kernel\n", &crash_base, + &crash_end)) + goto out; + } + + /* Add kernel segment */ + add_segment_check(info, kernel_buf + IMAGE_READ_OFFSET, + kernel_size - IMAGE_READ_OFFSET, IMAGE_READ_OFFSET, + kernel_size - IMAGE_READ_OFFSET); + + /* We do want to change the kernel image */ + krnl_buffer = (void *) kernel_buf + IMAGE_READ_OFFSET; + + /* + * Load ramdisk if present: If image is larger than RAMDISK_ORIGIN_ADDR, + * we load the ramdisk directly behind the image with 1 MiB alignment. + */ + if (ramdisk) { + rd_buffer = slurp_file_mmap(ramdisk, &ramdisk_len); + if (rd_buffer == NULL) { + fprintf(stderr, "Could not read ramdisk.\n"); + goto out; + } + ramdisk_origin = MAX(RAMDISK_ORIGIN_ADDR, kernel_size); + ramdisk_origin = _ALIGN_UP(ramdisk_origin, 0x100000); + add_segment_check(info, rd_buffer, ramdisk_len, + ramdisk_origin, ramdisk_len); + } + if (info->kexec_flags & KEXEC_ON_CRASH) { + if (load_crashdump_segments(info, crash_base, crash_end)) + goto out; + } else { + info->entry = (void *) IMAGE_READ_OFFSET; + } + + /* Register the ramdisk and crashkernel memory in the kernel. */ + { + unsigned long long *tmp; + + tmp = krnl_buffer + INITRD_START_OFFS; + *tmp = (unsigned long long) ramdisk_origin; + + tmp = krnl_buffer + INITRD_SIZE_OFFS; + *tmp = (unsigned long long) ramdisk_len; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + tmp = krnl_buffer + OLDMEM_BASE_OFFS; + *tmp = crash_base; + + tmp = krnl_buffer + OLDMEM_SIZE_OFFS; + *tmp = crash_end - crash_base + 1; + } + } + + if (info->command_line) { + unsigned long maxsize; + char *dest = krnl_buffer + COMMAND_LINE_OFFS; + + maxsize = *(unsigned long *)(krnl_buffer + MAX_COMMAND_LINESIZE_OFFS); + if (!maxsize) + maxsize = LEGACY_COMMAND_LINESIZE; + + if (strlen(info->command_line) > maxsize-1) { + fprintf(stderr, "command line too long, maximum allowed size %ld\n", + maxsize-1); + goto out; + } + strncpy(dest, info->command_line, maxsize-1); + dest[maxsize-1] = '\0'; + } + ret = 0; +out: + free(info->command_line); + info->command_line = NULL; + return ret; +} + +int +image_s390_probe(const char *UNUSED(kernel_buf), off_t UNUSED(kernel_size)) +{ + /* + * Can't reliably tell if an image is valid, + * therefore everything is valid. + */ + return 0; +} + +void +image_s390_usage(void) +{ + printf("--command-line=STRING Set the kernel command line to STRING.\n" + "--append=STRING Set the kernel command line to STRING.\n" + "--initrd=FILENAME Use the file FILENAME as a ramdisk.\n" + "--reuse-cmdline Use kernel command line from running system.\n" + ); +} |