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