]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0+ | |
2 | /* | |
3 | * EFI application loader | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
6 | */ | |
7 | ||
8 | #define LOG_CATEGORY LOGC_EFI | |
9 | ||
10 | #include <command.h> | |
11 | #include <efi.h> | |
12 | #include <efi_loader.h> | |
13 | #include <exports.h> | |
14 | #include <log.h> | |
15 | #include <malloc.h> | |
16 | #include <mapmem.h> | |
17 | #include <vsprintf.h> | |
18 | #include <asm-generic/sections.h> | |
19 | #include <asm/global_data.h> | |
20 | #include <linux/string.h> | |
21 | ||
22 | DECLARE_GLOBAL_DATA_PTR; | |
23 | ||
24 | static struct efi_device_path *test_image_path; | |
25 | static struct efi_device_path *test_device_path; | |
26 | ||
27 | static efi_status_t bootefi_run_prepare(const char *load_options_path, | |
28 | struct efi_device_path *device_path, | |
29 | struct efi_device_path *image_path, | |
30 | struct efi_loaded_image_obj **image_objp, | |
31 | struct efi_loaded_image **loaded_image_infop) | |
32 | { | |
33 | efi_status_t ret; | |
34 | u16 *load_options; | |
35 | ||
36 | ret = efi_setup_loaded_image(device_path, image_path, image_objp, | |
37 | loaded_image_infop); | |
38 | if (ret != EFI_SUCCESS) | |
39 | return ret; | |
40 | ||
41 | /* Transfer environment variable as load options */ | |
42 | return efi_env_set_load_options((efi_handle_t)*image_objp, | |
43 | load_options_path, | |
44 | &load_options); | |
45 | } | |
46 | ||
47 | /** | |
48 | * bootefi_test_prepare() - prepare to run an EFI test | |
49 | * | |
50 | * Prepare to run a test as if it were provided by a loaded image. | |
51 | * | |
52 | * @image_objp: pointer to be set to the loaded image handle | |
53 | * @loaded_image_infop: pointer to be set to the loaded image protocol | |
54 | * @path: dummy file path used to construct the device path | |
55 | * set in the loaded image protocol | |
56 | * @load_options_path: name of a U-Boot environment variable. Its value is | |
57 | * set as load options in the loaded image protocol. | |
58 | * Return: status code | |
59 | */ | |
60 | static efi_status_t bootefi_test_prepare | |
61 | (struct efi_loaded_image_obj **image_objp, | |
62 | struct efi_loaded_image **loaded_image_infop, const char *path, | |
63 | const char *load_options_path) | |
64 | { | |
65 | efi_status_t ret; | |
66 | ||
67 | /* Construct a dummy device path */ | |
68 | test_device_path = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, 0, 0); | |
69 | if (!test_device_path) | |
70 | return EFI_OUT_OF_RESOURCES; | |
71 | ||
72 | test_image_path = efi_dp_from_file(NULL, path); | |
73 | if (!test_image_path) { | |
74 | ret = EFI_OUT_OF_RESOURCES; | |
75 | goto failure; | |
76 | } | |
77 | ||
78 | ret = bootefi_run_prepare(load_options_path, test_device_path, | |
79 | test_image_path, image_objp, | |
80 | loaded_image_infop); | |
81 | if (ret == EFI_SUCCESS) | |
82 | return ret; | |
83 | ||
84 | failure: | |
85 | efi_free_pool(test_device_path); | |
86 | efi_free_pool(test_image_path); | |
87 | /* TODO: not sure calling clear function is necessary */ | |
88 | efi_clear_bootdev(); | |
89 | return ret; | |
90 | } | |
91 | ||
92 | /** | |
93 | * do_efi_selftest() - execute EFI selftest | |
94 | * | |
95 | * Return: status code | |
96 | */ | |
97 | static int do_efi_selftest(void) | |
98 | { | |
99 | struct efi_loaded_image_obj *image_obj; | |
100 | struct efi_loaded_image *loaded_image_info; | |
101 | efi_status_t ret; | |
102 | ||
103 | ret = bootefi_test_prepare(&image_obj, &loaded_image_info, | |
104 | "\\selftest", "efi_selftest"); | |
105 | if (ret != EFI_SUCCESS) | |
106 | return CMD_RET_FAILURE; | |
107 | ||
108 | /* Execute the test */ | |
109 | ret = EFI_CALL(efi_selftest(&image_obj->header, &systab)); | |
110 | free(loaded_image_info->load_options); | |
111 | efi_free_pool(test_device_path); | |
112 | efi_free_pool(test_image_path); | |
113 | if (ret != EFI_SUCCESS) | |
114 | efi_delete_handle(&image_obj->header); | |
115 | else | |
116 | ret = efi_delete_handle(&image_obj->header); | |
117 | ||
118 | return ret != EFI_SUCCESS; | |
119 | } | |
120 | ||
121 | /** | |
122 | * do_bootefi() - execute `bootefi` command | |
123 | * | |
124 | * @cmdtp: table entry describing command | |
125 | * @flag: bitmap indicating how the command was invoked | |
126 | * @argc: number of arguments | |
127 | * @argv: command line arguments | |
128 | * Return: status code | |
129 | */ | |
130 | static int do_bootefi(struct cmd_tbl *cmdtp, int flag, int argc, | |
131 | char *const argv[]) | |
132 | { | |
133 | efi_status_t ret; | |
134 | char *p; | |
135 | void *fdt, *image_buf; | |
136 | unsigned long addr, size; | |
137 | void *image_addr; | |
138 | size_t image_size; | |
139 | ||
140 | if (argc < 2) | |
141 | return CMD_RET_USAGE; | |
142 | ||
143 | if (argc > 2) { | |
144 | uintptr_t fdt_addr; | |
145 | ||
146 | fdt_addr = hextoul(argv[2], NULL); | |
147 | fdt = map_sysmem(fdt_addr, 0); | |
148 | } else { | |
149 | fdt = EFI_FDT_USE_INTERNAL; | |
150 | } | |
151 | ||
152 | if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && | |
153 | !strcmp(argv[1], "bootmgr")) { | |
154 | ret = efi_bootmgr_run(fdt); | |
155 | ||
156 | if (ret != EFI_SUCCESS) | |
157 | return CMD_RET_FAILURE; | |
158 | ||
159 | return CMD_RET_SUCCESS; | |
160 | } | |
161 | ||
162 | if (IS_ENABLED(CONFIG_CMD_BOOTEFI_SELFTEST) && | |
163 | !strcmp(argv[1], "selftest")) { | |
164 | /* Initialize EFI drivers */ | |
165 | ret = efi_init_obj_list(); | |
166 | if (ret != EFI_SUCCESS) { | |
167 | log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", | |
168 | ret & ~EFI_ERROR_MASK); | |
169 | return CMD_RET_FAILURE; | |
170 | } | |
171 | ||
172 | ret = efi_install_fdt(fdt); | |
173 | if (ret != EFI_SUCCESS) | |
174 | return CMD_RET_FAILURE; | |
175 | ||
176 | return do_efi_selftest(); | |
177 | } | |
178 | ||
179 | if (!IS_ENABLED(CONFIG_CMD_BOOTEFI_BINARY)) | |
180 | return CMD_RET_SUCCESS; | |
181 | ||
182 | if (IS_ENABLED(CONFIG_CMD_BOOTEFI_HELLO) && | |
183 | !strcmp(argv[1], "hello")) { | |
184 | image_buf = __efi_helloworld_begin; | |
185 | size = __efi_helloworld_end - __efi_helloworld_begin; | |
186 | /* TODO: not sure calling clear function is necessary */ | |
187 | efi_clear_bootdev(); | |
188 | } else { | |
189 | addr = strtoul(argv[1], NULL, 16); | |
190 | /* Check that a numeric value was passed */ | |
191 | if (!addr) | |
192 | return CMD_RET_USAGE; | |
193 | image_buf = map_sysmem(addr, 0); | |
194 | ||
195 | p = strchr(argv[1], ':'); | |
196 | if (p) { | |
197 | size = strtoul(++p, NULL, 16); | |
198 | if (!size) | |
199 | return CMD_RET_USAGE; | |
200 | efi_clear_bootdev(); | |
201 | } else { | |
202 | /* Image should be already loaded */ | |
203 | efi_get_image_parameters(&image_addr, &image_size); | |
204 | ||
205 | if (image_buf != image_addr) { | |
206 | log_err("No UEFI binary known at %s\n", | |
207 | argv[1]); | |
208 | return CMD_RET_FAILURE; | |
209 | } | |
210 | size = image_size; | |
211 | } | |
212 | } | |
213 | ||
214 | ret = efi_binary_run(image_buf, size, fdt); | |
215 | ||
216 | if (ret != EFI_SUCCESS) | |
217 | return CMD_RET_FAILURE; | |
218 | ||
219 | return CMD_RET_SUCCESS; | |
220 | } | |
221 | ||
222 | U_BOOT_LONGHELP(bootefi, | |
223 | "<image address>[:<image size>] [<fdt address>]\n" | |
224 | " - boot EFI payload\n" | |
225 | #ifdef CONFIG_CMD_BOOTEFI_HELLO | |
226 | "bootefi hello\n" | |
227 | " - boot a sample Hello World application stored within U-Boot\n" | |
228 | #endif | |
229 | #ifdef CONFIG_CMD_BOOTEFI_SELFTEST | |
230 | "bootefi selftest [fdt address]\n" | |
231 | " - boot an EFI selftest application stored within U-Boot\n" | |
232 | " Use environment variable efi_selftest to select a single test.\n" | |
233 | " Use 'setenv efi_selftest list' to enumerate all tests.\n" | |
234 | #endif | |
235 | #ifdef CONFIG_CMD_BOOTEFI_BOOTMGR | |
236 | "bootefi bootmgr [fdt address]\n" | |
237 | " - load and boot EFI payload based on BootOrder/BootXXXX variables.\n" | |
238 | "\n" | |
239 | " If specified, the device tree located at <fdt address> gets\n" | |
240 | " exposed as EFI configuration table.\n" | |
241 | #endif | |
242 | ); | |
243 | ||
244 | U_BOOT_CMD( | |
245 | bootefi, 4, 0, do_bootefi, | |
246 | "Boots an EFI payload from memory", | |
247 | bootefi_help_text | |
248 | ); |