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 <common.h>
12#include <bootdev.h>
13#include <bootflow.h>
14#include <bootmeth.h>
15#include <command.h>
16#include <dm.h>
17#include <efi_loader.h>
18#include <efi_variable.h>
19#include <malloc.h>
20
21/**
22 * struct efi_mgr_priv - private info for the efi-mgr driver
23 *
24 * @fake_bootflow: Fake a valid bootflow for testing
25 */
26struct efi_mgr_priv {
27	bool fake_dev;
28};
29
30void sandbox_set_fake_efi_mgr_dev(struct udevice *dev, bool fake_dev)
31{
32	struct efi_mgr_priv *priv = dev_get_priv(dev);
33
34	priv->fake_dev = fake_dev;
35}
36
37static int efi_mgr_check(struct udevice *dev, struct bootflow_iter *iter)
38{
39	int ret;
40
41	/* Must be an bootstd device */
42	ret = bootflow_iter_check_system(iter);
43	if (ret)
44		return log_msg_ret("net", ret);
45
46	return 0;
47}
48
49static int efi_mgr_read_bootflow(struct udevice *dev, struct bootflow *bflow)
50{
51	struct efi_mgr_priv *priv = dev_get_priv(dev);
52	efi_status_t ret;
53	efi_uintn_t size;
54	u16 *bootorder;
55
56	if (priv->fake_dev) {
57		bflow->state = BOOTFLOWST_READY;
58		return 0;
59	}
60
61	ret = efi_init_obj_list();
62	if (ret)
63		return log_msg_ret("init", ret);
64
65	/* Enable this method if the "BootOrder" UEFI exists. */
66	bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid,
67				&size);
68	if (bootorder) {
69		free(bootorder);
70		bflow->state = BOOTFLOWST_READY;
71		return 0;
72	}
73
74	return -EINVAL;
75}
76
77static int efi_mgr_read_file(struct udevice *dev, struct bootflow *bflow,
78				const char *file_path, ulong addr, ulong *sizep)
79{
80	/* Files are loaded by the 'bootefi bootmgr' command */
81
82	return -ENOSYS;
83}
84
85static 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
95static 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
105static 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
112static const struct udevice_id efi_mgr_bootmeth_ids[] = {
113	{ .compatible = "u-boot,efi-bootmgr" },
114	{ }
115};
116
117U_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