]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Bds/BootOption.c
ArmPlatformPkg/Bds: Add Linux 'initrd' support to BDS
[mirror_edk2.git] / ArmPlatformPkg / Bds / BootOption.c
1 /** @file
2 *
3 * Copyright (c) 2011, 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* FdtDevicePath;
26 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;
27 UINT32 LoaderType;
28
29 Status = EFI_UNSUPPORTED;
30 LoaderType = ReadUnaligned32 (&BootOption->OptionalData->LoaderType);
31
32 if (LoaderType == BDS_LOADER_EFI_APPLICATION) {
33 // Need to connect every drivers to ensure no dependencies are missing for the application
34 BdsConnectAllDrivers();
35
36 Status = BdsStartEfiApplication (mImageHandle, BootOption->FilePathList);
37 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) {
38 Status = BdsBootLinux (BootOption->FilePathList,
39 (EFI_DEVICE_PATH_PROTOCOL*)ReadUnaligned32((UINT32*)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList)),
40 BootOption->OptionalData->Arguments.LinuxAtagArguments.CmdLine,
41 NULL);
42 } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) {
43 // Convert the FDT path into a Device Path
44 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
45 ASSERT_EFI_ERROR(Status);
46 FdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
47
48 Status = BdsBootLinux (BootOption->FilePathList,
49 NULL,
50 NULL,
51 FdtDevicePath);
52
53 FreePool(FdtDevicePath);
54 }
55
56 return Status;
57 }
58
59 EFI_STATUS
60 BootOptionParseLoadOption (
61 IN EFI_LOAD_OPTION EfiLoadOption,
62 IN UINTN EfiLoadOptionSize,
63 OUT BDS_LOAD_OPTION **BdsLoadOption
64 )
65 {
66 BDS_LOAD_OPTION *LoadOption;
67 UINTN FilePathListLength;
68 UINTN DescriptionLength;
69
70 if (EfiLoadOption == NULL) {
71 return EFI_INVALID_PARAMETER;
72 }
73
74 if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
75 return EFI_BAD_BUFFER_SIZE;
76 }
77
78 LoadOption = (BDS_LOAD_OPTION*)AllocatePool(sizeof(BDS_LOAD_OPTION));
79 if (LoadOption == NULL) {
80 return EFI_OUT_OF_RESOURCES;
81 }
82
83 LoadOption->LoadOption = EfiLoadOption;
84 LoadOption->LoadOptionSize = EfiLoadOptionSize;
85
86 LoadOption->Attributes = *(UINT32*)EfiLoadOption;
87 FilePathListLength = *(UINT16*)(EfiLoadOption + sizeof(UINT32));
88 LoadOption->Description = (CHAR16*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16));
89 DescriptionLength = StrSize (LoadOption->Description);
90 LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);
91
92 if ((UINTN)((UINT8*)LoadOption->FilePathList + FilePathListLength - EfiLoadOption) == EfiLoadOptionSize) {
93 LoadOption->OptionalData = NULL;
94 } else {
95 LoadOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)((UINT8*)LoadOption->FilePathList + FilePathListLength);
96 }
97
98 *BdsLoadOption = LoadOption;
99 return EFI_SUCCESS;
100 }
101
102 EFI_STATUS
103 BootOptionFromLoadOptionVariable (
104 IN UINT16 LoadOptionIndex,
105 OUT BDS_LOAD_OPTION **BdsLoadOption
106 )
107 {
108 EFI_STATUS Status;
109 CHAR16 BootVariableName[9];
110 EFI_LOAD_OPTION EfiLoadOption;
111 UINTN EfiLoadOptionSize;
112
113 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex);
114
115 Status = GetEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption);
116 if (!EFI_ERROR(Status)) {
117 Status = BootOptionParseLoadOption (EfiLoadOption,EfiLoadOptionSize,BdsLoadOption);
118 if (!EFI_ERROR(Status)) {
119 (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex;
120 }
121 }
122
123 return Status;
124 }
125
126 EFI_STATUS
127 BootOptionList (
128 IN OUT LIST_ENTRY *BootOptionList
129 )
130 {
131 EFI_STATUS Status;
132 UINTN Index;
133 UINT16 *BootOrder;
134 UINTN BootOrderSize;
135 BDS_LOAD_OPTION *BdsLoadOption;
136
137 InitializeListHead (BootOptionList);
138
139 // Get the Boot Option Order from the environment variable
140 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
141 if (EFI_ERROR(Status)) {
142 return Status;
143 }
144
145 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
146 Status = BootOptionFromLoadOptionVariable (BootOrder[Index],&BdsLoadOption);
147 if (!EFI_ERROR(Status)) {
148 InsertTailList (BootOptionList,&BdsLoadOption->Link);
149 }
150 }
151
152 return EFI_SUCCESS;
153 }
154
155 UINT16
156 BootOptionAllocateBootIndex (
157 VOID
158 )
159 {
160 EFI_STATUS Status;
161 UINTN Index;
162 UINT32 BootIndex;
163 UINT16 *BootOrder;
164 UINTN BootOrderSize;
165 BOOLEAN Found;
166
167 // Get the Boot Option Order from the environment variable
168 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
169 if (!EFI_ERROR(Status)) {
170 for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) {
171 Found = FALSE;
172 for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
173 if (BootOrder[Index] == BootIndex) {
174 Found = TRUE;
175 break;
176 }
177 }
178 if (!Found) {
179 return BootIndex;
180 }
181 }
182 }
183 // Return the first index
184 return 0;
185 }
186
187 STATIC
188 EFI_STATUS
189 BootOptionSetFields (
190 IN BDS_LOAD_OPTION* BootOption,
191 IN UINT32 Attributes,
192 IN CHAR16* BootDescription,
193 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
194 IN BDS_LOADER_TYPE BootType,
195 IN BDS_LOADER_ARGUMENTS* BootArguments
196 )
197 {
198 EFI_LOAD_OPTION EfiLoadOption;
199 UINTN EfiLoadOptionSize;
200 UINTN BootDescriptionSize;
201 UINTN BootOptionalDataSize;
202 UINT16 FilePathListLength;
203 UINT16 InitrdPathListLength;
204 EFI_DEVICE_PATH_PROTOCOL* DevicePathNode;
205 EFI_DEVICE_PATH_PROTOCOL* InitrdPathNode;
206 UINTN NodeLength;
207 UINT8* EfiLoadOptionPtr;
208 UINT8 *InitrdPathListPtr;
209
210 // If we are overwriting an existent Boot Option then we have to free previously allocated memory
211 if (BootOption->LoadOption) {
212 FreePool(BootOption->LoadOption);
213 }
214
215 BootDescriptionSize = StrSize(BootDescription);
216 BootOptionalDataSize = sizeof(BDS_LOADER_TYPE) + (BootType == BDS_LOADER_KERNEL_LINUX_ATAG ?
217 (sizeof(UINT16) + sizeof(EFI_DEVICE_PATH_PROTOCOL*) + BOOT_DEVICE_OPTION_MAX + 1)
218 : 0);
219
220 // Compute the size of the FilePath list
221 FilePathListLength = 0;
222 DevicePathNode = DevicePath;
223 while (!IsDevicePathEndType (DevicePathNode)) {
224 FilePathListLength += DevicePathNodeLength (DevicePathNode);
225 DevicePathNode = NextDevicePathNode (DevicePathNode);
226 }
227 // Add the length of the DevicePath EndType
228 FilePathListLength += DevicePathNodeLength (DevicePathNode);
229
230 InitrdPathListLength = 0;
231 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG && BootArguments->LinuxAtagArguments.InitrdPathList != NULL) {
232 // Compute the size of the InitrdPath list
233 InitrdPathNode = BootArguments->LinuxAtagArguments.InitrdPathList;
234 while (!IsDevicePathEndType (InitrdPathNode)) {
235 InitrdPathListLength += DevicePathNodeLength (InitrdPathNode);
236 InitrdPathNode = NextDevicePathNode (InitrdPathNode);
237 }
238 // Add the length of the DevicePath EndType
239 InitrdPathListLength += DevicePathNodeLength (InitrdPathNode);
240 }
241
242 // Allocate the memory for the EFI Load Option
243 EfiLoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + BootDescriptionSize + FilePathListLength + BootOptionalDataSize;
244 EfiLoadOption = (EFI_LOAD_OPTION)AllocatePool(EfiLoadOptionSize);
245 EfiLoadOptionPtr = EfiLoadOption;
246
247 //
248 // Populate the EFI Load Option and BDS Boot Option structures
249 //
250
251 // Attributes fields
252 BootOption->Attributes = Attributes;
253 *(UINT32*)EfiLoadOptionPtr = Attributes;
254 EfiLoadOptionPtr += sizeof(UINT32);
255
256 // FilePath List fields
257 BootOption->FilePathListLength = FilePathListLength;
258 *(UINT16*)EfiLoadOptionPtr = FilePathListLength;
259 EfiLoadOptionPtr += sizeof(UINT16);
260
261 // Boot description fields
262 BootOption->Description = (CHAR16*)EfiLoadOptionPtr;
263 CopyMem (EfiLoadOptionPtr, BootDescription, BootDescriptionSize);
264 EfiLoadOptionPtr += BootDescriptionSize;
265
266 // File path fields
267 BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)EfiLoadOptionPtr;
268 DevicePathNode = DevicePath;
269 while (!IsDevicePathEndType (DevicePathNode)) {
270 NodeLength = DevicePathNodeLength(DevicePathNode);
271 CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength);
272 EfiLoadOptionPtr += NodeLength;
273 DevicePathNode = NextDevicePathNode (DevicePathNode);
274 }
275
276 // Set the End Device Path Type
277 SetDevicePathEndNode (EfiLoadOptionPtr);
278 EfiLoadOptionPtr = (UINT8 *)EfiLoadOptionPtr + sizeof(EFI_DEVICE_PATH);
279
280 // Optional Data fields, Do unaligned writes
281 WriteUnaligned32 ((UINT32 *)EfiLoadOptionPtr, BootType);
282
283 BootOption->OptionalData = (BDS_LOADER_OPTIONAL_DATA *)EfiLoadOptionPtr;
284
285 if (BootType == BDS_LOADER_KERNEL_LINUX_ATAG) {
286 CopyMem (&((BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments.LinuxAtagArguments.CmdLine,
287 BootArguments->LinuxAtagArguments.CmdLine,
288 AsciiStrSize(BootArguments->LinuxAtagArguments.CmdLine));
289
290 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathListLength), InitrdPathListLength);
291
292 if ((EFI_DEVICE_PATH_PROTOCOL*)ReadUnaligned32((UINT32 *)&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList) != NULL
293 && BootArguments->LinuxAtagArguments.InitrdPathList != NULL) {
294 InitrdPathListPtr = AllocatePool(InitrdPathListLength);
295 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList), (UINT32)InitrdPathListPtr);
296 InitrdPathNode = BootArguments->LinuxAtagArguments.InitrdPathList;
297
298 while (!IsDevicePathEndType (InitrdPathNode)) {
299 NodeLength = DevicePathNodeLength(InitrdPathNode);
300 CopyMem (InitrdPathListPtr, InitrdPathNode, NodeLength);
301 InitrdPathListPtr += NodeLength;
302 InitrdPathNode = NextDevicePathNode (InitrdPathNode);
303 }
304
305 // Set the End Device Path Type
306 SetDevicePathEndNode (InitrdPathListPtr);
307 } else {
308 WriteUnaligned32 ((UINT32 *)(&BootOption->OptionalData->Arguments.LinuxAtagArguments.InitrdPathList), (UINT32)NULL);
309 }
310 }
311 // If this function is called at the creation of the Boot Device entry (not at the update) the
312 // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry
313 if (BootOption->LoadOptionSize == 0) {
314 BootOption->LoadOptionIndex = BootOptionAllocateBootIndex();
315 }
316
317 // Fill the EFI Load option fields
318 BootOption->LoadOption = EfiLoadOption;
319 BootOption->LoadOptionSize = EfiLoadOptionSize;
320
321 return EFI_SUCCESS;
322 }
323
324 EFI_STATUS
325 BootOptionCreate (
326 IN UINT32 Attributes,
327 IN CHAR16* BootDescription,
328 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
329 IN BDS_LOADER_TYPE BootType,
330 IN BDS_LOADER_ARGUMENTS* BootArguments,
331 OUT BDS_LOAD_OPTION **BdsLoadOption
332 )
333 {
334 EFI_STATUS Status;
335 BDS_LOAD_OPTION *BootOption;
336 CHAR16 BootVariableName[9];
337 UINT16 *BootOrder;
338 UINTN BootOrderSize;
339
340 //
341 // Allocate and fill the memory for the BDS Load Option structure
342 //
343 BootOption = (BDS_LOAD_OPTION*)AllocateZeroPool(sizeof(BDS_LOAD_OPTION));
344
345 InitializeListHead (&BootOption->Link);
346 BootOptionSetFields (BootOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);
347
348 //
349 // Set the related environment variables
350 //
351
352 // Create Boot#### environment variable
353 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BootOption->LoadOptionIndex);
354 Status = gRT->SetVariable (
355 BootVariableName,
356 &gEfiGlobalVariableGuid,
357 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
358 BootOption->LoadOptionSize,
359 BootOption->LoadOption
360 );
361
362 // Add the new Boot Index to the list
363 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
364 if (!EFI_ERROR(Status)) {
365 BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);
366 // Add the new index at the end
367 BootOrder[BootOrderSize / sizeof(UINT16)] = BootOption->LoadOptionIndex;
368 BootOrderSize += sizeof(UINT16);
369 } else {
370 // BootOrder does not exist. Create it
371 BootOrderSize = sizeof(UINT16);
372 BootOrder = &(BootOption->LoadOptionIndex);
373 }
374
375 // Update (or Create) the BootOrder environment variable
376 Status = gRT->SetVariable (
377 L"BootOrder",
378 &gEfiGlobalVariableGuid,
379 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
380 BootOrderSize,
381 BootOrder
382 );
383
384 *BdsLoadOption = BootOption;
385 return Status;
386 }
387
388 EFI_STATUS
389 BootOptionUpdate (
390 IN BDS_LOAD_OPTION *BdsLoadOption,
391 IN UINT32 Attributes,
392 IN CHAR16* BootDescription,
393 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
394 IN BDS_LOADER_TYPE BootType,
395 IN BDS_LOADER_ARGUMENTS* BootArguments
396 )
397 {
398 EFI_STATUS Status;
399 CHAR16 BootVariableName[9];
400
401 // Update the BDS Load Option structure
402 BootOptionSetFields (BdsLoadOption, Attributes, BootDescription, DevicePath, BootType, BootArguments);
403
404 // Update the related environment variables
405 UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);
406
407 Status = gRT->SetVariable (
408 BootVariableName,
409 &gEfiGlobalVariableGuid,
410 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
411 BdsLoadOption->LoadOptionSize,
412 BdsLoadOption->LoadOption
413 );
414
415 return Status;
416 }
417
418 EFI_STATUS
419 BootOptionDelete (
420 IN BDS_LOAD_OPTION *BootOption
421 )
422 {
423 UINTN Index;
424 UINTN BootOrderSize;
425 UINT16* BootOrder;
426 UINTN BootOrderCount;
427 EFI_STATUS Status;
428
429 // If the Boot Optiono was attached to a list remove it
430 if (!IsListEmpty (&BootOption->Link)) {
431 // Remove the entry from the list
432 RemoveEntryList (&BootOption->Link);
433 }
434
435 // Remove the entry from the BootOrder environment variable
436 Status = GetEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
437 if (!EFI_ERROR(Status)) {
438 BootOrderCount = BootOrderSize / sizeof(UINT16);
439
440 // Find the index of the removed entry
441 for (Index = 0; Index < BootOrderCount; Index++) {
442 if (BootOrder[Index] == BootOption->LoadOptionIndex) {
443 // If it the last entry we do not need to rearrange the BootOrder list
444 if (Index + 1 != BootOrderCount) {
445 CopyMem (&BootOrder[Index],&BootOrder[Index+1], BootOrderCount - (Index + 1));
446 }
447 break;
448 }
449 }
450
451 // Update the BootOrder environment variable
452 Status = gRT->SetVariable (
453 L"BootOrder",
454 &gEfiGlobalVariableGuid,
455 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
456 BootOrderSize - sizeof(UINT16),
457 BootOrder
458 );
459 }
460
461 return EFI_SUCCESS;
462 }