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