]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/Bds.c
ArmPlatformPkg/Bds: Upgrade the BDS to be more conformed to the UEFI Specification
[mirror_edk2.git] / ArmPlatformPkg / Bds / Bds.c
1 /** @file
2 *
3 * Copyright (c) 2011, ARM Limited. All rights reserved.
4 *
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
9 *
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.
12 *
13 **/
14
15 #include "BdsInternal.h"
16
17 #include <Library/PcdLib.h>
18 #include <Library/PerformanceLib.h>
19
20 #include <Protocol/Bds.h>
21
22 #define EFI_SET_TIMER_TO_SECOND 10000000
23
24 EFI_HANDLE mImageHandle;
25
26 STATIC
27 EFI_STATUS
28 GetConsoleDevicePathFromVariable (
29 IN CHAR16* ConsoleVarName,
30 IN CHAR16* DefaultConsolePaths,
31 OUT EFI_DEVICE_PATH** DevicePaths
32 )
33 {
34 EFI_STATUS Status;
35 UINTN Size;
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;
41
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);
46
47 DevicePathInstances = NULL;
48
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;
55 } else {
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++;
61 }
62 }
63
64 DevicePathInstance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr);
65 ASSERT(DevicePathInstance != NULL);
66 DevicePathInstances = AppendDevicePathInstance (DevicePathInstances, DevicePathInstance);
67
68 if (NextDevicePathStr != NULL) {
69 FreePool (DevicePathStr);
70 }
71 FreePool (DevicePathInstance);
72 }
73
74 // Set the environment variable with this device path multi-instances
75 Size = GetDevicePathSize (DevicePathInstances);
76 if (Size > 0) {
77 Status = gRT->SetVariable (
78 ConsoleVarName,
79 &gEfiGlobalVariableGuid,
80 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
81 Size,
82 DevicePathInstances
83 );
84 } else {
85 Status = EFI_INVALID_PARAMETER;
86 }
87 }
88
89 if (!EFI_ERROR(Status)) {
90 *DevicePaths = DevicePathInstances;
91 }
92 return EFI_SUCCESS;
93 }
94
95 STATIC
96 EFI_STATUS
97 InitializeConsolePipe (
98 IN EFI_DEVICE_PATH *ConsoleDevicePaths,
99 IN EFI_GUID *Protocol,
100 OUT EFI_HANDLE *Handle,
101 OUT VOID* *Interface
102 )
103 {
104 EFI_STATUS Status;
105 UINTN Size;
106 UINTN NoHandles;
107 EFI_HANDLE *Buffer;
108 EFI_DEVICE_PATH_PROTOCOL* DevicePath;
109
110 // Connect all the Device Path Consoles
111 do {
112 DevicePath = GetNextDevicePathInstance (&ConsoleDevicePaths, &Size);
113
114 Status = BdsConnectDevicePath (DevicePath, Handle, NULL);
115 DEBUG_CODE_BEGIN();
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;
120
121 ASSERT_EFI_ERROR(gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol));
122 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (DevicePath, TRUE, TRUE);
123
124 DEBUG((EFI_D_ERROR,"Fail to start the console with the Device Path '%s'. (Error '%r')\n", DevicePathTxt, Status));
125
126 FreePool (DevicePathTxt);
127 }
128 DEBUG_CODE_END();
129
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);
134 }
135 } while (ConsoleDevicePaths != NULL);
136
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);
143 }
144
145 if (!EFI_ERROR(Status)) {
146 *Handle = Buffer[0];
147 Status = gBS->HandleProtocol (*Handle, Protocol, Interface);
148 ASSERT_EFI_ERROR(Status);
149 }
150 FreePool (Buffer);
151 } else {
152 Status = EFI_SUCCESS;
153 }
154
155 return Status;
156 }
157
158 EFI_STATUS
159 InitializeConsole (
160 VOID
161 )
162 {
163 EFI_STATUS Status;
164 EFI_DEVICE_PATH* ConOutDevicePaths;
165 EFI_DEVICE_PATH* ConInDevicePaths;
166 EFI_DEVICE_PATH* ConErrDevicePaths;
167
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);
177
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;
188 }
189
190 return EFI_SUCCESS;
191 }
192
193 EFI_STATUS
194 DefineDefaultBootEntries (
195 VOID
196 )
197 {
198 BDS_LOAD_OPTION *BdsLoadOption;
199 UINTN Size;
200 EFI_STATUS Status;
201 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;
202 EFI_DEVICE_PATH* BootDevicePath;
203
204 //
205 // If Boot Order does not exist then create a default entry
206 //
207 Size = 0;
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));
213
214 DEBUG_CODE_BEGIN();
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;
218
219 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
220 ASSERT_EFI_ERROR(Status);
221 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootDevicePath, TRUE, TRUE);
222
223 ASSERT (StrCmp ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath), DevicePathTxt) == 0);
224
225 FreePool (DevicePathTxt);
226 DEBUG_CODE_END();
227
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),
232 BootDevicePath,
233 (BDS_LOADER_TYPE)PcdGet32 (PcdDefaultBootType),
234 (CHAR8*)PcdGetPtr(PcdDefaultBootArgument),
235 &BdsLoadOption
236 );
237 FreePool (BdsLoadOption);
238 }
239 }
240
241 return EFI_SUCCESS;
242 }
243
244 EFI_STATUS
245 StartDefaultBootOnTimeout (
246 VOID
247 )
248 {
249 UINTN Size;
250 UINT16 Timeout;
251 UINT16 *TimeoutPtr;
252 EFI_EVENT WaitList[2];
253 UINTN WaitIndex;
254 UINT16 *BootOrder;
255 UINTN BootOrderSize;
256 UINTN Index;
257 CHAR16 BootVariableName[9];
258 EFI_STATUS Status;
259
260 Size = sizeof(UINT16);
261 Timeout = (UINT16)PcdGet16 (PcdPlatformBootTimeOut);
262 TimeoutPtr = &Timeout;
263 GetEnvironmentVariable (L"Timeout", &Timeout, &Size, (VOID**)&TimeoutPtr);
264
265 if (Timeout != 0xFFFF) {
266 if (Timeout > 0) {
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;
271
272 // Start the timer
273 WaitIndex = 0;
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");
280 Timeout--;
281 }
282 }
283 gBS->CloseEvent (WaitList[0]);
284 Print(L"\n\r");
285 }
286
287 // In case of Timeout we start the default boot selection
288 if (Timeout == 0) {
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);
291
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
297 break;
298 }
299 // In case of success, we should not return from this call.
300 }
301 }
302 }
303 return EFI_SUCCESS;
304 }
305
306 /**
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.
321
322 @param This The EFI_BDS_ARCH_PROTOCOL instance.
323
324 @return None.
325
326 **/
327 VOID
328 EFIAPI
329 BdsEntry (
330 IN EFI_BDS_ARCH_PROTOCOL *This
331 )
332 {
333 UINTN Size;
334 EFI_STATUS Status;
335
336 PERF_END (NULL, "DXE", NULL, 0);
337
338 //
339 // Declare the Firmware Vendor
340 //
341 Size = 0x100;
342 gST->FirmwareVendor = AllocateRuntimePool (Size);
343 ASSERT (gST->FirmwareVendor != NULL);
344 UnicodeSPrint (gST->FirmwareVendor, Size, L"%a EFI %a %a", PcdGetPtr(PcdFirmwareVendor), __DATE__, __TIME__);
345
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");
352 }
353
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,
357 0, NULL);
358 }
359
360 // If Boot Order does not exist then create a default entry
361 DefineDefaultBootEntries ();
362
363 // Now we need to setup the EFI System Table with information about the console devices.
364 InitializeConsole ();
365
366 // Timer before initiating the default boot selection
367 StartDefaultBootOnTimeout ();
368
369 // Start the Boot Menu
370 Status = BootMenuMain ();
371 ASSERT_EFI_ERROR (Status);
372
373 }
374
375 EFI_BDS_ARCH_PROTOCOL gBdsProtocol = {
376 BdsEntry,
377 };
378
379 EFI_STATUS
380 EFIAPI
381 BdsInitialize (
382 IN EFI_HANDLE ImageHandle,
383 IN EFI_SYSTEM_TABLE *SystemTable
384 )
385 {
386 EFI_STATUS Status;
387
388 mImageHandle = ImageHandle;
389
390 Status = gBS->InstallMultipleProtocolInterfaces (
391 &ImageHandle,
392 &gEfiBdsArchProtocolGuid, &gBdsProtocol,
393 NULL
394 );
395 ASSERT_EFI_ERROR (Status);
396
397 return Status;
398 }