/** @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;
UINTN EfiLoadOptionPtr;
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;
}
EfiLoadOptionPtr = (UINTN)EfiLoadOption;
LoadOption->LoadOption = EfiLoadOption;
LoadOption->LoadOptionSize = EfiLoadOptionSize;
LoadOption->Attributes = *(UINT32*)EfiLoadOptionPtr;
LoadOption->FilePathListLength = *(UINT16*)(EfiLoadOptionPtr + sizeof(UINT32));
LoadOption->Description = (CHAR16*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16));
DescriptionLength = StrSize (LoadOption->Description);
LoadOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);
// If ((End of EfiLoadOptiony - Start of EfiLoadOption) == EfiLoadOptionSize) then No Optional Data
if ((UINTN)((UINTN)LoadOption->FilePathList + LoadOption->FilePathListLength - EfiLoadOptionPtr) == EfiLoadOptionSize) {
LoadOption->OptionalData = NULL;
LoadOption->OptionalDataSize = 0;
} else {
LoadOption->OptionalData = (VOID*)((UINTN)(LoadOption->FilePathList) + LoadOption->FilePathListLength);
LoadOption->OptionalDataSize = EfiLoadOptionSize - ((UINTN)LoadOption->OptionalData - EfiLoadOptionPtr);
}
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 = (UINT8 *) 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;
}