/** @file File System Access for NvVarsFileLib Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "NvVarsFileLib.h" #include #include #include /** 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 ); 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 = (UINTN) 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 occurred. **/ 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; EFI_HANDLE SerializedVariables; 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 %Lu bytes from NV Variables file\n", (UINT64)FileSize )); Status = SerializeVariablesNewInstanceFromBuffer ( &SerializedVariables, FileContents, FileSize ); if (!RETURN_ERROR (Status)) { Status = SerializeVariablesSetSerializedVariables (SerializedVariables); } FreePool (FileContents); FileHandleClose (File); return Status; } /** Writes a variable to indicate that the NV variables have been loaded from the file system. **/ STATIC VOID SetNvVarsVariable ( VOID ) { BOOLEAN VarData; UINTN Size; // // 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 ); } /** 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 relevant 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. // SetNvVarsVariable(); DEBUG (( EFI_D_INFO, "FsAccess.c: Read NV Variables file (size=%Lu)\n", (UINT64)Size )); return Status; } STATIC RETURN_STATUS EFIAPI IterateVariablesCallbackAddAllNvVariables ( IN VOID *Context, IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data ) { EFI_HANDLE Instance; Instance = (EFI_HANDLE) Context; // // Only save non-volatile variables // if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { return RETURN_SUCCESS; } return SerializeVariablesAddVariable ( Instance, VariableName, VendorGuid, Attributes, DataSize, Data ); } /** 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 WriteSize; UINTN VariableDataSize; VOID *VariableData; EFI_HANDLE SerializedVariables; SerializedVariables = NULL; Status = SerializeVariablesNewInstance (&SerializedVariables); if (EFI_ERROR (Status)) { return Status; } Status = SerializeVariablesIterateSystemVariables ( IterateVariablesCallbackAddAllNvVariables, (VOID*) SerializedVariables ); if (EFI_ERROR (Status)) { return Status; } VariableData = NULL; VariableDataSize = 0; Status = SerializeVariablesToBuffer ( SerializedVariables, NULL, &VariableDataSize ); if (Status == RETURN_BUFFER_TOO_SMALL) { VariableData = AllocatePool (VariableDataSize); if (VariableData == NULL) { Status = EFI_OUT_OF_RESOURCES; } else { Status = SerializeVariablesToBuffer ( SerializedVariables, VariableData, &VariableDataSize ); } } SerializeVariablesFreeInstance (SerializedVariables); if (EFI_ERROR (Status)) { return Status; } // // 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; } WriteSize = VariableDataSize; Status = FileHandleWrite (File, &WriteSize, VariableData); if (EFI_ERROR (Status)) { return Status; } FileHandleClose (File); if (!EFI_ERROR (Status)) { // // Write a variable to indicate we've already loaded the // variable data. If it is found, we skip the loading on // subsequent attempts. // SetNvVarsVariable(); DEBUG ((EFI_D_INFO, "Saved NV Variables to NvVars file\n")); } return Status; }