#include <errno.h> #include <getopt.h> #include <fcntl.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include "kexec.h" // Offsets same as in kernel asm/kexec.h #define KEXEC_ARM_ATAGS_OFFSET 0x1000 #define KEXEC_ARM_ZIMAGE_OFFSET 0x8000 #define MEMORY_SIZE 0x0800000 // Physical buffer address cannot overlap with other regions #define START_ADDRESS 0x44000000 #define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1))) /* * Gives file position and resets current position to begining of file */ int get_file_size(int f) { struct stat st; fstat(f, &st); return st.st_size; } int test_kexeccall() { int rv; rv = kexec_load(0, 0, NULL, KEXEC_ARCH_DEFAULT); if (rv != 0) { printf("ERROR: kexec_load: %d \n", errno); return 1; } printf("Kexec test: Success \n"); return 0; } void usage(void) { fprintf(stderr, "usage: kexecload [ <option> ] <atags path> <kernel path>\n" "\n" "options:\n" " -t tests syscall\n" " -s <start address> specify start address of kernel\n" ); } /* * Loads kexec into the kernel and sets kexec on crash */ int main(int argc, char *argv[]) { int rv; int atag_file, zimage_file; int atag_size, zimage_size, total_size; void *atag_buffer; void *zimage_buffer; struct kexec_segment segment[2]; int page_size = getpagesize(); void *start_address = (void *)START_ADDRESS; int c; const struct option longopts[] = { {"start_address", required_argument, 0, 's'}, {"test", 0, 0, 't'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; c = getopt_long(argc, argv, "s:th", longopts, NULL); if (c < 0) { break; } /* Alphabetical cases */ switch (c) { case 's': start_address = (void *) strtoul(optarg, 0, 16); break; case 'h': usage(); return 1; case 't': test_kexeccall(); return 1; case '?': return 1; default: abort(); } } argc -= optind; argv += optind; if (argc < 2) { usage(); return 1; } atag_file = open(argv[0], O_RDONLY); zimage_file = open(argv[1], O_RDONLY); if (atag_file < 0 || zimage_file < 0) { fprintf(stderr, "Error during opening of atag file or the zImage file %s\n", strerror(errno)); return 1; } atag_size = ROUND_TO_PAGE(get_file_size(atag_file), page_size); zimage_size = ROUND_TO_PAGE(get_file_size(zimage_file), page_size); if (atag_size >= KEXEC_ARM_ZIMAGE_OFFSET - KEXEC_ARM_ATAGS_OFFSET) { fprintf(stderr, "Atag file is too large\n"); return 1; } atag_buffer = (char *) mmap(NULL, atag_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, atag_file, 0); zimage_buffer = (char *) mmap(NULL, zimage_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, zimage_file, 0); if(atag_buffer == MAP_FAILED || zimage_buffer == MAP_FAILED) { fprintf(stderr, "Unable to map files into memory"); return 1; } segment[0].buf = zimage_buffer; segment[0].bufsz = zimage_size; segment[0].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET); segment[0].memsz = zimage_size; segment[1].buf = atag_buffer; segment[1].bufsz = atag_size; segment[1].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ATAGS_OFFSET); segment[1].memsz = atag_size; rv = kexec_load(((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET), 2, (void *) segment, KEXEC_ARCH_DEFAULT | KEXEC_ON_CRASH); if (rv != 0) { fprintf(stderr, "Kexec_load returned non-zero exit code: %d with errno %d\n", rv, errno); return 1; } printf("Done! Kexec loaded\n"); printf("New kernel should start at 0x%08x\n", START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET); return 0; }