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 */
get_file_size(int f)29 int get_file_size(int f)
30 {
31 struct stat st;
32 fstat(f, &st);
33 return st.st_size;
34 }
35
test_kexeccall()36 int 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
usage(void)51 void 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 */
main(int argc,char * argv[])65 int main(int argc, char *argv[])
66 {
67 int rv;
68 int atag_file,
69 zimage_file;
70 int atag_size,
71 zimage_size;
72 void *atag_buffer;
73 void *zimage_buffer;
74 struct kexec_segment segment[2];
75 int page_size = getpagesize();
76 void *start_address = (void *)START_ADDRESS;
77 int c;
78
79 const struct option longopts[] = {
80 {"start_address", required_argument, 0, 's'},
81 {"test", 0, 0, 't'},
82 {"help", 0, 0, 'h'},
83 {0, 0, 0, 0}
84 };
85
86 while (1) {
87 c = getopt_long(argc, argv, "s:th", longopts, NULL);
88 if (c < 0) {
89 break;
90 }
91 /* Alphabetical cases */
92 switch (c) {
93 case 's':
94 start_address = (void *) strtoul(optarg, 0, 16);
95 break;
96 case 'h':
97 usage();
98 return 1;
99 case 't':
100 test_kexeccall();
101 return 1;
102 case '?':
103 return 1;
104 default:
105 abort();
106 }
107 }
108
109 argc -= optind;
110 argv += optind;
111
112 if (argc < 2) {
113 usage();
114 return 1;
115 }
116
117 atag_file = open(argv[0], O_RDONLY);
118 zimage_file = open(argv[1], O_RDONLY);
119
120 if (atag_file < 0 || zimage_file < 0) {
121 fprintf(stderr, "Error during opening of atag file or the zImage file %s\n", strerror(errno));
122 return 1;
123 }
124
125 atag_size = ROUND_TO_PAGE(get_file_size(atag_file), page_size);
126 zimage_size = ROUND_TO_PAGE(get_file_size(zimage_file), page_size);
127
128 if (atag_size >= KEXEC_ARM_ZIMAGE_OFFSET - KEXEC_ARM_ATAGS_OFFSET) {
129 fprintf(stderr, "Atag file is too large\n");
130 return 1;
131 }
132
133 atag_buffer = (char *) mmap(NULL, atag_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, atag_file, 0);
134 zimage_buffer = (char *) mmap(NULL, zimage_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, zimage_file, 0);
135
136 if(atag_buffer == MAP_FAILED || zimage_buffer == MAP_FAILED) {
137 fprintf(stderr, "Unable to map files into memory");
138 return 1;
139 }
140
141 segment[0].buf = zimage_buffer;
142 segment[0].bufsz = zimage_size;
143 segment[0].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET);
144 segment[0].memsz = zimage_size;
145
146 segment[1].buf = atag_buffer;
147 segment[1].bufsz = atag_size;
148 segment[1].mem = (void *) ((uintptr_t) start_address + KEXEC_ARM_ATAGS_OFFSET);
149 segment[1].memsz = atag_size;
150
151 rv = kexec_load(((uintptr_t) start_address + KEXEC_ARM_ZIMAGE_OFFSET),
152 2, (void *) segment, KEXEC_ARCH_DEFAULT | KEXEC_ON_CRASH);
153
154 if (rv != 0) {
155 fprintf(stderr, "Kexec_load returned non-zero exit code: %d with errno %d\n", rv, errno);
156 return 1;
157 }
158
159 printf("Done! Kexec loaded\n");
160 printf("New kernel should start at 0x%08x\n", START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET);
161
162 return 0;
163
164 }
165
166