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 multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) /* Marshal up a multiboot-style kernel */ { struct multiboot_info *mbi; void *mbi_buf; struct mod_list *modp; unsigned long freespace; unsigned long long mem_lower = 0, mem_upper = 0; struct mem_ehdr ehdr; unsigned long mbi_base; struct entry32_regs regs; size_t mbi_bytes, mbi_offset; char *command_line = NULL; char *imagename, *cp, *append = NULL;; struct memory_range *range; int ranges; struct AddrRangeDesc *mmap; int command_line_len; int i; uint32_t u; int opt; int modules, mod_command_line_space; /* See options.h -- add any more there, too. */ static const struct option options[] = { KEXEC_ARCH_OPTIONS { "command-line", 1, 0, OPT_CL }, { "append", 1, 0, OPT_CL }, { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, { "module", 1, 0, OPT_MOD }, { 0, 0, 0, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR ""; /* Probe for the MB header if it's not already found */ if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) { fprintf(stderr, "Cannot find a loadable multiboot header.\n"); return -1; } /* Parse the command line */ command_line = ""; command_line_len = 0; modules = 0; mod_command_line_space = 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_CL: append = optarg; break; case OPT_REUSE_CMDLINE: command_line = get_command_line(); break; case OPT_MOD: modules++; mod_command_line_space += strlen(optarg) + 1; break; } } imagename = argv[optind]; command_line = concat_cmdline(command_line, append); command_line_len = strlen(command_line) + strlen(imagename) + 2; /* Load the ELF executable */ elf_exec_build_load(info, &ehdr, buf, len, 0); /* Load the setup code */ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, ULONG_MAX, 1, 0); /* The first segment will contain the multiboot headers: * ============= * multiboot information (mbi) * ------------- * kernel command line * ------------- * bootloader name * ------------- * module information entries * ------------- * module command lines * ============== */ mbi_bytes = (sizeof(*mbi) + command_line_len + strlen (BOOTLOADER " " BOOTLOADER_VERSION) + 1 + 3) & ~3; mbi_buf = xmalloc(mbi_bytes); mbi = mbi_buf; memset(mbi, 0, sizeof(*mbi)); sprintf(((char *)mbi) + sizeof(*mbi), "%s %s", imagename, command_line); sprintf(((char *)mbi) + sizeof(*mbi) + command_line_len, "%s", BOOTLOADER " " BOOTLOADER_VERSION); mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME; /* We'll relocate these to absolute addresses later. For now, * all addresses within the first segment are relative to the * start of the MBI. */ mbi->cmdline = sizeof(*mbi); mbi->boot_loader_name = sizeof(*mbi) + command_line_len; /* Memory map */ if ((get_memory_ranges(&range, &ranges, info->kexec_flags) < 0) || ranges == 0) { fprintf(stderr, "Cannot get memory information\n"); return -1; } mmap = xmalloc(ranges * sizeof(*mmap)); for (i=0; i<ranges; i++) { unsigned long long length; length = range[i].end - range[i].start; /* Translate bzImage mmap to multiboot-speak */ mmap[i].size = sizeof(mmap[i]) - 4; mmap[i].base_addr_low = range[i].start & 0xffffffff; mmap[i].base_addr_high = range[i].start >> 32; mmap[i].length_low = length & 0xffffffff; mmap[i].length_high = length >> 32; if (range[i].type == RANGE_RAM) { mmap[i].Type = 1; /* RAM */ /* Is this the "low" memory? */ if ((range[i].start == 0) && (range[i].end > mem_lower)) mem_lower = range[i].end; /* Is this the "high" memory? */ if ((range[i].start <= 0x100000) && (range[i].end > mem_upper + 0x100000)) mem_upper = range[i].end - 0x100000; } else mmap[i].Type = 0xbad; /* Not RAM */ } if (mbh->flags & MULTIBOOT_MEMORY_INFO) { /* Provide a copy of the memory map to the kernel */ mbi->flags |= MB_INFO_MEMORY | MB_INFO_MEM_MAP; freespace = add_buffer(info, mmap, ranges * sizeof(*mmap), ranges * sizeof(*mmap), 4, 0, 0xFFFFFFFFUL, 1); mbi->mmap_addr = freespace; mbi->mmap_length = ranges * sizeof(*mmap); /* For kernels that care naught for fancy memory maps * and just want the size of low and high memory */ mbi->mem_lower = MIN(mem_lower>>10, 0xffffffff); mbi->mem_upper = MIN(mem_upper>>10, 0xffffffff); /* done */ }