]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - boot/bootdev-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2021 Google LLC
4 * Written by Simon Glass <sjg@chromium.org>
7 #define LOG_CATEGORY UCLASS_BOOTSTD
21 #include <dm/device-internal.h>
23 #include <dm/uclass-internal.h>
27 * Set some sort of limit on the number of partitions a bootdev can
28 * have. Note that for disks this limits the partitions numbers that
29 * are scanned to 1..MAX_BOOTFLOWS_PER_BOOTDEV
31 MAX_PART_PER_BOOTDEV
= 30,
33 /* Maximum supported length of the "boot_targets" env string */
34 BOOT_TARGETS_MAX_LEN
= 100,
37 int bootdev_add_bootflow(struct bootflow
*bflow
)
39 struct bootstd_priv
*std
;
44 ret
= bootstd_get_priv(&std
);
48 new = malloc(sizeof(*bflow
));
50 return log_msg_ret("bflow", -ENOMEM
);
51 memcpy(new, bflow
, sizeof(*bflow
));
53 list_add_tail(&new->glob_node
, &std
->glob_head
);
55 struct bootdev_uc_plat
*ucp
= dev_get_uclass_plat(bflow
->dev
);
57 list_add_tail(&new->bm_node
, &ucp
->bootflow_head
);
63 int bootdev_first_bootflow(struct udevice
*dev
, struct bootflow
**bflowp
)
65 struct bootdev_uc_plat
*ucp
= dev_get_uclass_plat(dev
);
67 if (list_empty(&ucp
->bootflow_head
))
70 *bflowp
= list_first_entry(&ucp
->bootflow_head
, struct bootflow
,
76 int bootdev_next_bootflow(struct bootflow
**bflowp
)
78 struct bootflow
*bflow
= *bflowp
;
79 struct bootdev_uc_plat
*ucp
= dev_get_uclass_plat(bflow
->dev
);
83 if (list_is_last(&bflow
->bm_node
, &ucp
->bootflow_head
))
86 *bflowp
= list_entry(bflow
->bm_node
.next
, struct bootflow
, bm_node
);
91 int bootdev_bind(struct udevice
*parent
, const char *drv_name
, const char *name
,
92 struct udevice
**devp
)
99 snprintf(dev_name
, sizeof(dev_name
), "%s.%s", parent
->name
, name
);
100 str
= strdup(dev_name
);
103 ret
= device_bind_driver(parent
, drv_name
, str
, &dev
);
106 device_set_name_alloced(dev
);
112 int bootdev_find_in_blk(struct udevice
*dev
, struct udevice
*blk
,
113 struct bootflow_iter
*iter
, struct bootflow
*bflow
)
115 struct blk_desc
*desc
= dev_get_uclass_plat(blk
);
116 struct disk_partition info
;
122 if (iter
->part
>= MAX_PART_PER_BOOTDEV
)
123 return log_msg_ret("max", -ESHUTDOWN
);
127 snprintf(partstr
, sizeof(partstr
), "part_%x", iter
->part
);
129 strcpy(partstr
, "whole");
130 snprintf(name
, sizeof(name
), "%s.%s", dev
->name
, partstr
);
131 bflow
->name
= strdup(name
);
133 return log_msg_ret("name", -ENOMEM
);
135 bflow
->part
= iter
->part
;
137 ret
= bootmeth_check(bflow
->method
, iter
);
139 return log_msg_ret("check", ret
);
142 * partition numbers start at 0 so this cannot succeed, but it can tell
143 * us whether there is valid media there
145 ret
= part_get_info(desc
, iter
->part
, &info
);
146 if (!iter
->part
&& ret
== -ENOENT
)
150 * This error indicates the media is not present. Otherwise we just
151 * blindly scan the next partition. We could be more intelligent here
152 * and check which partition numbers actually exist.
154 if (ret
== -EOPNOTSUPP
)
157 bflow
->state
= BOOTFLOWST_MEDIA
;
159 return log_msg_ret("part", ret
);
162 * Currently we don't get the number of partitions, so just
163 * assume a large number
165 iter
->max_part
= MAX_PART_PER_BOOTDEV
;
168 ret
= fs_set_blk_dev_with_part(desc
, bflow
->part
);
169 bflow
->state
= BOOTFLOWST_PART
;
171 /* Use an #ifdef due to info.sys_ind */
172 #ifdef CONFIG_DOS_PARTITION
173 log_debug("%s: Found partition %x type %x fstype %d\n",
174 blk
->name
, bflow
->part
, info
.sys_ind
,
175 ret
? -1 : fs_get_type());
178 return log_msg_ret("fs", ret
);
179 bflow
->state
= BOOTFLOWST_FS
;
182 ret
= bootmeth_read_bootflow(bflow
->method
, bflow
);
184 return log_msg_ret("method", ret
);
189 void bootdev_list(bool probe
)
195 printf("Seq Probed Status Uclass Name\n");
196 printf("--- ------ ------ -------- ------------------\n");
198 ret
= uclass_first_device_check(UCLASS_BOOTDEV
, &dev
);
200 ret
= uclass_find_first_device(UCLASS_BOOTDEV
, &dev
);
201 for (i
= 0; dev
; i
++) {
202 printf("%3x [ %c ] %6s %-9.9s %s\n", dev_seq(dev
),
203 device_active(dev
) ? '+' : ' ',
204 ret
? simple_itoa(ret
) : "OK",
205 dev_get_uclass_name(dev_get_parent(dev
)), dev
->name
);
207 ret
= uclass_next_device_check(&dev
);
209 ret
= uclass_find_next_device(&dev
);
211 printf("--- ------ ------ -------- ------------------\n");
212 printf("(%d bootdev%s)\n", i
, i
!= 1 ? "s" : "");
215 int bootdev_setup_for_dev(struct udevice
*parent
, const char *drv_name
)
217 struct udevice
*bdev
;
220 ret
= device_find_first_child_by_uclass(parent
, UCLASS_BOOTDEV
,
223 if (ret
!= -ENODEV
) {
224 log_debug("Cannot access bootdev device\n");
228 ret
= bootdev_bind(parent
, drv_name
, "bootdev", &bdev
);
230 log_debug("Cannot create bootdev device\n");
238 int bootdev_setup_sibling_blk(struct udevice
*blk
, const char *drv_name
)
240 struct udevice
*parent
, *dev
;
244 snprintf(dev_name
, sizeof(dev_name
), "%s.%s", blk
->name
, "bootdev");
246 parent
= dev_get_parent(blk
);
247 ret
= device_find_child_by_name(parent
, dev_name
, &dev
);
251 if (ret
!= -ENODEV
) {
252 log_debug("Cannot access bootdev device\n");
255 str
= strdup(dev_name
);
259 ret
= device_bind_driver(parent
, drv_name
, str
, &dev
);
261 log_debug("Cannot create bootdev device\n");
264 device_set_name_alloced(dev
);
270 int bootdev_get_sibling_blk(struct udevice
*dev
, struct udevice
**blkp
)
272 struct udevice
*parent
= dev_get_parent(dev
);
277 if (device_get_uclass_id(dev
) != UCLASS_BOOTDEV
)
280 /* This should always work if bootdev_setup_sibling_blk() was used */
281 p
= strstr(dev
->name
, ".bootdev");
283 return log_msg_ret("str", -EINVAL
);
286 ret
= device_find_child_by_namelen(parent
, dev
->name
, len
, &blk
);
288 return log_msg_ret("find", ret
);
294 static int bootdev_get_from_blk(struct udevice
*blk
, struct udevice
**bootdevp
)
296 struct udevice
*parent
= dev_get_parent(blk
);
297 struct udevice
*bootdev
;
301 if (device_get_uclass_id(blk
) != UCLASS_BLK
)
304 /* This should always work if bootdev_setup_sibling_blk() was used */
305 snprintf(dev_name
, sizeof(dev_name
), "%s.%s", blk
->name
, "bootdev");
306 ret
= device_find_child_by_name(parent
, dev_name
, &bootdev
);
308 return log_msg_ret("find", ret
);
314 int bootdev_unbind_dev(struct udevice
*parent
)
319 ret
= device_find_first_child_by_uclass(parent
, UCLASS_BOOTDEV
, &dev
);
321 ret
= device_remove(dev
, DM_REMOVE_NORMAL
);
323 return log_msg_ret("rem", ret
);
324 ret
= device_unbind(dev
);
326 return log_msg_ret("unb", ret
);
333 * bootdev_find_by_label() - Convert a label string to a bootdev device
335 * Looks up a label name to find the associated bootdev. For example, if the
336 * label name is "mmc2", this will find a bootdev for an mmc device whose
337 * sequence number is 2.
339 * @label: Label string to convert, e.g. "mmc2"
340 * @devp: Returns bootdev device corresponding to that boot label
341 * Return: 0 if OK, -EINVAL if the label name (e.g. "mmc") does not refer to a
342 * uclass, -ENOENT if no bootdev for that media has the sequence number
345 int bootdev_find_by_label(const char *label
, struct udevice
**devp
)
347 struct udevice
*media
;
353 seq
= trailing_strtoln_end(label
, NULL
, &end
);
354 id
= uclass_get_by_namelen(label
, end
- label
);
355 log_debug("find %s: seq=%d, id=%d/%s\n", label
, seq
, id
,
356 uclass_get_name(id
));
357 if (id
== UCLASS_INVALID
) {
358 log_warning("Unknown uclass '%s' in label\n", label
);
361 if (id
== UCLASS_USB
)
362 id
= UCLASS_MASS_STORAGE
;
364 /* Iterate through devices in the media uclass (e.g. UCLASS_MMC) */
365 uclass_id_foreach_dev(id
, media
, uc
) {
366 struct udevice
*bdev
, *blk
;
369 /* if there is no seq, match anything */
370 if (seq
!= -1 && dev_seq(media
) != seq
) {
371 log_debug("- skip, media seq=%d\n", dev_seq(media
));
375 ret
= device_find_first_child_by_uclass(media
, UCLASS_BOOTDEV
,
378 log_debug("- looking via blk, seq=%d, id=%d\n", seq
,
380 ret
= blk_find_device(id
, seq
, &blk
);
382 log_debug("- get from blk %s\n", blk
->name
);
383 ret
= bootdev_get_from_blk(blk
, &bdev
);
387 log_debug("- found %s\n", bdev
->name
);
391 log_debug("- no device in %s\n", media
->name
);
393 log_warning("Unknown seq %d for label '%s'\n", seq
, label
);
398 int bootdev_find_by_any(const char *name
, struct udevice
**devp
)
404 seq
= simple_strtol(name
, &endp
, 16);
406 /* Select by name, label or number */
408 ret
= uclass_get_device_by_name(UCLASS_BOOTDEV
, name
, &dev
);
409 if (ret
== -ENODEV
) {
410 ret
= bootdev_find_by_label(name
, &dev
);
412 printf("Cannot find bootdev '%s' (err=%d)\n",
416 ret
= device_probe(dev
);
419 printf("Cannot probe bootdev '%s' (err=%d)\n", name
,
424 ret
= uclass_get_device_by_seq(UCLASS_BOOTDEV
, seq
, &dev
);
427 printf("Cannot find '%s' (err=%d)\n", name
, ret
);
436 int bootdev_get_bootflow(struct udevice
*dev
, struct bootflow_iter
*iter
,
437 struct bootflow
*bflow
)
439 const struct bootdev_ops
*ops
= bootdev_get_ops(dev
);
441 if (!ops
->get_bootflow
)
443 bootflow_init(bflow
, dev
, iter
->method
);
445 return ops
->get_bootflow(dev
, iter
, bflow
);
448 void bootdev_clear_bootflows(struct udevice
*dev
)
450 struct bootdev_uc_plat
*ucp
= dev_get_uclass_plat(dev
);
452 while (!list_empty(&ucp
->bootflow_head
)) {
453 struct bootflow
*bflow
;
455 bflow
= list_first_entry(&ucp
->bootflow_head
, struct bootflow
,
457 bootflow_remove(bflow
);
462 * h_cmp_bootdev() - Compare two bootdevs to find out which should go first
464 * @v1: struct udevice * of first bootdev device
465 * @v2: struct udevice * of second bootdev device
466 * Return: sort order (<0 if dev1 < dev2, ==0 if equal, >0 if dev1 > dev2)
468 static int h_cmp_bootdev(const void *v1
, const void *v2
)
470 const struct udevice
*dev1
= *(struct udevice
**)v1
;
471 const struct udevice
*dev2
= *(struct udevice
**)v2
;
472 const struct bootdev_uc_plat
*ucp1
= dev_get_uclass_plat(dev1
);
473 const struct bootdev_uc_plat
*ucp2
= dev_get_uclass_plat(dev2
);
476 /* Use priority first */
477 diff
= ucp1
->prio
- ucp2
->prio
;
481 /* Fall back to seq for devices of the same priority */
482 diff
= dev_seq(dev1
) - dev_seq(dev2
);
488 * build_order() - Build the ordered list of bootdevs to use
490 * This builds an ordered list of devices by one of three methods:
491 * - using the boot_targets environment variable, if non-empty
492 * - using the bootdev-order devicetree property, if present
493 * - sorted by priority and sequence number
495 * @bootstd: BOOTSTD device to use
496 * @order: Bootdevs listed in default order
497 * @max_count: Number of entries in @order
498 * Return: number of bootdevs found in the ordering, or -E2BIG if the
499 * boot_targets string is too long, or -EXDEV if the ordering produced 0 results
501 static int build_order(struct udevice
*bootstd
, struct udevice
**order
,
504 const char *overflow_target
= NULL
;
505 const char *const *labels
;
510 targets
= env_get("boot_targets");
511 labels
= IS_ENABLED(CONFIG_BOOTSTD_FULL
) ?
512 bootstd_get_bootdev_order(bootstd
) : NULL
;
514 char str
[BOOT_TARGETS_MAX_LEN
];
517 if (strlen(targets
) >= BOOT_TARGETS_MAX_LEN
)
518 return log_msg_ret("len", -E2BIG
);
520 /* make a copy of the string, since strok() will change it */
521 strcpy(str
, targets
);
522 for (i
= 0, target
= strtok(str
, " "); target
;
523 target
= strtok(NULL
, " ")) {
524 ret
= bootdev_find_by_label(target
, &dev
);
526 if (i
== max_count
) {
527 overflow_target
= target
;
538 for (i
= 0; labels
[i
]; i
++) {
539 ret
= bootdev_find_by_label(labels
[i
], &dev
);
541 if (upto
== max_count
) {
542 overflow_target
= labels
[i
];
550 /* sort them into priority order */
552 qsort(order
, count
, sizeof(struct udevice
*), h_cmp_bootdev
);
555 if (overflow_target
) {
556 log_warning("Expected at most %d bootdevs, but overflowed with boot_target '%s'\n",
557 max_count
, overflow_target
);
561 return log_msg_ret("targ", -EXDEV
);
566 int bootdev_setup_iter_order(struct bootflow_iter
*iter
, struct udevice
**devp
)
568 struct udevice
*bootstd
, *dev
= *devp
, **order
;
573 ret
= uclass_first_device_err(UCLASS_BOOTSTD
, &bootstd
);
575 log_err("Missing bootstd device\n");
576 return log_msg_ret("std", ret
);
579 /* Handle scanning a single device */
581 iter
->flags
|= BOOTFLOWF_SINGLE_DEV
;
585 count
= uclass_id_count(UCLASS_BOOTDEV
);
587 return log_msg_ret("count", -ENOENT
);
589 order
= calloc(count
, sizeof(struct udevice
*));
591 return log_msg_ret("order", -ENOMEM
);
594 * Get a list of bootdevs, in seq order (i.e. using aliases). There may
595 * be gaps so try to count up high enough to find them all.
597 for (i
= 0, upto
= 0; upto
< count
&& i
< 20 + count
* 2; i
++) {
598 ret
= uclass_find_device_by_seq(UCLASS_BOOTDEV
, i
, &dev
);
602 log_debug("Found %d bootdevs\n", count
);
604 log_debug("Expected %d bootdevs, found %d using aliases\n",
607 ret
= build_order(bootstd
, order
, upto
);
610 return log_msg_ret("build", ret
);
613 iter
->num_devs
= ret
;
614 iter
->dev_order
= order
;
618 ret
= device_probe(dev
);
620 return log_msg_ret("probe", ret
);
626 static int bootdev_post_bind(struct udevice
*dev
)
628 struct bootdev_uc_plat
*ucp
= dev_get_uclass_plat(dev
);
630 INIT_LIST_HEAD(&ucp
->bootflow_head
);
635 static int bootdev_pre_unbind(struct udevice
*dev
)
637 bootdev_clear_bootflows(dev
);
642 UCLASS_DRIVER(bootdev
) = {
643 .id
= UCLASS_BOOTDEV
,
645 .flags
= DM_UC_FLAG_SEQ_ALIAS
,
646 .per_device_plat_auto
= sizeof(struct bootdev_uc_plat
),
647 .post_bind
= bootdev_post_bind
,
648 .pre_unbind
= bootdev_pre_unbind
,