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
59struct atag_info {
60    unsigned size;
61    unsigned type;
62};
63
64struct atag_initrd2 {
65    unsigned start;
66    unsigned size;
67};
68
69struct atag_cmdline {
70    char cmdline[0];
71};
72
73struct atag {
74    struct atag_info info;
75    union {
76        struct atag_initrd2 initrd2;
77        struct atag_cmdline cmdline;
78    } data;
79};
80
81
82long 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 */
92int 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
148unsigned *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
233char *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