| /** @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; | |
| } |