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