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