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