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