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