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