blob: fc60e6e355dabef4d58bf1203997b41d99c536f9 [file] [log] [blame]
Martin Schwand8f36162025-06-04 14:15:29 +02001// 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
26static 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 */
39struct 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 */
51struct distro_rauc_priv {
52 struct distro_rauc_slot **slots;
53};
54
55static 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
68static 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
82static int distro_rauc_scan_boot_part(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 bool exists;
89 int ret;
90 int i;
91 int j;
92
93 desc = dev_get_uclass_plat(bflow->blk);
94
95 priv = bflow->bootmeth_priv;
96 if (!priv || !priv->slots)
97 return log_msg_ret("priv", -EINVAL);
98
99 boot_order = env_get("BOOT_ORDER");
100 boot_order_list = str_to_list(boot_order);
101 for (i = 0; boot_order_list[i]; i++) {
102 exists = false;
103 for (j = 0; script_names[j]; j++) {
104 const struct distro_rauc_slot *slot;
105
106 slot = get_slot(priv, boot_order_list[i]);
107 if (!slot)
108 return log_msg_ret("env", -ENOENT);
109 ret = fs_set_blk_dev_with_part(desc, slot->boot_part);
110 if (ret)
111 return log_msg_ret("blk", ret);
112 exists |= fs_exists(script_names[j]);
113 }
114 if (!exists)
115 return log_msg_ret("fs", -ENOENT);
116 }
117 str_free_list(boot_order_list);
118
119 return 0;
120}
121
122static 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
172 s = calloc(1, sizeof(struct distro_rauc_slot));
173 s->name = strdup(slot);
174 s->boot_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
175 s->root_part = simple_strtoul(strsep(&parts, ","), NULL, 10);
176 priv->slots = realloc(priv->slots, (i + 1) *
177 sizeof(struct distro_rauc_slot));
178 priv->slots[i - 1] = s;
179 priv->slots[i]->name = NULL;
180 }
181
182 bflow->bootmeth_priv = priv;
183
184 ret = distro_rauc_scan_boot_part(bflow);
185 if (ret < 0) {
186 for (i = 0; priv->slots[i]->name; i++) {
187 free(priv->slots[i]->name);
188 free(priv->slots[i]);
189 }
190 free(priv);
191 free(boot_order_copy);
192 bflow->bootmeth_priv = NULL;
193 return ret;
194 }
195
196 bflow->state = BOOTFLOWST_READY;
197
198 return 0;
199}
200
201static int distro_rauc_read_file(struct udevice *dev, struct bootflow *bflow,
202 const char *file_path, ulong addr,
203 enum bootflow_img_t type, ulong *sizep)
204{
205 /*
206 * Reading individual files is not supported since we only operate on
207 * whole MMC devices (because we require multiple partitions).
208 */
209 return log_msg_ret("Unsupported", -ENOSYS);
210}
211
212static int distro_rauc_load_boot_script(struct bootflow *bflow,
213 const struct distro_rauc_slot *slot)
214{
215 struct blk_desc *desc;
216 struct distro_rauc_priv *priv;
217 struct udevice *bootstd;
218 const char *const *prefixes;
219 int ret;
220 int i;
221 int j;
222
223 ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
224 if (ret)
225 return log_msg_ret("std", ret);
226 prefixes = bootstd_get_prefixes(bootstd);
227
228 desc = dev_get_uclass_plat(bflow->blk);
229 priv = bflow->bootmeth_priv;
230 if (!priv || !priv->slots)
231 return log_msg_ret("priv", -EINVAL);
232
233 bflow->part = slot->boot_part;
234 if (!bflow->part)
235 return log_msg_ret("part", -ENOENT);
236
237 ret = bootmeth_setup_fs(bflow, desc);
238 if (ret)
239 return log_msg_ret("set", ret);
240
241 for (i = 0; prefixes[i] && bflow->state != BOOTFLOWST_FILE; i++) {
242 for (j = 0; script_names[j] && bflow->state != BOOTFLOWST_FILE; j++) {
243 if (!bootmeth_try_file(bflow, desc, prefixes[i], script_names[j])) {
244 log_debug("Found file '%s%s' in %s.part_%x\n",
245 prefixes[i], script_names[j],
246 bflow->dev->name, bflow->part);
247 bflow->subdir = strdup(prefixes[i]);
248 }
249 }
250 }
251 if (bflow->state != BOOTFLOWST_FILE)
252 return log_msg_ret("file", -ENOENT);
253
254 ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN,
255 (enum bootflow_img_t)IH_TYPE_SCRIPT);
256 if (ret)
257 return log_msg_ret("read", ret);
258
259 return 0;
260}
261
262static int find_active_slot(char **slot_name, ulong *slot_tries)
263{
264 ulong tries;
265 char boot_left[BOOT_LEFT_LEN];
266 char *boot_order;
267 const char **boot_order_list;
268 bool slot_found = false;
269 int ret;
270 int i;
271
272 boot_order = env_get("BOOT_ORDER");
273 if (!boot_order)
274 return log_msg_ret("env", -ENOENT);
275 boot_order_list = str_to_list(boot_order);
276 for (i = 0; boot_order_list[i] && !slot_found; i++) {
277 sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
278 tries = env_get_ulong(boot_left, 10, ULONG_MAX);
279 if (tries == ULONG_MAX)
280 return log_msg_ret("env", -ENOENT);
281
282 if (tries) {
283 ret = env_set_ulong(boot_left, tries - 1);
284 if (ret)
285 return log_msg_ret("env", ret);
286 *slot_name = strdup(boot_order_list[i]);
287 *slot_tries = tries;
288 slot_found = true;
289 }
290 }
291 str_free_list(boot_order_list);
292
293 if (!slot_found) {
294 if (IS_ENABLED(CONFIG_BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES)) {
295 log_warning("WARNING: No valid slot found\n");
296 log_info("INFO: Resetting boot order and all slot tries\n");
297 boot_order_list = str_to_list(CONFIG_BOOTMETH_RAUC_BOOT_ORDER);
298 for (i = 0; boot_order_list[i]; i++) {
299 sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
300 ret = env_set_ulong(boot_left, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
301 if (ret)
302 return log_msg_ret("env", ret);
303 }
304 str_free_list(boot_order_list);
305 ret = env_save();
306 if (ret)
307 return log_msg_ret("env", ret);
308 do_reset(NULL, 0, 0, NULL);
309 }
310 log_err("ERROR: No valid slot found\n");
311 return -EINVAL;
312 }
313
314 return 0;
315}
316
317static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow)
318{
319 struct blk_desc *desc;
320 struct distro_rauc_priv *priv;
321 const struct distro_rauc_slot *slot;
322 char *boot_order;
323 const char **boot_order_list;
324 char *active_slot;
325 ulong active_slot_tries;
326 char raucargs[64];
327 char boot_left[BOOT_LEFT_LEN];
328 ulong addr;
329 int ret = 0;
330 int i;
331
332 desc = dev_get_uclass_plat(bflow->blk);
333 if (desc->uclass_id != UCLASS_MMC)
334 return log_msg_ret("blk", -EINVAL);
335 priv = bflow->bootmeth_priv;
336
337 /* Device info variables */
338 ret = env_set("devtype", blk_get_devtype(bflow->blk));
339 if (ret)
340 return log_msg_ret("env", ret);
341
342 ret = env_set_hex("devnum", desc->devnum);
343 if (ret)
344 return log_msg_ret("env", ret);
345
346 /* Find active, valid slot */
347 ret = find_active_slot(&active_slot, &active_slot_tries);
348 if (ret)
349 return log_msg_ret("env", ret);
350
351 /* Kernel command line arguments */
352 sprintf(raucargs, "rauc.slot=%s", active_slot);
353 ret = env_set("raucargs", raucargs);
354 if (ret)
355 return log_msg_ret("env", ret);
356
357 /* Active slot info */
358 slot = get_slot(priv, active_slot);
359 if (!slot)
360 return log_msg_ret("env", -ENOENT);
361 ret = env_set_hex("distro_bootpart", slot->boot_part);
362 if (ret)
363 return log_msg_ret("env", ret);
364 ret = env_set_hex("distro_rootpart", slot->root_part);
365 if (ret)
366 return log_msg_ret("env", ret);
367 ret = env_save();
368 if (ret)
369 return log_msg_ret("env", ret);
370
371 /* Load distro boot script */
372 ret = distro_rauc_load_boot_script(bflow, slot);
373 if (ret)
374 return log_msg_ret("load", ret);
375
376 log_info("INFO: Booting slot %s, %lu of %d tries left\n",
377 active_slot, active_slot_tries, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES);
378
379 log_debug("devtype: %s\n", env_get("devtype"));
380 log_debug("devnum: %s\n", env_get("devnum"));
381 log_debug("distro_bootpart: %s\n", env_get("distro_bootpart"));
382 log_debug("distro_rootpart: %s\n", env_get("distro_rootpart"));
383 log_debug("raucargs: %s\n", env_get("raucargs"));
384 boot_order = env_get("BOOT_ORDER");
385 if (!boot_order)
386 return log_msg_ret("env", -EPERM);
387 log_debug("BOOT_ORDER: %s\n", boot_order);
388 boot_order_list = str_to_list(boot_order);
389 for (i = 0; boot_order_list[i]; i++) {
390 sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]);
391 log_debug("%s: %s\n", boot_left, env_get(boot_left));
392 }
393 str_free_list(boot_order_list);
394
395 /* Run distro boot script */
396 addr = map_to_sysmem(bflow->buf);
397 ret = cmd_source_script(addr, NULL, NULL);
398 if (ret)
399 return log_msg_ret("boot", ret);
400
401 return 0;
402}
403
404static int distro_rauc_bootmeth_bind(struct udevice *dev)
405{
406 struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
407
408 plat->desc = "RAUC distro boot from MMC";
409 plat->flags = BOOTMETHF_GLOBAL;
410
411 return 0;
412}
413
414static struct bootmeth_ops distro_rauc_bootmeth_ops = {
415 .check = distro_rauc_check,
416 .read_bootflow = distro_rauc_read_bootflow,
417 .read_file = distro_rauc_read_file,
418 .boot = distro_rauc_boot,
419};
420
421static const struct udevice_id distro_rauc_bootmeth_ids[] = {
422 { .compatible = "u-boot,distro-rauc" },
423 { }
424};
425
426U_BOOT_DRIVER(bootmeth_rauc) = {
427 .name = "bootmeth_rauc",
428 .id = UCLASS_BOOTMETH,
429 .of_match = distro_rauc_bootmeth_ids,
430 .ops = &distro_rauc_bootmeth_ops,
431 .bind = distro_rauc_bootmeth_bind,
432};