]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Library/GenericBdsLib/BdsBoot.c
Move BdsDxe and GenericBdsLib to IntelFrameworkModulePkg, these modules need dependen...
[mirror_edk2.git] / IntelFrameworkModulePkg / Library / GenericBdsLib / BdsBoot.c
1 /** @file
2 BDS Lib functions which relate with create or process the boot option.
3
4 Copyright (c) 2004 - 2008, Intel Corporation. <BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "InternalBdsLib.h"
16
17 BOOLEAN mEnumBootDevice = FALSE;
18
19 ///
20 /// This GUID is used for an EFI Variable that stores the front device pathes
21 /// for a partial device path that starts with the HD node.
22 ///
23 EFI_GUID mHdBootVariablePrivateGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x8, 0xe2, 0xe, 0x90, 0x6c, 0xb6, 0xde } };
24
25
26
27 /**
28 Boot the legacy system with the boot option
29
30 @param Option The legacy boot option which have BBS device path
31
32 @retval EFI_UNSUPPORTED There is no legacybios protocol, do not support
33 legacy boot.
34 @retval EFI_STATUS Return the status of LegacyBios->LegacyBoot ().
35
36 **/
37 EFI_STATUS
38 BdsLibDoLegacyBoot (
39 IN BDS_COMMON_OPTION *Option
40 )
41 {
42 EFI_STATUS Status;
43 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
44
45 Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios);
46 if (EFI_ERROR (Status)) {
47 //
48 // If no LegacyBios protocol we do not support legacy boot
49 //
50 return EFI_UNSUPPORTED;
51 }
52 //
53 // Notes: if we separate the int 19, then we don't need to refresh BBS
54 //
55 BdsRefreshBbsTableForBoot (Option);
56
57 //
58 // Write boot to OS performance data to a file
59 //
60 PERF_CODE (
61 WriteBootToOsPerformanceData ();
62 );
63
64 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Legacy Boot: %S\n", Option->Description));
65 return LegacyBios->LegacyBoot (
66 LegacyBios,
67 (BBS_BBS_DEVICE_PATH *) Option->DevicePath,
68 Option->LoadOptionsSize,
69 Option->LoadOptions
70 );
71 }
72
73
74 /**
75 Process the boot option follow the UEFI specification and
76 special treat the legacy boot option with BBS_DEVICE_PATH.
77
78 @param Option The boot option need to be processed
79 @param DevicePath The device path which describe where to load the
80 boot image or the legacy BBS device path to boot
81 the legacy OS
82 @param ExitDataSize The size of exit data.
83 @param ExitData Data returned when Boot image failed.
84
85 @retval EFI_SUCCESS Boot from the input boot option successfully.
86 @retval EFI_NOT_FOUND If the Device Path is not found in the system
87
88 **/
89 EFI_STATUS
90 EFIAPI
91 BdsLibBootViaBootOption (
92 IN BDS_COMMON_OPTION *Option,
93 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
94 OUT UINTN *ExitDataSize,
95 OUT CHAR16 **ExitData OPTIONAL
96 )
97 {
98 EFI_STATUS Status;
99 EFI_HANDLE Handle;
100 EFI_HANDLE ImageHandle;
101 EFI_DEVICE_PATH_PROTOCOL *FilePath;
102 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
103 EFI_DEVICE_PATH_PROTOCOL *WorkingDevicePath;
104 EFI_ACPI_S3_SAVE_PROTOCOL *AcpiS3Save;
105 LIST_ENTRY TempBootLists;
106
107 //
108 // Record the performance data for End of BDS
109 //
110 PERF_END (0, BDS_TOK, NULL, 0);
111
112 *ExitDataSize = 0;
113 *ExitData = NULL;
114
115 //
116 // Notes: put EFI64 ROM Shadow Solution
117 //
118 EFI64_SHADOW_ALL_LEGACY_ROM ();
119
120 //
121 // Notes: this code can be remove after the s3 script table
122 // hook on the event EVT_SIGNAL_READY_TO_BOOT or
123 // EVT_SIGNAL_LEGACY_BOOT
124 //
125 Status = gBS->LocateProtocol (&gEfiAcpiS3SaveProtocolGuid, NULL, (VOID **) &AcpiS3Save);
126 if (!EFI_ERROR (Status)) {
127 AcpiS3Save->S3Save (AcpiS3Save, NULL);
128 }
129 //
130 // If it's Device Path that starts with a hard drive path, append it with the front part to compose a
131 // full device path
132 //
133 WorkingDevicePath = NULL;
134 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
135 (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)) {
136 WorkingDevicePath = BdsExpandPartitionPartialDevicePathToFull (
137 (HARDDRIVE_DEVICE_PATH *)DevicePath
138 );
139 if (WorkingDevicePath != NULL) {
140 DevicePath = WorkingDevicePath;
141 }
142 }
143 //
144 // Signal the EVT_SIGNAL_READY_TO_BOOT event
145 //
146 EfiSignalEventReadyToBoot();
147
148
149 //
150 // Set Boot Current
151 //
152 gRT->SetVariable (
153 L"BootCurrent",
154 &gEfiGlobalVariableGuid,
155 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
156 sizeof (UINT16),
157 &Option->BootCurrent
158 );
159
160 ASSERT (Option->DevicePath != NULL);
161 if ((DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&
162 (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)
163 ) {
164 //
165 // Check to see if we should legacy BOOT. If yes then do the legacy boot
166 //
167 return BdsLibDoLegacyBoot (Option);
168 }
169
170 //
171 // If the boot option point to Internal FV shell, make sure it is valid
172 //
173 Status = BdsLibUpdateFvFileDevicePath (&DevicePath, &gEfiShellFileGuid);
174 if (!EFI_ERROR(Status)) {
175 if (Option->DevicePath != NULL) {
176 FreePool(Option->DevicePath);
177 }
178 Option->DevicePath = AllocateZeroPool (GetDevicePathSize (DevicePath));
179 ASSERT(Option->DevicePath != NULL);
180 CopyMem (Option->DevicePath, DevicePath, GetDevicePathSize (DevicePath));
181 //
182 // Update the shell boot option
183 //
184 InitializeListHead (&TempBootLists);
185 BdsLibRegisterNewOption (&TempBootLists, DevicePath, L"EFI Internal Shell", L"BootOrder");
186
187 //
188 // free the temporary device path created by BdsLibUpdateFvFileDevicePath()
189 //
190 FreePool (DevicePath);
191 DevicePath = Option->DevicePath;
192 }
193
194 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Booting %S\n", Option->Description));
195
196 Status = gBS->LoadImage (
197 TRUE,
198 mBdsImageHandle,
199 DevicePath,
200 NULL,
201 0,
202 &ImageHandle
203 );
204
205 //
206 // If we didn't find an image directly, we need to try as if it is a removable device boot opotion
207 // and load the image according to the default boot behavior for removable device.
208 //
209 if (EFI_ERROR (Status)) {
210 //
211 // check if there is a bootable removable media could be found in this device path ,
212 // and get the bootable media handle
213 //
214 Handle = BdsLibGetBootableHandle(DevicePath);
215 if (Handle == NULL) {
216 goto Done;
217 }
218 //
219 // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
220 // machinename is ia32, ia64, x64, ...
221 //
222 FilePath = FileDevicePath (Handle, (CONST CHAR16*)PcdGetPtr(PcdDefaultBootFileName));
223 if (FilePath != NULL) {
224 Status = gBS->LoadImage (
225 TRUE,
226 mBdsImageHandle,
227 FilePath,
228 NULL,
229 0,
230 &ImageHandle
231 );
232 if (EFI_ERROR (Status)) {
233 //
234 // The DevicePath failed, and it's not a valid
235 // removable media device.
236 //
237 goto Done;
238 }
239 }
240 }
241
242 if (EFI_ERROR (Status)) {
243 //
244 // It there is any error from the Boot attempt exit now.
245 //
246 goto Done;
247 }
248 //
249 // Provide the image with it's load options
250 //
251 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
252 ASSERT_EFI_ERROR (Status);
253
254 if (Option->LoadOptionsSize != 0) {
255 ImageInfo->LoadOptionsSize = Option->LoadOptionsSize;
256 ImageInfo->LoadOptions = Option->LoadOptions;
257 }
258 //
259 // Before calling the image, enable the Watchdog Timer for
260 // the 5 Minute period
261 //
262 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
263
264 Status = gBS->StartImage (ImageHandle, ExitDataSize, ExitData);
265 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
266
267 //
268 // Clear the Watchdog Timer after the image returns
269 //
270 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
271
272 Done:
273 //
274 // Clear Boot Current
275 //
276 gRT->SetVariable (
277 L"BootCurrent",
278 &gEfiGlobalVariableGuid,
279 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
280 0,
281 &Option->BootCurrent
282 );
283
284 return Status;
285 }
286
287
288 /**
289 Expand a device path that starts with a hard drive media device path node to be a
290 full device path that includes the full hardware path to the device. We need
291 to do this so it can be booted. As an optimization the front match (the part point
292 to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
293 so a connect all is not required on every boot. All successful history device path
294 which point to partition node (the front part) will be saved.
295
296 @param HardDriveDevicePath EFI Device Path to boot, if it starts with a hard
297 drive media device path.
298 @return A Pointer to the full device path or NULL if a valid Hard Drive devic path
299 cannot be found.
300
301 **/
302 EFI_DEVICE_PATH_PROTOCOL *
303 EFIAPI
304 BdsExpandPartitionPartialDevicePathToFull (
305 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
306 )
307 {
308 EFI_STATUS Status;
309 UINTN BlockIoHandleCount;
310 EFI_HANDLE *BlockIoBuffer;
311 EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
312 EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
313 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
314 UINTN Index;
315 UINTN InstanceNum;
316 EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
317 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
318 UINTN CachedDevicePathSize;
319 BOOLEAN DeviceExist;
320 BOOLEAN NeedAdjust;
321 EFI_DEVICE_PATH_PROTOCOL *Instance;
322 UINTN Size;
323
324 FullDevicePath = NULL;
325 //
326 // Check if there is prestore 'HDDP' variable.
327 // If exist, search the front path which point to partition node in the variable instants.
328 // If fail to find or 'HDDP' not exist, reconnect all and search in all system
329 //
330 CachedDevicePath = BdsLibGetVariableAndSize (
331 L"HDDP",
332 &mHdBootVariablePrivateGuid,
333 &CachedDevicePathSize
334 );
335
336 if (CachedDevicePath != NULL) {
337 TempNewDevicePath = CachedDevicePath;
338 DeviceExist = FALSE;
339 NeedAdjust = FALSE;
340 do {
341 //
342 // Check every instance of the variable
343 // First, check whether the instance contain the partition node, which is needed for distinguishing multi
344 // partial partition boot option. Second, check whether the instance could be connected.
345 //
346 Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
347 if (MatchPartitionDevicePathNode (Instance, HardDriveDevicePath)) {
348 //
349 // Connect the device path instance, the device path point to hard drive media device path node
350 // e.g. ACPI() /PCI()/ATA()/Partition()
351 //
352 Status = BdsLibConnectDevicePath (Instance);
353 if (!EFI_ERROR (Status)) {
354 DeviceExist = TRUE;
355 break;
356 }
357 }
358 //
359 // Come here means the first instance is not matched
360 //
361 NeedAdjust = TRUE;
362 FreePool(Instance);
363 } while (TempNewDevicePath != NULL);
364
365 if (DeviceExist) {
366 //
367 // Find the matched device path.
368 // Append the file path information from the boot option and return the fully expanded device path.
369 //
370 DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
371 FullDevicePath = AppendDevicePath (Instance, DevicePath);
372
373 //
374 // Adjust the 'HDDP' instances sequence if the matched one is not first one.
375 //
376 if (NeedAdjust) {
377 //
378 // First delete the matched instance.
379 //
380 TempNewDevicePath = CachedDevicePath;
381 CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, Instance );
382 FreePool (TempNewDevicePath);
383
384 //
385 // Second, append the remaining path after the matched instance
386 //
387 TempNewDevicePath = CachedDevicePath;
388 CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );
389 FreePool (TempNewDevicePath);
390 //
391 // Save the matching Device Path so we don't need to do a connect all next time
392 //
393 Status = gRT->SetVariable (
394 L"HDDP",
395 &mHdBootVariablePrivateGuid,
396 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
397 GetDevicePathSize (CachedDevicePath),
398 CachedDevicePath
399 );
400 }
401
402 FreePool (Instance);
403 FreePool (CachedDevicePath);
404 return FullDevicePath;
405 }
406 }
407
408 //
409 // If we get here we fail to find or 'HDDP' not exist, and now we need
410 // to search all devices in the system for a matched partition
411 //
412 BdsLibConnectAllDriversToAllControllers ();
413 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
414 if (EFI_ERROR (Status) || BlockIoHandleCount == 0 || BlockIoBuffer == NULL) {
415 //
416 // If there was an error or there are no device handles that support
417 // the BLOCK_IO Protocol, then return.
418 //
419 return NULL;
420 }
421 //
422 // Loop through all the device handles that support the BLOCK_IO Protocol
423 //
424 for (Index = 0; Index < BlockIoHandleCount; Index++) {
425
426 Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);
427 if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {
428 continue;
429 }
430
431 if (MatchPartitionDevicePathNode (BlockIoDevicePath, HardDriveDevicePath)) {
432 //
433 // Find the matched partition device path
434 //
435 DevicePath = NextDevicePathNode ((EFI_DEVICE_PATH_PROTOCOL *) HardDriveDevicePath);
436 FullDevicePath = AppendDevicePath (BlockIoDevicePath, DevicePath);
437
438 //
439 // Save the matched partition device path in 'HDDP' variable
440 //
441 if (CachedDevicePath != NULL) {
442 //
443 // Save the matched partition device path as first instance of 'HDDP' variable
444 //
445 if (BdsLibMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {
446 TempNewDevicePath = CachedDevicePath;
447 CachedDevicePath = BdsLibDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);
448 FreePool(TempNewDevicePath);
449
450 TempNewDevicePath = CachedDevicePath;
451 CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
452 FreePool(TempNewDevicePath);
453 } else {
454 TempNewDevicePath = CachedDevicePath;
455 CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
456 FreePool(TempNewDevicePath);
457 }
458 //
459 // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
460 // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.
461 //
462 InstanceNum = 0;
463 ASSERT (CachedDevicePath != NULL);
464 TempNewDevicePath = CachedDevicePath;
465 while (!IsDevicePathEnd (TempNewDevicePath)) {
466 TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
467 //
468 // Parse one instance
469 //
470 while (!IsDevicePathEndType (TempNewDevicePath)) {
471 TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
472 }
473 InstanceNum++;
474 //
475 // If the CachedDevicePath variable contain too much instance, only remain 12 instances.
476 //
477 if (InstanceNum >= 12) {
478 SetDevicePathEndNode (TempNewDevicePath);
479 break;
480 }
481 }
482 } else {
483 CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
484 }
485
486 //
487 // Save the matching Device Path so we don't need to do a connect all next time
488 //
489 Status = gRT->SetVariable (
490 L"HDDP",
491 &mHdBootVariablePrivateGuid,
492 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
493 GetDevicePathSize (CachedDevicePath),
494 CachedDevicePath
495 );
496
497 break;
498 }
499 }
500
501 FreePool (CachedDevicePath);
502 if (BlockIoBuffer != NULL) {
503 FreePool (BlockIoBuffer);
504 }
505 return FullDevicePath;
506 }
507
508 /**
509 Check whether there is a instance in BlockIoDevicePath, which contain multi device path
510 instances, has the same partition node with HardDriveDevicePath device path
511
512 @param BlockIoDevicePath Multi device path instances which need to check
513 @param HardDriveDevicePath A device path which starts with a hard drive media
514 device path.
515
516 @retval TRUE There is a matched device path instance.
517 @retval FALSE There is no matched device path instance.
518
519 **/
520 BOOLEAN
521 EFIAPI
522 MatchPartitionDevicePathNode (
523 IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
524 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
525 )
526 {
527 HARDDRIVE_DEVICE_PATH *TmpHdPath;
528 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
529 BOOLEAN Match;
530 EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode;
531
532 if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
533 return FALSE;
534 }
535
536 //
537 // Make PreviousDevicePath == the device path node before the end node
538 //
539 DevicePath = BlockIoDevicePath;
540 BlockIoHdDevicePathNode = NULL;
541
542 //
543 // find the partition device path node
544 //
545 while (!IsDevicePathEnd (DevicePath)) {
546 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
547 (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)
548 ) {
549 BlockIoHdDevicePathNode = DevicePath;
550 break;
551 }
552
553 DevicePath = NextDevicePathNode (DevicePath);
554 }
555
556 if (BlockIoHdDevicePathNode == NULL) {
557 return FALSE;
558 }
559 //
560 // See if the harddrive device path in blockio matches the orig Hard Drive Node
561 //
562 TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;
563 Match = FALSE;
564
565 //
566 // Check for the match
567 //
568 if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&
569 (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {
570 switch (TmpHdPath->SignatureType) {
571 case SIGNATURE_TYPE_GUID:
572 Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);
573 break;
574 case SIGNATURE_TYPE_MBR:
575 Match = (BOOLEAN)(*((UINT32 *)(&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));
576 break;
577 default:
578 Match = FALSE;
579 break;
580 }
581 }
582
583 return Match;
584 }
585
586 /**
587 Delete the boot option associated with the handle passed in.
588
589 @param Handle The handle which present the device path to create
590 boot option
591
592 @retval EFI_SUCCESS Delete the boot option success
593 @retval EFI_NOT_FOUND If the Device Path is not found in the system
594 @retval EFI_OUT_OF_RESOURCES Lack of memory resource
595 @retval Other Error return value from SetVariable()
596
597 **/
598 EFI_STATUS
599 BdsLibDeleteOptionFromHandle (
600 IN EFI_HANDLE Handle
601 )
602 {
603 UINT16 *BootOrder;
604 UINT8 *BootOptionVar;
605 UINTN BootOrderSize;
606 UINTN BootOptionSize;
607 EFI_STATUS Status;
608 UINTN Index;
609 UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
610 UINTN DevicePathSize;
611 UINTN OptionDevicePathSize;
612 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
613 EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
614 UINT8 *TempPtr;
615
616 Status = EFI_SUCCESS;
617 BootOrder = NULL;
618 BootOrderSize = 0;
619
620 //
621 // Check "BootOrder" variable, if no, means there is no any boot order.
622 //
623 BootOrder = BdsLibGetVariableAndSize (
624 L"BootOrder",
625 &gEfiGlobalVariableGuid,
626 &BootOrderSize
627 );
628 if (BootOrder == NULL) {
629 return EFI_NOT_FOUND;
630 }
631
632 //
633 // Convert device handle to device path protocol instance
634 //
635 DevicePath = DevicePathFromHandle (Handle);
636 if (DevicePath == NULL) {
637 return EFI_NOT_FOUND;
638 }
639 DevicePathSize = GetDevicePathSize (DevicePath);
640
641 //
642 // Loop all boot order variable and find the matching device path
643 //
644 Index = 0;
645 while (Index < BootOrderSize / sizeof (UINT16)) {
646 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
647 BootOptionVar = BdsLibGetVariableAndSize (
648 BootOption,
649 &gEfiGlobalVariableGuid,
650 &BootOptionSize
651 );
652
653 if (BootOptionVar == NULL) {
654 FreePool (BootOrder);
655 return EFI_OUT_OF_RESOURCES;
656 }
657
658 TempPtr = BootOptionVar;
659 TempPtr += sizeof (UINT32) + sizeof (UINT16);
660 TempPtr += StrSize ((CHAR16 *) TempPtr);
661 OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
662 OptionDevicePathSize = GetDevicePathSize (OptionDevicePath);
663
664 //
665 // Check whether the device path match
666 //
667 if ((OptionDevicePathSize == DevicePathSize) &&
668 (CompareMem (DevicePath, OptionDevicePath, DevicePathSize) == 0)) {
669 BdsDeleteBootOption (BootOrder[Index], BootOrder, &BootOrderSize);
670 FreePool (BootOptionVar);
671 break;
672 }
673
674 FreePool (BootOptionVar);
675 Index++;
676 }
677
678 //
679 // Adjust number of boot option for "BootOrder" variable.
680 //
681 Status = gRT->SetVariable (
682 L"BootOrder",
683 &gEfiGlobalVariableGuid,
684 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
685 BootOrderSize,
686 BootOrder
687 );
688
689 FreePool (BootOrder);
690
691 return Status;
692 }
693
694
695 /**
696 Delete all invalid EFI boot options. The probable invalid boot option could
697 be Removable media or Network boot device.
698
699 @retval EFI_SUCCESS Delete all invalid boot option success
700 @retval EFI_NOT_FOUND Variable "BootOrder" is not found
701 @retval EFI_OUT_OF_RESOURCES Lack of memory resource
702 @retval Other Error return value from SetVariable()
703
704 **/
705 EFI_STATUS
706 BdsDeleteAllInvalidEfiBootOption (
707 VOID
708 )
709 {
710 UINT16 *BootOrder;
711 UINT8 *BootOptionVar;
712 UINTN BootOrderSize;
713 UINTN BootOptionSize;
714 EFI_STATUS Status;
715 UINTN Index;
716 UINTN Index2;
717 UINT16 BootOption[BOOT_OPTION_MAX_CHAR];
718 EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath;
719 UINT8 *TempPtr;
720
721 Status = EFI_SUCCESS;
722 BootOrder = NULL;
723 BootOrderSize = 0;
724
725 //
726 // Check "BootOrder" variable firstly, this variable hold the number of boot options
727 //
728 BootOrder = BdsLibGetVariableAndSize (
729 L"BootOrder",
730 &gEfiGlobalVariableGuid,
731 &BootOrderSize
732 );
733 if (NULL == BootOrder) {
734 return EFI_NOT_FOUND;
735 }
736
737 Index = 0;
738 while (Index < BootOrderSize / sizeof (UINT16)) {
739 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
740 BootOptionVar = BdsLibGetVariableAndSize (
741 BootOption,
742 &gEfiGlobalVariableGuid,
743 &BootOptionSize
744 );
745 if (NULL == BootOptionVar) {
746 FreePool (BootOrder);
747 return EFI_OUT_OF_RESOURCES;
748 }
749
750 TempPtr = BootOptionVar;
751 TempPtr += sizeof (UINT32) + sizeof (UINT16);
752 TempPtr += StrSize ((CHAR16 *) TempPtr);
753 OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
754
755 //
756 // Skip legacy boot option (BBS boot device)
757 //
758 if ((DevicePathType (OptionDevicePath) == BBS_DEVICE_PATH) &&
759 (DevicePathSubType (OptionDevicePath) == BBS_BBS_DP)) {
760 FreePool (BootOptionVar);
761 Index++;
762 continue;
763 }
764
765 if (!BdsLibIsValidEFIBootOptDevicePath (OptionDevicePath, FALSE)) {
766 //
767 // Delete this invalid boot option "Boot####"
768 //
769 Status = gRT->SetVariable (
770 BootOption,
771 &gEfiGlobalVariableGuid,
772 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
773 0,
774 NULL
775 );
776 //
777 // Mark this boot option in boot order as deleted
778 //
779 BootOrder[Index] = 0xffff;
780 }
781
782 FreePool (BootOptionVar);
783 Index++;
784 }
785
786 //
787 // Adjust boot order array
788 //
789 Index2 = 0;
790 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
791 if (BootOrder[Index] != 0xffff) {
792 BootOrder[Index2] = BootOrder[Index];
793 Index2 ++;
794 }
795 }
796 Status = gRT->SetVariable (
797 L"BootOrder",
798 &gEfiGlobalVariableGuid,
799 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
800 Index2 * sizeof (UINT16),
801 BootOrder
802 );
803
804 FreePool (BootOrder);
805
806 return Status;
807 }
808
809
810 /**
811 This function will enumerate all possible boot device in the system,
812 it will only execute once of every boot.
813
814 @param BdsBootOptionList The header of the link list which indexed all
815 current boot options
816
817 @retval EFI_SUCCESS Finished all the boot device enumerate and create
818 the boot option base on that boot device
819
820 **/
821 EFI_STATUS
822 EFIAPI
823 BdsLibEnumerateAllBootOption (
824 IN OUT LIST_ENTRY *BdsBootOptionList
825 )
826 {
827 EFI_STATUS Status;
828 UINT16 FloppyNumber;
829 UINT16 CdromNumber;
830 UINT16 UsbNumber;
831 UINT16 MiscNumber;
832 UINT16 NonBlockNumber;
833 UINTN NumberBlockIoHandles;
834 EFI_HANDLE *BlockIoHandles;
835 EFI_BLOCK_IO_PROTOCOL *BlkIo;
836 UINTN Index;
837 UINTN NumberSimpleNetworkHandles;
838 EFI_HANDLE *SimpleNetworkHandles;
839 UINTN FvHandleCount;
840 EFI_HANDLE *FvHandleBuffer;
841 EFI_FV_FILETYPE Type;
842 UINTN Size;
843 EFI_FV_FILE_ATTRIBUTES Attributes;
844 UINT32 AuthenticationStatus;
845 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
846 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
847 UINTN DevicePathType;
848 CHAR16 Buffer[40];
849 EFI_HANDLE *FileSystemHandles;
850 UINTN NumberFileSystemHandles;
851 BOOLEAN NeedDelete;
852 EFI_IMAGE_DOS_HEADER DosHeader;
853 EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
854 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
855
856 FloppyNumber = 0;
857 CdromNumber = 0;
858 UsbNumber = 0;
859 MiscNumber = 0;
860 ZeroMem (Buffer, sizeof (Buffer));
861
862 //
863 // If the boot device enumerate happened, just get the boot
864 // device from the boot order variable
865 //
866 if (mEnumBootDevice) {
867 BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
868 return EFI_SUCCESS;
869 }
870
871 //
872 // Notes: this dirty code is to get the legacy boot option from the
873 // BBS table and create to variable as the EFI boot option, it should
874 // be removed after the CSM can provide legacy boot option directly
875 //
876 REFRESH_LEGACY_BOOT_OPTIONS;
877
878 //
879 // Delete invalid boot option
880 //
881 BdsDeleteAllInvalidEfiBootOption ();
882
883 //
884 // Parse removable media
885 //
886 gBS->LocateHandleBuffer (
887 ByProtocol,
888 &gEfiBlockIoProtocolGuid,
889 NULL,
890 &NumberBlockIoHandles,
891 &BlockIoHandles
892 );
893
894 for (Index = 0; Index < NumberBlockIoHandles; Index++) {
895 Status = gBS->HandleProtocol (
896 BlockIoHandles[Index],
897 &gEfiBlockIoProtocolGuid,
898 (VOID **) &BlkIo
899 );
900 if (!EFI_ERROR (Status)) {
901 if (!BlkIo->Media->RemovableMedia) {
902 //
903 // skip the non-removable block devices
904 //
905 continue;
906 }
907 }
908 DevicePath = DevicePathFromHandle (BlockIoHandles[Index]);
909 DevicePathType = BdsGetBootTypeFromDevicePath (DevicePath);
910
911 switch (DevicePathType) {
912 case BDS_EFI_ACPI_FLOPPY_BOOT:
913 if (FloppyNumber == 0) {
914 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Floppy");
915 } else {
916 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Floppy %d", FloppyNumber);
917 }
918 BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
919 FloppyNumber++;
920 break;
921
922 case BDS_EFI_MESSAGE_ATAPI_BOOT:
923 if (CdromNumber == 0) {
924 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI DVD/CDROM");
925 } else {
926 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI DVD/CDROM %d", CdromNumber);
927 }
928 BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
929 CdromNumber++;
930 break;
931
932 case BDS_EFI_MESSAGE_USB_DEVICE_BOOT:
933 if (UsbNumber == 0) {
934 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI USB Device");
935 } else {
936 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI USB Device %d", UsbNumber);
937 }
938 BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
939 UsbNumber++;
940 break;
941
942 case BDS_EFI_MESSAGE_SCSI_BOOT:
943 if (UsbNumber == 0) {
944 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI SCSI Device");
945 } else {
946 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI SCSI Device %d", UsbNumber);
947 }
948 BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
949 UsbNumber++;
950 break;
951
952 case BDS_EFI_MESSAGE_MISC_BOOT:
953 if (MiscNumber == 0) {
954 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Misc Device");
955 } else {
956 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Misc Device %d", MiscNumber);
957 }
958 BdsLibBuildOptionFromHandle (BlockIoHandles[Index], BdsBootOptionList, Buffer);
959 MiscNumber++;
960 break;
961
962 default:
963 break;
964 }
965 }
966
967 if (NumberBlockIoHandles != 0) {
968 FreePool (BlockIoHandles);
969 }
970
971 //
972 // If there is simple file protocol which does not consume block Io protocol, create a boot option for it here.
973 //
974 NonBlockNumber = 0;
975 gBS->LocateHandleBuffer (
976 ByProtocol,
977 &gEfiSimpleFileSystemProtocolGuid,
978 NULL,
979 &NumberFileSystemHandles,
980 &FileSystemHandles
981 );
982 for (Index = 0; Index < NumberFileSystemHandles; Index++) {
983 Status = gBS->HandleProtocol (
984 FileSystemHandles[Index],
985 &gEfiBlockIoProtocolGuid,
986 (VOID **) &BlkIo
987 );
988 if (!EFI_ERROR (Status)) {
989 //
990 // Skip if the file system handle supports a BlkIo protocol,
991 //
992 continue;
993 }
994
995 //
996 // Do the removable Media thing. \EFI\BOOT\boot{machinename}.EFI
997 // machinename is ia32, ia64, x64, ...
998 //
999 Hdr.Union = &HdrData;
1000 NeedDelete = TRUE;
1001 Status = BdsLibGetImageHeader (
1002 FileSystemHandles[Index],
1003 (CHAR16*)PcdGetPtr (PcdDefaultBootFileName),
1004 &DosHeader,
1005 Hdr
1006 );
1007 if (!EFI_ERROR (Status) &&
1008 EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
1009 Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
1010 NeedDelete = FALSE;
1011 }
1012
1013 if (NeedDelete) {
1014 //
1015 // No such file or the file is not a EFI application, delete this boot option
1016 //
1017 BdsLibDeleteOptionFromHandle (FileSystemHandles[Index]);
1018 } else {
1019 if (NonBlockNumber == 0) {
1020 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Non-Block Boot Device");
1021 } else {
1022 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Non-Block Boot Device %d", NonBlockNumber);
1023 }
1024 BdsLibBuildOptionFromHandle (FileSystemHandles[Index], BdsBootOptionList, Buffer);
1025 NonBlockNumber++;
1026 }
1027 }
1028
1029 if (NumberFileSystemHandles != 0) {
1030 FreePool (FileSystemHandles);
1031 }
1032
1033 //
1034 // Parse Network Boot Device
1035 //
1036 gBS->LocateHandleBuffer (
1037 ByProtocol,
1038 &gEfiSimpleNetworkProtocolGuid,
1039 NULL,
1040 &NumberSimpleNetworkHandles,
1041 &SimpleNetworkHandles
1042 );
1043 for (Index = 0; Index < NumberSimpleNetworkHandles; Index++) {
1044 if (Index == 0) {
1045 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Network");
1046 } else {
1047 UnicodeSPrint (Buffer, sizeof (Buffer), L"EFI Network %d", Index);
1048 }
1049 BdsLibBuildOptionFromHandle (SimpleNetworkHandles[Index], BdsBootOptionList, Buffer);
1050 }
1051
1052 if (NumberSimpleNetworkHandles != 0) {
1053 FreePool (SimpleNetworkHandles);
1054 }
1055
1056 //
1057 // Check if we have on flash shell
1058 //
1059 gBS->LocateHandleBuffer (
1060 ByProtocol,
1061 &gEfiFirmwareVolume2ProtocolGuid,
1062 NULL,
1063 &FvHandleCount,
1064 &FvHandleBuffer
1065 );
1066 for (Index = 0; Index < FvHandleCount; Index++) {
1067 //
1068 // Only care the dispatched FV. If no dispatch protocol on the FV, it is not dispatched, then skip it.
1069 //
1070 Status = gBS->HandleProtocol (
1071 FvHandleBuffer[Index],
1072 &gEfiFirmwareVolumeDispatchProtocolGuid,
1073 (VOID **) &Fv
1074 );
1075 if (EFI_ERROR (Status)) {
1076 continue;
1077 }
1078
1079 gBS->HandleProtocol (
1080 FvHandleBuffer[Index],
1081 &gEfiFirmwareVolume2ProtocolGuid,
1082 (VOID **) &Fv
1083 );
1084
1085 Status = Fv->ReadFile (
1086 Fv,
1087 &gEfiShellFileGuid,
1088 NULL,
1089 &Size,
1090 &Type,
1091 &Attributes,
1092 &AuthenticationStatus
1093 );
1094 if (EFI_ERROR (Status)) {
1095 //
1096 // Skip if no shell file in the FV
1097 //
1098 continue;
1099 }
1100 //
1101 // Build the shell boot option
1102 //
1103 BdsLibBuildOptionFromShell (FvHandleBuffer[Index], BdsBootOptionList);
1104 }
1105
1106 if (FvHandleCount != 0) {
1107 FreePool (FvHandleBuffer);
1108 }
1109 //
1110 // Make sure every boot only have one time
1111 // boot device enumerate
1112 //
1113 BdsLibBuildOptionFromVar (BdsBootOptionList, L"BootOrder");
1114 mEnumBootDevice = TRUE;
1115
1116 return EFI_SUCCESS;
1117 }
1118
1119 /**
1120 Build the boot option with the handle parsed in
1121
1122 @param Handle The handle which present the device path to create
1123 boot option
1124 @param BdsBootOptionList The header of the link list which indexed all
1125 current boot options
1126 @param String The description of the boot option.
1127
1128 **/
1129 VOID
1130 EFIAPI
1131 BdsLibBuildOptionFromHandle (
1132 IN EFI_HANDLE Handle,
1133 IN LIST_ENTRY *BdsBootOptionList,
1134 IN CHAR16 *String
1135 )
1136 {
1137 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1138
1139 DevicePath = DevicePathFromHandle (Handle);
1140
1141 //
1142 // Create and register new boot option
1143 //
1144 BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, String, L"BootOrder");
1145 }
1146
1147
1148 /**
1149 Build the on flash shell boot option with the handle parsed in.
1150
1151 @param Handle The handle which present the device path to create
1152 on flash shell boot option
1153 @param BdsBootOptionList The header of the link list which indexed all
1154 current boot options
1155
1156 **/
1157 VOID
1158 EFIAPI
1159 BdsLibBuildOptionFromShell (
1160 IN EFI_HANDLE Handle,
1161 IN OUT LIST_ENTRY *BdsBootOptionList
1162 )
1163 {
1164 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1165 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ShellNode;
1166
1167 DevicePath = DevicePathFromHandle (Handle);
1168
1169 //
1170 // Build the shell device path
1171 //
1172 EfiInitializeFwVolDevicepathNode (&ShellNode, &gEfiShellFileGuid);
1173
1174 DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ShellNode);
1175
1176 //
1177 // Create and register the shell boot option
1178 //
1179 BdsLibRegisterNewOption (BdsBootOptionList, DevicePath, L"EFI Internal Shell", L"BootOrder");
1180
1181 }
1182
1183 /**
1184 Boot from the UEFI spec defined "BootNext" variable.
1185
1186 **/
1187 VOID
1188 EFIAPI
1189 BdsLibBootNext (
1190 VOID
1191 )
1192 {
1193 UINT16 *BootNext;
1194 UINTN BootNextSize;
1195 CHAR16 Buffer[20];
1196 BDS_COMMON_OPTION *BootOption;
1197 LIST_ENTRY TempList;
1198 UINTN ExitDataSize;
1199 CHAR16 *ExitData;
1200
1201 //
1202 // Init the boot option name buffer and temp link list
1203 //
1204 InitializeListHead (&TempList);
1205 ZeroMem (Buffer, sizeof (Buffer));
1206
1207 BootNext = BdsLibGetVariableAndSize (
1208 L"BootNext",
1209 &gEfiGlobalVariableGuid,
1210 &BootNextSize
1211 );
1212
1213 //
1214 // Clear the boot next variable first
1215 //
1216 if (BootNext != NULL) {
1217 gRT->SetVariable (
1218 L"BootNext",
1219 &gEfiGlobalVariableGuid,
1220 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1221 0,
1222 BootNext
1223 );
1224
1225 //
1226 // Start to build the boot option and try to boot
1227 //
1228 UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *BootNext);
1229 BootOption = BdsLibVariableToOption (&TempList, Buffer);
1230 ASSERT (BootOption != NULL);
1231 BdsLibConnectDevicePath (BootOption->DevicePath);
1232 BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
1233 }
1234
1235 }
1236
1237 /**
1238 Return the bootable media handle.
1239 First, check the device is connected
1240 Second, check whether the device path point to a device which support SimpleFileSystemProtocol,
1241 Third, detect the the default boot file in the Media, and return the removable Media handle.
1242
1243 @param DevicePath Device Path to a bootable device
1244
1245 @retval NULL The media on the DevicePath is not bootable
1246
1247 **/
1248 EFI_HANDLE
1249 EFIAPI
1250 BdsLibGetBootableHandle (
1251 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1252 )
1253 {
1254 EFI_STATUS Status;
1255 EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath;
1256 EFI_DEVICE_PATH_PROTOCOL *DupDevicePath;
1257 EFI_HANDLE Handle;
1258 EFI_BLOCK_IO_PROTOCOL *BlockIo;
1259 VOID *Buffer;
1260 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1261 UINTN Size;
1262 UINTN TempSize;
1263 EFI_HANDLE ReturnHandle;
1264 EFI_HANDLE *SimpleFileSystemHandles;
1265
1266 UINTN NumberSimpleFileSystemHandles;
1267 UINTN Index;
1268 EFI_IMAGE_DOS_HEADER DosHeader;
1269 EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
1270 EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
1271
1272 UpdatedDevicePath = DevicePath;
1273
1274 //
1275 // Check whether the device is connected
1276 //
1277 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);
1278 if (EFI_ERROR (Status)) {
1279 //
1280 // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,
1281 //
1282 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);
1283 if (EFI_ERROR (Status)) {
1284 //
1285 // Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly
1286 //
1287 UpdatedDevicePath = DevicePath;
1288 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
1289 gBS->ConnectController (Handle, NULL, NULL, TRUE);
1290 }
1291 } else {
1292 //
1293 // Get BlockIo protocol and check removable attribute
1294 //
1295 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
1296 //
1297 // Issue a dummy read to the device to check for media change.
1298 // When the removable media is changed, any Block IO read/write will
1299 // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
1300 // returned. After the Block IO protocol is reinstalled, subsequent
1301 // Block IO read/write will success.
1302 //
1303 Buffer = AllocatePool (BlockIo->Media->BlockSize);
1304 if (Buffer != NULL) {
1305 BlockIo->ReadBlocks (
1306 BlockIo,
1307 BlockIo->Media->MediaId,
1308 0,
1309 BlockIo->Media->BlockSize,
1310 Buffer
1311 );
1312 FreePool(Buffer);
1313 }
1314 }
1315
1316 //
1317 // Detect the the default boot file from removable Media
1318 //
1319
1320 //
1321 // If fail to get bootable handle specified by a USB boot option, the BDS should try to find other bootable device in the same USB bus
1322 // Try to locate the USB node device path first, if fail then use its previous PCI node to search
1323 //
1324 DupDevicePath = DuplicateDevicePath (DevicePath);
1325 ASSERT (DupDevicePath != NULL);
1326
1327 UpdatedDevicePath = DupDevicePath;
1328 Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
1329 //
1330 // if the resulting device path point to a usb node, and the usb node is a dummy node, should only let device path only point to the previous Pci node
1331 // Acpi()/Pci()/Usb() --> Acpi()/Pci()
1332 //
1333 if ((DevicePathType (UpdatedDevicePath) == MESSAGING_DEVICE_PATH) &&
1334 (DevicePathSubType (UpdatedDevicePath) == MSG_USB_DP)) {
1335 //
1336 // Remove the usb node, let the device path only point to PCI node
1337 //
1338 SetDevicePathEndNode (UpdatedDevicePath);
1339 UpdatedDevicePath = DupDevicePath;
1340 } else {
1341 UpdatedDevicePath = DevicePath;
1342 }
1343
1344 //
1345 // Get the device path size of boot option
1346 //
1347 Size = GetDevicePathSize(UpdatedDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
1348 ReturnHandle = NULL;
1349 gBS->LocateHandleBuffer (
1350 ByProtocol,
1351 &gEfiSimpleFileSystemProtocolGuid,
1352 NULL,
1353 &NumberSimpleFileSystemHandles,
1354 &SimpleFileSystemHandles
1355 );
1356 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
1357 //
1358 // Get the device path size of SimpleFileSystem handle
1359 //
1360 TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
1361 TempSize = GetDevicePathSize (TempDevicePath)- sizeof (EFI_DEVICE_PATH_PROTOCOL); // minus the end node
1362 //
1363 // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
1364 //
1365 if (Size <= TempSize && CompareMem (TempDevicePath, UpdatedDevicePath, Size)==0) {
1366 //
1367 // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
1368 // machinename is ia32, ia64, x64, ...
1369 //
1370 Hdr.Union = &HdrData;
1371 Status = BdsLibGetImageHeader (
1372 SimpleFileSystemHandles[Index],
1373 (CHAR16*)PcdGetPtr(PcdDefaultBootFileName),
1374 &DosHeader,
1375 Hdr
1376 );
1377 if (!EFI_ERROR (Status) &&
1378 EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
1379 Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) {
1380 ReturnHandle = SimpleFileSystemHandles[Index];
1381 break;
1382 }
1383 }
1384 }
1385
1386 FreePool(DupDevicePath);
1387
1388 if (SimpleFileSystemHandles != NULL) {
1389 FreePool(SimpleFileSystemHandles);
1390 }
1391
1392 return ReturnHandle;
1393 }
1394
1395 /**
1396 Check to see if the network cable is plugged in. If the DevicePath is not
1397 connected it will be connected.
1398
1399 @param DevicePath Device Path to check
1400
1401 @retval TRUE DevicePath points to an Network that is connected
1402 @retval FALSE DevicePath does not point to a bootable network
1403
1404 **/
1405 BOOLEAN
1406 BdsLibNetworkBootWithMediaPresent (
1407 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1408 )
1409 {
1410 EFI_STATUS Status;
1411 EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath;
1412 EFI_HANDLE Handle;
1413 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
1414 BOOLEAN MediaPresent;
1415
1416 MediaPresent = FALSE;
1417
1418 UpdatedDevicePath = DevicePath;
1419 Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);
1420 if (EFI_ERROR (Status)) {
1421 //
1422 // Device not present so see if we need to connect it
1423 //
1424 Status = BdsLibConnectDevicePath (DevicePath);
1425 if (!EFI_ERROR (Status)) {
1426 //
1427 // This one should work after we did the connect
1428 //
1429 Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &UpdatedDevicePath, &Handle);
1430 }
1431 }
1432
1433 if (!EFI_ERROR (Status)) {
1434 Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);
1435 if (!EFI_ERROR (Status)) {
1436 if (Snp->Mode->MediaPresentSupported) {
1437 if (Snp->Mode->State == EfiSimpleNetworkInitialized) {
1438 //
1439 // In case some one else is using the SNP check to see if it's connected
1440 //
1441 MediaPresent = Snp->Mode->MediaPresent;
1442 } else {
1443 //
1444 // No one is using SNP so we need to Start and Initialize so
1445 // MediaPresent will be valid.
1446 //
1447 Status = Snp->Start (Snp);
1448 if (!EFI_ERROR (Status)) {
1449 Status = Snp->Initialize (Snp, 0, 0);
1450 if (!EFI_ERROR (Status)) {
1451 MediaPresent = Snp->Mode->MediaPresent;
1452 Snp->Shutdown (Snp);
1453 }
1454 Snp->Stop (Snp);
1455 }
1456 }
1457 } else {
1458 MediaPresent = TRUE;
1459 }
1460 }
1461 }
1462
1463 return MediaPresent;
1464 }
1465
1466 /**
1467 For a bootable Device path, return its boot type.
1468
1469 @param DevicePath The bootable device Path to check
1470
1471 @retval BDS_EFI_MEDIA_HD_BOOT If the device path contains any media device path node, it is media boot type
1472 For the floppy node, handle it as media node
1473 @retval BDS_EFI_MEDIA_CDROM_BOOT If the device path contains any media device path node, it is media boot type
1474 For the floppy node, handle it as media node
1475 @retval BDS_EFI_ACPI_FLOPPY_BOOT If the device path contains any media device path node, it is media boot type
1476 For the floppy node, handle it as media node
1477 @retval BDS_EFI_MESSAGE_ATAPI_BOOT If the device path not contains any media device path node, and
1478 its last device path node point to a message device path node, it is
1479
1480 @retval BDS_EFI_MESSAGE_SCSI_BOOT If the device path not contains any media device path node, and
1481 its last device path node point to a message device path node, it is
1482 @retval BDS_EFI_MESSAGE_USB_DEVICE_BOOT If the device path not contains any media device path node, and
1483 its last device path node point to a message device path node, it is
1484 @retval BDS_EFI_MESSAGE_MISC_BOOT If the device path not contains any media device path node, and
1485 its last device path node point to a message device path node, it is
1486 @retval BDS_LEGACY_BBS_BOOT Legacy boot type
1487 @retval BDS_EFI_UNSUPPORT An EFI Removable BlockIO device path not point to a media and message device,
1488
1489 **/
1490 UINT32
1491 EFIAPI
1492 BdsGetBootTypeFromDevicePath (
1493 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1494 )
1495 {
1496 ACPI_HID_DEVICE_PATH *Acpi;
1497 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1498 EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
1499
1500
1501 if (NULL == DevicePath) {
1502 return BDS_EFI_UNSUPPORT;
1503 }
1504
1505 TempDevicePath = DevicePath;
1506
1507 while (!IsDevicePathEndType (TempDevicePath)) {
1508 switch (DevicePathType (TempDevicePath)) {
1509 case BBS_DEVICE_PATH:
1510 return BDS_LEGACY_BBS_BOOT;
1511 case MEDIA_DEVICE_PATH:
1512 if (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP) {
1513 return BDS_EFI_MEDIA_HD_BOOT;
1514 } else if (DevicePathSubType (TempDevicePath) == MEDIA_CDROM_DP) {
1515 return BDS_EFI_MEDIA_CDROM_BOOT;
1516 }
1517 break;
1518 case ACPI_DEVICE_PATH:
1519 Acpi = (ACPI_HID_DEVICE_PATH *) TempDevicePath;
1520 if (EISA_ID_TO_NUM (Acpi->HID) == 0x0604) {
1521 return BDS_EFI_ACPI_FLOPPY_BOOT;
1522 }
1523 break;
1524 case MESSAGING_DEVICE_PATH:
1525 //
1526 // Get the last device path node
1527 //
1528 LastDeviceNode = NextDevicePathNode (TempDevicePath);
1529 if (DevicePathSubType(LastDeviceNode) == MSG_DEVICE_LOGICAL_UNIT_DP) {
1530 //
1531 // if the next node type is Device Logical Unit, which specify the Logical Unit Number (LUN),
1532 // skit it
1533 //
1534 LastDeviceNode = NextDevicePathNode (LastDeviceNode);
1535 }
1536 //
1537 // if the device path not only point to driver device, it is not a messaging device path,
1538 //
1539 if (!IsDevicePathEndType (LastDeviceNode)) {
1540 break;
1541 }
1542
1543 if (DevicePathSubType(TempDevicePath) == MSG_ATAPI_DP) {
1544 return BDS_EFI_MESSAGE_ATAPI_BOOT;
1545 } else if (DevicePathSubType(TempDevicePath) == MSG_USB_DP) {
1546 return BDS_EFI_MESSAGE_USB_DEVICE_BOOT;
1547 } else if (DevicePathSubType(TempDevicePath) == MSG_SCSI_DP) {
1548 return BDS_EFI_MESSAGE_SCSI_BOOT;
1549 }
1550 return BDS_EFI_MESSAGE_MISC_BOOT;
1551 default:
1552 break;
1553 }
1554 TempDevicePath = NextDevicePathNode (TempDevicePath);
1555 }
1556
1557 return BDS_EFI_UNSUPPORT;
1558 }
1559
1560 /**
1561 Check whether the Device path in a boot option point to a valid bootable device,
1562 And if CheckMedia is true, check the device is ready to boot now.
1563
1564 @param DevPath the Device path in a boot option
1565 @param CheckMedia if true, check the device is ready to boot now.
1566
1567 @retval TRUE the Device path is valid
1568 @retval FALSE the Device path is invalid .
1569
1570 **/
1571 BOOLEAN
1572 EFIAPI
1573 BdsLibIsValidEFIBootOptDevicePath (
1574 IN EFI_DEVICE_PATH_PROTOCOL *DevPath,
1575 IN BOOLEAN CheckMedia
1576 )
1577 {
1578 EFI_STATUS Status;
1579 EFI_HANDLE Handle;
1580 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1581 EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
1582 EFI_BLOCK_IO_PROTOCOL *BlockIo;
1583
1584 TempDevicePath = DevPath;
1585 LastDeviceNode = DevPath;
1586
1587 //
1588 // Check if it's a valid boot option for network boot device
1589 // Only check if there is SimpleNetworkProtocol installed. If yes, that means
1590 // there is the network card there.
1591 //
1592 Status = gBS->LocateDevicePath (
1593 &gEfiSimpleNetworkProtocolGuid,
1594 &TempDevicePath,
1595 &Handle
1596 );
1597 if (EFI_ERROR (Status)) {
1598 //
1599 // Device not present so see if we need to connect it
1600 //
1601 TempDevicePath = DevPath;
1602 BdsLibConnectDevicePath (TempDevicePath);
1603 Status = gBS->LocateDevicePath (
1604 &gEfiSimpleNetworkProtocolGuid,
1605 &TempDevicePath,
1606 &Handle
1607 );
1608 }
1609
1610 if (!EFI_ERROR (Status)) {
1611 if (CheckMedia) {
1612 //
1613 // Test if it is ready to boot now
1614 //
1615 if (BdsLibNetworkBootWithMediaPresent(DevPath)) {
1616 return TRUE;
1617 }
1618 } else {
1619 return TRUE;
1620 }
1621 }
1622
1623 //
1624 // If the boot option point to a file, it is a valid EFI boot option,
1625 // and assume it is ready to boot now
1626 //
1627 while (!IsDevicePathEnd (TempDevicePath)) {
1628 LastDeviceNode = TempDevicePath;
1629 TempDevicePath = NextDevicePathNode (TempDevicePath);
1630 }
1631 if ((DevicePathType (LastDeviceNode) == MEDIA_DEVICE_PATH) &&
1632 (DevicePathSubType (LastDeviceNode) == MEDIA_FILEPATH_DP)) {
1633 return TRUE;
1634 }
1635
1636 //
1637 // Check if it's a valid boot option for internal Shell
1638 //
1639 if (EfiGetNameGuidFromFwVolDevicePathNode ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode) != NULL) {
1640 //
1641 // If the boot option point to Internal FV shell, make sure it is valid
1642 //
1643 TempDevicePath = DevPath;
1644 Status = BdsLibUpdateFvFileDevicePath (&TempDevicePath, &gEfiShellFileGuid);
1645 if (Status == EFI_ALREADY_STARTED) {
1646 return TRUE;
1647 } else {
1648 if (Status == EFI_SUCCESS) {
1649 FreePool (TempDevicePath);
1650 }
1651 return FALSE;
1652 }
1653 }
1654
1655 //
1656 // If the boot option point to a blockIO device, no matter whether or not it is a removeable device, it is a valid EFI boot option
1657 //
1658 TempDevicePath = DevPath;
1659 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
1660 if (EFI_ERROR (Status)) {
1661 //
1662 // Device not present so see if we need to connect it
1663 //
1664 Status = BdsLibConnectDevicePath (DevPath);
1665 if (!EFI_ERROR (Status)) {
1666 //
1667 // Try again to get the Block Io protocol after we did the connect
1668 //
1669 TempDevicePath = DevPath;
1670 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
1671 }
1672 }
1673
1674 if (!EFI_ERROR (Status)) {
1675 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
1676 if (!EFI_ERROR (Status)) {
1677 if (CheckMedia) {
1678 //
1679 // Test if it is ready to boot now
1680 //
1681 if (BdsLibGetBootableHandle (DevPath) != NULL) {
1682 return TRUE;
1683 }
1684 } else {
1685 return TRUE;
1686 }
1687 }
1688 } else {
1689 //
1690 // if the boot option point to a simple file protocol which does not consume block Io protocol, it is also a valid EFI boot option,
1691 //
1692 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
1693 if (!EFI_ERROR (Status)) {
1694 if (CheckMedia) {
1695 //
1696 // Test if it is ready to boot now
1697 //
1698 if (BdsLibGetBootableHandle (DevPath) != NULL) {
1699 return TRUE;
1700 }
1701 } else {
1702 return TRUE;
1703 }
1704 }
1705 }
1706
1707 return FALSE;
1708 }
1709
1710
1711 /**
1712 According to a file guild, check a Fv file device path is valid. If it is invalid,
1713 try to return the valid device path.
1714 FV address maybe changes for memory layout adjust from time to time, use this function
1715 could promise the Fv file device path is right.
1716
1717 @param DevicePath on input, the Fv file device path need to check on
1718 output, the updated valid Fv file device path
1719 @param FileGuid the Fv file guild
1720
1721 @retval EFI_INVALID_PARAMETER the input DevicePath or FileGuid is invalid
1722 parameter
1723 @retval EFI_UNSUPPORTED the input DevicePath does not contain Fv file
1724 guild at all
1725 @retval EFI_ALREADY_STARTED the input DevicePath has pointed to Fv file, it is
1726 valid
1727 @retval EFI_SUCCESS has successfully updated the invalid DevicePath,
1728 and return the updated device path in DevicePath
1729
1730 **/
1731 EFI_STATUS
1732 EFIAPI
1733 BdsLibUpdateFvFileDevicePath (
1734 IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath,
1735 IN EFI_GUID *FileGuid
1736 )
1737 {
1738 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
1739 EFI_DEVICE_PATH_PROTOCOL *LastDeviceNode;
1740 EFI_STATUS Status;
1741 EFI_GUID *GuidPoint;
1742 UINTN Index;
1743 UINTN FvHandleCount;
1744 EFI_HANDLE *FvHandleBuffer;
1745 EFI_FV_FILETYPE Type;
1746 UINTN Size;
1747 EFI_FV_FILE_ATTRIBUTES Attributes;
1748 UINT32 AuthenticationStatus;
1749 BOOLEAN FindFvFile;
1750 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
1751 EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
1752 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileNode;
1753 EFI_HANDLE FoundFvHandle;
1754 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
1755
1756 if ((DevicePath == NULL) || (*DevicePath == NULL)) {
1757 return EFI_INVALID_PARAMETER;
1758 }
1759 if (FileGuid == NULL) {
1760 return EFI_INVALID_PARAMETER;
1761 }
1762
1763 //
1764 // Check whether the device path point to the default the input Fv file
1765 //
1766 TempDevicePath = *DevicePath;
1767 LastDeviceNode = TempDevicePath;
1768 while (!IsDevicePathEnd (TempDevicePath)) {
1769 LastDeviceNode = TempDevicePath;
1770 TempDevicePath = NextDevicePathNode (TempDevicePath);
1771 }
1772 GuidPoint = EfiGetNameGuidFromFwVolDevicePathNode (
1773 (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) LastDeviceNode
1774 );
1775 if (GuidPoint == NULL) {
1776 //
1777 // if this option does not points to a Fv file, just return EFI_UNSUPPORTED
1778 //
1779 return EFI_UNSUPPORTED;
1780 }
1781 if (!CompareGuid (GuidPoint, FileGuid)) {
1782 //
1783 // If the Fv file is not the input file guid, just return EFI_UNSUPPORTED
1784 //
1785 return EFI_UNSUPPORTED;
1786 }
1787
1788 //
1789 // Check whether the input Fv file device path is valid
1790 //
1791 TempDevicePath = *DevicePath;
1792 FoundFvHandle = NULL;
1793 Status = gBS->LocateDevicePath (
1794 &gEfiFirmwareVolume2ProtocolGuid,
1795 &TempDevicePath,
1796 &FoundFvHandle
1797 );
1798 if (!EFI_ERROR (Status)) {
1799 Status = gBS->HandleProtocol (
1800 FoundFvHandle,
1801 &gEfiFirmwareVolume2ProtocolGuid,
1802 (VOID **) &Fv
1803 );
1804 if (!EFI_ERROR (Status)) {
1805 //
1806 // Set FV ReadFile Buffer as NULL, only need to check whether input Fv file exist there
1807 //
1808 Status = Fv->ReadFile (
1809 Fv,
1810 FileGuid,
1811 NULL,
1812 &Size,
1813 &Type,
1814 &Attributes,
1815 &AuthenticationStatus
1816 );
1817 if (!EFI_ERROR (Status)) {
1818 return EFI_ALREADY_STARTED;
1819 }
1820 }
1821 }
1822
1823 //
1824 // Look for the input wanted FV file in current FV
1825 // First, try to look for in Bds own FV. Bds and input wanted FV file usually are in the same FV
1826 //
1827 FindFvFile = FALSE;
1828 FoundFvHandle = NULL;
1829 Status = gBS->HandleProtocol (
1830 mBdsImageHandle,
1831 &gEfiLoadedImageProtocolGuid,
1832 (VOID **) &LoadedImage
1833 );
1834 if (!EFI_ERROR (Status)) {
1835 Status = gBS->HandleProtocol (
1836 LoadedImage->DeviceHandle,
1837 &gEfiFirmwareVolume2ProtocolGuid,
1838 (VOID **) &Fv
1839 );
1840 if (!EFI_ERROR (Status)) {
1841 Status = Fv->ReadFile (
1842 Fv,
1843 FileGuid,
1844 NULL,
1845 &Size,
1846 &Type,
1847 &Attributes,
1848 &AuthenticationStatus
1849 );
1850 if (!EFI_ERROR (Status)) {
1851 FindFvFile = TRUE;
1852 FoundFvHandle = LoadedImage->DeviceHandle;
1853 }
1854 }
1855 }
1856 //
1857 // Second, if fail to find, try to enumerate all FV
1858 //
1859 if (!FindFvFile) {
1860 FvHandleBuffer = NULL;
1861 gBS->LocateHandleBuffer (
1862 ByProtocol,
1863 &gEfiFirmwareVolume2ProtocolGuid,
1864 NULL,
1865 &FvHandleCount,
1866 &FvHandleBuffer
1867 );
1868 for (Index = 0; Index < FvHandleCount; Index++) {
1869 gBS->HandleProtocol (
1870 FvHandleBuffer[Index],
1871 &gEfiFirmwareVolume2ProtocolGuid,
1872 (VOID **) &Fv
1873 );
1874
1875 Status = Fv->ReadFile (
1876 Fv,
1877 FileGuid,
1878 NULL,
1879 &Size,
1880 &Type,
1881 &Attributes,
1882 &AuthenticationStatus
1883 );
1884 if (EFI_ERROR (Status)) {
1885 //
1886 // Skip if input Fv file not in the FV
1887 //
1888 continue;
1889 }
1890 FindFvFile = TRUE;
1891 FoundFvHandle = FvHandleBuffer[Index];
1892 break;
1893 }
1894
1895 if (FvHandleBuffer != NULL) {
1896 FreePool (FvHandleBuffer);
1897 }
1898 }
1899
1900 if (FindFvFile) {
1901 //
1902 // Build the shell device path
1903 //
1904 NewDevicePath = DevicePathFromHandle (FoundFvHandle);
1905 EfiInitializeFwVolDevicepathNode (&FvFileNode, FileGuid);
1906 NewDevicePath = AppendDevicePathNode (NewDevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFileNode);
1907 *DevicePath = NewDevicePath;
1908 return EFI_SUCCESS;
1909 }
1910 return EFI_NOT_FOUND;
1911 }