/* * kexec-elf-m68k.c - kexec Elf loader for m68k * * Copyright (C) 2013 Geert Uytterhoeven * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "../../kexec.h" #include "../../kexec-elf.h" #include "../../kexec-syscall.h" #include "kexec-m68k.h" #include "bootinfo.h" #include #define KiB * 1024 #define MiB * 1024 KiB #define PAGE_SIZE 4 KiB int elf_m68k_probe(const char *buf, off_t len) { struct mem_ehdr ehdr; int result; result = build_elf_exec_info(buf, len, &ehdr, 0); if (result < 0) goto out; /* Verify the architecuture specific bits */ if (ehdr.e_machine != EM_68K) { /* for a different architecture */ fprintf(stderr, "Not for this architecture.\n"); result = -1; goto out; } result = 0; out: free_elf_info(&ehdr); return result; } void elf_m68k_usage(void) { printf(" --command-line=STRING Set the kernel command line to STRING\n" " --append=STRING Set the kernel command line to STRING\n" " --reuse-cmdline Use kernel command line from running system.\n" " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" " --bootinfo=FILE Use FILE as the kernel's bootinfo\n" ); } static unsigned long segment_end(const struct kexec_info *info, int i) { return (unsigned long)info->segment[i].mem + info->segment[i].memsz - 1; } int elf_m68k_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info) { struct mem_ehdr ehdr; const char *cmdline = NULL, *ramdisk_file = NULL; int opt, result, i; unsigned long bootinfo_addr, ramdisk_addr = 0; off_t ramdisk_size = 0; /* See options.h if adding any more options. */ 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 }, { "ramdisk", 1, NULL, OPT_RAMDISK }, { "initrd", 1, NULL, OPT_RAMDISK }, { "bootinfo", 1, NULL, OPT_BOOTINFO }, { 0, 0, NULL, 0 }, }; static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; 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_REUSE_CMDLINE: cmdline = get_command_line(); break; case OPT_RAMDISK: ramdisk_file = optarg; break; case OPT_BOOTINFO: break; } } result = build_elf_exec_info(buf, len, &ehdr, 0); if (result < 0) die("ELF exec parse failed\n"); /* Fixup PT_LOAD segments that include the ELF header (offset zero) */ 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_offset) continue; dbgprintf("Removing ELF header from segment %d\n", i); phdr->p_paddr += PAGE_SIZE; phdr->p_vaddr += PAGE_SIZE; phdr->p_filesz -= PAGE_SIZE; phdr->p_memsz -= PAGE_SIZE; phdr->p_offset += PAGE_SIZE; phdr->p_data += PAGE_SIZE; } /* 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); /* Bootinfo must be stored right after the kernel */ bootinfo_addr = segment_end(info, info->nr_segments - 1) + 1; /* Load ramdisk */ if (ramdisk_file) { void *ramdisk = slurp_decompress_file(ramdisk_file, &ramdisk_size); /* Store ramdisk at top of first memory chunk */ ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end - ramdisk_size + 1, PAGE_SIZE); if (!buf) die("Ramdisk load failed\n"); add_buffer(info, ramdisk, ramdisk_size, ramdisk_size, PAGE_SIZE, ramdisk_addr, info->memory_range[0].end, 1); } /* Update and add bootinfo */ bootinfo_set_cmdline(cmdline); bootinfo_set_ramdisk(ramdisk_addr, ramdisk_size); bootinfo_add_rng_seed(); if (kexec_debug) bootinfo_print(); add_bootinfo(info, bootinfo_addr); /* * Check if the kernel (and bootinfo) exceed 4 MiB, as current kernels * don't support that. * As the segments are still unsorted, the bootinfo is located in the * last segment. */ if (segment_end(info, info->nr_segments - 1) >= virt_to_phys(4 MiB - 1)) printf("WARNING: Kernel is larger than 4 MiB\n"); /* Check struct bootversion at start of kernel */ bootinfo_check_bootversion(info); return 0; }