int image_arm64_load(int argc, char **argv, const char *kernel_buf, off_t kernel_size, struct kexec_info *info) { int result; uint64_t start; const struct arm64_image_header *h; char *header_option = NULL; h = (const struct arm64_image_header *)(kernel_buf); arm64_mem.text_offset = le64_to_cpu(h->text_offset); arm64_mem.image_size = le64_to_cpu(h->image_size); arm64_mem.page_offset = get_kernel_page_offset(); if (info->kexec_flags & KEXEC_ON_CRASH) { result = load_crashdump_segments(info, &header_option); if (result) { fprintf(stderr, "kexec: load crashdump segments failed.\n"); return -1; } start = crash_reserved_mem.start; } else { result = parse_iomem_single("Kernel code\n", &start, NULL); if (result) { fprintf(stderr, "kexec: Could not get kernel code address.\n"); return -1; } start -= arm64_mem.text_offset; } /* Add kernel */ add_segment_phys_virt(info, kernel_buf, kernel_size, start + arm64_mem.text_offset, kernel_size, 0); info->entry = (void *)start + arm64_mem.text_offset; result = arm64_load_other_segments(info, (unsigned long)info->entry, header_option); if (header_option) free(header_option); return result; }
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { unsigned long base; unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ unsigned int offset = 0x8000; /* 32k offset from memory start */ unsigned int opt_ramdisk_addr; unsigned int opt_atags_addr; const char *command_line; char *modified_cmdline = NULL; off_t command_line_len; const char *ramdisk; char *ramdisk_buf; int opt; char *endptr; int use_dtb; const char *dtb_file; char *dtb_buf; off_t dtb_length; off_t dtb_offset; struct arm_mach *mach; /* See options.h -- add any more there, too. */ static const struct option options[] = { KEXEC_ARCH_OPTIONS { "command-line", 1, 0, OPT_APPEND }, { "append", 1, 0, OPT_APPEND }, { "initrd", 1, 0, OPT_RAMDISK }, { "ramdisk", 1, 0, OPT_RAMDISK }, { "dtb", 2, 0, OPT_DTB }, { "rd-addr", 1, 0, OPT_RD_ADDR }, { "atags-addr", 1, 0, OPT_ATAGS_ADDR }, { "boardname", 1, 0, OPT_BOARDNAME }, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:d::i:g:b:"; /* * Parse the command line arguments */ command_line = 0; command_line_len = 0; ramdisk = 0; ramdisk_buf = 0; use_dtb = 0; dtb_file = NULL; opt_ramdisk_addr = 0; opt_atags_addr = 0; mach = NULL; while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch(opt) { default: /* Ignore core options */ if (opt < OPT_ARCH_MAX) { break; } case '?': usage(); return -1; case OPT_APPEND: command_line = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; case OPT_DTB: use_dtb = 1; if(optarg) dtb_file = optarg; break; case OPT_RD_ADDR: opt_ramdisk_addr = strtoul(optarg, &endptr, 0); if (*endptr) { fprintf(stderr, "Bad option value in --rd-addr=%s\n", optarg); usage(); return -1; } break; case OPT_ATAGS_ADDR: opt_atags_addr = strtoul(optarg, &endptr, 0); if (*endptr) { fprintf(stderr, "Bad option value in --atag-addr=%s\n", optarg); usage(); return -1; } break; case OPT_BOARDNAME: mach = arm_mach_choose(optarg); if(!mach) { fprintf(stderr, "Unknown boardname '%s'!\n", optarg); return -1; } break; } } if (command_line) { command_line_len = strlen(command_line) + 1; if (command_line_len > COMMAND_LINE_SIZE) command_line_len = COMMAND_LINE_SIZE; } if (ramdisk) { ramdisk_buf = slurp_file(ramdisk, &initrd_size); } /* * If we are loading a dump capture kernel, we need to update kernel * command line and also add some additional segments. */ if (info->kexec_flags & KEXEC_ON_CRASH) { uint64_t start, end; modified_cmdline = xmalloc(COMMAND_LINE_SIZE); if (!modified_cmdline) return -1; if (command_line) { (void) strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } if (load_crashdump_segments(info, modified_cmdline) < 0) { free(modified_cmdline); return -1; } command_line = modified_cmdline; command_line_len = strlen(command_line) + 1; /* * We put the dump capture kernel at the start of crashkernel * reserved memory. */ if (parse_iomem_single("Crash kernel\n", &start, &end)) { /* * No crash kernel memory reserved. We cannot do more * but just bail out. */ return -1; } base = start; } else { base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX); } if (base == ULONG_MAX) return -1; /* assume the maximum kernel compression ratio is 4, * and just to be safe, place ramdisk after that */ if(opt_ramdisk_addr == 0) initrd_base = _ALIGN(base + len * 4, getpagesize()); else initrd_base = opt_ramdisk_addr; if(!use_dtb) { if (atag_arm_load(info, base + atag_offset, command_line, command_line_len, ramdisk_buf, initrd_size, initrd_base) == -1) return -1; } else { char *dtb_img = NULL; off_t dtb_img_len = 0; int free_dtb_img = 0; int choose_res = 0; if(!mach) { fprintf(stderr, "DTB: --boardname was not specified.\n"); return -1; } if(dtb_file) { if(!load_dtb_image(dtb_file, &dtb_img, &dtb_img_len)) return -1; printf("DTB: Using DTB from file %s\n", dtb_file); free_dtb_img = 1; } else { if(!get_appended_dtb(buf, len, &dtb_img, &dtb_img_len)) return -1; printf("DTB: Using DTB appended to zImage\n"); } choose_res = (mach->choose_dtb)(dtb_img, dtb_img_len, &dtb_buf, &dtb_length); if(free_dtb_img) free(dtb_img); if(choose_res) { int ret, off; dtb_length = fdt_totalsize(dtb_buf) + DTB_PAD_SIZE; dtb_buf = xrealloc(dtb_buf, dtb_length); ret = fdt_open_into(dtb_buf, dtb_buf, dtb_length); if(ret) die("DTB: fdt_open_into failed"); ret = (mach->add_extra_regs)(dtb_buf); if (ret < 0) { fprintf(stderr, "DTB: error while adding mach-specific extra regs\n"); return -1; } if (command_line) { const char *node_name = "/chosen"; const char *prop_name = "bootargs"; /* check if a /choosen subnode already exists */ off = fdt_path_offset(dtb_buf, node_name); if (off == -FDT_ERR_NOTFOUND) off = fdt_add_subnode(dtb_buf, off, node_name); if (off < 0) { fprintf(stderr, "DTB: Error adding %s node.\n", node_name); return -1; } if (fdt_setprop(dtb_buf, off, prop_name, command_line, strlen(command_line) + 1) != 0) { fprintf(stderr, "DTB: Error setting %s/%s property.\n", node_name, prop_name); return -1; } } if(ramdisk) { const char *node_name = "/chosen"; uint32_t initrd_start, initrd_end; /* check if a /choosen subnode already exists */ off = fdt_path_offset(dtb_buf, node_name); if (off == -FDT_ERR_NOTFOUND) off = fdt_add_subnode(dtb_buf, off, node_name); if (off < 0) { fprintf(stderr, "DTB: Error adding %s node.\n", node_name); return -1; } initrd_start = cpu_to_fdt32(initrd_base); initrd_end = cpu_to_fdt32(initrd_base + initrd_size); ret = fdt_setprop(dtb_buf, off, "linux,initrd-start", &initrd_start, sizeof(initrd_start)); if (ret) die("DTB: Error setting %s/linux,initrd-start property.\n", node_name); ret = fdt_setprop(dtb_buf, off, "linux,initrd-end", &initrd_end, sizeof(initrd_end)); if (ret) die("DTB: Error setting %s/linux,initrd-end property.\n", node_name); } fdt_pack(dtb_buf); } else { /* * Extract the DTB from /proc/device-tree. */ printf("DTB: Failed to load dtb from zImage or dtb.img, using /proc/device-tree. This is unlikely to work.\n"); create_flatten_tree(&dtb_buf, &dtb_length, command_line); } if(ramdisk) { add_segment(info, ramdisk_buf, initrd_size, initrd_base, initrd_size); } if(opt_atags_addr != 0) dtb_offset = opt_atags_addr; else { dtb_offset = initrd_base + initrd_size + getpagesize(); dtb_offset = _ALIGN_DOWN(dtb_offset, getpagesize()); } printf("DTB: add dtb segment 0x%x\n", (unsigned int)dtb_offset); add_segment(info, dtb_buf, dtb_length, dtb_offset, dtb_length); } add_segment(info, buf, len, base + offset, len); info->entry = (void*)base + offset; return 0; }
int elf_x86_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; char *command_line = NULL, *modified_cmdline = NULL; const char *append = NULL; char *tmp_cmdline = NULL; char *error_msg = NULL; int result; int command_line_len; const char *ramdisk; unsigned long entry, max_addr; int arg_style; #define ARG_STYLE_ELF 0 #define ARG_STYLE_LINUX 1 #define ARG_STYLE_NONE 2 int opt; /* 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 }, { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, { "initrd", 1, NULL, OPT_RAMDISK }, { "ramdisk", 1, NULL, OPT_RAMDISK }, { "args-elf", 0, NULL, OPT_ARGS_ELF }, { "args-linux", 0, NULL, OPT_ARGS_LINUX }, { "args-none", 0, NULL, OPT_ARGS_NONE }, { 0, 0, NULL, 0 }, }; static const char short_options[] = KEXEC_OPT_STR ""; /* * Parse the command line arguments */ arg_style = ARG_STYLE_ELF; ramdisk = 0; result = 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: append = optarg; break; case OPT_REUSE_CMDLINE: tmp_cmdline = get_command_line(); break; case OPT_RAMDISK: ramdisk = optarg; break; case OPT_ARGS_ELF: arg_style = ARG_STYLE_ELF; break; case OPT_ARGS_LINUX: arg_style = ARG_STYLE_LINUX; break; case OPT_ARGS_NONE: #ifdef __i386__ arg_style = ARG_STYLE_NONE; #else die("--args-none only works on arch i386\n"); #endif break; } } command_line = concat_cmdline(tmp_cmdline, append); if (tmp_cmdline) { free(tmp_cmdline); } command_line_len = 0; if (command_line) { command_line_len = strlen(command_line) +1; } else { command_line = strdup("\0"); command_line_len = 1; } /* Need to append some command line parameters internally in case of * taking crash dumps. */ if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) { modified_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); if (command_line) { strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } } /* Load the ELF executable */ elf_exec_build_load(info, &ehdr, buf, len, 0); entry = ehdr.e_entry; max_addr = elf_max_addr(&ehdr); /* Do we want arguments? */ if (arg_style != ARG_STYLE_NONE) { /* Load the setup code */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, ULONG_MAX, 1, 0); } if (arg_style == ARG_STYLE_NONE) { info->entry = (void *)entry; } else if (arg_style == ARG_STYLE_ELF) { unsigned long note_base; struct entry32_regs regs; uint32_t arg1, arg2; /* Setup the ELF boot notes */ note_base = elf_boot_notes(info, max_addr, command_line, command_line_len); /* Initialize the stack arguments */ arg2 = 0; /* No return address */ arg1 = note_base; elf_rel_set_symbol(&info->rhdr, "stack_arg32_1", &arg1, sizeof(arg1)); elf_rel_set_symbol(&info->rhdr, "stack_arg32_2", &arg2, sizeof(arg2)); /* Initialize the registers */ elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); regs.eip = entry; /* The entry point */ regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2"); elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); if (ramdisk) { error_msg = "Ramdisks not supported with generic elf arguments"; goto out; } } else if (arg_style == ARG_STYLE_LINUX) { struct x86_linux_faked_param_header *hdr; unsigned long param_base; const char *ramdisk_buf; off_t ramdisk_length; struct entry32_regs regs; int rc = 0; /* Get the linux parameter header */ hdr = xmalloc(sizeof(*hdr)); /* Hack: With some ld versions, vmlinux program headers show * a gap of two pages between bss segment and data segment * but effectively kernel considers it as bss segment and * overwrites the any data placed there. Hence bloat the * memsz of parameter segment to 16K to avoid being placed * in such gaps. * This is a makeshift solution until it is fixed in kernel */ param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024, 16, 0, max_addr, 1); /* Initialize the parameter header */ memset(hdr, 0, sizeof(*hdr)); init_linux_parameters(&hdr->hdr); /* Add a ramdisk to the current image */ ramdisk_buf = NULL; ramdisk_length = 0; if (ramdisk) { ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); } /* If panic kernel is being loaded, additional segments need * to be created. */ if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) { rc = load_crashdump_segments(info, modified_cmdline, max_addr, 0); if (rc < 0) { result = -1; goto out; } /* Use new command line. */ free(command_line); command_line = modified_cmdline; command_line_len = strlen(modified_cmdline) + 1; modified_cmdline = NULL; } /* Tell the kernel what is going on */ setup_linux_bootloader_parameters(info, &hdr->hdr, param_base, offsetof(struct x86_linux_faked_param_header, command_line), command_line, command_line_len, ramdisk_buf, ramdisk_length); /* Fill in the information bios calls would usually provide */ setup_linux_system_parameters(info, &hdr->hdr); /* Initialize the registers */ elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); regs.ebx = 0; /* Bootstrap processor */ regs.esi = param_base; /* Pointer to the parameters */ regs.eip = entry; /* The entry point */ regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); } else {
int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; const char *command_line, *ramdisk=0, *vmm=0, *kernel_buf; char *ramdisk_buf = NULL; off_t ramdisk_size = 0, kernel_size; unsigned long command_line_len; unsigned long entry, max_addr, gp_value; unsigned long command_line_base, ramdisk_base, image_base; unsigned long efi_memmap_base, efi_memmap_size; unsigned long boot_param_base; unsigned long noio=0; int result; int opt; char *efi_memmap_buf, *boot_param; static const struct option options[] = { KEXEC_ARCH_OPTIONS {"command-line", 1, 0, OPT_APPEND}, {"append", 1, 0, OPT_APPEND}, {"initrd", 1, 0, OPT_RAMDISK}, {"noio", 0, 0, OPT_NOIO}, {"vmm", 1, 0, OPT_VMM}, {0, 0, 0, 0}, }; static const char short_options[] = KEXEC_ARCH_OPT_STR ""; command_line = 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 '?': usage(); return -1; case OPT_APPEND: command_line = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; case OPT_NOIO: /* disable PIO and MMIO in purgatory code*/ noio = 1; break; case OPT_VMM: vmm = optarg; break; } } command_line_len = 0; if (command_line) { command_line_len = strlen(command_line) + 16; } if (vmm) kernel_buf = slurp_decompress_file(vmm, &kernel_size); else { kernel_buf = buf; kernel_size = len; } /* Parse the Elf file */ result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); if (result < 0) { fprintf(stderr, "ELF parse failed\n"); free_elf_info(&ehdr); return result; } if (info->kexec_flags & KEXEC_ON_CRASH ) { if ((mem_min == 0x00) && (mem_max == ULONG_MAX)) { fprintf(stderr, "Failed to find crash kernel region " "in %s\n", proc_iomem()); free_elf_info(&ehdr); return -1; } move_loaded_segments(info, &ehdr, mem_min); } else if (update_loaded_segments(info, &ehdr) < 0) { fprintf(stderr, "Failed to place kernel\n"); return -1; } entry = ehdr.e_entry; max_addr = elf_max_addr(&ehdr); /* Load the Elf data */ result = elf_exec_load(&ehdr, info); if (result < 0) { fprintf(stderr, "ELF load failed\n"); free_elf_info(&ehdr); return result; } /* Load the setup code */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0x0, ULONG_MAX, -1, 0); if (load_crashdump_segments(info, &ehdr, max_addr, 0, &command_line) < 0) return -1; // reverve 4k for ia64_boot_param boot_param = xmalloc(4096); boot_param_base = add_buffer(info, boot_param, 4096, 4096, 4096, 0, max_addr, -1); elf_rel_set_symbol(&info->rhdr, "__noio", &noio, sizeof(long)); elf_rel_set_symbol(&info->rhdr, "__boot_param_base", &boot_param_base, sizeof(long)); // reserve efi_memmap of actual size allocated in production kernel efi_memmap_size = saved_efi_memmap_size; efi_memmap_buf = xmalloc(efi_memmap_size); efi_memmap_base = add_buffer(info, efi_memmap_buf, efi_memmap_size, efi_memmap_size, 4096, 0, max_addr, -1); elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base", &efi_memmap_base, sizeof(long)); elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size", &efi_memmap_size, sizeof(long)); if (command_line) { command_line_len = strlen(command_line) + 1; } if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) { char *cmdline = xmalloc(command_line_len); strcpy(cmdline, command_line); if (info->kexec_flags & KEXEC_ON_CRASH) { char buf[128]; sprintf(buf," max_addr=%lluM min_addr=%lluM", mem_max>>20, mem_min>>20); command_line_len = strlen(cmdline) + strlen(buf) + 1; cmdline = xrealloc(cmdline, command_line_len); strcat(cmdline, buf); }
int elf_mips_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; const char *command_line; int command_line_len; char *crash_cmdline; int opt; int result; unsigned long cmdline_addr; size_t i; unsigned long bss_start = 0, bss_size = 0; static const struct option options[] = { KEXEC_ARCH_OPTIONS {"command-line", 1, 0, OPT_APPEND}, {"append", 1, 0, OPT_APPEND}, {0, 0, 0, 0}, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; command_line = 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 '?': usage(); return -1; case OPT_APPEND: command_line = optarg; break; } } command_line_len = 0; /* Need to append some command line parameters internally in case of * taking crash dumps. */ if (info->kexec_flags & KEXEC_ON_CRASH) { crash_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE); } else crash_cmdline = NULL; result = build_elf_exec_info(buf, len, &ehdr, 0); if (result < 0) die("ELF exec parse failed\n"); /* Read in the PT_LOAD segments and remove CKSEG0 mask from address*/ for (i = 0; i < ehdr.e_phnum; i++) { struct mem_phdr *phdr; phdr = &ehdr.e_phdr[i]; if (phdr->p_type == PT_LOAD) phdr->p_paddr = virt_to_phys(phdr->p_paddr); } for (i = 0; i < ehdr.e_shnum; i++) { struct mem_shdr *shdr; unsigned char *strtab; strtab = (unsigned char *)ehdr.e_shdr[ehdr.e_shstrndx].sh_data; shdr = &ehdr.e_shdr[i]; if (shdr->sh_size && strcmp((char *)&strtab[shdr->sh_name], ".bss") == 0) { bss_start = virt_to_phys(shdr->sh_addr); bss_size = shdr->sh_size; break; } } /* Load the Elf data */ result = elf_exec_load(&ehdr, info); if (result < 0) die("ELF exec load failed\n"); info->entry = (void *)virt_to_phys(ehdr.e_entry); /* Put cmdline right after bss for crash*/ if (info->kexec_flags & KEXEC_ON_CRASH) cmdline_addr = bss_start + bss_size; else cmdline_addr = 0; if (!bss_size) die("No .bss segment present\n"); if (command_line) command_line_len = strlen(command_line) + 1; if (info->kexec_flags & KEXEC_ON_CRASH) { result = load_crashdump_segments(info, crash_cmdline, 0, 0); if (result < 0) { free(crash_cmdline); return -1; } } if (command_line) strncat(cmdline_buf, command_line, command_line_len); if (crash_cmdline) strncat(cmdline_buf, crash_cmdline, sizeof(crash_cmdline) - strlen(crash_cmdline) - 1); add_buffer(info, cmdline_buf, sizeof(cmdline_buf), sizeof(cmdline_buf), sizeof(void *), cmdline_addr, 0x0fffffff, 1); return 0; }
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { unsigned long base; unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ unsigned int offset = 0x8000; /* 32k offset from memory start */ const char *command_line; off_t command_line_len; const char *ramdisk; char *ramdisk_buf; char *modified_cmdline; off_t ramdisk_length; int opt; #define OPT_APPEND 'a' #define OPT_RAMDISK 'r' static const struct option options[] = { KEXEC_ARCH_OPTIONS { "command-line", 1, 0, OPT_APPEND }, { "append", 1, 0, OPT_APPEND }, { "initrd", 1, 0, OPT_RAMDISK }, { "ramdisk", 1, 0, OPT_RAMDISK }, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:"; /* * Parse the command line arguments */ command_line = 0; command_line_len = 0; ramdisk = 0; ramdisk_buf = 0; ramdisk_length = 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 '?': usage(); return -1; case OPT_APPEND: command_line = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; } } if (command_line) { command_line_len = strlen(command_line) + 1; if (command_line_len > COMMAND_LINE_SIZE) command_line_len = COMMAND_LINE_SIZE; } if (ramdisk) { ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); } /* This code is just to load the elf headers for /proc/vmcore */ if (info->kexec_flags & KEXEC_ON_CRASH) { modified_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); if (command_line) { strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } /* If panic kernel is being loaded, additional segments need * to be created. load_crashdump_segments will take care of * loading the segments as high in memory as possible, hence * in turn as away as possible from kernel to avoid being * stomped by the kernel. */ if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0) return -1; /* Use new command line buffer */ command_line = modified_cmdline; command_line_len = strlen(command_line) + 1; } //base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX); base = crashkernel_mem_start(); if (base == ULONG_MAX) return -1; if (atag_arm_load(info, base + atag_offset, command_line, command_line_len, ramdisk_buf, ramdisk_length) == -1) return -1; add_segment(info, buf, len, base + offset, len); info->entry = (void*)base + offset; return 0; }
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; char *cmdline, *modified_cmdline = NULL; const char *devicetreeblob; int cmdline_len, modified_cmdline_len; uint64_t max_addr, hole_addr; unsigned char *seg_buf = NULL; off_t seg_size = 0; struct mem_phdr *phdr; size_t size; uint64_t *rsvmap_ptr; struct bootblock *bb_ptr; unsigned int nr_segments, i; int result, opt; uint64_t my_kernel, my_dt_offset; unsigned int my_panic_kernel; uint64_t my_stack, my_backup_start; uint64_t toc_addr; unsigned int slave_code[256/sizeof (unsigned int)], master_entry; #define OPT_APPEND (OPT_ARCH_MAX+0) #define OPT_RAMDISK (OPT_ARCH_MAX+1) #define OPT_DEVICETREEBLOB (OPT_ARCH_MAX+2) #define OPT_ARGS_IGNORE (OPT_ARCH_MAX+3) 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 }, { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, { 0, 0, NULL, 0 }, }; static const char short_options[] = KEXEC_OPT_STR ""; /* Parse command line arguments */ initrd_base = 0; initrd_size = 0; cmdline = 0; ramdisk = 0; devicetreeblob = 0; max_addr = 0xFFFFFFFFFFFFFFFFUL; hole_addr = 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 '?': usage(); return -1; case OPT_APPEND: cmdline = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; case OPT_DEVICETREEBLOB: devicetreeblob = optarg; break; case OPT_ARGS_IGNORE: break; } } cmdline_len = 0; if (cmdline) cmdline_len = strlen(cmdline) + 1; else fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n"); if (ramdisk && reuse_initrd) die("Can't specify --ramdisk or --initrd with --reuseinitrd\n"); setup_memory_ranges(info->kexec_flags); /* Need to append some command line parameters internally in case of * taking crash dumps. */ if (info->kexec_flags & KEXEC_ON_CRASH) { modified_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); if (cmdline) { strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } modified_cmdline_len = strlen(modified_cmdline); } /* Parse the Elf file */ result = build_elf_exec_info(buf, len, &ehdr, 0); if (result < 0) { free_elf_info(&ehdr); return result; } /* Load the Elf data. Physical load addresses in elf64 header do not * show up correctly. Use user supplied address for now to patch the * elf header */ phdr = &ehdr.e_phdr[0]; size = phdr->p_filesz; if (size > phdr->p_memsz) size = phdr->p_memsz; hole_addr = (uint64_t)locate_hole(info, size, 0, 0, max_addr, 1); ehdr.e_phdr[0].p_paddr = hole_addr; result = elf_exec_load(&ehdr, info); if (result < 0) { free_elf_info(&ehdr); return result; } /* If panic kernel is being loaded, additional segments need * to be created. */ if (info->kexec_flags & KEXEC_ON_CRASH) { result = load_crashdump_segments(info, modified_cmdline, max_addr, 0); if (result < 0) return -1; /* Use new command line. */ cmdline = modified_cmdline; cmdline_len = strlen(modified_cmdline) + 1; } /* Add v2wrap to the current image */ seg_buf = NULL; seg_size = 0; seg_buf = (unsigned char *) malloc(purgatory_size); if (seg_buf == NULL) { free_elf_info(&ehdr); return -1; } memcpy(seg_buf, purgatory, purgatory_size); seg_size = purgatory_size; elf_rel_build_load(info, &info->rhdr, (const char *)purgatory, purgatory_size, 0, max_addr, 1, 0); /* Add a ram-disk to the current image * Note: Add the ramdisk after elf_rel_build_load */ if (ramdisk) { if (devicetreeblob) { fprintf(stderr, "Can't use ramdisk with device tree blob input\n"); return -1; } seg_buf = (unsigned char *)slurp_file(ramdisk, &seg_size); add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, 1); hole_addr = (uint64_t) info->segment[info->nr_segments-1].mem; initrd_base = hole_addr; initrd_size = (uint64_t) info->segment[info->nr_segments-1].memsz; } /* ramdisk */ if (devicetreeblob) { unsigned char *blob_buf = NULL; off_t blob_size = 0; /* Grab device tree from buffer */ blob_buf = (unsigned char *)slurp_file(devicetreeblob, &blob_size); add_buffer(info, blob_buf, blob_size, blob_size, 0, 0, max_addr, -1); } else { /* create from fs2dt */ seg_buf = NULL; seg_size = 0; create_flatten_tree(info, (unsigned char **)&seg_buf, (unsigned long *)&seg_size,cmdline); add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, -1); } /* patch reserve map address for flattened device-tree * find last entry (both 0) in the reserve mem list. Assume DT * entry is before this one */ bb_ptr = (struct bootblock *)( (unsigned char *)info->segment[(info->nr_segments)-1].buf); rsvmap_ptr = (uint64_t *)( (unsigned char *)info->segment[(info->nr_segments)-1].buf + bb_ptr->off_mem_rsvmap); while (*rsvmap_ptr || *(rsvmap_ptr+1)) rsvmap_ptr += 2; rsvmap_ptr -= 2; *rsvmap_ptr = (uint64_t)( info->segment[(info->nr_segments)-1].mem); rsvmap_ptr++; *rsvmap_ptr = (uint64_t)bb_ptr->totalsize; nr_segments = info->nr_segments; /* Set kernel */ my_kernel = (uint64_t)info->segment[0].mem; elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); /* Set dt_offset */ my_dt_offset = (uint64_t)info->segment[nr_segments-1].mem; elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset, sizeof(my_dt_offset)); /* get slave code from new kernel, put in purgatory */ elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code, sizeof(slave_code)); master_entry = slave_code[0]; memcpy(slave_code, info->segment[0].buf, sizeof(slave_code)); slave_code[0] = master_entry; elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code, sizeof(slave_code)); if (info->kexec_flags & KEXEC_ON_CRASH) { my_panic_kernel = 1; /* Set panic flag */ elf_rel_set_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, sizeof(my_panic_kernel)); /* Set backup address */ my_backup_start = info->backup_start; elf_rel_set_symbol(&info->rhdr, "backup_start", &my_backup_start, sizeof(my_backup_start)); } /* Set stack address */ my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1); my_stack += 16*1024; elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); /* Set toc */ toc_addr = (unsigned long) my_r2(&info->rhdr); elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); #ifdef DEBUG my_kernel = 0; my_dt_offset = 0; my_panic_kernel = 0; my_backup_start = 0; my_stack = 0; toc_addr = 0; elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset, sizeof(my_dt_offset)); elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, sizeof(my_panic_kernel)); elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start, sizeof(my_backup_start)); elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); fprintf(stderr, "info->entry is %p\n", info->entry); fprintf(stderr, "kernel is %lx\n", my_kernel); fprintf(stderr, "dt_offset is %lx\n", my_dt_offset); fprintf(stderr, "panic_kernel is %x\n", my_panic_kernel); fprintf(stderr, "backup_start is %lx\n", my_backup_start); fprintf(stderr, "stack is %lx\n", my_stack); fprintf(stderr, "toc_addr is %lx\n", toc_addr); fprintf(stderr, "purgatory size is %lu\n", purgatory_size); #endif for (i = 0; i < nr_segments; i++) fprintf(stderr, "segment[%d].mem:%p memsz:%ld\n", i, info->segment[i].mem, info->segment[i].memsz); return 0; }
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; char *cmdline, *modified_cmdline = NULL; const char *devicetreeblob; uint64_t max_addr, hole_addr; char *seg_buf = NULL; off_t seg_size = 0; struct mem_phdr *phdr; size_t size; #ifdef NEED_RESERVE_DTB uint64_t *rsvmap_ptr; struct bootblock *bb_ptr; #endif int result, opt; uint64_t my_kernel, my_dt_offset; uint64_t my_opal_base = 0, my_opal_entry = 0; unsigned int my_panic_kernel; uint64_t my_stack, my_backup_start; uint64_t toc_addr; uint32_t my_run_at_load; unsigned int slave_code[256/sizeof (unsigned int)], master_entry; /* 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 ""; if (info->file_mode) return elf_ppc64_load_file(argc, argv, info); /* Parse command line arguments */ initrd_base = 0; initrd_size = 0; cmdline = 0; ramdisk = 0; devicetreeblob = 0; max_addr = 0xFFFFFFFFFFFFFFFFULL; hole_addr = 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: devicetreeblob = optarg; break; case OPT_ARGS_IGNORE: break; } } if (!cmdline) fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n"); if (ramdisk && reuse_initrd) die("Can't specify --ramdisk or --initrd with --reuseinitrd\n"); /* Need to append some command line parameters internally in case of * taking crash dumps. */ if (info->kexec_flags & KEXEC_ON_CRASH) { modified_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); if (cmdline) { strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } } /* Parse the Elf file */ result = build_elf_exec_info(buf, len, &ehdr, 0); if (result < 0) { free_elf_info(&ehdr); return result; } /* Load the Elf data. Physical load addresses in elf64 header do not * show up correctly. Use user supplied address for now to patch the * elf header */ phdr = &ehdr.e_phdr[0]; size = phdr->p_filesz; if (size > phdr->p_memsz) size = phdr->p_memsz; my_kernel = hole_addr = locate_hole(info, size, 0, 0, max_addr, 1); ehdr.e_phdr[0].p_paddr = hole_addr; result = elf_exec_load(&ehdr, info); if (result < 0) { free_elf_info(&ehdr); return result; } /* If panic kernel is being loaded, additional segments need * to be created. */ if (info->kexec_flags & KEXEC_ON_CRASH) { result = load_crashdump_segments(info, modified_cmdline, max_addr, 0); if (result < 0) return -1; /* Use new command line. */ cmdline = modified_cmdline; } /* Add v2wrap to the current image */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, max_addr, 1, 0); /* Add a ram-disk to the current image * Note: Add the ramdisk after elf_rel_build_load */ if (ramdisk) { if (devicetreeblob) { fprintf(stderr, "Can't use ramdisk with device tree blob input\n"); return -1; } seg_buf = slurp_file(ramdisk, &seg_size); hole_addr = add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, 1); initrd_base = hole_addr; initrd_size = seg_size; } /* ramdisk */ if (devicetreeblob) { /* Grab device tree from buffer */ seg_buf = slurp_file(devicetreeblob, &seg_size); } else { /* create from fs2dt */ create_flatten_tree(&seg_buf, &seg_size, cmdline); } result = fixup_dt(&seg_buf, &seg_size); if (result < 0) return result; my_dt_offset = add_buffer(info, seg_buf, seg_size, seg_size, 0, 0, max_addr, -1); #ifdef NEED_RESERVE_DTB /* patch reserve map address for flattened device-tree * find last entry (both 0) in the reserve mem list. Assume DT * entry is before this one */ bb_ptr = (struct bootblock *)(seg_buf); rsvmap_ptr = (uint64_t *)(seg_buf + be32_to_cpu(bb_ptr->off_mem_rsvmap)); while (*rsvmap_ptr || *(rsvmap_ptr+1)) rsvmap_ptr += 2; rsvmap_ptr -= 2; *rsvmap_ptr = cpu_to_be64(my_dt_offset); rsvmap_ptr++; *rsvmap_ptr = cpu_to_be64((uint64_t)be32_to_cpu(bb_ptr->totalsize)); #endif if (read_prop("/proc/device-tree/ibm,opal/opal-base-address", &my_opal_base, sizeof(my_opal_base)) == 0) { my_opal_base = be64_to_cpu(my_opal_base); elf_rel_set_symbol(&info->rhdr, "opal_base", &my_opal_base, sizeof(my_opal_base)); } if (read_prop("/proc/device-tree/ibm,opal/opal-entry-address", &my_opal_entry, sizeof(my_opal_entry)) == 0) { my_opal_entry = be64_to_cpu(my_opal_entry); elf_rel_set_symbol(&info->rhdr, "opal_entry", &my_opal_entry, sizeof(my_opal_entry)); } /* Set kernel */ elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); /* Set dt_offset */ elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset, sizeof(my_dt_offset)); /* get slave code from new kernel, put in purgatory */ elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code, sizeof(slave_code)); master_entry = slave_code[0]; memcpy(slave_code, phdr->p_data, sizeof(slave_code)); slave_code[0] = master_entry; elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code, sizeof(slave_code)); if (info->kexec_flags & KEXEC_ON_CRASH) { my_panic_kernel = 1; /* Set panic flag */ elf_rel_set_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, sizeof(my_panic_kernel)); /* Set backup address */ my_backup_start = info->backup_start; elf_rel_set_symbol(&info->rhdr, "backup_start", &my_backup_start, sizeof(my_backup_start)); /* Tell relocatable kernel to run at load address * via word before slave code in purgatory */ elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load, sizeof(my_run_at_load)); if (my_run_at_load == KERNEL_RUN_AT_ZERO_MAGIC) my_run_at_load = 1; /* else it should be a fixed offset image */ elf_rel_set_symbol(&info->rhdr, "run_at_load", &my_run_at_load, sizeof(my_run_at_load)); } /* Set stack address */ my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1); my_stack += 16*1024; elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); /* Set toc */ toc_addr = my_r2(&info->rhdr); elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); /* Set debug */ elf_rel_set_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); my_kernel = 0; my_dt_offset = 0; my_panic_kernel = 0; my_backup_start = 0; my_stack = 0; toc_addr = 0; my_run_at_load = 0; my_debug = 0; my_opal_base = 0; my_opal_entry = 0; elf_rel_get_symbol(&info->rhdr, "opal_base", &my_opal_base, sizeof(my_opal_base)); elf_rel_get_symbol(&info->rhdr, "opal_entry", &my_opal_entry, sizeof(my_opal_entry)); elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset, sizeof(my_dt_offset)); elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load, sizeof(my_run_at_load)); elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, sizeof(my_panic_kernel)); elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start, sizeof(my_backup_start)); elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); elf_rel_get_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); dbgprintf("info->entry is %p\n", info->entry); dbgprintf("kernel is %llx\n", (unsigned long long)my_kernel); dbgprintf("dt_offset is %llx\n", (unsigned long long)my_dt_offset); dbgprintf("run_at_load flag is %x\n", my_run_at_load); dbgprintf("panic_kernel is %x\n", my_panic_kernel); dbgprintf("backup_start is %llx\n", (unsigned long long)my_backup_start); dbgprintf("stack is %llx\n", (unsigned long long)my_stack); dbgprintf("toc_addr is %llx\n", (unsigned long long)toc_addr); dbgprintf("purgatory size is %zu\n", purgatory_size); dbgprintf("debug is %d\n", my_debug); dbgprintf("opal_base is %llx\n", (unsigned long long) my_opal_base); dbgprintf("opal_entry is %llx\n", (unsigned long long) my_opal_entry); return 0; }
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { unsigned long base; unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ unsigned int offset = 0x8000; /* 32k offset from memory start */ const char *command_line; char *modified_cmdline = NULL; off_t command_line_len; const char *ramdisk; char *ramdisk_buf; off_t ramdisk_length; off_t ramdisk_offset; int opt; /* See options.h -- add any more there, too. */ static const struct option options[] = { KEXEC_ARCH_OPTIONS { "command-line", 1, 0, OPT_APPEND }, { "append", 1, 0, OPT_APPEND }, { "initrd", 1, 0, OPT_RAMDISK }, { "ramdisk", 1, 0, OPT_RAMDISK }, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:"; /* * Parse the command line arguments */ command_line = 0; command_line_len = 0; ramdisk = 0; ramdisk_buf = 0; ramdisk_length = 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 '?': usage(); return -1; case OPT_APPEND: command_line = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; } } if (command_line) { command_line_len = strlen(command_line) + 1; if (command_line_len > COMMAND_LINE_SIZE) command_line_len = COMMAND_LINE_SIZE; } if (ramdisk) { ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); } /* * If we are loading a dump capture kernel, we need to update kernel * command line and also add some additional segments. */ if (info->kexec_flags & KEXEC_ON_CRASH) { uint64_t start, end; modified_cmdline = xmalloc(COMMAND_LINE_SIZE); if (!modified_cmdline) return -1; if (command_line) { (void) strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } if (load_crashdump_segments(info, modified_cmdline) < 0) { free(modified_cmdline); return -1; } command_line = modified_cmdline; command_line_len = strlen(command_line) + 1; /* * We put the dump capture kernel at the start of crashkernel * reserved memory. */ if (parse_iomem_single("Crash kernel\n", &start, &end)) { /* * No crash kernel memory reserved. We cannot do more * but just bail out. */ return -1; } base = start; } else { base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX); } if (base == ULONG_MAX) return -1; /* assume the maximum kernel compression ratio is 4, * and just to be safe, place ramdisk after that */ ramdisk_offset = base + len * 4; if (atag_arm_load(info, base + atag_offset, command_line, command_line_len, ramdisk_buf, ramdisk_length, ramdisk_offset) == -1) return -1; add_segment(info, buf, len, base + offset, len); info->entry = (void*)base + offset; return 0; }
int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { unsigned long base; unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ unsigned int offset = 0x8000; /* 32k offset from memory start */ const char *command_line; char *modified_cmdline = NULL; off_t command_line_len; const char *ramdisk; char *ramdisk_buf; int opt; int use_atags; char *dtb_buf; off_t dtb_length; char *dtb_file; off_t dtb_offset; char *end; /* See options.h -- add any more there, too. */ static const struct option options[] = { KEXEC_ARCH_OPTIONS { "command-line", 1, 0, OPT_APPEND }, { "append", 1, 0, OPT_APPEND }, { "initrd", 1, 0, OPT_RAMDISK }, { "ramdisk", 1, 0, OPT_RAMDISK }, { "dtb", 1, 0, OPT_DTB }, { "atags", 0, 0, OPT_ATAGS }, { "image-size", 1, 0, OPT_IMAGE_SIZE }, { "atags-file", 1, 0, OPT_ATAGS }, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "a:r:"; /* * Parse the command line arguments */ command_line = 0; command_line_len = 0; ramdisk = 0; ramdisk_buf = 0; initrd_size = 0; use_atags = 0; dtb_file = NULL; 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: command_line = optarg; break; case OPT_RAMDISK: ramdisk = optarg; break; case OPT_DTB: dtb_file = optarg; break; case OPT_ATAGS: use_atags = 1; break; case OPT_IMAGE_SIZE: kexec_arm_image_size = strtoul(optarg, &end, 0); break; case OPT_ATAGS_FILE: atags_file = optarg; break; } } if (use_atags && dtb_file) { fprintf(stderr, "You can only use ATAGs if you don't specify a " "dtb file.\n"); return -1; } if (command_line) { command_line_len = strlen(command_line) + 1; if (command_line_len > COMMAND_LINE_SIZE) command_line_len = COMMAND_LINE_SIZE; } if (ramdisk) { ramdisk_buf = slurp_file(ramdisk, &initrd_size); } /* * If we are loading a dump capture kernel, we need to update kernel * command line and also add some additional segments. */ if (info->kexec_flags & KEXEC_ON_CRASH) { uint64_t start, end; modified_cmdline = xmalloc(COMMAND_LINE_SIZE); if (!modified_cmdline) return -1; if (command_line) { (void) strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } if (load_crashdump_segments(info, modified_cmdline) < 0) { free(modified_cmdline); return -1; } command_line = modified_cmdline; command_line_len = strlen(command_line) + 1; /* * We put the dump capture kernel at the start of crashkernel * reserved memory. */ if (parse_iomem_single("Crash kernel\n", &start, &end)) { /* * No crash kernel memory reserved. We cannot do more * but just bail out. */ return -1; } base = start; } else { base = locate_hole(info,len+offset,0,0,ULONG_MAX,INT_MAX); } if (base == ULONG_MAX) return -1; printf("Kernel segment stats: %lx (%ld)\n", base, len); if (kexec_arm_image_size) { /* If the image size was passed as command line argument, * use that value for determining the address for initrd, * atags and dtb images. page-align the given length.*/ initrd_base = base + _ALIGN(kexec_arm_image_size, getpagesize()); } else { /* Otherwise, assume the maximum kernel compression ratio * is 4, and just to be safe, place ramdisk after that */ initrd_base = base + _ALIGN(len * 4, getpagesize()); } if (use_atags) { /* * use ATAGs from /proc/atags */ if (atag_arm_load(info, base + atag_offset, command_line, command_line_len, ramdisk_buf, initrd_size, initrd_base) == -1) return -1; } else { /* * Read a user-specified DTB file. */ if (dtb_file) { dtb_buf = slurp_file(dtb_file, &dtb_length); if (fdt_check_header(dtb_buf) != 0) { fprintf(stderr, "Invalid FDT buffer.\n"); return -1; } if (command_line) { /* * Error should have been reported so * directly return -1 */ if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen", "bootargs", command_line, strlen(command_line) + 1)) return -1; } } else { /* * Extract the DTB from /proc/device-tree. */ create_flatten_tree(&dtb_buf, &dtb_length, command_line); } if (base + atag_offset + dtb_length > base + offset) { fprintf(stderr, "DTB too large!\n"); return -1; } if (ramdisk) { add_segment(info, ramdisk_buf, initrd_size, initrd_base, initrd_size); unsigned long start, end; start = cpu_to_be32((unsigned long)(initrd_base)); end = cpu_to_be32((unsigned long)(initrd_base + initrd_size)); if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen", "linux,initrd-start", &start, sizeof(start))) return -1; if (setup_dtb_prop(&dtb_buf, &dtb_length, "/chosen", "linux,initrd-end", &end, sizeof(end))) return -1; } /* Stick the dtb at the end of the initrd and page * align it. */ dtb_offset = initrd_base + initrd_size + getpagesize(); dtb_offset = _ALIGN_DOWN(dtb_offset, getpagesize()); add_segment(info, dtb_buf, dtb_length, dtb_offset, dtb_length); } printf("Kernel segment info: %lx (%d)\n", base+offset, len); add_segment(info, buf, len, base + offset, len); info->entry = (void*)base + offset; return 0; }
int do_bzImage_load(struct kexec_info *info, const char *kernel, off_t kernel_len, const char *command_line, off_t command_line_len, const char *initrd, off_t initrd_len, int real_mode_entry, int debug) { struct x86_linux_header setup_header; struct x86_linux_param_header *real_mode; int setup_sects; char *kernel_version; size_t size; int kern16_size; unsigned long setup_base, setup_size; struct entry32_regs regs32; struct entry16_regs regs16; unsigned int relocatable_kernel = 0; unsigned long kernel32_load_addr; char *modified_cmdline; /* * Find out about the file I am about to load. */ if (kernel_len < sizeof(setup_header)) { return -1; } memcpy(&setup_header, kernel, sizeof(setup_header)); setup_sects = setup_header.setup_sects; if (setup_sects == 0) { setup_sects = 4; } kern16_size = (setup_sects +1) *512; kernel_version = ((unsigned char *)&setup_header) + 512 + setup_header.kver_addr; if (kernel_len < kern16_size) { fprintf(stderr, "BzImage truncated?\n"); return -1; } if (setup_header.protocol_version >= 0x0205) { relocatable_kernel = setup_header.relocatable_kernel; dfprintf(stdout, "bzImage is relocatable\n"); } /* Can't use bzImage for crash dump purposes with real mode entry */ if((info->kexec_flags & KEXEC_ON_CRASH) && real_mode_entry) { fprintf(stderr, "Can't use bzImage for crash dump purposes" " with real mode entry\n"); return -1; } if((info->kexec_flags & KEXEC_ON_CRASH) && !relocatable_kernel) { fprintf(stderr, "BzImage is not relocatable. Can't be used" " as capture kernel.\n"); return -1; } /* Need to append some command line parameters internally in case of * taking crash dumps. */ if (info->kexec_flags & KEXEC_ON_CRASH) { modified_cmdline = xmalloc(COMMAND_LINE_SIZE); memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); if (command_line) { strncpy(modified_cmdline, command_line, COMMAND_LINE_SIZE); modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; } /* If panic kernel is being loaded, additional segments need * to be created. load_crashdump_segments will take care of * loading the segments as high in memory as possible, hence * in turn as away as possible from kernel to avoid being * stomped by the kernel. */ if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0) return -1; /* Use new command line buffer */ command_line = modified_cmdline; command_line_len = strlen(command_line) +1; } /* Load the trampoline. This must load at a higher address * the the argument/parameter segment or the kernel will stomp * it's gdt. * * x86_64 purgatory code has got relocations type R_X86_64_32S * that means purgatory got to be loaded within first 2G otherwise * overflow takes place while applying relocations. */ if (!real_mode_entry && relocatable_kernel) elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0x3000, 0x7fffffff, -1, 0); else elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0x3000, 640*1024, -1, 0); dfprintf(stdout, "Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr); /* The argument/parameter segment */ setup_size = kern16_size + command_line_len; real_mode = xmalloc(setup_size); memcpy(real_mode, kernel, kern16_size); if (info->kexec_flags & KEXEC_ON_CRASH) { /* If using bzImage for capture kernel, then we will not be * executing real mode code. setup segment can be loaded * anywhere as we will be just reading command line. */ setup_base = add_buffer(info, real_mode, setup_size, setup_size, 16, 0x3000, -1, 1); } else if (real_mode->protocol_version >= 0x0200) { /* Careful setup_base must be greater than 8K */ setup_base = add_buffer(info, real_mode, setup_size, setup_size, 16, 0x3000, 640*1024, 1); } else { add_segment(info, real_mode, setup_size, SETUP_BASE, setup_size); setup_base = SETUP_BASE; } dfprintf(stdout, "Loaded real-mode code and command line at 0x%lx\n", setup_base); /* Verify purgatory loads higher than the parameters */ if (info->rhdr.rel_addr < setup_base) { die("Could not put setup code above the kernel parameters\n"); } /* The main kernel segment */ size = kernel_len - kern16_size; if (real_mode->protocol_version >=0x0205 && relocatable_kernel) { /* Relocatable bzImage */ unsigned long kern_align = real_mode->kernel_alignment; unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX; if (real_mode->protocol_version >= 0x0203) { if (kernel32_max_addr > real_mode->initrd_addr_max) kernel32_max_addr = real_mode->initrd_addr_max; } kernel32_load_addr = add_buffer(info, kernel + kern16_size, size, size, kern_align, 0x100000, kernel32_max_addr, 1); } else { kernel32_load_addr = KERN32_BASE; add_segment(info, kernel + kern16_size, size, kernel32_load_addr, size); } dfprintf(stdout, "Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr); /* Tell the kernel what is going on */ setup_linux_bootloader_parameters(info, real_mode, setup_base, kern16_size, command_line, command_line_len, initrd, initrd_len); /* Get the initial register values */ elf_rel_get_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16)); elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); /* * Initialize the 32bit start information. */ regs32.eax = 0; /* unused */ regs32.ebx = 0; /* 0 == boot not AP processor start */ regs32.ecx = 0; /* unused */ regs32.edx = 0; /* unused */ regs32.esi = setup_base; /* kernel parameters */ regs32.edi = 0; /* unused */ regs32.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* stack, unused */ regs32.ebp = 0; /* unused */ regs32.eip = kernel32_load_addr; /* kernel entry point */ /* * Initialize the 16bit start information. */ regs16.cs = (setup_base>>4) + 0x20; regs16.ip = 0; regs16.ss = (elf_rel_get_addr(&info->rhdr, "stack_end") - 64*1024) >> 4; regs16.esp = 0xFFFC; if (real_mode_entry) { printf("Starting the kernel in real mode\n"); regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16"); } if (real_mode_entry && debug) { unsigned long entry16_debug, pre32, first32; uint32_t old_first32; /* Find the location of the symbols */ entry16_debug = elf_rel_get_addr(&info->rhdr, "entry16_debug"); pre32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_pre32"); first32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_first32"); /* Hook all of the linux kernel hooks */ real_mode->rmode_switch_cs = entry16_debug >> 4; real_mode->rmode_switch_ip = pre32 - entry16_debug; old_first32 = real_mode->kernel_start; real_mode->kernel_start = first32; elf_rel_set_symbol(&info->rhdr, "entry16_debug_old_first32", &old_first32, sizeof(old_first32)); regs32.eip = entry16_debug; }