summaryrefslogtreecommitdiff
path: root/kexec_tools/kexecload.c
blob: cef0147506d8568b9476d94a0cfc8aad37411fd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.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;
    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) {
        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;
}