• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
36 #include <unistd.h>
37 
38 #include "boot.h"
39 #include "debug.h"
40 #include "utils.h"
41 #include "bootimg.h"
42 
43 
44 #define KEXEC_ARM_ATAGS_OFFSET  0x1000
45 #define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
46 
47 #define MEMORY_SIZE 0x0800000
48 #define START_ADDRESS 0x44000000
49 #define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
50 
51 #define ATAG_NONE_TYPE      0x00000000
52 #define ATAG_CORE_TYPE      0x54410001
53 #define ATAG_RAMDISK_TYPE   0x54410004
54 #define ATAG_INITRD2_TYPE   0x54420005
55 #define ATAG_CMDLINE_TYPE   0x54410009
56 
57 #define MAX_ATAG_SIZE 0x4000
58 
59 struct atag_info {
60     unsigned size;
61     unsigned type;
62 };
63 
64 struct atag_initrd2 {
65     unsigned start;
66     unsigned size;
67 };
68 
69 struct atag_cmdline {
70     char cmdline[0];
71 };
72 
73 struct atag {
74     struct atag_info info;
75     union {
76         struct atag_initrd2 initrd2;
77         struct atag_cmdline cmdline;
78     } data;
79 };
80 
81 
kexec_load(unsigned int entry,unsigned long nr_segments,struct kexec_segment * segment,unsigned long flags)82 long kexec_load(unsigned int entry, unsigned long nr_segments,
83                 struct kexec_segment *segment, unsigned long flags) {
84    return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
85 }
86 
87 /*
88  * Prepares arguments for kexec
89  * Kernel address is not set into kernel_phys
90  * Ramdisk is set to position relative to kernel
91  */
prepare_boot_linux(uintptr_t kernel_phys,void * kernel_addr,int kernel_size,uintptr_t ramdisk_phys,void * ramdisk_addr,int ramdisk_size,uintptr_t second_phys,void * second_addr,int second_size,uintptr_t atags_phys,void * atags_addr,int atags_size)92 int prepare_boot_linux(uintptr_t kernel_phys, void *kernel_addr, int kernel_size,
93                        uintptr_t ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
94                        uintptr_t second_phys, void *second_addr, int second_size,
95                        uintptr_t atags_phys, void *atags_addr, int atags_size) {
96     struct kexec_segment segment[4];
97     int segment_count = 2;
98     unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
99     int rv;
100     int page_size = getpagesize();
101 
102     segment[0].buf = kernel_addr;
103     segment[0].bufsz = kernel_size;
104     segment[0].mem = (void *) KERNEL_START;
105     segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
106 
107     if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
108         D(INFO, "Kernel image too big");
109         return -1;
110     }
111 
112     segment[1].buf = atags_addr;
113     segment[1].bufsz = atags_size;
114     segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
115     segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
116 
117     D(INFO, "Ramdisk size is %d", ramdisk_size);
118 
119     if (ramdisk_size != 0) {
120         segment[segment_count].buf = ramdisk_addr;
121         segment[segment_count].bufsz = ramdisk_size;
122         segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
123         segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
124         ++segment_count;
125     }
126 
127     D(INFO, "Ramdisk size is %d", ramdisk_size);
128     if (second_size != 0) {
129         segment[segment_count].buf = second_addr;
130         segment[segment_count].bufsz = second_size;
131         segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
132         segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
133         entry = second_phys;
134         ++segment_count;
135     }
136 
137     rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
138 
139     if (rv != 0) {
140         D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
141         return -1;
142     }
143 
144     return 1;
145 
146 }
147 
create_atags(unsigned * atags_position,int atag_size,const struct boot_img_hdr * hdr,int * size)148 unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
149     struct atag *current_tag = (struct atag *) atags_position;
150     unsigned *current_tag_raw = atags_position;
151     unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
152                                                hdr->page_size));
153     //This pointer will point into the beggining of buffer free space
154     unsigned *natags_raw_buff = new_atags;
155     int new_atags_size = 0;
156     int current_size;
157     int cmdl_length;
158 
159     // copy tags from current atag file
160     while (current_tag->info.type != ATAG_NONE_TYPE) {
161         switch (current_tag->info.type) {
162             case ATAG_CMDLINE_TYPE:
163             case ATAG_RAMDISK_TYPE:
164             case ATAG_INITRD2_TYPE: break;
165             default:
166                 memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
167                 natags_raw_buff += current_tag->info.size;
168                 new_atags_size += current_tag->info.size;
169         }
170 
171         current_tag_raw += current_tag->info.size;
172         current_tag = (struct atag *) current_tag_raw;
173 
174         if (current_tag_raw >= atags_position + atag_size) {
175             D(ERR, "Critical error in atags");
176             return NULL;
177         }
178     }
179 
180     // set INITRD2 tag
181     if (hdr->ramdisk_size > 0) {
182         current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
183         *((struct atag *) natags_raw_buff) = (struct atag) {
184             .info = {
185                 .size = current_size,
186                 .type = ATAG_INITRD2_TYPE
187             },
188             .data = {
189                 .initrd2 = (struct atag_initrd2) {
190                     .start = hdr->ramdisk_addr,
191                     .size = hdr->ramdisk_size
192                 }
193             }
194         };
195 
196         new_atags_size += current_size;
197         natags_raw_buff += current_size;
198     }
199 
200     // set ATAG_CMDLINE
201     cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
202     current_size = sizeof(struct atag_info) + (1 + cmdl_length);
203     current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
204     *((struct atag *) natags_raw_buff) = (struct atag) {
205         .info = {
206             .size = current_size,
207             .type = ATAG_CMDLINE_TYPE
208         },
209     };
210 
211     //copy cmdline and ensure that there is null character
212     memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
213            (char *) hdr->cmdline, cmdl_length);
214     ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
215 
216     new_atags_size += current_size;
217     natags_raw_buff += current_size;
218 
219     // set ATAG_NONE
220     *((struct atag *) natags_raw_buff) = (struct atag) {
221         .info = {
222             .size = 0,
223             .type = ATAG_NONE_TYPE
224         },
225     };
226     new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
227     natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
228 
229     *size = new_atags_size * sizeof(unsigned);
230     return new_atags;
231 }
232 
read_atags(const char * path,int * atags_sz)233 char *read_atags(const char * path, int *atags_sz) {
234     int afd = -1;
235     char *atags_ptr = NULL;
236 
237     afd = open(path, O_RDONLY);
238     if (afd < 0) {
239         D(ERR, "wrong atags file");
240         return 0;
241     }
242 
243     atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
244     if (atags_ptr == NULL) {
245         D(ERR, "insufficient memory");
246         return 0;
247     }
248 
249     *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
250 
251     close(afd);
252     return atags_ptr;
253 }
254 
255