/** @file | |
* | |
* Copyright (c) 2011-2013, ARM Limited. All rights reserved. | |
* | |
* This program and the accompanying materials | |
* are licensed and made available under the terms and conditions of the BSD License | |
* which accompanies this distribution. The full text of the license may be found at | |
* http://opensource.org/licenses/bsd-license.php | |
* | |
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
* | |
**/ | |
#include "BdsInternal.h" | |
EFI_STATUS | |
BootOptionParseLoadOption ( | |
IN EFI_LOAD_OPTION EfiLoadOption, | |
IN UINTN EfiLoadOptionSize, | |
IN OUT BDS_LOAD_OPTION **BdsLoadOption | |
) | |
{ | |
BDS_LOAD_OPTION *LoadOption; | |
UINTN DescriptionLength; | |
if (EfiLoadOption == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
if (*BdsLoadOption == NULL) { | |
LoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION)); | |
if (LoadOption == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} else { | |
LoadOption = *BdsLoadOption; | |
} | |
LoadOption->LoadOption = EfiLoadOption; | |
LoadOption->LoadOptionSize = EfiLoadOptionSize; | |
LoadOption->Attributes = *(UINT32*)EfiLoadOption; | |
LoadOption->FilePathListLength = *(UINT16*)(EfiLoadOption + sizeof(UINT32)); | |
LoadOption->Description = (CHAR16*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16)); | |
DescriptionLength = StrSize (LoadOption->Description); | |
LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOption + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength); | |
// If ((End of EfiLoadOptiony - Start of EfiLoadOption) == EfiLoadOptionSize) then No Optional Data | |
if ((UINTN)((UINTN)LoadOption->FilePathList + LoadOption->FilePathListLength - (UINTN)EfiLoadOption) == EfiLoadOptionSize) { | |
LoadOption->OptionalData = NULL; | |
LoadOption->OptionalDataSize = 0; | |
} else { | |
LoadOption->OptionalData = (VOID*)((UINTN)(LoadOption->FilePathList) + LoadOption->FilePathListLength); | |
LoadOption->OptionalDataSize = EfiLoadOptionSize - ((UINTN)LoadOption->OptionalData - (UINTN)EfiLoadOption); | |
} | |
if (*BdsLoadOption == NULL) { | |
*BdsLoadOption = LoadOption; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
BootOptionFromLoadOptionVariable ( | |
IN CHAR16* BootVariableName, | |
OUT BDS_LOAD_OPTION** BdsLoadOption | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_LOAD_OPTION EfiLoadOption; | |
UINTN EfiLoadOptionSize; | |
Status = GetGlobalEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption); | |
if (!EFI_ERROR(Status)) { | |
*BdsLoadOption = NULL; | |
Status = BootOptionParseLoadOption (EfiLoadOption, EfiLoadOptionSize, BdsLoadOption); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
BootOptionFromLoadOptionIndex ( | |
IN UINT16 LoadOptionIndex, | |
OUT BDS_LOAD_OPTION **BdsLoadOption | |
) | |
{ | |
CHAR16 BootVariableName[9]; | |
EFI_STATUS Status; | |
UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex); | |
Status = BootOptionFromLoadOptionVariable (BootVariableName, BdsLoadOption); | |
if (!EFI_ERROR(Status)) { | |
(*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex; | |
} | |
return Status; | |
} | |
EFI_STATUS | |
BootOptionToLoadOptionVariable ( | |
IN BDS_LOAD_OPTION* BdsLoadOption | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN DescriptionSize; | |
//UINT16 FilePathListLength; | |
EFI_DEVICE_PATH_PROTOCOL* DevicePathNode; | |
UINTN NodeLength; | |
UINT8* EfiLoadOptionPtr; | |
VOID* OldLoadOption; | |
CHAR16 BootVariableName[9]; | |
UINTN BootOrderSize; | |
UINT16* BootOrder; | |
// If we are overwriting an existent Boot Option then we have to free previously allocated memory | |
if (BdsLoadOption->LoadOptionSize > 0) { | |
OldLoadOption = BdsLoadOption->LoadOption; | |
} else { | |
OldLoadOption = NULL; | |
// If this function is called at the creation of the Boot Device entry (not at the update) the | |
// BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry | |
BdsLoadOption->LoadOptionIndex = BootOptionAllocateBootIndex (); | |
//TODO: Add to the the Boot Entry List | |
} | |
DescriptionSize = StrSize(BdsLoadOption->Description); | |
// Ensure the FilePathListLength information is correct | |
ASSERT (GetDevicePathSize (BdsLoadOption->FilePathList) == BdsLoadOption->FilePathListLength); | |
// Allocate the memory for the EFI Load Option | |
BdsLoadOption->LoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + DescriptionSize + BdsLoadOption->FilePathListLength + BdsLoadOption->OptionalDataSize; | |
BdsLoadOption->LoadOption = (EFI_LOAD_OPTION)AllocateZeroPool (BdsLoadOption->LoadOptionSize); | |
if (BdsLoadOption->LoadOption == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
EfiLoadOptionPtr = BdsLoadOption->LoadOption; | |
// | |
// Populate the EFI Load Option and BDS Boot Option structures | |
// | |
// Attributes fields | |
*(UINT32*)EfiLoadOptionPtr = BdsLoadOption->Attributes; | |
EfiLoadOptionPtr += sizeof(UINT32); | |
// FilePath List fields | |
*(UINT16*)EfiLoadOptionPtr = BdsLoadOption->FilePathListLength; | |
EfiLoadOptionPtr += sizeof(UINT16); | |
// Boot description fields | |
CopyMem (EfiLoadOptionPtr, BdsLoadOption->Description, DescriptionSize); | |
EfiLoadOptionPtr += DescriptionSize; | |
// File path fields | |
DevicePathNode = BdsLoadOption->FilePathList; | |
while (!IsDevicePathEndType (DevicePathNode)) { | |
NodeLength = DevicePathNodeLength(DevicePathNode); | |
CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength); | |
EfiLoadOptionPtr += NodeLength; | |
DevicePathNode = NextDevicePathNode (DevicePathNode); | |
} | |
// Set the End Device Path Type | |
SetDevicePathEndNode (EfiLoadOptionPtr); | |
EfiLoadOptionPtr += sizeof(EFI_DEVICE_PATH); | |
// Fill the Optional Data | |
if (BdsLoadOption->OptionalDataSize > 0) { | |
CopyMem (EfiLoadOptionPtr, BdsLoadOption->OptionalData, BdsLoadOption->OptionalDataSize); | |
} | |
// Case where the fields have been updated | |
if (OldLoadOption) { | |
// Now, the old data has been copied to the new allocated packed structure, we need to update the pointers of BdsLoadOption | |
BootOptionParseLoadOption (BdsLoadOption->LoadOption, BdsLoadOption->LoadOptionSize, &BdsLoadOption); | |
// Free the old packed structure | |
FreePool (OldLoadOption); | |
} | |
// Create/Update Boot#### environment variable | |
UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex); | |
Status = gRT->SetVariable ( | |
BootVariableName, | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
BdsLoadOption->LoadOptionSize, | |
BdsLoadOption->LoadOption | |
); | |
// When it is a new entry we must add the entry to the BootOrder | |
if (OldLoadOption == NULL) { | |
// Add the new Boot Index to the list | |
Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); | |
if (!EFI_ERROR(Status)) { | |
BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder); | |
// Add the new index at the end | |
BootOrder[BootOrderSize / sizeof(UINT16)] = BdsLoadOption->LoadOptionIndex; | |
BootOrderSize += sizeof(UINT16); | |
} else { | |
// BootOrder does not exist. Create it | |
BootOrderSize = sizeof(UINT16); | |
BootOrder = &(BdsLoadOption->LoadOptionIndex); | |
} | |
// Update (or Create) the BootOrder environment variable | |
gRT->SetVariable ( | |
L"BootOrder", | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
BootOrderSize, | |
BootOrder | |
); | |
DEBUG((EFI_D_ERROR,"Create %s\n",BootVariableName)); | |
// Free memory allocated by GetGlobalEnvironmentVariable | |
if (!EFI_ERROR(Status)) { | |
FreePool (BootOrder); | |
} | |
} else { | |
DEBUG((EFI_D_ERROR,"Update %s\n",BootVariableName)); | |
} | |
return EFI_SUCCESS; | |
} | |
UINT16 | |
BootOptionAllocateBootIndex ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
UINT32 BootIndex; | |
UINT16 *BootOrder; | |
UINTN BootOrderSize; | |
BOOLEAN Found; | |
// Get the Boot Option Order from the environment variable | |
Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); | |
if (!EFI_ERROR(Status)) { | |
for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) { | |
Found = FALSE; | |
for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { | |
if (BootOrder[Index] == BootIndex) { | |
Found = TRUE; | |
break; | |
} | |
} | |
if (!Found) { | |
return BootIndex; | |
} | |
} | |
FreePool (BootOrder); | |
} | |
// Return the first index | |
return 0; | |
} |