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