1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #define LOG_CATEGORY UCLASS_BOOTSTD
8 
9 #include <dm.h>
10 #include <bootdev.h>
11 #include <bootflow.h>
12 #include <bootmeth.h>
13 #include <bootstd.h>
14 #include <fs.h>
15 #include <log.h>
16 #include <malloc.h>
17 #include <part.h>
18 #include <sort.h>
19 #include <spl.h>
20 #include <dm/device-internal.h>
21 #include <dm/lists.h>
22 #include <dm/uclass-internal.h>
23 
24 enum {
25 	/*
26 	 * Set some sort of limit on the number of partitions a bootdev can
27 	 * have. Note that for disks this limits the partitions numbers that
28 	 * are scanned to 1..MAX_BOOTFLOWS_PER_BOOTDEV
29 	 */
30 	MAX_PART_PER_BOOTDEV	= 30,
31 
32 	/* Maximum supported length of the "boot_targets" env string */
33 	BOOT_TARGETS_MAX_LEN	= 100,
34 };
35 
bootdev_first_bootflow(struct udevice * dev,struct bootflow ** bflowp)36 int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp)
37 {
38 	struct bootstd_priv *std;
39 	struct bootflow *bflow;
40 	int ret;
41 
42 	ret = bootstd_get_priv(&std);
43 	if (ret)
44 		return log_msg_ret("bff", ret);
45 
46 	bflow = alist_getw(&std->bootflows, 0, struct bootflow);
47 	if (!bflow)
48 		return -ENOENT;
49 	*bflowp = bflow;
50 
51 	return 0;
52 }
53 
bootdev_next_bootflow(struct bootflow ** bflowp)54 int bootdev_next_bootflow(struct bootflow **bflowp)
55 {
56 	struct bootstd_priv *std;
57 	struct bootflow *bflow;
58 	int ret;
59 
60 	ret = bootstd_get_priv(&std);
61 	if (ret)
62 		return log_msg_ret("bff", ret);
63 
64 	bflow = alist_nextw(&std->bootflows, *bflowp);
65 	if (!bflow)
66 		return -ENOENT;
67 	*bflowp = bflow;
68 
69 	return 0;
70 }
71 
bootdev_bind(struct udevice * parent,const char * drv_name,const char * name,struct udevice ** devp)72 int bootdev_bind(struct udevice *parent, const char *drv_name, const char *name,
73 		 struct udevice **devp)
74 {
75 	struct udevice *dev;
76 	char dev_name[30];
77 	char *str;
78 	int ret;
79 
80 	snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
81 	str = strdup(dev_name);
82 	if (!str)
83 		return -ENOMEM;
84 	ret = device_bind_driver(parent, drv_name, str, &dev);
85 	if (ret)
86 		return ret;
87 	device_set_name_alloced(dev);
88 	*devp = dev;
89 
90 	return 0;
91 }
92 
bootdev_find_in_blk(struct udevice * dev,struct udevice * blk,struct bootflow_iter * iter,struct bootflow * bflow)93 int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
94 			struct bootflow_iter *iter, struct bootflow *bflow)
95 {
96 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(bflow->method);
97 	bool allow_any_part = plat->flags & BOOTMETHF_ANY_PART;
98 	struct blk_desc *desc = dev_get_uclass_plat(blk);
99 	struct disk_partition info;
100 	char partstr[20];
101 	char name[60];
102 	int ret;
103 
104 	/* Sanity check */
105 	if (iter->part >= MAX_PART_PER_BOOTDEV)
106 		return log_msg_ret("max", -ESHUTDOWN);
107 
108 	bflow->blk = blk;
109 	if (iter->part)
110 		snprintf(partstr, sizeof(partstr), "part_%x", iter->part);
111 	else
112 		strcpy(partstr, "whole");
113 	snprintf(name, sizeof(name), "%s.%s", dev->name, partstr);
114 	bflow->name = strdup(name);
115 	if (!bflow->name)
116 		return log_msg_ret("name", -ENOMEM);
117 
118 	bflow->part = iter->part;
119 
120 	ret = bootmeth_check(bflow->method, iter);
121 	if (ret)
122 		return log_msg_ret("check", ret);
123 
124 	/*
125 	 * partition numbers start at 0 so this cannot succeed, but it can tell
126 	 * us whether there is valid media there
127 	 */
128 	ret = part_get_info(desc, iter->part, &info);
129 	log_debug("part_get_info() returned %d\n", ret);
130 	if (!iter->part && ret == -ENOENT)
131 		ret = 0;
132 
133 	/*
134 	 * This error indicates the media is not present. Otherwise we just
135 	 * blindly scan the next partition. We could be more intelligent here
136 	 * and check which partition numbers actually exist.
137 	 */
138 	if (ret == -EOPNOTSUPP)
139 		ret = -ESHUTDOWN;
140 	else
141 		bflow->state = BOOTFLOWST_MEDIA;
142 	if (ret && !allow_any_part) {
143 		/* allow partition 1 to be missing */
144 		if (iter->part == 1) {
145 			iter->max_part = 3;
146 			ret = -ENOENT;
147 		}
148 
149 		return log_msg_ret("part", ret);
150 	}
151 
152 	/*
153 	 * Currently we don't get the number of partitions, so just
154 	 * assume a large number
155 	 */
156 	iter->max_part = MAX_PART_PER_BOOTDEV;
157 
158 	if (iter->flags & BOOTFLOWIF_SINGLE_PARTITION) {
159 		/* a particular partition was specified, scan it without checking */
160 	} else if (!iter->part) {
161 		/* This is the whole disk, check if we have bootable partitions */
162 		iter->first_bootable = part_get_bootable(desc);
163 		log_debug("checking bootable=%d\n", iter->first_bootable);
164 	} else if (allow_any_part) {
165 		/*
166 		 * allow any partition to be scanned, by skipping any checks
167 		 * for filesystems or partition contents on this disk
168 		 */
169 
170 	/* if there are bootable partitions, scan only those */
171 	} else if ((iter->flags & BOOTFLOWIF_ONLY_BOOTABLE) &&
172 		   iter->first_bootable >= 0 &&
173 		   (iter->first_bootable ? !info.bootable : iter->part != 1)) {
174 		log_debug("Skipping non-bootable partition %d\n", iter->part);
175 		return log_msg_ret("boot", -EINVAL);
176 	} else {
177 		ret = fs_set_blk_dev_with_part(desc, bflow->part);
178 		bflow->state = BOOTFLOWST_PART;
179 		if (ret)
180 			return log_msg_ret("fs", ret);
181 
182 		log_debug("%s: Found partition %x type %x fstype %d\n",
183 			  blk->name, bflow->part,
184 			  IS_ENABLED(CONFIG_DOS_PARTITION) ?
185 			  disk_partition_sys_ind(&info) : 0,
186 			  ret ? -1 : fs_get_type());
187 		bflow->blk = blk;
188 		bflow->state = BOOTFLOWST_FS;
189 	}
190 
191 	log_debug("method %s\n", bflow->method->name);
192 	ret = bootmeth_read_bootflow(bflow->method, bflow);
193 	if (ret)
194 		return log_msg_ret("method", ret);
195 
196 	return 0;
197 }
198 
bootdev_list(bool probe)199 void bootdev_list(bool probe)
200 {
201 	struct udevice *dev;
202 	int ret;
203 	int i;
204 
205 	printf("Seq  Probed  Status  Uclass    Name\n");
206 	printf("---  ------  ------  --------  ------------------\n");
207 	if (probe)
208 		ret = uclass_first_device_check(UCLASS_BOOTDEV, &dev);
209 	else
210 		ret = uclass_find_first_device(UCLASS_BOOTDEV, &dev);
211 	for (i = 0; dev; i++) {
212 		printf("%3x   [ %c ]  %6s  %-9.9s %s\n", dev_seq(dev),
213 		       device_active(dev) ? '+' : ' ',
214 		       ret ? simple_itoa(-ret) : "OK",
215 		       dev_get_uclass_name(dev_get_parent(dev)), dev->name);
216 		if (probe) {
217 			ret = uclass_next_device_check(&dev);
218 		} else {
219 			uclass_find_next_device(&dev);
220 			ret = 0;
221 		}
222 	}
223 	printf("---  ------  ------  --------  ------------------\n");
224 	printf("(%d bootdev%s)\n", i, i != 1 ? "s" : "");
225 }
226 
bootdev_setup_for_dev(struct udevice * parent,const char * drv_name)227 int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name)
228 {
229 	struct udevice *bdev;
230 	int ret;
231 
232 	ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV,
233 						&bdev);
234 	if (ret) {
235 		if (ret != -ENODEV) {
236 			log_debug("Cannot access bootdev device\n");
237 			return ret;
238 		}
239 
240 		ret = bootdev_bind(parent, drv_name, "bootdev", &bdev);
241 		if (ret) {
242 			log_debug("Cannot create bootdev device\n");
243 			return ret;
244 		}
245 	}
246 
247 	return 0;
248 }
249 
bootdev_get_suffix_start(struct udevice * dev,const char * suffix)250 static int bootdev_get_suffix_start(struct udevice *dev, const char *suffix)
251 {
252 	int len, slen;
253 
254 	len = strlen(dev->name);
255 	slen = strlen(suffix);
256 	if (len > slen && !strcmp(suffix, dev->name + len - slen))
257 		return len - slen;
258 
259 	return len;
260 }
261 
bootdev_setup_for_sibling_blk(struct udevice * blk,const char * drv_name)262 int bootdev_setup_for_sibling_blk(struct udevice *blk, const char *drv_name)
263 {
264 	struct udevice *parent, *dev;
265 	char dev_name[50];
266 	int ret, len;
267 
268 	len = bootdev_get_suffix_start(blk, ".blk");
269 	if (xpl_phase() < PHASE_BOARD_R) {
270 		strlcpy(dev_name, blk->name, sizeof(dev_name) - 5);
271 		strcat(dev_name, ".sib");
272 	} else {
273 		snprintf(dev_name, sizeof(dev_name), "%.*s.%s", len, blk->name,
274 			 "bootdev");
275 	}
276 
277 	parent = dev_get_parent(blk);
278 	ret = device_find_child_by_name(parent, dev_name, &dev);
279 	if (ret) {
280 		char *str;
281 
282 		if (ret != -ENODEV) {
283 			log_debug("Cannot access bootdev device\n");
284 			return ret;
285 		}
286 		str = strdup(dev_name);
287 		if (!str)
288 			return -ENOMEM;
289 
290 		ret = device_bind_driver(parent, drv_name, str, &dev);
291 		if (ret) {
292 			log_debug("Cannot create bootdev device\n");
293 			return ret;
294 		}
295 		device_set_name_alloced(dev);
296 	}
297 
298 	return 0;
299 }
300 
bootdev_get_sibling_blk(struct udevice * dev,struct udevice ** blkp)301 int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp)
302 {
303 	struct udevice *parent = dev_get_parent(dev);
304 	struct udevice *blk;
305 	int ret, len;
306 
307 	if (device_get_uclass_id(dev) != UCLASS_BOOTDEV)
308 		return -EINVAL;
309 
310 	/*
311 	 * This should always work if bootdev_setup_for_sibling_blk() was used
312 	 */
313 	len = bootdev_get_suffix_start(dev, ".bootdev");
314 	ret = device_find_child_by_namelen(parent, dev->name, len, &blk);
315 	if (ret) {
316 		char dev_name[50];
317 
318 		snprintf(dev_name, sizeof(dev_name), "%.*s.blk", len,
319 			 dev->name);
320 		ret = device_find_child_by_name(parent, dev_name, &blk);
321 		if (ret)
322 			return log_msg_ret("find", ret);
323 	}
324 	ret = device_probe(blk);
325 	if (ret)
326 		return log_msg_ret("act", ret);
327 	*blkp = blk;
328 
329 	return 0;
330 }
331 
bootdev_get_from_blk(struct udevice * blk,struct udevice ** bootdevp)332 int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp)
333 {
334 	struct udevice *parent = dev_get_parent(blk);
335 	struct udevice *bootdev;
336 	char dev_name[50];
337 	int ret, len;
338 
339 	if (device_get_uclass_id(blk) != UCLASS_BLK)
340 		return -EINVAL;
341 
342 	/* This should always work if bootdev_setup_for_sibling_blk() was used */
343 	len = bootdev_get_suffix_start(blk, ".blk");
344 	snprintf(dev_name, sizeof(dev_name), "%.*s.%s", len, blk->name,
345 		 "bootdev");
346 	ret = device_find_child_by_name(parent, dev_name, &bootdev);
347 	if (ret)
348 		return log_msg_ret("find", ret);
349 	*bootdevp = bootdev;
350 
351 	return 0;
352 }
353 
bootdev_unbind_dev(struct udevice * parent)354 int bootdev_unbind_dev(struct udevice *parent)
355 {
356 	struct udevice *dev;
357 	int ret;
358 
359 	ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV, &dev);
360 	if (!ret) {
361 		ret = device_remove(dev, DM_REMOVE_NORMAL);
362 		if (ret)
363 			return log_msg_ret("rem", ret);
364 		ret = device_unbind(dev);
365 		if (ret)
366 			return log_msg_ret("unb", ret);
367 	}
368 
369 	return 0;
370 }
371 
372 /**
373  * label_to_uclass() - Convert a label to a uclass and sequence number
374  *
375  * @label: Label to look up (e.g. "mmc1" or "mmc0")
376  * @seqp: Returns the sequence number, or -1 if none
377  * @method_flagsp: If non-NULL, returns any flags implied by the label
378  * (enum bootflow_meth_flags_t), 0 if none
379  * Returns: sequence number on success, -EPFNOSUPPORT is the uclass is not
380  * known, other -ve error code on other error
381  */
label_to_uclass(const char * label,int * seqp,int * method_flagsp)382 static int label_to_uclass(const char *label, int *seqp, int *method_flagsp)
383 {
384 	int seq, len, method_flags;
385 	enum uclass_id id;
386 	const char *end;
387 
388 	method_flags = 0;
389 	seq = trailing_strtoln_end(label, NULL, &end);
390 	len = end - label;
391 	if (!len)
392 		return -EINVAL;
393 	id = uclass_get_by_namelen(label, len);
394 	log_debug("find %s: seq=%d, id=%d/%s\n", label, seq, id,
395 		  uclass_get_name(id));
396 	if (id == UCLASS_INVALID) {
397 		/* try some special cases */
398 		if (IS_ENABLED(CONFIG_BOOTDEV_SPI_FLASH) &&
399 		    !strncmp("spi", label, len)) {
400 			id = UCLASS_SPI_FLASH;
401 		} else if (IS_ENABLED(CONFIG_BOOTDEV_ETH) &&
402 		    !strncmp("pxe", label, len)) {
403 			id = UCLASS_ETH;
404 			method_flags |= BOOTFLOW_METHF_PXE_ONLY;
405 		} else if (IS_ENABLED(CONFIG_BOOTDEV_ETH) &&
406 		    !strncmp("dhcp", label, len)) {
407 			id = UCLASS_ETH;
408 			method_flags |= BOOTFLOW_METHF_DHCP_ONLY;
409 		} else {
410 			return -EPFNOSUPPORT;
411 		}
412 	}
413 	if (id == UCLASS_USB)
414 		id = UCLASS_MASS_STORAGE;
415 	*seqp = seq;
416 	if (method_flagsp)
417 		*method_flagsp = method_flags;
418 
419 	return id;
420 }
421 
bootdev_find_by_label(const char * label,struct udevice ** devp,int * method_flagsp)422 int bootdev_find_by_label(const char *label, struct udevice **devp,
423 			  int *method_flagsp)
424 {
425 	int seq, ret, method_flags = 0;
426 	struct udevice *media;
427 	struct uclass *uc;
428 	enum uclass_id id;
429 
430 	if (!CONFIG_IS_ENABLED(BLK))
431 		return -ENOSYS;
432 
433 	ret = label_to_uclass(label, &seq, &method_flags);
434 	if (ret < 0)
435 		return log_msg_ret("uc", ret);
436 	id = ret;
437 
438 	/* Iterate through devices in the media uclass (e.g. UCLASS_MMC) */
439 	uclass_id_foreach_dev(id, media, uc) {
440 		struct udevice *bdev, *blk;
441 		int ret;
442 
443 		/* if there is no seq, match anything */
444 		if (seq != -1 && dev_seq(media) != seq) {
445 			log_debug("- skip, media seq=%d\n", dev_seq(media));
446 			continue;
447 		}
448 
449 		ret = device_find_first_child_by_uclass(media, UCLASS_BOOTDEV,
450 							&bdev);
451 		if (ret) {
452 			log_debug("- looking via blk, seq=%d, id=%d\n", seq,
453 				  id);
454 			ret = blk_find_device(id, seq, &blk);
455 			if (!ret) {
456 				log_debug("- get from blk %s\n", blk->name);
457 				ret = bootdev_get_from_blk(blk, &bdev);
458 			}
459 		}
460 		if (!ret) {
461 			log_debug("- found %s\n", bdev->name);
462 			*devp = bdev;
463 
464 			/*
465 			 * if no sequence number was provided, we must scan all
466 			 * bootdevs for this media uclass
467 			 */
468 			if (seq == -1)
469 				method_flags |= BOOTFLOW_METHF_SINGLE_UCLASS;
470 			if (method_flagsp)
471 				*method_flagsp = method_flags;
472 			log_debug("method flags %x\n", method_flags);
473 			return 0;
474 		}
475 		log_debug("- no device in %s\n", media->name);
476 	}
477 
478 	return -ENOENT;
479 }
480 
bootdev_find_by_any(const char * name,struct udevice ** devp,int * method_flagsp)481 int bootdev_find_by_any(const char *name, struct udevice **devp,
482 			int *method_flagsp)
483 {
484 	struct udevice *dev;
485 	int method_flags = 0;
486 	int ret = -ENODEV, seq;
487 	char *endp;
488 
489 	seq = simple_strtol(name, &endp, 16);
490 
491 	/* Select by name, label or number */
492 	if (*endp) {
493 		ret = uclass_get_device_by_name(UCLASS_BOOTDEV, name, &dev);
494 		if (ret == -ENODEV) {
495 			ret = bootdev_find_by_label(name, &dev, &method_flags);
496 			if (ret) {
497 				printf("Cannot find bootdev '%s' (err=%d)\n",
498 				       name, ret);
499 				return log_msg_ret("lab", ret);
500 			}
501 			ret = device_probe(dev);
502 		}
503 		if (ret) {
504 			printf("Cannot probe bootdev '%s' (err=%d)\n", name,
505 			       ret);
506 			return log_msg_ret("pro", ret);
507 		}
508 	} else if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
509 		ret = uclass_get_device_by_seq(UCLASS_BOOTDEV, seq, &dev);
510 		method_flags |= BOOTFLOW_METHF_SINGLE_DEV;
511 	}
512 	if (ret) {
513 		printf("Cannot find '%s' (err=%d)\n", name, ret);
514 		return ret;
515 	}
516 
517 	*devp = dev;
518 	if (method_flagsp)
519 		*method_flagsp = method_flags;
520 
521 	return 0;
522 }
523 
bootdev_hunt_and_find_by_label(const char * label,struct udevice ** devp,int * method_flagsp)524 int bootdev_hunt_and_find_by_label(const char *label, struct udevice **devp,
525 				   int *method_flagsp)
526 {
527 	int ret;
528 
529 	ret = bootdev_hunt(label, false);
530 	if (ret)
531 		return log_msg_ret("scn", ret);
532 	ret = bootdev_find_by_label(label, devp, method_flagsp);
533 	if (ret)
534 		return log_msg_ret("fnd", ret);
535 
536 	return 0;
537 }
538 
default_get_bootflow(struct udevice * dev,struct bootflow_iter * iter,struct bootflow * bflow)539 static int default_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
540 				struct bootflow *bflow)
541 {
542 	struct udevice *blk;
543 	int ret;
544 
545 	ret = bootdev_get_sibling_blk(dev, &blk);
546 	log_debug("sibling_blk ret=%d, blk=%s\n", ret,
547 		  ret ? "(none)" : blk->name);
548 	/*
549 	 * If there is no media, indicate that no more partitions should be
550 	 * checked
551 	 */
552 	if (ret == -EOPNOTSUPP)
553 		ret = -ESHUTDOWN;
554 	if (ret)
555 		return log_msg_ret("blk", ret);
556 	assert(blk);
557 	ret = bootdev_find_in_blk(dev, blk, iter, bflow);
558 	if (ret)
559 		return log_msg_ret("find", ret);
560 
561 	return 0;
562 }
563 
bootdev_get_bootflow(struct udevice * dev,struct bootflow_iter * iter,struct bootflow * bflow)564 int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
565 			 struct bootflow *bflow)
566 {
567 	const struct bootdev_ops *ops = bootdev_get_ops(dev);
568 
569 	log_debug("->get_bootflow %s,%x=%p\n", dev->name, iter->part,
570 		  ops->get_bootflow);
571 	bootflow_init(bflow, dev, iter->method);
572 	if (!ops->get_bootflow)
573 		return default_get_bootflow(dev, iter, bflow);
574 
575 	return ops->get_bootflow(dev, iter, bflow);
576 }
577 
bootdev_next_label(struct bootflow_iter * iter,struct udevice ** devp,int * method_flagsp)578 int bootdev_next_label(struct bootflow_iter *iter, struct udevice **devp,
579 		       int *method_flagsp)
580 {
581 	struct udevice *dev;
582 
583 	log_debug("next\n");
584 	if (iter->cur_label >= 0 && !iter->labels[iter->cur_label])
585 		return log_msg_ret("fil", -ENODEV);
586 
587 	for (dev = NULL; !dev && iter->labels[++iter->cur_label];) {
588 		const char *label = iter->labels[iter->cur_label];
589 		int ret;
590 
591 		log_debug("Scanning: %s\n", label);
592 		ret = bootdev_hunt_and_find_by_label(label, &dev,
593 						     method_flagsp);
594 		if (iter->flags & BOOTFLOWIF_SHOW) {
595 			if (ret == -EPFNOSUPPORT) {
596 				log_warning("Unknown uclass '%s' in label\n",
597 					    label);
598 			} else if (ret == -ENOENT) {
599 				/*
600 				 * looking for, e.g. 'scsi0' should find
601 				 * something if SCSI is present
602 				 */
603 				if (!trailing_strtol(label)) {
604 					log_warning("No bootdevs for '%s'\n",
605 						    label);
606 				}
607 			}
608 		}
609 
610 	}
611 
612 	if (!dev)
613 		return log_msg_ret("fin", -ENODEV);
614 	*devp = dev;
615 
616 	return 0;
617 }
618 
bootdev_next_prio(struct bootflow_iter * iter,struct udevice ** devp)619 int bootdev_next_prio(struct bootflow_iter *iter, struct udevice **devp)
620 {
621 	struct udevice *dev = *devp;
622 	bool found;
623 	int ret;
624 
625 	/* find the next device with this priority */
626 	*devp = NULL;
627 	log_debug("next prio %d: dev=%p/%s\n", iter->cur_prio, dev,
628 		  dev ? dev->name : "none");
629 	found = false;
630 	do {
631 		/*
632 		 * Don't probe devices here since they may not be of the
633 		 * required priority
634 		 */
635 		if (!dev)
636 			uclass_find_first_device(UCLASS_BOOTDEV, &dev);
637 		else
638 			uclass_find_next_device(&dev);
639 		found = false;
640 
641 		/* scan for the next device with the correct priority */
642 		while (dev) {
643 			struct bootdev_uc_plat *plat;
644 
645 			plat = dev_get_uclass_plat(dev);
646 			log_debug("- %s: %d, want %d\n", dev->name, plat->prio,
647 				  iter->cur_prio);
648 			if (plat->prio == iter->cur_prio)
649 				break;
650 			uclass_find_next_device(&dev);
651 		}
652 
653 		/* none found for this priority, so move to the next */
654 		if (!dev) {
655 			log_debug("None found at prio %d, moving to %d\n",
656 				  iter->cur_prio, iter->cur_prio + 1);
657 			if (++iter->cur_prio == BOOTDEVP_COUNT)
658 				return log_msg_ret("fin", -ENODEV);
659 
660 			if (iter->flags & BOOTFLOWIF_HUNT) {
661 				/* hunt to find new bootdevs */
662 				ret = bootdev_hunt_prio(iter->cur_prio,
663 							iter->flags &
664 							BOOTFLOWIF_SHOW);
665 				log_debug("- bootdev_hunt_prio() ret %d\n",
666 					  ret);
667 				if (ret)
668 					return log_msg_ret("hun", ret);
669 			}
670 		} else {
671 			ret = device_probe(dev);
672 			if (ret)
673 				log_debug("Device '%s' failed to probe\n",
674 					  dev->name);
675 			else
676 				found = true;
677 		}
678 	} while (!found);
679 
680 	*devp = dev;
681 
682 	return 0;
683 }
684 
bootdev_setup_iter(struct bootflow_iter * iter,const char * label,struct udevice ** devp,int * method_flagsp)685 int bootdev_setup_iter(struct bootflow_iter *iter, const char *label,
686 		       struct udevice **devp, int *method_flagsp)
687 {
688 	struct udevice *bootstd, *dev = NULL;
689 	bool show = iter->flags & BOOTFLOWIF_SHOW;
690 	int method_flags;
691 	char buf[32];
692 	int ret;
693 
694 	if (label) {
695 		const char *end = strchr(label, ':');
696 
697 		if (end) {
698 			size_t len = (size_t)(end - label);
699 			const char *part = end + 1;
700 
701 			if (len + 1 > sizeof(buf)) {
702 				log_err("label \"%s\" is way too long\n", label);
703 				return -EINVAL;
704 			}
705 
706 			memcpy(buf, label, len);
707 			buf[len] = '\0';
708 			label = buf;
709 
710 			unsigned long tmp;
711 
712 			if (strict_strtoul(part, 0, &tmp)) {
713 				log_err("Invalid partition number: %s\n", part);
714 				return -EINVAL;
715 			}
716 
717 			iter->flags |= BOOTFLOWIF_SINGLE_PARTITION;
718 			iter->part = tmp;
719 		}
720 	}
721 
722 	ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
723 	if (ret) {
724 		log_err("Missing bootstd device\n");
725 		return log_msg_ret("std", ret);
726 	}
727 
728 	/* hunt for any pre-scan devices */
729 	if (iter->flags & BOOTFLOWIF_HUNT) {
730 		ret = bootdev_hunt_prio(BOOTDEVP_1_PRE_SCAN, show);
731 		log_debug("- bootdev_hunt_prio() ret %d\n", ret);
732 		if (ret)
733 			return log_msg_ret("pre", ret);
734 	}
735 
736 	/* Handle scanning a single device */
737 	if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && label) {
738 		if (iter->flags & BOOTFLOWIF_HUNT) {
739 			ret = bootdev_hunt(label, show);
740 			if (ret)
741 				return log_msg_ret("hun", ret);
742 		}
743 		ret = bootdev_find_by_any(label, &dev, &method_flags);
744 		if (ret)
745 			return log_msg_ret("lab", ret);
746 
747 		log_debug("method_flags: %x\n", method_flags);
748 		if (method_flags & BOOTFLOW_METHF_SINGLE_UCLASS)
749 			iter->flags |= BOOTFLOWIF_SINGLE_UCLASS;
750 		else if (method_flags & BOOTFLOW_METHF_SINGLE_DEV)
751 			iter->flags |= BOOTFLOWIF_SINGLE_DEV;
752 		else
753 			iter->flags |= BOOTFLOWIF_SINGLE_MEDIA;
754 		log_debug("Selected label: %s, flags %x\n", label, iter->flags);
755 	} else {
756 		bool ok;
757 
758 		/* This either returns a non-empty list or NULL */
759 		iter->labels = bootstd_get_bootdev_order(bootstd, &ok);
760 		if (!ok)
761 			return log_msg_ret("ord", -ENOMEM);
762 		log_debug("setup labels %p\n", iter->labels);
763 		if (iter->labels) {
764 			iter->cur_label = -1;
765 			ret = bootdev_next_label(iter, &dev, &method_flags);
766 		} else {
767 			ret = bootdev_next_prio(iter, &dev);
768 			method_flags = 0;
769 		}
770 		if (!dev)
771 			return log_msg_ret("fin", -ENOENT);
772 		log_debug("Selected bootdev: %s\n", dev->name);
773 	}
774 
775 	ret = device_probe(dev);
776 	if (ret)
777 		return log_msg_ret("probe", ret);
778 	if (method_flagsp)
779 		*method_flagsp = method_flags;
780 	*devp = dev;
781 
782 	return 0;
783 }
784 
bootdev_hunt_drv(struct bootdev_hunter * info,uint seq,bool show)785 static int bootdev_hunt_drv(struct bootdev_hunter *info, uint seq, bool show)
786 {
787 	const char *name = uclass_get_name(info->uclass);
788 	struct bootstd_priv *std;
789 	int ret;
790 
791 	ret = bootstd_get_priv(&std);
792 	if (ret)
793 		return log_msg_ret("std", ret);
794 
795 	if (!(std->hunters_used & BIT(seq))) {
796 		if (show)
797 			printf("Hunting with: %s\n",
798 			       uclass_get_name(info->uclass));
799 		log_debug("Hunting with: %s\n", name);
800 		if (info->hunt) {
801 			ret = info->hunt(info, show);
802 			log_debug("  - hunt result %d\n", ret);
803 			if (ret && ret != -ENOENT)
804 				return ret;
805 		}
806 		std->hunters_used |= BIT(seq);
807 	}
808 
809 	return 0;
810 }
811 
bootdev_hunt(const char * spec,bool show)812 int bootdev_hunt(const char *spec, bool show)
813 {
814 	struct bootdev_hunter *start;
815 	const char *end;
816 	int n_ent, i;
817 	int result;
818 	size_t len;
819 
820 	start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
821 	n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
822 	result = 0;
823 
824 	len = SIZE_MAX;
825 	if (spec) {
826 		trailing_strtoln_end(spec, NULL, &end);
827 		len = end - spec;
828 	}
829 
830 	for (i = 0; i < n_ent; i++) {
831 		struct bootdev_hunter *info = start + i;
832 		const char *name = uclass_get_name(info->uclass);
833 		int ret;
834 
835 		log_debug("looking at %.*s for %s\n",
836 			  (int)max(strlen(name), len), spec, name);
837 		if (spec && strncmp(spec, name, max(strlen(name), len))) {
838 			if (info->uclass != UCLASS_ETH ||
839 			    (strcmp("dhcp", spec) && strcmp("pxe", spec)))
840 				continue;
841 		}
842 		ret = bootdev_hunt_drv(info, i, show);
843 		if (ret)
844 			result = ret;
845 	}
846 
847 	return result;
848 }
849 
bootdev_unhunt(enum uclass_id id)850 int bootdev_unhunt(enum uclass_id id)
851 {
852 	struct bootdev_hunter *start;
853 	int n_ent, i;
854 
855 	start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
856 	n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
857 	for (i = 0; i < n_ent; i++) {
858 		struct bootdev_hunter *info = start + i;
859 
860 		if (info->uclass == id) {
861 			struct bootstd_priv *std;
862 			int ret;
863 
864 			ret = bootstd_get_priv(&std);
865 			if (ret)
866 				return log_msg_ret("std", ret);
867 			if (!(std->hunters_used & BIT(i)))
868 				return -EALREADY;
869 			std->hunters_used &= ~BIT(i);
870 			return 0;
871 		}
872 	}
873 
874 	return -ENOENT;
875 }
876 
bootdev_hunt_prio(enum bootdev_prio_t prio,bool show)877 int bootdev_hunt_prio(enum bootdev_prio_t prio, bool show)
878 {
879 	struct bootdev_hunter *start;
880 	int n_ent, i;
881 	int result;
882 
883 	start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
884 	n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
885 	result = 0;
886 
887 	log_debug("Hunting for priority %d\n", prio);
888 	for (i = 0; i < n_ent; i++) {
889 		struct bootdev_hunter *info = start + i;
890 		int ret;
891 
892 		if (prio != info->prio)
893 			continue;
894 		ret = bootdev_hunt_drv(info, i, show);
895 		log_debug("bootdev_hunt_drv() return %d\n", ret);
896 		if (ret && ret != -ENOENT)
897 			result = ret;
898 	}
899 	log_debug("exit %d\n", result);
900 
901 	return result;
902 }
903 
bootdev_list_hunters(struct bootstd_priv * std)904 void bootdev_list_hunters(struct bootstd_priv *std)
905 {
906 	struct bootdev_hunter *orig, *start;
907 	int n_ent, i;
908 
909 	orig = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
910 	n_ent = ll_entry_count(struct bootdev_hunter, bootdev_hunter);
911 
912 	/*
913 	 * workaround for strange bug in clang-12 which sees all the below data
914 	 * as zeroes. Any access of start seems to fix it, such as
915 	 *
916 	 *    printf("%p", start);
917 	 *
918 	 * Use memcpy() to force the correct behaviour.
919 	 */
920 	memcpy(&start, &orig, sizeof(orig));
921 	printf("%4s  %4s  %-15s  %s\n", "Prio", "Used", "Uclass", "Hunter");
922 	printf("%4s  %4s  %-15s  %s\n", "----", "----", "---------------", "---------------");
923 	for (i = 0; i < n_ent; i++) {
924 		struct bootdev_hunter *info = start + i;
925 
926 		printf("%4d  %4s  %-15s  %s\n", info->prio,
927 		       std->hunters_used & BIT(i) ? "*" : "",
928 		       uclass_get_name(info->uclass),
929 		       info->drv ? info->drv->name : "(none)");
930 	}
931 
932 	printf("(total hunters: %d)\n", n_ent);
933 }
934 
bootdev_pre_unbind(struct udevice * dev)935 static int bootdev_pre_unbind(struct udevice *dev)
936 {
937 	int ret;
938 
939 	ret = bootstd_clear_bootflows_for_bootdev(dev);
940 	if (ret)
941 		return log_msg_ret("bun", ret);
942 
943 	return 0;
944 }
945 
946 UCLASS_DRIVER(bootdev) = {
947 	.id		= UCLASS_BOOTDEV,
948 	.name		= "bootdev",
949 	.flags		= DM_UC_FLAG_SEQ_ALIAS,
950 	.per_device_plat_auto	= sizeof(struct bootdev_uc_plat),
951 	.pre_unbind	= bootdev_pre_unbind,
952 };
953