ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/common/cmd_fastboot.c b/marvell/uboot/common/cmd_fastboot.c
new file mode 100644
index 0000000..6246589
--- /dev/null
+++ b/marvell/uboot/common/cmd_fastboot.c
@@ -0,0 +1,887 @@
+/*
+ * 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",
+		""
+	  );