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