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