]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/Bds/Bds.c
ArmPlatformPkg: Fix initialization of the Global Variable when stack pointer higher...
[mirror_edk2.git] / ArmPlatformPkg / Bds / Bds.c
CommitLineData
705b0c03 1/** @file\r
2*\r
3* Copyright (c) 2011, ARM Limited. All rights reserved.\r
4* \r
5* This program and the accompanying materials \r
6* are licensed and made available under the terms and conditions of the BSD License \r
7* which accompanies this distribution. The full text of the license may be found at \r
8* http://opensource.org/licenses/bsd-license.php \r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
12*\r
13**/\r
14\r
15#include "BdsInternal.h"\r
16\r
17#include <Library/PcdLib.h>\r
18#include <Library/PerformanceLib.h>\r
19\r
20#include <Protocol/Bds.h>\r
21\r
22#define EFI_SET_TIMER_TO_SECOND 10000000\r
23\r
24EFI_HANDLE mImageHandle;\r
25\r
26STATIC\r
27EFI_STATUS\r
28GetConsoleDevicePathFromVariable (\r
29 IN CHAR16* ConsoleVarName,\r
30 IN CHAR16* DefaultConsolePaths,\r
31 OUT EFI_DEVICE_PATH** DevicePaths\r
32 )\r
33{\r
34 EFI_STATUS Status;\r
35 UINTN Size;\r
36 EFI_DEVICE_PATH_PROTOCOL* DevicePathInstances;\r
37 EFI_DEVICE_PATH_PROTOCOL* DevicePathInstance;\r
38 CHAR16* DevicePathStr;\r
39 CHAR16* NextDevicePathStr;\r
40 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol;\r
41\r
42 Status = GetEnvironmentVariable (ConsoleVarName, NULL, NULL, (VOID**)&DevicePathInstances);\r
43 if (EFI_ERROR(Status)) {\r
44 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);\r
45 ASSERT_EFI_ERROR(Status);\r
46\r
47 DevicePathInstances = NULL;\r
48\r
49 // Extract the Device Path instances from the multi-device path string\r
50 while ((DefaultConsolePaths != NULL) && (DefaultConsolePaths[0] != L'\0')) {\r
51 NextDevicePathStr = StrStr (DefaultConsolePaths, L";");\r
52 if (NextDevicePathStr == NULL) {\r
53 DevicePathStr = DefaultConsolePaths;\r
54 DefaultConsolePaths = NULL;\r
55 } else {\r
56 DevicePathStr = (CHAR16*)AllocateCopyPool ((NextDevicePathStr - DefaultConsolePaths + 1) * sizeof(CHAR16), DefaultConsolePaths);\r
57 *(DevicePathStr + (NextDevicePathStr - DefaultConsolePaths)) = L'\0';\r
58 DefaultConsolePaths = NextDevicePathStr;\r
59 if (DefaultConsolePaths[0] == L';') {\r
60 DefaultConsolePaths++;\r
61 }\r
62 }\r
63\r
64 DevicePathInstance = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath (DevicePathStr);\r
65 ASSERT(DevicePathInstance != NULL);\r
66 DevicePathInstances = AppendDevicePathInstance (DevicePathInstances, DevicePathInstance);\r
67\r
68 if (NextDevicePathStr != NULL) {\r
69 FreePool (DevicePathStr);\r
70 }\r
71 FreePool (DevicePathInstance);\r
72 }\r
73\r
74 // Set the environment variable with this device path multi-instances\r
75 Size = GetDevicePathSize (DevicePathInstances);\r
76 if (Size > 0) {\r
77 gRT->SetVariable (\r
78 ConsoleVarName,\r
79 &gEfiGlobalVariableGuid,\r
80 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
81 Size,\r
82 DevicePathInstances\r
83 );\r
84 } else {\r
85 Status = EFI_INVALID_PARAMETER;\r
86 }\r
87 }\r
88\r
89 if (!EFI_ERROR(Status)) {\r
90 *DevicePaths = DevicePathInstances;\r
91 }\r
92 return EFI_SUCCESS;\r
93}\r
94\r
95STATIC\r
96EFI_STATUS\r
97InitializeConsolePipe (\r
98 IN EFI_DEVICE_PATH *ConsoleDevicePaths,\r
99 IN EFI_GUID *Protocol,\r
100 OUT EFI_HANDLE *Handle,\r
101 OUT VOID* *Interface\r
102 )\r
103{\r
104 EFI_STATUS Status;\r
105 UINTN Size;\r
106 UINTN NoHandles;\r
107 EFI_HANDLE *Buffer;\r
108 EFI_DEVICE_PATH_PROTOCOL* DevicePath;\r
109\r
110 // Connect all the Device Path Consoles\r
111 do {\r
112 DevicePath = GetNextDevicePathInstance (&ConsoleDevicePaths, &Size);\r
113\r
114 Status = BdsConnectDevicePath (DevicePath, Handle, NULL);\r
115 DEBUG_CODE_BEGIN();\r
116 if (EFI_ERROR(Status)) {\r
117 // We convert back to the text representation of the device Path\r
118 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
119 CHAR16* DevicePathTxt;\r
120 EFI_STATUS Status;\r
121 \r
122 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
123 if (!EFI_ERROR(Status)) {\r
124 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (DevicePath, TRUE, TRUE);\r
125\r
126 DEBUG((EFI_D_ERROR,"Fail to start the console with the Device Path '%s'. (Error '%r')\n", DevicePathTxt, Status));\r
127\r
128 FreePool (DevicePathTxt);\r
129 }\r
130 }\r
131 DEBUG_CODE_END();\r
132\r
133 // If the console splitter driver is not supported by the platform then use the first Device Path\r
134 // instance for the console interface.\r
135 if (!EFI_ERROR(Status) && (*Interface == NULL)) {\r
136 Status = gBS->HandleProtocol (*Handle, Protocol, Interface);\r
137 }\r
138 } while (ConsoleDevicePaths != NULL);\r
139\r
140 // No Device Path has been defined for this console interface. We take the first protocol implementation\r
141 if (*Interface == NULL) {\r
142 Status = gBS->LocateHandleBuffer (ByProtocol, Protocol, NULL, &NoHandles, &Buffer);\r
143 if (EFI_ERROR (Status)) {\r
144 BdsConnectAllDrivers();\r
145 Status = gBS->LocateHandleBuffer (ByProtocol, Protocol, NULL, &NoHandles, &Buffer);\r
146 }\r
147\r
148 if (!EFI_ERROR(Status)) {\r
149 *Handle = Buffer[0];\r
150 Status = gBS->HandleProtocol (*Handle, Protocol, Interface);\r
151 ASSERT_EFI_ERROR(Status);\r
152 }\r
153 FreePool (Buffer);\r
154 } else {\r
155 Status = EFI_SUCCESS;\r
156 }\r
157\r
158 return Status;\r
159}\r
160\r
161EFI_STATUS\r
162InitializeConsole (\r
163 VOID\r
164 )\r
165{\r
166 EFI_STATUS Status;\r
167 EFI_DEVICE_PATH* ConOutDevicePaths;\r
168 EFI_DEVICE_PATH* ConInDevicePaths;\r
169 EFI_DEVICE_PATH* ConErrDevicePaths;\r
170\r
171 // By getting the Console Device Paths from the environment variables before initializing the console pipe, we\r
172 // create the 3 environment variables (ConIn, ConOut, ConErr) that allows to initialize all the console interface\r
173 // of newly installed console drivers\r
174 Status = GetConsoleDevicePathFromVariable (L"ConOut", (CHAR16*)PcdGetPtr(PcdDefaultConOutPaths),&ConOutDevicePaths);\r
175 ASSERT_EFI_ERROR (Status);\r
176 Status = GetConsoleDevicePathFromVariable (L"ConIn", (CHAR16*)PcdGetPtr(PcdDefaultConInPaths),&ConInDevicePaths);\r
177 ASSERT_EFI_ERROR (Status);\r
178 Status = GetConsoleDevicePathFromVariable (L"ConErr", (CHAR16*)PcdGetPtr(PcdDefaultConOutPaths),&ConErrDevicePaths);\r
179 ASSERT_EFI_ERROR (Status);\r
180\r
181 // Initialize the Consoles\r
182 Status = InitializeConsolePipe (ConOutDevicePaths, &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **)&gST->ConOut);\r
183 ASSERT_EFI_ERROR (Status);\r
184 Status = InitializeConsolePipe (ConInDevicePaths, &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **)&gST->ConIn);\r
185 ASSERT_EFI_ERROR (Status);\r
186 Status = InitializeConsolePipe (ConErrDevicePaths, &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **)&gST->StdErr);\r
187 if (EFI_ERROR(Status)) {\r
188 // In case of error, we reuse the console output for the error output\r
189 gST->StandardErrorHandle = gST->ConsoleOutHandle;\r
190 gST->StdErr = gST->ConOut;\r
191 }\r
192\r
193 return EFI_SUCCESS;\r
194}\r
195\r
196EFI_STATUS\r
197DefineDefaultBootEntries (\r
198 VOID\r
199 )\r
200{\r
201 BDS_LOAD_OPTION* BdsLoadOption;\r
202 UINTN Size;\r
203 EFI_STATUS Status;\r
204 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;\r
205 EFI_DEVICE_PATH* BootDevicePath;\r
206 ARM_BDS_LOADER_ARGUMENTS* BootArguments;\r
207 ARM_BDS_LOADER_TYPE BootType;\r
208 EFI_DEVICE_PATH* InitrdPath;\r
209 UINTN CmdLineSize;\r
210 UINTN InitrdSize;\r
211\r
212 //\r
213 // If Boot Order does not exist then create a default entry\r
214 //\r
215 Size = 0;\r
216 Status = gRT->GetVariable (L"BootOrder", &gEfiGlobalVariableGuid, NULL, &Size, NULL);\r
217 if (Status == EFI_NOT_FOUND) {\r
218 if ((PcdGetPtr(PcdDefaultBootDevicePath) == NULL) || (StrLen ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath)) == 0)) {\r
219 return EFI_UNSUPPORTED;\r
220 }\r
221\r
222 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);\r
223 if (EFI_ERROR(Status)) {\r
224 // You must provide an implementation of DevicePathFromTextProtocol in your firmware (eg: DevicePathDxe)\r
225 DEBUG((EFI_D_ERROR,"Error: Bds requires DevicePathFromTextProtocol\n"));\r
226 return Status;\r
227 }\r
228 BootDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath));\r
229\r
230 DEBUG_CODE_BEGIN();\r
231 // We convert back to the text representation of the device Path to see if the initial text is correct\r
232 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;\r
233 CHAR16* DevicePathTxt;\r
234\r
235 Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);\r
236 ASSERT_EFI_ERROR(Status);\r
237 DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (BootDevicePath, TRUE, TRUE);\r
238\r
239 ASSERT (StrCmp ((CHAR16*)PcdGetPtr(PcdDefaultBootDevicePath), DevicePathTxt) == 0);\r
240\r
241 FreePool (DevicePathTxt);\r
242 DEBUG_CODE_END();\r
243\r
244 // Create the entry is the Default values are correct\r
245 if (BootDevicePath != NULL) {\r
246 BootType = (ARM_BDS_LOADER_TYPE)PcdGet32 (PcdDefaultBootType);\r
247\r
248 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {\r
249 CmdLineSize = AsciiStrSize ((CHAR8*)PcdGetPtr(PcdDefaultBootArgument));\r
250 InitrdPath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultBootInitrdPath));\r
251 InitrdSize = GetDevicePathSize (InitrdPath);\r
252\r
253 BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize);\r
254 BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;\r
255 BootArguments->LinuxArguments.InitrdSize = InitrdSize;\r
256\r
257 CopyMem ((VOID*)(BootArguments + 1), (CHAR8*)PcdGetPtr(PcdDefaultBootArgument), CmdLineSize);\r
258 CopyMem ((VOID*)((UINTN)(BootArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);\r
259 } else {\r
260 BootArguments = NULL;\r
261 }\r
262\r
263 BootOptionCreate (LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,\r
264 (CHAR16*)PcdGetPtr(PcdDefaultBootDescription),\r
265 BootDevicePath,\r
266 BootType,\r
267 BootArguments,\r
268 &BdsLoadOption\r
269 );\r
270 FreePool (BdsLoadOption);\r
271 } else {\r
272 Status = EFI_UNSUPPORTED;\r
273 }\r
274 }\r
275\r
276 return EFI_SUCCESS;\r
277}\r
278\r
279EFI_STATUS\r
280StartDefaultBootOnTimeout (\r
281 VOID\r
282 )\r
283{\r
284 UINTN Size;\r
285 UINT16 Timeout;\r
286 UINT16 *TimeoutPtr;\r
287 EFI_EVENT WaitList[2];\r
288 UINTN WaitIndex;\r
289 UINT16 *BootOrder;\r
290 UINTN BootOrderSize;\r
291 UINTN Index;\r
292 CHAR16 BootVariableName[9];\r
293 EFI_STATUS Status;\r
294 EFI_INPUT_KEY Key;\r
295\r
296 Size = sizeof(UINT16);\r
297 Timeout = (UINT16)PcdGet16 (PcdPlatformBootTimeOut);\r
298 TimeoutPtr = &Timeout;\r
299 GetEnvironmentVariable (L"Timeout", &Timeout, &Size, (VOID**)&TimeoutPtr);\r
300\r
301 if (Timeout != 0xFFFF) {\r
302 if (Timeout > 0) {\r
303 // Create the waiting events (keystroke and 1sec timer)\r
304 gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &WaitList[0]);\r
305 gBS->SetTimer (WaitList[0], TimerPeriodic, EFI_SET_TIMER_TO_SECOND);\r
306 WaitList[1] = gST->ConIn->WaitForKey;\r
307\r
308 // Start the timer\r
309 WaitIndex = 0;\r
310 Print(L"The default boot selection will start in ");\r
311 while ((Timeout > 0) && (WaitIndex == 0)) {\r
312 Print(L"%3d seconds",Timeout);\r
313 gBS->WaitForEvent (2, WaitList, &WaitIndex);\r
314 if (WaitIndex == 0) {\r
315 Print(L"\b\b\b\b\b\b\b\b\b\b\b");\r
316 Timeout--;\r
317 }\r
318 }\r
319 // Discard key in the buffer\r
320 do {\r
321 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
322 } while(!EFI_ERROR(Status));\r
323 gBS->CloseEvent (WaitList[0]);\r
324 Print(L"\n\r");\r
325 }\r
326\r
327 // In case of Timeout we start the default boot selection\r
328 if (Timeout == 0) {\r
329 // Get the Boot Option Order from the environment variable (a default value should have been created)\r
330 GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
331\r
332 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
333 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOrder[Index]);\r
334 Status = BdsStartBootOption (BootVariableName);\r
335 if(!EFI_ERROR(Status)){\r
336 // Boot option returned successfully, hence don't need to start next boot option\r
337 break;\r
338 }\r
339 // In case of success, we should not return from this call.\r
340 }\r
341 }\r
342 }\r
343 return EFI_SUCCESS;\r
344}\r
345\r
346/**\r
347 This function uses policy data from the platform to determine what operating \r
348 system or system utility should be loaded and invoked. This function call \r
349 also optionally make the use of user input to determine the operating system \r
350 or system utility to be loaded and invoked. When the DXE Core has dispatched \r
351 all the drivers on the dispatch queue, this function is called. This \r
352 function will attempt to connect the boot devices required to load and invoke \r
353 the selected operating system or system utility. During this process, \r
354 additional firmware volumes may be discovered that may contain addition DXE \r
355 drivers that can be dispatched by the DXE Core. If a boot device cannot be \r
356 fully connected, this function calls the DXE Service Dispatch() to allow the \r
357 DXE drivers from any newly discovered firmware volumes to be dispatched. \r
358 Then the boot device connection can be attempted again. If the same boot \r
359 device connection operation fails twice in a row, then that boot device has \r
360 failed, and should be skipped. This function should never return.\r
361\r
362 @param This The EFI_BDS_ARCH_PROTOCOL instance.\r
363\r
364 @return None.\r
365\r
366**/\r
367VOID\r
368EFIAPI\r
369BdsEntry (\r
370 IN EFI_BDS_ARCH_PROTOCOL *This\r
371 )\r
372{\r
373 UINTN Size;\r
374 EFI_STATUS Status;\r
375\r
376 PERF_END (NULL, "DXE", NULL, 0);\r
377\r
378 //\r
379 // Declare the Firmware Vendor\r
380 //\r
381 if (FixedPcdGetPtr(PcdFirmwareVendor) != NULL) {\r
382 Size = 0x100;\r
383 gST->FirmwareVendor = AllocateRuntimePool (Size);\r
384 ASSERT (gST->FirmwareVendor != NULL);\r
385 UnicodeSPrint (gST->FirmwareVendor, Size, L"%a EFI %a %a", PcdGetPtr(PcdFirmwareVendor), __DATE__, __TIME__);\r
386 }\r
387\r
388 // If BootNext environment variable is defined then we just load it !\r
389 Status = BdsStartBootOption (L"BootNext");\r
390 if (Status != EFI_NOT_FOUND) {\r
391 // BootNext has not been succeeded launched\r
392 if (EFI_ERROR(Status)) {\r
393 Print(L"Fail to start BootNext.\n");\r
394 }\r
395\r
396 // Delete the BootNext environment variable\r
397 gRT->SetVariable (L"BootNext", &gEfiGlobalVariableGuid,\r
398 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
399 0, NULL);\r
400 }\r
401\r
402 // If Boot Order does not exist then create a default entry\r
403 DefineDefaultBootEntries ();\r
404\r
405 // Now we need to setup the EFI System Table with information about the console devices.\r
406 InitializeConsole ();\r
407\r
408 // Timer before initiating the default boot selection\r
409 StartDefaultBootOnTimeout ();\r
410\r
411 // Start the Boot Menu\r
412 Status = BootMenuMain ();\r
413 ASSERT_EFI_ERROR (Status);\r
414\r
415}\r
416\r
417EFI_BDS_ARCH_PROTOCOL gBdsProtocol = {\r
418 BdsEntry,\r
419};\r
420\r
421EFI_STATUS\r
422EFIAPI\r
423BdsInitialize (\r
424 IN EFI_HANDLE ImageHandle,\r
425 IN EFI_SYSTEM_TABLE *SystemTable\r
426 )\r
427{\r
428 EFI_STATUS Status;\r
429\r
430 mImageHandle = ImageHandle;\r
431\r
432 Status = gBS->InstallMultipleProtocolInterfaces (\r
433 &ImageHandle,\r
434 &gEfiBdsArchProtocolGuid, &gBdsProtocol,\r
435 NULL\r
436 );\r
437 ASSERT_EFI_ERROR (Status);\r
438\r
439 return Status;\r
440}\r