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