255 lines
8.2 KiB
C
255 lines
8.2 KiB
C
/*
|
|
* Copyright (c) 2009-2013, Google Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Google, Inc. nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/syscall.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include "boot.h"
|
|
#include "debug.h"
|
|
#include "utils.h"
|
|
#include "bootimg.h"
|
|
|
|
|
|
#define KEXEC_ARM_ATAGS_OFFSET 0x1000
|
|
#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
|
|
|
|
#define MEMORY_SIZE 0x0800000
|
|
#define START_ADDRESS 0x44000000
|
|
#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
|
|
|
|
#define ATAG_NONE_TYPE 0x00000000
|
|
#define ATAG_CORE_TYPE 0x54410001
|
|
#define ATAG_RAMDISK_TYPE 0x54410004
|
|
#define ATAG_INITRD2_TYPE 0x54420005
|
|
#define ATAG_CMDLINE_TYPE 0x54410009
|
|
|
|
#define MAX_ATAG_SIZE 0x4000
|
|
|
|
struct atag_info {
|
|
unsigned size;
|
|
unsigned type;
|
|
};
|
|
|
|
struct atag_initrd2 {
|
|
unsigned start;
|
|
unsigned size;
|
|
};
|
|
|
|
struct atag_cmdline {
|
|
char cmdline[0];
|
|
};
|
|
|
|
struct atag {
|
|
struct atag_info info;
|
|
union {
|
|
struct atag_initrd2 initrd2;
|
|
struct atag_cmdline cmdline;
|
|
} data;
|
|
};
|
|
|
|
|
|
long kexec_load(unsigned int entry, unsigned long nr_segments,
|
|
struct kexec_segment *segment, unsigned long flags) {
|
|
return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
|
|
}
|
|
|
|
/*
|
|
* Prepares arguments for kexec
|
|
* Kernel address is not set into kernel_phys
|
|
* Ramdisk is set to position relative to kernel
|
|
*/
|
|
int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
|
|
unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
|
|
unsigned second_phys, void *second_addr, int second_size,
|
|
unsigned atags_phys, void *atags_addr, int atags_size) {
|
|
struct kexec_segment segment[4];
|
|
int segment_count = 2;
|
|
unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
|
|
int rv;
|
|
int page_size = getpagesize();
|
|
|
|
segment[0].buf = kernel_addr;
|
|
segment[0].bufsz = kernel_size;
|
|
segment[0].mem = (void *) KERNEL_START;
|
|
segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
|
|
|
|
if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
|
|
D(INFO, "Kernel image too big");
|
|
return -1;
|
|
}
|
|
|
|
segment[1].buf = atags_addr;
|
|
segment[1].bufsz = atags_size;
|
|
segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
|
|
segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
|
|
|
|
D(INFO, "Ramdisk size is %d", ramdisk_size);
|
|
|
|
if (ramdisk_size != 0) {
|
|
segment[segment_count].buf = ramdisk_addr;
|
|
segment[segment_count].bufsz = ramdisk_size;
|
|
segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
|
|
segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
|
|
++segment_count;
|
|
}
|
|
|
|
D(INFO, "Ramdisk size is %d", ramdisk_size);
|
|
if (second_size != 0) {
|
|
segment[segment_count].buf = second_addr;
|
|
segment[segment_count].bufsz = second_size;
|
|
segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
|
|
segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
|
|
entry = second_phys;
|
|
++segment_count;
|
|
}
|
|
|
|
rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
|
|
|
|
if (rv != 0) {
|
|
D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
|
|
struct atag *current_tag = (struct atag *) atags_position;
|
|
unsigned *current_tag_raw = atags_position;
|
|
unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
|
|
hdr->page_size));
|
|
//This pointer will point into the beggining of buffer free space
|
|
unsigned *natags_raw_buff = new_atags;
|
|
int new_atags_size = 0;
|
|
int current_size;
|
|
int cmdl_length;
|
|
|
|
// copy tags from current atag file
|
|
while (current_tag->info.type != ATAG_NONE_TYPE) {
|
|
switch (current_tag->info.type) {
|
|
case ATAG_CMDLINE_TYPE:
|
|
case ATAG_RAMDISK_TYPE:
|
|
case ATAG_INITRD2_TYPE: break;
|
|
default:
|
|
memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
|
|
natags_raw_buff += current_tag->info.size;
|
|
new_atags_size += current_tag->info.size;
|
|
}
|
|
|
|
current_tag_raw += current_tag->info.size;
|
|
current_tag = (struct atag *) current_tag_raw;
|
|
|
|
if (current_tag_raw >= atags_position + atag_size) {
|
|
D(ERR, "Critical error in atags");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// set INITRD2 tag
|
|
if (hdr->ramdisk_size > 0) {
|
|
current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
|
|
*((struct atag *) natags_raw_buff) = (struct atag) {
|
|
.info = {
|
|
.size = current_size,
|
|
.type = ATAG_INITRD2_TYPE
|
|
},
|
|
.data = {
|
|
.initrd2 = (struct atag_initrd2) {
|
|
.start = hdr->ramdisk_addr,
|
|
.size = hdr->ramdisk_size
|
|
}
|
|
}
|
|
};
|
|
|
|
new_atags_size += current_size;
|
|
natags_raw_buff += current_size;
|
|
}
|
|
|
|
// set ATAG_CMDLINE
|
|
cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
|
|
current_size = sizeof(struct atag_info) + (1 + cmdl_length);
|
|
current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
|
|
*((struct atag *) natags_raw_buff) = (struct atag) {
|
|
.info = {
|
|
.size = current_size,
|
|
.type = ATAG_CMDLINE_TYPE
|
|
},
|
|
};
|
|
|
|
//copy cmdline and ensure that there is null character
|
|
memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
|
|
(char *) hdr->cmdline, cmdl_length);
|
|
((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
|
|
|
|
new_atags_size += current_size;
|
|
natags_raw_buff += current_size;
|
|
|
|
// set ATAG_NONE
|
|
*((struct atag *) natags_raw_buff) = (struct atag) {
|
|
.info = {
|
|
.size = 0,
|
|
.type = ATAG_NONE_TYPE
|
|
},
|
|
};
|
|
new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
|
|
natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
|
|
|
|
*size = new_atags_size * sizeof(unsigned);
|
|
return new_atags;
|
|
}
|
|
|
|
char *read_atags(const char * path, int *atags_sz) {
|
|
int afd = -1;
|
|
char *atags_ptr = NULL;
|
|
|
|
afd = open(path, O_RDONLY);
|
|
if (afd < 0) {
|
|
D(ERR, "wrong atags file");
|
|
return 0;
|
|
}
|
|
|
|
atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
|
|
if (atags_ptr == NULL) {
|
|
D(ERR, "insufficient memory");
|
|
return 0;
|
|
}
|
|
|
|
*atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
|
|
|
|
close(afd);
|
|
return atags_ptr;
|
|
}
|
|
|