3 * Copyright (c) 2011, ARM Limited. All rights reserved.
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "BdsInternal.h"
17 #include <Library/PcdLib.h>
18 #include <Library/PerformanceLib.h>
20 #include <Protocol/Bds.h>
22 #define EFI_SET_TIMER_TO_SECOND 10000000
24 EFI_HANDLE mImageHandle
;
28 GetConsoleDevicePathFromVariable (
29 IN CHAR16
* ConsoleVarName
,
30 IN CHAR16
* DefaultConsolePaths
,
31 OUT EFI_DEVICE_PATH
** DevicePaths
36 EFI_DEVICE_PATH_PROTOCOL
* DevicePathInstances
;
37 EFI_DEVICE_PATH_PROTOCOL
* DevicePathInstance
;
38 CHAR16
* DevicePathStr
;
39 CHAR16
* NextDevicePathStr
;
40 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL
*EfiDevicePathFromTextProtocol
;
42 Status
= GetEnvironmentVariable (ConsoleVarName
, NULL
, NULL
, (VOID
**)&DevicePathInstances
);
43 if (EFI_ERROR(Status
)) {
44 Status
= gBS
->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid
, NULL
, (VOID
**)&EfiDevicePathFromTextProtocol
);
45 ASSERT_EFI_ERROR(Status
);
47 DevicePathInstances
= NULL
;
49 // Extract the Device Path instances from the multi-device path string
50 while ((DefaultConsolePaths
!= NULL
) && (DefaultConsolePaths
[0] != L
'\0')) {
51 NextDevicePathStr
= StrStr (DefaultConsolePaths
, L
";");
52 if (NextDevicePathStr
== NULL
) {
53 DevicePathStr
= DefaultConsolePaths
;
54 DefaultConsolePaths
= NULL
;
56 DevicePathStr
= (CHAR16
*)AllocateCopyPool ((NextDevicePathStr
- DefaultConsolePaths
+ 1) * sizeof(CHAR16
), DefaultConsolePaths
);
57 *(DevicePathStr
+ (NextDevicePathStr
- DefaultConsolePaths
)) = L
'\0';
58 DefaultConsolePaths
= NextDevicePathStr
;
59 if (DefaultConsolePaths
[0] == L
';') {
60 DefaultConsolePaths
++;
64 DevicePathInstance
= EfiDevicePathFromTextProtocol
->ConvertTextToDevicePath (DevicePathStr
);
65 ASSERT(DevicePathInstance
!= NULL
);
66 DevicePathInstances
= AppendDevicePathInstance (DevicePathInstances
, DevicePathInstance
);
68 if (NextDevicePathStr
!= NULL
) {
69 FreePool (DevicePathStr
);
71 FreePool (DevicePathInstance
);
74 // Set the environment variable with this device path multi-instances
75 Size
= GetDevicePathSize (DevicePathInstances
);
77 Status
= gRT
->SetVariable (
79 &gEfiGlobalVariableGuid
,
80 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
85 Status
= EFI_INVALID_PARAMETER
;
89 if (!EFI_ERROR(Status
)) {
90 *DevicePaths
= DevicePathInstances
;
97 InitializeConsolePipe (
98 IN EFI_DEVICE_PATH
*ConsoleDevicePaths
,
99 IN EFI_GUID
*Protocol
,
100 OUT EFI_HANDLE
*Handle
,
108 EFI_DEVICE_PATH_PROTOCOL
* DevicePath
;
110 // Connect all the Device Path Consoles
112 DevicePath
= GetNextDevicePathInstance (&ConsoleDevicePaths
, &Size
);
114 Status
= BdsConnectDevicePath (DevicePath
, Handle
, NULL
);
116 if (EFI_ERROR(Status
)) {
117 // We convert back to the text representation of the device Path
118 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
* DevicePathToTextProtocol
;
119 CHAR16
* DevicePathTxt
;
121 ASSERT_EFI_ERROR(gBS
->LocateProtocol(&gEfiDevicePathToTextProtocolGuid
, NULL
, (VOID
**)&DevicePathToTextProtocol
));
122 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText (DevicePath
, TRUE
, TRUE
);
124 DEBUG((EFI_D_ERROR
,"Fail to start the console with the Device Path '%s'. (Error '%r')\n", DevicePathTxt
, Status
));
126 FreePool (DevicePathTxt
);
130 // If the console splitter driver is not supported by the platform then use the first Device Path
131 // instance for the console interface.
132 if (!EFI_ERROR(Status
) && (*Interface
== NULL
)) {
133 Status
= gBS
->HandleProtocol (*Handle
, Protocol
, Interface
);
135 } while (ConsoleDevicePaths
!= NULL
);
137 // No Device Path has been defined for this console interface. We take the first protocol implementation
138 if (*Interface
== NULL
) {
139 Status
= gBS
->LocateHandleBuffer (ByProtocol
, Protocol
, NULL
, &NoHandles
, &Buffer
);
140 if (EFI_ERROR (Status
)) {
141 BdsConnectAllDrivers();
142 Status
= gBS
->LocateHandleBuffer (ByProtocol
, Protocol
, NULL
, &NoHandles
, &Buffer
);
145 if (!EFI_ERROR(Status
)) {
147 Status
= gBS
->HandleProtocol (*Handle
, Protocol
, Interface
);
148 ASSERT_EFI_ERROR(Status
);
152 Status
= EFI_SUCCESS
;
164 EFI_DEVICE_PATH
* ConOutDevicePaths
;
165 EFI_DEVICE_PATH
* ConInDevicePaths
;
166 EFI_DEVICE_PATH
* ConErrDevicePaths
;
168 // By getting the Console Device Paths from the environment variables before initializing the console pipe, we
169 // create the 3 environment variables (ConIn, ConOut, ConErr) that allows to initialize all the console interface
170 // of newly installed console drivers
171 Status
= GetConsoleDevicePathFromVariable (L
"ConOut", (CHAR16
*)PcdGetPtr(PcdDefaultConOutPaths
),&ConOutDevicePaths
);
172 ASSERT_EFI_ERROR (Status
);
173 Status
= GetConsoleDevicePathFromVariable (L
"ConIn", (CHAR16
*)PcdGetPtr(PcdDefaultConInPaths
),&ConInDevicePaths
);
174 ASSERT_EFI_ERROR (Status
);
175 Status
= GetConsoleDevicePathFromVariable (L
"ConErr", (CHAR16
*)PcdGetPtr(PcdDefaultConOutPaths
),&ConErrDevicePaths
);
176 ASSERT_EFI_ERROR (Status
);
178 // Initialize the Consoles
179 Status
= InitializeConsolePipe (ConOutDevicePaths
, &gEfiSimpleTextOutProtocolGuid
, &gST
->ConsoleOutHandle
, (VOID
**)&gST
->ConOut
);
180 ASSERT_EFI_ERROR (Status
);
181 Status
= InitializeConsolePipe (ConInDevicePaths
, &gEfiSimpleTextInProtocolGuid
, &gST
->ConsoleInHandle
, (VOID
**)&gST
->ConIn
);
182 ASSERT_EFI_ERROR (Status
);
183 Status
= InitializeConsolePipe (ConErrDevicePaths
, &gEfiSimpleTextOutProtocolGuid
, &gST
->StandardErrorHandle
, (VOID
**)&gST
->StdErr
);
184 if (EFI_ERROR(Status
)) {
185 // In case of error, we reuse the console output for the error output
186 gST
->StandardErrorHandle
= gST
->ConsoleOutHandle
;
187 gST
->StdErr
= gST
->ConOut
;
194 DefineDefaultBootEntries (
198 BDS_LOAD_OPTION
*BdsLoadOption
;
201 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL
*EfiDevicePathFromTextProtocol
;
202 EFI_DEVICE_PATH
* BootDevicePath
;
205 // If Boot Order does not exist then create a default entry
208 Status
= gRT
->GetVariable (L
"BootOrder", &gEfiGlobalVariableGuid
, NULL
, &Size
, NULL
);
209 if (Status
== EFI_NOT_FOUND
) {
210 Status
= gBS
->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid
, NULL
, (VOID
**)&EfiDevicePathFromTextProtocol
);
211 ASSERT_EFI_ERROR(Status
);
212 BootDevicePath
= EfiDevicePathFromTextProtocol
->ConvertTextToDevicePath ((CHAR16
*)PcdGetPtr(PcdDefaultBootDevicePath
));
215 // We convert back to the text representation of the device Path to see if the initial text is correct
216 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
* DevicePathToTextProtocol
;
217 CHAR16
* DevicePathTxt
;
219 Status
= gBS
->LocateProtocol(&gEfiDevicePathToTextProtocolGuid
, NULL
, (VOID
**)&DevicePathToTextProtocol
);
220 ASSERT_EFI_ERROR(Status
);
221 DevicePathTxt
= DevicePathToTextProtocol
->ConvertDevicePathToText (BootDevicePath
, TRUE
, TRUE
);
223 ASSERT (StrCmp ((CHAR16
*)PcdGetPtr(PcdDefaultBootDevicePath
), DevicePathTxt
) == 0);
225 FreePool (DevicePathTxt
);
228 // Create the entry is the Default values are correct
229 if (BootDevicePath
!= NULL
) {
230 BootOptionCreate (LOAD_OPTION_ACTIVE
| LOAD_OPTION_CATEGORY_BOOT
,
231 (CHAR16
*)PcdGetPtr(PcdDefaultBootDescription
),
233 (BDS_LOADER_TYPE
)PcdGet32 (PcdDefaultBootType
),
234 (CHAR8
*)PcdGetPtr(PcdDefaultBootArgument
),
237 FreePool (BdsLoadOption
);
245 StartDefaultBootOnTimeout (
252 EFI_EVENT WaitList
[2];
257 CHAR16 BootVariableName
[9];
260 Size
= sizeof(UINT16
);
261 Timeout
= (UINT16
)PcdGet16 (PcdPlatformBootTimeOut
);
262 TimeoutPtr
= &Timeout
;
263 GetEnvironmentVariable (L
"Timeout", &Timeout
, &Size
, (VOID
**)&TimeoutPtr
);
265 if (Timeout
!= 0xFFFF) {
267 // Create the waiting events (keystroke and 1sec timer)
268 gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &WaitList
[0]);
269 gBS
->SetTimer (WaitList
[0], TimerPeriodic
, EFI_SET_TIMER_TO_SECOND
);
270 WaitList
[1] = gST
->ConIn
->WaitForKey
;
274 Print(L
"The default boot selection will start in ");
275 while ((Timeout
> 0) && (WaitIndex
== 0)) {
276 Print(L
"%3d seconds",Timeout
);
277 gBS
->WaitForEvent (2, WaitList
, &WaitIndex
);
278 if (WaitIndex
== 0) {
279 Print(L
"\b\b\b\b\b\b\b\b\b\b\b");
283 gBS
->CloseEvent (WaitList
[0]);
287 // In case of Timeout we start the default boot selection
289 // Get the Boot Option Order from the environment variable (a default value should have been created)
290 GetEnvironmentVariable (L
"BootOrder", NULL
, &BootOrderSize
, (VOID
**)&BootOrder
);
292 for (Index
= 0; Index
< BootOrderSize
/ sizeof (UINT16
); Index
++) {
293 UnicodeSPrint (BootVariableName
, 9 * sizeof(CHAR16
), L
"Boot%04X", BootOrder
[Index
]);
294 Status
= BdsStartBootOption (BootVariableName
);
295 if(!EFI_ERROR(Status
)){
296 // Boot option returned successfully, hence don't need to start next boot option
299 // In case of success, we should not return from this call.
307 This function uses policy data from the platform to determine what operating
308 system or system utility should be loaded and invoked. This function call
309 also optionally make the use of user input to determine the operating system
310 or system utility to be loaded and invoked. When the DXE Core has dispatched
311 all the drivers on the dispatch queue, this function is called. This
312 function will attempt to connect the boot devices required to load and invoke
313 the selected operating system or system utility. During this process,
314 additional firmware volumes may be discovered that may contain addition DXE
315 drivers that can be dispatched by the DXE Core. If a boot device cannot be
316 fully connected, this function calls the DXE Service Dispatch() to allow the
317 DXE drivers from any newly discovered firmware volumes to be dispatched.
318 Then the boot device connection can be attempted again. If the same boot
319 device connection operation fails twice in a row, then that boot device has
320 failed, and should be skipped. This function should never return.
322 @param This The EFI_BDS_ARCH_PROTOCOL instance.
330 IN EFI_BDS_ARCH_PROTOCOL
*This
336 PERF_END (NULL
, "DXE", NULL
, 0);
339 // Declare the Firmware Vendor
342 gST
->FirmwareVendor
= AllocateRuntimePool (Size
);
343 ASSERT (gST
->FirmwareVendor
!= NULL
);
344 UnicodeSPrint (gST
->FirmwareVendor
, Size
, L
"%a EFI %a %a", PcdGetPtr(PcdFirmwareVendor
), __DATE__
, __TIME__
);
346 // If BootNext environment variable is defined then we just load it !
347 Status
= BdsStartBootOption (L
"BootNext");
348 if (Status
!= EFI_NOT_FOUND
) {
349 // BootNext has not been succeeded launched
350 if (EFI_ERROR(Status
)) {
351 Print(L
"Fail to start BootNext.\n");
354 // Delete the BootNext environment variable
355 gRT
->SetVariable (L
"BootNext", &gEfiGlobalVariableGuid
,
356 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
360 // If Boot Order does not exist then create a default entry
361 DefineDefaultBootEntries ();
363 // Now we need to setup the EFI System Table with information about the console devices.
364 InitializeConsole ();
366 // Timer before initiating the default boot selection
367 StartDefaultBootOnTimeout ();
369 // Start the Boot Menu
370 Status
= BootMenuMain ();
371 ASSERT_EFI_ERROR (Status
);
375 EFI_BDS_ARCH_PROTOCOL gBdsProtocol
= {
382 IN EFI_HANDLE ImageHandle
,
383 IN EFI_SYSTEM_TABLE
*SystemTable
388 mImageHandle
= ImageHandle
;
390 Status
= gBS
->InstallMultipleProtocolInterfaces (
392 &gEfiBdsArchProtocolGuid
, &gBdsProtocol
,
395 ASSERT_EFI_ERROR (Status
);