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