1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * dfu.c -- DFU back-end routines
4  *
5  * Copyright (C) 2012 Samsung Electronics
6  * author: Lukasz Majewski <l.majewski@samsung.com>
7  */
8 
9 #include <common.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <errno.h>
13 #include <div64.h>
14 #include <dfu.h>
15 #include <ext4fs.h>
16 #include <fat.h>
17 #include <mmc.h>
18 #include <part.h>
19 #include <command.h>
20 
21 static unsigned char *dfu_file_buf;
22 static u64 dfu_file_buf_len;
23 static u64 dfu_file_buf_offset;
24 
mmc_block_op(enum dfu_op op,struct dfu_entity * dfu,u64 offset,void * buf,long * len)25 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
26 			u64 offset, void *buf, long *len)
27 {
28 	struct mmc *mmc;
29 	u32 blk_start, blk_count, n = 0;
30 	int ret, part_num_bkp = 0;
31 
32 	mmc = find_mmc_device(dfu->data.mmc.dev_num);
33 	if (!mmc) {
34 		pr_err("Device MMC %d - not found!", dfu->data.mmc.dev_num);
35 		return -ENODEV;
36 	}
37 
38 	/*
39 	 * We must ensure that we work in lba_blk_size chunks, so ALIGN
40 	 * this value.
41 	 */
42 	*len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
43 
44 	blk_start = dfu->data.mmc.lba_start +
45 			(u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
46 	blk_count = *len / dfu->data.mmc.lba_blk_size;
47 	if (blk_start + blk_count >
48 			dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
49 		puts("Request would exceed designated area!\n");
50 		return -EINVAL;
51 	}
52 
53 	if (dfu->data.mmc.hw_partition >= 0) {
54 		part_num_bkp = mmc_get_blk_desc(mmc)->hwpart;
55 		ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
56 					       dfu->data.mmc.dev_num,
57 					       dfu->data.mmc.hw_partition);
58 		if (ret)
59 			return ret;
60 	}
61 
62 	debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
63 	      op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
64 	      dfu->data.mmc.dev_num, blk_start, blk_count, buf);
65 	switch (op) {
66 	case DFU_OP_READ:
67 		n = blk_dread(mmc_get_blk_desc(mmc), blk_start, blk_count, buf);
68 		break;
69 	case DFU_OP_WRITE:
70 		n = blk_dwrite(mmc_get_blk_desc(mmc), blk_start, blk_count,
71 			       buf);
72 		break;
73 	default:
74 		pr_err("Operation not supported\n");
75 	}
76 
77 	if (n != blk_count) {
78 		pr_err("MMC operation failed");
79 		if (dfu->data.mmc.hw_partition >= 0)
80 			blk_select_hwpart_devnum(IF_TYPE_MMC,
81 						 dfu->data.mmc.dev_num,
82 						 part_num_bkp);
83 		return -EIO;
84 	}
85 
86 	if (dfu->data.mmc.hw_partition >= 0) {
87 		ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
88 					       dfu->data.mmc.dev_num,
89 					       part_num_bkp);
90 		if (ret)
91 			return ret;
92 	}
93 
94 	return 0;
95 }
96 
mmc_file_op(enum dfu_op op,struct dfu_entity * dfu,u64 offset,void * buf,u64 * len)97 static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
98 			u64 offset, void *buf, u64 *len)
99 {
100 	char dev_part_str[8];
101 	int ret;
102 	int fstype;
103 	loff_t size = 0;
104 
105 	switch (dfu->layout) {
106 	case DFU_FS_FAT:
107 		fstype = FS_TYPE_FAT;
108 		break;
109 	case DFU_FS_EXT4:
110 		fstype = FS_TYPE_EXT;
111 		break;
112 	case DFU_SKIP:
113 		return 0;
114 	default:
115 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
116 		       dfu_get_layout(dfu->layout));
117 		return -1;
118 	}
119 
120 	snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
121 		 dfu->data.mmc.dev, dfu->data.mmc.part);
122 
123 	ret = fs_set_blk_dev("mmc", dev_part_str, fstype);
124 	if (ret) {
125 		puts("dfu: fs_set_blk_dev error!\n");
126 		return ret;
127 	}
128 
129 	switch (op) {
130 	case DFU_OP_READ:
131 		ret = fs_read(dfu->name, (size_t)buf, offset, *len, &size);
132 		if (ret) {
133 			puts("dfu: fs_read error!\n");
134 			return ret;
135 		}
136 		*len = size;
137 		break;
138 	case DFU_OP_WRITE:
139 		ret = fs_write(dfu->name, (size_t)buf, offset, *len, &size);
140 		if (ret) {
141 			puts("dfu: fs_write error!\n");
142 			return ret;
143 		}
144 		break;
145 	case DFU_OP_SIZE:
146 		ret = fs_size(dfu->name, &size);
147 		if (ret) {
148 			puts("dfu: fs_size error!\n");
149 			return ret;
150 		}
151 		*len = size;
152 		break;
153 	default:
154 		return -1;
155 	}
156 
157 	return ret;
158 }
159 
mmc_file_buf_write(struct dfu_entity * dfu,u64 offset,void * buf,long * len)160 static int mmc_file_buf_write(struct dfu_entity *dfu, u64 offset, void *buf, long *len)
161 {
162 	int ret = 0;
163 
164 	if (offset == 0) {
165 		dfu_file_buf_len = 0;
166 		dfu_file_buf_offset = 0;
167 	}
168 
169 	/* Add to the current buffer. */
170 	if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE)
171 		*len = CONFIG_SYS_DFU_MAX_FILE_SIZE - dfu_file_buf_len;
172 	memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
173 	dfu_file_buf_len += *len;
174 
175 	if (dfu_file_buf_len == CONFIG_SYS_DFU_MAX_FILE_SIZE) {
176 		ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset,
177 				  dfu_file_buf, &dfu_file_buf_len);
178 		dfu_file_buf_offset += dfu_file_buf_len;
179 		dfu_file_buf_len = 0;
180 	}
181 
182 	return ret;
183 }
184 
mmc_file_buf_write_finish(struct dfu_entity * dfu)185 static int mmc_file_buf_write_finish(struct dfu_entity *dfu)
186 {
187 	int ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset,
188 			dfu_file_buf, &dfu_file_buf_len);
189 
190 	/* Now that we're done */
191 	dfu_file_buf_len = 0;
192 	dfu_file_buf_offset = 0;
193 
194 	return ret;
195 }
196 
dfu_write_medium_mmc(struct dfu_entity * dfu,u64 offset,void * buf,long * len)197 int dfu_write_medium_mmc(struct dfu_entity *dfu,
198 		u64 offset, void *buf, long *len)
199 {
200 	int ret = -1;
201 
202 	switch (dfu->layout) {
203 	case DFU_RAW_ADDR:
204 		ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
205 		break;
206 	case DFU_FS_FAT:
207 	case DFU_FS_EXT4:
208 		ret = mmc_file_buf_write(dfu, offset, buf, len);
209 		break;
210 	case DFU_SCRIPT:
211 		ret = run_command_list(buf, *len, 0);
212 		break;
213 	case DFU_SKIP:
214 		ret = 0;
215 		break;
216 	default:
217 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
218 		       dfu_get_layout(dfu->layout));
219 	}
220 
221 	return ret;
222 }
223 
dfu_flush_medium_mmc(struct dfu_entity * dfu)224 int dfu_flush_medium_mmc(struct dfu_entity *dfu)
225 {
226 	int ret = 0;
227 
228 	switch (dfu->layout) {
229 	case DFU_FS_FAT:
230 	case DFU_FS_EXT4:
231 		ret = mmc_file_buf_write_finish(dfu);
232 		break;
233 	case DFU_SCRIPT:
234 		/* script may have changed the dfu_alt_info */
235 		dfu_reinit_needed = true;
236 		break;
237 	case DFU_RAW_ADDR:
238 	case DFU_SKIP:
239 		break;
240 	default:
241 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
242 		       dfu_get_layout(dfu->layout));
243 	}
244 
245 	return ret;
246 }
247 
dfu_get_medium_size_mmc(struct dfu_entity * dfu,u64 * size)248 int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
249 {
250 	int ret;
251 
252 	switch (dfu->layout) {
253 	case DFU_RAW_ADDR:
254 		*size = dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
255 		return 0;
256 	case DFU_FS_FAT:
257 	case DFU_FS_EXT4:
258 		ret = mmc_file_op(DFU_OP_SIZE, dfu, 0, NULL, size);
259 		if (ret < 0)
260 			return ret;
261 		return 0;
262 	case DFU_SCRIPT:
263 	case DFU_SKIP:
264 		return 0;
265 	default:
266 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
267 		       dfu_get_layout(dfu->layout));
268 		return -1;
269 	}
270 }
271 
272 
mmc_file_buf_read(struct dfu_entity * dfu,u64 offset,void * buf,long * len)273 static int mmc_file_buf_read(struct dfu_entity *dfu, u64 offset, void *buf,
274 			     long *len)
275 {
276 	int ret;
277 
278 	if (offset == 0 || offset >= dfu_file_buf_offset + dfu_file_buf_len ||
279 	    offset + *len < dfu_file_buf_offset) {
280 		u64 file_len = CONFIG_SYS_DFU_MAX_FILE_SIZE;
281 
282 		ret = mmc_file_op(DFU_OP_READ, dfu, offset, dfu_file_buf,
283 				  &file_len);
284 		if (ret < 0)
285 			return ret;
286 		dfu_file_buf_len = file_len;
287 		dfu_file_buf_offset = offset;
288 	}
289 	if (offset + *len > dfu_file_buf_offset + dfu_file_buf_len)
290 		return -EINVAL;
291 
292 	/* Add to the current buffer. */
293 	memcpy(buf, dfu_file_buf + offset - dfu_file_buf_offset, *len);
294 
295 	return 0;
296 }
297 
dfu_read_medium_mmc(struct dfu_entity * dfu,u64 offset,void * buf,long * len)298 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
299 		long *len)
300 {
301 	int ret = -1;
302 
303 	switch (dfu->layout) {
304 	case DFU_RAW_ADDR:
305 		ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
306 		break;
307 	case DFU_FS_FAT:
308 	case DFU_FS_EXT4:
309 		ret = mmc_file_buf_read(dfu, offset, buf, len);
310 		break;
311 	default:
312 		printf("%s: Layout (%s) not (yet) supported!\n", __func__,
313 		       dfu_get_layout(dfu->layout));
314 	}
315 
316 	return ret;
317 }
318 
dfu_free_entity_mmc(struct dfu_entity * dfu)319 void dfu_free_entity_mmc(struct dfu_entity *dfu)
320 {
321 	if (dfu_file_buf) {
322 		free(dfu_file_buf);
323 		dfu_file_buf = NULL;
324 	}
325 }
326 
327 /*
328  * @param s Parameter string containing space-separated arguments:
329  *	1st:
330  *		raw	(raw read/write)
331  *		fat	(files)
332  *		ext4	(^)
333  *		part	(partition image)
334  *	2nd and 3rd:
335  *		lba_start and lba_size, for raw write
336  *		mmc_dev and mmc_part, for filesystems and part
337  *	4th (optional):
338  *		mmcpart <num> (access to HW eMMC partitions)
339  */
dfu_fill_entity_mmc(struct dfu_entity * dfu,char * devstr,char * s)340 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
341 {
342 	const char *entity_type;
343 	ssize_t second_arg;
344 	size_t third_arg;
345 
346 	struct mmc *mmc;
347 
348 	const char *argv[3];
349 	const char **parg = argv;
350 
351 	dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
352 
353 	for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
354 		*parg = strsep(&s, " ");
355 		if (*parg == NULL) {
356 			pr_err("Invalid number of arguments.\n");
357 			return -ENODEV;
358 		}
359 	}
360 
361 	entity_type = argv[0];
362 	/*
363 	 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
364 	 * with default 10.
365 	 */
366 	second_arg = simple_strtol(argv[1], NULL, 0);
367 	third_arg = simple_strtoul(argv[2], NULL, 0);
368 
369 	mmc = find_mmc_device(dfu->data.mmc.dev_num);
370 	if (mmc == NULL) {
371 		pr_err("Couldn't find MMC device no. %d.\n",
372 		      dfu->data.mmc.dev_num);
373 		return -ENODEV;
374 	}
375 
376 	if (mmc_init(mmc)) {
377 		pr_err("Couldn't init MMC device.\n");
378 		return -ENODEV;
379 	}
380 
381 	dfu->data.mmc.hw_partition = -EINVAL;
382 	if (!strcmp(entity_type, "raw")) {
383 		dfu->layout			= DFU_RAW_ADDR;
384 		dfu->data.mmc.lba_start		= second_arg;
385 		dfu->data.mmc.lba_size		= third_arg;
386 		dfu->data.mmc.lba_blk_size	= mmc->read_bl_len;
387 
388 		/*
389 		 * Check for an extra entry at dfu_alt_info env variable
390 		 * specifying the mmc HW defined partition number
391 		 */
392 		if (s)
393 			if (!strcmp(strsep(&s, " "), "mmcpart"))
394 				dfu->data.mmc.hw_partition =
395 					simple_strtoul(s, NULL, 0);
396 
397 	} else if (!strcmp(entity_type, "part")) {
398 		struct disk_partition partinfo;
399 		struct blk_desc *blk_dev = mmc_get_blk_desc(mmc);
400 		int mmcdev = second_arg;
401 		int mmcpart = third_arg;
402 		int offset = 0;
403 
404 		if (part_get_info(blk_dev, mmcpart, &partinfo) != 0) {
405 			pr_err("Couldn't find part #%d on mmc device #%d\n",
406 			      mmcpart, mmcdev);
407 			return -ENODEV;
408 		}
409 
410 		/*
411 		 * Check for an extra entry at dfu_alt_info env variable
412 		 * specifying the mmc HW defined partition number
413 		 */
414 		if (s)
415 			if (!strcmp(strsep(&s, " "), "offset"))
416 				offset = simple_strtoul(s, NULL, 0);
417 
418 		dfu->layout			= DFU_RAW_ADDR;
419 		dfu->data.mmc.lba_start		= partinfo.start + offset;
420 		dfu->data.mmc.lba_size		= partinfo.size-offset;
421 		dfu->data.mmc.lba_blk_size	= partinfo.blksz;
422 	} else if (!strcmp(entity_type, "fat")) {
423 		dfu->layout = DFU_FS_FAT;
424 	} else if (!strcmp(entity_type, "ext4")) {
425 		dfu->layout = DFU_FS_EXT4;
426 	} else if (!strcmp(entity_type, "skip")) {
427 		dfu->layout = DFU_SKIP;
428 	} else if (!strcmp(entity_type, "script")) {
429 		dfu->layout = DFU_SCRIPT;
430 	} else {
431 		pr_err("Memory layout (%s) not supported!\n", entity_type);
432 		return -ENODEV;
433 	}
434 
435 	/* if it's NOT a raw write */
436 	if (strcmp(entity_type, "raw")) {
437 		dfu->data.mmc.dev = (second_arg != -1) ? second_arg :
438 							 dfu->data.mmc.dev_num;
439 		dfu->data.mmc.part = third_arg;
440 	}
441 
442 	dfu->dev_type = DFU_DEV_MMC;
443 	dfu->get_medium_size = dfu_get_medium_size_mmc;
444 	dfu->read_medium = dfu_read_medium_mmc;
445 	dfu->write_medium = dfu_write_medium_mmc;
446 	dfu->flush_medium = dfu_flush_medium_mmc;
447 	dfu->inited = 0;
448 	dfu->free_entity = dfu_free_entity_mmc;
449 
450 	/* Check if file buffer is ready */
451 	if (!dfu_file_buf) {
452 		dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
453 					CONFIG_SYS_DFU_MAX_FILE_SIZE);
454 		if (!dfu_file_buf) {
455 			pr_err("Could not memalign 0x%x bytes",
456 			      CONFIG_SYS_DFU_MAX_FILE_SIZE);
457 			return -ENOMEM;
458 		}
459 	}
460 
461 	return 0;
462 }
463