board: ls1043ardb: fdt fixups for revision v7.0 boards
[platform/kernel/u-boot.git] / boot / vbe_simple.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Verified Boot for Embedded (VBE) 'simple' method
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <log.h>
11 #include <memalign.h>
12 #include <part.h>
13 #include <bootflow.h>
14 #include <bootmeth.h>
15 #include <dm.h>
16 #include <mmc.h>
17 #include <vbe.h>
18 #include <version_string.h>
19 #include <dm/device-internal.h>
20 #include <dm/ofnode.h>
21 #include <u-boot/crc.h>
22
23 enum {
24         MAX_VERSION_LEN         = 256,
25
26         NVD_HDR_VER_SHIFT       = 0,
27         NVD_HDR_VER_MASK        = 0xf,
28         NVD_HDR_SIZE_SHIFT      = 4,
29         NVD_HDR_SIZE_MASK       = 0xf << NVD_HDR_SIZE_SHIFT,
30
31         /* Firmware key-version is in the top 16 bits of fw_ver */
32         FWVER_KEY_SHIFT         = 16,
33         FWVER_FW_MASK           = 0xffff,
34
35         NVD_HDR_VER_CUR         = 1,    /* current version */
36 };
37
38 /** struct simple_priv - information read from the device tree */
39 struct simple_priv {
40         u32 area_start;
41         u32 area_size;
42         u32 skip_offset;
43         u32 state_offset;
44         u32 state_size;
45         u32 version_offset;
46         u32 version_size;
47         const char *storage;
48 };
49
50 /** struct simple_state - state information read from media
51  *
52  * @fw_version: Firmware version string
53  * @fw_vernum: Firmware version number
54  */
55 struct simple_state {
56         char fw_version[MAX_VERSION_LEN];
57         u32 fw_vernum;
58 };
59
60 /** struct simple_nvdata - storage format for non-volatile data */
61 struct simple_nvdata {
62         u8 crc8;
63         u8 hdr;
64         u16 spare1;
65         u32 fw_vernum;
66         u8 spare2[0x38];
67 };
68
69 static int simple_read_version(struct udevice *dev, struct blk_desc *desc,
70                                u8 *buf, struct simple_state *state)
71 {
72         struct simple_priv *priv = dev_get_priv(dev);
73         int start;
74
75         if (priv->version_size > MMC_MAX_BLOCK_LEN)
76                 return log_msg_ret("ver", -E2BIG);
77
78         start = priv->area_start + priv->version_offset;
79         if (start & (MMC_MAX_BLOCK_LEN - 1))
80                 return log_msg_ret("get", -EBADF);
81         start /= MMC_MAX_BLOCK_LEN;
82
83         if (blk_dread(desc, start, 1, buf) != 1)
84                 return log_msg_ret("read", -EIO);
85         strlcpy(state->fw_version, buf, MAX_VERSION_LEN);
86         log_debug("version=%s\n", state->fw_version);
87
88         return 0;
89 }
90
91 static int simple_read_nvdata(struct udevice *dev, struct blk_desc *desc,
92                               u8 *buf, struct simple_state *state)
93 {
94         struct simple_priv *priv = dev_get_priv(dev);
95         uint hdr_ver, hdr_size, size, crc;
96         const struct simple_nvdata *nvd;
97         int start;
98
99         if (priv->state_size > MMC_MAX_BLOCK_LEN)
100                 return log_msg_ret("state", -E2BIG);
101
102         start = priv->area_start + priv->state_offset;
103         if (start & (MMC_MAX_BLOCK_LEN - 1))
104                 return log_msg_ret("get", -EBADF);
105         start /= MMC_MAX_BLOCK_LEN;
106
107         if (blk_dread(desc, start, 1, buf) != 1)
108                 return log_msg_ret("read", -EIO);
109         nvd = (struct simple_nvdata *)buf;
110         hdr_ver = (nvd->hdr & NVD_HDR_VER_MASK) >> NVD_HDR_VER_SHIFT;
111         hdr_size = (nvd->hdr & NVD_HDR_SIZE_MASK) >> NVD_HDR_SIZE_SHIFT;
112         if (hdr_ver != NVD_HDR_VER_CUR)
113                 return log_msg_ret("hdr", -EPERM);
114         size = 1 << hdr_size;
115         if (size > sizeof(*nvd))
116                 return log_msg_ret("sz", -ENOEXEC);
117
118         crc = crc8(0, buf + 1, size - 1);
119         if (crc != nvd->crc8)
120                 return log_msg_ret("crc", -EPERM);
121         state->fw_vernum = nvd->fw_vernum;
122
123         log_debug("version=%s\n", state->fw_version);
124
125         return 0;
126 }
127
128 static int simple_read_state(struct udevice *dev, struct simple_state *state)
129 {
130         ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
131         struct simple_priv *priv = dev_get_priv(dev);
132         struct blk_desc *desc;
133         char devname[16];
134         const char *end;
135         int devnum;
136         int ret;
137
138         /* First figure out the block device */
139         log_debug("storage=%s\n", priv->storage);
140         devnum = trailing_strtoln_end(priv->storage, NULL, &end);
141         if (devnum == -1)
142                 return log_msg_ret("num", -ENODEV);
143         if (end - priv->storage >= sizeof(devname))
144                 return log_msg_ret("end", -E2BIG);
145         strlcpy(devname, priv->storage, end - priv->storage + 1);
146         log_debug("dev=%s, %x\n", devname, devnum);
147
148         desc = blk_get_dev(devname, devnum);
149         if (!desc)
150                 return log_msg_ret("get", -ENXIO);
151
152         ret = simple_read_version(dev, desc, buf, state);
153         if (ret)
154                 return log_msg_ret("ver", ret);
155
156         ret = simple_read_nvdata(dev, desc, buf, state);
157         if (ret)
158                 return log_msg_ret("nvd", ret);
159
160         return 0;
161 }
162
163 static int vbe_simple_get_state_desc(struct udevice *dev, char *buf,
164                                      int maxsize)
165 {
166         struct simple_state state;
167         int ret;
168
169         ret = simple_read_state(dev, &state);
170         if (ret)
171                 return log_msg_ret("read", ret);
172
173         if (maxsize < 30)
174                 return -ENOSPC;
175         snprintf(buf, maxsize, "Version: %s\nVernum: %x/%x", state.fw_version,
176                  state.fw_vernum >> FWVER_KEY_SHIFT,
177                  state.fw_vernum & FWVER_FW_MASK);
178
179         return 0;
180 }
181
182 static int vbe_simple_read_bootflow(struct udevice *dev, struct bootflow *bflow)
183 {
184         /* To be implemented */
185
186         return -EINVAL;
187 }
188
189 static struct bootmeth_ops bootmeth_vbe_simple_ops = {
190         .get_state_desc = vbe_simple_get_state_desc,
191         .read_bootflow  = vbe_simple_read_bootflow,
192         .read_file      = bootmeth_common_read_file,
193 };
194
195 int vbe_simple_fixup_node(ofnode node, struct simple_state *state)
196 {
197         char *version;
198         int ret;
199
200         version = strdup(state->fw_version);
201         if (!version)
202                 return log_msg_ret("ver", -ENOMEM);
203
204         ret = ofnode_write_string(node, "cur-version", version);
205         if (ret)
206                 return log_msg_ret("ver", ret);
207         ret = ofnode_write_u32(node, "cur-vernum", state->fw_vernum);
208         if (ret)
209                 return log_msg_ret("ver", ret);
210         ret = ofnode_write_string(node, "bootloader-version", version_string);
211         if (ret)
212                 return log_msg_ret("fix", ret);
213
214         return 0;
215 }
216
217 /**
218  * bootmeth_vbe_simple_ft_fixup() - Write out all VBE simple data to the DT
219  *
220  * @ctx: Context for event
221  * @event: Event to process
222  * @return 0 if OK, -ve on error
223  */
224 static int bootmeth_vbe_simple_ft_fixup(void *ctx, struct event *event)
225 {
226         oftree tree = event->data.ft_fixup.tree;
227         struct udevice *dev;
228         ofnode node;
229         int ret;
230
231         /*
232          * Ideally we would have driver model support for fixups, but that does
233          * not exist yet. It is a step too far to try to do this before VBE is
234          * in place.
235          */
236         for (ret = vbe_find_first_device(&dev); dev;
237              ret = vbe_find_next_device(&dev)) {
238                 struct simple_state state;
239
240                 if (strcmp("vbe_simple", dev->driver->name))
241                         continue;
242
243                 /* Check if there is a node to fix up */
244                 node = ofnode_path_root(tree, "/chosen/fwupd");
245                 if (!ofnode_valid(node))
246                         continue;
247                 node = ofnode_find_subnode(node, dev->name);
248                 if (!ofnode_valid(node))
249                         continue;
250
251                 log_debug("Fixing up: %s\n", dev->name);
252                 ret = device_probe(dev);
253                 if (ret)
254                         return log_msg_ret("probe", ret);
255                 ret = simple_read_state(dev, &state);
256                 if (ret)
257                         return log_msg_ret("read", ret);
258
259                 ret = vbe_simple_fixup_node(node, &state);
260                 if (ret)
261                         return log_msg_ret("fix", ret);
262         }
263
264         return 0;
265 }
266 EVENT_SPY(EVT_FT_FIXUP, bootmeth_vbe_simple_ft_fixup);
267
268 static int bootmeth_vbe_simple_probe(struct udevice *dev)
269 {
270         struct simple_priv *priv = dev_get_priv(dev);
271
272         memset(priv, '\0', sizeof(*priv));
273         if (dev_read_u32(dev, "area-start", &priv->area_start) ||
274             dev_read_u32(dev, "area-size", &priv->area_size) ||
275             dev_read_u32(dev, "version-offset", &priv->version_offset) ||
276             dev_read_u32(dev, "version-size", &priv->version_size) ||
277             dev_read_u32(dev, "state-offset", &priv->state_offset) ||
278             dev_read_u32(dev, "state-size", &priv->state_size))
279                 return log_msg_ret("read", -EINVAL);
280         dev_read_u32(dev, "skip-offset", &priv->skip_offset);
281         priv->storage = strdup(dev_read_string(dev, "storage"));
282         if (!priv->storage)
283                 return log_msg_ret("str", -EINVAL);
284
285         return 0;
286 }
287
288 static int bootmeth_vbe_simple_bind(struct udevice *dev)
289 {
290         struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
291
292         plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
293                 "VBE simple" : "vbe-simple";
294         plat->flags = BOOTMETHF_GLOBAL;
295
296         return 0;
297 }
298
299 #if CONFIG_IS_ENABLED(OF_REAL)
300 static const struct udevice_id generic_simple_vbe_simple_ids[] = {
301         { .compatible = "fwupd,vbe-simple" },
302         { }
303 };
304 #endif
305
306 U_BOOT_DRIVER(vbe_simple) = {
307         .name   = "vbe_simple",
308         .id     = UCLASS_BOOTMETH,
309         .of_match = of_match_ptr(generic_simple_vbe_simple_ids),
310         .ops    = &bootmeth_vbe_simple_ops,
311         .bind   = bootmeth_vbe_simple_bind,
312         .probe  = bootmeth_vbe_simple_probe,
313         .flags  = DM_FLAG_PRE_RELOC,
314         .priv_auto      = sizeof(struct simple_priv),
315 };