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