ArmPlatformPkg/Bds: Get User inputs in Unicode
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootOption.c
CommitLineData
ea46ebbe 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
17extern EFI_HANDLE mImageHandle;\r
18\r
19EFI_STATUS\r
20BootOptionStart (\r
21 IN BDS_LOAD_OPTION *BootOption\r
22 )\r
23{\r
c60ea9a8 24 EFI_STATUS Status;\r
25 EFI_DEVICE_PATH* FdtDevicePath;\r
26 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;\r
27 UINT32 LoaderType;\r
ea46ebbe 28\r
29 Status = EFI_UNSUPPORTED;\r
c60ea9a8 30 LoaderType = ReadUnaligned32 (&BootOption->OptionalData->LoaderType);\r
ea46ebbe 31\r
c60ea9a8 32 if (LoaderType == BDS_LOADER_EFI_APPLICATION) {\r
ea46ebbe 33 // Need to connect every drivers to ensure no dependencies are missing for the application\r
34 BdsConnectAllDrivers();\r
35\r
36 Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList);\r
c60ea9a8 37 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) {\r
656416bc 38 Status = BdsBootLinux (BootOption->FilePathList, \r
39 (EFI_DEVICE_PATH_PROTOCOL*)ReadUnaligned32((UINT32*)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)),\r
40 BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine,\r
41 NULL);\r
c60ea9a8 42 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) {\r
ea46ebbe 43 // Convert the FDT path into a Device Path\r
44 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);\r
45 ASSERT_EFI_ERROR(Status);\r
46 FdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));\r
47\r
656416bc 48 Status = BdsBootLinux (BootOption->FilePathList,\r
49 NULL,\r
50 NULL,\r
51 FdtDevicePath);\r
52\r
ea46ebbe 53 FreePool(FdtDevicePath);\r
54 }\r
55\r
56 return Status;\r
57}\r
58\r
59EFI_STATUS\r
60BootOptionParseLoadOption (\r
61 IN EFI_LOAD_OPTION EfiLoadOption,\r
62 IN UINTN EfiLoadOptionSize,\r
63 OUT BDS_LOAD_OPTION **BdsLoadOption\r
64 )\r
65{\r
66 BDS_LOAD_OPTION *LoadOption;\r
67 UINTN FilePathListLength;\r
68 UINTN DescriptionLength;\r
69\r
70 if (EfiLoadOption == NULL) {\r
71 return EFI_INVALID_PARAMETER;\r
72 }\r
73\r
74 if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) {\r
75 return EFI_BAD_BUFFER_SIZE;\r
76 }\r
77\r
78 LoadOption = (BDS_LOAD_OPTION*)AllocatePool(sizeof(BDS_LOAD_OPTION));\r
79 if (LoadOption == NULL) {\r
80 return EFI_OUT_OF_RESOURCES;\r
81 }\r
82\r
83 LoadOption->LoadOption = EfiLoadOption;\r
84 LoadOption->LoadOptionSize = EfiLoadOptionSize;\r
85\r
86 LoadOption->Attributes = *(UINT32*)EfiLoadOption;\r
87 FilePathListLength = *(UINT16*)(EfiLoadOption + sizeof(UINT32));\r
88 LoadOption->Description = (CHAR16*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16));\r
89 DescriptionLength = StrSize (LoadOption->Description);\r
90 LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);\r
91\r
92 if ((UINTN)((UINT8*)LoadOption->FilePathList + FilePathListLength - EfiLoadOption) == EfiLoadOptionSize) {\r
93 LoadOption->OptionalData = NULL;\r
94 } else {\r
95 LoadOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)((UINT8*)LoadOption->FilePathList + FilePathListLength);\r
96 }\r
97\r
98 *BdsLoadOption = LoadOption;\r
99 return EFI_SUCCESS;\r
100}\r
101\r
102EFI_STATUS\r
103BootOptionFromLoadOptionVariable (\r
104 IN UINT16 LoadOptionIndex,\r
105 OUT BDS_LOAD_OPTION **BdsLoadOption\r
106 )\r
107{\r
108 EFI_STATUS Status;\r
109 CHAR16 BootVariableName[9];\r
110 EFI_LOAD_OPTION EfiLoadOption;\r
111 UINTN EfiLoadOptionSize;\r
112\r
113 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex);\r
114\r
115 Status = GetEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption);\r
116 if (!EFI_ERROR(Status)) {\r
117 Status = BootOptionParseLoadOption (EfiLoadOption,EfiLoadOptionSize,BdsLoadOption);\r
118 if (!EFI_ERROR(Status)) {\r
119 (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex;\r
120 }\r
121 }\r
122\r
123 return Status;\r
124}\r
125\r
126EFI_STATUS\r
127BootOptionList (\r
128 IN OUT LIST_ENTRY *BootOptionList\r
129 )\r
130{\r
131 EFI_STATUS Status;\r
132 UINTN Index;\r
133 UINT16 *BootOrder;\r
134 UINTN BootOrderSize;\r
135 BDS_LOAD_OPTION *BdsLoadOption;\r
136\r
137 InitializeListHead (BootOptionList);\r
138\r
139 // Get the Boot Option Order from the environment variable\r
140 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
141 if (EFI_ERROR(Status)) {\r
142 return Status;\r
143 }\r
144\r
145 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
146 Status = BootOptionFromLoadOptionVariable (BootOrder[Index],&BdsLoadOption);\r
147 if (!EFI_ERROR(Status)) {\r
148 InsertTailList (BootOptionList,&BdsLoadOption->Link);\r
149 }\r
150 }\r
151\r
152 return EFI_SUCCESS;\r
153}\r
154\r
155UINT16\r
156BootOptionAllocateBootIndex (\r
157 VOID\r
158 )\r
159{\r
160 EFI_STATUS Status;\r
161 UINTN Index;\r
162 UINT32 BootIndex;\r
163 UINT16 *BootOrder;\r
164 UINTN BootOrderSize;\r
165 BOOLEAN Found;\r
166\r
167 // Get the Boot Option Order from the environment variable\r
168 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
169 if (!EFI_ERROR(Status)) {\r
170 for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) {\r
171 Found = FALSE;\r
172 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
173 if (BootOrder[Index] == BootIndex) {\r
174 Found = TRUE;\r
175 break;\r
176 }\r
177 }\r
178 if (!Found) {\r
179 return BootIndex;\r
180 }\r
181 }\r
182 }\r
183 // Return the first index\r
184 return 0;\r
185}\r
186\r
187STATIC\r
188EFI_STATUS\r
189BootOptionSetFields (\r
c60ea9a8 190 IN BDS_LOAD_OPTION* BootOption,\r
191 IN UINT32 Attributes,\r
192 IN CHAR16* BootDescription,\r
193 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
656416bc 194 IN BDS_LOADER_TYPE BootType,\r
195 IN BDS_LOADER_ARGUMENTS* BootArguments\r
ea46ebbe 196 )\r
197{\r
198 EFI_LOAD_OPTION EfiLoadOption;\r
199 UINTN EfiLoadOptionSize;\r
200 UINTN BootDescriptionSize;\r
201 UINTN BootOptionalDataSize;\r
202 UINT16 FilePathListLength;\r
656416bc 203 UINT16 InitrdPathListLength;\r
ea46ebbe 204 EFI_DEVICE_PATH_PROTOCOL* DevicePathNode;\r
656416bc 205 EFI_DEVICE_PATH_PROTOCOL* InitrdPathNode;\r
ea46ebbe 206 UINTN NodeLength;\r
207 UINT8* EfiLoadOptionPtr;\r
656416bc 208 UINT8 *InitrdPathListPtr;\r
ea46ebbe 209\r
210 // If we are overwriting an existent Boot Option then we have to free previously allocated memory\r
211 if (BootOption->LoadOption) {\r
212 FreePool(BootOption->LoadOption);\r
213 }\r
214\r
215 BootDescriptionSize = StrSize(BootDescription);\r
656416bc 216 BootOptionalDataSize = sizeof(BDS_LOADER_TYPE) + (BootType == BDS_LOADER_KERNEL_LINUX_ATAG ? \r
217 (sizeof(UINT16) + sizeof(EFI_DEVICE_PATH_PROTOCOL*) + BOOT_DEVICE_OPTION_MAX + 1)\r
218 : 0);\r
ea46ebbe 219\r
220 // Compute the size of the FilePath list\r
221 FilePathListLength = 0;\r
222 DevicePathNode = DevicePath;\r
223 while (!IsDevicePathEndType (DevicePathNode)) {\r
224 FilePathListLength += DevicePathNodeLength (DevicePathNode);\r
225 DevicePathNode = NextDevicePathNode (DevicePathNode);\r
226 }\r
227 // Add the length of the DevicePath EndType\r
228 FilePathListLength += DevicePathNodeLength (DevicePathNode);\r
229\r
656416bc 230 InitrdPathListLength = 0;\r
231 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG && BootArguments->LinuxAtagArguments.InitrdPathList != NULL) {\r
232 // Compute the size of the InitrdPath list \r
233 InitrdPathNode = BootArguments->LinuxAtagArguments.InitrdPathList;\r
234 while (!IsDevicePathEndType (InitrdPathNode)) {\r
235 InitrdPathListLength += DevicePathNodeLength (InitrdPathNode);\r
236 InitrdPathNode = NextDevicePathNode (InitrdPathNode);\r
237 }\r
238 // Add the length of the DevicePath EndType\r
239 InitrdPathListLength += DevicePathNodeLength (InitrdPathNode);\r
240 }\r
241\r
ea46ebbe 242 // Allocate the memory for the EFI Load Option\r
243 EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + BootOptionalDataSize;\r
244 EfiLoadOption = (EFI_LOAD_OPTION)AllocatePool(EfiLoadOptionSize);\r
245 EfiLoadOptionPtr = EfiLoadOption;\r
246\r
247 //\r
248 // Populate the EFI Load Option and BDS Boot Option structures\r
249 //\r
250\r
251 // Attributes fields\r
252 BootOption->Attributes = Attributes;\r
253 *(UINT32*)EfiLoadOptionPtr = Attributes;\r
254 EfiLoadOptionPtr += sizeof(UINT32);\r
255\r
256 // FilePath List fields\r
257 BootOption->FilePathListLength = FilePathListLength;\r
258 *(UINT16*)EfiLoadOptionPtr = FilePathListLength;\r
259 EfiLoadOptionPtr += sizeof(UINT16);\r
260\r
261 // Boot description fields\r
262 BootOption->Description = (CHAR16*)EfiLoadOptionPtr;\r
263 CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize);\r
264 EfiLoadOptionPtr += BootDescriptionSize;\r
265\r
266 // File path fields\r
267 BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr;\r
268 DevicePathNode = DevicePath;\r
269 while (!IsDevicePathEndType (DevicePathNode)) {\r
270 NodeLength = DevicePathNodeLength(DevicePathNode);\r
271 CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength);\r
272 EfiLoadOptionPtr += NodeLength;\r
273 DevicePathNode = NextDevicePathNode (DevicePathNode);\r
274 }\r
275\r
276 // Set the End Device Path Type\r
277 SetDevicePathEndNode (EfiLoadOptionPtr);\r
278 EfiLoadOptionPtr = (UINT8 *)EfiLoadOptionPtr + sizeof(EFI_DEVICE_PATH);\r
279\r
280 // Optional Data fields, Do unaligned writes\r
281 WriteUnaligned32 ((UINT32 *)EfiLoadOptionPtr, BootType);\r
282\r
ea46ebbe 283 BootOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)EfiLoadOptionPtr;\r
284\r
656416bc 285 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG) {\r
286 CopyMem (&((BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments.LinuxAtagArguments.CmdLine,\r
287 BootArguments->LinuxAtagArguments.CmdLine,\r
288 AsciiStrSize(BootArguments->LinuxAtagArguments.CmdLine));\r
289\r
290 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathListLength), InitrdPathListLength);\r
291\r
292 if ((EFI_DEVICE_PATH_PROTOCOL*)ReadUnaligned32((UINT32 *)&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList) != NULL\r
293 && BootArguments->LinuxAtagArguments.InitrdPathList != NULL) {\r
294 InitrdPathListPtr = AllocatePool(InitrdPathListLength);\r
295 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList), (UINT32)InitrdPathListPtr);\r
296 InitrdPathNode = BootArguments->LinuxAtagArguments.InitrdPathList;\r
297\r
298 while (!IsDevicePathEndType (InitrdPathNode)) {\r
299 NodeLength = DevicePathNodeLength(InitrdPathNode);\r
300 CopyMem (InitrdPathListPtr, InitrdPathNode, NodeLength);\r
301 InitrdPathListPtr += NodeLength;\r
302 InitrdPathNode = NextDevicePathNode (InitrdPathNode);\r
303 }\r
304\r
305 // Set the End Device Path Type\r
306 SetDevicePathEndNode (InitrdPathListPtr);\r
307 } else {\r
308 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList), (UINT32)NULL);\r
309 }\r
310 }\r
6bab33c7 311 // If this function is called at the creation of the Boot Device entry (not at the update) the\r
312 // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry\r
313 if (BootOption->LoadOptionSize == 0) {\r
314 BootOption->LoadOptionIndex = BootOptionAllocateBootIndex();\r
315 }\r
316\r
ea46ebbe 317 // Fill the EFI Load option fields\r
ea46ebbe 318 BootOption->LoadOption = EfiLoadOption;\r
319 BootOption->LoadOptionSize = EfiLoadOptionSize;\r
320\r
321 return EFI_SUCCESS;\r
322}\r
323\r
324EFI_STATUS\r
325BootOptionCreate (\r
c60ea9a8 326 IN UINT32 Attributes,\r
327 IN CHAR16* BootDescription,\r
ea46ebbe 328 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
c60ea9a8 329 IN BDS_LOADER_TYPE BootType,\r
656416bc 330 IN BDS_LOADER_ARGUMENTS* BootArguments,\r
c60ea9a8 331 OUT BDS_LOAD_OPTION **BdsLoadOption\r
ea46ebbe 332 )\r
333{\r
c60ea9a8 334 EFI_STATUS Status;\r
335 BDS_LOAD_OPTION *BootOption;\r
336 CHAR16 BootVariableName[9];\r
ea46ebbe 337 UINT16 *BootOrder;\r
338 UINTN BootOrderSize;\r
339\r
340 //\r
341 // Allocate and fill the memory for the BDS Load Option structure\r
342 //\r
343 BootOption = (BDS_LOAD_OPTION*)AllocateZeroPool(sizeof(BDS_LOAD_OPTION));\r
344\r
345 InitializeListHead (&BootOption->Link);\r
346 BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);\r
347\r
348 //\r
349 // Set the related environment variables\r
350 //\r
351\r
352 // Create Boot#### environment variable\r
353 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);\r
354 Status = gRT->SetVariable (\r
355 BootVariableName,\r
356 &gEfiGlobalVariableGuid,\r
357 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
358 BootOption->LoadOptionSize,\r
359 BootOption->LoadOption\r
360 );\r
361\r
362 // Add the new Boot Index to the list\r
363 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
364 if (!EFI_ERROR(Status)) {\r
365 BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);\r
366 // Add the new index at the end\r
367 BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex;\r
368 BootOrderSize += sizeof(UINT16);\r
369 } else {\r
370 // BootOrder does not exist. Create it\r
371 BootOrderSize = sizeof(UINT16);\r
372 BootOrder = &(BootOption->LoadOptionIndex);\r
373 }\r
374\r
375 // Update (or Create) the BootOrder environment variable\r
376 Status = gRT->SetVariable (\r
377 L"BootOrder",\r
378 &gEfiGlobalVariableGuid,\r
379 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
380 BootOrderSize,\r
381 BootOrder\r
382 );\r
383\r
384 *BdsLoadOption = BootOption;\r
385 return Status;\r
386}\r
387\r
388EFI_STATUS\r
389BootOptionUpdate (\r
390 IN BDS_LOAD_OPTION *BdsLoadOption,\r
391 IN UINT32 Attributes,\r
392 IN CHAR16* BootDescription,\r
393 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
394 IN BDS_LOADER_TYPE BootType,\r
656416bc 395 IN BDS_LOADER_ARGUMENTS* BootArguments\r
ea46ebbe 396 )\r
397{\r
398 EFI_STATUS Status;\r
ea46ebbe 399 CHAR16 BootVariableName[9];\r
400\r
401 // Update the BDS Load Option structure\r
402 BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);\r
403\r
404 // Update the related environment variables\r
6bab33c7 405 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);\r
406\r
ea46ebbe 407 Status = gRT->SetVariable (\r
408 BootVariableName,\r
409 &gEfiGlobalVariableGuid,\r
410 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
6bab33c7 411 BdsLoadOption->LoadOptionSize,\r
412 BdsLoadOption->LoadOption\r
ea46ebbe 413 );\r
414\r
415 return Status;\r
416}\r
417\r
418EFI_STATUS\r
419BootOptionDelete (\r
420 IN BDS_LOAD_OPTION *BootOption\r
421 )\r
422{\r
423 UINTN Index;\r
424 UINTN BootOrderSize;\r
425 UINT16* BootOrder;\r
426 UINTN BootOrderCount;\r
427 EFI_STATUS Status;\r
428\r
429 // If the Boot Optiono was attached to a list remove it\r
430 if (!IsListEmpty (&BootOption->Link)) {\r
431 // Remove the entry from the list\r
432 RemoveEntryList (&BootOption->Link);\r
433 }\r
434\r
435 // Remove the entry from the BootOrder environment variable\r
436 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
437 if (!EFI_ERROR(Status)) {\r
438 BootOrderCount = BootOrderSize / sizeof(UINT16);\r
439\r
440 // Find the index of the removed entry\r
441 for (Index = 0; Index < BootOrderCount; Index++) {\r
442 if (BootOrder[Index] == BootOption->LoadOptionIndex) {\r
443 // If it the last entry we do not need to rearrange the BootOrder list\r
444 if (Index + 1 != BootOrderCount) {\r
445 CopyMem (&BootOrder[Index],&BootOrder[Index+1], BootOrderCount - (Index + 1));\r
446 }\r
447 break;\r
448 }\r
449 }\r
450\r
451 // Update the BootOrder environment variable\r
452 Status = gRT->SetVariable (\r
453 L"BootOrder",\r
454 &gEfiGlobalVariableGuid,\r
455 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
456 BootOrderSize - sizeof(UINT16),\r
457 BootOrder\r
458 );\r
459 }\r
460\r
461 return EFI_SUCCESS;\r
462}\r