| /* |
| * 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); |
| } |