Home | History | Annotate | Download | only in commands
      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 
     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  */
     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 
    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 
    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