1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for distro boot with RAUC
4  *
5  * Copyright 2025 PHYTEC Messtechnik GmbH
6  * Written by Martin Schwan <m.schwan@phytec.de>
7  */
8 
9 #define LOG_CATEGORY UCLASS_BOOTSTD
10 
11 #include <blk.h>
12 #include <bootflow.h>
13 #include <bootmeth.h>
14 #include <bootstd.h>
15 #include <dm.h>
16 #include <env.h>
17 #include <fs.h>
18 #include <malloc.h>
19 #include <mapmem.h>
20 #include <string.h>
21 #include <asm/cache.h>
22 
23 /* Length of env var "BOOT_*_LEFT" */
24 #define BOOT_LEFT_LEN	(5 + 32 + 5)
25 
26 static const char * const script_names[] = { "boot.scr", "boot.scr.uimg", NULL };
27 
28 /**
29  * struct distro_rauc_slot - Slot information
30  *
31  * A slot describes the unit of a bootable system consisting of one or multiple
32  * partitions. This usually includes a root filesystem, kernel and potentially other
33  * files, like device trees and boot scripts for that particular distribution.
34  *
35  * @name	The slot name
36  * @boot_part	The boot partition number on disk
37  * @root_part	The root partition number on disk
38  */
39 struct distro_rauc_slot {
40 	char *name;
41 	int boot_part;
42 	int root_part;
43 };
44 
45 /**
46  * struct distro_rauc_priv - Private data
47  *
48  * @slots	All slots of the device in default order
49  * @boot_order	String of the current boot order containing the active slot names
50  */
51 struct distro_rauc_priv {
52 	struct distro_rauc_slot **slots;
53 };
54 
get_slot(struct distro_rauc_priv * priv,const char * slot_name)55 static struct distro_rauc_slot *get_slot(struct distro_rauc_priv *priv,
56 					 const char *slot_name)
57 {
58 	int i;
59 
60 	for (i = 0; priv->slots[i]->name; i++) {
61 		if (!strcmp(priv->slots[i]->name, slot_name))
62 			return priv->slots[i];
63 	}
64 
65 	return NULL;
66 }
67 
distro_rauc_check(struct udevice * dev,struct bootflow_iter * iter)68 static int distro_rauc_check(struct udevice *dev, struct bootflow_iter *iter)
69 {
70 	/*
71 	 * This distro only works on whole MMC devices, as multiple partitions
72 	 * are needed for an A/B system.
73 	 */
74 	if (bootflow_iter_check_mmc(iter))
75 		return log_msg_ret("mmc", -EOPNOTSUPP);
76 	if (iter->part)
77 		return log_msg_ret("part", -EOPNOTSUPP);
78 
79 	return 0;
80 }
81 
distro_rauc_scan_parts(struct bootflow * bflow)82 static int distro_rauc_scan_parts(struct bootflow *bflow)
83 {
84 	struct blk_desc *desc;
85 	struct distro_rauc_priv *priv;
86 	char *boot_order;
87 	const char **boot_order_list;
88 	int ret;
89 	int i;
90 
91 	if (bflow->blk)
92 		desc = dev_get_uclass_plat(bflow->blk);
93 
94 	priv = bflow->bootmeth_priv;
95 	if (!priv || !priv->slots)
96 		return log_msg_ret("priv", -EINVAL);
97 
98 	boot_order = env_get("BOOT_ORDER");
99 	boot_order_list = str_to_list(boot_order);
100 	for (i = 0; boot_order_list[i]; i++) {
101 		const struct distro_rauc_slot *slot;
102 
103 		slot = get_slot(priv, boot_order_list[i]);
104 		if (!slot)
105 			return log_msg_ret("slot", -EINVAL);
106 		if (desc) {
107 			ret = fs_set_blk_dev_with_part(desc, slot->boot_part);
108 			if (ret)
109 				return log_msg_ret("part", ret);
110 			fs_close();
111 			ret = fs_set_blk_dev_with_part(desc, slot->root_part);
112 			if (ret)
113 				return log_msg_ret("part", ret);
114 			fs_close();
115 		}
116 	}
117 	str_free_list(boot_order_list);
118 
119 	return 0;
120 }
121 
distro_rauc_read_bootflow(struct udevice * dev,struct bootflow * bflow)122 static int distro_rauc_read_bootflow(struct udevice *dev, struct bootflow *bflow)
123 {
124 	struct distro_rauc_priv *priv;
125 	int ret;
126 	char *slot;
127 	int i;
128 	char *partitions;
129 	char *boot_order;
130 	const char *default_boot_order;
131 	const char **default_boot_order_list;
132 	char *boot_order_copy;
133 	char boot_left[BOOT_LEFT_LEN];
134 	char *parts;
135 
136 	/* Get RAUC variables or set their default values */
137 	boot_order = env_get("BOOT_ORDER");
138 	if (!boot_order) {
139 		log_debug("BOOT_ORDER did not exist yet, setting default value\n");
140 		if (env_set("BOOT_ORDER", CONFIG_BOOTMETH_RAUC_BOOT_ORDER))
141 			return log_msg_ret("env", -EPERM);
142 		boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER;
143 	}
144 	default_boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER;
145 	default_boot_order_list = str_to_list(default_boot_order);
146 	for (i = 0; default_boot_order_list[i]; i++) {
147 		sprintf(boot_left, "BOOT_%s_LEFT", default_boot_order_list[i]);
148 		if (!env_get(boot_left)) {
149 			log_debug("%s did not exist yet, setting default value\n",
150 				  boot_left);
151 			if (env_set_ulong(boot_left, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES))
152 				return log_msg_ret("env", -EPERM);
153 		}
154 	}
155 	str_free_list(default_boot_order_list);
156 
157 	priv = calloc(1, sizeof(struct distro_rauc_priv));
158 	if (!priv)
159 		return log_msg_ret("buf", -ENOMEM);
160 	priv->slots = calloc(1, sizeof(struct distro_rauc_slot));
161 
162 	/* Copy default boot_order, so we can leave the original unmodified */
163 	boot_order_copy = strdup(default_boot_order);
164 	partitions = strdup(CONFIG_BOOTMETH_RAUC_PARTITIONS);
165 
166 	for (i = 1;
167 	     (parts = strsep(&partitions, " ")) &&
168 	     (slot = strsep(&boot_order_copy, " "));
169 	     i++) {
170 		struct distro_rauc_slot *s;
171 		struct distro_rauc_slot **new_slots;
172 
173 		s = calloc(1, sizeof(struct distro_rauc_slot));
174 		s->name = strdup(slot);
175 		s->boot_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
176 		s->root_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
177 		new_slots = realloc(priv->slots, (i + 1) *
178 				    sizeof(struct distro_rauc_slot));
179 		if (!new_slots)
180 			return log_msg_ret("buf", -ENOMEM);
181 		priv->slots = new_slots;
182 		priv->slots[i - 1] = s;
183 		priv->slots[i] = NULL;
184 	}
185 
186 	bflow->bootmeth_priv = priv;
187 
188 	ret = distro_rauc_scan_parts(bflow);
189 	if (ret < 0) {
190 		for (i = 0; priv->slots[i]->name; i++) {
191 			free(priv->slots[i]->name);
192 			free(priv->slots[i]);
193 		}
194 		free(priv);
195 		free(boot_order_copy);
196 		bflow->bootmeth_priv = NULL;
197 		return ret;
198 	}
199 
200 	bflow->state = BOOTFLOWST_READY;
201 
202 	return 0;
203 }
204 
distro_rauc_read_file(struct udevice * dev,struct bootflow * bflow,const char * file_path,ulong addr,enum bootflow_img_t type,ulong * sizep)205 static int distro_rauc_read_file(struct udevice *dev, struct bootflow *bflow,
206 				 const char *file_path, ulong addr,
207 				 enum bootflow_img_t type, ulong *sizep)
208 {
209 	/*
210 	 * Reading individual files is not supported since we only operate on
211 	 * whole MMC devices (because we require multiple partitions).
212 	 */
213 	return log_msg_ret("Unsupported", -ENOSYS);
214 }
215 
distro_rauc_load_boot_script(struct bootflow * bflow,const struct distro_rauc_slot * slot)216 static int distro_rauc_load_boot_script(struct bootflow *bflow,
217 					const struct distro_rauc_slot *slot)
218 {
219 	struct blk_desc *desc;
220 	struct distro_rauc_priv *priv;
221 	struct udevice *bootstd;
222 	const char *const *prefixes;
223 	int ret;
224 	int i;
225 	int j;
226 
227 	ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
228 	if (ret)
229 		return log_msg_ret("std", ret);
230 	prefixes = bootstd_get_prefixes(bootstd);
231 
232 	desc = dev_get_uclass_plat(bflow->blk);
233 	priv = bflow->bootmeth_priv;
234 	if (!priv || !priv->slots)
235 		return log_msg_ret("priv", -EINVAL);
236 
237 	bflow->part = slot->boot_part;
238 	if (!bflow->part)
239 		return log_msg_ret("part", -ENOENT);
240 
241 	ret = bootmeth_setup_fs(bflow, desc);
242 	if (ret)
243 		return log_msg_ret("set", ret);
244 
245 	for (i = 0; prefixes[i] && bflow->state != BOOTFLOWST_FILE; i++) {
246 		for (j = 0; script_names[j] && bflow->state != BOOTFLOWST_FILE; j++) {
247 			if (!bootmeth_try_file(bflow, desc, prefixes[i], script_names[j])) {
248 				log_debug("Found file '%s%s' in %s.part_%x\n",
249 					  prefixes[i], script_names[j],
250 					  bflow->dev->name, bflow->part);
251 				bflow->subdir = strdup(prefixes[i]);
252 			}
253 		}
254 	}
255 	if (bflow->state != BOOTFLOWST_FILE)
256 		return log_msg_ret("file", -ENOENT);
257 
258 	ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN,
259 				  (enum bootflow_img_t)IH_TYPE_SCRIPT);
260 	if (ret)
261 		return log_msg_ret("read", ret);
262 
263 	return 0;
264 }
265 
find_active_slot(char ** slot_name,ulong * slot_tries)266 static int find_active_slot(char **slot_name, ulong *slot_tries)
267 {
268 	ulong tries;
269 	char boot_left[BOOT_LEFT_LEN];
270 	char *boot_order;
271 	const char **boot_order_list;
272 	bool slot_found = false;
273 	int ret;
274 	int i;
275 
276 	boot_order = env_get("BOOT_ORDER");
277 	if (!boot_order)
278 		return log_msg_ret("env", -ENOENT);
279 	boot_order_list = str_to_list(boot_order);
280 	for (i = 0; boot_order_list[i] && !slot_found; i++) {
281 		sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
282 		tries = env_get_ulong(boot_left, 10, ULONG_MAX);
283 		if (tries == ULONG_MAX)
284 			return log_msg_ret("env", -ENOENT);
285 
286 		if (tries) {
287 			ret = env_set_ulong(boot_left, tries - 1);
288 			if (ret)
289 				return log_msg_ret("env", ret);
290 			*slot_name = strdup(boot_order_list[i]);
291 			*slot_tries = tries;
292 			slot_found = true;
293 		}
294 	}
295 	str_free_list(boot_order_list);
296 
297 	if (!slot_found) {
298 		if (IS_ENABLED(CONFIG_BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES)) {
299 			log_warning("WARNING: No valid slot found\n");
300 			log_info("INFO: Resetting boot order and all slot tries\n");
301 			boot_order_list = str_to_list(CONFIG_BOOTMETH_RAUC_BOOT_ORDER);
302 			for (i = 0; boot_order_list[i]; i++) {
303 				sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
304 				ret = env_set_ulong(boot_left, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
305 				if (ret)
306 					return log_msg_ret("env", ret);
307 			}
308 			str_free_list(boot_order_list);
309 			ret = env_save();
310 			if (ret)
311 				return log_msg_ret("env", ret);
312 			do_reset(NULL, 0, 0, NULL);
313 		}
314 		log_err("ERROR: No valid slot found\n");
315 		return -EINVAL;
316 	}
317 
318 	return 0;
319 }
320 
distro_rauc_boot(struct udevice * dev,struct bootflow * bflow)321 static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow)
322 {
323 	struct blk_desc *desc;
324 	struct distro_rauc_priv *priv;
325 	const struct distro_rauc_slot *slot;
326 	char *boot_order;
327 	const char **boot_order_list;
328 	char *active_slot;
329 	ulong active_slot_tries;
330 	char raucargs[64];
331 	char boot_left[BOOT_LEFT_LEN];
332 	ulong addr;
333 	int ret = 0;
334 	int i;
335 
336 	desc = dev_get_uclass_plat(bflow->blk);
337 	if (desc->uclass_id != UCLASS_MMC)
338 		return log_msg_ret("blk", -EINVAL);
339 	priv = bflow->bootmeth_priv;
340 
341 	/* Device info variables */
342 	ret = env_set("devtype", blk_get_devtype(bflow->blk));
343 	if (ret)
344 		return log_msg_ret("env", ret);
345 
346 	ret = env_set_hex("devnum", desc->devnum);
347 	if (ret)
348 		return log_msg_ret("env", ret);
349 
350 	/* Find active, valid slot */
351 	ret = find_active_slot(&active_slot, &active_slot_tries);
352 	if (ret)
353 		return log_msg_ret("env", ret);
354 
355 	/* Kernel command line arguments */
356 	sprintf(raucargs, "rauc.slot=%s", active_slot);
357 	ret = env_set("raucargs", raucargs);
358 	if (ret)
359 		return log_msg_ret("env", ret);
360 
361 	/* Active slot info */
362 	slot = get_slot(priv, active_slot);
363 	if (!slot)
364 		return log_msg_ret("env", -ENOENT);
365 	ret = env_set_hex("distro_bootpart", slot->boot_part);
366 	if (ret)
367 		return log_msg_ret("env", ret);
368 	ret = env_set_hex("distro_rootpart", slot->root_part);
369 	if (ret)
370 		return log_msg_ret("env", ret);
371 	ret = env_save();
372 	if (ret)
373 		return log_msg_ret("env", ret);
374 
375 	/* Load distro boot script */
376 	ret = distro_rauc_load_boot_script(bflow, slot);
377 	if (ret)
378 		return log_msg_ret("load", ret);
379 
380 	log_info("INFO: Booting slot %s, %lu of %d tries left\n",
381 		 active_slot, active_slot_tries, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
382 
383 	log_debug("devtype: %s\n", env_get("devtype"));
384 	log_debug("devnum: %s\n", env_get("devnum"));
385 	log_debug("distro_bootpart: %s\n", env_get("distro_bootpart"));
386 	log_debug("distro_rootpart: %s\n", env_get("distro_rootpart"));
387 	log_debug("raucargs: %s\n", env_get("raucargs"));
388 	boot_order = env_get("BOOT_ORDER");
389 	if (!boot_order)
390 		return log_msg_ret("env", -EPERM);
391 	log_debug("BOOT_ORDER: %s\n", boot_order);
392 	boot_order_list = str_to_list(boot_order);
393 	for (i = 0; boot_order_list[i]; i++) {
394 		sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
395 		log_debug("%s: %s\n", boot_left, env_get(boot_left));
396 	}
397 	str_free_list(boot_order_list);
398 
399 	/* Run distro boot script */
400 	addr = map_to_sysmem(bflow->buf);
401 	ret = cmd_source_script(addr, NULL, NULL);
402 	if (ret)
403 		return log_msg_ret("boot", ret);
404 
405 	return 0;
406 }
407 
distro_rauc_bootmeth_bind(struct udevice * dev)408 static int distro_rauc_bootmeth_bind(struct udevice *dev)
409 {
410 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
411 
412 	plat->desc = "RAUC distro boot from MMC";
413 	plat->flags = BOOTMETHF_ANY_PART;
414 
415 	return 0;
416 }
417 
418 static struct bootmeth_ops distro_rauc_bootmeth_ops = {
419 	.check		= distro_rauc_check,
420 	.read_bootflow	= distro_rauc_read_bootflow,
421 	.read_file	= distro_rauc_read_file,
422 	.boot		= distro_rauc_boot,
423 };
424 
425 static const struct udevice_id distro_rauc_bootmeth_ids[] = {
426 	{ .compatible = "u-boot,distro-rauc" },
427 	{ }
428 };
429 
430 U_BOOT_DRIVER(bootmeth_rauc) = {
431 	.name		= "bootmeth_rauc",
432 	.id		= UCLASS_BOOTMETH,
433 	.of_match	= distro_rauc_bootmeth_ids,
434 	.ops		= &distro_rauc_bootmeth_ops,
435 	.bind		= distro_rauc_bootmeth_bind,
436 };
437