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