2 * Copyright 2012-2013 Red Hat, Inc.
5 * See "COPYING" for license terms.
7 * Author(s): Peter Jones <pjones@redhat.com>
15 EFI_LOADED_IMAGE
*this_image
= NULL
;
18 FindSubDevicePath(EFI_DEVICE_PATH
*In
, UINT8 Type
, UINT8 SubType
,
19 EFI_DEVICE_PATH
**Out
)
21 EFI_DEVICE_PATH
*dp
= In
;
23 return EFI_INVALID_PARAMETER
;
25 for (dp
= In
; !IsDevicePathEnd(dp
); dp
= NextDevicePathNode(dp
)) {
26 if (DevicePathType(dp
) == Type
&&
27 DevicePathSubType(dp
) == SubType
) {
28 *Out
= DuplicateDevicePath(dp
);
30 return EFI_OUT_OF_RESOURCES
;
39 get_file_size(EFI_FILE_HANDLE fh
, UINTN
*retsize
)
44 EFI_GUID finfo
= EFI_FILE_INFO_ID
;
46 /* The API here is "Call it once with bs=0, it fills in bs,
47 * then allocate a buffer and ask again to get it filled. */
48 rc
= uefi_call_wrapper(fh
->GetInfo
, 4, fh
, &finfo
, &bs
, NULL
);
49 if (rc
== EFI_BUFFER_TOO_SMALL
) {
50 buffer
= AllocateZeroPool(bs
);
52 Print(L
"Could not allocate memory\n");
53 return EFI_OUT_OF_RESOURCES
;
55 rc
= uefi_call_wrapper(fh
->GetInfo
, 4, fh
, &finfo
,
58 /* This checks *either* the error from the first GetInfo, if it isn't
59 * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call
62 Print(L
"Could not get file info: %d\n", rc
);
67 EFI_FILE_INFO
*fi
= buffer
;
68 *retsize
= fi
->FileSize
;
74 read_file(EFI_FILE_HANDLE fh
, CHAR16
*fullpath
, CHAR16
**buffer
, UINT64
*bs
)
77 EFI_STATUS rc
= uefi_call_wrapper(fh
->Open
, 5, fh
, &fh2
, fullpath
,
78 EFI_FILE_READ_ONLY
, 0);
80 Print(L
"Couldn't open \"%s\": %d\n", fullpath
, rc
);
86 rc
= get_file_size(fh2
, &len
);
88 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
92 b
= AllocateZeroPool(len
+ 2);
94 Print(L
"Could not allocate memory\n");
95 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
96 return EFI_OUT_OF_RESOURCES
;
99 rc
= uefi_call_wrapper(fh
->Read
, 3, fh
, &len
, b
);
102 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
103 Print(L
"Could not read file: %d\n", rc
);
108 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
113 make_full_path(CHAR16
*dirname
, CHAR16
*filename
, CHAR16
**out
, UINT64
*outlen
)
117 len
= StrLen(L
"\\EFI\\") + StrLen(dirname
)
118 + StrLen(L
"\\") + StrLen(filename
)
121 CHAR16
*fullpath
= AllocateZeroPool(len
*sizeof(CHAR16
));
123 Print(L
"Could not allocate memory\n");
124 return EFI_OUT_OF_RESOURCES
;
127 StrCat(fullpath
, L
"\\EFI\\");
128 StrCat(fullpath
, dirname
);
129 StrCat(fullpath
, L
"\\");
130 StrCat(fullpath
, filename
);
137 CHAR16
*bootorder
= NULL
;
140 EFI_DEVICE_PATH
*first_new_option
= NULL
;
141 VOID
*first_new_option_args
= NULL
;
142 UINTN first_new_option_size
= 0;
145 add_boot_option(EFI_DEVICE_PATH
*hddp
, EFI_DEVICE_PATH
*fulldp
,
146 CHAR16
*filename
, CHAR16
*label
, CHAR16
*arguments
)
149 CHAR16 varname
[] = L
"Boot0000";
150 CHAR16 hexmap
[] = L
"0123456789ABCDEF";
151 EFI_GUID global
= EFI_GLOBAL_VARIABLE
;
154 for(; i
<= 0xffff; i
++) {
155 varname
[4] = hexmap
[(i
& 0xf000) >> 12];
156 varname
[5] = hexmap
[(i
& 0x0f00) >> 8];
157 varname
[6] = hexmap
[(i
& 0x00f0) >> 4];
158 varname
[7] = hexmap
[(i
& 0x000f) >> 0];
160 void *var
= LibGetVariable(varname
, &global
);
162 int size
= sizeof(UINT32
) + sizeof (UINT16
) +
163 StrLen(label
)*2 + 2 + DevicePathSize(hddp
) +
164 StrLen(arguments
) * 2;
166 CHAR8
*data
= AllocateZeroPool(size
);
167 CHAR8
*cursor
= data
;
168 *(UINT32
*)cursor
= LOAD_OPTION_ACTIVE
;
169 cursor
+= sizeof (UINT32
);
170 *(UINT16
*)cursor
= DevicePathSize(hddp
);
171 cursor
+= sizeof (UINT16
);
172 StrCpy((CHAR16
*)cursor
, label
);
173 cursor
+= StrLen(label
)*2 + 2;
174 CopyMem(cursor
, hddp
, DevicePathSize(hddp
));
175 cursor
+= DevicePathSize(hddp
);
176 StrCpy((CHAR16
*)cursor
, arguments
);
178 Print(L
"Creating boot entry \"%s\" with label \"%s\" "
179 L
"for file \"%s\"\n",
180 varname
, label
, filename
);
182 if (!first_new_option
) {
183 first_new_option
= DuplicateDevicePath(fulldp
);
184 first_new_option_args
= arguments
;
185 first_new_option_size
= StrLen(arguments
) * sizeof (CHAR16
);
188 rc
= uefi_call_wrapper(RT
->SetVariable
, 5, varname
,
189 &global
, EFI_VARIABLE_NON_VOLATILE
|
190 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
191 EFI_VARIABLE_RUNTIME_ACCESS
,
197 Print(L
"Could not create variable: %d\n", rc
);
201 CHAR16
*newbootorder
= AllocateZeroPool(sizeof (CHAR16
)
204 return EFI_OUT_OF_RESOURCES
;
207 newbootorder
[0] = i
& 0xffff;
209 for (j
= 0; j
< nbootorder
; j
++)
210 newbootorder
[j
+1] = bootorder
[j
];
213 bootorder
= newbootorder
;
215 #ifdef DEBUG_FALLBACK
216 Print(L
"nbootorder: %d\nBootOrder: ", nbootorder
);
217 for (j
= 0 ; j
< nbootorder
; j
++)
218 Print(L
"%04x ", bootorder
[j
]);
225 return EFI_OUT_OF_RESOURCES
;
229 find_boot_option(EFI_DEVICE_PATH
*dp
, EFI_DEVICE_PATH
*fulldp
,
230 CHAR16
*filename
, CHAR16
*label
, CHAR16
*arguments
,
233 unsigned int size
= sizeof(UINT32
) + sizeof (UINT16
) +
234 StrLen(label
)*2 + 2 + DevicePathSize(dp
) +
235 StrLen(arguments
) * 2;
237 CHAR8
*data
= AllocateZeroPool(size
);
239 return EFI_OUT_OF_RESOURCES
;
240 CHAR8
*cursor
= data
;
241 *(UINT32
*)cursor
= LOAD_OPTION_ACTIVE
;
242 cursor
+= sizeof (UINT32
);
243 *(UINT16
*)cursor
= DevicePathSize(dp
);
244 cursor
+= sizeof (UINT16
);
245 StrCpy((CHAR16
*)cursor
, label
);
246 cursor
+= StrLen(label
)*2 + 2;
247 CopyMem(cursor
, dp
, DevicePathSize(dp
));
248 cursor
+= DevicePathSize(dp
);
249 StrCpy((CHAR16
*)cursor
, arguments
);
252 CHAR16 varname
[] = L
"Boot0000";
253 CHAR16 hexmap
[] = L
"0123456789ABCDEF";
254 EFI_GUID global
= EFI_GLOBAL_VARIABLE
;
257 CHAR8
*candidate
= AllocateZeroPool(size
);
260 return EFI_OUT_OF_RESOURCES
;
263 for(i
= 0; i
< nbootorder
&& i
< 0x10000; i
++) {
264 varname
[4] = hexmap
[(bootorder
[i
] & 0xf000) >> 12];
265 varname
[5] = hexmap
[(bootorder
[i
] & 0x0f00) >> 8];
266 varname
[6] = hexmap
[(bootorder
[i
] & 0x00f0) >> 4];
267 varname
[7] = hexmap
[(bootorder
[i
] & 0x000f) >> 0];
269 UINTN candidate_size
= size
;
270 rc
= uefi_call_wrapper(RT
->GetVariable
, 5, varname
, &global
,
271 NULL
, &candidate_size
, candidate
);
275 if (candidate_size
!= size
)
278 if (CompareMem(candidate
, data
, size
))
281 /* at this point, we have duplicate data. */
282 if (!first_new_option
) {
283 first_new_option
= DuplicateDevicePath(fulldp
);
284 first_new_option_args
= arguments
;
285 first_new_option_size
= StrLen(arguments
) * sizeof (CHAR16
);
295 return EFI_NOT_FOUND
;
301 CHAR16
*oldbootorder
;
303 EFI_GUID global
= EFI_GLOBAL_VARIABLE
;
305 oldbootorder
= LibGetVariableAndSize(L
"BootOrder", &global
, &size
);
307 nbootorder
= size
/ sizeof (CHAR16
);
308 bootorder
= oldbootorder
;
315 update_boot_order(void)
319 EFI_GUID global
= EFI_GLOBAL_VARIABLE
;
320 CHAR16
*newbootorder
= NULL
;
323 size
= nbootorder
* sizeof(CHAR16
);
324 newbootorder
= AllocateZeroPool(size
);
326 return EFI_OUT_OF_RESOURCES
;
327 CopyMem(newbootorder
, bootorder
, size
);
329 #ifdef DEBUG_FALLBACK
330 Print(L
"nbootorder: %d\nBootOrder: ", size
/ sizeof (CHAR16
));
332 for (j
= 0 ; j
< size
/ sizeof (CHAR16
); j
++)
333 Print(L
"%04x ", newbootorder
[j
]);
336 rc
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"BootOrder", &global
,
338 if (rc
== EFI_BUFFER_TOO_SMALL
)
339 LibDeleteVariable(L
"BootOrder", &global
);
341 rc
= uefi_call_wrapper(RT
->SetVariable
, 5, L
"BootOrder", &global
,
342 EFI_VARIABLE_NON_VOLATILE
|
343 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
344 EFI_VARIABLE_RUNTIME_ACCESS
,
346 FreePool(newbootorder
);
351 add_to_boot_list(EFI_FILE_HANDLE fh
, CHAR16
*dirname
, CHAR16
*filename
, CHAR16
*label
, CHAR16
*arguments
)
353 CHAR16
*fullpath
= NULL
;
355 EFI_STATUS rc
= EFI_SUCCESS
;
357 rc
= make_full_path(dirname
, filename
, &fullpath
, &pathlen
);
361 EFI_DEVICE_PATH
*dph
= NULL
;
362 EFI_DEVICE_PATH
*file
= NULL
;
363 EFI_DEVICE_PATH
*full_device_path
= NULL
;
364 EFI_DEVICE_PATH
*dp
= NULL
;
366 dph
= DevicePathFromHandle(this_image
->DeviceHandle
);
368 rc
= EFI_OUT_OF_RESOURCES
;
372 file
= FileDevicePath(fh
, fullpath
);
374 rc
= EFI_OUT_OF_RESOURCES
;
378 full_device_path
= AppendDevicePath(dph
, file
);
379 if (!full_device_path
) {
380 rc
= EFI_OUT_OF_RESOURCES
;
384 rc
= FindSubDevicePath(full_device_path
,
385 MEDIA_DEVICE_PATH
, MEDIA_HARDDRIVE_DP
, &dp
);
387 if (rc
== EFI_NOT_FOUND
) {
388 dp
= full_device_path
;
390 rc
= EFI_OUT_OF_RESOURCES
;
395 #ifdef DEBUG_FALLBACK
397 UINTN s
= DevicePathSize(dp
);
399 UINT8
*dpv
= (void *)dp
;
400 for (i
= 0; i
< s
; i
++) {
401 if (i
> 0 && i
% 16 == 0)
403 Print(L
"%02x ", dpv
[i
]);
407 CHAR16
*dps
= DevicePathToStr(dp
);
408 Print(L
"device path: \"%s\"\n", dps
);
413 rc
= find_boot_option(dp
, full_device_path
, fullpath
, label
, arguments
, &option
);
415 add_boot_option(dp
, full_device_path
, fullpath
, label
, arguments
);
416 } else if (option
!= 0) {
417 CHAR16
*newbootorder
;
418 newbootorder
= AllocateZeroPool(sizeof (CHAR16
) * nbootorder
);
420 return EFI_OUT_OF_RESOURCES
;
422 newbootorder
[0] = bootorder
[option
];
423 CopyMem(newbootorder
+ 1, bootorder
, sizeof (CHAR16
) * option
);
424 CopyMem(newbootorder
+ option
+ 1, bootorder
+ option
+ 1,
425 sizeof (CHAR16
) * (nbootorder
- option
- 1));
427 bootorder
= newbootorder
;
433 if (full_device_path
)
434 FreePool(full_device_path
);
443 populate_stanza(EFI_FILE_HANDLE fh
, CHAR16
*dirname
, CHAR16
*filename
, CHAR16
*csv
)
445 #ifdef DEBUG_FALLBACK
446 Print(L
"CSV data: \"%s\"\n", csv
);
450 UINTN comma0
= StrCSpn(csv
, L
",");
452 return EFI_INVALID_PARAMETER
;
453 file
[comma0
] = L
'\0';
454 #ifdef DEBUG_FALLBACK
455 Print(L
"filename: \"%s\"\n", file
);
458 CHAR16
*label
= csv
+ comma0
+ 1;
459 UINTN comma1
= StrCSpn(label
, L
",");
461 return EFI_INVALID_PARAMETER
;
462 label
[comma1
] = L
'\0';
463 #ifdef DEBUG_FALLBACK
464 Print(L
"label: \"%s\"\n", label
);
467 CHAR16
*arguments
= csv
+ comma0
+1 + comma1
+1;
468 UINTN comma2
= StrCSpn(arguments
, L
",");
469 arguments
[comma2
] = L
'\0';
470 /* This one is optional, so don't check if comma2 is 0 */
471 #ifdef DEBUG_FALLBACK
472 Print(L
"arguments: \"%s\"\n", arguments
);
475 add_to_boot_list(fh
, dirname
, file
, label
, arguments
);
481 try_boot_csv(EFI_FILE_HANDLE fh
, CHAR16
*dirname
, CHAR16
*filename
)
483 CHAR16
*fullpath
= NULL
;
487 rc
= make_full_path(dirname
, filename
, &fullpath
, &pathlen
);
491 #ifdef DEBUG_FALLBACK
492 Print(L
"Found file \"%s\"\n", fullpath
);
497 rc
= read_file(fh
, fullpath
, &buffer
, &bs
);
499 Print(L
"Could not read file \"%s\": %d\n", fullpath
, rc
);
505 #ifdef DEBUG_FALLBACK
506 Print(L
"File looks like:\n%s\n", buffer
);
509 CHAR16
*start
= buffer
;
510 /* The file may or may not start with the Unicode byte order marker.
511 * Sadness ensues. Since UEFI is defined as LE, I'm going to decree
512 * that these files must also be LE.
516 * But if we find the LE byte order marker, just skip it.
518 if (*start
== 0xfeff)
521 while (*start
== L
'\r' || *start
== L
'\n')
523 UINTN l
= StrCSpn(start
, L
"\r\n");
525 if (start
[l
] == L
'\0')
533 populate_stanza(fh
, dirname
, filename
, start
);
544 find_boot_csv(EFI_FILE_HANDLE fh
, CHAR16
*dirname
)
549 EFI_GUID finfo
= EFI_FILE_INFO_ID
;
551 /* The API here is "Call it once with bs=0, it fills in bs,
552 * then allocate a buffer and ask again to get it filled. */
553 rc
= uefi_call_wrapper(fh
->GetInfo
, 4, fh
, &finfo
, &bs
, NULL
);
554 if (rc
== EFI_BUFFER_TOO_SMALL
) {
555 buffer
= AllocateZeroPool(bs
);
557 Print(L
"Could not allocate memory\n");
558 return EFI_OUT_OF_RESOURCES
;
560 rc
= uefi_call_wrapper(fh
->GetInfo
, 4, fh
, &finfo
,
563 /* This checks *either* the error from the first GetInfo, if it isn't
564 * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call
567 Print(L
"Could not get info for \"%s\": %d\n", dirname
, rc
);
573 EFI_FILE_INFO
*fi
= buffer
;
574 if (!(fi
->Attribute
& EFI_FILE_DIRECTORY
)) {
584 rc
= uefi_call_wrapper(fh
->Read
, 3, fh
, &bs
, NULL
);
585 if (EFI_ERROR(rc
) && rc
!= EFI_BUFFER_TOO_SMALL
) {
586 Print(L
"Could not read \\EFI\\%s\\: %d\n", dirname
, rc
);
592 buffer
= AllocateZeroPool(bs
);
594 Print(L
"Could not allocate memory\n");
595 return EFI_OUT_OF_RESOURCES
;
598 rc
= uefi_call_wrapper(fh
->Read
, 3, fh
, &bs
, buffer
);
600 Print(L
"Could not read \\EFI\\%s\\: %d\n", dirname
, rc
);
610 if (!StrCaseCmp(fi
->FileName
, L
"boot.csv")) {
612 rc
= uefi_call_wrapper(fh
->Open
, 5, fh
, &fh2
,
614 EFI_FILE_READ_ONLY
, 0);
615 if (EFI_ERROR(rc
) || fh2
== NULL
) {
616 Print(L
"Couldn't open \\EFI\\%s\\%s: %d\n",
617 dirname
, fi
->FileName
, rc
);
622 rc
= try_boot_csv(fh2
, dirname
, fi
->FileName
);
623 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
636 find_boot_options(EFI_HANDLE device
)
638 EFI_STATUS rc
= EFI_SUCCESS
;
640 EFI_FILE_IO_INTERFACE
*fio
= NULL
;
641 rc
= uefi_call_wrapper(BS
->HandleProtocol
, 3, device
,
642 &FileSystemProtocol
, (void **)&fio
);
644 Print(L
"Couldn't find file system: %d\n", rc
);
648 /* EFI_FILE_HANDLE is a pointer to an EFI_FILE, and I have
649 * *no idea* what frees the memory allocated here. Hopefully
651 EFI_FILE_HANDLE fh
= NULL
;
652 rc
= uefi_call_wrapper(fio
->OpenVolume
, 2, fio
, &fh
);
653 if (EFI_ERROR(rc
) || fh
== NULL
) {
654 Print(L
"Couldn't open file system: %d\n", rc
);
658 EFI_FILE_HANDLE fh2
= NULL
;
659 rc
= uefi_call_wrapper(fh
->Open
, 5, fh
, &fh2
, L
"EFI",
660 EFI_FILE_READ_ONLY
, 0);
661 if (EFI_ERROR(rc
) || fh2
== NULL
) {
662 Print(L
"Couldn't open EFI: %d\n", rc
);
663 uefi_call_wrapper(fh
->Close
, 1, fh
);
666 rc
= uefi_call_wrapper(fh2
->SetPosition
, 2, fh2
, 0);
668 Print(L
"Couldn't set file position: %d\n", rc
);
669 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
670 uefi_call_wrapper(fh
->Close
, 1, fh
);
678 rc
= uefi_call_wrapper(fh2
->Read
, 3, fh2
, &bs
, NULL
);
679 if (rc
== EFI_BUFFER_TOO_SMALL
||
680 (rc
== EFI_SUCCESS
&& bs
!= 0)) {
681 buffer
= AllocateZeroPool(bs
);
683 Print(L
"Could not allocate memory\n");
684 /* sure, this might work, why not? */
685 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
686 uefi_call_wrapper(fh
->Close
, 1, fh
);
687 return EFI_OUT_OF_RESOURCES
;
690 rc
= uefi_call_wrapper(fh2
->Read
, 3, fh2
, &bs
, buffer
);
696 Print(L
"Could not read \\EFI\\: %d\n", rc
);
701 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
702 uefi_call_wrapper(fh
->Close
, 1, fh
);
705 EFI_FILE_INFO
*fi
= buffer
;
707 if (!(fi
->Attribute
& EFI_FILE_DIRECTORY
)) {
712 if (!StrCmp(fi
->FileName
, L
".") ||
713 !StrCmp(fi
->FileName
, L
"..") ||
714 !StrCaseCmp(fi
->FileName
, L
"BOOT")) {
719 #ifdef DEBUG_FALLBACK
720 Print(L
"Found directory named \"%s\"\n", fi
->FileName
);
724 rc
= uefi_call_wrapper(fh
->Open
, 5, fh2
, &fh3
, fi
->FileName
,
725 EFI_FILE_READ_ONLY
, 0);
727 Print(L
"%d Couldn't open %s: %d\n", __LINE__
, fi
->FileName
, rc
);
733 rc
= find_boot_csv(fh3
, fi
->FileName
);
736 if (rc
== EFI_OUT_OF_RESOURCES
)
741 if (rc
== EFI_SUCCESS
&& nbootorder
> 0)
742 rc
= update_boot_order();
744 uefi_call_wrapper(fh2
->Close
, 1, fh2
);
745 uefi_call_wrapper(fh
->Close
, 1, fh
);
750 try_start_first_option(EFI_HANDLE parent_image_handle
)
753 EFI_HANDLE image_handle
;
755 if (!first_new_option
) {
759 rc
= uefi_call_wrapper(BS
->LoadImage
, 6, 0, parent_image_handle
,
760 first_new_option
, NULL
, 0,
763 CHAR16
*dps
= DevicePathToStr(first_new_option
);
764 UINTN s
= DevicePathSize(first_new_option
);
766 UINT8
*dpv
= (void *)first_new_option
;
767 Print(L
"LoadImage failed: %d\nDevice path: \"%s\"\n", rc
, dps
);
768 for (i
= 0; i
< s
; i
++) {
769 if (i
> 0 && i
% 16 == 0)
771 Print(L
"%02x ", dpv
[i
]);
775 uefi_call_wrapper(BS
->Stall
, 1, 500000000);
779 EFI_LOADED_IMAGE
*image
;
780 rc
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image_handle
, &LoadedImageProtocol
, (void *)&image
);
781 if (!EFI_ERROR(rc
)) {
782 image
->LoadOptions
= first_new_option_args
;
783 image
->LoadOptionsSize
= first_new_option_size
;
786 rc
= uefi_call_wrapper(BS
->StartImage
, 3, image_handle
, NULL
, NULL
);
788 Print(L
"StartImage failed: %d\n", rc
);
789 uefi_call_wrapper(BS
->Stall
, 1, 500000000);
795 efi_main(EFI_HANDLE image
, EFI_SYSTEM_TABLE
*systab
)
799 InitializeLib(image
, systab
);
801 rc
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image
, &LoadedImageProtocol
, (void *)&this_image
);
803 Print(L
"Error: could not find loaded image: %d\n", rc
);
807 Print(L
"System BootOrder not found. Initializing defaults.\n");
811 rc
= find_boot_options(this_image
->DeviceHandle
);
813 Print(L
"Error: could not find boot options: %d\n", rc
);
817 try_start_first_option(image
);
819 Print(L
"Reset System\n");
820 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetCold
,
821 EFI_SUCCESS
, 0, NULL
);