]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Universal/BdsDxe/BdsEntry.c
8c7c340bfd3c4ce06c081190832e27dfe907a769
[mirror_edk2.git] / IntelFrameworkModulePkg / Universal / BdsDxe / BdsEntry.c
1 /** @file
2 This module produce main entry for BDS phase - BdsEntry.
3 When this module was dispatched by DxeCore, gEfiBdsArchProtocolGuid will be installed
4 which contains interface of BdsEntry.
5 After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked
6 to enter BDS phase.
7
8 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10
11 **/
12
13 #include "Bds.h"
14 #include "Language.h"
15 #include "FrontPage.h"
16 #include "Hotkey.h"
17 #include "HwErrRecSupport.h"
18
19 ///
20 /// BDS arch protocol instance initial value.
21 ///
22 /// Note: Current BDS not directly get the BootMode, DefaultBoot,
23 /// TimeoutDefault, MemoryTestLevel value from the BDS arch protocol.
24 /// Please refer to the library useage of BdsLibGetBootMode, BdsLibGetTimeout
25 /// and PlatformBdsDiagnostics in BdsPlatform.c
26 ///
27 EFI_HANDLE gBdsHandle = NULL;
28
29 EFI_BDS_ARCH_PROTOCOL gBds = {
30 BdsEntry
31 };
32
33 UINT16 *mBootNext = NULL;
34
35 ///
36 /// The read-only variables defined in UEFI Spec.
37 ///
38 CHAR16 *mReadOnlyVariables[] = {
39 L"PlatformLangCodes",
40 L"LangCodes",
41 L"BootOptionSupport",
42 L"HwErrRecSupport",
43 L"OsIndicationsSupported"
44 };
45
46 /**
47
48 Install Boot Device Selection Protocol
49
50 @param ImageHandle The image handle.
51 @param SystemTable The system table.
52
53 @retval EFI_SUCEESS BDS has finished initializing.
54 Return the dispatcher and recall BDS.Entry
55 @retval Other Return status from AllocatePool() or gBS->InstallProtocolInterface
56
57 **/
58 EFI_STATUS
59 EFIAPI
60 BdsInitialize (
61 IN EFI_HANDLE ImageHandle,
62 IN EFI_SYSTEM_TABLE *SystemTable
63 )
64 {
65 EFI_STATUS Status;
66
67 //
68 // Install protocol interface
69 //
70 Status = gBS->InstallMultipleProtocolInterfaces (
71 &gBdsHandle,
72 &gEfiBdsArchProtocolGuid, &gBds,
73 NULL
74 );
75 ASSERT_EFI_ERROR (Status);
76
77 return Status;
78 }
79
80 /**
81
82 This function attempts to boot for the boot order specified
83 by platform policy.
84
85 **/
86 VOID
87 BdsBootDeviceSelect (
88 VOID
89 )
90 {
91 EFI_STATUS Status;
92 LIST_ENTRY *Link;
93 BDS_COMMON_OPTION *BootOption;
94 UINTN ExitDataSize;
95 CHAR16 *ExitData;
96 UINT16 Timeout;
97 LIST_ENTRY BootLists;
98 CHAR16 Buffer[20];
99 BOOLEAN BootNextExist;
100 LIST_ENTRY *LinkBootNext;
101 EFI_EVENT ConnectConInEvent;
102
103 //
104 // Got the latest boot option
105 //
106 BootNextExist = FALSE;
107 LinkBootNext = NULL;
108 ConnectConInEvent = NULL;
109 InitializeListHead (&BootLists);
110
111 //
112 // First check the boot next option
113 //
114 ZeroMem (Buffer, sizeof (Buffer));
115
116 //
117 // Create Event to signal ConIn connection request
118 //
119 if (PcdGetBool (PcdConInConnectOnDemand)) {
120 Status = gBS->CreateEventEx (
121 EVT_NOTIFY_SIGNAL,
122 TPL_CALLBACK,
123 EfiEventEmptyFunction,
124 NULL,
125 &gConnectConInEventGuid,
126 &ConnectConInEvent
127 );
128 if (EFI_ERROR(Status)) {
129 ConnectConInEvent = NULL;
130 }
131 }
132
133 if (mBootNext != NULL) {
134 //
135 // Indicate we have the boot next variable, so this time
136 // boot will always have this boot option
137 //
138 BootNextExist = TRUE;
139
140 //
141 // Clear the this variable so it's only exist in this time boot
142 //
143 Status = gRT->SetVariable (
144 L"BootNext",
145 &gEfiGlobalVariableGuid,
146 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
147 0,
148 NULL
149 );
150 //
151 // Deleting variable with current variable implementation shouldn't fail.
152 //
153 ASSERT_EFI_ERROR (Status);
154
155 //
156 // Add the boot next boot option
157 //
158 UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *mBootNext);
159 BootOption = BdsLibVariableToOption (&BootLists, Buffer);
160
161 //
162 // If fail to get boot option from variable, just return and do nothing.
163 //
164 if (BootOption == NULL) {
165 return;
166 }
167
168 BootOption->BootCurrent = *mBootNext;
169 }
170 //
171 // Parse the boot order to get boot option
172 //
173 BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
174
175 //
176 // When we didn't have chance to build boot option variables in the first
177 // full configuration boot (e.g.: Reset in the first page or in Device Manager),
178 // we have no boot options in the following mini configuration boot.
179 // Give the last chance to enumerate the boot options.
180 //
181 if (IsListEmpty (&BootLists)) {
182 BdsLibEnumerateAllBootOption (&BootLists);
183 }
184
185 Link = BootLists.ForwardLink;
186
187 //
188 // Parameter check, make sure the loop will be valid
189 //
190 if (Link == NULL) {
191 return ;
192 }
193 //
194 // Here we make the boot in a loop, every boot success will
195 // return to the front page
196 //
197 for (;;) {
198 //
199 // Check the boot option list first
200 //
201 if (Link == &BootLists) {
202 //
203 // When LazyConIn enabled, signal connect ConIn event before enter UI
204 //
205 if (PcdGetBool (PcdConInConnectOnDemand) && ConnectConInEvent != NULL) {
206 gBS->SignalEvent (ConnectConInEvent);
207 }
208
209 //
210 // There are two ways to enter here:
211 // 1. There is no active boot option, give user chance to
212 // add new boot option
213 // 2. All the active boot option processed, and there is no
214 // one is success to boot, then we back here to allow user
215 // add new active boot option
216 //
217 Timeout = 0xffff;
218 PlatformBdsEnterFrontPage (Timeout, FALSE);
219 InitializeListHead (&BootLists);
220 BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
221 Link = BootLists.ForwardLink;
222 continue;
223 }
224 //
225 // Get the boot option from the link list
226 //
227 BootOption = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
228
229 //
230 // According to EFI Specification, if a load option is not marked
231 // as LOAD_OPTION_ACTIVE, the boot manager will not automatically
232 // load the option.
233 //
234 if (!IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE)) {
235 //
236 // skip the header of the link list, because it has no boot option
237 //
238 Link = Link->ForwardLink;
239 continue;
240 }
241 //
242 // Make sure the boot option device path connected,
243 // but ignore the BBS device path
244 //
245 if (DevicePathType (BootOption->DevicePath) != BBS_DEVICE_PATH) {
246 //
247 // Notes: the internal shell can not been connected with device path
248 // so we do not check the status here
249 //
250 BdsLibConnectDevicePath (BootOption->DevicePath);
251 }
252
253 //
254 // Restore to original mode before launching boot option.
255 //
256 BdsSetConsoleMode (FALSE);
257
258 //
259 // All the driver options should have been processed since
260 // now boot will be performed.
261 //
262 Status = BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
263 if (Status != EFI_SUCCESS) {
264 //
265 // Call platform action to indicate the boot fail
266 //
267 BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
268 PlatformBdsBootFail (BootOption, Status, ExitData, ExitDataSize);
269
270 //
271 // Check the next boot option
272 //
273 Link = Link->ForwardLink;
274
275 } else {
276 //
277 // Call platform action to indicate the boot success
278 //
279 BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
280 PlatformBdsBootSuccess (BootOption);
281
282 //
283 // Boot success, then stop process the boot order, and
284 // present the boot manager menu, front page
285 //
286
287 //
288 // When LazyConIn enabled, signal connect ConIn Event before enter UI
289 //
290 if (PcdGetBool (PcdConInConnectOnDemand) && ConnectConInEvent != NULL) {
291 gBS->SignalEvent (ConnectConInEvent);
292 }
293
294 Timeout = 0xffff;
295 PlatformBdsEnterFrontPage (Timeout, FALSE);
296
297 //
298 // Rescan the boot option list, avoid potential risk of the boot
299 // option change in front page
300 //
301 if (BootNextExist) {
302 LinkBootNext = BootLists.ForwardLink;
303 }
304
305 InitializeListHead (&BootLists);
306 if (LinkBootNext != NULL) {
307 //
308 // Reserve the boot next option
309 //
310 InsertTailList (&BootLists, LinkBootNext);
311 }
312
313 BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
314 Link = BootLists.ForwardLink;
315 }
316 }
317
318 }
319
320 /**
321
322 Validate input console variable data.
323
324 If found the device path is not a valid device path, remove the variable.
325
326 @param VariableName Input console variable name.
327
328 **/
329 VOID
330 BdsFormalizeConsoleVariable (
331 IN CHAR16 *VariableName
332 )
333 {
334 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
335 UINTN VariableSize;
336 EFI_STATUS Status;
337
338 DevicePath = BdsLibGetVariableAndSize (
339 VariableName,
340 &gEfiGlobalVariableGuid,
341 &VariableSize
342 );
343 if ((DevicePath != NULL) && !IsDevicePathValid (DevicePath, VariableSize)) {
344 Status = gRT->SetVariable (
345 VariableName,
346 &gEfiGlobalVariableGuid,
347 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
348 0,
349 NULL
350 );
351 //
352 // Deleting variable with current variable implementation shouldn't fail.
353 //
354 ASSERT_EFI_ERROR (Status);
355 }
356 }
357
358 /**
359
360 Formalize Bds global variables.
361
362 1. For ConIn/ConOut/ConErr, if found the device path is not a valid device path, remove the variable.
363 2. For OsIndicationsSupported, Create a BS/RT/UINT64 variable to report caps
364 3. Delete OsIndications variable if it is not NV/BS/RT UINT64
365 Item 3 is used to solve case when OS corrupts OsIndications. Here simply delete this NV variable.
366
367 **/
368 VOID
369 BdsFormalizeEfiGlobalVariable (
370 VOID
371 )
372 {
373 EFI_STATUS Status;
374 UINT64 OsIndicationSupport;
375 UINT64 OsIndication;
376 UINTN DataSize;
377 UINT32 Attributes;
378
379 //
380 // Validate Console variable.
381 //
382 BdsFormalizeConsoleVariable (L"ConIn");
383 BdsFormalizeConsoleVariable (L"ConOut");
384 BdsFormalizeConsoleVariable (L"ErrOut");
385
386 //
387 // OS indicater support variable
388 //
389 OsIndicationSupport = EFI_OS_INDICATIONS_BOOT_TO_FW_UI \
390 | EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
391
392 BdsDxeSetVariableAndReportStatusCodeOnError (
393 L"OsIndicationsSupported",
394 &gEfiGlobalVariableGuid,
395 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
396 sizeof(UINT64),
397 &OsIndicationSupport
398 );
399
400 //
401 // If OsIndications is invalid, remove it.
402 // Invalid case
403 // 1. Data size != UINT64
404 // 2. OsIndication value inconsistence
405 // 3. OsIndication attribute inconsistence
406 //
407 OsIndication = 0;
408 Attributes = 0;
409 DataSize = sizeof(UINT64);
410 Status = gRT->GetVariable (
411 L"OsIndications",
412 &gEfiGlobalVariableGuid,
413 &Attributes,
414 &DataSize,
415 &OsIndication
416 );
417
418 if (!EFI_ERROR(Status)) {
419 if (DataSize != sizeof(UINT64) ||
420 (OsIndication & ~OsIndicationSupport) != 0 ||
421 Attributes != (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE)){
422
423 DEBUG ((EFI_D_ERROR, "Unformalized OsIndications variable exists. Delete it\n"));
424 Status = gRT->SetVariable (
425 L"OsIndications",
426 &gEfiGlobalVariableGuid,
427 0,
428 0,
429 NULL
430 );
431 //
432 // Deleting variable with current variable implementation shouldn't fail.
433 //
434 ASSERT_EFI_ERROR (Status);
435 }
436 }
437
438 }
439
440 /**
441
442 Service routine for BdsInstance->Entry(). Devices are connected, the
443 consoles are initialized, and the boot options are tried.
444
445 @param This Protocol Instance structure.
446
447 **/
448 VOID
449 EFIAPI
450 BdsEntry (
451 IN EFI_BDS_ARCH_PROTOCOL *This
452 )
453 {
454 LIST_ENTRY DriverOptionList;
455 LIST_ENTRY BootOptionList;
456 UINTN BootNextSize;
457 CHAR16 *FirmwareVendor;
458 EFI_STATUS Status;
459 UINT16 BootTimeOut;
460 UINTN Index;
461 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
462
463 //
464 // Insert the performance probe
465 //
466 PERF_END (NULL, "DXE", NULL, 0);
467 PERF_START (NULL, "BDS", NULL, 0);
468
469 //
470 // Initialize the global system boot option and driver option
471 //
472 InitializeListHead (&DriverOptionList);
473 InitializeListHead (&BootOptionList);
474
475 //
476 // Initialize hotkey service
477 //
478 InitializeHotkeyService ();
479
480 //
481 // Fill in FirmwareVendor and FirmwareRevision from PCDs
482 //
483 FirmwareVendor = (CHAR16 *)PcdGetPtr (PcdFirmwareVendor);
484 gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);
485 ASSERT (gST->FirmwareVendor != NULL);
486 gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);
487
488 //
489 // Fixup Tasble CRC after we updated Firmware Vendor and Revision
490 //
491 gST->Hdr.CRC32 = 0;
492 gBS->CalculateCrc32 ((VOID *)gST, sizeof(EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);
493
494 //
495 // Validate Variable.
496 //
497 BdsFormalizeEfiGlobalVariable();
498
499 //
500 // Mark the read-only variables if the Variable Lock protocol exists
501 //
502 Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
503 DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status));
504 if (!EFI_ERROR (Status)) {
505 for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {
506 Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid);
507 ASSERT_EFI_ERROR (Status);
508 }
509 }
510
511 //
512 // Report Status Code to indicate connecting drivers will happen
513 //
514 REPORT_STATUS_CODE (
515 EFI_PROGRESS_CODE,
516 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS)
517 );
518
519 InitializeHwErrRecSupport();
520
521 //
522 // Initialize L"Timeout" EFI global variable.
523 //
524 BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);
525 if (BootTimeOut != 0xFFFF) {
526 //
527 // If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification
528 // define same behavior between no value or 0xFFFF value for L"Timeout".
529 //
530 BdsDxeSetVariableAndReportStatusCodeOnError (
531 L"Timeout",
532 &gEfiGlobalVariableGuid,
533 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
534 sizeof (UINT16),
535 &BootTimeOut
536 );
537 }
538
539 //
540 // bugbug: platform specific code
541 // Initialize the platform specific string and language
542 //
543 InitializeStringSupport ();
544 InitializeLanguage (TRUE);
545 InitializeFrontPage (TRUE);
546
547 //
548 // Do the platform init, can be customized by OEM/IBV
549 //
550 PERF_START (NULL, "PlatformBds", "BDS", 0);
551 PlatformBdsInit ();
552
553 //
554 // Set up the device list based on EFI 1.1 variables
555 // process Driver#### and Load the driver's in the
556 // driver option list
557 //
558 BdsLibBuildOptionFromVar (&DriverOptionList, L"DriverOrder");
559 if (!IsListEmpty (&DriverOptionList)) {
560 BdsLibLoadDrivers (&DriverOptionList);
561 }
562 //
563 // Check if we have the boot next option
564 //
565 mBootNext = BdsLibGetVariableAndSize (
566 L"BootNext",
567 &gEfiGlobalVariableGuid,
568 &BootNextSize
569 );
570
571 //
572 // Setup some platform policy here
573 //
574 PlatformBdsPolicyBehavior (&DriverOptionList, &BootOptionList, BdsProcessCapsules, BdsMemoryTest);
575 PERF_END (NULL, "PlatformBds", "BDS", 0);
576
577 //
578 // BDS select the boot device to load OS
579 //
580 BdsBootDeviceSelect ();
581
582 //
583 // Only assert here since this is the right behavior, we should never
584 // return back to DxeCore.
585 //
586 ASSERT (FALSE);
587
588 return ;
589 }
590
591
592 /**
593 Set the variable and report the error through status code upon failure.
594
595 @param VariableName A Null-terminated string that is the name of the vendor's variable.
596 Each VariableName is unique for each VendorGuid. VariableName must
597 contain 1 or more characters. If VariableName is an empty string,
598 then EFI_INVALID_PARAMETER is returned.
599 @param VendorGuid A unique identifier for the vendor.
600 @param Attributes Attributes bitmask to set for the variable.
601 @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
602 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or
603 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
604 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
605 set, then a SetVariable() call with a DataSize of zero will not cause any change to
606 the variable value (the timestamp associated with the variable may be updated however
607 even if no new data value is provided,see the description of the
608 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
609 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
610 @param Data The contents for the variable.
611
612 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
613 defined by the Attributes.
614 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the
615 DataSize exceeds the maximum allowed.
616 @retval EFI_INVALID_PARAMETER VariableName is an empty string.
617 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
618 @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error.
619 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
620 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
621 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
622 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo
623 does NOT pass the validation check carried out by the firmware.
624
625 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
626 **/
627 EFI_STATUS
628 BdsDxeSetVariableAndReportStatusCodeOnError (
629 IN CHAR16 *VariableName,
630 IN EFI_GUID *VendorGuid,
631 IN UINT32 Attributes,
632 IN UINTN DataSize,
633 IN VOID *Data
634 )
635 {
636 EFI_STATUS Status;
637 EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
638 UINTN NameSize;
639
640 Status = gRT->SetVariable (
641 VariableName,
642 VendorGuid,
643 Attributes,
644 DataSize,
645 Data
646 );
647 if (EFI_ERROR (Status)) {
648 NameSize = StrSize (VariableName);
649 SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
650 if (SetVariableStatus != NULL) {
651 CopyGuid (&SetVariableStatus->Guid, VendorGuid);
652 SetVariableStatus->NameSize = NameSize;
653 SetVariableStatus->DataSize = DataSize;
654 SetVariableStatus->SetStatus = Status;
655 SetVariableStatus->Attributes = Attributes;
656 CopyMem (SetVariableStatus + 1, VariableName, NameSize);
657 CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize);
658
659 REPORT_STATUS_CODE_EX (
660 EFI_ERROR_CODE,
661 PcdGet32 (PcdErrorCodeSetVariable),
662 0,
663 NULL,
664 &gEdkiiStatusCodeDataTypeVariableGuid,
665 SetVariableStatus,
666 sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
667 );
668
669 FreePool (SetVariableStatus);
670 }
671 }
672
673 return Status;
674 }
675