diff options
Diffstat (limited to 'OvmfPkg/Library/NvVarsFileLib/FsAccess.c')
-rw-r--r-- | OvmfPkg/Library/NvVarsFileLib/FsAccess.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/OvmfPkg/Library/NvVarsFileLib/FsAccess.c b/OvmfPkg/Library/NvVarsFileLib/FsAccess.c new file mode 100644 index 0000000000..cbae15afad --- /dev/null +++ b/OvmfPkg/Library/NvVarsFileLib/FsAccess.c @@ -0,0 +1,540 @@ +/** @file + File System Access for NvVarsFileLib + + Copyright (c) 2004 - 2009, Intel Corporation. <BR> + 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 "NvVarsFileLib.h" + +#include <Library/BaseMemoryLib.h> +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> + + +/** + Open the NvVars file for reading or writing + + @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance + @param[in] ReadingFile - TRUE: open the file for reading. FALSE: writing + @param[out] NvVarsFile - If EFI_SUCCESS is returned, then this is updated + with the opened NvVars file. + + @return EFI_SUCCESS if the file was opened + +**/ +EFI_STATUS +GetNvVarsFile ( + IN EFI_HANDLE FsHandle, + IN BOOLEAN ReadingFile, + OUT EFI_FILE_HANDLE *NvVarsFile + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; + EFI_FILE_HANDLE Root; + + // + // Get the FileSystem protocol on that handle + // + Status = gBS->HandleProtocol ( + FsHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **)&Fs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the volume (the root directory) + // + Status = Fs->OpenVolume (Fs, &Root); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Attempt to open the NvVars file in the root directory + // + Status = Root->Open ( + Root, + NvVarsFile, + L"NvVars", + ReadingFile ? + EFI_FILE_MODE_READ : + ( + EFI_FILE_MODE_CREATE | + EFI_FILE_MODE_READ | + EFI_FILE_MODE_WRITE + ), + 0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + + +/** + Open the NvVars file for reading or writing + + @param[in] File - The file to inspect + @param[out] Exists - Returns whether the file exists + @param[out] Size - Returns the size of the file + (0 if the file does not exist) + +**/ +VOID +NvVarsFileReadCheckup ( + IN EFI_FILE_HANDLE File, + OUT BOOLEAN *Exists, + OUT UINTN *Size + ) +{ + EFI_FILE_INFO *FileInfo; + + *Exists = FALSE; + *Size = 0; + + FileInfo = FileHandleGetInfo (File); + if (FileInfo == NULL) { + return; + } + + if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) { + FreePool (FileInfo); + return; + } + + *Exists = TRUE; + *Size = FileInfo->FileSize; + + FreePool (FileInfo); +} + + +/** + Open the NvVars file for reading or writing + + @param[in] File - The file to inspect + @param[out] Exists - Returns whether the file exists + @param[out] Size - Returns the size of the file + (0 if the file does not exist) + +**/ +EFI_STATUS +FileHandleEmpty ( + IN EFI_FILE_HANDLE File + ) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + + // + // Retrieve the FileInfo structure + // + FileInfo = FileHandleGetInfo (File); + if (FileInfo == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // If the path is a directory, then return an error + // + if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) { + FreePool (FileInfo); + return EFI_INVALID_PARAMETER; + } + + // + // If the file size is already 0, then it is empty, so + // we can return success. + // + if (FileInfo->FileSize == 0) { + FreePool (FileInfo); + return EFI_SUCCESS; + } + + // + // Set the file size to 0. + // + FileInfo->FileSize = 0; + Status = FileHandleSetInfo (File, FileInfo); + + FreePool (FileInfo); + + return Status; +} + + +/** + Reads a file to a newly allocated buffer + + @param[in] File - The file to read + @param[in] ReadSize - The size of data to read from the file + + @return Pointer to buffer allocated to hold the file + contents. NULL if an error occured. + +**/ +VOID* +FileHandleReadToNewBuffer ( + IN EFI_FILE_HANDLE FileHandle, + IN UINTN ReadSize + ) +{ + EFI_STATUS Status; + UINTN ActualReadSize; + VOID *FileContents; + + ActualReadSize = ReadSize; + FileContents = AllocatePool (ReadSize); + if (FileContents != NULL) { + Status = FileHandleRead ( + FileHandle, + &ReadSize, + FileContents + ); + if (EFI_ERROR (Status) || (ActualReadSize != ReadSize)) { + FreePool (FileContents); + return NULL; + } + } + + return FileContents; +} + + +/** + Reads the contents of the NvVars file on the file system + + @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance + + @return EFI_STATUS based on the success or failure of the file read + +**/ +EFI_STATUS +ReadNvVarsFile ( + IN EFI_HANDLE FsHandle + ) +{ + EFI_STATUS Status; + EFI_FILE_HANDLE File; + UINTN FileSize; + BOOLEAN FileExists; + VOID *FileContents; + + Status = GetNvVarsFile (FsHandle, TRUE, &File); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n")); + return Status; + } + + NvVarsFileReadCheckup (File, &FileExists, &FileSize); + if (FileSize == 0) { + FileHandleClose (File); + return EFI_UNSUPPORTED; + } + + FileContents = FileHandleReadToNewBuffer (File, FileSize); + if (FileContents == NULL) { + FileHandleClose (File); + return EFI_UNSUPPORTED; + } + + DEBUG (( + EFI_D_INFO, + "FsAccess.c: Read %d bytes from NV Variables file\n", + FileSize + )); + + Status = SetVariablesFromBuffer (FileContents, FileSize); + + FreePool (FileContents); + FileHandleClose (File); + + return Status; +} + + +/** + Loads the non-volatile variables from the NvVars file on the + given file system. + + @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance + + @return EFI_STATUS based on the success or failure of load operation + +**/ +EFI_STATUS +LoadNvVarsFromFs ( + EFI_HANDLE FsHandle + ) +{ + EFI_STATUS Status; + BOOLEAN VarData; + UINTN Size; + + DEBUG ((EFI_D_INFO, "FsAccess.c: LoadNvVarsFromFs\n")); + + // + // We write a variable to indicate we've already loaded the + // variable data. If it is found, we skip the loading. + // + // This is relevent if the non-volatile variable have been + // able to survive a reboot operation. In that case, we don't + // want to re-load the file as it would overwrite newer changes + // made to the variables. + // + Size = sizeof (VarData); + VarData = TRUE; + Status = gRT->GetVariable ( + L"NvVars", + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &Size, + (VOID*) &VarData + ); + if (Status == EFI_SUCCESS) { + DEBUG ((EFI_D_INFO, "NV Variables were already loaded\n")); + return EFI_ALREADY_STARTED; + } + + // + // Attempt to restore the variables from the NvVars file. + // + Status = ReadNvVarsFile (FsHandle); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Error while restoring NV variable data\n")); + return Status; + } + + // + // Write a variable to indicate we've already loaded the + // variable data. If it is found, we skip the loading on + // subsequent attempts. + // + Size = sizeof (VarData); + VarData = TRUE; + gRT->SetVariable ( + L"NvVars", + &gEfiSimpleFileSystemProtocolGuid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + Size, + (VOID*) &VarData + ); + + DEBUG (( + EFI_D_INFO, + "FsAccess.c: Read NV Variables file (size=%d)\n", + Size + )); + + return Status; +} + + +/** + Saves the non-volatile variables into the NvVars file on the + given file system. + + @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance + + @return EFI_STATUS based on the success or failure of load operation + +**/ +EFI_STATUS +SaveNvVarsToFs ( + EFI_HANDLE FsHandle + ) +{ + EFI_STATUS Status; + EFI_FILE_HANDLE File; + UINTN VariableNameBufferSize; + UINTN VariableNameSize; + CHAR16 *VariableName; + EFI_GUID VendorGuid; + UINTN VariableDataBufferSize; + UINTN VariableDataSize; + VOID *VariableData; + UINT32 VariableAttributes; + VOID *NewBuffer; + + // + // Open the NvVars file for writing. + // + Status = GetNvVarsFile (FsHandle, FALSE, &File); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n")); + return Status; + } + + // + // Empty the starting file contents. + // + Status = FileHandleEmpty (File); + if (EFI_ERROR (Status)) { + FileHandleClose (File); + return Status; + } + + // + // Initialize the variable name and data buffer variables. + // + VariableNameBufferSize = sizeof (CHAR16); + VariableName = AllocateZeroPool (VariableNameBufferSize); + + VariableDataBufferSize = 0; + VariableData = NULL; + + for (;;) { + // + // Get the next variable name and guid + // + VariableNameSize = VariableNameBufferSize; + Status = gRT->GetNextVariableName ( + &VariableNameSize, + VariableName, + &VendorGuid + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // The currently allocated VariableName buffer is too small, + // so we allocate a larger buffer, and copy the old buffer + // to it. + // + NewBuffer = AllocatePool (VariableNameSize); + if (NewBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + CopyMem (NewBuffer, VariableName, VariableNameBufferSize); + if (VariableName != NULL) { + FreePool (VariableName); + } + VariableName = NewBuffer; + VariableNameBufferSize = VariableNameSize; + + // + // Try to get the next variable name again with the larger buffer. + // + Status = gRT->GetNextVariableName ( + &VariableNameSize, + VariableName, + &VendorGuid + ); + } + + if (EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + break; + } + + // + // Get the variable data and attributes + // + VariableDataSize = VariableDataBufferSize; + Status = gRT->GetVariable ( + VariableName, + &VendorGuid, + &VariableAttributes, + &VariableDataSize, + VariableData + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // The currently allocated VariableData buffer is too small, + // so we allocate a larger buffer. + // + if (VariableDataBufferSize != 0) { + FreePool (VariableData); + VariableData = NULL; + VariableDataBufferSize = 0; + } + VariableData = AllocatePool (VariableDataSize); + if (VariableData == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + VariableDataBufferSize = VariableDataSize; + + // + // Try to read the variable again with the larger buffer. + // + Status = gRT->GetVariable ( + VariableName, + &VendorGuid, + &VariableAttributes, + &VariableDataSize, + VariableData + ); + } + if (EFI_ERROR (Status)) { + break; + } + + // + // Skip volatile variables. We only preserve non-volatile variables. + // + if ((VariableAttributes & EFI_VARIABLE_NON_VOLATILE) == 0) { + continue; + } + + DEBUG (( + EFI_D_INFO, + "Saving variable %g:%s to file\n", + &VendorGuid, + VariableName + )); + + // + // Write the variable information out to the file + // + Status = PackVariableIntoFile ( + File, + VariableName, + VariableNameSize, + &VendorGuid, + VariableAttributes, + VariableData, + VariableDataSize + ); + if (EFI_ERROR (Status)) { + break; + } + + } + + if (VariableName != NULL) { + FreePool (VariableName); + } + + if (VariableData != NULL) { + FreePool (VariableData); + } + + FileHandleClose (File); + + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Saved NV Variables to NvVars file\n")); + } + + return Status; + +} + + |