Implement NvVarsFileLib to save and restore non-volatile variables using a file.
[mirror_edk2.git] / OvmfPkg / Library / NvVarsFileLib / FsAccess.c
1 /** @file
2 File System Access for NvVarsFileLib
3
4 Copyright (c) 2004 - 2009, Intel Corporation. <BR>
5 All rights reserved. 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 = 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 occured.
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
237 Status = GetNvVarsFile (FsHandle, TRUE, &File);
238 if (EFI_ERROR (Status)) {
239 DEBUG ((EFI_D_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n"));
240 return Status;
241 }
242
243 NvVarsFileReadCheckup (File, &FileExists, &FileSize);
244 if (FileSize == 0) {
245 FileHandleClose (File);
246 return EFI_UNSUPPORTED;
247 }
248
249 FileContents = FileHandleReadToNewBuffer (File, FileSize);
250 if (FileContents == NULL) {
251 FileHandleClose (File);
252 return EFI_UNSUPPORTED;
253 }
254
255 DEBUG ((
256 EFI_D_INFO,
257 "FsAccess.c: Read %d bytes from NV Variables file\n",
258 FileSize
259 ));
260
261 Status = SetVariablesFromBuffer (FileContents, FileSize);
262
263 FreePool (FileContents);
264 FileHandleClose (File);
265
266 return Status;
267 }
268
269
270 /**
271 Loads the non-volatile variables from the NvVars file on the
272 given file system.
273
274 @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
275
276 @return EFI_STATUS based on the success or failure of load operation
277
278 **/
279 EFI_STATUS
280 LoadNvVarsFromFs (
281 EFI_HANDLE FsHandle
282 )
283 {
284 EFI_STATUS Status;
285 BOOLEAN VarData;
286 UINTN Size;
287
288 DEBUG ((EFI_D_INFO, "FsAccess.c: LoadNvVarsFromFs\n"));
289
290 //
291 // We write a variable to indicate we've already loaded the
292 // variable data. If it is found, we skip the loading.
293 //
294 // This is relevent if the non-volatile variable have been
295 // able to survive a reboot operation. In that case, we don't
296 // want to re-load the file as it would overwrite newer changes
297 // made to the variables.
298 //
299 Size = sizeof (VarData);
300 VarData = TRUE;
301 Status = gRT->GetVariable (
302 L"NvVars",
303 &gEfiSimpleFileSystemProtocolGuid,
304 NULL,
305 &Size,
306 (VOID*) &VarData
307 );
308 if (Status == EFI_SUCCESS) {
309 DEBUG ((EFI_D_INFO, "NV Variables were already loaded\n"));
310 return EFI_ALREADY_STARTED;
311 }
312
313 //
314 // Attempt to restore the variables from the NvVars file.
315 //
316 Status = ReadNvVarsFile (FsHandle);
317 if (EFI_ERROR (Status)) {
318 DEBUG ((EFI_D_INFO, "Error while restoring NV variable data\n"));
319 return Status;
320 }
321
322 //
323 // Write a variable to indicate we've already loaded the
324 // variable data. If it is found, we skip the loading on
325 // subsequent attempts.
326 //
327 Size = sizeof (VarData);
328 VarData = TRUE;
329 gRT->SetVariable (
330 L"NvVars",
331 &gEfiSimpleFileSystemProtocolGuid,
332 EFI_VARIABLE_NON_VOLATILE |
333 EFI_VARIABLE_BOOTSERVICE_ACCESS |
334 EFI_VARIABLE_RUNTIME_ACCESS,
335 Size,
336 (VOID*) &VarData
337 );
338
339 DEBUG ((
340 EFI_D_INFO,
341 "FsAccess.c: Read NV Variables file (size=%d)\n",
342 Size
343 ));
344
345 return Status;
346 }
347
348
349 /**
350 Saves the non-volatile variables into the NvVars file on the
351 given file system.
352
353 @param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
354
355 @return EFI_STATUS based on the success or failure of load operation
356
357 **/
358 EFI_STATUS
359 SaveNvVarsToFs (
360 EFI_HANDLE FsHandle
361 )
362 {
363 EFI_STATUS Status;
364 EFI_FILE_HANDLE File;
365 UINTN VariableNameBufferSize;
366 UINTN VariableNameSize;
367 CHAR16 *VariableName;
368 EFI_GUID VendorGuid;
369 UINTN VariableDataBufferSize;
370 UINTN VariableDataSize;
371 VOID *VariableData;
372 UINT32 VariableAttributes;
373 VOID *NewBuffer;
374
375 //
376 // Open the NvVars file for writing.
377 //
378 Status = GetNvVarsFile (FsHandle, FALSE, &File);
379 if (EFI_ERROR (Status)) {
380 DEBUG ((EFI_D_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
381 return Status;
382 }
383
384 //
385 // Empty the starting file contents.
386 //
387 Status = FileHandleEmpty (File);
388 if (EFI_ERROR (Status)) {
389 FileHandleClose (File);
390 return Status;
391 }
392
393 //
394 // Initialize the variable name and data buffer variables.
395 //
396 VariableNameBufferSize = sizeof (CHAR16);
397 VariableName = AllocateZeroPool (VariableNameBufferSize);
398
399 VariableDataBufferSize = 0;
400 VariableData = NULL;
401
402 for (;;) {
403 //
404 // Get the next variable name and guid
405 //
406 VariableNameSize = VariableNameBufferSize;
407 Status = gRT->GetNextVariableName (
408 &VariableNameSize,
409 VariableName,
410 &VendorGuid
411 );
412 if (Status == EFI_BUFFER_TOO_SMALL) {
413 //
414 // The currently allocated VariableName buffer is too small,
415 // so we allocate a larger buffer, and copy the old buffer
416 // to it.
417 //
418 NewBuffer = AllocatePool (VariableNameSize);
419 if (NewBuffer == NULL) {
420 Status = EFI_OUT_OF_RESOURCES;
421 break;
422 }
423 CopyMem (NewBuffer, VariableName, VariableNameBufferSize);
424 if (VariableName != NULL) {
425 FreePool (VariableName);
426 }
427 VariableName = NewBuffer;
428 VariableNameBufferSize = VariableNameSize;
429
430 //
431 // Try to get the next variable name again with the larger buffer.
432 //
433 Status = gRT->GetNextVariableName (
434 &VariableNameSize,
435 VariableName,
436 &VendorGuid
437 );
438 }
439
440 if (EFI_ERROR (Status)) {
441 if (Status == EFI_NOT_FOUND) {
442 Status = EFI_SUCCESS;
443 }
444 break;
445 }
446
447 //
448 // Get the variable data and attributes
449 //
450 VariableDataSize = VariableDataBufferSize;
451 Status = gRT->GetVariable (
452 VariableName,
453 &VendorGuid,
454 &VariableAttributes,
455 &VariableDataSize,
456 VariableData
457 );
458 if (Status == EFI_BUFFER_TOO_SMALL) {
459 //
460 // The currently allocated VariableData buffer is too small,
461 // so we allocate a larger buffer.
462 //
463 if (VariableDataBufferSize != 0) {
464 FreePool (VariableData);
465 VariableData = NULL;
466 VariableDataBufferSize = 0;
467 }
468 VariableData = AllocatePool (VariableDataSize);
469 if (VariableData == NULL) {
470 Status = EFI_OUT_OF_RESOURCES;
471 break;
472 }
473 VariableDataBufferSize = VariableDataSize;
474
475 //
476 // Try to read the variable again with the larger buffer.
477 //
478 Status = gRT->GetVariable (
479 VariableName,
480 &VendorGuid,
481 &VariableAttributes,
482 &VariableDataSize,
483 VariableData
484 );
485 }
486 if (EFI_ERROR (Status)) {
487 break;
488 }
489
490 //
491 // Skip volatile variables. We only preserve non-volatile variables.
492 //
493 if ((VariableAttributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
494 continue;
495 }
496
497 DEBUG ((
498 EFI_D_INFO,
499 "Saving variable %g:%s to file\n",
500 &VendorGuid,
501 VariableName
502 ));
503
504 //
505 // Write the variable information out to the file
506 //
507 Status = PackVariableIntoFile (
508 File,
509 VariableName,
510 VariableNameSize,
511 &VendorGuid,
512 VariableAttributes,
513 VariableData,
514 VariableDataSize
515 );
516 if (EFI_ERROR (Status)) {
517 break;
518 }
519
520 }
521
522 if (VariableName != NULL) {
523 FreePool (VariableName);
524 }
525
526 if (VariableData != NULL) {
527 FreePool (VariableData);
528 }
529
530 FileHandleClose (File);
531
532 if (!EFI_ERROR (Status)) {
533 DEBUG ((EFI_D_INFO, "Saved NV Variables to NvVars file\n"));
534 }
535
536 return Status;
537
538 }
539
540