diff options
-rw-r--r-- | ArmPkg/Library/BdsLib/BdsLoadOption.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/ArmPkg/Library/BdsLib/BdsLoadOption.c b/ArmPkg/Library/BdsLib/BdsLoadOption.c new file mode 100644 index 0000000000..fbf92c616e --- /dev/null +++ b/ArmPkg/Library/BdsLib/BdsLoadOption.c @@ -0,0 +1,264 @@ +/** @file +* +* Copyright (c) 2011, 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 = GetEnvironmentVariable (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 = GetEnvironmentVariable (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 + Status = 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)); + } 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 = GetEnvironmentVariable (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; + } + } + } + // Return the first index + return 0; +} |