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