blob: 451d02ddd6d171dddfb8aba09ac3b7f5c01b030f [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2019 NXP
*/
#include <asm/mach-imx/sys_proto.h>
#include <fb_fsl.h>
#include <fastboot.h>
#include <mmc.h>
#include <android_image.h>
#include <asm/bootm.h>
#include <nand.h>
#include <part.h>
#include <sparse_format.h>
#include <image-sparse.h>
#include <image.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/arch/sys_proto.h>
#include <asm/setup.h>
#include <environment.h>
static lbaint_t mmc_sparse_write(struct sparse_storage *info,
lbaint_t blk, lbaint_t blkcnt, const void *buffer)
{
#define SPARSE_FILL_BUF_SIZE (2 * 1024 * 1024)
struct blk_desc *dev_desc = (struct blk_desc *)info->priv;
ulong ret = 0;
void *data;
int fill_buf_num_blks, cnt;
if ((unsigned long)buffer & (CONFIG_SYS_CACHELINE_SIZE - 1)) {
fill_buf_num_blks = SPARSE_FILL_BUF_SIZE / info->blksz;
data = memalign(CONFIG_SYS_CACHELINE_SIZE, fill_buf_num_blks * info->blksz);
while (blkcnt) {
if (blkcnt > fill_buf_num_blks)
cnt = fill_buf_num_blks;
else
cnt = blkcnt;
memcpy(data, buffer, cnt * info->blksz);
ret += blk_dwrite(dev_desc, blk, cnt, data);
blk += cnt;
blkcnt -= cnt;
buffer = (void *)((unsigned long)buffer + cnt * info->blksz);
}
free(data);
} else {
ret = blk_dwrite(dev_desc, blk, blkcnt, buffer);
}
return ret;
}
static lbaint_t mmc_sparse_reserve(struct sparse_storage *info,
lbaint_t blk, lbaint_t blkcnt)
{
return blkcnt;
}
int write_backup_gpt(void *download_buffer)
{
int mmc_no = 0;
struct mmc *mmc;
struct blk_desc *dev_desc;
mmc_no = fastboot_devinfo.dev_id;
mmc = find_mmc_device(mmc_no);
if (mmc == NULL) {
printf("invalid mmc device\n");
return -1;
}
dev_desc = blk_get_dev("mmc", mmc_no);
if (dev_desc == NULL) {
printf("Can't get Block device MMC %d\n",
mmc_no);
return -ENODEV;
}
/* write backup get partition */
if (write_backup_gpt_partitions(dev_desc, download_buffer)) {
printf("writing GPT image fail\n");
return -1;
}
printf("flash backup gpt image successfully\n");
return 0;
}
static int get_fastboot_target_dev(char *mmc_dev, struct fastboot_ptentry *ptn)
{
int dev = 0;
struct mmc *target_mmc;
/* Support flash bootloader to mmc 'target_ubootdev' devices, if the
* 'target_ubootdev' env is not set just flash bootloader to current
* mmc device.
*/
if ((!strncmp(ptn->name, FASTBOOT_PARTITION_BOOTLOADER,
sizeof(FASTBOOT_PARTITION_BOOTLOADER))) &&
(env_get("target_ubootdev"))) {
dev = simple_strtoul(env_get("target_ubootdev"), NULL, 10);
/* if target_ubootdev is set, it must be that users want to change
* fastboot device, then fastboot environment need to be updated */
fastboot_setup();
target_mmc = find_mmc_device(dev);
if ((target_mmc == NULL) || mmc_init(target_mmc)) {
printf("MMC card init failed!\n");
return -1;
} else {
printf("Flash target is mmc%d\n", dev);
if (target_mmc->part_config != MMCPART_NOAVAILABLE)
sprintf(mmc_dev, "mmc dev %x %x", dev, /*slot no*/
FASTBOOT_MMC_BOOT_PARTITION_ID/*part no*/);
else
sprintf(mmc_dev, "mmc dev %x", dev);
}
} else if (ptn->partition_id != FASTBOOT_MMC_NONE_PARTITION_ID)
sprintf(mmc_dev, "mmc dev %x %x",
fastboot_devinfo.dev_id, /*slot no*/
ptn->partition_id /*part no*/);
else
sprintf(mmc_dev, "mmc dev %x",
fastboot_devinfo.dev_id /*slot no*/);
return 0;
}
static void process_flash_blkdev(const char *cmdbuf, void *download_buffer,
u32 download_bytes, char *response)
{
if (download_bytes) {
struct fastboot_ptentry *ptn;
/* Next is the partition name */
ptn = fastboot_flash_find_ptn(cmdbuf);
if (ptn == NULL) {
fastboot_fail("partition does not exist", response);
fastboot_flash_dump_ptn();
} else if ((download_bytes >
ptn->length * MMC_SATA_BLOCK_SIZE) &&
!(ptn->flags & FASTBOOT_PTENTRY_FLAGS_WRITE_ENV)) {
printf("Image too large for the partition\n");
fastboot_fail("image too large for partition", response);
} else {
unsigned int temp;
char blk_dev[128];
char blk_write[128];
int blkret;
printf("writing to partition '%s'\n", ptn->name);
/* Get target flash device. */
if (get_fastboot_target_dev(blk_dev, ptn) != 0)
return;
if (!fastboot_parts_is_raw(ptn) &&
is_sparse_image(download_buffer)) {
int dev_no = 0;
struct mmc *mmc;
struct blk_desc *dev_desc;
disk_partition_t info;
struct sparse_storage sparse;
int err;
dev_no = fastboot_devinfo.dev_id;
printf("sparse flash target is %s:%d\n",
fastboot_devinfo.type == DEV_SATA ? "sata" : "mmc",
dev_no);
if (fastboot_devinfo.type == DEV_MMC) {
mmc = find_mmc_device(dev_no);
if (mmc && mmc_init(mmc))
printf("MMC card init failed!\n");
}
dev_desc = blk_get_dev(fastboot_devinfo.type == DEV_SATA ? "sata" : "mmc", dev_no);
if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
printf("** Block device %s %d not supported\n",
fastboot_devinfo.type == DEV_SATA ? "sata" : "mmc",
dev_no);
return;
}
if( strncmp(ptn->name, FASTBOOT_PARTITION_ALL,
strlen(FASTBOOT_PARTITION_ALL)) == 0) {
info.blksz = dev_desc->blksz;
info.size = dev_desc->lba;
info.start = 0;
} else {
if (part_get_info(dev_desc,
ptn->partition_index, &info)) {
printf("Bad partition index:%d for partition:%s\n",
ptn->partition_index, ptn->name);
return;
}
}
printf("writing to partition '%s' for sparse, buffer size %d\n",
ptn->name, download_bytes);
sparse.blksz = info.blksz;
sparse.start = info.start;
sparse.size = info.size;
sparse.write = mmc_sparse_write;
sparse.reserve = mmc_sparse_reserve;
sparse.mssg = fastboot_fail;
printf("Flashing sparse image at offset " LBAFU "\n",
sparse.start);
sparse.priv = dev_desc;
err = write_sparse_image(&sparse, ptn->name, download_buffer,
response);
if (!err)
fastboot_okay(NULL, response);
} else {
/* Will flash images in below case:
* 1. Is not gpt partition.
* 2. Is gpt partition but no overlay detected.
* */
if (strncmp(ptn->name, "gpt", 3) || !bootloader_gpt_overlay()) {
/* block count */
if (strncmp(ptn->name, "gpt", 3) == 0) {
temp = (ANDROID_GPT_END +
MMC_SATA_BLOCK_SIZE - 1) /
MMC_SATA_BLOCK_SIZE;
} else {
temp = (download_bytes +
MMC_SATA_BLOCK_SIZE - 1) /
MMC_SATA_BLOCK_SIZE;
}
sprintf(blk_write, "%s write 0x%x 0x%x 0x%x",
fastboot_devinfo.type == DEV_SATA ? "sata" : "mmc",
(unsigned int)(uintptr_t)download_buffer, /*source*/
ptn->start, /*dest*/
temp /*length*/);
printf("Initializing '%s'\n", ptn->name);
blkret = run_command(blk_dev, 0);
if (blkret)
fastboot_fail("Init of BLK device failed", response);
else
fastboot_okay(NULL, response);
printf("Writing '%s'\n", ptn->name);
if (run_command(blk_write, 0)) {
printf("Writing '%s' FAILED!\n", ptn->name);
fastboot_fail("Write partition failed", response);
} else {
printf("Writing '%s' DONE!\n", ptn->name);
fastboot_okay(NULL, response);
}
}
/* Write backup gpt image */
if (strncmp(ptn->name, "gpt", 3) == 0) {
if (write_backup_gpt(download_buffer))
fastboot_fail("write backup GPT image fail", response);
else
fastboot_okay(NULL, response);
/* will force scan the device,
* so dev_desc can be re-inited
* with the latest data */
run_command(blk_dev, 0);
}
}
}
} else {
fastboot_fail("no image downloaded", response);
}
}
static void process_erase_blkdev(const char *cmdbuf, char *response)
{
int mmc_no = 0;
lbaint_t blks, blks_start, blks_size, grp_size;
struct mmc *mmc;
struct blk_desc *dev_desc;
struct fastboot_ptentry *ptn;
disk_partition_t info;
ptn = fastboot_flash_find_ptn(cmdbuf);
if ((ptn == NULL) || (ptn->flags & FASTBOOT_PTENTRY_FLAGS_UNERASEABLE)) {
fastboot_fail("partition does not exist or uneraseable", response);
fastboot_flash_dump_ptn();
return;
}
if (fastboot_devinfo.type == DEV_SATA) {
printf("Not support erase on SATA\n");
return;
}
mmc_no = fastboot_devinfo.dev_id;
printf("erase target is MMC:%d\n", mmc_no);
mmc = find_mmc_device(mmc_no);
if ((mmc == NULL) || mmc_init(mmc)) {
printf("MMC card init failed!\n");
return;
}
dev_desc = blk_get_dev("mmc", mmc_no);
if (NULL == dev_desc) {
printf("Block device MMC %d not supported\n",
mmc_no);
fastboot_fail("not valid MMC card", response);
return;
}
if (part_get_info(dev_desc,
ptn->partition_index, &info)) {
printf("Bad partition index:%d for partition:%s\n",
ptn->partition_index, ptn->name);
fastboot_fail("erasing of MMC card", response);
return;
}
/* Align blocks to erase group size to avoid erasing other partitions */
grp_size = mmc->erase_grp_size;
blks_start = (info.start + grp_size - 1) & ~(grp_size - 1);
if (info.size >= grp_size)
blks_size = (info.size - (blks_start - info.start)) &
(~(grp_size - 1));
else
blks_size = 0;
printf("Erasing blocks " LBAFU " to " LBAFU " due to alignment\n",
blks_start, blks_start + blks_size);
blks = blk_derase(dev_desc, blks_start, blks_size);
if (blks != blks_size) {
printf("failed erasing from device %d", dev_desc->devnum);
fastboot_fail("erasing of MMC card", response);
return;
}
printf("........ erased " LBAFU " bytes from '%s'\n",
blks_size * info.blksz, cmdbuf);
fastboot_okay(NULL, response);
return;
}
#ifdef CONFIG_FLASH_MCUFIRMWARE_SUPPORT
static void process_flash_sf(const char *cmdbuf, void *download_buffer,
u32 download_bytes, char *response)
{
int blksz = 0;
blksz = get_block_size();
if (download_bytes) {
struct fastboot_ptentry *ptn;
ptn = fastboot_flash_find_ptn(cmdbuf);
if (ptn == 0) {
fastboot_fail("partition does not exist", response);
fastboot_flash_dump_ptn();
} else if ((download_bytes > ptn->length * blksz)) {
fastboot_fail("image too large for partition", response);
/* TODO : Improve check for yaffs write */
} else {
int ret;
char sf_command[128];
/* Normal case */
/* Probe device */
sprintf(sf_command, "sf probe");
ret = run_command(sf_command, 0);
if (ret){
fastboot_fail("Probe sf failed", response);
return;
}
/* Erase */
sprintf(sf_command, "sf erase 0x%x 0x%x", ptn->start * blksz, /*start*/
ptn->length * blksz /*size*/);
ret = run_command(sf_command, 0);
if (ret) {
fastboot_fail("Erasing sf failed", response);
return;
}
/* Write image */
sprintf(sf_command, "sf write 0x%x 0x%x 0x%x",
(unsigned int)(ulong)download_buffer, /* source */
ptn->start * blksz, /* start */
download_bytes /*size*/);
printf("sf write '%s'\n", ptn->name);
ret = run_command(sf_command, 0);
if (ret){
fastboot_fail("Writing sf failed", response);
return;
}
printf("sf write finished '%s'\n", ptn->name);
fastboot_okay(NULL, response);
}
} else {
fastboot_fail("no image downloaded", response);
}
}
#ifdef CONFIG_ARCH_IMX8M
/* Check if the mcu image is built for running from TCM */
static bool is_tcm_image(unsigned char *image_addr)
{
u32 stack;
stack = *(u32 *)image_addr;
if ((stack != (u32)ANDROID_MCU_FIRMWARE_HEADER_STACK)) {
printf("Please flash mcu firmware images for running from TCM\n");
return false;
} else
return true;
}
#endif
#endif
void fastboot_process_erase(const char *cmdbuf, char *response)
{
switch (fastboot_devinfo.type) {
case DEV_SATA:
case DEV_MMC:
process_erase_blkdev(cmdbuf, response);
break;
default:
printf("Not support flash command for current device %d\n",
fastboot_devinfo.type);
fastboot_fail("failed to flash device", response);
break;
}
}
void fastboot_process_flash(const char *cmdbuf, void *download_buffer,
u32 download_bytes, char *response)
{
/* Check if we need to flash mcu firmware */
#ifdef CONFIG_FLASH_MCUFIRMWARE_SUPPORT
if (!strncmp(cmdbuf, FASTBOOT_MCU_FIRMWARE_PARTITION,
sizeof(FASTBOOT_MCU_FIRMWARE_PARTITION))) {
switch (fastboot_firmwareinfo.type) {
case DEV_SF:
process_flash_sf(cmdbuf, download_buffer,
download_bytes, response);
break;
#ifdef CONFIG_ARCH_IMX8M
case DEV_MMC:
if (is_tcm_image(download_buffer))
process_flash_blkdev(cmdbuf, download_buffer,
download_bytes, response);
break;
#endif
default:
printf("Don't support flash firmware\n");
}
return;
}
#endif
/* Normal case */
switch (fastboot_devinfo.type) {
case DEV_SATA:
case DEV_MMC:
process_flash_blkdev(cmdbuf, download_buffer,
download_bytes, response);
break;
default:
printf("Not support flash command for current device %d\n",
fastboot_devinfo.type);
fastboot_fail("failed to flash device", response);
break;
}
}