blob: bb939176daf2ac4fa7cbceec730fd2c71c1861aa [file] [log] [blame]
Szymon Starzyckib6c5f282013-07-24 17:08:04 -07001/*
2 * Copyright (c) 2009-2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google, Inc. nor the names of its contributors
15 * may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/syscall.h>
33#include <stdio.h>
34#include <string.h>
35#include <fcntl.h>
Elliott Hughesa744b052015-01-28 11:37:57 -080036#include <malloc.h>
Szymon Starzyckib6c5f282013-07-24 17:08:04 -070037#include <unistd.h>
38
39#include "boot.h"
40#include "debug.h"
41#include "utils.h"
42#include "bootimg.h"
43
44
45#define KEXEC_ARM_ATAGS_OFFSET 0x1000
46#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
47
48#define MEMORY_SIZE 0x0800000
49#define START_ADDRESS 0x44000000
50#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
51
52#define ATAG_NONE_TYPE 0x00000000
53#define ATAG_CORE_TYPE 0x54410001
54#define ATAG_RAMDISK_TYPE 0x54410004
55#define ATAG_INITRD2_TYPE 0x54420005
56#define ATAG_CMDLINE_TYPE 0x54410009
57
58#define MAX_ATAG_SIZE 0x4000
59
60struct atag_info {
61 unsigned size;
62 unsigned type;
63};
64
65struct atag_initrd2 {
66 unsigned start;
67 unsigned size;
68};
69
70struct atag_cmdline {
71 char cmdline[0];
72};
73
74struct atag {
75 struct atag_info info;
76 union {
77 struct atag_initrd2 initrd2;
78 struct atag_cmdline cmdline;
79 } data;
80};
81
82
83long kexec_load(unsigned int entry, unsigned long nr_segments,
84 struct kexec_segment *segment, unsigned long flags) {
85 return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
86}
87
88/*
89 * Prepares arguments for kexec
90 * Kernel address is not set into kernel_phys
91 * Ramdisk is set to position relative to kernel
92 */
Elliott Hughes5d9fe772014-02-05 17:50:35 -080093int prepare_boot_linux(uintptr_t kernel_phys, void *kernel_addr, int kernel_size,
94 uintptr_t ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
95 uintptr_t second_phys, void *second_addr, int second_size,
96 uintptr_t atags_phys, void *atags_addr, int atags_size) {
Szymon Starzyckib6c5f282013-07-24 17:08:04 -070097 struct kexec_segment segment[4];
98 int segment_count = 2;
99 unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
100 int rv;
101 int page_size = getpagesize();
102
103 segment[0].buf = kernel_addr;
104 segment[0].bufsz = kernel_size;
105 segment[0].mem = (void *) KERNEL_START;
106 segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
107
108 if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
109 D(INFO, "Kernel image too big");
110 return -1;
111 }
112
113 segment[1].buf = atags_addr;
114 segment[1].bufsz = atags_size;
115 segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
116 segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
117
118 D(INFO, "Ramdisk size is %d", ramdisk_size);
119
120 if (ramdisk_size != 0) {
121 segment[segment_count].buf = ramdisk_addr;
122 segment[segment_count].bufsz = ramdisk_size;
123 segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
124 segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
125 ++segment_count;
126 }
127
128 D(INFO, "Ramdisk size is %d", ramdisk_size);
129 if (second_size != 0) {
130 segment[segment_count].buf = second_addr;
131 segment[segment_count].bufsz = second_size;
132 segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
133 segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
134 entry = second_phys;
135 ++segment_count;
136 }
137
138 rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
139
140 if (rv != 0) {
141 D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
142 return -1;
143 }
144
145 return 1;
146
147}
148
149unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
150 struct atag *current_tag = (struct atag *) atags_position;
151 unsigned *current_tag_raw = atags_position;
152 unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
153 hdr->page_size));
154 //This pointer will point into the beggining of buffer free space
155 unsigned *natags_raw_buff = new_atags;
156 int new_atags_size = 0;
157 int current_size;
158 int cmdl_length;
159
160 // copy tags from current atag file
161 while (current_tag->info.type != ATAG_NONE_TYPE) {
162 switch (current_tag->info.type) {
163 case ATAG_CMDLINE_TYPE:
164 case ATAG_RAMDISK_TYPE:
165 case ATAG_INITRD2_TYPE: break;
166 default:
167 memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
168 natags_raw_buff += current_tag->info.size;
169 new_atags_size += current_tag->info.size;
170 }
171
172 current_tag_raw += current_tag->info.size;
173 current_tag = (struct atag *) current_tag_raw;
174
175 if (current_tag_raw >= atags_position + atag_size) {
176 D(ERR, "Critical error in atags");
177 return NULL;
178 }
179 }
180
181 // set INITRD2 tag
182 if (hdr->ramdisk_size > 0) {
183 current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
184 *((struct atag *) natags_raw_buff) = (struct atag) {
185 .info = {
186 .size = current_size,
187 .type = ATAG_INITRD2_TYPE
188 },
189 .data = {
190 .initrd2 = (struct atag_initrd2) {
191 .start = hdr->ramdisk_addr,
192 .size = hdr->ramdisk_size
193 }
194 }
195 };
196
197 new_atags_size += current_size;
198 natags_raw_buff += current_size;
199 }
200
201 // set ATAG_CMDLINE
202 cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
203 current_size = sizeof(struct atag_info) + (1 + cmdl_length);
204 current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
205 *((struct atag *) natags_raw_buff) = (struct atag) {
206 .info = {
207 .size = current_size,
208 .type = ATAG_CMDLINE_TYPE
209 },
210 };
211
212 //copy cmdline and ensure that there is null character
213 memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
214 (char *) hdr->cmdline, cmdl_length);
215 ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
216
217 new_atags_size += current_size;
218 natags_raw_buff += current_size;
219
220 // set ATAG_NONE
221 *((struct atag *) natags_raw_buff) = (struct atag) {
222 .info = {
223 .size = 0,
224 .type = ATAG_NONE_TYPE
225 },
226 };
227 new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
228 natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
229
230 *size = new_atags_size * sizeof(unsigned);
231 return new_atags;
232}
233
234char *read_atags(const char * path, int *atags_sz) {
235 int afd = -1;
236 char *atags_ptr = NULL;
237
238 afd = open(path, O_RDONLY);
239 if (afd < 0) {
240 D(ERR, "wrong atags file");
241 return 0;
242 }
243
244 atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
245 if (atags_ptr == NULL) {
246 D(ERR, "insufficient memory");
247 return 0;
248 }
249
250 *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
251
252 close(afd);
253 return atags_ptr;
254}
255