1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for distro boot via EFI
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 <bootdev.h>
12 #include <bootflow.h>
13 #include <bootmeth.h>
14 #include <command.h>
15 #include <dm.h>
16 #include <efi.h>
17 #include <efi_loader.h>
18 #include <env.h>
19 #include <fs.h>
20 #include <malloc.h>
21 #include <mapmem.h>
22 #include <mmc.h>
23 #include <net.h>
24 #include <pxe_utils.h>
25 #include <linux/sizes.h>
26 
27 #define EFI_DIRNAME	"/EFI/BOOT/"
28 
get_efi_pxe_vci(char * str,int max_len)29 static int get_efi_pxe_vci(char *str, int max_len)
30 {
31 	int ret;
32 
33 	ret = efi_get_pxe_arch();
34 	if (ret < 0)
35 		return ret;
36 
37 	snprintf(str, max_len, "PXEClient:Arch:%05x:UNDI:003000", ret);
38 
39 	return 0;
40 }
41 
42 /**
43  * bootmeth_uses_network() - check if the media device is Ethernet
44  *
45  * @bflow: Bootflow to check
46  * Returns: true if the media device is Ethernet, else false
47  */
bootmeth_uses_network(struct bootflow * bflow)48 static bool bootmeth_uses_network(struct bootflow *bflow)
49 {
50 	const struct udevice *media = dev_get_parent(bflow->dev);
51 
52 	return IS_ENABLED(CONFIG_CMD_DHCP) &&
53 	    device_get_uclass_id(media) == UCLASS_ETH;
54 }
55 
efiload_read_file(struct bootflow * bflow,ulong addr)56 static int efiload_read_file(struct bootflow *bflow, ulong addr)
57 {
58 	struct blk_desc *desc = NULL;
59 	ulong size;
60 	int ret;
61 
62 	if (bflow->blk)
63 		 desc = dev_get_uclass_plat(bflow->blk);
64 
65 	size = SZ_1G;
66 	ret = bootmeth_common_read_file(bflow->method, bflow, bflow->fname,
67 					addr, BFI_EFI, &size);
68 	if (ret)
69 		return log_msg_ret("rdf", ret);
70 	bflow->buf = map_sysmem(addr, bflow->size);
71 
72 	return 0;
73 }
74 
distro_efi_check(struct udevice * dev,struct bootflow_iter * iter)75 static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
76 {
77 	/* This only works on block and network devices */
78 	if (bootflow_iter_check_blk(iter) && bootflow_iter_check_net(iter))
79 		return log_msg_ret("blk", -ENOTSUPP);
80 
81 	/* This works on block devices and network devices */
82 	if (iter->method_flags & BOOTFLOW_METHF_PXE_ONLY)
83 		return log_msg_ret("pxe", -ENOTSUPP);
84 
85 	return 0;
86 }
87 
88 /*
89  * distro_efi_try_bootflow_files() - Check that files are present
90  *
91  * This reads any FDT file and checks whether the bootflow file is present, for
92  * later reading. We avoid reading the bootflow now, since it is likely large,
93  * it may take a long time and we want to avoid needing to allocate memory for
94  * it
95  *
96  * @dev: bootmeth device to use
97  * @bflow: bootflow to update
98  */
distro_efi_try_bootflow_files(struct udevice * dev,struct bootflow * bflow)99 static int distro_efi_try_bootflow_files(struct udevice *dev,
100 					 struct bootflow *bflow)
101 {
102 	struct blk_desc *desc = NULL;
103 	ulong fdt_addr, size;
104 	char fname[256];
105 	int ret, seq;
106 
107 	/* We require a partition table */
108 	if (!bflow->part) {
109 		log_debug("no partitions\n");
110 		return -ENOENT;
111 	}
112 
113 	strcpy(fname, EFI_DIRNAME);
114 	strcat(fname, efi_get_basename());
115 
116 	if (bflow->blk)
117 		 desc = dev_get_uclass_plat(bflow->blk);
118 	ret = bootmeth_try_file(bflow, desc, NULL, fname);
119 	if (ret) {
120 		log_debug("File '%s' not found\n", fname);
121 		return log_msg_ret("try", ret);
122 	}
123 
124 	/* Since we can access the file, let's call it ready */
125 	bflow->state = BOOTFLOWST_READY;
126 
127 	fdt_addr = env_get_hex("fdt_addr_r", 0);
128 
129 	/* try the various available names */
130 	ret = -ENOENT;
131 	*fname = '\0';
132 	for (seq = 0; ret == -ENOENT; seq++) {
133 		ret = efi_get_distro_fdt_name(fname, sizeof(fname), seq);
134 		if (ret == -EALREADY)
135 			bflow->flags = BOOTFLOWF_USE_PRIOR_FDT;
136 		if (!ret) {
137 			/* Limit FDT files to 4MB */
138 			size = SZ_4M;
139 			ret = bootmeth_common_read_file(dev, bflow, fname,
140 				fdt_addr, (enum bootflow_img_t)IH_TYPE_FLATDT,
141 				&size);
142 		}
143 	}
144 
145 	if (*fname) {
146 		bflow->fdt_fname = strdup(fname);
147 		if (!bflow->fdt_fname)
148 			return log_msg_ret("fil", -ENOMEM);
149 	}
150 
151 	if (!ret) {
152 		bflow->fdt_size = size;
153 		bflow->fdt_addr = fdt_addr;
154 
155 		/*
156 		 * TODO: Apply extension overlay
157 		 *
158 		 * Here we need to load and apply the extension overlay. This is
159 		 * not implemented. See do_extension_apply(). The extension
160 		 * stuff needs an implementation in boot/extension.c so it is
161 		 * separate from the command code. Really the extension stuff
162 		 * should use the device tree and a uclass / driver interface
163 		 * rather than implementing its own list
164 		 */
165 	} else {
166 		log_debug("No device tree available\n");
167 		bflow->flags |= BOOTFLOWF_USE_BUILTIN_FDT;
168 	}
169 
170 	return 0;
171 }
172 
distro_efi_read_bootflow_net(struct bootflow * bflow)173 static int distro_efi_read_bootflow_net(struct bootflow *bflow)
174 {
175 	char file_addr[17], fname[256];
176 	char *tftp_argv[] = {"tftp", file_addr, fname, NULL};
177 	struct cmd_tbl cmdtp = {};	/* dummy */
178 	const char *addr_str, *fdt_addr_str, *bootfile_name;
179 	int ret, arch, size;
180 	ulong addr, fdt_addr;
181 	char str[36];
182 
183 	ret = get_efi_pxe_vci(str, sizeof(str));
184 	if (ret)
185 		return log_msg_ret("vci", ret);
186 	ret = efi_get_pxe_arch();
187 	if (ret < 0)
188 		return log_msg_ret("arc", ret);
189 	arch = ret;
190 
191 	ret = env_set("bootp_vci", str);
192 	if (ret)
193 		return log_msg_ret("vcs", ret);
194 	ret = env_set_hex("bootp_arch", arch);
195 	if (ret)
196 		return log_msg_ret("ars", ret);
197 
198 	/* figure out the load address */
199 	addr_str = env_get("kernel_addr_r");
200 	addr = addr_str ? hextoul(addr_str, NULL) : image_load_addr;
201 
202 	/* clear any previous bootfile */
203 	env_set("bootfile", NULL);
204 
205 	/* read the kernel */
206 	ret = dhcp_run(addr, NULL, true);
207 	if (ret)
208 		return log_msg_ret("dhc", ret);
209 
210 	size = env_get_hex("filesize", -1);
211 	if (size <= 0)
212 		return log_msg_ret("sz", -EINVAL);
213 	bflow->size = size;
214 	bflow->buf = map_sysmem(addr, size);
215 
216 	/* bootfile should be setup by dhcp */
217 	bootfile_name = env_get("bootfile");
218 	if (!bootfile_name)
219 		return log_msg_ret("bootfile_name", ret);
220 	bflow->fname = strdup(bootfile_name);
221 	if (!bflow->fname)
222 		return log_msg_ret("fi0", -ENOMEM);
223 
224 	/* read the DT file also */
225 	fdt_addr_str = env_get("fdt_addr_r");
226 	if (!fdt_addr_str)
227 		return log_msg_ret("fdt", -EINVAL);
228 	fdt_addr = hextoul(fdt_addr_str, NULL);
229 	sprintf(file_addr, "%lx", fdt_addr);
230 
231 	/* We only allow the first prefix with PXE */
232 	ret = efi_get_distro_fdt_name(fname, sizeof(fname), 0);
233 	if (ret)
234 		return log_msg_ret("nam", ret);
235 
236 	bflow->fdt_fname = strdup(fname);
237 	if (!bflow->fdt_fname)
238 		return log_msg_ret("fil", -ENOMEM);
239 
240 	if (!do_tftpb(&cmdtp, 0, 3, tftp_argv)) {
241 		bflow->fdt_size = env_get_hex("filesize", 0);
242 		bflow->fdt_addr = fdt_addr;
243 	} else {
244 		log_debug("No device tree available\n");
245 		bflow->flags |= BOOTFLOWF_USE_BUILTIN_FDT;
246 	}
247 
248 	bflow->state = BOOTFLOWST_READY;
249 
250 	return 0;
251 }
252 
distro_efi_read_bootflow(struct udevice * dev,struct bootflow * bflow)253 static int distro_efi_read_bootflow(struct udevice *dev, struct bootflow *bflow)
254 {
255 	int ret;
256 
257 	log_debug("dev='%s', part=%d\n", bflow->dev->name, bflow->part);
258 
259 	/*
260 	 * bootmeth_efi doesn't allocate any buffer neither for blk nor net device
261 	 * set flag to avoid freeing static buffer.
262 	 */
263 	bflow->flags |= BOOTFLOWF_STATIC_BUF;
264 
265 	if (bootmeth_uses_network(bflow)) {
266 		/* we only support reading from one device, so ignore 'dev' */
267 		ret = distro_efi_read_bootflow_net(bflow);
268 		if (ret)
269 			return log_msg_ret("net", ret);
270 	} else {
271 		ret = distro_efi_try_bootflow_files(dev, bflow);
272 		if (ret)
273 			return log_msg_ret("blk", ret);
274 	}
275 
276 	return 0;
277 }
278 
distro_efi_boot(struct udevice * dev,struct bootflow * bflow)279 static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow)
280 {
281 	ulong kernel, fdt;
282 	int ret;
283 
284 	log_debug("distro EFI boot\n");
285 	kernel = env_get_hex("kernel_addr_r", 0);
286 	if (!bootmeth_uses_network(bflow)) {
287 		ret = efiload_read_file(bflow, kernel);
288 		if (ret)
289 			return log_msg_ret("read", ret);
290 
291 		/*
292 		 * use the provided device tree if not using the built-in fdt
293 		 */
294 		if (bflow->flags & ~BOOTFLOWF_USE_BUILTIN_FDT)
295 			fdt = bflow->fdt_addr;
296 
297 	}
298 
299 	if (efi_bootflow_run(bflow))
300 		return log_msg_ret("run", -EINVAL);
301 
302 	return 0;
303 }
304 
distro_bootmeth_efi_bind(struct udevice * dev)305 static int distro_bootmeth_efi_bind(struct udevice *dev)
306 {
307 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
308 
309 	plat->desc = IS_ENABLED(CONFIG_BOOTSTD_FULL) ?
310 		"EFI boot from an .efi file" : "EFI";
311 
312 	return 0;
313 }
314 
315 static struct bootmeth_ops distro_efi_bootmeth_ops = {
316 	.check		= distro_efi_check,
317 	.read_bootflow	= distro_efi_read_bootflow,
318 	.read_file	= bootmeth_common_read_file,
319 	.boot		= distro_efi_boot,
320 };
321 
322 static const struct udevice_id distro_efi_bootmeth_ids[] = {
323 	{ .compatible = "u-boot,distro-efi" },
324 	{ }
325 };
326 
327 /* Put a number before 'efi' to provide a default ordering */
328 U_BOOT_DRIVER(bootmeth_4efi) = {
329 	.name		= "bootmeth_efi",
330 	.id		= UCLASS_BOOTMETH,
331 	.of_match	= distro_efi_bootmeth_ids,
332 	.ops		= &distro_efi_bootmeth_ops,
333 	.bind		= distro_bootmeth_efi_bind,
334 };
335