blob: 38eda21e0ce4c3d87be14b45056978d95cfba02c [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Copyright (C) 2011
* Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
* - Added prep subcommand support
* - Reorganized source - modeled after powerpc version
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
*
* See file CREDITS for list of people who contributed to this project.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <config.h>
#include <libpayload.h>
#include <endian.h>
#include "image/symbols.h"
#include "atags.h"
#include "crc32.h"
#include "legacy_image.h"
/* Check header CRC of the uImage header */
static int image_check_hcrc(const image_header_t *hdr)
{
uint32_t hcrc;
image_header_t header;
/* Copy header so we can blank CRC field for re-calculation */
memcpy(&header, hdr, sizeof(header));
image_set_hcrc(&header, 0);
hcrc = crc32(0, &header, sizeof(header));
return (hcrc == image_get_hcrc(hdr));
}
/*
* Check uImage payload CRC, usually the kernel blob wrapped in uImage
* header
*/
static int image_check_dcrc(const image_header_t *hdr)
{
uint32_t dcrc = crc32(0, hdr + 1, image_get_size(hdr));
return (dcrc == image_get_dcrc(hdr));
}
/* verify that two areas do not overlap */
static int check_overlap(const void *area1_start,
const void *area1_end,
const void *area2_start,
const void *area2_end)
{
unsigned s1 = (unsigned) area1_start;
unsigned e1 = (unsigned) area1_end;
unsigned s2 = (unsigned) area2_start;
unsigned e2 = (unsigned) area2_end;
return ((s1 < s2) && (e1 > s2)) || ((s1 < e2) && (e1 > e2));
}
/*
* Process the legacy image - basically copy it into the required location if
* necessary. Image compression is not (yet) supported.
*/
static int bootm_load_os(bootm_header_t *bootm_header_p)
{
image_info_t* pos = &bootm_header_p->os; /* Just to cache it. */
uint8_t comp = pos->comp;
uint8_t *load_buf = (uint8_t *)pos->load;
uint8_t *image_buf = (uint8_t *)pos->image_start;
uint32_t image_len = pos->image_len;
switch (comp) {
case IH_COMP_NONE:
pos->actual_size = image_len;
if (load_buf == image_buf)
break; /* No relocation needed. */
if (check_overlap(load_buf, load_buf + image_len,
image_buf, image_buf + image_len) ||
check_overlap(load_buf, load_buf + image_len,
&_start, &_end)) {
printf("%s:%d - Overlap trying to relocate the kernel\n",
__func__, __LINE__);
return BOOTM_ERR_OVERLAP;
}
printf(" relocating legacy kernel to %p ... ", load_buf);
memcpy(load_buf, image_buf, image_len);
printf("done!\n");
break;
default:
printf("Usupported compression type %d\n", comp);
return BOOTM_ERR_UNIMPLEMENTED;
}
printf("OK\n");
return 0;
}
/* Functions to generate various ATAGs */
static struct tag *setup_start_tag (struct tag *ktags)
{
ktags->hdr.tag = ATAG_CORE;
ktags->hdr.size = tag_size(tag_core);
ktags->u.core.flags = 0;
ktags->u.core.pagesize = 0;
ktags->u.core.rootdev = 0;
return tag_next (ktags);
}
static struct tag *setup_commandline_tag(struct tag *ktags,
const char *commandline)
{
const char *p;
if (!commandline)
return ktags;
/* eat leading white space */
for (p = commandline; *p == ' '; p++)
;
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return ktags;
ktags->hdr.tag = ATAG_CMDLINE;
ktags->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 5) >> 2;
strcpy(ktags->u.cmdline.cmdline, p);
printf("Kernel command line: \"%s\"\n", p);
return tag_next(ktags);
}
static struct tag *setup_memory_tags(struct tag *ktags)
{
for (int i = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *range = &lib_sysinfo.memrange[i];
if (range->type != CB_MEM_RAM)
continue;
ktags->hdr.tag = ATAG_MEM;
ktags->hdr.size = tag_size(tag_mem32);
ktags->u.mem.start = (uint32_t) range->base;
ktags->u.mem.size = (uint32_t) range->size;
printf("MEM tag added %#8.8x..%#8.8x\n",
ktags->u.mem.start,
ktags->u.mem.start + ktags->u.mem.size - 1);
ktags = tag_next(ktags);
}
return ktags;
}
static void setup_end_tag(struct tag *ktags)
{
ktags->hdr.tag = ATAG_NONE;
ktags->hdr.size = 0;
}
/* Everything seems ok - jump to the kernel passing the expected parameters. */
static int jump_to_kernel(bootm_header_t *images, struct tag *start_tag)
{
void (*kernel_entry)(int zero, int arch, unsigned params);
unsigned r2 = (unsigned long) start_tag;
int mach_id = get_mach_id();
printf("Using machine ID %d\n", mach_id);
kernel_entry = (void (*)(int, int, unsigned))images->ep;
kernel_entry(0, mach_id, r2);
return -1; /* should never come here */
}
/* Prepare ATAGs, flash the cache and start the kernel */
static int start_legacy_kernel(bootm_header_t *bm_hdr_p)
{
int ret;
struct tag *start_tag, *current_tag;
ret = bootm_load_os(bm_hdr_p);
if (ret) {
printf("%s:%d failed to load os (%d)\n",
__func__, __LINE__, ret);
return ret;
}
start_tag = (struct tag *)CONFIG_ATAG_BASE;;
current_tag = setup_start_tag(start_tag);
current_tag = setup_commandline_tag(current_tag, bm_hdr_p->cmdline);
current_tag = setup_memory_tags(current_tag);
setup_end_tag(current_tag);
cache_sync_instructions();
dcache_mmu_disable();
return jump_to_kernel(bm_hdr_p, start_tag);
}
/* verify and prepare for booting of a legacy kernel */
int legacy_boot(void *kernel, const char *cmd_line_buf)
{
const image_header_t *hdr = kernel;
bootm_header_t bootm_header;
memset(&bootm_header, 0, sizeof(bootm_header));
if (image_get_magic(hdr) != IH_MAGIC)
return 1;
if (!image_check_hcrc(hdr)) {
printf("Bad Header CRC\n");
return 1;
}
if (!image_check_dcrc(hdr)) {
printf("Bad Data CRC\n");
return 1;
}
bootm_header.os.type = image_get_type(hdr);
bootm_header.os.comp = image_get_comp(hdr);
bootm_header.os.end = (uint32_t)hdr +
image_get_size(hdr) + sizeof(*hdr);
bootm_header.os.load = image_get_load(hdr);
bootm_header.os.start = (uint32_t) hdr;
bootm_header.os.image_start = (uint32_t)(hdr + 1);
bootm_header.os.image_len = image_get_size(hdr);
bootm_header.ep = image_get_ep(hdr);
bootm_header.cmdline = cmd_line_buf;
return start_legacy_kernel(&bootm_header);
}