Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2025 Collabora Ltd. */ #include <blk.h> #include <config.h> #include <env.h> #include <fastboot.h> #include <image-sparse.h> #include <spi.h> #include <spi_flash.h> #include <dm.h> #include <dm/device-internal.h> static struct spi_flash *flash; __weak int board_fastboot_spi_flash_write_setup(void) { return 0; } __weak int board_fastboot_spi_flash_erase_setup(void) { return 0; } static int raw_part_get_info_by_name(const char *name, struct disk_partition *part_info) { /* strlen("fastboot_raw_partition_") + PART_NAME_LEN + 1 */ char env_desc_name[23 + PART_NAME_LEN + 1]; char *raw_part_desc; const char *argv[2]; const char **parg = argv; /* check for raw partition descriptor */ strcpy(env_desc_name, "fastboot_raw_partition_"); strlcat(env_desc_name, name, sizeof(env_desc_name)); raw_part_desc = strdup(env_get(env_desc_name)); if (!raw_part_desc) return -ENODEV; /* parse partition descriptor: <start> <size> */ for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { *parg = strsep(&raw_part_desc, " "); if (!*parg) { pr_err("Invalid number of arguments.\n"); return -ENODEV; } } part_info->start = simple_strtoul(argv[0], NULL, 0); part_info->size = simple_strtoul(argv[1], NULL, 0); strlcpy((char *)part_info->name, name, PART_NAME_LEN); return 0; } static int fastboot_spi_flash_probe(void) { unsigned int bus = CONFIG_SF_DEFAULT_BUS; unsigned int cs = CONFIG_SF_DEFAULT_CS; struct udevice *new, *bus_dev; int ret; /* Remove the old device, otherwise probe will just be a nop */ ret = spi_find_bus_and_cs(bus, cs, &bus_dev, &new); if (!ret) device_remove(new, DM_REMOVE_NORMAL); spi_flash_probe_bus_cs(bus, cs, &new); flash = dev_get_uclass_priv(new); if (!flash) { printf("Failed to initialize SPI flash at %u:%u (error %d)\n", bus, cs, ret); return 1; } return 0; } static int fastboot_spi_flash_unlock(struct spi_flash *flash, struct disk_partition *part_info) { int ret = spi_flash_protect(flash, part_info->start, part_info->size, false); if (ret && ret != -EOPNOTSUPP) { printf("Failed to unlock SPI flash (%d)\n", ret); return ret; } return 0; } static lbaint_t fb_spi_flash_sparse_write(struct sparse_storage *info, lbaint_t blk, lbaint_t blkcnt, const void *buffer) { size_t len = blkcnt * info->blksz; u32 offset = blk * info->blksz; int ret; ret = spi_flash_erase(flash, offset, ROUND(len, flash->erase_size)); if (ret < 0) { printf("Failed to erase sparse chunk (%d)\n", ret); return ret; } ret = spi_flash_write(flash, offset, len, buffer); if (ret < 0) { printf("Failed to write sparse chunk (%d)\n", ret); return ret; } return blkcnt; } static lbaint_t fb_spi_flash_sparse_reserve(struct sparse_storage *info, lbaint_t blk, lbaint_t blkcnt) { return blkcnt; } /** * fastboot_spi_flash_get_part_info() - Lookup SPI partition by name * * @part_name: Named device to lookup * @part_info: Pointer to returned struct disk_partition * @response: Pointer to fastboot response buffer * Return: 0 if OK, -ENOENT if no partition name was given, -ENODEV on invalid * raw partition descriptor */ int fastboot_spi_flash_get_part_info(const char *part_name, struct disk_partition *part_info, char *response) { int ret; if (!part_name || !strcmp(part_name, "")) { fastboot_fail("partition not given", response); return -ENOENT; } /* TODO: Support partitions on the device */ ret = raw_part_get_info_by_name(part_name, part_info); if (ret < 0) fastboot_fail("invalid partition or device", response); return ret; } /** * fastboot_spi_flash_write() - Write image to SPI for fastboot * * @cmd: Named device to write image to * @download_buffer: Pointer to image data * @download_bytes: Size of image data * @response: Pointer to fastboot response buffer */ void fastboot_spi_flash_write(const char *cmd, void *download_buffer, u32 download_bytes, char *response) { struct disk_partition part_info; int ret; if (fastboot_spi_flash_get_part_info(cmd, &part_info, response)) return; if (fastboot_spi_flash_probe()) return; if (board_fastboot_spi_flash_write_setup()) return; if (fastboot_spi_flash_unlock(flash, &part_info)) return; if (is_sparse_image(download_buffer)) { struct sparse_storage sparse; sparse.blksz = flash->sector_size; sparse.start = part_info.start / sparse.blksz; sparse.size = part_info.size / sparse.blksz; sparse.write = fb_spi_flash_sparse_write; sparse.reserve = fb_spi_flash_sparse_reserve; sparse.mssg = fastboot_fail; printf("Flashing sparse image at offset " LBAFU "\n", sparse.start); ret = write_sparse_image(&sparse, cmd, download_buffer, response); } else { printf("Flashing raw image at offset " LBAFU "\n", part_info.start); ret = spi_flash_erase(flash, part_info.start, ROUND(download_bytes, flash->erase_size)); if (ret < 0) { printf("Failed to erase raw image (%d)\n", ret); return; } ret = spi_flash_write(flash, part_info.start, download_bytes, download_buffer); if (ret < 0) { printf("Failed to write raw image (%d)\n", ret); return; } printf("........ wrote %u bytes\n", download_bytes); } if (ret) fastboot_fail("error writing the image", response); else fastboot_okay(NULL, response); } /** * fastboot_spi_flash_erase() - Erase SPI for fastboot * * @cmd: Named device to erase * @response: Pointer to fastboot response buffer */ void fastboot_spi_flash_erase(const char *cmd, char *response) { struct disk_partition part_info; int ret; if (fastboot_spi_flash_get_part_info(cmd, &part_info, response)) return; if (fastboot_spi_flash_probe()) return; if (board_fastboot_spi_flash_erase_setup()) return; if (fastboot_spi_flash_unlock(flash, &part_info)) return; ret = spi_flash_erase(flash, part_info.start, part_info.size); if (ret < 0) { pr_err("failed erasing from SPI flash"); fastboot_fail("failed erasing from SPI flash", response); return; } fastboot_okay(NULL, response); } |