1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2021 Google LLC
4 * Written by Simon Glass <sjg@chromium.org>
7 #define LOG_CATEGORY UCLASS_BOOTSTD
15 #include <env_internal.h>
19 #include <dm/uclass-internal.h>
21 DECLARE_GLOBAL_DATA_PTR
;
23 int bootmeth_get_state_desc(struct udevice
*dev
, char *buf
, int maxsize
)
25 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
27 if (!ops
->get_state_desc
)
30 return ops
->get_state_desc(dev
, buf
, maxsize
);
33 int bootmeth_check(struct udevice
*dev
, struct bootflow_iter
*iter
)
35 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
40 return ops
->check(dev
, iter
);
43 int bootmeth_read_bootflow(struct udevice
*dev
, struct bootflow
*bflow
)
45 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
47 if (!ops
->read_bootflow
)
50 return ops
->read_bootflow(dev
, bflow
);
53 int bootmeth_set_bootflow(struct udevice
*dev
, struct bootflow
*bflow
,
56 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
58 if (!ops
->set_bootflow
)
61 return ops
->set_bootflow(dev
, bflow
, buf
, size
);
64 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
65 int bootmeth_read_all(struct udevice
*dev
, struct bootflow
*bflow
)
67 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
72 return ops
->read_all(dev
, bflow
);
74 #endif /* BOOTSTD_FULL */
76 int bootmeth_boot(struct udevice
*dev
, struct bootflow
*bflow
)
78 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
83 return ops
->boot(dev
, bflow
);
86 int bootmeth_read_file(struct udevice
*dev
, struct bootflow
*bflow
,
87 const char *file_path
, ulong addr
,
88 enum bootflow_img_t type
, ulong
*sizep
)
90 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
95 return ops
->read_file(dev
, bflow
, file_path
, addr
, type
, sizep
);
98 int bootmeth_get_bootflow(struct udevice
*dev
, struct bootflow
*bflow
)
100 const struct bootmeth_ops
*ops
= bootmeth_get_ops(dev
);
102 if (!ops
->read_bootflow
)
104 bootflow_init(bflow
, NULL
, dev
);
106 return ops
->read_bootflow(dev
, bflow
);
109 int bootmeth_setup_iter_order(struct bootflow_iter
*iter
, bool include_global
)
111 struct bootstd_priv
*std
;
112 struct udevice
**order
;
116 ret
= bootstd_get_priv(&std
);
120 /* Create an array large enough */
121 count
= std
->bootmeth_count
? std
->bootmeth_count
:
122 uclass_id_count(UCLASS_BOOTMETH
);
124 return log_msg_ret("count", -ENOENT
);
126 order
= calloc(count
, sizeof(struct udevice
*));
128 return log_msg_ret("order", -ENOMEM
);
130 /* If we have an ordering, copy it */
131 if (IS_ENABLED(CONFIG_BOOTSTD_FULL
) && std
->bootmeth_count
) {
135 * We don't support skipping global bootmeths. Instead, the user
136 * should omit them from the ordering
139 return log_msg_ret("glob", -EPERM
);
140 memcpy(order
, std
->bootmeth_order
,
141 count
* sizeof(struct bootmeth
*));
143 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL
)) {
144 for (i
= 0; i
< count
; i
++) {
145 struct udevice
*dev
= order
[i
];
146 struct bootmeth_uc_plat
*ucp
;
149 ucp
= dev_get_uclass_plat(dev
);
150 is_global
= ucp
->flags
&
153 iter
->first_glob_method
= i
;
163 * Do two passes, one to find the normal bootmeths and another
164 * to find the global ones, if required, The global ones go at
167 for (pass
= 0, upto
= 0; pass
< 1 + include_global
; pass
++) {
169 iter
->first_glob_method
= upto
;
171 * Get a list of bootmethods, in seq order (i.e. using
172 * aliases). There may be gaps so try to count up high
173 * enough to find them all.
175 for (i
= 0; upto
< count
&& i
< 20 + count
* 2; i
++) {
176 struct bootmeth_uc_plat
*ucp
;
179 ret
= uclass_get_device_by_seq(UCLASS_BOOTMETH
,
183 ucp
= dev_get_uclass_plat(dev
);
185 IS_ENABLED(CONFIG_BOOTMETH_GLOBAL
) &&
186 (ucp
->flags
& BOOTMETHF_GLOBAL
);
187 if (pass
? is_global
: !is_global
)
194 return log_msg_ret("count2", -ENOENT
);
196 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL
) && include_global
&&
197 iter
->first_glob_method
!= -1 && iter
->first_glob_method
!= count
) {
198 iter
->cur_method
= iter
->first_glob_method
;
199 iter
->doing_global
= true;
201 iter
->method_order
= order
;
202 iter
->num_methods
= count
;
207 int bootmeth_set_order(const char *order_str
)
209 struct bootstd_priv
*std
;
210 struct udevice
**order
;
211 int count
, ret
, i
, len
;
214 ret
= bootstd_get_priv(&std
);
219 free(std
->bootmeth_order
);
220 std
->bootmeth_order
= NULL
;
221 std
->bootmeth_count
= 0;
225 /* Create an array large enough */
226 count
= uclass_id_count(UCLASS_BOOTMETH
);
228 return log_msg_ret("count", -ENOENT
);
230 order
= calloc(count
+ 1, sizeof(struct udevice
*));
232 return log_msg_ret("order", -ENOMEM
);
234 for (i
= 0, s
= order_str
; *s
&& i
< count
; s
= p
+ (*p
== ' '), i
++) {
237 p
= strchrnul(s
, ' ');
239 ret
= uclass_find_device_by_namelen(UCLASS_BOOTMETH
, s
, len
,
242 printf("Unknown bootmeth '%.*s'\n", len
, s
);
249 free(std
->bootmeth_order
);
250 std
->bootmeth_order
= order
;
251 std
->bootmeth_count
= i
;
256 int bootmeth_set_property(const char *name
, const char *property
, const char *value
)
261 const struct bootmeth_ops
*ops
;
265 ret
= uclass_find_device_by_namelen(UCLASS_BOOTMETH
, name
, len
,
268 printf("Unknown bootmeth '%s'\n", name
);
272 ops
= bootmeth_get_ops(dev
);
273 if (!ops
->set_property
) {
274 printf("set_property not found\n");
278 return ops
->set_property(dev
, property
, value
);
281 int bootmeth_setup_fs(struct bootflow
*bflow
, struct blk_desc
*desc
)
286 ret
= fs_set_blk_dev_with_part(desc
, bflow
->part
);
288 return log_msg_ret("set", ret
);
289 } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL
) && bflow
->fs_type
) {
290 fs_set_type(bflow
->fs_type
);
296 int bootmeth_try_file(struct bootflow
*bflow
, struct blk_desc
*desc
,
297 const char *prefix
, const char *fname
)
303 snprintf(path
, sizeof(path
), "%s%s", prefix
? prefix
: "", fname
);
304 log_debug("trying: %s\n", path
);
307 bflow
->fname
= strdup(path
);
309 return log_msg_ret("name", -ENOMEM
);
311 if (IS_ENABLED(CONFIG_BOOTSTD_FULL
) && bflow
->fs_type
)
312 fs_set_type(bflow
->fs_type
);
314 ret
= fs_size(path
, &size
);
315 log_debug(" %s - err=%d\n", path
, ret
);
317 /* Sadly FS closes the file after fs_size() so we must redo this */
318 ret2
= bootmeth_setup_fs(bflow
, desc
);
320 return log_msg_ret("fs", ret2
);
323 return log_msg_ret("size", ret
);
326 bflow
->state
= BOOTFLOWST_FILE
;
331 int bootmeth_alloc_file(struct bootflow
*bflow
, uint size_limit
, uint align
,
332 enum bootflow_img_t type
)
334 struct blk_desc
*desc
= NULL
;
340 log_debug(" - script file size %x\n", size
);
341 if (size
> size_limit
)
342 return log_msg_ret("chk", -E2BIG
);
344 ret
= fs_read_alloc(bflow
->fname
, bflow
->size
, align
, &buf
);
346 return log_msg_ret("all", ret
);
348 bflow
->state
= BOOTFLOWST_READY
;
352 desc
= dev_get_uclass_plat(bflow
->blk
);
354 if (!bootflow_img_add(bflow
, bflow
->fname
, type
, map_to_sysmem(buf
),
356 return log_msg_ret("bai", -ENOMEM
);
361 int bootmeth_alloc_other(struct bootflow
*bflow
, const char *fname
,
362 enum bootflow_img_t type
, void **bufp
, uint
*sizep
)
364 struct blk_desc
*desc
= NULL
;
370 snprintf(path
, sizeof(path
), "%s%s", bflow
->subdir
, fname
);
371 log_debug("trying: %s\n", path
);
374 desc
= dev_get_uclass_plat(bflow
->blk
);
376 ret
= bootmeth_setup_fs(bflow
, desc
);
378 return log_msg_ret("fs", ret
);
380 ret
= fs_size(path
, &size
);
381 log_debug(" %s - err=%d\n", path
, ret
);
383 ret
= bootmeth_setup_fs(bflow
, desc
);
385 return log_msg_ret("fs", ret
);
387 ret
= fs_read_alloc(path
, size
, 0, &buf
);
389 return log_msg_ret("all", ret
);
391 if (!bootflow_img_add(bflow
, bflow
->fname
, type
, map_to_sysmem(buf
),
393 return log_msg_ret("boi", -ENOMEM
);
401 int bootmeth_common_read_file(struct udevice
*dev
, struct bootflow
*bflow
,
402 const char *file_path
, ulong addr
,
403 enum bootflow_img_t type
, ulong
*sizep
)
405 struct blk_desc
*desc
= NULL
;
411 desc
= dev_get_uclass_plat(bflow
->blk
);
413 ret
= bootmeth_setup_fs(bflow
, desc
);
415 return log_msg_ret("fs", ret
);
417 ret
= fs_size(file_path
, &size
);
419 return log_msg_ret("size", ret
);
421 return log_msg_ret("spc", -ENOSPC
);
423 ret
= bootmeth_setup_fs(bflow
, desc
);
425 return log_msg_ret("fs", ret
);
427 ret
= fs_read(file_path
, addr
, 0, 0, &len_read
);
432 if (!bootflow_img_add(bflow
, bflow
->fname
, type
, addr
, size
))
433 return log_msg_ret("bci", -ENOMEM
);
438 #ifdef CONFIG_BOOTSTD_FULL
440 * on_bootmeths() - Update the bootmeth order
442 * This will check for a valid list of bootmeths and only apply it if valid.
444 static int on_bootmeths(const char *name
, const char *value
, enum env_op op
,
451 case env_op_overwrite
:
452 ret
= bootmeth_set_order(value
);
457 bootmeth_set_order(NULL
);
463 U_BOOT_ENV_CALLBACK(bootmeths
, on_bootmeths
);
464 #endif /* CONFIG_BOOTSTD_FULL */
466 UCLASS_DRIVER(bootmeth
) = {
467 .id
= UCLASS_BOOTMETH
,
469 .flags
= DM_UC_FLAG_SEQ_ALIAS
,
470 .per_device_plat_auto
= sizeof(struct bootmeth_uc_plat
),