Home | History | Annotate | Download | only in NvVarsFileLib
      1 /** @file
      2   File System Access for NvVarsFileLib
      3 
      4   Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "NvVarsFileLib.h"
     16 
     17 #include <Library/BaseMemoryLib.h>
     18 #include <Library/DebugLib.h>
     19 #include <Library/MemoryAllocationLib.h>
     20 
     21 
     22 /**
     23   Open the NvVars file for reading or writing
     24 
     25   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
     26   @param[in]  ReadingFile - TRUE: open the file for reading.  FALSE: writing
     27   @param[out] NvVarsFile - If EFI_SUCCESS is returned, then this is updated
     28                            with the opened NvVars file.
     29 
     30   @return     EFI_SUCCESS if the file was opened
     31 
     32 **/
     33 EFI_STATUS
     34 GetNvVarsFile (
     35   IN  EFI_HANDLE            FsHandle,
     36   IN  BOOLEAN               ReadingFile,
     37   OUT EFI_FILE_HANDLE       *NvVarsFile
     38   )
     39 {
     40   EFI_STATUS                            Status;
     41   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL       *Fs;
     42   EFI_FILE_HANDLE                       Root;
     43 
     44   //
     45   // Get the FileSystem protocol on that handle
     46   //
     47   Status = gBS->HandleProtocol (
     48                   FsHandle,
     49                   &gEfiSimpleFileSystemProtocolGuid,
     50                   (VOID **)&Fs
     51                   );
     52   if (EFI_ERROR (Status)) {
     53     return Status;
     54   }
     55 
     56   //
     57   // Get the volume (the root directory)
     58   //
     59   Status = Fs->OpenVolume (Fs, &Root);
     60   if (EFI_ERROR (Status)) {
     61     return Status;
     62   }
     63 
     64   //
     65   // Attempt to open the NvVars file in the root directory
     66   //
     67   Status = Root->Open (
     68                    Root,
     69                    NvVarsFile,
     70                    L"NvVars",
     71                    ReadingFile ?
     72                      EFI_FILE_MODE_READ :
     73                      (
     74                        EFI_FILE_MODE_CREATE |
     75                        EFI_FILE_MODE_READ |
     76                        EFI_FILE_MODE_WRITE
     77                      ),
     78                    0
     79                    );
     80   if (EFI_ERROR (Status)) {
     81     return Status;
     82   }
     83 
     84   return Status;
     85 }
     86 
     87 
     88 /**
     89   Open the NvVars file for reading or writing
     90 
     91   @param[in]  File - The file to inspect
     92   @param[out] Exists - Returns whether the file exists
     93   @param[out] Size - Returns the size of the file
     94                      (0 if the file does not exist)
     95 
     96 **/
     97 VOID
     98 NvVarsFileReadCheckup (
     99   IN  EFI_FILE_HANDLE        File,
    100   OUT BOOLEAN                *Exists,
    101   OUT UINTN                  *Size
    102   )
    103 {
    104   EFI_FILE_INFO               *FileInfo;
    105 
    106   *Exists = FALSE;
    107   *Size = 0;
    108 
    109   FileInfo = FileHandleGetInfo (File);
    110   if (FileInfo == NULL) {
    111     return;
    112   }
    113 
    114   if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
    115     FreePool (FileInfo);
    116     return;
    117   }
    118 
    119   *Exists = TRUE;
    120   *Size = (UINTN) FileInfo->FileSize;
    121 
    122   FreePool (FileInfo);
    123 }
    124 
    125 
    126 /**
    127   Open the NvVars file for reading or writing
    128 
    129   @param[in]  File - The file to inspect
    130   @param[out] Exists - Returns whether the file exists
    131   @param[out] Size - Returns the size of the file
    132                      (0 if the file does not exist)
    133 
    134 **/
    135 EFI_STATUS
    136 FileHandleEmpty (
    137   IN  EFI_FILE_HANDLE        File
    138   )
    139 {
    140   EFI_STATUS                  Status;
    141   EFI_FILE_INFO               *FileInfo;
    142 
    143   //
    144   // Retrieve the FileInfo structure
    145   //
    146   FileInfo = FileHandleGetInfo (File);
    147   if (FileInfo == NULL) {
    148     return EFI_INVALID_PARAMETER;
    149   }
    150 
    151   //
    152   // If the path is a directory, then return an error
    153   //
    154   if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
    155     FreePool (FileInfo);
    156     return EFI_INVALID_PARAMETER;
    157   }
    158 
    159   //
    160   // If the file size is already 0, then it is empty, so
    161   // we can return success.
    162   //
    163   if (FileInfo->FileSize == 0) {
    164     FreePool (FileInfo);
    165     return EFI_SUCCESS;
    166   }
    167 
    168   //
    169   // Set the file size to 0.
    170   //
    171   FileInfo->FileSize = 0;
    172   Status = FileHandleSetInfo (File, FileInfo);
    173 
    174   FreePool (FileInfo);
    175 
    176   return Status;
    177 }
    178 
    179 
    180 /**
    181   Reads a file to a newly allocated buffer
    182 
    183   @param[in]  File - The file to read
    184   @param[in]  ReadSize - The size of data to read from the file
    185 
    186   @return     Pointer to buffer allocated to hold the file
    187               contents.  NULL if an error occurred.
    188 
    189 **/
    190 VOID*
    191 FileHandleReadToNewBuffer (
    192   IN EFI_FILE_HANDLE            FileHandle,
    193   IN UINTN                      ReadSize
    194   )
    195 {
    196   EFI_STATUS                  Status;
    197   UINTN                       ActualReadSize;
    198   VOID                        *FileContents;
    199 
    200   ActualReadSize = ReadSize;
    201   FileContents = AllocatePool (ReadSize);
    202   if (FileContents != NULL) {
    203     Status = FileHandleRead (
    204                FileHandle,
    205                &ReadSize,
    206                FileContents
    207                );
    208     if (EFI_ERROR (Status) || (ActualReadSize != ReadSize)) {
    209       FreePool (FileContents);
    210       return NULL;
    211     }
    212   }
    213 
    214   return FileContents;
    215 }
    216 
    217 
    218 /**
    219   Reads the contents of the NvVars file on the file system
    220 
    221   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
    222 
    223   @return     EFI_STATUS based on the success or failure of the file read
    224 
    225 **/
    226 EFI_STATUS
    227 ReadNvVarsFile (
    228   IN  EFI_HANDLE            FsHandle
    229   )
    230 {
    231   EFI_STATUS                  Status;
    232   EFI_FILE_HANDLE             File;
    233   UINTN                       FileSize;
    234   BOOLEAN                     FileExists;
    235   VOID                        *FileContents;
    236   EFI_HANDLE                  SerializedVariables;
    237 
    238   Status = GetNvVarsFile (FsHandle, TRUE, &File);
    239   if (EFI_ERROR (Status)) {
    240     DEBUG ((EFI_D_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n"));
    241     return Status;
    242   }
    243 
    244   NvVarsFileReadCheckup (File, &FileExists, &FileSize);
    245   if (FileSize == 0) {
    246     FileHandleClose (File);
    247     return EFI_UNSUPPORTED;
    248   }
    249 
    250   FileContents = FileHandleReadToNewBuffer (File, FileSize);
    251   if (FileContents == NULL) {
    252     FileHandleClose (File);
    253     return EFI_UNSUPPORTED;
    254   }
    255 
    256   DEBUG ((
    257     EFI_D_INFO,
    258     "FsAccess.c: Read %Lu bytes from NV Variables file\n",
    259     (UINT64)FileSize
    260     ));
    261 
    262   Status = SerializeVariablesNewInstanceFromBuffer (
    263              &SerializedVariables,
    264              FileContents,
    265              FileSize
    266              );
    267   if (!RETURN_ERROR (Status)) {
    268     Status = SerializeVariablesSetSerializedVariables (SerializedVariables);
    269   }
    270 
    271   FreePool (FileContents);
    272   FileHandleClose (File);
    273 
    274   return Status;
    275 }
    276 
    277 
    278 /**
    279   Writes a variable to indicate that the NV variables
    280   have been loaded from the file system.
    281 
    282 **/
    283 STATIC
    284 VOID
    285 SetNvVarsVariable (
    286   VOID
    287   )
    288 {
    289   BOOLEAN                        VarData;
    290   UINTN                          Size;
    291 
    292   //
    293   // Write a variable to indicate we've already loaded the
    294   // variable data.  If it is found, we skip the loading on
    295   // subsequent attempts.
    296   //
    297   Size = sizeof (VarData);
    298   VarData = TRUE;
    299   gRT->SetVariable (
    300          L"NvVars",
    301          &gEfiSimpleFileSystemProtocolGuid,
    302          EFI_VARIABLE_NON_VOLATILE |
    303            EFI_VARIABLE_BOOTSERVICE_ACCESS |
    304            EFI_VARIABLE_RUNTIME_ACCESS,
    305          Size,
    306          (VOID*) &VarData
    307          );
    308 }
    309 
    310 
    311 /**
    312   Loads the non-volatile variables from the NvVars file on the
    313   given file system.
    314 
    315   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
    316 
    317   @return     EFI_STATUS based on the success or failure of load operation
    318 
    319 **/
    320 EFI_STATUS
    321 LoadNvVarsFromFs (
    322   EFI_HANDLE                            FsHandle
    323   )
    324 {
    325   EFI_STATUS                     Status;
    326   BOOLEAN                        VarData;
    327   UINTN                          Size;
    328 
    329   DEBUG ((EFI_D_INFO, "FsAccess.c: LoadNvVarsFromFs\n"));
    330 
    331   //
    332   // We write a variable to indicate we've already loaded the
    333   // variable data.  If it is found, we skip the loading.
    334   //
    335   // This is relevant if the non-volatile variable have been
    336   // able to survive a reboot operation.  In that case, we don't
    337   // want to re-load the file as it would overwrite newer changes
    338   // made to the variables.
    339   //
    340   Size = sizeof (VarData);
    341   VarData = TRUE;
    342   Status = gRT->GetVariable (
    343                   L"NvVars",
    344                   &gEfiSimpleFileSystemProtocolGuid,
    345                   NULL,
    346                   &Size,
    347                   (VOID*) &VarData
    348                   );
    349   if (Status == EFI_SUCCESS) {
    350     DEBUG ((EFI_D_INFO, "NV Variables were already loaded\n"));
    351     return EFI_ALREADY_STARTED;
    352   }
    353 
    354   //
    355   // Attempt to restore the variables from the NvVars file.
    356   //
    357   Status = ReadNvVarsFile (FsHandle);
    358   if (EFI_ERROR (Status)) {
    359     DEBUG ((EFI_D_INFO, "Error while restoring NV variable data\n"));
    360     return Status;
    361   }
    362 
    363   //
    364   // Write a variable to indicate we've already loaded the
    365   // variable data.  If it is found, we skip the loading on
    366   // subsequent attempts.
    367   //
    368   SetNvVarsVariable();
    369 
    370   DEBUG ((
    371     EFI_D_INFO,
    372     "FsAccess.c: Read NV Variables file (size=%Lu)\n",
    373     (UINT64)Size
    374     ));
    375 
    376   return Status;
    377 }
    378 
    379 
    380 STATIC
    381 RETURN_STATUS
    382 EFIAPI
    383 IterateVariablesCallbackAddAllNvVariables (
    384   IN  VOID                         *Context,
    385   IN  CHAR16                       *VariableName,
    386   IN  EFI_GUID                     *VendorGuid,
    387   IN  UINT32                       Attributes,
    388   IN  UINTN                        DataSize,
    389   IN  VOID                         *Data
    390   )
    391 {
    392   EFI_HANDLE  Instance;
    393 
    394   Instance = (EFI_HANDLE) Context;
    395 
    396   //
    397   // Only save non-volatile variables
    398   //
    399   if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
    400     return RETURN_SUCCESS;
    401   }
    402 
    403   return SerializeVariablesAddVariable (
    404            Instance,
    405            VariableName,
    406            VendorGuid,
    407            Attributes,
    408            DataSize,
    409            Data
    410            );
    411 }
    412 
    413 
    414 /**
    415   Saves the non-volatile variables into the NvVars file on the
    416   given file system.
    417 
    418   @param[in]  FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
    419 
    420   @return     EFI_STATUS based on the success or failure of load operation
    421 
    422 **/
    423 EFI_STATUS
    424 SaveNvVarsToFs (
    425   EFI_HANDLE                            FsHandle
    426   )
    427 {
    428   EFI_STATUS                  Status;
    429   EFI_FILE_HANDLE             File;
    430   UINTN                       WriteSize;
    431   UINTN                       VariableDataSize;
    432   VOID                        *VariableData;
    433   EFI_HANDLE                  SerializedVariables;
    434 
    435   SerializedVariables = NULL;
    436 
    437   Status = SerializeVariablesNewInstance (&SerializedVariables);
    438   if (EFI_ERROR (Status)) {
    439     return Status;
    440   }
    441 
    442   Status = SerializeVariablesIterateSystemVariables (
    443              IterateVariablesCallbackAddAllNvVariables,
    444              (VOID*) SerializedVariables
    445              );
    446   if (EFI_ERROR (Status)) {
    447     return Status;
    448   }
    449 
    450   VariableData = NULL;
    451   VariableDataSize = 0;
    452   Status = SerializeVariablesToBuffer (
    453              SerializedVariables,
    454              NULL,
    455              &VariableDataSize
    456              );
    457   if (Status == RETURN_BUFFER_TOO_SMALL) {
    458     VariableData = AllocatePool (VariableDataSize);
    459     if (VariableData == NULL) {
    460       Status = EFI_OUT_OF_RESOURCES;
    461     } else {
    462       Status = SerializeVariablesToBuffer (
    463                  SerializedVariables,
    464                  VariableData,
    465                  &VariableDataSize
    466                  );
    467     }
    468   }
    469 
    470   SerializeVariablesFreeInstance (SerializedVariables);
    471 
    472   if (EFI_ERROR (Status)) {
    473     return Status;
    474   }
    475 
    476   //
    477   // Open the NvVars file for writing.
    478   //
    479   Status = GetNvVarsFile (FsHandle, FALSE, &File);
    480   if (EFI_ERROR (Status)) {
    481     DEBUG ((EFI_D_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
    482     return Status;
    483   }
    484 
    485   //
    486   // Empty the starting file contents.
    487   //
    488   Status = FileHandleEmpty (File);
    489   if (EFI_ERROR (Status)) {
    490     FileHandleClose (File);
    491     return Status;
    492   }
    493 
    494   WriteSize = VariableDataSize;
    495   Status = FileHandleWrite (File, &WriteSize, VariableData);
    496   if (EFI_ERROR (Status)) {
    497     return Status;
    498   }
    499 
    500   FileHandleClose (File);
    501 
    502   if (!EFI_ERROR (Status)) {
    503     //
    504     // Write a variable to indicate we've already loaded the
    505     // variable data.  If it is found, we skip the loading on
    506     // subsequent attempts.
    507     //
    508     SetNvVarsVariable();
    509 
    510     DEBUG ((EFI_D_INFO, "Saved NV Variables to NvVars file\n"));
    511   }
    512 
    513   return Status;
    514 
    515 }
    516 
    517 
    518