expo: Add basic implementation
[platform/kernel/u-boot.git] / boot / bootmeth_script.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for booting via a U-Boot script
4  *
5  * Copyright 2021 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #define LOG_CATEGORY UCLASS_BOOTSTD
10
11 #include <common.h>
12 #include <blk.h>
13 #include <bootflow.h>
14 #include <bootmeth.h>
15 #include <bootstd.h>
16 #include <dm.h>
17 #include <env.h>
18 #include <fs.h>
19 #include <image.h>
20 #include <malloc.h>
21 #include <mapmem.h>
22
23 #define SCRIPT_FNAME1   "boot.scr.uimg"
24 #define SCRIPT_FNAME2   "boot.scr"
25
26 static int script_check(struct udevice *dev, struct bootflow_iter *iter)
27 {
28         int ret;
29
30         /* This only works on block devices */
31         ret = bootflow_iter_uses_blk_dev(iter);
32         if (ret)
33                 return log_msg_ret("blk", ret);
34
35         return 0;
36 }
37
38 /**
39  * script_fill_info() - Decode the U-Boot script to find out distro info
40  *
41  * @bflow: Bootflow to process
42  * @return 0 if OK, -ve on error
43  */
44 static int script_fill_info(struct bootflow *bflow)
45 {
46         char *name = NULL;
47         char *data;
48         uint len;
49         int ret;
50
51         log_debug("parsing bflow file size %x\n", bflow->size);
52
53         ret = image_locate_script(bflow->buf, bflow->size, NULL, NULL, &data, &len);
54         if (!ret) {
55                 if (strstr(data, "armbianEnv"))
56                         name = "Armbian";
57         }
58
59         if (name) {
60                 bflow->os_name = strdup(name);
61                 if (!bflow->os_name)
62                         return log_msg_ret("os", -ENOMEM);
63         }
64
65         return 0;
66 }
67
68 static int script_read_bootflow(struct udevice *dev, struct bootflow *bflow)
69 {
70         struct blk_desc *desc = NULL;
71         const char *const *prefixes;
72         struct udevice *bootstd;
73         const char *prefix;
74         int ret, i;
75
76         ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd);
77         if (ret)
78                 return log_msg_ret("std", ret);
79
80         /* We require a partition table */
81         if (!bflow->part)
82                 return -ENOENT;
83
84         if (bflow->blk)
85                  desc = dev_get_uclass_plat(bflow->blk);
86
87         prefixes = bootstd_get_prefixes(bootstd);
88         i = 0;
89         do {
90                 prefix = prefixes ? prefixes[i] : NULL;
91
92                 ret = bootmeth_try_file(bflow, desc, prefix, SCRIPT_FNAME1);
93                 if (ret)
94                         ret = bootmeth_try_file(bflow, desc, prefix,
95                                                 SCRIPT_FNAME2);
96         } while (ret && prefixes && prefixes[++i]);
97         if (ret)
98                 return log_msg_ret("try", ret);
99
100         bflow->subdir = strdup(prefix ? prefix : "");
101         if (!bflow->subdir)
102                 return log_msg_ret("prefix", -ENOMEM);
103
104         ret = bootmeth_alloc_file(bflow, 0x10000, 1);
105         if (ret)
106                 return log_msg_ret("read", ret);
107
108         ret = script_fill_info(bflow);
109         if (ret)
110                 return log_msg_ret("inf", ret);
111
112         ret = bootmeth_alloc_other(bflow, "boot.bmp", &bflow->logo,
113                                    &bflow->logo_size);
114         /* ignore error */
115
116         return 0;
117 }
118
119 static int script_boot(struct udevice *dev, struct bootflow *bflow)
120 {
121         struct blk_desc *desc = dev_get_uclass_plat(bflow->blk);
122         ulong addr;
123         int ret;
124
125         ret = env_set("devtype", blk_get_devtype(bflow->blk));
126         if (!ret)
127                 ret = env_set_hex("devnum", desc->devnum);
128         if (!ret)
129                 ret = env_set("prefix", bflow->subdir);
130         if (!ret && IS_ENABLED(CONFIG_ARCH_SUNXI) &&
131             !strcmp("mmc", blk_get_devtype(bflow->blk)))
132                 ret = env_set_hex("mmc_bootdev", desc->devnum);
133         if (ret)
134                 return log_msg_ret("env", ret);
135
136         log_debug("devtype: %s\n", env_get("devtype"));
137         log_debug("devnum: %s\n", env_get("devnum"));
138         log_debug("prefix: %s\n", env_get("prefix"));
139         log_debug("mmc_bootdev: %s\n", env_get("mmc_bootdev"));
140
141         addr = map_to_sysmem(bflow->buf);
142         ret = cmd_source_script(addr, NULL, NULL);
143         if (ret)
144                 return log_msg_ret("boot", ret);
145
146         return 0;
147 }
148
149 static int script_bootmeth_bind(struct udevice *dev)
150 {
151         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
152
153         plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
154                 "Script boot from a block device" : "script";
155
156         return 0;
157 }
158
159 static struct bootmeth_ops script_bootmeth_ops = {
160         .check          = script_check,
161         .read_bootflow  = script_read_bootflow,
162         .read_file      = bootmeth_common_read_file,
163         .boot           = script_boot,
164 };
165
166 static const struct udevice_id script_bootmeth_ids[] = {
167         { .compatible = "u-boot,script" },
168         { }
169 };
170
171 U_BOOT_DRIVER(bootmeth_script) = {
172         .name           = "bootmeth_script",
173         .id             = UCLASS_BOOTMETH,
174         .of_match       = script_bootmeth_ids,
175         .ops            = &script_bootmeth_ops,
176         .bind           = script_bootmeth_bind,
177 };