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