1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Verified Boot for Embedded (VBE) loading firmware phases
4  *
5  * Copyright 2022 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #define LOG_CATEGORY LOGC_BOOT
10 
11 #include <binman_sym.h>
12 #include <bloblist.h>
13 #include <bootdev.h>
14 #include <bootflow.h>
15 #include <bootmeth.h>
16 #include <bootstage.h>
17 #include <dm.h>
18 #include <image.h>
19 #include <log.h>
20 #include <mapmem.h>
21 #include <mmc.h>
22 #include <spl.h>
23 #include <vbe.h>
24 #include <dm/device-internal.h>
25 #include "vbe_common.h"
26 #include "vbe_simple.h"
27 
28 #ifdef CONFIG_BOOTMETH_VBE_SIMPLE
29 binman_sym_extern(ulong, vbe_a, image_pos);
30 binman_sym_extern(ulong, vbe_a, size);
31 #else
32 binman_sym_declare(ulong, vbe_a, image_pos);
33 binman_sym_declare(ulong, vbe_a, size);
34 #endif
35 
36 binman_sym_declare(ulong, vpl, image_pos);
37 binman_sym_declare(ulong, vpl, size);
38 
39 /**
40  * vbe_simple_read_bootflow_fw() - Create a bootflow for firmware
41  *
42  * Locates and loads the firmware image (FIT) needed for the next phase. The FIT
43  * should ideally use external data, to reduce the amount of it that needs to be
44  * read.
45  *
46  * @bdev: bootdev device containing the firmwre
47  * @meth: VBE simple bootmeth
48  * @blow: Place to put the created bootflow, on success
49  * @return 0 if OK, -ve on error
50  */
vbe_simple_read_bootflow_fw(struct udevice * dev,struct bootflow * bflow)51 int vbe_simple_read_bootflow_fw(struct udevice *dev, struct bootflow *bflow)
52 {
53 	struct udevice *media = dev_get_parent(bflow->dev);
54 	struct udevice *meth = bflow->method;
55 	struct simple_priv *priv = dev_get_priv(meth);
56 	ulong len, load_addr;
57 	struct udevice *blk;
58 	int ret;
59 
60 	log_debug("media=%s\n", media->name);
61 	ret = blk_get_from_parent(media, &blk);
62 	if (ret)
63 		return log_msg_ret("med", ret);
64 	log_debug("blk=%s\n", blk->name);
65 
66 	ret = vbe_read_fit(blk, priv->area_start + priv->skip_offset,
67 			   priv->area_size, NULL, &load_addr, &len,
68 			   &bflow->name);
69 	if (ret)
70 		return log_msg_ret("vbe", ret);
71 
72 	/* set up the bootflow with the info we obtained */
73 	bflow->blk = blk;
74 	bflow->buf = map_sysmem(load_addr, len);
75 	bflow->size = len;
76 
77 	return 0;
78 }
79 
simple_load_from_image(struct spl_image_info * image,struct spl_boot_device * bootdev)80 static int simple_load_from_image(struct spl_image_info *image,
81 				  struct spl_boot_device *bootdev)
82 {
83 	struct vbe_handoff *handoff;
84 	int ret;
85 
86 	if (xpl_phase() != PHASE_VPL && xpl_phase() != PHASE_SPL &&
87 	    xpl_phase() != PHASE_TPL)
88 		return -ENOENT;
89 
90 	ret = bloblist_ensure_size(BLOBLISTT_VBE, sizeof(struct vbe_handoff),
91 				   0, (void **)&handoff);
92 	if (ret)
93 		return log_msg_ret("ro", ret);
94 
95 	if (USE_BOOTMETH) {
96 		struct udevice *meth, *bdev;
97 		struct simple_priv *priv;
98 		struct bootflow bflow;
99 
100 		vbe_find_first_device(&meth);
101 		if (!meth)
102 			return log_msg_ret("vd", -ENODEV);
103 		log_debug("vbe dev %s\n", meth->name);
104 		ret = device_probe(meth);
105 		if (ret)
106 			return log_msg_ret("probe", ret);
107 
108 		priv = dev_get_priv(meth);
109 		log_debug("simple %s\n", priv->storage);
110 		ret = bootdev_find_by_label(priv->storage, &bdev, NULL);
111 		if (ret)
112 			return log_msg_ret("bd", ret);
113 		log_debug("bootdev %s\n", bdev->name);
114 
115 		bootflow_init(&bflow, bdev, meth);
116 		ret = bootmeth_read_bootflow(meth, &bflow);
117 		log_debug("\nfw ret=%d\n", ret);
118 		if (ret)
119 			return log_msg_ret("rd", ret);
120 
121 		/* jump to the image */
122 		image->flags = SPL_SANDBOXF_ARG_IS_BUF;
123 		image->arg = bflow.buf;
124 		image->size = bflow.size;
125 		log_debug("Image: %s at %p size %x\n", bflow.name, bflow.buf,
126 			  bflow.size);
127 
128 		/* this is not used from now on, so free it */
129 		bootflow_free(&bflow);
130 	} else {
131 		struct udevice *media, *blk;
132 		ulong offset, size;
133 
134 		ret = uclass_get_device_by_seq(UCLASS_MMC, 1, &media);
135 		if (ret)
136 			return log_msg_ret("vdv", ret);
137 		ret = blk_get_from_parent(media, &blk);
138 		if (ret)
139 			return log_msg_ret("med", ret);
140 		if (xpl_phase() == PHASE_TPL) {
141 			offset = binman_sym(ulong, vpl, image_pos);
142 			size = binman_sym(ulong, vpl, size);
143 		} else {
144 			offset = binman_sym(ulong, vbe_a, image_pos);
145 			size = binman_sym(ulong, vbe_a, size);
146 			printf("offset=%lx\n", offset);
147 		}
148 
149 		ret = vbe_read_fit(blk, offset, size, image, NULL, NULL, NULL);
150 		if (ret)
151 			return log_msg_ret("vbe", ret);
152 	}
153 
154 	/* Record that VBE was used in this phase */
155 	handoff->phases |= 1 << xpl_phase();
156 
157 	return 0;
158 }
159 SPL_LOAD_IMAGE_METHOD("vbe_simple", 5, BOOT_DEVICE_VBE,
160 		      simple_load_from_image);
161