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