blob: 696efb4b19ed463d783e066f10c7c5de661078d7 [file] [log] [blame]
Simon Glass201417d2022-04-24 23:31:07 -06001// 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 <common.h>
10#include <dm.h>
11#include <bootdev.h>
12#include <bootflow.h>
Simon Glassa8f5be12022-04-24 23:31:09 -060013#include <bootmeth.h>
Simon Glass201417d2022-04-24 23:31:07 -060014#include <bootstd.h>
Simon Glass201417d2022-04-24 23:31:07 -060015#include <fs.h>
16#include <log.h>
17#include <malloc.h>
18#include <part.h>
19#include <sort.h>
20#include <dm/device-internal.h>
21#include <dm/lists.h>
22#include <dm/uclass-internal.h>
23
24enum {
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
36int bootdev_add_bootflow(struct bootflow *bflow)
37{
Simon Glass201417d2022-04-24 23:31:07 -060038 struct bootstd_priv *std;
39 struct bootflow *new;
40 int ret;
41
42 assert(bflow->dev);
43 ret = bootstd_get_priv(&std);
44 if (ret)
45 return ret;
46
47 new = malloc(sizeof(*bflow));
48 if (!new)
49 return log_msg_ret("bflow", -ENOMEM);
50 memcpy(new, bflow, sizeof(*bflow));
51
52 list_add_tail(&new->glob_node, &std->glob_head);
Simon Glasseccb25c2022-07-30 15:52:23 -060053 if (bflow->dev) {
54 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev);
55
56 list_add_tail(&new->bm_node, &ucp->bootflow_head);
57 }
Simon Glass201417d2022-04-24 23:31:07 -060058
59 return 0;
60}
61
62int bootdev_first_bootflow(struct udevice *dev, struct bootflow **bflowp)
63{
64 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
65
66 if (list_empty(&ucp->bootflow_head))
67 return -ENOENT;
68
69 *bflowp = list_first_entry(&ucp->bootflow_head, struct bootflow,
70 bm_node);
71
72 return 0;
73}
74
75int bootdev_next_bootflow(struct bootflow **bflowp)
76{
77 struct bootflow *bflow = *bflowp;
78 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(bflow->dev);
79
80 *bflowp = NULL;
81
82 if (list_is_last(&bflow->bm_node, &ucp->bootflow_head))
83 return -ENOENT;
84
85 *bflowp = list_entry(bflow->bm_node.next, struct bootflow, bm_node);
86
87 return 0;
88}
89
90int bootdev_bind(struct udevice *parent, const char *drv_name, const char *name,
91 struct udevice **devp)
92{
93 struct udevice *dev;
94 char dev_name[30];
95 char *str;
96 int ret;
97
98 snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
99 str = strdup(dev_name);
100 if (!str)
101 return -ENOMEM;
102 ret = device_bind_driver(parent, drv_name, str, &dev);
103 if (ret)
104 return ret;
105 device_set_name_alloced(dev);
106 *devp = dev;
107
108 return 0;
109}
110
111int bootdev_find_in_blk(struct udevice *dev, struct udevice *blk,
112 struct bootflow_iter *iter, struct bootflow *bflow)
113{
114 struct blk_desc *desc = dev_get_uclass_plat(blk);
115 struct disk_partition info;
116 char partstr[20];
117 char name[60];
118 int ret;
119
120 /* Sanity check */
121 if (iter->part >= MAX_PART_PER_BOOTDEV)
122 return log_msg_ret("max", -ESHUTDOWN);
123
124 bflow->blk = blk;
125 if (iter->part)
126 snprintf(partstr, sizeof(partstr), "part_%x", iter->part);
127 else
128 strcpy(partstr, "whole");
129 snprintf(name, sizeof(name), "%s.%s", dev->name, partstr);
130 bflow->name = strdup(name);
131 if (!bflow->name)
132 return log_msg_ret("name", -ENOMEM);
133
134 bflow->part = iter->part;
135
Simon Glassa8f5be12022-04-24 23:31:09 -0600136 ret = bootmeth_check(bflow->method, iter);
137 if (ret)
138 return log_msg_ret("check", ret);
139
Simon Glass201417d2022-04-24 23:31:07 -0600140 /*
141 * partition numbers start at 0 so this cannot succeed, but it can tell
142 * us whether there is valid media there
143 */
144 ret = part_get_info(desc, iter->part, &info);
145 if (!iter->part && ret == -ENOENT)
146 ret = 0;
147
148 /*
149 * This error indicates the media is not present. Otherwise we just
150 * blindly scan the next partition. We could be more intelligent here
151 * and check which partition numbers actually exist.
152 */
153 if (ret == -EOPNOTSUPP)
154 ret = -ESHUTDOWN;
155 else
156 bflow->state = BOOTFLOWST_MEDIA;
157 if (ret)
158 return log_msg_ret("part", ret);
159
160 /*
161 * Currently we don't get the number of partitions, so just
162 * assume a large number
163 */
164 iter->max_part = MAX_PART_PER_BOOTDEV;
165
166 if (iter->part) {
167 ret = fs_set_blk_dev_with_part(desc, bflow->part);
168 bflow->state = BOOTFLOWST_PART;
169
170 /* Use an #ifdef due to info.sys_ind */
171#ifdef CONFIG_DOS_PARTITION
172 log_debug("%s: Found partition %x type %x fstype %d\n",
173 blk->name, bflow->part, info.sys_ind,
174 ret ? -1 : fs_get_type());
175#endif
176 if (ret)
177 return log_msg_ret("fs", ret);
178 bflow->state = BOOTFLOWST_FS;
179 }
180
Simon Glassa8f5be12022-04-24 23:31:09 -0600181 ret = bootmeth_read_bootflow(bflow->method, bflow);
182 if (ret)
183 return log_msg_ret("method", ret);
184
Simon Glass201417d2022-04-24 23:31:07 -0600185 return 0;
186}
187
188void bootdev_list(bool probe)
189{
190 struct udevice *dev;
191 int ret;
192 int i;
193
194 printf("Seq Probed Status Uclass Name\n");
195 printf("--- ------ ------ -------- ------------------\n");
196 if (probe)
Michal Suchanek28a22cd2022-10-12 21:57:53 +0200197 ret = uclass_first_device_check(UCLASS_BOOTDEV, &dev);
Simon Glass201417d2022-04-24 23:31:07 -0600198 else
199 ret = uclass_find_first_device(UCLASS_BOOTDEV, &dev);
200 for (i = 0; dev; i++) {
201 printf("%3x [ %c ] %6s %-9.9s %s\n", dev_seq(dev),
202 device_active(dev) ? '+' : ' ',
203 ret ? simple_itoa(ret) : "OK",
204 dev_get_uclass_name(dev_get_parent(dev)), dev->name);
205 if (probe)
Michal Suchanek28a22cd2022-10-12 21:57:53 +0200206 ret = uclass_next_device_check(&dev);
Simon Glass201417d2022-04-24 23:31:07 -0600207 else
208 ret = uclass_find_next_device(&dev);
209 }
210 printf("--- ------ ------ -------- ------------------\n");
211 printf("(%d bootdev%s)\n", i, i != 1 ? "s" : "");
212}
213
214int bootdev_setup_for_dev(struct udevice *parent, const char *drv_name)
215{
216 struct udevice *bdev;
217 int ret;
218
219 ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV,
220 &bdev);
221 if (ret) {
222 if (ret != -ENODEV) {
223 log_debug("Cannot access bootdev device\n");
224 return ret;
225 }
226
227 ret = bootdev_bind(parent, drv_name, "bootdev", &bdev);
228 if (ret) {
229 log_debug("Cannot create bootdev device\n");
230 return ret;
231 }
232 }
233
234 return 0;
235}
236
237int bootdev_setup_sibling_blk(struct udevice *blk, const char *drv_name)
238{
239 struct udevice *parent, *dev;
240 char dev_name[50];
241 int ret;
242
243 snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
244
245 parent = dev_get_parent(blk);
246 ret = device_find_child_by_name(parent, dev_name, &dev);
247 if (ret) {
248 char *str;
249
250 if (ret != -ENODEV) {
251 log_debug("Cannot access bootdev device\n");
252 return ret;
253 }
254 str = strdup(dev_name);
255 if (!str)
256 return -ENOMEM;
257
258 ret = device_bind_driver(parent, drv_name, str, &dev);
259 if (ret) {
260 log_debug("Cannot create bootdev device\n");
261 return ret;
262 }
263 device_set_name_alloced(dev);
264 }
265
266 return 0;
267}
268
269int bootdev_get_sibling_blk(struct udevice *dev, struct udevice **blkp)
270{
271 struct udevice *parent = dev_get_parent(dev);
272 struct udevice *blk;
273 int ret, len;
274 char *p;
275
276 if (device_get_uclass_id(dev) != UCLASS_BOOTDEV)
277 return -EINVAL;
278
279 /* This should always work if bootdev_setup_sibling_blk() was used */
280 p = strstr(dev->name, ".bootdev");
281 if (!p)
282 return log_msg_ret("str", -EINVAL);
283
284 len = p - dev->name;
285 ret = device_find_child_by_namelen(parent, dev->name, len, &blk);
286 if (ret)
287 return log_msg_ret("find", ret);
288 *blkp = blk;
289
290 return 0;
291}
292
293static int bootdev_get_from_blk(struct udevice *blk, struct udevice **bootdevp)
294{
295 struct udevice *parent = dev_get_parent(blk);
296 struct udevice *bootdev;
297 char dev_name[50];
298 int ret;
299
300 if (device_get_uclass_id(blk) != UCLASS_BLK)
301 return -EINVAL;
302
303 /* This should always work if bootdev_setup_sibling_blk() was used */
304 snprintf(dev_name, sizeof(dev_name), "%s.%s", blk->name, "bootdev");
305 ret = device_find_child_by_name(parent, dev_name, &bootdev);
306 if (ret)
307 return log_msg_ret("find", ret);
308 *bootdevp = bootdev;
309
310 return 0;
311}
312
313int bootdev_unbind_dev(struct udevice *parent)
314{
315 struct udevice *dev;
316 int ret;
317
318 ret = device_find_first_child_by_uclass(parent, UCLASS_BOOTDEV, &dev);
319 if (!ret) {
320 ret = device_remove(dev, DM_REMOVE_NORMAL);
321 if (ret)
322 return log_msg_ret("rem", ret);
323 ret = device_unbind(dev);
324 if (ret)
325 return log_msg_ret("unb", ret);
326 }
327
328 return 0;
329}
330
331/**
332 * bootdev_find_by_label() - Convert a label string to a bootdev device
333 *
334 * Looks up a label name to find the associated bootdev. For example, if the
335 * label name is "mmc2", this will find a bootdev for an mmc device whose
336 * sequence number is 2.
337 *
338 * @label: Label string to convert, e.g. "mmc2"
339 * @devp: Returns bootdev device corresponding to that boot label
340 * Return: 0 if OK, -EINVAL if the label name (e.g. "mmc") does not refer to a
341 * uclass, -ENOENT if no bootdev for that media has the sequence number
342 * (e.g. 2)
343 */
344int bootdev_find_by_label(const char *label, struct udevice **devp)
345{
346 struct udevice *media;
347 struct uclass *uc;
348 enum uclass_id id;
349 const char *end;
350 int seq;
351
352 seq = trailing_strtoln_end(label, NULL, &end);
353 id = uclass_get_by_namelen(label, end - label);
354 log_debug("find %s: seq=%d, id=%d/%s\n", label, seq, id,
355 uclass_get_name(id));
356 if (id == UCLASS_INVALID) {
357 log_warning("Unknown uclass '%s' in label\n", label);
358 return -EINVAL;
359 }
360 if (id == UCLASS_USB)
361 id = UCLASS_MASS_STORAGE;
362
363 /* Iterate through devices in the media uclass (e.g. UCLASS_MMC) */
364 uclass_id_foreach_dev(id, media, uc) {
365 struct udevice *bdev, *blk;
366 int ret;
367
368 /* if there is no seq, match anything */
369 if (seq != -1 && dev_seq(media) != seq) {
370 log_debug("- skip, media seq=%d\n", dev_seq(media));
371 continue;
372 }
373
374 ret = device_find_first_child_by_uclass(media, UCLASS_BOOTDEV,
375 &bdev);
376 if (ret) {
377 log_debug("- looking via blk, seq=%d, id=%d\n", seq,
378 id);
379 ret = blk_find_device(id, seq, &blk);
380 if (!ret) {
381 log_debug("- get from blk %s\n", blk->name);
382 ret = bootdev_get_from_blk(blk, &bdev);
383 }
384 }
385 if (!ret) {
386 log_debug("- found %s\n", bdev->name);
387 *devp = bdev;
388 return 0;
389 }
390 log_debug("- no device in %s\n", media->name);
391 }
392 log_warning("Unknown seq %d for label '%s'\n", seq, label);
393
394 return -ENOENT;
395}
396
397int bootdev_find_by_any(const char *name, struct udevice **devp)
398{
399 struct udevice *dev;
400 int ret, seq;
401 char *endp;
402
403 seq = simple_strtol(name, &endp, 16);
404
405 /* Select by name, label or number */
406 if (*endp) {
407 ret = uclass_get_device_by_name(UCLASS_BOOTDEV, name, &dev);
408 if (ret == -ENODEV) {
409 ret = bootdev_find_by_label(name, &dev);
410 if (ret) {
411 printf("Cannot find bootdev '%s' (err=%d)\n",
412 name, ret);
413 return ret;
414 }
415 ret = device_probe(dev);
416 }
417 if (ret) {
418 printf("Cannot probe bootdev '%s' (err=%d)\n", name,
419 ret);
420 return ret;
421 }
422 } else {
423 ret = uclass_get_device_by_seq(UCLASS_BOOTDEV, seq, &dev);
424 }
425 if (ret) {
426 printf("Cannot find '%s' (err=%d)\n", name, ret);
427 return ret;
428 }
429
430 *devp = dev;
431
432 return 0;
433}
434
435int bootdev_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
436 struct bootflow *bflow)
437{
438 const struct bootdev_ops *ops = bootdev_get_ops(dev);
439
440 if (!ops->get_bootflow)
441 return -ENOSYS;
Simon Glassb190deb2022-10-20 18:22:51 -0600442 bootflow_init(bflow, dev, iter->method);
Simon Glass201417d2022-04-24 23:31:07 -0600443
444 return ops->get_bootflow(dev, iter, bflow);
445}
446
447void bootdev_clear_bootflows(struct udevice *dev)
448{
449 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
450
451 while (!list_empty(&ucp->bootflow_head)) {
452 struct bootflow *bflow;
453
454 bflow = list_first_entry(&ucp->bootflow_head, struct bootflow,
455 bm_node);
Simon Glassa8f5be12022-04-24 23:31:09 -0600456 bootflow_remove(bflow);
Simon Glass201417d2022-04-24 23:31:07 -0600457 }
458}
459
460/**
461 * h_cmp_bootdev() - Compare two bootdevs to find out which should go first
462 *
463 * @v1: struct udevice * of first bootdev device
464 * @v2: struct udevice * of second bootdev device
465 * Return: sort order (<0 if dev1 < dev2, ==0 if equal, >0 if dev1 > dev2)
466 */
467static int h_cmp_bootdev(const void *v1, const void *v2)
468{
469 const struct udevice *dev1 = *(struct udevice **)v1;
470 const struct udevice *dev2 = *(struct udevice **)v2;
471 const struct bootdev_uc_plat *ucp1 = dev_get_uclass_plat(dev1);
472 const struct bootdev_uc_plat *ucp2 = dev_get_uclass_plat(dev2);
473 int diff;
474
475 /* Use priority first */
476 diff = ucp1->prio - ucp2->prio;
477 if (diff)
478 return diff;
479
480 /* Fall back to seq for devices of the same priority */
481 diff = dev_seq(dev1) - dev_seq(dev2);
482
483 return diff;
484}
485
486/**
487 * build_order() - Build the ordered list of bootdevs to use
488 *
489 * This builds an ordered list of devices by one of three methods:
490 * - using the boot_targets environment variable, if non-empty
491 * - using the bootdev-order devicetree property, if present
492 * - sorted by priority and sequence number
493 *
494 * @bootstd: BOOTSTD device to use
495 * @order: Bootdevs listed in default order
496 * @max_count: Number of entries in @order
497 * Return: number of bootdevs found in the ordering, or -E2BIG if the
498 * boot_targets string is too long, or -EXDEV if the ordering produced 0 results
499 */
500static int build_order(struct udevice *bootstd, struct udevice **order,
501 int max_count)
502{
503 const char *overflow_target = NULL;
504 const char *const *labels;
505 struct udevice *dev;
Simon Glass201417d2022-04-24 23:31:07 -0600506 int i, ret, count;
Simon Glass6a6638f2023-01-17 10:47:15 -0700507 bool ok;
Simon Glass201417d2022-04-24 23:31:07 -0600508
Simon Glass6a6638f2023-01-17 10:47:15 -0700509 labels = bootstd_get_bootdev_order(bootstd, &ok);
510 if (!ok)
511 return log_msg_ret("ord", -ENOMEM);
512 if (labels) {
Simon Glass201417d2022-04-24 23:31:07 -0600513 int upto;
514
515 upto = 0;
516 for (i = 0; labels[i]; i++) {
517 ret = bootdev_find_by_label(labels[i], &dev);
518 if (!ret) {
519 if (upto == max_count) {
520 overflow_target = labels[i];
521 break;
522 }
523 order[upto++] = dev;
524 }
525 }
526 count = upto;
527 } else {
528 /* sort them into priority order */
529 count = max_count;
530 qsort(order, count, sizeof(struct udevice *), h_cmp_bootdev);
531 }
532
533 if (overflow_target) {
534 log_warning("Expected at most %d bootdevs, but overflowed with boot_target '%s'\n",
535 max_count, overflow_target);
536 }
537
538 if (!count)
539 return log_msg_ret("targ", -EXDEV);
540
541 return count;
542}
543
544int bootdev_setup_iter_order(struct bootflow_iter *iter, struct udevice **devp)
545{
546 struct udevice *bootstd, *dev = *devp, **order;
547 int upto, i;
548 int count;
549 int ret;
550
551 ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
552 if (ret) {
553 log_err("Missing bootstd device\n");
554 return log_msg_ret("std", ret);
555 }
556
557 /* Handle scanning a single device */
558 if (dev) {
559 iter->flags |= BOOTFLOWF_SINGLE_DEV;
560 return 0;
561 }
562
563 count = uclass_id_count(UCLASS_BOOTDEV);
564 if (!count)
565 return log_msg_ret("count", -ENOENT);
566
567 order = calloc(count, sizeof(struct udevice *));
568 if (!order)
569 return log_msg_ret("order", -ENOMEM);
570
571 /*
572 * Get a list of bootdevs, in seq order (i.e. using aliases). There may
573 * be gaps so try to count up high enough to find them all.
574 */
575 for (i = 0, upto = 0; upto < count && i < 20 + count * 2; i++) {
576 ret = uclass_find_device_by_seq(UCLASS_BOOTDEV, i, &dev);
577 if (!ret)
578 order[upto++] = dev;
579 }
580 log_debug("Found %d bootdevs\n", count);
581 if (upto != count)
582 log_debug("Expected %d bootdevs, found %d using aliases\n",
583 count, upto);
584
Simon Glassa18686c2022-07-30 15:52:20 -0600585 ret = build_order(bootstd, order, upto);
586 if (ret < 0) {
Simon Glass201417d2022-04-24 23:31:07 -0600587 free(order);
Simon Glassa18686c2022-07-30 15:52:20 -0600588 return log_msg_ret("build", ret);
Simon Glass201417d2022-04-24 23:31:07 -0600589 }
590
Simon Glassa18686c2022-07-30 15:52:20 -0600591 iter->num_devs = ret;
Simon Glass201417d2022-04-24 23:31:07 -0600592 iter->dev_order = order;
Simon Glass201417d2022-04-24 23:31:07 -0600593 iter->cur_dev = 0;
594
595 dev = *order;
596 ret = device_probe(dev);
597 if (ret)
598 return log_msg_ret("probe", ret);
599 *devp = dev;
600
601 return 0;
602}
603
604static int bootdev_post_bind(struct udevice *dev)
605{
606 struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
607
608 INIT_LIST_HEAD(&ucp->bootflow_head);
609
610 return 0;
611}
612
613static int bootdev_pre_unbind(struct udevice *dev)
614{
615 bootdev_clear_bootflows(dev);
616
617 return 0;
618}
619
620UCLASS_DRIVER(bootdev) = {
621 .id = UCLASS_BOOTDEV,
622 .name = "bootdev",
623 .flags = DM_UC_FLAG_SEQ_ALIAS,
624 .per_device_plat_auto = sizeof(struct bootdev_uc_plat),
625 .post_bind = bootdev_post_bind,
626 .pre_unbind = bootdev_pre_unbind,
627};