ArmPlatformPkg/Bds: Add support for 'BootCurrent'
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootOption.c
CommitLineData
ea46ebbe 1/** @file\r
2*\r
b148591a 3* Copyright (c) 2011-2012, ARM Limited. All rights reserved.\r
ea46ebbe 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
c60ea9a8 25 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;\r
26 UINT32 LoaderType;\r
2ccfb71e 27 ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;\r
28 ARM_BDS_LINUX_ARGUMENTS* LinuxArguments;\r
29 EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath;\r
30 EFI_DEVICE_PATH_PROTOCOL* DefaultFdtDevicePath;\r
31 UINTN FdtDevicePathSize;\r
32 UINTN CmdLineSize;\r
33 UINTN InitrdSize;\r
34 EFI_DEVICE_PATH* Initrd;\r
91213ad3 35 UINT16 LoadOptionIndexSize;\r
2ccfb71e 36\r
a6e97d28 37 if (IS_ARM_BDS_BOOTENTRY (BootOption)) {\r
2ccfb71e 38 Status = EFI_UNSUPPORTED;\r
39 OptionalData = BootOption->OptionalData;\r
40 LoaderType = ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);\r
41\r
42 if (LoaderType == BDS_LOADER_EFI_APPLICATION) {\r
43 // Need to connect every drivers to ensure no dependencies are missing for the application\r
44 BdsConnectAllDrivers();\r
45\r
46 Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList, 0, NULL);\r
47 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) {\r
48 LinuxArguments = &(OptionalData->Arguments.LinuxArguments);\r
49 CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);\r
50 InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);\r
51\r
52 if (InitrdSize > 0) {\r
53 Initrd = GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize));\r
54 } else {\r
55 Initrd = NULL;\r
56 }\r
ea46ebbe 57\r
76d17c31 58 Status = BdsBootLinuxAtag (BootOption->FilePathList,\r
2ccfb71e 59 Initrd, // Initrd\r
76d17c31 60 (CHAR8*)(LinuxArguments + 1)); // CmdLine\r
2ccfb71e 61 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) {\r
62 LinuxArguments = &(OptionalData->Arguments.LinuxArguments);\r
63 CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);\r
64 InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);\r
65\r
66 if (InitrdSize > 0) {\r
67 Initrd = GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize));\r
68 } else {\r
69 Initrd = NULL;\r
70 }\r
71\r
72 // Get the default FDT device path\r
73 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);\r
74 ASSERT_EFI_ERROR(Status);\r
75 DefaultFdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));\r
76\r
77 // Get the FDT device path\r
78 FdtDevicePathSize = GetDevicePathSize (DefaultFdtDevicePath);\r
c0658bd6 79 Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", DefaultFdtDevicePath, &FdtDevicePathSize, (VOID **)&FdtDevicePath);\r
2ccfb71e 80 ASSERT_EFI_ERROR(Status);\r
81\r
76d17c31 82 Status = BdsBootLinuxFdt (BootOption->FilePathList,\r
2ccfb71e 83 Initrd, // Initrd\r
84 (CHAR8*)(LinuxArguments + 1),\r
85 FdtDevicePath);\r
b148591a 86\r
87 FreePool (FdtDevicePath);\r
a6e97d28 88 }\r
89 } else {\r
91213ad3 90 // Set BootCurrent variable\r
91 LoadOptionIndexSize = sizeof(UINT16);\r
92 gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,\r
93 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
94 LoadOptionIndexSize, &(BootOption->LoadOptionIndex));\r
95\r
2755d844 96 Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList, BootOption->OptionalDataSize, BootOption->OptionalData);\r
91213ad3 97\r
98 // Clear BootCurrent variable\r
99 LoadOptionIndexSize = sizeof(UINT16);\r
100 gRT->SetVariable (L"BootCurrent", &gEfiGlobalVariableGuid,\r
101 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
102 0, NULL);\r
a6e97d28 103 }\r
ea46ebbe 104\r
105 return Status;\r
106}\r
107\r
ea46ebbe 108EFI_STATUS\r
109BootOptionList (\r
110 IN OUT LIST_ENTRY *BootOptionList\r
111 )\r
112{\r
a6e97d28 113 EFI_STATUS Status;\r
114 UINTN Index;\r
115 UINT16* BootOrder;\r
116 UINTN BootOrderSize;\r
117 BDS_LOAD_OPTION* BdsLoadOption;\r
118 BDS_LOAD_OPTION_ENTRY* BdsLoadOptionEntry;\r
ea46ebbe 119\r
120 InitializeListHead (BootOptionList);\r
121\r
122 // Get the Boot Option Order from the environment variable\r
123 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
124 if (EFI_ERROR(Status)) {\r
125 return Status;\r
126 }\r
127\r
128 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {\r
4aa24170 129 Status = BootOptionFromLoadOptionIndex (BootOrder[Index], &BdsLoadOption);\r
ea46ebbe 130 if (!EFI_ERROR(Status)) {\r
a6e97d28 131 BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool(sizeof(BDS_LOAD_OPTION_ENTRY));\r
132 BdsLoadOptionEntry->BdsLoadOption = BdsLoadOption;\r
133 InsertTailList (BootOptionList,&BdsLoadOptionEntry->Link);\r
ea46ebbe 134 }\r
135 }\r
136\r
b148591a 137 FreePool (BootOrder);\r
138\r
ea46ebbe 139 return EFI_SUCCESS;\r
140}\r
141\r
ea46ebbe 142STATIC\r
143EFI_STATUS\r
144BootOptionSetFields (\r
c60ea9a8 145 IN BDS_LOAD_OPTION* BootOption,\r
146 IN UINT32 Attributes,\r
147 IN CHAR16* BootDescription,\r
148 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
2ccfb71e 149 IN ARM_BDS_LOADER_TYPE BootType,\r
150 IN ARM_BDS_LOADER_ARGUMENTS* BootArguments\r
ea46ebbe 151 )\r
152{\r
2ccfb71e 153 EFI_LOAD_OPTION EfiLoadOption;\r
154 UINTN EfiLoadOptionSize;\r
155 UINTN BootDescriptionSize;\r
156 UINTN BootOptionalDataSize;\r
157 UINT16 FilePathListLength;\r
158 UINT8* EfiLoadOptionPtr;\r
159 UINT8* InitrdPathListPtr;\r
160 UINTN OptionalDataSize;\r
161 ARM_BDS_LINUX_ARGUMENTS* DestLinuxArguments;\r
162 ARM_BDS_LINUX_ARGUMENTS* SrcLinuxArguments;\r
ea46ebbe 163\r
164 // If we are overwriting an existent Boot Option then we have to free previously allocated memory\r
165 if (BootOption->LoadOption) {\r
166 FreePool(BootOption->LoadOption);\r
167 }\r
168\r
2ccfb71e 169 BootDescriptionSize = StrSize (BootDescription);\r
170 BootOptionalDataSize = sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER);\r
171 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {\r
172 BootOptionalDataSize += sizeof(ARM_BDS_LINUX_ARGUMENTS) + BootArguments->LinuxArguments.CmdLineSize + BootArguments->LinuxArguments.InitrdSize;\r
173 }\r
ea46ebbe 174\r
175 // Compute the size of the FilePath list\r
2ccfb71e 176 FilePathListLength = GetUnalignedDevicePathSize (DevicePath);\r
656416bc 177\r
ea46ebbe 178 // Allocate the memory for the EFI Load Option\r
179 EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + BootOptionalDataSize;\r
180 EfiLoadOption = (EFI_LOAD_OPTION)AllocatePool(EfiLoadOptionSize);\r
181 EfiLoadOptionPtr = EfiLoadOption;\r
182\r
183 //\r
184 // Populate the EFI Load Option and BDS Boot Option structures\r
185 //\r
186\r
187 // Attributes fields\r
188 BootOption->Attributes = Attributes;\r
189 *(UINT32*)EfiLoadOptionPtr = Attributes;\r
190 EfiLoadOptionPtr += sizeof(UINT32);\r
191\r
192 // FilePath List fields\r
193 BootOption->FilePathListLength = FilePathListLength;\r
194 *(UINT16*)EfiLoadOptionPtr = FilePathListLength;\r
195 EfiLoadOptionPtr += sizeof(UINT16);\r
196\r
197 // Boot description fields\r
198 BootOption->Description = (CHAR16*)EfiLoadOptionPtr;\r
199 CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize);\r
200 EfiLoadOptionPtr += BootDescriptionSize;\r
201\r
202 // File path fields\r
203 BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr;\r
2ccfb71e 204 CopyMem (EfiLoadOptionPtr, DevicePath, FilePathListLength);\r
205 EfiLoadOptionPtr += FilePathListLength;\r
ea46ebbe 206\r
207 // Optional Data fields, Do unaligned writes\r
2ccfb71e 208 BootOption->OptionalData = EfiLoadOptionPtr;\r
209 WriteUnaligned32 ((UINT32 *)EfiLoadOptionPtr, ARM_BDS_OPTIONAL_DATA_SIGNATURE);\r
210 WriteUnaligned32 ((UINT32 *)(EfiLoadOptionPtr + 4), BootType);\r
ea46ebbe 211\r
2ccfb71e 212 OptionalDataSize = sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER);\r
ea46ebbe 213\r
2ccfb71e 214 if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {\r
215 SrcLinuxArguments = &(BootArguments->LinuxArguments);\r
216 DestLinuxArguments = &((ARM_BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments.LinuxArguments;\r
656416bc 217\r
2ccfb71e 218 WriteUnaligned16 ((UINT16 *)&(DestLinuxArguments->CmdLineSize), SrcLinuxArguments->CmdLineSize);\r
219 WriteUnaligned16 ((UINT16 *)&(DestLinuxArguments->InitrdSize), SrcLinuxArguments->InitrdSize);\r
220 OptionalDataSize += sizeof (ARM_BDS_LINUX_ARGUMENTS);\r
656416bc 221\r
2ccfb71e 222 if (SrcLinuxArguments->CmdLineSize > 0) {\r
223 CopyMem ((VOID*)(DestLinuxArguments + 1), (VOID*)(SrcLinuxArguments + 1), SrcLinuxArguments->CmdLineSize);\r
224 OptionalDataSize += SrcLinuxArguments->CmdLineSize;\r
225 }\r
656416bc 226\r
2ccfb71e 227 if (SrcLinuxArguments->InitrdSize > 0) {\r
228 InitrdPathListPtr = (UINT8*)((UINTN)(DestLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize);\r
229 CopyMem (InitrdPathListPtr, (VOID*)((UINTN)(SrcLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize), SrcLinuxArguments->InitrdSize);\r
656416bc 230 }\r
231 }\r
2ccfb71e 232 BootOption->OptionalDataSize = OptionalDataSize;\r
233\r
6bab33c7 234 // If this function is called at the creation of the Boot Device entry (not at the update) the\r
235 // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry\r
236 if (BootOption->LoadOptionSize == 0) {\r
2ccfb71e 237 BootOption->LoadOptionIndex = BootOptionAllocateBootIndex ();\r
6bab33c7 238 }\r
239\r
ea46ebbe 240 // Fill the EFI Load option fields\r
ea46ebbe 241 BootOption->LoadOption = EfiLoadOption;\r
242 BootOption->LoadOptionSize = EfiLoadOptionSize;\r
243\r
244 return EFI_SUCCESS;\r
245}\r
246\r
247EFI_STATUS\r
248BootOptionCreate (\r
c60ea9a8 249 IN UINT32 Attributes,\r
250 IN CHAR16* BootDescription,\r
ea46ebbe 251 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
2ccfb71e 252 IN ARM_BDS_LOADER_TYPE BootType,\r
253 IN ARM_BDS_LOADER_ARGUMENTS* BootArguments,\r
a6e97d28 254 OUT BDS_LOAD_OPTION** BdsLoadOption\r
ea46ebbe 255 )\r
256{\r
a6e97d28 257 EFI_STATUS Status;\r
258 BDS_LOAD_OPTION_ENTRY* BootOptionEntry;\r
259 BDS_LOAD_OPTION* BootOption;\r
260 CHAR16 BootVariableName[9];\r
261 UINT16* BootOrder;\r
262 UINTN BootOrderSize;\r
ea46ebbe 263\r
264 //\r
265 // Allocate and fill the memory for the BDS Load Option structure\r
266 //\r
a6e97d28 267 BootOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof (BDS_LOAD_OPTION_ENTRY));\r
268 InitializeListHead (&BootOptionEntry->Link);\r
269 BootOptionEntry->BdsLoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));\r
ea46ebbe 270\r
a6e97d28 271 BootOption = BootOptionEntry->BdsLoadOption;\r
ea46ebbe 272 BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);\r
273\r
274 //\r
275 // Set the related environment variables\r
276 //\r
277\r
278 // Create Boot#### environment variable\r
279 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);\r
280 Status = gRT->SetVariable (\r
281 BootVariableName,\r
282 &gEfiGlobalVariableGuid,\r
283 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
284 BootOption->LoadOptionSize,\r
285 BootOption->LoadOption\r
286 );\r
287\r
288 // Add the new Boot Index to the list\r
289 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
290 if (!EFI_ERROR(Status)) {\r
291 BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);\r
292 // Add the new index at the end\r
293 BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex;\r
294 BootOrderSize += sizeof(UINT16);\r
295 } else {\r
296 // BootOrder does not exist. Create it\r
297 BootOrderSize = sizeof(UINT16);\r
298 BootOrder = &(BootOption->LoadOptionIndex);\r
299 }\r
300\r
301 // Update (or Create) the BootOrder environment variable\r
302 Status = gRT->SetVariable (\r
303 L"BootOrder",\r
304 &gEfiGlobalVariableGuid,\r
305 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
306 BootOrderSize,\r
307 BootOrder\r
308 );\r
309\r
b148591a 310 // We only free it if the UEFI Variable 'BootOrder' was already existing\r
311 if (BootOrderSize > sizeof(UINT16)) {\r
312 FreePool (BootOrder);\r
313 }\r
314\r
ea46ebbe 315 *BdsLoadOption = BootOption;\r
316 return Status;\r
317}\r
318\r
319EFI_STATUS\r
320BootOptionUpdate (\r
2ccfb71e 321 IN BDS_LOAD_OPTION* BdsLoadOption,\r
322 IN UINT32 Attributes,\r
323 IN CHAR16* BootDescription,\r
ea46ebbe 324 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
2ccfb71e 325 IN ARM_BDS_LOADER_TYPE BootType,\r
326 IN ARM_BDS_LOADER_ARGUMENTS* BootArguments\r
ea46ebbe 327 )\r
328{\r
329 EFI_STATUS Status;\r
ea46ebbe 330 CHAR16 BootVariableName[9];\r
331\r
332 // Update the BDS Load Option structure\r
333 BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);\r
334\r
335 // Update the related environment variables\r
6bab33c7 336 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);\r
337\r
ea46ebbe 338 Status = gRT->SetVariable (\r
339 BootVariableName,\r
340 &gEfiGlobalVariableGuid,\r
341 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
6bab33c7 342 BdsLoadOption->LoadOptionSize,\r
343 BdsLoadOption->LoadOption\r
ea46ebbe 344 );\r
345\r
346 return Status;\r
347}\r
348\r
349EFI_STATUS\r
350BootOptionDelete (\r
351 IN BDS_LOAD_OPTION *BootOption\r
352 )\r
353{\r
a6e97d28 354 UINTN Index;\r
355 UINTN BootOrderSize;\r
356 UINT16* BootOrder;\r
357 UINTN BootOrderCount;\r
358 EFI_STATUS Status;\r
ea46ebbe 359\r
360 // Remove the entry from the BootOrder environment variable\r
361 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);\r
362 if (!EFI_ERROR(Status)) {\r
363 BootOrderCount = BootOrderSize / sizeof(UINT16);\r
364\r
365 // Find the index of the removed entry\r
366 for (Index = 0; Index < BootOrderCount; Index++) {\r
367 if (BootOrder[Index] == BootOption->LoadOptionIndex) {\r
368 // If it the last entry we do not need to rearrange the BootOrder list\r
369 if (Index + 1 != BootOrderCount) {\r
370 CopyMem (&BootOrder[Index],&BootOrder[Index+1], BootOrderCount - (Index + 1));\r
371 }\r
372 break;\r
373 }\r
374 }\r
375\r
376 // Update the BootOrder environment variable\r
377 Status = gRT->SetVariable (\r
378 L"BootOrder",\r
379 &gEfiGlobalVariableGuid,\r
380 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
381 BootOrderSize - sizeof(UINT16),\r
382 BootOrder\r
383 );\r
384 }\r
385\r
b148591a 386 FreePool (BootOrder);\r
387\r
ea46ebbe 388 return EFI_SUCCESS;\r
389}\r