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