treewide: Drop bootm_headers_t typedef
[platform/kernel/u-boot.git] / boot / bootstd-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Uclass implementation for standard boot
4  *
5  * Copyright 2021 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <bootflow.h>
11 #include <bootstd.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <malloc.h>
15 #include <dm/device-internal.h>
16 #include <dm/lists.h>
17 #include <dm/read.h>
18 #include <dm/uclass-internal.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 /* These are used if filename-prefixes is not present */
23 const char *const default_prefixes[] = {"/", "/boot/", NULL};
24
25 static int bootstd_of_to_plat(struct udevice *dev)
26 {
27         struct bootstd_priv *priv = dev_get_priv(dev);
28         int ret;
29
30         if (IS_ENABLED(CONFIG_BOOTSTD_FULL)) {
31                 /* Don't check errors since livetree and flattree are different */
32                 ret = dev_read_string_list(dev, "filename-prefixes",
33                                            &priv->prefixes);
34                 dev_read_string_list(dev, "bootdev-order",
35                                      &priv->bootdev_order);
36         }
37
38         return 0;
39 }
40
41 static void bootstd_clear_glob_(struct bootstd_priv *priv)
42 {
43         while (!list_empty(&priv->glob_head)) {
44                 struct bootflow *bflow;
45
46                 bflow = list_first_entry(&priv->glob_head, struct bootflow,
47                                          glob_node);
48                 bootflow_remove(bflow);
49         }
50 }
51
52 void bootstd_clear_glob(void)
53 {
54         struct bootstd_priv *std;
55
56         if (bootstd_get_priv(&std))
57                 return;
58
59         bootstd_clear_glob_(std);
60 }
61
62 static int bootstd_remove(struct udevice *dev)
63 {
64         struct bootstd_priv *priv = dev_get_priv(dev);
65
66         free(priv->prefixes);
67         free(priv->bootdev_order);
68         bootstd_clear_glob_(priv);
69
70         return 0;
71 }
72
73 const char *const *const bootstd_get_bootdev_order(struct udevice *dev)
74 {
75         struct bootstd_priv *std = dev_get_priv(dev);
76
77         return std->bootdev_order;
78 }
79
80 const char *const *const bootstd_get_prefixes(struct udevice *dev)
81 {
82         struct bootstd_priv *std = dev_get_priv(dev);
83
84         return std->prefixes ? std->prefixes : default_prefixes;
85 }
86
87 int bootstd_get_priv(struct bootstd_priv **stdp)
88 {
89         struct udevice *dev;
90         int ret;
91
92         ret = uclass_first_device_err(UCLASS_BOOTSTD, &dev);
93         if (ret)
94                 return ret;
95         *stdp = dev_get_priv(dev);
96
97         return 0;
98 }
99
100 static int bootstd_probe(struct udevice *dev)
101 {
102         struct bootstd_priv *std = dev_get_priv(dev);
103
104         INIT_LIST_HEAD(&std->glob_head);
105
106         return 0;
107 }
108
109 /* For now, bind the boormethod device if none are found in the devicetree */
110 int dm_scan_other(bool pre_reloc_only)
111 {
112         struct driver *drv = ll_entry_start(struct driver, driver);
113         const int n_ents = ll_entry_count(struct driver, driver);
114         struct udevice *dev, *bootstd;
115         int i, ret;
116
117         /* These are not needed before relocation */
118         if (!(gd->flags & GD_FLG_RELOC))
119                 return 0;
120
121         /* Create a bootstd device if needed */
122         uclass_find_first_device(UCLASS_BOOTSTD, &bootstd);
123         if (!bootstd) {
124                 ret = device_bind_driver(gd->dm_root, "bootstd_drv", "bootstd",
125                                          &bootstd);
126                 if (ret)
127                         return log_msg_ret("bootstd", ret);
128         }
129
130         /* If there are no bootmeth devices, create them */
131         uclass_find_first_device(UCLASS_BOOTMETH, &dev);
132         if (dev)
133                 return 0;
134
135         for (i = 0; i < n_ents; i++, drv++) {
136                 if (drv->id == UCLASS_BOOTMETH) {
137                         const char *name = drv->name;
138
139                         if (!strncmp("bootmeth_", name, 9))
140                                 name += 9;
141                         ret = device_bind(bootstd, drv, name, 0, ofnode_null(),
142                                           &dev);
143                         if (ret)
144                                 return log_msg_ret("meth", ret);
145                 }
146         }
147
148         return 0;
149 }
150
151 static const struct udevice_id bootstd_ids[] = {
152         { .compatible = "u-boot,boot-std" },
153         { }
154 };
155
156 U_BOOT_DRIVER(bootstd_drv) = {
157         .id             = UCLASS_BOOTSTD,
158         .name           = "bootstd_drv",
159         .of_to_plat     = bootstd_of_to_plat,
160         .probe          = bootstd_probe,
161         .remove         = bootstd_remove,
162         .of_match       = bootstd_ids,
163         .priv_auto      = sizeof(struct bootstd_priv),
164 };
165
166 UCLASS_DRIVER(bootstd) = {
167         .id             = UCLASS_BOOTSTD,
168         .name           = "bootstd",
169 #if CONFIG_IS_ENABLED(OF_REAL)
170         .post_bind      = dm_scan_fdt_dev,
171 #endif
172 };