2016-09-09 23:32:05 +02:00
|
|
|
/** @file
|
|
|
|
File System Access for NvVarsFileLib
|
|
|
|
|
|
|
|
Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
|
|
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 = (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
|
2016-09-09 22:32:15 +02:00
|
|
|
contents. NULL if an error occurred.
|
2016-09-09 23:32:05 +02:00
|
|
|
|
|
|
|
**/
|
|
|
|
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.
|
|
|
|
//
|
2016-09-09 22:32:15 +02:00
|
|
|
// This is relevant if the non-volatile variable have been
|
2016-09-09 23:32:05 +02:00
|
|
|
// 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|