]> git.proxmox.com Git - efi-boot-shim.git/blame - fallback.c
Reset the system after restoring the boot entries
[efi-boot-shim.git] / fallback.c
CommitLineData
3ce517fd
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"
14
15EFI_LOADED_IMAGE *this_image = NULL;
16
17static EFI_STATUS
18get_file_size(EFI_FILE_HANDLE fh, UINT64 *retsize)
19{
20 EFI_STATUS rc;
21 void *buffer = NULL;
22 UINTN bs = 0;
23 EFI_GUID finfo = EFI_FILE_INFO_ID;
24
25 /* The API here is "Call it once with bs=0, it fills in bs,
26 * then allocate a buffer and ask again to get it filled. */
27 rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL);
28 if (rc == EFI_BUFFER_TOO_SMALL) {
29 buffer = AllocateZeroPool(bs);
30 if (!buffer) {
31 Print(L"Could not allocate memory\n");
32 return EFI_OUT_OF_RESOURCES;
33 }
34 rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo,
35 &bs, buffer);
36 }
37 /* This checks *either* the error from the first GetInfo, if it isn't
38 * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call
39 * in *any* case. */
40 if (EFI_ERROR(rc)) {
41 Print(L"Could not get file info: %d\n", rc);
42 if (buffer)
43 FreePool(buffer);
44 return rc;
45 }
46 EFI_FILE_INFO *fi = buffer;
47 *retsize = fi->FileSize;
48 FreePool(buffer);
49 return EFI_SUCCESS;
50}
51
52EFI_STATUS
53read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs)
54{
55 EFI_FILE_HANDLE fh2;
56 EFI_STATUS rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, fullpath,
57 EFI_FILE_READ_ONLY, 0);
58 if (EFI_ERROR(rc)) {
59 Print(L"Couldn't open \"%s\": %d\n", fullpath, rc);
60 return rc;
61 }
62
63 UINT64 len = 0;
64 CHAR16 *b = NULL;
65 rc = get_file_size(fh2, &len);
66 if (EFI_ERROR(rc)) {
67 uefi_call_wrapper(fh2->Close, 1, fh2);
68 return rc;
69 }
70
71 b = AllocateZeroPool(len + 2);
72 if (!buffer) {
73 Print(L"Could not allocate memory\n");
74 uefi_call_wrapper(fh2->Close, 1, fh2);
75 return EFI_OUT_OF_RESOURCES;
76 }
77
78 rc = uefi_call_wrapper(fh->Read, 3, fh, &len, b);
79 if (EFI_ERROR(rc)) {
80 FreePool(buffer);
81 uefi_call_wrapper(fh2->Close, 1, fh2);
82 Print(L"Could not read file: %d\n", rc);
83 return rc;
84 }
85 *buffer = b;
86 *bs = len;
87 uefi_call_wrapper(fh2->Close, 1, fh2);
88 return EFI_SUCCESS;
89}
90
91EFI_STATUS
92make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen)
93{
94 UINT64 len;
95
96 len = StrLen(dirname) + StrLen(filename) + StrLen(L"\\EFI\\\\") + 2;
97
4665fcab 98 CHAR16 *fullpath = AllocateZeroPool(len*sizeof(CHAR16));
3ce517fd
PJ
99 if (!fullpath) {
100 Print(L"Could not allocate memory\n");
101 return EFI_OUT_OF_RESOURCES;
102 }
103
104 StrCat(fullpath, L"\\EFI\\");
105 StrCat(fullpath, dirname);
106 StrCat(fullpath, L"\\");
107 StrCat(fullpath, filename);
108
109 *out = fullpath;
110 *outlen = len;
111 return EFI_SUCCESS;
112}
113
114CHAR16 *bootorder = NULL;
115int nbootorder = 0;
116
117EFI_STATUS
118add_boot_option(EFI_DEVICE_PATH *dp, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments)
119{
120 static int i = 0;
121 CHAR16 varname[] = L"Boot0000";
122 CHAR16 hexmap[] = L"0123456789ABCDEF";
123 EFI_GUID global = EFI_GLOBAL_VARIABLE;
124 EFI_STATUS rc;
125
126 for(; i <= 0xffff; i++) {
127 varname[4] = hexmap[(i & 0xf000) >> 12];
128 varname[5] = hexmap[(i & 0x0f00) >> 8];
129 varname[6] = hexmap[(i & 0x00f0) >> 4];
130 varname[7] = hexmap[(i & 0x000f) >> 0];
131
132 void *var = LibGetVariable(varname, &global);
133 if (!var) {
134 int size = sizeof(UINT32) + sizeof (UINT16) +
135 StrLen(label)*2 + 2 + DevicePathSize(dp) +
136 StrLen(arguments) * 2 + 2;
137
138 CHAR8 *data = AllocateZeroPool(size);
139 CHAR8 *cursor = data;
140 *(UINT32 *)cursor = LOAD_OPTION_ACTIVE;
141 cursor += sizeof (UINT32);
142 *(UINT16 *)cursor = DevicePathSize(dp);
143 cursor += sizeof (UINT16);
144 StrCpy((CHAR16 *)cursor, label);
145 cursor += StrLen(label)*2 + 2;
146 CopyMem(cursor, dp, DevicePathSize(dp));
147 cursor += DevicePathSize(dp);
148 StrCpy((CHAR16 *)cursor, arguments);
149
150 Print(L"Creating boot entry \"%s\" with label \"%s\" "
151 L"for file \"%s\"\n",
152 varname, label, filename);
153 rc = uefi_call_wrapper(RT->SetVariable, 5, varname,
154 &global, EFI_VARIABLE_NON_VOLATILE |
155 EFI_VARIABLE_BOOTSERVICE_ACCESS |
156 EFI_VARIABLE_RUNTIME_ACCESS,
157 size, data);
158
159 FreePool(data);
160
161 if (EFI_ERROR(rc)) {
162 Print(L"Could not create variable: %d\n", rc);
163 return rc;
164 }
165
166 CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16)
167 * (nbootorder + 1));
168 if (!newbootorder)
169 return EFI_OUT_OF_RESOURCES;
170
171 int j = 0;
172 if (nbootorder) {
173 for (j = 0; j < nbootorder; j++)
174 newbootorder[j] = bootorder[j];
175 FreePool(bootorder);
176 }
177 newbootorder[j] = i & 0xffff;
178 bootorder = newbootorder;
179 nbootorder += 1;
180#ifdef DEBUG_FALLBACK
181 Print(L"nbootorder: %d\nBootOrder: ", nbootorder);
182 for (j = 0 ; j < nbootorder ; j++)
183 Print(L"%04x ", bootorder[j]);
184 Print(L"\n");
185#endif
186
187 return EFI_SUCCESS;
188 }
189 }
190 return EFI_OUT_OF_RESOURCES;
191}
192
193EFI_STATUS
194update_boot_order(void)
195{
196 CHAR16 *oldbootorder;
197 UINTN size;
198 EFI_GUID global = EFI_GLOBAL_VARIABLE;
199 CHAR16 *newbootorder = NULL;
200
201 oldbootorder = LibGetVariableAndSize(L"BootOrder", &global, &size);
202 if (oldbootorder) {
203 int n = size / sizeof (CHAR16) + nbootorder;
204
205 newbootorder = AllocateZeroPool(n * sizeof (CHAR16));
1a75bb9f
PJ
206 if (!newbootorder)
207 return EFI_OUT_OF_RESOURCES;
3ce517fd
PJ
208 CopyMem(newbootorder, bootorder, nbootorder * sizeof (CHAR16));
209 CopyMem(newbootorder + nbootorder, oldbootorder, size);
210 size = n * sizeof (CHAR16);
211 } else {
212 size = nbootorder * sizeof(CHAR16);
213 newbootorder = AllocateZeroPool(size);
1a75bb9f
PJ
214 if (!newbootorder)
215 return EFI_OUT_OF_RESOURCES;
3ce517fd
PJ
216 CopyMem(newbootorder, bootorder, size);
217 }
3ce517fd
PJ
218
219#ifdef DEBUG_FALLBACK
220 Print(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16));
221 int j;
222 for (j = 0 ; j < size / sizeof (CHAR16); j++)
223 Print(L"%04x ", newbootorder[j]);
224 Print(L"\n");
225#endif
226
227 if (oldbootorder) {
228 LibDeleteVariable(L"BootOrder", &global);
229 FreePool(oldbootorder);
230 }
231
232 EFI_STATUS rc;
233 rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &global,
234 EFI_VARIABLE_NON_VOLATILE |
235 EFI_VARIABLE_BOOTSERVICE_ACCESS |
236 EFI_VARIABLE_RUNTIME_ACCESS,
237 size, newbootorder);
238 FreePool(newbootorder);
239 return rc;
240}
241
242EFI_STATUS
243add_to_boot_list(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments)
244{
245 CHAR16 *fullpath = NULL;
246 UINT64 pathlen = 0;
247 EFI_STATUS rc = EFI_SUCCESS;
248
249 rc = make_full_path(dirname, filename, &fullpath, &pathlen);
250 if (EFI_ERROR(rc))
251 return rc;
252
253 EFI_DEVICE_PATH *dph = NULL, *dpf = NULL, *dp = NULL;
254
255 dph = DevicePathFromHandle(this_image->DeviceHandle);
256 if (!dph) {
257 rc = EFI_OUT_OF_RESOURCES;
258 goto err;
259 }
260
261 dpf = FileDevicePath(fh, fullpath);
262 if (!dpf) {
263 rc = EFI_OUT_OF_RESOURCES;
264 goto err;
265 }
266
267 dp = AppendDevicePath(dph, dpf);
268 if (!dp) {
269 rc = EFI_OUT_OF_RESOURCES;
270 goto err;
271 }
272
273#ifdef DEBUG_FALLBACK
274 UINTN s = DevicePathSize(dp);
275 int i;
276 UINT8 *dpv = (void *)dp;
277 for (i = 0; i < s; i++) {
278 if (i > 0 && i % 16 == 0)
279 Print(L"\n");
280 Print(L"%02x ", dpv[i]);
281 }
282 Print(L"\n");
283
284 CHAR16 *dps = DevicePathToStr(dp);
285 Print(L"device path: \"%s\"\n", dps);
286#endif
287
288 add_boot_option(dp, fullpath, label, arguments);
3ce517fd
PJ
289
290err:
3ce517fd
PJ
291 if (dpf)
292 FreePool(dpf);
293 if (dp)
294 FreePool(dp);
295 if (fullpath)
296 FreePool(fullpath);
297 return rc;
298}
299
300EFI_STATUS
301populate_stanza(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *csv)
302{
303#ifdef DEBUG_FALLBACK
304 Print(L"CSV data: \"%s\"\n", csv);
305#endif
306 CHAR16 *file = csv;
307
308 UINTN comma0 = StrCSpn(csv, L",");
309 if (comma0 == 0)
310 return EFI_INVALID_PARAMETER;
311 file[comma0] = L'\0';
312#ifdef DEBUG_FALLBACK
313 Print(L"filename: \"%s\"\n", file);
314#endif
315
316 CHAR16 *label = csv + comma0 + 1;
317 UINTN comma1 = StrCSpn(label, L",");
318 if (comma1 == 0)
319 return EFI_INVALID_PARAMETER;
320 label[comma1] = L'\0';
321#ifdef DEBUG_FALLBACK
322 Print(L"label: \"%s\"\n", label);
323#endif
324
325 CHAR16 *arguments = csv + comma0 +1 + comma1 +1;
326 UINTN comma2 = StrCSpn(arguments, L",");
327 arguments[comma2] = L'\0';
328 /* This one is optional, so don't check if comma2 is 0 */
329#ifdef DEBUG_FALLBACK
330 Print(L"arguments: \"%s\"\n", arguments);
331#endif
332
333 add_to_boot_list(fh, dirname, file, label, arguments);
334
335 return EFI_SUCCESS;
336}
337
338EFI_STATUS
339try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename)
340{
341 CHAR16 *fullpath = NULL;
342 UINT64 pathlen = 0;
343 EFI_STATUS rc;
344
345 rc = make_full_path(dirname, filename, &fullpath, &pathlen);
346 if (EFI_ERROR(rc))
347 return rc;
348
349#ifdef DEBUG_FALLBACK
350 Print(L"Found file \"%s\"\n", fullpath);
351#endif
352
353 CHAR16 *buffer;
354 UINT64 bs;
355 rc = read_file(fh, fullpath, &buffer, &bs);
356 if (EFI_ERROR(rc)) {
357 Print(L"Could not read file \"%s\": %d\n", fullpath, rc);
358 FreePool(fullpath);
359 return rc;
360 }
361 FreePool(fullpath);
362
363#ifdef DEBUG_FALLBACK
364 Print(L"File looks like:\n%s\n", buffer);
365#endif
366
367 CHAR16 *start = buffer;
368 /* If I create boot.csv with the efi shell's "edit" command,
369 * it starts with 0xfeff. I assume there's some reason for this,
370 * but it doesn't matter much to me...
371 */
372 if (*start == 0xfeff)
373 start++;
374 while (*start) {
82a9c9fd 375 while (*start == L'\r' || *start == L'\n')
3ce517fd 376 start++;
3ce517fd
PJ
377 UINTN l = StrCSpn(start, L"\r\n");
378 if (l == 0) {
379 if (start[l] == L'\0')
380 break;
381 start++;
382 continue;
383 }
384 CHAR16 c = start[l];
385 start[l] = L'\0';
386
387 populate_stanza(fh, dirname, filename, start);
388
389 start[l] = c;
390 start += l;
391 }
392
393 FreePool(buffer);
394 return EFI_SUCCESS;
395}
396
397EFI_STATUS
398find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname)
399{
400 EFI_STATUS rc;
401 void *buffer = NULL;
402 UINTN bs = 0;
403 EFI_GUID finfo = EFI_FILE_INFO_ID;
404
405 /* The API here is "Call it once with bs=0, it fills in bs,
406 * then allocate a buffer and ask again to get it filled. */
407 rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL);
408 if (rc == EFI_BUFFER_TOO_SMALL) {
409 buffer = AllocateZeroPool(bs);
410 if (!buffer) {
411 Print(L"Could not allocate memory\n");
412 return EFI_OUT_OF_RESOURCES;
413 }
414 rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo,
415 &bs, buffer);
416 }
417 /* This checks *either* the error from the first GetInfo, if it isn't
418 * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call
419 * in *any* case. */
420 if (EFI_ERROR(rc)) {
421 Print(L"Could not get info for \"%s\": %d\n", dirname, rc);
422 if (buffer)
423 FreePool(buffer);
424 return rc;
425 }
426
427 EFI_FILE_INFO *fi = buffer;
428 if (!(fi->Attribute & EFI_FILE_DIRECTORY)) {
429 FreePool(buffer);
430 return EFI_SUCCESS;
431 }
432 FreePool(buffer);
433
434 bs = 0;
435 do {
436 bs = 0;
437 rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, NULL);
438 if (rc == EFI_BUFFER_TOO_SMALL) {
439 buffer = AllocateZeroPool(bs);
440 if (!buffer) {
441 Print(L"Could not allocate memory\n");
442 return EFI_OUT_OF_RESOURCES;
443 }
444
445 rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, buffer);
446 }
447 if (EFI_ERROR(rc)) {
448 Print(L"Could not read \\EFI\\%s\\: %d\n", dirname, rc);
449 FreePool(buffer);
450 return rc;
451 }
452 if (bs == 0)
453 break;
454
455 fi = buffer;
456
457 if (!StrCaseCmp(fi->FileName, L"boot.csv")) {
458 EFI_FILE_HANDLE fh2;
459 rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2,
460 fi->FileName,
461 EFI_FILE_READ_ONLY, 0);
462 if (EFI_ERROR(rc) || fh2 == NULL) {
463 Print(L"Couldn't open \\EFI\\%s\\%s: %d\n",
464 dirname, fi->FileName, rc);
465 FreePool(buffer);
466 buffer = NULL;
467 continue;
468 }
469 rc = try_boot_csv(fh2, dirname, fi->FileName);
470 uefi_call_wrapper(fh2->Close, 1, fh2);
471 }
472
473 FreePool(buffer);
474 buffer = NULL;
475 } while (bs != 0);
476
477 rc = EFI_SUCCESS;
478 if (nbootorder > 0)
479 rc = update_boot_order();
480
481 return rc;
482}
483
484EFI_STATUS
485find_boot_options(EFI_HANDLE device)
486{
487 EFI_STATUS rc = EFI_SUCCESS;
488
489 EFI_FILE_IO_INTERFACE *fio = NULL;
490 rc = uefi_call_wrapper(BS->HandleProtocol, 3, device,
491 &FileSystemProtocol, &fio);
492 if (EFI_ERROR(rc)) {
493 Print(L"Couldn't find file system: %d\n", rc);
494 return rc;
495 }
496
497 /* EFI_FILE_HANDLE is a pointer to an EFI_FILE, and I have
498 * *no idea* what frees the memory allocated here. Hopefully
499 * Close() does. */
500 EFI_FILE_HANDLE fh = NULL;
501 rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &fh);
502 if (EFI_ERROR(rc) || fh == NULL) {
503 Print(L"Couldn't open file system: %d\n", rc);
504 return rc;
505 }
506
507 EFI_FILE_HANDLE fh2 = NULL;
508 rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, L"EFI",
509 EFI_FILE_READ_ONLY, 0);
510 if (EFI_ERROR(rc) || fh2 == NULL) {
511 Print(L"Couldn't open EFI: %d\n", rc);
512 uefi_call_wrapper(fh->Close, 1, fh);
513 return rc;
514 }
515 rc = uefi_call_wrapper(fh2->SetPosition, 2, fh2, 0);
516 if (EFI_ERROR(rc)) {
517 Print(L"Couldn't set file position: %d\n", rc);
518 uefi_call_wrapper(fh2->Close, 1, fh2);
519 uefi_call_wrapper(fh->Close, 1, fh);
520 return rc;
521 }
522
523 void *buffer;
524 UINTN bs;
525 do {
526 bs = 0;
527 rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, NULL);
528 if (rc == EFI_BUFFER_TOO_SMALL ||
529 (rc == EFI_SUCCESS && bs != 0)) {
530 buffer = AllocateZeroPool(bs);
531 if (!buffer) {
532 Print(L"Could not allocate memory\n");
533 /* sure, this might work, why not? */
534 uefi_call_wrapper(fh2->Close, 1, fh2);
535 uefi_call_wrapper(fh->Close, 1, fh);
536 return EFI_OUT_OF_RESOURCES;
537 }
538
539 rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, buffer);
540 }
541 if (bs == 0)
542 break;
543
544 if (EFI_ERROR(rc)) {
545 Print(L"Could not read \\EFI\\: %d\n", rc);
546 if (buffer) {
547 FreePool(buffer);
548 buffer = NULL;
549 }
550 uefi_call_wrapper(fh2->Close, 1, fh2);
551 uefi_call_wrapper(fh->Close, 1, fh);
552 return rc;
553 }
554 EFI_FILE_INFO *fi = buffer;
555
556 if (!(fi->Attribute & EFI_FILE_DIRECTORY)) {
557 FreePool(buffer);
558 buffer = NULL;
559 continue;
560 }
561 if (!StrCmp(fi->FileName, L".") ||
562 !StrCmp(fi->FileName, L"..") ||
563 !StrCaseCmp(fi->FileName, L"BOOT")) {
564 FreePool(buffer);
565 buffer = NULL;
566 continue;
567 }
568#ifdef DEBUG_FALLBACK
569 Print(L"Found directory named \"%s\"\n", fi->FileName);
570#endif
571
572 EFI_FILE_HANDLE fh3;
573 rc = uefi_call_wrapper(fh->Open, 5, fh2, &fh3, fi->FileName,
574 EFI_FILE_READ_ONLY, 0);
575 if (EFI_ERROR(rc)) {
576 Print(L"%d Couldn't open %s: %d\n", __LINE__, fi->FileName, rc);
577 FreePool(buffer);
578 buffer = NULL;
579 continue;
580 }
581
582 rc = find_boot_csv(fh3, fi->FileName);
583 FreePool(buffer);
584 buffer = NULL;
585 if (rc == EFI_OUT_OF_RESOURCES)
586 break;
587
588 } while (1);
589
590 uefi_call_wrapper(fh2->Close, 1, fh2);
591 uefi_call_wrapper(fh->Close, 1, fh);
592 return EFI_SUCCESS;
593}
594EFI_STATUS
595efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
596{
597 EFI_STATUS rc;
598
599 InitializeLib(image, systab);
600
601 rc = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (void *)&this_image);
602 if (EFI_ERROR(rc)) {
603 Print(L"Error: could not find loaded image: %d\n", rc);
604 return rc;
605 }
606
607 Print(L"System BootOrder not found. Initializing defaults.\n");
608
609 rc = find_boot_options(this_image->DeviceHandle);
610 if (EFI_ERROR(rc)) {
611 Print(L"Error: could not find boot options: %d\n", rc);
612 return rc;
613 }
614
f7fbcdce
GCPL
615 Print(L"Reset System\n");
616 uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
617 EFI_SUCCESS, 0, NULL);
618
3ce517fd
PJ
619 return EFI_SUCCESS;
620}