]> Git Repo - J-u-boot.git/blob - boot/bootmeth-uclass.c
Merge patch series "clk: mediatek: add OPs to support OF_UPSTREAM"
[J-u-boot.git] / boot / bootmeth-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2021 Google LLC
4  * Written by Simon Glass <[email protected]>
5  */
6
7 #define LOG_CATEGORY UCLASS_BOOTSTD
8
9 #include <blk.h>
10 #include <bootflow.h>
11 #include <bootmeth.h>
12 #include <bootstd.h>
13 #include <dm.h>
14 #include <env_internal.h>
15 #include <fs.h>
16 #include <malloc.h>
17 #include <mapmem.h>
18 #include <dm/uclass-internal.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 int bootmeth_get_state_desc(struct udevice *dev, char *buf, int maxsize)
23 {
24         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
25
26         if (!ops->get_state_desc)
27                 return -ENOSYS;
28
29         return ops->get_state_desc(dev, buf, maxsize);
30 }
31
32 int bootmeth_check(struct udevice *dev, struct bootflow_iter *iter)
33 {
34         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
35
36         if (!ops->check)
37                 return 0;
38
39         return ops->check(dev, iter);
40 }
41
42 int bootmeth_read_bootflow(struct udevice *dev, struct bootflow *bflow)
43 {
44         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
45
46         if (!ops->read_bootflow)
47                 return -ENOSYS;
48
49         return ops->read_bootflow(dev, bflow);
50 }
51
52 int bootmeth_set_bootflow(struct udevice *dev, struct bootflow *bflow,
53                           char *buf, int size)
54 {
55         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
56
57         if (!ops->set_bootflow)
58                 return -ENOSYS;
59
60         return ops->set_bootflow(dev, bflow, buf, size);
61 }
62
63 #if CONFIG_IS_ENABLED(BOOTSTD_FULL)
64 int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow)
65 {
66         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
67
68         if (!ops->read_all)
69                 return -ENOSYS;
70
71         return ops->read_all(dev, bflow);
72 }
73 #endif /* BOOTSTD_FULL */
74
75 int bootmeth_boot(struct udevice *dev, struct bootflow *bflow)
76 {
77         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
78
79         if (!ops->boot)
80                 return -ENOSYS;
81
82         return ops->boot(dev, bflow);
83 }
84
85 int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow,
86                        const char *file_path, ulong addr, ulong *sizep)
87 {
88         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
89
90         if (!ops->read_file)
91                 return -ENOSYS;
92
93         return ops->read_file(dev, bflow, file_path, addr, sizep);
94 }
95
96 int bootmeth_get_bootflow(struct udevice *dev, struct bootflow *bflow)
97 {
98         const struct bootmeth_ops *ops = bootmeth_get_ops(dev);
99
100         if (!ops->read_bootflow)
101                 return -ENOSYS;
102         bootflow_init(bflow, NULL, dev);
103
104         return ops->read_bootflow(dev, bflow);
105 }
106
107 int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global)
108 {
109         struct bootstd_priv *std;
110         struct udevice **order;
111         int count;
112         int ret;
113
114         ret = bootstd_get_priv(&std);
115         if (ret)
116                 return ret;
117
118         /* Create an array large enough */
119         count = std->bootmeth_count ? std->bootmeth_count :
120                 uclass_id_count(UCLASS_BOOTMETH);
121         if (!count)
122                 return log_msg_ret("count", -ENOENT);
123
124         order = calloc(count, sizeof(struct udevice *));
125         if (!order)
126                 return log_msg_ret("order", -ENOMEM);
127
128         /* If we have an ordering, copy it */
129         if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && std->bootmeth_count) {
130                 int i;
131
132                 /*
133                  * We don't support skipping global bootmeths. Instead, the user
134                  * should omit them from the ordering
135                  */
136                 if (!include_global)
137                         return log_msg_ret("glob", -EPERM);
138                 memcpy(order, std->bootmeth_order,
139                        count * sizeof(struct bootmeth *));
140
141                 if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) {
142                         for (i = 0; i < count; i++) {
143                                 struct udevice *dev = order[i];
144                                 struct bootmeth_uc_plat *ucp;
145                                 bool is_global;
146
147                                 ucp = dev_get_uclass_plat(dev);
148                                 is_global = ucp->flags &
149                                         BOOTMETHF_GLOBAL;
150                                 if (is_global) {
151                                         iter->first_glob_method = i;
152                                         break;
153                                 }
154                         }
155                 }
156         } else {
157                 struct udevice *dev;
158                 int i, upto, pass;
159
160                 /*
161                  * Do two passes, one to find the normal bootmeths and another
162                  * to find the global ones, if required, The global ones go at
163                  * the end.
164                  */
165                 for (pass = 0, upto = 0; pass < 1 + include_global; pass++) {
166                         if (pass)
167                                 iter->first_glob_method = upto;
168                         /*
169                          * Get a list of bootmethods, in seq order (i.e. using
170                          * aliases). There may be gaps so try to count up high
171                          * enough to find them all.
172                          */
173                         for (i = 0; upto < count && i < 20 + count * 2; i++) {
174                                 struct bootmeth_uc_plat *ucp;
175                                 bool is_global;
176
177                                 ret = uclass_get_device_by_seq(UCLASS_BOOTMETH,
178                                                                i, &dev);
179                                 if (ret)
180                                         continue;
181                                 ucp = dev_get_uclass_plat(dev);
182                                 is_global =
183                                         IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) &&
184                                         (ucp->flags & BOOTMETHF_GLOBAL);
185                                 if (pass ? is_global : !is_global)
186                                         order[upto++] = dev;
187                         }
188                 }
189                 count = upto;
190         }
191         if (!count)
192                 return log_msg_ret("count2", -ENOENT);
193
194         if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global &&
195             iter->first_glob_method != -1 && iter->first_glob_method != count) {
196                 iter->cur_method = iter->first_glob_method;
197                 iter->doing_global = true;
198         }
199         iter->method_order = order;
200         iter->num_methods = count;
201
202         return 0;
203 }
204
205 int bootmeth_set_order(const char *order_str)
206 {
207         struct bootstd_priv *std;
208         struct udevice **order;
209         int count, ret, i, len;
210         const char *s, *p;
211
212         ret = bootstd_get_priv(&std);
213         if (ret)
214                 return ret;
215
216         if (!order_str) {
217                 free(std->bootmeth_order);
218                 std->bootmeth_order = NULL;
219                 std->bootmeth_count = 0;
220                 return 0;
221         }
222
223         /* Create an array large enough */
224         count = uclass_id_count(UCLASS_BOOTMETH);
225         if (!count)
226                 return log_msg_ret("count", -ENOENT);
227
228         order = calloc(count + 1, sizeof(struct udevice *));
229         if (!order)
230                 return log_msg_ret("order", -ENOMEM);
231
232         for (i = 0, s = order_str; *s && i < count; s = p + (*p == ' '), i++) {
233                 struct udevice *dev;
234
235                 p = strchrnul(s, ' ');
236                 len = p - s;
237                 ret = uclass_find_device_by_namelen(UCLASS_BOOTMETH, s, len,
238                                                     &dev);
239                 if (ret) {
240                         printf("Unknown bootmeth '%.*s'\n", len, s);
241                         free(order);
242                         return ret;
243                 }
244                 order[i] = dev;
245         }
246         order[i] = NULL;
247         free(std->bootmeth_order);
248         std->bootmeth_order = order;
249         std->bootmeth_count = i;
250
251         return 0;
252 }
253
254 int bootmeth_setup_fs(struct bootflow *bflow, struct blk_desc *desc)
255 {
256         int ret;
257
258         if (desc) {
259                 ret = fs_set_blk_dev_with_part(desc, bflow->part);
260                 if (ret)
261                         return log_msg_ret("set", ret);
262         } else if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type) {
263                 fs_set_type(bflow->fs_type);
264         }
265
266         return 0;
267 }
268
269 int bootmeth_try_file(struct bootflow *bflow, struct blk_desc *desc,
270                       const char *prefix, const char *fname)
271 {
272         char path[200];
273         loff_t size;
274         int ret, ret2;
275
276         snprintf(path, sizeof(path), "%s%s", prefix ? prefix : "", fname);
277         log_debug("trying: %s\n", path);
278
279         free(bflow->fname);
280         bflow->fname = strdup(path);
281         if (!bflow->fname)
282                 return log_msg_ret("name", -ENOMEM);
283
284         if (IS_ENABLED(CONFIG_BOOTSTD_FULL) && bflow->fs_type)
285                 fs_set_type(bflow->fs_type);
286
287         ret = fs_size(path, &size);
288         log_debug("   %s - err=%d\n", path, ret);
289
290         /* Sadly FS closes the file after fs_size() so we must redo this */
291         ret2 = bootmeth_setup_fs(bflow, desc);
292         if (ret2)
293                 return log_msg_ret("fs", ret2);
294
295         if (ret)
296                 return log_msg_ret("size", ret);
297
298         bflow->size = size;
299         bflow->state = BOOTFLOWST_FILE;
300
301         return 0;
302 }
303
304 int bootmeth_alloc_file(struct bootflow *bflow, uint size_limit, uint align)
305 {
306         void *buf;
307         uint size;
308         int ret;
309
310         size = bflow->size;
311         log_debug("   - script file size %x\n", size);
312         if (size > size_limit)
313                 return log_msg_ret("chk", -E2BIG);
314
315         ret = fs_read_alloc(bflow->fname, bflow->size, align, &buf);
316         if (ret)
317                 return log_msg_ret("all", ret);
318
319         bflow->state = BOOTFLOWST_READY;
320         bflow->buf = buf;
321
322         return 0;
323 }
324
325 int bootmeth_alloc_other(struct bootflow *bflow, const char *fname,
326                          void **bufp, uint *sizep)
327 {
328         struct blk_desc *desc = NULL;
329         char path[200];
330         loff_t size;
331         void *buf;
332         int ret;
333
334         snprintf(path, sizeof(path), "%s%s", bflow->subdir, fname);
335         log_debug("trying: %s\n", path);
336
337         if (bflow->blk)
338                 desc = dev_get_uclass_plat(bflow->blk);
339
340         ret = bootmeth_setup_fs(bflow, desc);
341         if (ret)
342                 return log_msg_ret("fs", ret);
343
344         ret = fs_size(path, &size);
345         log_debug("   %s - err=%d\n", path, ret);
346
347         ret = bootmeth_setup_fs(bflow, desc);
348         if (ret)
349                 return log_msg_ret("fs", ret);
350
351         ret = fs_read_alloc(path, size, 0, &buf);
352         if (ret)
353                 return log_msg_ret("all", ret);
354
355         *bufp = buf;
356         *sizep = size;
357
358         return 0;
359 }
360
361 int bootmeth_common_read_file(struct udevice *dev, struct bootflow *bflow,
362                               const char *file_path, ulong addr, ulong *sizep)
363 {
364         struct blk_desc *desc = NULL;
365         loff_t len_read;
366         loff_t size;
367         int ret;
368
369         if (bflow->blk)
370                 desc = dev_get_uclass_plat(bflow->blk);
371
372         ret = bootmeth_setup_fs(bflow, desc);
373         if (ret)
374                 return log_msg_ret("fs", ret);
375
376         ret = fs_size(file_path, &size);
377         if (ret)
378                 return log_msg_ret("size", ret);
379         if (size > *sizep)
380                 return log_msg_ret("spc", -ENOSPC);
381
382         ret = bootmeth_setup_fs(bflow, desc);
383         if (ret)
384                 return log_msg_ret("fs", ret);
385
386         ret = fs_read(file_path, addr, 0, 0, &len_read);
387         if (ret)
388                 return ret;
389         *sizep = len_read;
390
391         return 0;
392 }
393
394 #ifdef CONFIG_BOOTSTD_FULL
395 /**
396  * on_bootmeths() - Update the bootmeth order
397  *
398  * This will check for a valid list of bootmeths and only apply it if valid.
399  */
400 static int on_bootmeths(const char *name, const char *value, enum env_op op,
401                         int flags)
402 {
403         int ret;
404
405         switch (op) {
406         case env_op_create:
407         case env_op_overwrite:
408                 ret = bootmeth_set_order(value);
409                 if (ret)
410                         return 1;
411                 return 0;
412         case env_op_delete:
413                 bootmeth_set_order(NULL);
414                 fallthrough;
415         default:
416                 return 0;
417         }
418 }
419 U_BOOT_ENV_CALLBACK(bootmeths, on_bootmeths);
420 #endif /* CONFIG_BOOTSTD_FULL */
421
422 UCLASS_DRIVER(bootmeth) = {
423         .id             = UCLASS_BOOTMETH,
424         .name           = "bootmeth",
425         .flags          = DM_UC_FLAG_SEQ_ALIAS,
426         .per_device_plat_auto   = sizeof(struct bootmeth_uc_plat),
427 };
This page took 0.048585 seconds and 4 git commands to generate.