]>
Commit | Line | Data |
---|---|---|
eb9f7f1c PJ |
1 | /* |
2 | * Copyright 2012-2013 Red Hat, Inc. | |
3 | * All rights reserved. | |
4 | * | |
5 | * See "COPYING" for license terms. | |
6 | * | |
7 | * Author(s): Peter Jones <pjones@redhat.com> | |
8 | */ | |
9 | ||
10 | #include <efi.h> | |
11 | #include <efilib.h> | |
12 | ||
13 | #include "ucs2.h" | |
d3819813 | 14 | #include "variables.h" |
eb9f7f1c PJ |
15 | |
16 | EFI_LOADED_IMAGE *this_image = NULL; | |
17 | ||
6edc6ec0 PJ |
18 | static EFI_STATUS |
19 | FindSubDevicePath(EFI_DEVICE_PATH *In, UINT8 Type, UINT8 SubType, | |
20 | EFI_DEVICE_PATH **Out) | |
21 | { | |
22 | EFI_DEVICE_PATH *dp = In; | |
23 | if (!In || !Out) | |
24 | return EFI_INVALID_PARAMETER; | |
25 | ||
26 | for (dp = In; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) { | |
27 | if (DevicePathType(dp) == Type && | |
28 | DevicePathSubType(dp) == SubType) { | |
29 | *Out = DuplicateDevicePath(dp); | |
30 | if (!*Out) | |
31 | return EFI_OUT_OF_RESOURCES; | |
32 | return EFI_SUCCESS; | |
33 | } | |
34 | } | |
35 | *Out = NULL; | |
36 | return EFI_NOT_FOUND; | |
37 | } | |
38 | ||
eb9f7f1c | 39 | static EFI_STATUS |
2c5f9938 | 40 | get_file_size(EFI_FILE_HANDLE fh, UINTN *retsize) |
eb9f7f1c PJ |
41 | { |
42 | EFI_STATUS rc; | |
43 | void *buffer = NULL; | |
44 | UINTN bs = 0; | |
45 | EFI_GUID finfo = EFI_FILE_INFO_ID; | |
46 | ||
47 | /* The API here is "Call it once with bs=0, it fills in bs, | |
48 | * then allocate a buffer and ask again to get it filled. */ | |
49 | rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL); | |
50 | if (rc == EFI_BUFFER_TOO_SMALL) { | |
51 | buffer = AllocateZeroPool(bs); | |
52 | if (!buffer) { | |
53 | Print(L"Could not allocate memory\n"); | |
54 | return EFI_OUT_OF_RESOURCES; | |
55 | } | |
56 | rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, | |
57 | &bs, buffer); | |
58 | } | |
59 | /* This checks *either* the error from the first GetInfo, if it isn't | |
60 | * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call | |
61 | * in *any* case. */ | |
62 | if (EFI_ERROR(rc)) { | |
63 | Print(L"Could not get file info: %d\n", rc); | |
64 | if (buffer) | |
65 | FreePool(buffer); | |
66 | return rc; | |
67 | } | |
68 | EFI_FILE_INFO *fi = buffer; | |
69 | *retsize = fi->FileSize; | |
70 | FreePool(buffer); | |
71 | return EFI_SUCCESS; | |
72 | } | |
73 | ||
74 | EFI_STATUS | |
75 | read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) | |
76 | { | |
77 | EFI_FILE_HANDLE fh2; | |
78 | EFI_STATUS rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, fullpath, | |
79 | EFI_FILE_READ_ONLY, 0); | |
80 | if (EFI_ERROR(rc)) { | |
81 | Print(L"Couldn't open \"%s\": %d\n", fullpath, rc); | |
82 | return rc; | |
83 | } | |
84 | ||
2c5f9938 | 85 | UINTN len = 0; |
eb9f7f1c PJ |
86 | CHAR16 *b = NULL; |
87 | rc = get_file_size(fh2, &len); | |
88 | if (EFI_ERROR(rc)) { | |
89 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
90 | return rc; | |
91 | } | |
92 | ||
93 | b = AllocateZeroPool(len + 2); | |
94 | if (!buffer) { | |
95 | Print(L"Could not allocate memory\n"); | |
96 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
97 | return EFI_OUT_OF_RESOURCES; | |
98 | } | |
99 | ||
100 | rc = uefi_call_wrapper(fh->Read, 3, fh, &len, b); | |
101 | if (EFI_ERROR(rc)) { | |
102 | FreePool(buffer); | |
103 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
104 | Print(L"Could not read file: %d\n", rc); | |
105 | return rc; | |
106 | } | |
107 | *buffer = b; | |
108 | *bs = len; | |
109 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
110 | return EFI_SUCCESS; | |
111 | } | |
112 | ||
113 | EFI_STATUS | |
114 | make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) | |
115 | { | |
116 | UINT64 len; | |
117 | ||
6edc6ec0 PJ |
118 | len = StrLen(L"\\EFI\\") + StrLen(dirname) |
119 | + StrLen(L"\\") + StrLen(filename) | |
120 | + 2; | |
eb9f7f1c | 121 | |
17266fd0 | 122 | CHAR16 *fullpath = AllocateZeroPool(len*sizeof(CHAR16)); |
eb9f7f1c PJ |
123 | if (!fullpath) { |
124 | Print(L"Could not allocate memory\n"); | |
125 | return EFI_OUT_OF_RESOURCES; | |
126 | } | |
127 | ||
128 | StrCat(fullpath, L"\\EFI\\"); | |
129 | StrCat(fullpath, dirname); | |
130 | StrCat(fullpath, L"\\"); | |
131 | StrCat(fullpath, filename); | |
132 | ||
133 | *out = fullpath; | |
134 | *outlen = len; | |
135 | return EFI_SUCCESS; | |
136 | } | |
137 | ||
138 | CHAR16 *bootorder = NULL; | |
139 | int nbootorder = 0; | |
140 | ||
8807e36a PJ |
141 | EFI_DEVICE_PATH *first_new_option = NULL; |
142 | VOID *first_new_option_args = NULL; | |
143 | UINTN first_new_option_size = 0; | |
144 | ||
eb9f7f1c | 145 | EFI_STATUS |
6edc6ec0 PJ |
146 | add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp, |
147 | CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) | |
eb9f7f1c PJ |
148 | { |
149 | static int i = 0; | |
150 | CHAR16 varname[] = L"Boot0000"; | |
151 | CHAR16 hexmap[] = L"0123456789ABCDEF"; | |
152 | EFI_GUID global = EFI_GLOBAL_VARIABLE; | |
153 | EFI_STATUS rc; | |
154 | ||
155 | for(; i <= 0xffff; i++) { | |
156 | varname[4] = hexmap[(i & 0xf000) >> 12]; | |
157 | varname[5] = hexmap[(i & 0x0f00) >> 8]; | |
158 | varname[6] = hexmap[(i & 0x00f0) >> 4]; | |
159 | varname[7] = hexmap[(i & 0x000f) >> 0]; | |
160 | ||
161 | void *var = LibGetVariable(varname, &global); | |
162 | if (!var) { | |
163 | int size = sizeof(UINT32) + sizeof (UINT16) + | |
6edc6ec0 PJ |
164 | StrLen(label)*2 + 2 + DevicePathSize(hddp) + |
165 | StrLen(arguments) * 2; | |
eb9f7f1c | 166 | |
d3819813 | 167 | CHAR8 *data = AllocateZeroPool(size + 2); |
eb9f7f1c PJ |
168 | CHAR8 *cursor = data; |
169 | *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; | |
170 | cursor += sizeof (UINT32); | |
6edc6ec0 | 171 | *(UINT16 *)cursor = DevicePathSize(hddp); |
eb9f7f1c PJ |
172 | cursor += sizeof (UINT16); |
173 | StrCpy((CHAR16 *)cursor, label); | |
174 | cursor += StrLen(label)*2 + 2; | |
6edc6ec0 PJ |
175 | CopyMem(cursor, hddp, DevicePathSize(hddp)); |
176 | cursor += DevicePathSize(hddp); | |
eb9f7f1c PJ |
177 | StrCpy((CHAR16 *)cursor, arguments); |
178 | ||
179 | Print(L"Creating boot entry \"%s\" with label \"%s\" " | |
180 | L"for file \"%s\"\n", | |
181 | varname, label, filename); | |
6edc6ec0 PJ |
182 | |
183 | if (!first_new_option) { | |
184 | first_new_option = DuplicateDevicePath(fulldp); | |
185 | first_new_option_args = arguments; | |
186 | first_new_option_size = StrLen(arguments) * sizeof (CHAR16); | |
187 | } | |
188 | ||
eb9f7f1c PJ |
189 | rc = uefi_call_wrapper(RT->SetVariable, 5, varname, |
190 | &global, EFI_VARIABLE_NON_VOLATILE | | |
191 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
192 | EFI_VARIABLE_RUNTIME_ACCESS, | |
193 | size, data); | |
194 | ||
195 | FreePool(data); | |
196 | ||
197 | if (EFI_ERROR(rc)) { | |
198 | Print(L"Could not create variable: %d\n", rc); | |
199 | return rc; | |
200 | } | |
201 | ||
202 | CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16) | |
203 | * (nbootorder + 1)); | |
204 | if (!newbootorder) | |
205 | return EFI_OUT_OF_RESOURCES; | |
206 | ||
207 | int j = 0; | |
ec7eddbf | 208 | newbootorder[0] = i & 0xffff; |
eb9f7f1c PJ |
209 | if (nbootorder) { |
210 | for (j = 0; j < nbootorder; j++) | |
ec7eddbf | 211 | newbootorder[j+1] = bootorder[j]; |
eb9f7f1c PJ |
212 | FreePool(bootorder); |
213 | } | |
eb9f7f1c PJ |
214 | bootorder = newbootorder; |
215 | nbootorder += 1; | |
216 | #ifdef DEBUG_FALLBACK | |
217 | Print(L"nbootorder: %d\nBootOrder: ", nbootorder); | |
218 | for (j = 0 ; j < nbootorder ; j++) | |
219 | Print(L"%04x ", bootorder[j]); | |
220 | Print(L"\n"); | |
221 | #endif | |
222 | ||
223 | return EFI_SUCCESS; | |
224 | } | |
225 | } | |
226 | return EFI_OUT_OF_RESOURCES; | |
227 | } | |
228 | ||
9fcd221e | 229 | EFI_STATUS |
8bf83b55 GCPL |
230 | find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp, |
231 | CHAR16 *filename, CHAR16 *label, CHAR16 *arguments, | |
232 | UINT16 *optnum) | |
9fcd221e | 233 | { |
47a9d2c9 | 234 | unsigned int size = sizeof(UINT32) + sizeof (UINT16) + |
9fcd221e | 235 | StrLen(label)*2 + 2 + DevicePathSize(dp) + |
30cead3b | 236 | StrLen(arguments) * 2; |
9fcd221e | 237 | |
d3819813 | 238 | CHAR8 *data = AllocateZeroPool(size + 2); |
9fcd221e PJ |
239 | if (!data) |
240 | return EFI_OUT_OF_RESOURCES; | |
241 | CHAR8 *cursor = data; | |
242 | *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; | |
243 | cursor += sizeof (UINT32); | |
244 | *(UINT16 *)cursor = DevicePathSize(dp); | |
245 | cursor += sizeof (UINT16); | |
246 | StrCpy((CHAR16 *)cursor, label); | |
247 | cursor += StrLen(label)*2 + 2; | |
248 | CopyMem(cursor, dp, DevicePathSize(dp)); | |
249 | cursor += DevicePathSize(dp); | |
250 | StrCpy((CHAR16 *)cursor, arguments); | |
251 | ||
252 | int i = 0; | |
253 | CHAR16 varname[] = L"Boot0000"; | |
254 | CHAR16 hexmap[] = L"0123456789ABCDEF"; | |
255 | EFI_GUID global = EFI_GLOBAL_VARIABLE; | |
256 | EFI_STATUS rc; | |
257 | ||
258 | CHAR8 *candidate = AllocateZeroPool(size); | |
259 | if (!candidate) { | |
260 | FreePool(data); | |
261 | return EFI_OUT_OF_RESOURCES; | |
262 | } | |
263 | ||
264 | for(i = 0; i < nbootorder && i < 0x10000; i++) { | |
265 | varname[4] = hexmap[(bootorder[i] & 0xf000) >> 12]; | |
266 | varname[5] = hexmap[(bootorder[i] & 0x0f00) >> 8]; | |
267 | varname[6] = hexmap[(bootorder[i] & 0x00f0) >> 4]; | |
268 | varname[7] = hexmap[(bootorder[i] & 0x000f) >> 0]; | |
269 | ||
270 | UINTN candidate_size = size; | |
271 | rc = uefi_call_wrapper(RT->GetVariable, 5, varname, &global, | |
272 | NULL, &candidate_size, candidate); | |
273 | if (EFI_ERROR(rc)) | |
274 | continue; | |
275 | ||
276 | if (candidate_size != size) | |
277 | continue; | |
278 | ||
279 | if (CompareMem(candidate, data, size)) | |
280 | continue; | |
281 | ||
282 | /* at this point, we have duplicate data. */ | |
8bf83b55 GCPL |
283 | if (!first_new_option) { |
284 | first_new_option = DuplicateDevicePath(fulldp); | |
285 | first_new_option_args = arguments; | |
286 | first_new_option_size = StrLen(arguments) * sizeof (CHAR16); | |
287 | } | |
288 | ||
9fcd221e PJ |
289 | *optnum = i; |
290 | FreePool(candidate); | |
291 | FreePool(data); | |
292 | return EFI_SUCCESS; | |
293 | } | |
294 | FreePool(candidate); | |
295 | FreePool(data); | |
296 | return EFI_NOT_FOUND; | |
297 | } | |
298 | ||
299 | EFI_STATUS | |
300 | set_boot_order(void) | |
301 | { | |
302 | CHAR16 *oldbootorder; | |
303 | UINTN size; | |
304 | EFI_GUID global = EFI_GLOBAL_VARIABLE; | |
305 | ||
306 | oldbootorder = LibGetVariableAndSize(L"BootOrder", &global, &size); | |
307 | if (oldbootorder) { | |
308 | nbootorder = size / sizeof (CHAR16); | |
309 | bootorder = oldbootorder; | |
310 | } | |
311 | return EFI_SUCCESS; | |
312 | ||
313 | } | |
314 | ||
eb9f7f1c PJ |
315 | EFI_STATUS |
316 | update_boot_order(void) | |
317 | { | |
eb9f7f1c | 318 | UINTN size; |
ec7eddbf | 319 | UINTN len = 0; |
eb9f7f1c PJ |
320 | EFI_GUID global = EFI_GLOBAL_VARIABLE; |
321 | CHAR16 *newbootorder = NULL; | |
ec7eddbf | 322 | EFI_STATUS rc; |
eb9f7f1c | 323 | |
ec7eddbf GCPL |
324 | size = nbootorder * sizeof(CHAR16); |
325 | newbootorder = AllocateZeroPool(size); | |
326 | if (!newbootorder) | |
327 | return EFI_OUT_OF_RESOURCES; | |
328 | CopyMem(newbootorder, bootorder, size); | |
eb9f7f1c PJ |
329 | |
330 | #ifdef DEBUG_FALLBACK | |
331 | Print(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16)); | |
d3819813 | 332 | UINTN j; |
eb9f7f1c PJ |
333 | for (j = 0 ; j < size / sizeof (CHAR16); j++) |
334 | Print(L"%04x ", newbootorder[j]); | |
335 | Print(L"\n"); | |
336 | #endif | |
ec7eddbf GCPL |
337 | rc = uefi_call_wrapper(RT->GetVariable, 5, L"BootOrder", &global, |
338 | NULL, &len, NULL); | |
339 | if (rc == EFI_BUFFER_TOO_SMALL) | |
eb9f7f1c | 340 | LibDeleteVariable(L"BootOrder", &global); |
eb9f7f1c | 341 | |
eb9f7f1c PJ |
342 | rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &global, |
343 | EFI_VARIABLE_NON_VOLATILE | | |
344 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
345 | EFI_VARIABLE_RUNTIME_ACCESS, | |
346 | size, newbootorder); | |
347 | FreePool(newbootorder); | |
348 | return rc; | |
349 | } | |
350 | ||
351 | EFI_STATUS | |
352 | add_to_boot_list(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) | |
353 | { | |
354 | CHAR16 *fullpath = NULL; | |
355 | UINT64 pathlen = 0; | |
356 | EFI_STATUS rc = EFI_SUCCESS; | |
357 | ||
358 | rc = make_full_path(dirname, filename, &fullpath, &pathlen); | |
359 | if (EFI_ERROR(rc)) | |
360 | return rc; | |
361 | ||
6edc6ec0 PJ |
362 | EFI_DEVICE_PATH *dph = NULL; |
363 | EFI_DEVICE_PATH *file = NULL; | |
364 | EFI_DEVICE_PATH *full_device_path = NULL; | |
365 | EFI_DEVICE_PATH *dp = NULL; | |
eb9f7f1c PJ |
366 | |
367 | dph = DevicePathFromHandle(this_image->DeviceHandle); | |
368 | if (!dph) { | |
369 | rc = EFI_OUT_OF_RESOURCES; | |
370 | goto err; | |
371 | } | |
372 | ||
6edc6ec0 PJ |
373 | file = FileDevicePath(fh, fullpath); |
374 | if (!file) { | |
eb9f7f1c PJ |
375 | rc = EFI_OUT_OF_RESOURCES; |
376 | goto err; | |
377 | } | |
378 | ||
6edc6ec0 PJ |
379 | full_device_path = AppendDevicePath(dph, file); |
380 | if (!full_device_path) { | |
eb9f7f1c PJ |
381 | rc = EFI_OUT_OF_RESOURCES; |
382 | goto err; | |
383 | } | |
384 | ||
6edc6ec0 PJ |
385 | rc = FindSubDevicePath(full_device_path, |
386 | MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP, &dp); | |
387 | if (EFI_ERROR(rc)) { | |
388 | if (rc == EFI_NOT_FOUND) { | |
389 | dp = full_device_path; | |
390 | } else { | |
391 | rc = EFI_OUT_OF_RESOURCES; | |
392 | goto err; | |
393 | } | |
394 | } | |
395 | ||
eb9f7f1c | 396 | #ifdef DEBUG_FALLBACK |
6edc6ec0 | 397 | { |
eb9f7f1c | 398 | UINTN s = DevicePathSize(dp); |
d3819813 | 399 | UINTN i; |
eb9f7f1c PJ |
400 | UINT8 *dpv = (void *)dp; |
401 | for (i = 0; i < s; i++) { | |
402 | if (i > 0 && i % 16 == 0) | |
403 | Print(L"\n"); | |
404 | Print(L"%02x ", dpv[i]); | |
405 | } | |
406 | Print(L"\n"); | |
407 | ||
408 | CHAR16 *dps = DevicePathToStr(dp); | |
409 | Print(L"device path: \"%s\"\n", dps); | |
8807e36a | 410 | } |
6edc6ec0 | 411 | #endif |
eb9f7f1c | 412 | |
9fcd221e | 413 | UINT16 option; |
8bf83b55 | 414 | rc = find_boot_option(dp, full_device_path, fullpath, label, arguments, &option); |
9fcd221e PJ |
415 | if (EFI_ERROR(rc)) { |
416 | add_boot_option(dp, full_device_path, fullpath, label, arguments); | |
417 | } else if (option != 0) { | |
418 | CHAR16 *newbootorder; | |
419 | newbootorder = AllocateZeroPool(sizeof (CHAR16) * nbootorder); | |
420 | if (!newbootorder) | |
421 | return EFI_OUT_OF_RESOURCES; | |
422 | ||
423 | newbootorder[0] = bootorder[option]; | |
424 | CopyMem(newbootorder + 1, bootorder, sizeof (CHAR16) * option); | |
425 | CopyMem(newbootorder + option + 1, bootorder + option + 1, | |
426 | sizeof (CHAR16) * (nbootorder - option - 1)); | |
427 | FreePool(bootorder); | |
428 | bootorder = newbootorder; | |
429 | } | |
eb9f7f1c PJ |
430 | |
431 | err: | |
6edc6ec0 PJ |
432 | if (file) |
433 | FreePool(file); | |
434 | if (full_device_path) | |
435 | FreePool(full_device_path); | |
eb9f7f1c PJ |
436 | if (dp) |
437 | FreePool(dp); | |
438 | if (fullpath) | |
439 | FreePool(fullpath); | |
440 | return rc; | |
441 | } | |
442 | ||
443 | EFI_STATUS | |
444 | populate_stanza(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *csv) | |
445 | { | |
446 | #ifdef DEBUG_FALLBACK | |
447 | Print(L"CSV data: \"%s\"\n", csv); | |
448 | #endif | |
449 | CHAR16 *file = csv; | |
450 | ||
451 | UINTN comma0 = StrCSpn(csv, L","); | |
452 | if (comma0 == 0) | |
453 | return EFI_INVALID_PARAMETER; | |
454 | file[comma0] = L'\0'; | |
455 | #ifdef DEBUG_FALLBACK | |
456 | Print(L"filename: \"%s\"\n", file); | |
457 | #endif | |
458 | ||
459 | CHAR16 *label = csv + comma0 + 1; | |
460 | UINTN comma1 = StrCSpn(label, L","); | |
461 | if (comma1 == 0) | |
462 | return EFI_INVALID_PARAMETER; | |
463 | label[comma1] = L'\0'; | |
464 | #ifdef DEBUG_FALLBACK | |
465 | Print(L"label: \"%s\"\n", label); | |
466 | #endif | |
467 | ||
468 | CHAR16 *arguments = csv + comma0 +1 + comma1 +1; | |
469 | UINTN comma2 = StrCSpn(arguments, L","); | |
470 | arguments[comma2] = L'\0'; | |
471 | /* This one is optional, so don't check if comma2 is 0 */ | |
472 | #ifdef DEBUG_FALLBACK | |
473 | Print(L"arguments: \"%s\"\n", arguments); | |
474 | #endif | |
475 | ||
476 | add_to_boot_list(fh, dirname, file, label, arguments); | |
477 | ||
478 | return EFI_SUCCESS; | |
479 | } | |
480 | ||
481 | EFI_STATUS | |
482 | try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) | |
483 | { | |
484 | CHAR16 *fullpath = NULL; | |
485 | UINT64 pathlen = 0; | |
486 | EFI_STATUS rc; | |
487 | ||
488 | rc = make_full_path(dirname, filename, &fullpath, &pathlen); | |
489 | if (EFI_ERROR(rc)) | |
490 | return rc; | |
491 | ||
492 | #ifdef DEBUG_FALLBACK | |
493 | Print(L"Found file \"%s\"\n", fullpath); | |
494 | #endif | |
495 | ||
496 | CHAR16 *buffer; | |
497 | UINT64 bs; | |
498 | rc = read_file(fh, fullpath, &buffer, &bs); | |
499 | if (EFI_ERROR(rc)) { | |
500 | Print(L"Could not read file \"%s\": %d\n", fullpath, rc); | |
501 | FreePool(fullpath); | |
502 | return rc; | |
503 | } | |
504 | FreePool(fullpath); | |
505 | ||
506 | #ifdef DEBUG_FALLBACK | |
507 | Print(L"File looks like:\n%s\n", buffer); | |
508 | #endif | |
509 | ||
510 | CHAR16 *start = buffer; | |
4f80140b PJ |
511 | /* The file may or may not start with the Unicode byte order marker. |
512 | * Sadness ensues. Since UEFI is defined as LE, I'm going to decree | |
513 | * that these files must also be LE. | |
514 | * | |
515 | * IT IS THUS SO. | |
516 | * | |
517 | * But if we find the LE byte order marker, just skip it. | |
eb9f7f1c PJ |
518 | */ |
519 | if (*start == 0xfeff) | |
520 | start++; | |
521 | while (*start) { | |
404e1263 | 522 | while (*start == L'\r' || *start == L'\n') |
eb9f7f1c | 523 | start++; |
eb9f7f1c PJ |
524 | UINTN l = StrCSpn(start, L"\r\n"); |
525 | if (l == 0) { | |
526 | if (start[l] == L'\0') | |
527 | break; | |
528 | start++; | |
529 | continue; | |
530 | } | |
531 | CHAR16 c = start[l]; | |
532 | start[l] = L'\0'; | |
533 | ||
534 | populate_stanza(fh, dirname, filename, start); | |
535 | ||
536 | start[l] = c; | |
537 | start += l; | |
538 | } | |
539 | ||
540 | FreePool(buffer); | |
541 | return EFI_SUCCESS; | |
542 | } | |
543 | ||
544 | EFI_STATUS | |
545 | find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) | |
546 | { | |
547 | EFI_STATUS rc; | |
548 | void *buffer = NULL; | |
549 | UINTN bs = 0; | |
550 | EFI_GUID finfo = EFI_FILE_INFO_ID; | |
551 | ||
552 | /* The API here is "Call it once with bs=0, it fills in bs, | |
553 | * then allocate a buffer and ask again to get it filled. */ | |
554 | rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL); | |
555 | if (rc == EFI_BUFFER_TOO_SMALL) { | |
556 | buffer = AllocateZeroPool(bs); | |
557 | if (!buffer) { | |
558 | Print(L"Could not allocate memory\n"); | |
559 | return EFI_OUT_OF_RESOURCES; | |
560 | } | |
561 | rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, | |
562 | &bs, buffer); | |
563 | } | |
564 | /* This checks *either* the error from the first GetInfo, if it isn't | |
565 | * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call | |
566 | * in *any* case. */ | |
567 | if (EFI_ERROR(rc)) { | |
568 | Print(L"Could not get info for \"%s\": %d\n", dirname, rc); | |
569 | if (buffer) | |
570 | FreePool(buffer); | |
571 | return rc; | |
572 | } | |
573 | ||
574 | EFI_FILE_INFO *fi = buffer; | |
575 | if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { | |
576 | FreePool(buffer); | |
577 | return EFI_SUCCESS; | |
578 | } | |
579 | FreePool(buffer); | |
dd77b344 | 580 | buffer = NULL; |
eb9f7f1c | 581 | |
62f0afa2 MTL |
582 | CHAR16 *bootcsv=NULL, *bootarchcsv=NULL; |
583 | ||
eb9f7f1c PJ |
584 | bs = 0; |
585 | do { | |
586 | bs = 0; | |
587 | rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, NULL); | |
dd77b344 PJ |
588 | if (EFI_ERROR(rc) && rc != EFI_BUFFER_TOO_SMALL) { |
589 | Print(L"Could not read \\EFI\\%s\\: %d\n", dirname, rc); | |
590 | if (buffer) | |
591 | FreePool(buffer); | |
592 | return rc; | |
593 | } | |
eb9f7f1c | 594 | |
dd77b344 PJ |
595 | buffer = AllocateZeroPool(bs); |
596 | if (!buffer) { | |
597 | Print(L"Could not allocate memory\n"); | |
598 | return EFI_OUT_OF_RESOURCES; | |
eb9f7f1c | 599 | } |
dd77b344 PJ |
600 | |
601 | rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, buffer); | |
eb9f7f1c PJ |
602 | if (EFI_ERROR(rc)) { |
603 | Print(L"Could not read \\EFI\\%s\\: %d\n", dirname, rc); | |
604 | FreePool(buffer); | |
605 | return rc; | |
606 | } | |
dd77b344 | 607 | |
eb9f7f1c PJ |
608 | if (bs == 0) |
609 | break; | |
610 | ||
611 | fi = buffer; | |
612 | ||
62f0afa2 MTL |
613 | if (!bootcsv && !StrCaseCmp(fi->FileName, L"boot.csv")) |
614 | bootcsv = StrDuplicate(fi->FileName); | |
615 | ||
616 | if (!bootarchcsv && | |
617 | !StrCaseCmp(fi->FileName, L"boot" EFI_ARCH L".csv")) | |
618 | bootarchcsv = StrDuplicate(fi->FileName); | |
eb9f7f1c PJ |
619 | |
620 | FreePool(buffer); | |
621 | buffer = NULL; | |
622 | } while (bs != 0); | |
623 | ||
62f0afa2 MTL |
624 | rc = EFI_SUCCESS; |
625 | if (bootarchcsv) { | |
626 | EFI_FILE_HANDLE fh2; | |
627 | rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, | |
628 | bootarchcsv, EFI_FILE_READ_ONLY, 0); | |
629 | if (EFI_ERROR(rc) || fh2 == NULL) { | |
630 | Print(L"Couldn't open \\EFI\\%s\\%s: %d\n", | |
631 | dirname, bootarchcsv, rc); | |
632 | } else { | |
633 | rc = try_boot_csv(fh2, dirname, bootarchcsv); | |
634 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
635 | } | |
636 | } | |
637 | if ((EFI_ERROR(rc) || !bootarchcsv) && bootcsv) { | |
638 | EFI_FILE_HANDLE fh2; | |
639 | rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, | |
640 | bootcsv, EFI_FILE_READ_ONLY, 0); | |
641 | if (EFI_ERROR(rc) || fh2 == NULL) { | |
642 | Print(L"Couldn't open \\EFI\\%s\\%s: %d\n", | |
643 | dirname, bootcsv, rc); | |
644 | } else { | |
645 | rc = try_boot_csv(fh2, dirname, bootcsv); | |
646 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
647 | } | |
648 | } | |
eb9f7f1c | 649 | rc = EFI_SUCCESS; |
eb9f7f1c PJ |
650 | |
651 | return rc; | |
652 | } | |
653 | ||
654 | EFI_STATUS | |
655 | find_boot_options(EFI_HANDLE device) | |
656 | { | |
657 | EFI_STATUS rc = EFI_SUCCESS; | |
658 | ||
659 | EFI_FILE_IO_INTERFACE *fio = NULL; | |
660 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, | |
35b0b55b | 661 | &FileSystemProtocol, (void **)&fio); |
eb9f7f1c PJ |
662 | if (EFI_ERROR(rc)) { |
663 | Print(L"Couldn't find file system: %d\n", rc); | |
664 | return rc; | |
665 | } | |
666 | ||
667 | /* EFI_FILE_HANDLE is a pointer to an EFI_FILE, and I have | |
668 | * *no idea* what frees the memory allocated here. Hopefully | |
669 | * Close() does. */ | |
670 | EFI_FILE_HANDLE fh = NULL; | |
671 | rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &fh); | |
672 | if (EFI_ERROR(rc) || fh == NULL) { | |
673 | Print(L"Couldn't open file system: %d\n", rc); | |
674 | return rc; | |
675 | } | |
676 | ||
677 | EFI_FILE_HANDLE fh2 = NULL; | |
678 | rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, L"EFI", | |
679 | EFI_FILE_READ_ONLY, 0); | |
680 | if (EFI_ERROR(rc) || fh2 == NULL) { | |
681 | Print(L"Couldn't open EFI: %d\n", rc); | |
682 | uefi_call_wrapper(fh->Close, 1, fh); | |
683 | return rc; | |
684 | } | |
685 | rc = uefi_call_wrapper(fh2->SetPosition, 2, fh2, 0); | |
686 | if (EFI_ERROR(rc)) { | |
687 | Print(L"Couldn't set file position: %d\n", rc); | |
688 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
689 | uefi_call_wrapper(fh->Close, 1, fh); | |
690 | return rc; | |
691 | } | |
692 | ||
693 | void *buffer; | |
694 | UINTN bs; | |
695 | do { | |
696 | bs = 0; | |
697 | rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, NULL); | |
698 | if (rc == EFI_BUFFER_TOO_SMALL || | |
699 | (rc == EFI_SUCCESS && bs != 0)) { | |
700 | buffer = AllocateZeroPool(bs); | |
701 | if (!buffer) { | |
702 | Print(L"Could not allocate memory\n"); | |
703 | /* sure, this might work, why not? */ | |
704 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
705 | uefi_call_wrapper(fh->Close, 1, fh); | |
706 | return EFI_OUT_OF_RESOURCES; | |
707 | } | |
708 | ||
709 | rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, buffer); | |
710 | } | |
711 | if (bs == 0) | |
712 | break; | |
713 | ||
714 | if (EFI_ERROR(rc)) { | |
715 | Print(L"Could not read \\EFI\\: %d\n", rc); | |
716 | if (buffer) { | |
717 | FreePool(buffer); | |
718 | buffer = NULL; | |
719 | } | |
720 | uefi_call_wrapper(fh2->Close, 1, fh2); | |
721 | uefi_call_wrapper(fh->Close, 1, fh); | |
722 | return rc; | |
723 | } | |
724 | EFI_FILE_INFO *fi = buffer; | |
725 | ||
726 | if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { | |
727 | FreePool(buffer); | |
728 | buffer = NULL; | |
729 | continue; | |
730 | } | |
731 | if (!StrCmp(fi->FileName, L".") || | |
732 | !StrCmp(fi->FileName, L"..") || | |
733 | !StrCaseCmp(fi->FileName, L"BOOT")) { | |
734 | FreePool(buffer); | |
735 | buffer = NULL; | |
736 | continue; | |
737 | } | |
738 | #ifdef DEBUG_FALLBACK | |
739 | Print(L"Found directory named \"%s\"\n", fi->FileName); | |
740 | #endif | |
741 | ||
742 | EFI_FILE_HANDLE fh3; | |
743 | rc = uefi_call_wrapper(fh->Open, 5, fh2, &fh3, fi->FileName, | |
744 | EFI_FILE_READ_ONLY, 0); | |
745 | if (EFI_ERROR(rc)) { | |
746 | Print(L"%d Couldn't open %s: %d\n", __LINE__, fi->FileName, rc); | |
747 | FreePool(buffer); | |
748 | buffer = NULL; | |
749 | continue; | |
750 | } | |
751 | ||
752 | rc = find_boot_csv(fh3, fi->FileName); | |
753 | FreePool(buffer); | |
754 | buffer = NULL; | |
755 | if (rc == EFI_OUT_OF_RESOURCES) | |
756 | break; | |
757 | ||
758 | } while (1); | |
759 | ||
39baf6df GCPL |
760 | if (rc == EFI_SUCCESS && nbootorder > 0) |
761 | rc = update_boot_order(); | |
762 | ||
eb9f7f1c PJ |
763 | uefi_call_wrapper(fh2->Close, 1, fh2); |
764 | uefi_call_wrapper(fh->Close, 1, fh); | |
39baf6df | 765 | return rc; |
eb9f7f1c | 766 | } |
8807e36a PJ |
767 | |
768 | static EFI_STATUS | |
769 | try_start_first_option(EFI_HANDLE parent_image_handle) | |
770 | { | |
771 | EFI_STATUS rc; | |
772 | EFI_HANDLE image_handle; | |
773 | ||
774 | if (!first_new_option) { | |
775 | return EFI_SUCCESS; | |
776 | } | |
777 | ||
778 | rc = uefi_call_wrapper(BS->LoadImage, 6, 0, parent_image_handle, | |
779 | first_new_option, NULL, 0, | |
780 | &image_handle); | |
781 | if (EFI_ERROR(rc)) { | |
6edc6ec0 PJ |
782 | CHAR16 *dps = DevicePathToStr(first_new_option); |
783 | UINTN s = DevicePathSize(first_new_option); | |
47a9d2c9 | 784 | unsigned int i; |
6edc6ec0 PJ |
785 | UINT8 *dpv = (void *)first_new_option; |
786 | Print(L"LoadImage failed: %d\nDevice path: \"%s\"\n", rc, dps); | |
787 | for (i = 0; i < s; i++) { | |
788 | if (i > 0 && i % 16 == 0) | |
789 | Print(L"\n"); | |
790 | Print(L"%02x ", dpv[i]); | |
791 | } | |
792 | Print(L"\n"); | |
793 | ||
794 | uefi_call_wrapper(BS->Stall, 1, 500000000); | |
8807e36a PJ |
795 | return rc; |
796 | } | |
40cf2a42 PJ |
797 | |
798 | EFI_LOADED_IMAGE *image; | |
799 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &LoadedImageProtocol, (void *)&image); | |
800 | if (!EFI_ERROR(rc)) { | |
801 | image->LoadOptions = first_new_option_args; | |
802 | image->LoadOptionsSize = first_new_option_size; | |
803 | } | |
804 | ||
8807e36a PJ |
805 | rc = uefi_call_wrapper(BS->StartImage, 3, image_handle, NULL, NULL); |
806 | if (EFI_ERROR(rc)) { | |
807 | Print(L"StartImage failed: %d\n", rc); | |
6edc6ec0 | 808 | uefi_call_wrapper(BS->Stall, 1, 500000000); |
8807e36a PJ |
809 | } |
810 | return rc; | |
811 | } | |
812 | ||
d3819813 MTL |
813 | EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; |
814 | extern EFI_STATUS | |
815 | efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab); | |
816 | ||
817 | static void | |
818 | __attribute__((__optimize__("0"))) | |
819 | debug_hook(void) | |
820 | { | |
821 | EFI_GUID guid = SHIM_LOCK_GUID; | |
822 | UINT8 *data = NULL; | |
823 | UINTN dataSize = 0; | |
824 | EFI_STATUS efi_status; | |
825 | volatile register int x = 0; | |
826 | extern char _etext, _edata; | |
827 | ||
828 | efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, guid); | |
829 | if (EFI_ERROR(efi_status)) { | |
830 | return; | |
831 | } | |
832 | ||
833 | if (x) | |
834 | return; | |
835 | ||
836 | x = 1; | |
837 | Print(L"add-symbol-file "DEBUGDIR | |
62f0afa2 | 838 | L"fb" EFI_ARCH L".efi.debug %p -s .data %p\n", &_etext, |
d3819813 MTL |
839 | &_edata); |
840 | } | |
841 | ||
eb9f7f1c PJ |
842 | EFI_STATUS |
843 | efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) | |
844 | { | |
845 | EFI_STATUS rc; | |
846 | ||
847 | InitializeLib(image, systab); | |
848 | ||
d3819813 MTL |
849 | /* |
850 | * if SHIM_DEBUG is set, wait for a debugger to attach. | |
851 | */ | |
852 | debug_hook(); | |
853 | ||
eb9f7f1c PJ |
854 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (void *)&this_image); |
855 | if (EFI_ERROR(rc)) { | |
856 | Print(L"Error: could not find loaded image: %d\n", rc); | |
857 | return rc; | |
858 | } | |
859 | ||
860 | Print(L"System BootOrder not found. Initializing defaults.\n"); | |
861 | ||
9fcd221e PJ |
862 | set_boot_order(); |
863 | ||
eb9f7f1c PJ |
864 | rc = find_boot_options(this_image->DeviceHandle); |
865 | if (EFI_ERROR(rc)) { | |
866 | Print(L"Error: could not find boot options: %d\n", rc); | |
867 | return rc; | |
868 | } | |
869 | ||
8807e36a PJ |
870 | try_start_first_option(image); |
871 | ||
117b1214 | 872 | Print(L"Reset System\n"); |
8807e36a | 873 | uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, |
117b1214 GCPL |
874 | EFI_SUCCESS, 0, NULL); |
875 | ||
eb9f7f1c PJ |
876 | return EFI_SUCCESS; |
877 | } |