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