| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * 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. |
| * |
| * 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. |
| */ |
| |
| /* |
| * Copyright (c) 2010 Marvell Inc. |
| * Modified by Lei Wen <leiwen@marvell.com> |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| * |
| * ported from m2011.09 by yizhang@marvell.com, 28-Mar-2014 |
| */ |
| #include <common.h> |
| #include <fastboot.h> |
| #include <sparse_format.h> |
| #include <jffs2/load_kernel.h> |
| #include <linux/mtd/mtd.h> |
| #include <mmc.h> |
| #include <malloc.h> |
| |
| #ifdef CONFIG_MRVL_BOOT |
| #include <mv_boot.h> |
| #endif |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| #define SECTOR_SIZE 512 |
| |
| #ifdef CONFIG_TEE_OS |
| #define FB_BUFF_ADDR 0x2800000 /* 40MB offset */ |
| #else |
| #define FB_BUFF_ADDR 0x2000000 /* 32MB offset */ |
| #endif |
| |
| enum fb_dev { |
| FB_INVALID = 0, |
| FB_NOR = MTD_DEV_TYPE_NOR, |
| FB_NAND = MTD_DEV_TYPE_NAND, |
| FB_ONENAND = MTD_DEV_TYPE_ONENAND, |
| FB_SPIFLASH, |
| FB_MMC, |
| FB_MAX, |
| }; |
| |
| #ifndef CONFIG_FB_RESV |
| #define CONFIG_FB_RESV 64 |
| #endif |
| |
| #define MAX_NAME_NUM 36 |
| #define UDC_OUT_PACKET_SIZE 0x200 |
| block_dev_desc_t *mmc_dev; |
| static char commands[CONFIG_SYS_CBSIZE]; |
| /* For each part has four attributes: |
| * device type, device num, start address, size |
| */ |
| struct fb_part { |
| char name[MAX_NAME_NUM]; |
| #define PART_ATTR_YAFFS 1 |
| #define PART_NUM_MASK 0xE0000000 |
| #define PART_NUM(x) (((x) & PART_NUM_MASK) >> 29) |
| unsigned int attr; |
| unsigned long long start; |
| unsigned long long size; |
| }; |
| |
| struct fb_parts { |
| enum fb_dev dev; |
| int dev_num; |
| int part_num; |
| int align_len; |
| struct fb_part *part; |
| }; |
| |
| static struct fb_parts *parts; |
| #ifdef CONFIG_MTD_DEVICE |
| #ifdef CONFIG_SYS_FB_YAFFS |
| static char *part_yaffs[] = CONFIG_SYS_FB_YAFFS; |
| #else |
| static char *part_yaffs[] = {NULL}; |
| #endif |
| #endif |
| |
| static char *rcv_buf; |
| static unsigned fb_mem, ramdisk_addr, ramdisk_size, kernel_addr, kernel_size; |
| static int keep_running, is_yaffs, part_num, onfly; |
| static int bootsp, first_rcv, need_unsparse; |
| |
| #define BOOT_MAGIC "ANDROID!" |
| #define BOOT_MAGIC_SIZE 8 |
| #define BOOT_NAME_SIZE 16 |
| #define BOOT_ARGS_SIZE 512 |
| |
| struct boot_img_hdr { |
| unsigned char magic[BOOT_MAGIC_SIZE]; |
| |
| unsigned kernel_size; /* size in bytes */ |
| unsigned kernel_addr; /* physical load addr */ |
| |
| unsigned ramdisk_size; /* size in bytes */ |
| unsigned ramdisk_addr; /* physical load addr */ |
| |
| unsigned second_size; /* size in bytes */ |
| unsigned second_addr; /* physical load addr */ |
| |
| unsigned tags_addr; /* physical addr for kernel tags */ |
| unsigned page_size; /* flash page size we assume */ |
| unsigned unused[2]; /* future expansion: should be 0 */ |
| |
| unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ |
| |
| unsigned char cmdline[BOOT_ARGS_SIZE]; |
| |
| unsigned id[8]; /* timestamp / checksum / sha1 / etc */ |
| }; |
| |
| static unsigned rx_addr; |
| static unsigned rx_length; |
| static unsigned long long flash_start; |
| static unsigned long long flash_len; |
| |
| static int init_boot_linux(void) |
| { |
| struct boot_img_hdr *hdr = (void *)(uintptr_t)fb_mem; |
| unsigned page_mask = 2047; |
| unsigned kernel_actual; |
| unsigned ramdisk_actual; |
| unsigned second_actual; |
| |
| if (kernel_size < 2048) { |
| printf("bootimg: bad header, kernel_size is wrong\n"); |
| return -1; |
| } |
| |
| if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { |
| printf("bootimg: bad header\n"); |
| return -1; |
| } |
| |
| if (hdr->page_size != 2048) { |
| printf("bootimg: invalid page size\n"); |
| return -1; |
| } |
| |
| kernel_actual = (hdr->kernel_size + page_mask) & (~page_mask); |
| ramdisk_actual = (hdr->ramdisk_size + page_mask) & (~page_mask); |
| second_actual = (hdr->second_size + page_mask) & (~page_mask); |
| |
| if (kernel_size != |
| (kernel_actual + ramdisk_actual + second_actual + 2048)) { |
| printf("bootimg: invalid image size"); |
| return -1; |
| } |
| |
| /* XXX process commandline here */ |
| if (hdr->cmdline[0]) { |
| hdr->cmdline[BOOT_ARGS_SIZE - 1] = 0; |
| printf("cmdline is: %s\n", hdr->cmdline); |
| setenv("bootargs", (char *)hdr->cmdline); |
| } |
| |
| /* XXX how to validate addresses? */ |
| kernel_addr = (uintptr_t)hdr + 2048; |
| kernel_size = hdr->kernel_size; |
| ramdisk_size = hdr->ramdisk_size; |
| if (ramdisk_size > 0) |
| ramdisk_addr = kernel_addr + 2048 + kernel_size; |
| else |
| ramdisk_addr = 0; |
| |
| printf("bootimg: kernel addr=0x%x size=0x%x\n", |
| kernel_addr, kernel_size); |
| printf("bootimg: ramdisk addr=0x%x size=0x%x\n", |
| ramdisk_addr, ramdisk_size); |
| |
| return 0; |
| } |
| |
| static int composite_command(int is_write) |
| { |
| char tmp[CONFIG_SYS_CBSIZE]; |
| ulong start, len; |
| |
| if (need_unsparse) { |
| need_unsparse = 0; |
| sprintf(commands, |
| "mmc dev %d 0; unsparse mmc %d 0x%x 0x%llx 0x%llx", |
| parts->dev_num, parts->dev_num, |
| fb_mem, flash_start, parts->part[part_num].size); |
| goto DIRECT_BURN; |
| } |
| |
| start = (parts->dev == FB_MMC) |
| ? flash_start / SECTOR_SIZE : flash_start; |
| len = (parts->dev == FB_MMC) ? flash_len / SECTOR_SIZE : flash_len; |
| if (is_write) { |
| if (is_yaffs) |
| sprintf(tmp, "write.yaffs 0x%x 0x%lx 0x%lx", |
| fb_mem, start, len); |
| else |
| sprintf(tmp, "write 0x%x 0x%lx 0x%lx", |
| fb_mem, start, len); |
| } else { |
| sprintf(tmp, "erase 0x%lx 0x%lx", start, len); |
| } |
| |
| switch (parts->dev) { |
| case FB_NOR: |
| sprintf(commands, "sf %s", tmp); break; |
| case FB_NAND: |
| #ifdef CONFIG_PXA182X |
| sprintf(commands, "nand device %d; nand %s", |
| parts->dev_num, tmp); |
| #else |
| sprintf(commands, "spi_flash %s", tmp); |
| #endif |
| break; |
| case FB_ONENAND: |
| sprintf(commands, "onenand %s", tmp); break; |
| case FB_SPIFLASH: |
| sprintf(commands, "spi_flash %s", |
| parts->dev_num, tmp); break; |
| case FB_MMC: |
| sprintf(commands, "mmc dev %d %d; mmc %s; mmc dev %d 0", |
| parts->dev_num, PART_NUM(parts->part[part_num].attr), |
| tmp, parts->dev_num); |
| break; |
| default: |
| printf("Err dev!! %d\n", parts->dev); |
| return 0; |
| } |
| |
| DIRECT_BURN: |
| printf("command::%s\n", commands); |
| run_command(commands, 0); |
| return 0; |
| } |
| |
| static void burn_image(int force_burn) |
| { |
| int len = rx_addr - fb_mem; |
| int checklen = (is_yaffs) ? ((CONFIG_SYS_FASTBOOT_ONFLY_SZ / 32) * 33) |
| : CONFIG_SYS_FASTBOOT_ONFLY_SZ; |
| if (len == checklen || force_burn) { |
| if (!is_yaffs) { |
| flash_len = len & ~(parts->align_len - 1); |
| if (flash_len != len) { |
| flash_len += parts->align_len; |
| memset((char *)(uintptr_t)rx_addr, 0xff, |
| flash_len - len); |
| } |
| } else { |
| flash_len = len; |
| } |
| composite_command(1); |
| flash_start += CONFIG_SYS_FASTBOOT_ONFLY_SZ; |
| fb_set_buf((void *)(uintptr_t)fb_mem); |
| rx_addr = fb_mem; |
| } |
| } |
| |
| static int check_part(char *name) |
| { |
| int i; |
| for (i = 0; i < parts->part_num; i++) { |
| if (strcmp(name, parts->part[i].name) == 0) |
| break; |
| } |
| if (i == parts->part_num) { |
| printf("!!!!!!!!!!There is no such part!\n"); |
| fb_tx_status("FAILno such part"); |
| return -1; |
| } |
| |
| return i; |
| } |
| |
| static int find_devnum(const char *p) |
| { |
| int devnum; |
| if (*p == '\0') { |
| printf("no partition number specified\n"); |
| return -1; |
| } |
| devnum = simple_strtoul(p, (char **)&p, 0); |
| if (*p != '\0') { |
| printf("unexpected trailing character '%c'\n", *p); |
| return -1; |
| } |
| return devnum; |
| } |
| |
| static int init_partitions(void) |
| { |
| struct mmc *mmc; |
| int i, pnum = 0; |
| disk_partition_t info; |
| const char *p; |
| |
| mmc_dev = NULL; |
| p = getenv("fbenv"); |
| if (0 == strncmp(p, "mmc", 3)) { |
| #ifdef CONFIG_MMC |
| parts = malloc(sizeof(struct fb_parts)); |
| if (!parts) { |
| printf("Out of memory %s:%d\n", __func__, __LINE__); |
| return 1; |
| } |
| i = find_devnum(p + 3); |
| if (i == -1) |
| return 1; |
| mmc = find_mmc_device(i); |
| if (!mmc) |
| return 1; |
| mmc_init(mmc); |
| parts->part_num = 0; |
| parts->part = 0; |
| parts->align_len = SECTOR_SIZE; |
| parts->dev = FB_MMC; |
| parts->dev_num = i; |
| mmc_dev = mmc_get_dev(i); |
| pnum = get_partition_num(mmc_dev); |
| if (pnum) { |
| parts->part = malloc(sizeof(struct fb_part)*pnum); |
| if (!parts->part) { |
| printf("Out of memory %s:%d\n", __func__, __LINE__); |
| return 1; |
| } |
| } else { |
| parts->part = NULL; |
| } |
| parts->part_num = pnum; |
| if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) { |
| for (i = 1; i <= pnum; i++) { |
| int len; |
| if (get_partition_info(mmc_dev, i, &info)) |
| break; |
| len = strlen((char *)info.name); |
| len = (len < MAX_NAME_NUM) |
| ? len : MAX_NAME_NUM - 1; |
| memcpy(parts->part[i - 1].name, info.name, len); |
| parts->part[i - 1].name[len] = 0; |
| parts->part[i - 1].start = |
| (unsigned long long)info.start * info.blksz; |
| parts->part[i - 1].size = |
| (unsigned long long)info.size * info.blksz; |
| parts->part[i - 1].attr = 0; |
| } |
| } |
| #else |
| printf("CONFIG_MMC not be defined!!\n"); |
| return 1; |
| #endif |
| } else { |
| #ifdef CONFIG_MTD_DEVICE |
| enum fb_dev type; |
| struct mtd_device *dev; |
| struct part_info *part; |
| int j, align; |
| if (mtdparts_init()) { |
| printf("mtd part init fail!\n"); |
| return 1; |
| } |
| if (0 == strncmp(p, "nand", 4)) { |
| p += 4; |
| type = FB_NAND; |
| align = CONFIG_SYS_FASTBOOT_ONFLY_SZ; |
| } else if (0 == strncmp(p, "onenand", 4)) { |
| p += 7; |
| type = FB_ONENAND; |
| align = CONFIG_SYS_FASTBOOT_ONFLY_SZ; |
| } else if (0 == strncmp(p, "nor", 3)) { |
| p += 3; |
| type = FB_NOR; |
| align = 1; |
| } else if (0 == strncmp(p, "spi_flash", 9)) { |
| p += 9; |
| type = MTD_DEV_TYPE_NAND; |
| align = CONFIG_SYS_FASTBOOT_ONFLY_SZ; |
| } else { |
| printf("fbenv must de defined!!"); |
| return 1; |
| } |
| |
| i = find_devnum(p); |
| if (i == -1) |
| return 1; |
| |
| dev = device_find(type, i); |
| if (dev == NULL) { |
| printf("There is no device as %s\n", getenv("fbenv")); |
| return 1; |
| } |
| if (!dev->num_parts) { |
| printf("Dev num parts is 0\n"); |
| return 1; |
| } |
| parts = malloc(sizeof(struct fb_parts)); |
| if (!parts) { |
| printf("Out of memory %s:%d\n", __func__, __LINE__); |
| return 1; |
| } |
| parts->part = malloc(sizeof(struct fb_part)*dev->num_parts); |
| if (!parts->part) { |
| printf("Out of memory %s:%d\n", __func__, __LINE__); |
| return 1; |
| } |
| parts->dev = type; |
| parts->dev_num = i; |
| parts->part_num = dev->num_parts; |
| parts->align_len = align; |
| pnum = dev->num_parts; |
| for (i = 0; i < dev->num_parts; i++) { |
| part = mtd_part_info(dev, i); |
| if (part) { |
| int len; |
| len = strlen((char *)part->name); |
| len = (len < MAX_NAME_NUM) |
| ? len : MAX_NAME_NUM - 1; |
| memcpy(parts->part[i].name, part->name, len); |
| parts->part[i].name[len] = 0; |
| parts->part[i].start = part->offset; |
| parts->part[i].size = part->size; |
| parts->part[i].attr = 0; |
| for (j = 0; j < ARRAY_SIZE(part_yaffs); j++) |
| if (!memcmp(part_yaffs[j], part->name, |
| strlen(part->name))) { |
| parts->part[i].attr = |
| PART_ATTR_YAFFS; |
| break; |
| } |
| } else { |
| printf("fail to find part: %s\n", part->name); |
| return 1; |
| } |
| } |
| #else |
| printf("CONFIG_MTD_DEVICE not be defined!!\n"); |
| return 1; |
| #endif |
| } |
| |
| switch (parts->dev) { |
| case FB_NOR: |
| printf("nor\n"); break; |
| case FB_NAND: |
| printf("nand\n"); break; |
| case FB_ONENAND: |
| printf("onenand\n"); break; |
| case FB_MMC: |
| printf("mmc\n"); break; |
| case FB_SPIFLASH: |
| printf("spi_flash\n"); break; |
| default: |
| printf("error!!!\n"); break; |
| } |
| for (i = 0; i < pnum; i++) |
| printf("part %3d::attr 0x%x::%12s\t start 0x%08llx, size 0x%08llx\n", |
| i, parts->part[i].attr, parts->part[i].name, parts->part[i].start, parts->part[i].size); |
| return 0; |
| } |
| |
| static int modify_part(char *name, unsigned long long x[3]) |
| { |
| int len = strlen(name); |
| parts->part = realloc(parts->part, |
| sizeof(struct fb_part)*(parts->part_num + 1)); |
| if (!parts->part) { |
| printf("Out of memory %s:%d\n", __func__, __LINE__); |
| return 1; |
| } |
| len = (len < MAX_NAME_NUM) ? len : MAX_NAME_NUM - 1; |
| memcpy(parts->part[parts->part_num].name, name, len); |
| parts->part[parts->part_num].name[len] = 0; |
| parts->part[parts->part_num].attr = (unsigned int)x[0]; |
| parts->part[parts->part_num].start = x[1]; |
| parts->part[parts->part_num].size = x[2]; |
| parts->part_num++; |
| |
| return 0; |
| } |
| /* for test */ |
| /* |
| #define RAMDISK_LOADADDR 0x2000000 |
| #define BOOTIMG_EMMC_ADDR 0x1000000 |
| #define CONFIG_MMC_BOOT_DEV "mmc dev 2 0" |
| */ |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| static int mkbootimg(void *kernel, unsigned kernel_size) |
| { |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| unsigned long ramdisk_addr = RAMDISK_LOADADDR; |
| #endif |
| struct boot_img_hdr *hdr = (struct boot_img_hdr *)kernel; |
| struct boot_img_hdr *ahdr = (void *)ramdisk_addr; |
| unsigned ramdisk_offsize; |
| char cmd[128]; |
| unsigned long base_addr = BOOTIMG_EMMC_ADDR; |
| base_addr /= SECTOR_SIZE; |
| |
| /* If the burning image is not bootimg or already have ramdisk*/ |
| if (memcmp(BOOT_MAGIC, hdr->magic, BOOT_MAGIC_SIZE) || |
| hdr->ramdisk_size) { |
| return 0; |
| } |
| |
| /* parse the raw image header and read the ramdisk to memory*/ |
| sprintf(cmd, "%s; mmc read %lx %lx 0x8", |
| CONFIG_MMC_BOOT_DEV, ramdisk_addr, base_addr); |
| run_command(cmd, 0); |
| if (!memcmp(BOOT_MAGIC, ahdr->magic, BOOT_MAGIC_SIZE) && |
| ahdr->ramdisk_size) { |
| hdr->ramdisk_size = ahdr->ramdisk_size; |
| ramdisk_offsize = ALIGN(ahdr->kernel_size, ahdr->page_size) |
| + ahdr->page_size; |
| ramdisk_offsize = ramdisk_offsize / SECTOR_SIZE; |
| |
| sprintf(cmd, "%s; mmc read 0x%08lx %lx 0x06000", |
| CONFIG_MMC_BOOT_DEV, ramdisk_addr, base_addr + ramdisk_offsize); |
| run_command(cmd, 0); |
| } else { |
| /*In the flash, there is no boot.img*/ |
| return 0; |
| } |
| /*write ramdisk.img to flash */ |
| ramdisk_offsize = hdr->page_size + ALIGN(hdr->kernel_size, hdr->page_size); |
| ramdisk_offsize = ramdisk_offsize / SECTOR_SIZE; |
| sprintf(cmd, "%s; mmc write %lx %lx 0x06000", |
| CONFIG_MMC_BOOT_DEV, ramdisk_addr, base_addr + ramdisk_offsize); |
| run_command(cmd, 0); |
| return 1; |
| } |
| #endif |
| #define MAX_RESP_SZ 64 |
| void rcv_cmd(void) |
| { |
| char status[MAX_RESP_SZ]; |
| int len; |
| static char *cmdbuf; |
| |
| len = fb_get_rcv_len(); |
| cmdbuf = (char *)fb_get_buf(); |
| |
| if (rx_length) { |
| if (rx_length > len) { |
| rx_length -= len; |
| rx_addr += len; |
| } else { |
| printf("rx_length: 0x%x\n", rx_length); |
| rx_addr += rx_length; |
| rx_length = 0; |
| } |
| |
| fb_set_buf(cmdbuf + len); |
| /* Here we do some check for the downloaded image header */ |
| #ifdef CONFIG_CMD_UNSPARSE |
| if (first_rcv) { |
| sparse_header_t *header; |
| |
| first_rcv = 0; |
| header = (sparse_header_t *)(uintptr_t)fb_mem; |
| if (header->magic == SPARSE_HEADER_MAGIC) { |
| if (parts->dev == FB_MMC) { |
| if (onfly) |
| onfly = 0; |
| need_unsparse = 1; |
| } else { |
| printf("Only mmc support sparsed Image now!\n"); |
| } |
| } |
| } |
| #endif |
| if (!rx_length) { |
| if (onfly) |
| burn_image(1); |
| fb_tx_status("OKAY"); |
| fb_set_buf(rcv_buf); |
| onfly = 0; |
| } else { |
| if (onfly) |
| burn_image(0); |
| } |
| |
| return; |
| } |
| if (len >= UDC_OUT_PACKET_SIZE) |
| len = UDC_OUT_PACKET_SIZE - 1; |
| cmdbuf[len] = 0; |
| |
| printf("\n> %s\n", cmdbuf); |
| |
| if (memcmp(cmdbuf, "download:", 9) == 0) { |
| rx_addr = fb_mem; |
| first_rcv = 1; |
| fb_set_buf((void *)(uintptr_t)rx_addr); |
| rx_length = simple_strtoul(cmdbuf + 9, NULL, 16); |
| if ((onfly && (rx_length > flash_len)) || |
| (!onfly && (rx_length > (CONFIG_FB_RESV*1024*1024)))) { |
| fb_tx_status("FAILdata too large"); |
| rx_length = 0; |
| return; |
| } |
| kernel_size = rx_length; |
| printf("recv data addr=%x size=%x\n", rx_addr, rx_length); |
| strcpy(status, "DATA"); |
| sprintf(status + 4, "%08x", rx_length); |
| fb_tx_status(status); |
| return; |
| } |
| |
| if (memcmp(cmdbuf, "boot", 4) == 0) { |
| if (init_boot_linux()) { |
| fb_tx_status("FAIL invalid boot image"); |
| } else { |
| printf("booting linux...\n"); |
| fb_tx_status("OKAY"); |
| if (!bootsp) { |
| #ifdef CONFIG_MRVL_BOOT |
| image_load_notify(kernel_addr); |
| sprintf(status, "boot"); |
| #else |
| if (ramdisk_size > 0) { |
| printf("kernel_addr = 0x%x, ramdisk_addr = 0x%x, ramdisk_size = 0x%x", |
| kernel_addr, ramdisk_addr, ramdisk_size); |
| sprintf(status, "bootm 0x%x 0x%x 0x%x", |
| kernel_addr, ramdisk_addr, ramdisk_size); |
| } else { |
| printf("kernel_addr = 0x%x\n", kernel_addr); |
| sprintf(status, "bootm 0x%x", kernel_addr); |
| } |
| #endif |
| } else { |
| sprintf(status, "go 0x%x", kernel_addr); |
| } |
| |
| printf("cmd:%s\n", status); |
| run_command(status, 0); |
| fb_tx_status("FAILNot zImage, use bootsp oem cmd to boot special app"); |
| } |
| return; |
| } |
| onfly = 0; |
| bootsp = 0; |
| if (memcmp(cmdbuf, "flash", 5) == 0) { |
| int ret = 0; |
| if (rx_addr > fb_mem) { |
| part_num = check_part(cmdbuf + 6); |
| if (part_num < 0) |
| return; |
| is_yaffs = parts->part[part_num].attr & PART_ATTR_YAFFS; |
| flash_start = parts->part[part_num].start; |
| flash_len = parts->part[part_num].size; |
| #ifdef CONFIG_ANDROID_BOOT_IMAGE |
| if (mkbootimg((void *)(uintptr_t)fb_mem, kernel_size)) |
| flash_len = kernel_size; |
| #endif |
| if ((rx_addr - fb_mem) > flash_len) { |
| fb_tx_status("FAILdata too large"); |
| return; |
| } |
| burn_image(1); |
| } |
| is_yaffs = 0; |
| if (!ret) |
| fb_tx_status("OKAY"); |
| return; |
| } |
| |
| if (memcmp(cmdbuf, "senddata", 8) == 0) { |
| int i, ret; |
| char *p; |
| |
| ret = sprintf(status, "OKAY"); |
| p = status + ret; |
| for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { |
| if (!gd->bd->bi_dram[i].size) |
| continue; |
| |
| ret = sprintf(p, "%lx:%lx:", |
| gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); |
| p += ret; |
| } |
| |
| fb_tx_status(status); |
| return; |
| } |
| |
| if (memcmp(cmdbuf, "upload", 6) == 0) { |
| char *s = cmdbuf + 6, *p; |
| unsigned long upload_start, upload_sz; |
| |
| p = strchr(s, ':'); |
| if (!p) { |
| fb_tx_status("FAILno valid start pos"); |
| return; |
| } |
| *p = 0; |
| upload_start = simple_strtoul(s, (char **)&s, 16); |
| s = p + 1; |
| p = strchr(s, ':'); |
| if (!p) { |
| fb_tx_status("FAILno valid start pos"); |
| return; |
| } |
| *p = 0; |
| upload_sz = simple_strtoul(s, (char **)&s, 16); |
| if (!upload_sz) { |
| fb_tx_status("FAILno valid upload sz"); |
| return; |
| } |
| |
| fb_tx_data((void *)upload_start, upload_sz); |
| return; |
| } |
| |
| if (memcmp(cmdbuf, "erase", 5) == 0) { |
| part_num = check_part(cmdbuf + 6); |
| if (part_num < 0) |
| return; |
| flash_start = parts->part[part_num].start; |
| flash_len = parts->part[part_num].size; |
| composite_command(0); |
| fb_tx_status("OKAY"); |
| return; |
| } |
| |
| if (memcmp(cmdbuf, "oem", 3) == 0) { |
| if (memcmp(cmdbuf + 4, "onfly", 5) == 0) { |
| onfly = 0; |
| part_num = check_part(cmdbuf + 10); |
| if (part_num < 0) |
| return; |
| is_yaffs = parts->part[part_num].attr & PART_ATTR_YAFFS; |
| flash_start = parts->part[part_num].start; |
| flash_len = parts->part[part_num].size; |
| onfly = 1; |
| fb_tx_status("OKAY"); |
| return; |
| } |
| if (memcmp(cmdbuf + 4, "bootsp", 6) == 0) { |
| bootsp = 1; |
| fb_tx_status("OKAY"); |
| return; |
| } |
| if (memcmp(cmdbuf + 4, "setno", 5) == 0) { |
| char *s = cmdbuf + 4, *p; |
| p = strchr(s, ':'); |
| s = p + 1; |
| if (!p || !s) { |
| fb_tx_status("FAILno valid serial no"); |
| return; |
| } |
| setenv("fb_serial", s); |
| fb_tx_status("OKAY"); |
| fb_init(); |
| return; |
| } |
| if (memcmp(cmdbuf + 4, "part", 4) == 0) { |
| unsigned int i; |
| unsigned long long x[3]; |
| char *s = cmdbuf + 8, *p, *name; |
| p = strchr(s, ':'); |
| if (p) { |
| s = p + 1; |
| p = strchr(s, ':'); |
| *p = 0; |
| name = s; |
| if (!name) { |
| fb_tx_status("FAILno name specified"); |
| return; |
| } |
| s = p + 1; |
| for (i = 0; i < 3 && s; i++) { |
| p = strchr(s, ':'); |
| if (p) |
| *p = 0; |
| x[i] = simple_strtoull(s, |
| (char **)&s, 0); |
| if (p) |
| s = p + 1; |
| else |
| break; |
| } |
| if (i == 2) { |
| if (modify_part(name, x)) { |
| fb_tx_status("FAILadd part error"); |
| return; |
| } else { |
| fb_tx_status("OKAY"); |
| return; |
| } |
| } |
| } |
| |
| i = 0; |
| init_partitions(); |
| sprintf(status, "INFO::there %s %d partition", |
| (parts->part_num == 1) ? "is" : "are", |
| parts->part_num); |
| fb_tx_status(status); |
| do { |
| strcpy(status, "INFO"); |
| status[4] = '\0'; |
| for (; i < parts->part_num; i++) { |
| strcat(status, parts->part[i].name); |
| strcat(status, ","); |
| if ((i == (parts->part_num - 1)) || |
| (strlen(status) + strlen(parts->part[i + 1].name) >= (MAX_RESP_SZ - 1))) { |
| i++; |
| fb_tx_status(status); |
| break; |
| } |
| } |
| } while (i < parts->part_num); |
| fb_tx_status("OKAY"); |
| return; |
| } |
| fb_tx_status("FAILbad oem command"); |
| return; |
| } |
| if (memcmp(cmdbuf, "reboot", 6) == 0) { |
| fb_tx_status("OKAY"); |
| if (strncmp(cmdbuf + 6, "-bootloader", 11) == 0) |
| keep_running = 0; |
| else |
| reset_cpu(0); |
| return; |
| } |
| |
| fb_tx_status("FAILinvalid command"); |
| } |
| |
| int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| ALLOC_CACHE_ALIGN_BUFFER(char, cmd_pool, 512); |
| rx_length = 0; |
| part_num = -1; |
| onfly = 0; |
| bootsp = 0; |
| first_rcv = 0; |
| fb_mem = FB_BUFF_ADDR; |
| ramdisk_size = 0; |
| kernel_addr = 0; |
| kernel_size = 0; |
| keep_running = 1; |
| is_yaffs = 0; |
| need_unsparse = 0; |
| |
| init_partitions(); |
| |
| rcv_buf = cmd_pool; |
| fb_init(); |
| fb_set_buf((void *)rcv_buf); |
| while (keep_running) |
| fb_run(); |
| keep_running = 1; |
| fb_halt(); |
| return 0; |
| } |
| |
| U_BOOT_CMD( |
| fb, 1, 1, do_fastboot, |
| "android fastboot client application", |
| "" |
| ); |