1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Bootmethod for EFI boot manager
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_loader.h>
17 #include <efi_variable.h>
18 #include <malloc.h>
19 
20 /**
21  * struct efi_mgr_priv - private info for the efi-mgr driver
22  *
23  * @fake_bootflow: Fake a valid bootflow for testing
24  */
25 struct efi_mgr_priv {
26 	bool fake_dev;
27 };
28 
sandbox_set_fake_efi_mgr_dev(struct udevice * dev,bool fake_dev)29 void sandbox_set_fake_efi_mgr_dev(struct udevice *dev, bool fake_dev)
30 {
31 	struct efi_mgr_priv *priv = dev_get_priv(dev);
32 
33 	priv->fake_dev = fake_dev;
34 }
35 
efi_mgr_check(struct udevice * dev,struct bootflow_iter * iter)36 static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter)
37 {
38 	int ret;
39 
40 	/* Must be an bootstd device */
41 	ret = bootflow_iter_check_system(iter);
42 	if (ret)
43 		return log_msg_ret("net", ret);
44 
45 	return 0;
46 }
47 
efi_mgr_read_bootflow(struct udevice * dev,struct bootflow * bflow)48 static int efi_mgr_read_bootflow(struct udevice *dev, struct bootflow *bflow)
49 {
50 	struct efi_mgr_priv *priv = dev_get_priv(dev);
51 	efi_status_t ret;
52 	efi_uintn_t size;
53 	u16 *bootorder;
54 
55 	if (priv->fake_dev) {
56 		bflow->state = BOOTFLOWST_READY;
57 		return 0;
58 	}
59 
60 	ret = efi_init_obj_list();
61 	if (ret)
62 		return log_msg_ret("init", ret);
63 
64 	/* Enable this method if the "BootOrder" UEFI exists. */
65 	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid,
66 				&size);
67 	if (bootorder) {
68 		free(bootorder);
69 		bflow->state = BOOTFLOWST_READY;
70 		return 0;
71 	}
72 
73 	return -EINVAL;
74 }
75 
efi_mgr_read_file(struct udevice * dev,struct bootflow * bflow,const char * file_path,ulong addr,enum bootflow_img_t type,ulong * sizep)76 static int efi_mgr_read_file(struct udevice *dev, struct bootflow *bflow,
77 			     const char *file_path, ulong addr,
78 			     enum bootflow_img_t type, ulong *sizep)
79 {
80 	/* Files are loaded by the 'bootefi bootmgr' command */
81 
82 	return -ENOSYS;
83 }
84 
efi_mgr_boot(struct udevice * dev,struct bootflow * bflow)85 static int efi_mgr_boot(struct udevice *dev, struct bootflow *bflow)
86 {
87 	int ret;
88 
89 	/* Booting is handled by the 'bootefi bootmgr' command */
90 	ret = efi_bootmgr_run(EFI_FDT_USE_INTERNAL);
91 
92 	return 0;
93 }
94 
bootmeth_efi_mgr_bind(struct udevice * dev)95 static int bootmeth_efi_mgr_bind(struct udevice *dev)
96 {
97 	struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
98 
99 	plat->desc = "EFI bootmgr flow";
100 	plat->flags = BOOTMETHF_GLOBAL;
101 
102 	return 0;
103 }
104 
105 static struct bootmeth_ops efi_mgr_bootmeth_ops = {
106 	.check		= efi_mgr_check,
107 	.read_bootflow	= efi_mgr_read_bootflow,
108 	.read_file	= efi_mgr_read_file,
109 	.boot		= efi_mgr_boot,
110 };
111 
112 static const struct udevice_id efi_mgr_bootmeth_ids[] = {
113 	{ .compatible = "u-boot,efi-bootmgr" },
114 	{ }
115 };
116 
117 U_BOOT_DRIVER(bootmeth_3efi_mgr) = {
118 	.name		= "bootmeth_efi_mgr",
119 	.id		= UCLASS_BOOTMETH,
120 	.of_match	= efi_mgr_bootmeth_ids,
121 	.ops		= &efi_mgr_bootmeth_ops,
122 	.bind		= bootmeth_efi_mgr_bind,
123 	.priv_auto	= sizeof(struct efi_mgr_priv),
124 };
125