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