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