1#include <errno.h>
2#include <getopt.h>
3#include <fcntl.h>
4#include <sys/mman.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <sys/stat.h>
9#include <sys/syscall.h>
10#include <sys/types.h>
11#include <sys/wait.h>
12#include <unistd.h>
13
14#include "kexec.h"
15
16// Offsets same as in kernel asm/kexec.h
17#define KEXEC_ARM_ATAGS_OFFSET  0x1000
18#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
19
20#define MEMORY_SIZE 0x0800000
21// Physical buffer address cannot overlap with other regions
22#define START_ADDRESS 0x44000000
23
24#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
25
26/*
27 * Gives file position and resets current position to begining of file
28 */
29int get_file_size(int f)
30{
31    struct stat st;
32    fstat(f, &st);
33    return st.st_size;
34}
35
36int test_kexeccall() {
37    int rv;
38
39    rv = kexec_load(0, 0, NULL, KEXEC_ARCH_DEFAULT);
40
41    if (rv != 0) {
42        printf("ERROR: kexec_load: %d \n", errno);
43        return 1;
44    }
45
46    printf("Kexec test: Success \n");
47
48    return 0;
49}
50
51void usage(void)
52{
53    fprintf(stderr,
54            "usage: kexecload [ <option> ] <atags path> <kernel path>\n"
55            "\n"
56            "options:\n"
57            "  -t                                       tests syscall\n"
58            "  -s <start address>                       specify start address of kernel\n"
59        );
60}
61
62/*
63 * Loads kexec into the kernel and sets kexec on crash
64 */
65int main(int argc, char *argv[])
66{
67    int rv;
68    int atag_file,
69        zimage_file;
70    int atag_size,
71        zimage_size,
72        total_size;
73    void *atag_buffer;
74    void *zimage_buffer;
75    struct kexec_segment segment[2];
76    int page_size = getpagesize();
77    void *start_address = (void *)START_ADDRESS;
78    int c;
79
80    const struct option longopts[] = {
81        {"start_address", required_argument, 0, 's'},
82        {"test", 0, 0, 't'},
83        {"help", 0, 0, 'h'},
84        {0, 0, 0, 0}
85    };
86
87    while (1) {
88        int option_index = 0;
89        c = getopt_long(argc, argv, "s:th", longopts, NULL);
90        if (c < 0) {
91            break;
92        }
93        /* Alphabetical cases */
94        switch (c) {
95        case 's':
96            start_address = (void *) strtoul(optarg, 0, 16);
97            break;
98        case 'h':
99            usage();
100            return 1;
101        case 't':
102            test_kexeccall();
103            return 1;
104        case '?':
105            return 1;
106        default:
107            abort();
108        }
109    }
110
111    argc -= optind;
112    argv += optind;
113
114    if (argc < 2) {
115        usage();
116        return 1;
117    }
118
119    atag_file = open(argv[0], O_RDONLY);
120    zimage_file = open(argv[1], O_RDONLY);
121
122    if (atag_file < 0 || zimage_file < 0) {
123        fprintf(stderr, "Error during opening of atag file or the zImage file %s\n", strerror(errno));
124        return 1;
125    }
126
127    atag_size = ROUND_TO_PAGE(get_file_size(atag_file), page_size);
128    zimage_size = ROUND_TO_PAGE(get_file_size(zimage_file), page_size);
129
130    if (atag_size >= KEXEC_ARM_ZIMAGE_OFFSET - KEXEC_ARM_ATAGS_OFFSET) {
131        fprintf(stderr, "Atag file is too large\n");
132        return 1;
133    }
134
135    atag_buffer = (char *) mmap(NULL, atag_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, atag_file, 0);
136    zimage_buffer = (char *) mmap(NULL, zimage_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, zimage_file, 0);
137
138    if(atag_buffer == MAP_FAILED || zimage_buffer == MAP_FAILED) {
139        fprintf(stderr, "Unable to map files into memory");
140        return 1;
141    }
142
143    segment[0].buf = zimage_buffer;
144    segment[0].bufsz = zimage_size;
145    segment[0].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET);
146    segment[0].memsz = zimage_size;
147
148    segment[1].buf = atag_buffer;
149    segment[1].bufsz = atag_size;
150    segment[1].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ATAGS_OFFSET);
151    segment[1].memsz = atag_size;
152
153    rv = kexec_load(((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET),
154                    2, (void *) segment, KEXEC_ARCH_DEFAULT | KEXEC_ON_CRASH);
155
156    if (rv != 0) {
157        fprintf(stderr, "Kexec_load returned non-zero exit code: %d with errno %d\n", rv, errno);
158        return 1;
159    }
160
161    printf("Done! Kexec loaded\n");
162    printf("New kernel should start at 0x%08x\n", START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET);
163
164    return 0;
165
166}
167
168