• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  EFI boot manager
4  *
5  *  Copyright (c) 2017 Rob Clark
6  */
7 
8 #include <common.h>
9 #include <charset.h>
10 #include <malloc.h>
11 #include <efi_loader.h>
12 #include <asm/unaligned.h>
13 
14 static const struct efi_boot_services *bs;
15 static const struct efi_runtime_services *rs;
16 
17 /*
18  * bootmgr implements the logic of trying to find a payload to boot
19  * based on the BootOrder + BootXXXX variables, and then loading it.
20  *
21  * TODO detecting a special key held (f9?) and displaying a boot menu
22  * like you would get on a PC would be clever.
23  *
24  * TODO if we had a way to write and persist variables after the OS
25  * has started, we'd also want to check OsIndications to see if we
26  * should do normal or recovery boot.
27  */
28 
29 
30 /**
31  * efi_deserialize_load_option() - parse serialized data
32  *
33  * Parse serialized data describing a load option and transform it to the
34  * efi_load_option structure.
35  *
36  * @lo:		pointer to target
37  * @data:	serialized data
38  */
efi_deserialize_load_option(struct efi_load_option * lo,u8 * data)39 void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data)
40 {
41 	lo->attributes = get_unaligned_le32(data);
42 	data += sizeof(u32);
43 
44 	lo->file_path_length = get_unaligned_le16(data);
45 	data += sizeof(u16);
46 
47 	/* FIXME */
48 	lo->label = (u16 *)data;
49 	data += (u16_strlen(lo->label) + 1) * sizeof(u16);
50 
51 	/* FIXME */
52 	lo->file_path = (struct efi_device_path *)data;
53 	data += lo->file_path_length;
54 
55 	lo->optional_data = data;
56 }
57 
58 /**
59  * efi_serialize_load_option() - serialize load option
60  *
61  * Serialize efi_load_option structure into byte stream for BootXXXX.
62  *
63  * @data:	buffer for serialized data
64  * @lo:		load option
65  * Return:	size of allocated buffer
66  */
efi_serialize_load_option(struct efi_load_option * lo,u8 ** data)67 unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
68 {
69 	unsigned long label_len;
70 	unsigned long size;
71 	u8 *p;
72 
73 	label_len = (u16_strlen(lo->label) + 1) * sizeof(u16);
74 
75 	/* total size */
76 	size = sizeof(lo->attributes);
77 	size += sizeof(lo->file_path_length);
78 	size += label_len;
79 	size += lo->file_path_length;
80 	if (lo->optional_data)
81 		size += (utf8_utf16_strlen((const char *)lo->optional_data)
82 					   + 1) * sizeof(u16);
83 	p = malloc(size);
84 	if (!p)
85 		return 0;
86 
87 	/* copy data */
88 	*data = p;
89 	memcpy(p, &lo->attributes, sizeof(lo->attributes));
90 	p += sizeof(lo->attributes);
91 
92 	memcpy(p, &lo->file_path_length, sizeof(lo->file_path_length));
93 	p += sizeof(lo->file_path_length);
94 
95 	memcpy(p, lo->label, label_len);
96 	p += label_len;
97 
98 	memcpy(p, lo->file_path, lo->file_path_length);
99 	p += lo->file_path_length;
100 
101 	if (lo->optional_data) {
102 		utf8_utf16_strcpy((u16 **)&p, (const char *)lo->optional_data);
103 		p += sizeof(u16); /* size of trailing \0 */
104 	}
105 	return size;
106 }
107 
108 /**
109  * get_var() - get UEFI variable
110  *
111  * It is the caller's duty to free the returned buffer.
112  *
113  * @name:	name of variable
114  * @vendor:	vendor GUID of variable
115  * @size:	size of allocated buffer
116  * Return:	buffer with variable data or NULL
117  */
get_var(u16 * name,const efi_guid_t * vendor,efi_uintn_t * size)118 static void *get_var(u16 *name, const efi_guid_t *vendor,
119 		     efi_uintn_t *size)
120 {
121 	efi_guid_t *v = (efi_guid_t *)vendor;
122 	efi_status_t ret;
123 	void *buf = NULL;
124 
125 	*size = 0;
126 	EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
127 	if (ret == EFI_BUFFER_TOO_SMALL) {
128 		buf = malloc(*size);
129 		EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
130 	}
131 
132 	if (ret != EFI_SUCCESS) {
133 		free(buf);
134 		*size = 0;
135 		return NULL;
136 	}
137 
138 	return buf;
139 }
140 
141 /**
142  * try_load_entry() - try to load image for boot option
143  *
144  * Attempt to load load-option number 'n', returning device_path and file_path
145  * if successful. This checks that the EFI_LOAD_OPTION is active (enabled)
146  * and that the specified file to boot exists.
147  *
148  * @n:		number of the boot option, e.g. 0x0a13 for Boot0A13
149  * @handle:	on return handle for the newly installed image
150  * Return:	status code
151  */
try_load_entry(u16 n,efi_handle_t * handle)152 static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
153 {
154 	struct efi_load_option lo;
155 	u16 varname[] = L"Boot0000";
156 	u16 hexmap[] = L"0123456789ABCDEF";
157 	void *load_option;
158 	efi_uintn_t size;
159 	efi_status_t ret;
160 
161 	varname[4] = hexmap[(n & 0xf000) >> 12];
162 	varname[5] = hexmap[(n & 0x0f00) >> 8];
163 	varname[6] = hexmap[(n & 0x00f0) >> 4];
164 	varname[7] = hexmap[(n & 0x000f) >> 0];
165 
166 	load_option = get_var(varname, &efi_global_variable_guid, &size);
167 	if (!load_option)
168 		return EFI_LOAD_ERROR;
169 
170 	efi_deserialize_load_option(&lo, load_option);
171 
172 	if (lo.attributes & LOAD_OPTION_ACTIVE) {
173 		u32 attributes;
174 
175 		debug("%s: trying to load \"%ls\" from %pD\n",
176 		      __func__, lo.label, lo.file_path);
177 
178 		ret = EFI_CALL(efi_load_image(true, efi_root, lo.file_path,
179 					      NULL, 0, handle));
180 		if (ret != EFI_SUCCESS) {
181 			printf("Loading from Boot%04X '%ls' failed\n", n,
182 			       lo.label);
183 			goto error;
184 		}
185 
186 		attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
187 			     EFI_VARIABLE_RUNTIME_ACCESS;
188 		size = sizeof(n);
189 		ret = EFI_CALL(efi_set_variable(
190 				L"BootCurrent",
191 				(efi_guid_t *)&efi_global_variable_guid,
192 				attributes, size, &n));
193 		if (ret != EFI_SUCCESS) {
194 			if (EFI_CALL(efi_unload_image(*handle))
195 			    != EFI_SUCCESS)
196 				printf("Unloading image failed\n");
197 			goto error;
198 		}
199 
200 		printf("Booting: %ls\n", lo.label);
201 	} else {
202 		ret = EFI_LOAD_ERROR;
203 	}
204 
205 error:
206 	free(load_option);
207 
208 	return ret;
209 }
210 
211 /**
212  * efi_bootmgr_load() - try to load from BootNext or BootOrder
213  *
214  * Attempt to load from BootNext or in the order specified by BootOrder
215  * EFI variable, the available load-options, finding and returning
216  * the first one that can be loaded successfully.
217  *
218  * @handle:	on return handle for the newly installed image
219  * Return:	status code
220  */
efi_bootmgr_load(efi_handle_t * handle)221 efi_status_t efi_bootmgr_load(efi_handle_t *handle)
222 {
223 	u16 bootnext, *bootorder;
224 	efi_uintn_t size;
225 	int i, num;
226 	efi_status_t ret;
227 
228 	bs = systab.boottime;
229 	rs = systab.runtime;
230 
231 	/* BootNext */
232 	bootnext = 0;
233 	size = sizeof(bootnext);
234 	ret = EFI_CALL(efi_get_variable(L"BootNext",
235 					(efi_guid_t *)&efi_global_variable_guid,
236 					NULL, &size, &bootnext));
237 	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
238 		/* BootNext does exist here */
239 		if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
240 			printf("BootNext must be 16-bit integer\n");
241 
242 		/* delete BootNext */
243 		ret = EFI_CALL(efi_set_variable(
244 					L"BootNext",
245 					(efi_guid_t *)&efi_global_variable_guid,
246 					EFI_VARIABLE_NON_VOLATILE, 0,
247 					&bootnext));
248 
249 		/* load BootNext */
250 		if (ret == EFI_SUCCESS) {
251 			if (size == sizeof(u16)) {
252 				ret = try_load_entry(bootnext, handle);
253 				if (ret == EFI_SUCCESS)
254 					return ret;
255 				printf("Loading from BootNext failed, falling back to BootOrder\n");
256 			}
257 		} else {
258 			printf("Deleting BootNext failed\n");
259 		}
260 	}
261 
262 	/* BootOrder */
263 	bootorder = get_var(L"BootOrder", &efi_global_variable_guid, &size);
264 	if (!bootorder) {
265 		printf("BootOrder not defined\n");
266 		ret = EFI_NOT_FOUND;
267 		goto error;
268 	}
269 
270 	num = size / sizeof(uint16_t);
271 	for (i = 0; i < num; i++) {
272 		debug("%s: trying to load Boot%04X\n", __func__, bootorder[i]);
273 		ret = try_load_entry(bootorder[i], handle);
274 		if (ret == EFI_SUCCESS)
275 			break;
276 	}
277 
278 	free(bootorder);
279 
280 error:
281 	return ret;
282 }
283