#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;
}