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 
bootstd_of_to_plat(struct udevice * dev)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 
bootstd_clear_glob_(struct bootstd_priv * priv)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 
bootstd_clear_glob(void)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 
bootstd_remove(struct udevice * dev)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 
bootstd_get_bootdev_order(struct udevice * dev,bool * okp)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 
bootstd_get_prefixes(struct udevice * dev)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 
bootstd_get_priv(struct bootstd_priv ** stdp)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 
bootstd_probe(struct udevice * dev)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 */
dm_scan_other(bool pre_reloc_only)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 };
190