Home | History | Annotate | Download | only in fastboot
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright 2014 Broadcom Corporation.
      4  * Copyright 2015 Free Electrons.
      5  */
      6 
      7 #include <config.h>
      8 #include <common.h>
      9 
     10 #include <fastboot.h>
     11 #include <image-sparse.h>
     12 
     13 #include <linux/mtd/mtd.h>
     14 #include <jffs2/jffs2.h>
     15 #include <nand.h>
     16 
     17 struct fb_nand_sparse {
     18 	struct mtd_info		*mtd;
     19 	struct part_info	*part;
     20 };
     21 
     22 __weak int board_fastboot_erase_partition_setup(char *name)
     23 {
     24 	return 0;
     25 }
     26 
     27 __weak int board_fastboot_write_partition_setup(char *name)
     28 {
     29 	return 0;
     30 }
     31 
     32 static int fb_nand_lookup(const char *partname,
     33 			  struct mtd_info **mtd,
     34 			  struct part_info **part,
     35 			  char *response)
     36 {
     37 	struct mtd_device *dev;
     38 	int ret;
     39 	u8 pnum;
     40 
     41 	ret = mtdparts_init();
     42 	if (ret) {
     43 		pr_err("Cannot initialize MTD partitions\n");
     44 		fastboot_fail("cannot init mtdparts", response);
     45 		return ret;
     46 	}
     47 
     48 	ret = find_dev_and_part(partname, &dev, &pnum, part);
     49 	if (ret) {
     50 		pr_err("cannot find partition: '%s'", partname);
     51 		fastboot_fail("cannot find partition", response);
     52 		return ret;
     53 	}
     54 
     55 	if (dev->id->type != MTD_DEV_TYPE_NAND) {
     56 		pr_err("partition '%s' is not stored on a NAND device",
     57 		      partname);
     58 		fastboot_fail("not a NAND device", response);
     59 		return -EINVAL;
     60 	}
     61 
     62 	*mtd = get_nand_dev_by_index(dev->id->num);
     63 
     64 	return 0;
     65 }
     66 
     67 static int _fb_nand_erase(struct mtd_info *mtd, struct part_info *part)
     68 {
     69 	nand_erase_options_t opts;
     70 	int ret;
     71 
     72 	memset(&opts, 0, sizeof(opts));
     73 	opts.offset = part->offset;
     74 	opts.length = part->size;
     75 	opts.quiet = 1;
     76 
     77 	printf("Erasing blocks 0x%llx to 0x%llx\n",
     78 	       part->offset, part->offset + part->size);
     79 
     80 	ret = nand_erase_opts(mtd, &opts);
     81 	if (ret)
     82 		return ret;
     83 
     84 	printf("........ erased 0x%llx bytes from '%s'\n",
     85 	       part->size, part->name);
     86 
     87 	return 0;
     88 }
     89 
     90 static int _fb_nand_write(struct mtd_info *mtd, struct part_info *part,
     91 			  void *buffer, u32 offset,
     92 			  size_t length, size_t *written)
     93 {
     94 	int flags = WITH_WR_VERIFY;
     95 
     96 #ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS
     97 	flags |= WITH_DROP_FFS;
     98 #endif
     99 
    100 	return nand_write_skip_bad(mtd, offset, &length, written,
    101 				   part->size - (offset - part->offset),
    102 				   buffer, flags);
    103 }
    104 
    105 static lbaint_t fb_nand_sparse_write(struct sparse_storage *info,
    106 		lbaint_t blk, lbaint_t blkcnt, const void *buffer)
    107 {
    108 	struct fb_nand_sparse *sparse = info->priv;
    109 	size_t written;
    110 	int ret;
    111 
    112 	ret = _fb_nand_write(sparse->mtd, sparse->part, (void *)buffer,
    113 			     blk * info->blksz,
    114 			     blkcnt * info->blksz, &written);
    115 	if (ret < 0) {
    116 		printf("Failed to write sparse chunk\n");
    117 		return ret;
    118 	}
    119 
    120 /* TODO - verify that the value "written" includes the "bad-blocks" ... */
    121 
    122 	/*
    123 	 * the return value must be 'blkcnt' ("good-blocks") plus the
    124 	 * number of "bad-blocks" encountered within this space...
    125 	 */
    126 	return written / info->blksz;
    127 }
    128 
    129 static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info,
    130 		lbaint_t blk, lbaint_t blkcnt)
    131 {
    132 	int bad_blocks = 0;
    133 
    134 /*
    135  * TODO - implement a function to determine the total number
    136  * of blocks which must be used in order to reserve the specified
    137  * number ("blkcnt") of "good-blocks", starting at "blk"...
    138  * ( possibly something like the "check_skip_len()" function )
    139  */
    140 
    141 	/*
    142 	 * the return value must be 'blkcnt' ("good-blocks") plus the
    143 	 * number of "bad-blocks" encountered within this space...
    144 	 */
    145 	return blkcnt + bad_blocks;
    146 }
    147 
    148 /**
    149  * fastboot_nand_get_part_info() - Lookup NAND partion by name
    150  *
    151  * @part_name: Named device to lookup
    152  * @part_info: Pointer to returned part_info pointer
    153  * @response: Pointer to fastboot response buffer
    154  */
    155 int fastboot_nand_get_part_info(char *part_name, struct part_info **part_info,
    156 				char *response)
    157 {
    158 	struct mtd_info *mtd = NULL;
    159 
    160 	return fb_nand_lookup(part_name, &mtd, part_info, response);
    161 }
    162 
    163 /**
    164  * fastboot_nand_flash_write() - Write image to NAND for fastboot
    165  *
    166  * @cmd: Named device to write image to
    167  * @download_buffer: Pointer to image data
    168  * @download_bytes: Size of image data
    169  * @response: Pointer to fastboot response buffer
    170  */
    171 void fastboot_nand_flash_write(const char *cmd, void *download_buffer,
    172 			       u32 download_bytes, char *response)
    173 {
    174 	struct part_info *part;
    175 	struct mtd_info *mtd = NULL;
    176 	int ret;
    177 
    178 	ret = fb_nand_lookup(cmd, &mtd, &part, response);
    179 	if (ret) {
    180 		pr_err("invalid NAND device");
    181 		fastboot_fail("invalid NAND device", response);
    182 		return;
    183 	}
    184 
    185 	ret = board_fastboot_write_partition_setup(part->name);
    186 	if (ret)
    187 		return;
    188 
    189 	if (is_sparse_image(download_buffer)) {
    190 		struct fb_nand_sparse sparse_priv;
    191 		struct sparse_storage sparse;
    192 
    193 		sparse_priv.mtd = mtd;
    194 		sparse_priv.part = part;
    195 
    196 		sparse.blksz = mtd->writesize;
    197 		sparse.start = part->offset / sparse.blksz;
    198 		sparse.size = part->size / sparse.blksz;
    199 		sparse.write = fb_nand_sparse_write;
    200 		sparse.reserve = fb_nand_sparse_reserve;
    201 		sparse.mssg = fastboot_fail;
    202 
    203 		printf("Flashing sparse image at offset " LBAFU "\n",
    204 		       sparse.start);
    205 
    206 		sparse.priv = &sparse_priv;
    207 		ret = write_sparse_image(&sparse, cmd, download_buffer,
    208 					 response);
    209 		if (!ret)
    210 			fastboot_okay(NULL, response);
    211 	} else {
    212 		printf("Flashing raw image at offset 0x%llx\n",
    213 		       part->offset);
    214 
    215 		ret = _fb_nand_write(mtd, part, download_buffer, part->offset,
    216 				     download_bytes, NULL);
    217 
    218 		printf("........ wrote %u bytes to '%s'\n",
    219 		       download_bytes, part->name);
    220 	}
    221 
    222 	if (ret) {
    223 		fastboot_fail("error writing the image", response);
    224 		return;
    225 	}
    226 
    227 	fastboot_okay(NULL, response);
    228 }
    229 
    230 /**
    231  * fastboot_nand_flash_erase() - Erase NAND for fastboot
    232  *
    233  * @cmd: Named device to erase
    234  * @response: Pointer to fastboot response buffer
    235  */
    236 void fastboot_nand_erase(const char *cmd, char *response)
    237 {
    238 	struct part_info *part;
    239 	struct mtd_info *mtd = NULL;
    240 	int ret;
    241 
    242 	ret = fb_nand_lookup(cmd, &mtd, &part, response);
    243 	if (ret) {
    244 		pr_err("invalid NAND device");
    245 		fastboot_fail("invalid NAND device", response);
    246 		return;
    247 	}
    248 
    249 	ret = board_fastboot_erase_partition_setup(part->name);
    250 	if (ret)
    251 		return;
    252 
    253 	ret = _fb_nand_erase(mtd, part);
    254 	if (ret) {
    255 		pr_err("failed erasing from device %s", mtd->name);
    256 		fastboot_fail("failed erasing from device", response);
    257 		return;
    258 	}
    259 
    260 	fastboot_okay(NULL, response);
    261 }
    262