1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
|
In this lesson we are going to look how non-volatile variables are stored in the flash image.
Let's remember the `[FD.OVMF]` image structure from the [https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.fdf](https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgX64.fdf):
```
[FD.OVMF]
BaseAddress = $(FW_BASE_ADDRESS)
Size = $(FW_SIZE)
ErasePolarity = 1
BlockSize = $(BLOCK_SIZE)
NumBlocks = $(FW_BLOCKS)
!include VarStore.fdf.inc
$(VARS_SIZE)|$(FVMAIN_SIZE)
FV = FVMAIN_COMPACT
$(SECFV_OFFSET)|$(SECFV_SIZE)
FV = SECFV
```
The variable storage is declared inside the `VarStore.fdf.inc` file, so let's look into it [https://github.com/tianocore/edk2/blob/master/OvmfPkg/VarStore.fdf.inc](https://github.com/tianocore/edk2/blob/master/OvmfPkg/VarStore.fdf.inc).
As in our case the flash size is 4MB, the define `FD_SIZE_IN_KB` is equal to 4096. Therefore the file content can be simplified to:
```
0x00000000|0x00040000
#NV_VARIABLE_STORE
DATA = {
## This is the EFI_FIRMWARE_VOLUME_HEADER <----- EFI_FIRMWARE_VOLUME_HEADER
# ZeroVector []
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
# FileSystemGuid: gEfiSystemNvDataFvGuid =
# { 0xFFF12B8D, 0x7696, 0x4C8B,
# { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}
0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C,
0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
# FvLength: 0x84000 <----- All Content: EFI_FIRMWARE_VOLUME_HEADER + NV_VARIABLE_STORE + NV_EVENT_LOG + NV_FTW_WORKING + NV_FTW_SPARE
0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
# Signature "_FVH" # Attributes
0x5f, 0x46, 0x56, 0x48, 0xff, 0xfe, 0x04, 0x00,
# HeaderLength
0x48, 0x00,
# CheckSum
0xAF, 0xB8,
# ExtHeaderOffset #Reserved #Revision
0x00, 0x00, 0x00, 0x02,
# Blockmap[0]: 0x84 Blocks * 0x1000 Bytes / Block <------ 0x84 Blocks * 0x1000 Bytes / Block
0x84, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
# Blockmap[1]: End
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
## This is the VARIABLE_STORE_HEADER <------ VARIABLE_STORE_HEADER
# It is compatible with SECURE_BOOT_ENABLE == FALSE as well.
# Signature: gEfiAuthenticatedVariableGuid =
# { 0xaaf32c78, 0x947b, 0x439a,
# { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 }}
0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43,
0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92,
# Size: 0x40000 (gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize) -
# 0x48 (size of EFI_FIRMWARE_VOLUME_HEADER) = 0x3ffb8
# This can speed up the Variable Dispatch a bit.
0xB8, 0xFF, 0x03, 0x00,
# FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
0x5A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}
0x00040000|0x00001000
#NV_EVENT_LOG
0x00041000|0x00001000
#NV_FTW_WORKING
DATA = {
# EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid =
# { 0x9e58292b, 0x7c68, 0x497d, { 0xa0, 0xce, 0x65, 0x0, 0xfd, 0x9f, 0x1b, 0x95 }}
0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49,
0xa0, 0xce, 0x65, 0x0, 0xfd, 0x9f, 0x1b, 0x95,
# Crc:UINT32 #WorkingBlockValid:1, WorkingBlockInvalid:1, Reserved
0x2c, 0xaf, 0x2c, 0x64, 0xFE, 0xFF, 0xFF, 0xFF,
# WriteQueueSize: UINT64
0xE0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}
0x00042000|0x00042000
#NV_FTW_SPARE
```
On a high level this file declares 4 regions:
```
0x00000000..0x00040000 # NV_VARIABLE_STORE
0x00040000..0x00041000 # NV_EVENT_LOG
0x00041000..0x00042000 # NV_FTW_WORKING
0x00042000..0x00084000 # NV_FTW_SPARE
```
In this lesson we are interested in the first region `NV_VARIABLE_STORE` as this region is used as a variable storage in our image.
But as you can see the `NV_VARIABLE_STORE` region data actually starts not from the variable storage itself. At the beggining there is a Firmware Volume header (`EFI_FIRMWARE_VOLUME_HEADER`) for all 4 regions which is filled manually via the hardcoded `DATA` array.
In case you don't remember here is a description for the `EFI_FIRMWARE_VOLUME_HEADER` [https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Pi/PiFirmwareVolume.h](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Pi/PiFirmwareVolume.h):
```
typedef struct {
UINT8 ZeroVector[16];
EFI_GUID FileSystemGuid; // Declares the file system with which the firmware volume is formatted
UINT64 FvLength; // Length in bytes of the complete firmware volume, including the header
UINT32 Signature; // [#define EFI_FVH_SIGNATURE SIGNATURE_32 ('_', 'F', 'V', 'H')]
EFI_FVB_ATTRIBUTES_2 Attributes; // Declares capabilities and power-on defaults for the firmware volume
UINT16 HeaderLength; // Length in bytes of the complete firmware volume header
UINT16 Checksum; // A 16-bit checksum of the firmware volume header. A valid header sums to zero
UINT16 ExtHeaderOffset; // Offset to the extended header (EFI_FIRMWARE_VOLUME_EXT_HEADER) or zero if there is no extended header
UINT8 Reserved[1]; // This field must always be set to zero
UINT8 Revision; // [#define EFI_FVH_REVISION 0x02]
EFI_FV_BLOCK_MAP_ENTRY BlockMap[1]; // An array of run-length encoded FvBlockMapEntry structures. The array is terminated with an entry of {0,0}
} EFI_FIRMWARE_VOLUME_HEADER;
```
We can even check this header with the `VolInfo` utility. As you know the `OVMF_VARS.fd` image is just a `VarStore.fdf.inc` that we are currently discussing:
```
[FD.OVMF_VARS]
BaseAddress = $(FW_BASE_ADDRESS)
Size = $(VARS_SIZE)
ErasePolarity = 1
BlockSize = $(BLOCK_SIZE)
NumBlocks = $(VARS_BLOCKS)
!include VarStore.fdf.inc
```
So let's use the `VolInfo` on this image.
First rebuild `OVMF` image to clear all the current NV content:
```
$ build --platform=OvmfPkg/OvmfPkgX64.dsc --arch=X64 --buildtarget=RELEASE --tagname=GCC5
```
Now use `VolInfo` on the `OVMF_VARS.fd` file:
```
$ VolInfo Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd
VolInfo Version 1.0 Build Developer Build based on Revision: Unknown
Signature: _FVH (4856465F)
Attributes: 4FEFF
EFI_FVB2_READ_DISABLED_CAP
EFI_FVB2_READ_ENABLED_CAP
EFI_FVB2_READ_STATUS
EFI_FVB2_WRITE_DISABLED_CAP
EFI_FVB2_WRITE_ENABLED_CAP
EFI_FVB2_WRITE_STATUS
EFI_FVB2_LOCK_CAP
EFI_FVB2_LOCK_STATUS
EFI_FVB2_STICKY_WRITE
EFI_FVB2_MEMORY_MAPPED
EFI_FVB2_ERASE_POLARITY
EFI_FVB2_READ_LOCK_CAP
EFI_FVB2_READ_LOCK_STATUS
EFI_FVB2_WRITE_LOCK_CAP
EFI_FVB2_WRITE_LOCK_STATUS
EFI_FVB2_ALIGNMENT_16
Header Length: 0x00000048
File System ID: fff12b8d-7696-4c8b-a985-2747075b4f50
Revision: 0x0002
Number of Blocks: 0x00000084
Block Length: 0x00001000
Total Volume Size: 0x00084000
VolInfo: ERROR 0003: error parsing FV image
cannot find the first file in the FV image
```
Here you can see how `VolInfo` was able to parse the Firmware Volume header. `VolInfo` wasn't able to parse the FV content, as it is not the usual FFS files, but a custom data.
So let's invetigate this data ourself. First there is the `VARIABLE_STORE_HEADER` [https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h):
```cpp
typedef struct {
EFI_GUID Signature; // Variable store region signature
UINT32 Size; // Size of entire variable store, including size of variable store header but not including the size of FvHeader
UINT8 Format; // Variable region format state
UINT8 State; // Variable region healthy state
UINT16 Reserved;
UINT32 Reserved1;
} VARIABLE_STORE_HEADER;
```
It uses `gEfiAuthenticatedVariableGuid` GUID for the `Signature` field which is declared in the same file:
```cpp
#define EFI_AUTHENTICATED_VARIABLE_GUID \
{ 0xaaf32c78, 0x947b, 0x439a, { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 } }
extern EFI_GUID gEfiAuthenticatedVariableGuid;
```
Right after the variable storage header there is an array of actual variable records. In our case each record actual variable data is prepended with a `AUTHENTICATED_VARIABLE_HEADER`:
```cpp
///
/// Single Authenticated Variable Data Header Structure.
///
typedef struct {
UINT16 StartId; // Variable Data Start Flag
UINT8 State; // Variable State defined above
UINT8 Reserved;
UINT32 Attributes; // Attributes of variable defined in UEFI specification
UINT64 MonotonicCount; // Associated monotonic count value against replay attack
EFI_TIME TimeStamp; // Associated TimeStamp value against replay attack
UINT32 PubKeyIndex; // Index of associated public key in database
UINT32 NameSize; // Size of variable null-terminated Unicode string name
UINT32 DataSize; // Size of the variable data without this header
EFI_GUID VendorGuid; // A unique identifier for the vendor that produces and consumes this varaible
} AUTHENTICATED_VARIABLE_HEADER;
```
The `StartId` field in this structure contains a marker for the header:
```cpp
///
/// Variable data start flag.
///
#define VARIABLE_DATA 0x55AA
```
So it is easy to see when one variable ends and another one starts. We will need it later.
But if you look at the `OVMF_VARS.fd` right now you wouldn't see any variables:
```
$ hexdump -C Build/OvmfX64/RELEASE_GCC5/FV/OVMF_VARS.fd
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 8d 2b f1 ff 96 76 8b 4c a9 85 27 47 07 5b 4f 50 |.+...v.L..'G.[OP|
00000020 00 40 08 00 00 00 00 00 5f 46 56 48 ff fe 04 00 |.@......_FVH....|
00000030 48 00 af b8 00 00 00 02 84 00 00 00 00 10 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 78 2c f3 aa 7b 94 9a 43 |........x,..{..C|
00000050 a1 80 2e 14 4e c3 77 92 b8 ff 03 00 5a fe 00 00 |....N.w.....Z...|
00000060 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................|
00000070 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00041000 2b 29 58 9e 68 7c 7d 49 a0 ce 65 00 fd 9f 1b 95 |+)X.h|}I..e.....|
00041010 2c af 2c 64 fe ff ff ff e0 0f 00 00 00 00 00 00 |,.,d............|
00041020 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00084000
```
The same goes to the `OVMF.fd` image if you have any doubts:
```
$ hexdump -C Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd | head -n 12
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 8d 2b f1 ff 96 76 8b 4c a9 85 27 47 07 5b 4f 50 |.+...v.L..'G.[OP|
00000020 00 40 08 00 00 00 00 00 5f 46 56 48 ff fe 04 00 |.@......_FVH....|
00000030 48 00 af b8 00 00 00 02 84 00 00 00 00 10 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 78 2c f3 aa 7b 94 9a 43 |........x,..{..C|
00000050 a1 80 2e 14 4e c3 77 92 b8 ff 03 00 5a fe 00 00 |....N.w.....Z...|
00000060 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................|
00000070 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00041000 2b 29 58 9e 68 7c 7d 49 a0 ce 65 00 fd 9f 1b 95 |+)X.h|}I..e.....|
00041010 2c af 2c 64 fe ff ff ff e0 0f 00 00 00 00 00 00 |,.,d............|
00041020 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00084000 ...
```
This is because there is no NVRAM variables right after the re-build.
Now let's run QEMU for the first time with `OVMF.fd` image:
```
$ qemu-system-x86_64 \
-drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
-drive format=raw,file=fat:rw:~/UEFI_disk \
-net none \
-nographic
```
You can verify that there are a lot of UEFI Variables in the system via the `dmpstore` command:
```
Shell> dmpstore -all
...
```
All these variables were actually created on the OVMF execution.
And you can actually see the NV storage with `dmem`, as the flash chip is mapped to the `0xffc00000` address (=(4GB-4MB)). See the `DEFINE FW_BASE_ADDRESS = 0xFFC00000` in the [https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgDefines.fdf.inc](https://github.com/tianocore/edk2/blob/master/OvmfPkg/OvmfPkgDefines.fdf.inc):
```
Shell> dmem 0xffc00000 150
Memory Address 00000000FFC00000 150 Bytes
FFC00000: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
FFC00010: 8D 2B F1 FF 96 76 8B 4C-A9 85 27 47 07 5B 4F 50 *.+...v.L..'G.[OP*
FFC00020: 00 40 08 00 00 00 00 00-5F 46 56 48 FF FE 04 00 *.@......_FVH....* <---- _FVH (EFI_FVH_SIGNATURE)
FFC00030: 48 00 AF B8 00 00 00 02-84 00 00 00 00 10 00 00 *H...............*
FFC00040: 00 00 00 00 00 00 00 00-78 2C F3 AA 7B 94 9A 43 *........x,..{..C*
FFC00050: A1 80 2E 14 4E C3 77 92-B8 FF 03 00 5A FE 00 00 *....N.w.....Z...*
FFC00060: 00 00 00 00 AA 55 3F 00-07 00 00 00 00 00 00 00 *.....U?.........*
FFC00070: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
FFC00080: 00 00 00 00 00 00 00 00-08 00 00 00 04 00 00 00 *................*
FFC00090: 11 40 70 EB 02 14 D3 11-8E 77 00 A0 C9 69 72 3B *.@p......w...ir;*
FFC000A0: 4D 00 54 00 43 00 00 00-01 00 00 00 AA 55 3C 00 *M.T.C........U<.*
FFC000B0: 03 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
FFC000C0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
FFC000D0: 28 00 00 00 01 00 00 00-16 D6 47 4B D6 A8 52 45 *(.........GK..RE*
FFC000E0: 9D 44 CC AD 2E 0F 4C F9-49 00 6E 00 69 00 74 00 *.D....L.I.n.i.t.*
FFC000F0: 69 00 61 00 6C 00 41 00-74 00 74 00 65 00 6D 00 *i.a.l.A.t.t.e.m.*
FFC00100: 70 00 74 00 4F 00 72 00-64 00 65 00 72 00 00 00 *p.t.O.r.d.e.r...*
FFC00110: 01 FF FF FF AA 55 3F 00-03 00 00 00 00 00 00 00 *.....U?.........*
FFC00120: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................*
FFC00130: 00 00 00 00 00 00 00 00-14 00 00 00 19 04 00 00 *................*
FFC00140: 45 49 32 59 44 EC 0D 4C-B1 CD 9D B1 39 DF 07 0C *EI2YD..L....9...*
```
Now finish QEMU and look at the `OVMF.fd` content:
```
$ hexdump -C Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd | head -n 30
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 8d 2b f1 ff 96 76 8b 4c a9 85 27 47 07 5b 4f 50 |.+...v.L..'G.[OP|
00000020 00 40 08 00 00 00 00 00 5f 46 56 48 ff fe 04 00 |.@......_FVH....|
00000030 48 00 af b8 00 00 00 02 84 00 00 00 00 10 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 78 2c f3 aa 7b 94 9a 43 |........x,..{..C|
00000050 a1 80 2e 14 4e c3 77 92 b8 ff 03 00 5a fe 00 00 |....N.w.....Z...|
00000060 00 00 00 00 aa 55 3f 00 07 00 00 00 00 00 00 00 |.....U?.........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 08 00 00 00 04 00 00 00 |................|
00000090 11 40 70 eb 02 14 d3 11 8e 77 00 a0 c9 69 72 3b |.@p......w...ir;|
000000a0 4d 00 54 00 43 00 00 00 01 00 00 00 aa 55 3c 00 |M.T.C........U<.|
000000b0 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 28 00 00 00 01 00 00 00 16 d6 47 4b d6 a8 52 45 |(.........GK..RE|
000000e0 9d 44 cc ad 2e 0f 4c f9 49 00 6e 00 69 00 74 00 |.D....L.I.n.i.t.|
000000f0 69 00 61 00 6c 00 41 00 74 00 74 00 65 00 6d 00 |i.a.l.A.t.t.e.m.|
00000100 70 00 74 00 4f 00 72 00 64 00 65 00 72 00 00 00 |p.t.O.r.d.e.r...|
00000110 01 ff ff ff aa 55 3f 00 03 00 00 00 00 00 00 00 |.....U?.........|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 14 00 00 00 19 04 00 00 |................|
00000140 45 49 32 59 44 ec 0d 4c b1 cd 9d b1 39 df 07 0c |EI2YD..L....9...|
00000150 41 00 74 00 74 00 65 00 6d 00 70 00 74 00 20 00 |A.t.t.e.m.p.t. .|
00000160 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |1...............|
00000170 00 00 00 00 00 01 00 00 00 00 00 00 00 00 41 74 |..............At|
00000180 74 65 6d 70 74 20 31 00 00 00 00 00 00 00 00 00 |tempt 1.........|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000210 00 00 00 00 00 00 00 00 00 00 bc 0c 00 00 00 00 |................|
00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
```
You can see that is has changed. Now the variable storage contains NV variables.
`UEFITool` utility is able to parse NV storages [https://github.com/LongSoft/UEFITool](https://github.com/LongSoft/UEFITool). Load the `OVMF.fd` file to the `UEFITool` program. Here you can see that firmware indeed starts from the Firmware Volume with the `gEfiSystemNvDataFvGuid`:
![1](1.png?raw=true "1")
If you unravel the variable storage, you could see that the storage is filled with `Invalid` entries:
![2](2.png?raw=true "2")
What is that?
Let's re-launch QEMU for the second time. This would update `OVMF.fd` image. If you load the updated image to the tool you could see that now `EfiMtcGuid` that was in the first variable record now changed to `Invalid`:
![3](3.png?raw=true "3")
Although you can find it in the end of the storage:
![4](4.png?raw=true "4")
So what is happening here? If you execute `hexdump` on the start of the `OVMF.fd` image again there wouldn't be any much difference. It looks like the record for `MTC` variable is present:
```
$ hexdump -C Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd | head -n 30
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000010 8d 2b f1 ff 96 76 8b 4c a9 85 27 47 07 5b 4f 50 |.+...v.L..'G.[OP|
00000020 00 40 08 00 00 00 00 00 5f 46 56 48 ff fe 04 00 |.@......_FVH....|
00000030 48 00 af b8 00 00 00 02 84 00 00 00 00 10 00 00 |H...............|
00000040 00 00 00 00 00 00 00 00 78 2c f3 aa 7b 94 9a 43 |........x,..{..C|
00000050 a1 80 2e 14 4e c3 77 92 b8 ff 03 00 5a fe 00 00 |....N.w.....Z...|
00000060 00 00 00 00 aa 55 3c 00 07 00 00 00 00 00 00 00 |.....U<.........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 08 00 00 00 04 00 00 00 |................|
00000090 11 40 70 eb 02 14 d3 11 8e 77 00 a0 c9 69 72 3b |.@p......w...ir;|
000000a0 4d 00 54 00 43 00 00 00 01 00 00 00 aa 55 3c 00 |M.T.C........U<.| <----- MTC
000000b0 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 28 00 00 00 01 00 00 00 16 d6 47 4b d6 a8 52 45 |(.........GK..RE|
000000e0 9d 44 cc ad 2e 0f 4c f9 49 00 6e 00 69 00 74 00 |.D....L.I.n.i.t.|
000000f0 69 00 61 00 6c 00 41 00 74 00 74 00 65 00 6d 00 |i.a.l.A.t.t.e.m.|
00000100 70 00 74 00 4f 00 72 00 64 00 65 00 72 00 00 00 |p.t.O.r.d.e.r...|
00000110 01 ff ff ff aa 55 3f 00 03 00 00 00 00 00 00 00 |.....U?.........|
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000130 00 00 00 00 00 00 00 00 14 00 00 00 19 04 00 00 |................|
00000140 45 49 32 59 44 ec 0d 4c b1 cd 9d b1 39 df 07 0c |EI2YD..L....9...|
00000150 41 00 74 00 74 00 65 00 6d 00 70 00 74 00 20 00 |A.t.t.e.m.p.t. .|
00000160 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |1...............|
00000170 00 00 00 00 00 01 00 00 00 00 00 00 00 00 41 74 |..............At|
00000180 74 65 6d 70 74 20 31 00 00 00 00 00 00 00 00 00 |tempt 1.........|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000210 00 00 00 00 00 00 00 00 00 00 bc 0c 00 00 00 00 |................|
00000220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
```
Although one bit was changed. Let's see it. As we already know, the variable records start with the `0x55AA` (`VARIABLE_DATA`) marker. So let's cut data for this first record in the NV storage, the record which once was `MTC` variable.
Before (working):
```
00000060 aa 55 3f 00 07 00 00 00 00 00 00 00 |.....U?.........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 08 00 00 00 04 00 00 00 |................|
00000090 11 40 70 eb 02 14 d3 11 8e 77 00 a0 c9 69 72 3b |.@p......w...ir;|
000000a0 4d 00 54 00 43 00 00 00 01 00 00 00 |M.T.C....... |
```
After (invalid):
```
00000060 aa 55 3c 00 07 00 00 00 00 00 00 00 |.....U<.........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 08 00 00 00 04 00 00 00 |................|
00000090 11 40 70 eb 02 14 d3 11 8e 77 00 a0 c9 69 72 3b |.@p......w...ir;|
000000a0 4d 00 54 00 43 00 00 00 01 00 00 00 |M.T.C........U<.|
```
The only difference is a change for the `AUTHENTICATED_VARIABLE_HEADER.State` field from `0x3f` to `0x3c`.
In the [https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h](https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Include/Guid/VariableFormat.h) file there are some descriptions for the State field flags:
```cpp
//
/// Variable State flags.
///
#define VAR_IN_DELETED_TRANSITION 0xfe ///< Variable is in obsolete transition.
#define VAR_DELETED 0xfd ///< Variable is obsolete.
#define VAR_HEADER_VALID_ONLY 0x7f ///< Variable header has been valid.
#define VAR_ADDED 0x3f ///< Variable has been completely added.
```
With this terminology the `0x3f` value is (`VAR_ADDED`) and the `0x3c` value is (`VAR_ADDED & VAR_IN_DELETED_TRANSITION & VAR_DELETED)`. So our `MTC` variable was deleted and then created in another record at the end of the table.
Why?
In the real scenario the NV variables are stored in a physical flash chip. And this puts some limitations for the NV variable driver. For example it is not possible to erase random bytes of data, as you are only able to erase data in blocks for a flash chip (and a block can be pretty big, for example 1024 bytes). So to change one variable record you would have to delete and recreate a bunch of data.
Also there is a wear-leveling issue. Flash chips have a limited count of block erase operations. Around that time (or even earlier) the block erase wouldn't be completed successfully, so you can't use this block for your data no more. So if some variable changes often, modifying it in-place would wear-level flash chip pretty quickly.
Important conclusion is that it is better to append new records to the end of a storage, than change current records. And this is what we've observed.
Also after the block erase operation in a flash chip all bytes in that block are set to 0xFF (all 1's). The write operation is able to set bits from 1's to 0's (and not in the opposite way!). This is why it is possible to change `0x3f` value (`VAR_ADDED`) to `0x3c` value (`VAR_ADDED & VAR_IN_DELETED_TRANSITION & VAR_DELETED)` without any block erase operation.
# `SetVariableExample` test
For another test let's use our `SetVariableExample` application that we've created earlier.
As a control method let's use `UEFIExtract` utility from the same UEFITool repo [https://github.com/LongSoft/UEFITool](https://github.com/LongSoft/UEFITool). The `UEFIExtract` is a command-line utility, therefore it is easier to use it from CLI:
```
$ ./UEFIExtract
UEFIExtract NE alpha 60 (Aug 27 2022)
Usage: UEFIExtract imagefile - generate report and dump only leaf tree items into .dump folder.
UEFIExtract imagefile all - generate report and dump all tree items.
UEFIExtract imagefile unpack - generate report and dump all tree items in one dir.
UEFIExtract imagefile dump - only generate dump, no report needed.
UEFIExtract imagefile report - only generate report, no dump needed.
UEFIExtract imagefile GUID_1 ... [ -o FILE_1 ... ] [ -m MODE_1 ... ] [ -t TYPE_1 ... ] -
Dump only FFS file(s) with specific GUID(s), without report.
Type is section type or FF to ignore. Mode is one of: all, body, header, info, file.
Return value is a bit mask where 0 at position N means that file with GUID_N was found and unpacked, 1 otherwise.
```
In our test we would be checking `report` command output. Copy current `OVMF.fd` image, generate report for it and check the result. In the output below I've included only the NV storage part of the output:
```
$ cp <...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd ./
$ ./UEFIExtract OVMF.fd report
$ cat OVMF.fd.report.txt
Type | Subtype | Base | Size | CRC32 | Name
Image | UEFI | 00000000 | 00400000 | 90EAAFFF | UEFI image
Volume | NVRAM | 00000000 | 00084000 | 393BD56A | - FFF12B8D-7696-4C8B-A985-2747075B4F50
VSS2 store | | 00000048 | 0003FFB8 | E9A424DE | -- VSS2 store
VSS entry | Invalid | 00000064 | 00000048 | 0B1B3B2A | --- Invalid
VSS entry | Invalid | 000000AC | 00000065 | 43FDF85A | --- Invalid
VSS entry | Auth | 00000114 | 00000469 | 0E8A7CD5 | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 1
VSS entry | Invalid | 00000580 | 00000066 | B6ACED62 | --- Invalid
VSS entry | Auth | 000005E8 | 00000469 | 002D804B | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 2
VSS entry | Invalid | 00000A54 | 00000067 | B8B27872 | --- Invalid
VSS entry | Auth | 00000ABC | 00000469 | B36029FE | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 3
VSS entry | Invalid | 00000F28 | 00000068 | 70B54595 | --- Invalid
VSS entry | Auth | 00000F90 | 00000469 | 1D627977 | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 4
VSS entry | Invalid | 000013FC | 00000069 | A2979B6A | --- Invalid
VSS entry | Auth | 00001468 | 00000469 | AE2FD0C2 | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 5
VSS entry | Invalid | 000018D4 | 0000006A | 4C9EA244 | --- Invalid
VSS entry | Auth | 00001940 | 00000469 | A0882C5C | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 6
VSS entry | Invalid | 00001DAC | 0000006B | B0DA855D | --- Invalid
VSS entry | Auth | 00001E18 | 00000469 | 13C585E9 | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 7
VSS entry | Auth | 00002284 | 0000006C | 27A057BF | --- 4B47D616-A8D6-4552-9D44-CCAD2E0F4CF9 | InitialAttemptOrder
VSS entry | Auth | 000022F0 | 00000469 | 27FD8B0F | --- 59324945-EC44-4C0D-B1CD-9DB139DF070C | Attempt 8
VSS entry | Invalid | 0000275C | 00000052 | D508D27E | --- Invalid
VSS entry | Auth | 000027B0 | 0000008C | 3D8F8004 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Boot0000
VSS entry | Auth | 0000283C | 0000004E | C451514A | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Timeout
VSS entry | Auth | 0000288C | 00000059 | A16D4B75 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | PlatformLang
VSS entry | Auth | 000028E8 | 0000004A | A8279A0B | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Lang
VSS entry | Auth | 00002934 | 00000057 | 66192BDE | --- 04B37FE8-F6AE-480B-BDD5-37D98C5E89AA | VarErrorFlag
VSS entry | Invalid | 0000298C | 0000006A | BEDF0159 | --- Invalid
VSS entry | Invalid | 000029F8 | 00000093 | 133B4E89 | --- Invalid
VSS entry | Invalid | 00002A8C | 000000B3 | 2770C13D | --- Invalid
VSS entry | Invalid | 00002B40 | 00000093 | C5BEF246 | --- Invalid
VSS entry | Invalid | 00002BD4 | 000000DC | 94308B4F | --- Invalid
VSS entry | Invalid | 00002CB0 | 000000FC | 62B4896C | --- Invalid
VSS entry | Invalid | 00002DAC | 000000DC | A2F145DC | --- Invalid
VSS entry | Invalid | 00002E88 | 000000FA | 87389AFC | --- Invalid
VSS entry | Invalid | 00002F84 | 0000013B | 3039AE62 | --- Invalid
VSS entry | Invalid | 000030C0 | 00000139 | 76E1A61F | --- Invalid
VSS entry | Invalid | 000031FC | 0000011B | 82C266DE | --- Invalid
VSS entry | Invalid | 00003318 | 0000014A | FDC1C84F | --- Invalid
VSS entry | Invalid | 00003464 | 00000159 | 5B9AE227 | --- Invalid
VSS entry | Auth | 000035C0 | 0000005A | 7DB8CD95 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Key0000
VSS entry | Auth | 0000361C | 0000005A | 15F2ABEA | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Key0001
VSS entry | Invalid | 00003678 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00003788 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Invalid | 0000385C | 000000B1 | 1C806E85 | --- Invalid
VSS entry | Invalid | 00003910 | 00000101 | 7CAC293B | --- Invalid
VSS entry | Invalid | 00003A14 | 000000C2 | B7FD38BC | --- Invalid
VSS entry | Invalid | 00003AD8 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Invalid | 00003BAC | 00000093 | C5BEF246 | --- Invalid
VSS entry | Invalid | 00003C40 | 00000054 | 7D53807A | --- Invalid
VSS entry | Auth | 00003C94 | 000000B8 | 7F33223C | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Boot0001
VSS entry | Invalid | 00003D4C | 00000056 | B4142854 | --- Invalid
VSS entry | Auth | 00003DA4 | 000000BA | 9C2980E3 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Boot0002
VSS entry | Auth | 00003E60 | 00000058 | 4E619F14 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | BootOrder
VSS entry | Auth | 00003EB8 | 000000A6 | C30DAC1A | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | Boot0003
VSS entry | Auth | 00003F60 | 00000098 | 0B4D15F8 | --- 4C19049F-4137-4DD3-9C10-8B97A83FFDFA | MemoryTypeInformation
VSS entry | Auth | 00003FF8 | 00000048 | F204DC79 | --- EB704011-1402-11D3-8E77-00A0C969723B | MTC
VSS entry | Invalid | 00004040 | 000000FA | 7B7FE195 | --- Invalid
VSS entry | Invalid | 0000413C | 0000010B | 44135AC8 | --- Invalid
VSS entry | Invalid | 00004248 | 000000DC | A2F145DC | --- Invalid
VSS entry | Invalid | 00004324 | 0000014A | 5F63563F | --- Invalid
VSS entry | Invalid | 00004470 | 00000139 | 33BCF69E | --- Invalid
VSS entry | Invalid | 000045AC | 0000011B | 82C266DE | --- Invalid
VSS entry | Invalid | 000046C8 | 00000159 | 7E8D1B57 | --- Invalid
VSS entry | Invalid | 00004824 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00004934 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Auth | 00004A08 | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 00004ABC | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 00004BC0 | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00004C84 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00004D58 | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
Free space | | 00004DEC | 0003B214 | 8BE00E0B | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
Free space | | 00042000 | 00042000 | 4BC9AB8D | -- Free space
Padding | Non-empty | 00084000 | 00001000 | 306E7D61 | - Padding
<...>
```
Now let's boot `OVMF.fd` and use our `SetVariableExample.efi` program:
```
FS0:\> SetVariableExample.efi
Delete variable
SetVariableExample <variable name>
Set variable
SetVariableExample <variable name> <attributes> <value>
<attributes> can be <n|b|r>
n - NON_VOLATILE
b - BOOTSERVICE_ACCESS
r - RUNTIME_ACCESS
```
Create new non-volatile variable with it:
```
FS0:\> SetVariableExample.efi TestVariable nbr "Test string 1"
Variable TestVariable was successfully changed
```
Not closing QEMU, copy the updated `OVMF.fd` file to the `UEFIExtract` folder and check it for the `TestVariable` string like this:
```
$ cp <...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd ./ && ./UEFIExtract ./OVMF.fd report && grep TestVariable OVMF.fd.report.txt -A 3 -B 8
VSS entry | Invalid | 000054BC | 00000159 | 7E8D1B57 | --- Invalid
VSS entry | Invalid | 00005618 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00005728 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Auth | 000057FC | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 000058B0 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 000059B4 | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00005A78 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00005B4C | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
VSS entry | Auth | 00005BE0 | 00000072 | 8B20882D | --- BB2A829F-7943-4691-A03A-F1F48519D7E6 | TestVariable
Free space | | 00005C54 | 0003A3AC | D7ACFF21 | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
```
So our variable `TestVariable` was created right at the end of the NV storage.
Now modify variable content with `SetVariableExample` application. In case you don't remember, for that our application simply uses the same `gRT->SetVariable` call:
```
FS0:\> SetVariableExample.efi TestVariable nbr "Test string 2"
Variable TestVariable was successfully changed
```
Look at the NV storage again:
```
$ cp <...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd ./ && ./UEFIExtract ./OVMF.fd report && grep TestVariable OVMF.fd.report.txt -A 3 -B 8
VSS entry | Invalid | 00005618 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00005728 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Auth | 000057FC | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 000058B0 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 000059B4 | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00005A78 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00005B4C | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
VSS entry | Invalid | 00005BE0 | 00000072 | D928D86C | --- Invalid
VSS entry | Auth | 00005C54 | 00000072 | 999527C3 | --- BB2A829F-7943-4691-A03A-F1F48519D7E6 | TestVariable
Free space | | 00005CC8 | 0003A338 | FF1116DF | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
```
The record before was invalidated, and the new `TestVariable` record was created after it.
You can repeat the process:
```
FS0:\> SetVariableExample.efi TestVariable nbr "Test string 3"
Variable TestVariable was successfully changed
FS0:\> SetVariableExample.efi TestVariable nbr "Test string 4"
Variable TestVariable was successfully changed
FS0:\> SetVariableExample.efi TestVariable nbr "Test string 5"
Variable TestVariable was successfully changed
```
The behaviour would be the same:
```
$ cp <...>/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd ./ && ./UEFIExtract ./OVMF.fd report && grep TestVariable OVMF.fd.report.txt -A 3 -B 8
VSS entry | Invalid | 000058B0 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 000059B4 | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00005A78 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00005B4C | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
VSS entry | Invalid | 00005BE0 | 00000072 | D928D86C | --- Invalid
VSS entry | Invalid | 00005C54 | 00000072 | CB9D7782 | --- Invalid
VSS entry | Invalid | 00005CC8 | 00000072 | 732110E7 | --- Invalid
VSS entry | Invalid | 00005D3C | 00000072 | EEF6285E | --- Invalid
VSS entry | Auth | 00005DB0 | 00000072 | 04421F7A | --- BB2A829F-7943-4691-A03A-F1F48519D7E6 | TestVariable
Free space | | 00005E24 | 0003A1DC | B9B6D883 | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
```
Now re-launch QEMU and check the report for the updated `OVMF.fd` file:
```
$ cp ~/tiano/2021/edk2/Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd ./ && ./UEFIExtract ./OVMF.fd report && grep TestVariable OVMF.fd.report.txt -A 20 -B 8
VSS entry | Invalid | 000058B0 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Invalid | 000059B4 | 000000C2 | B7FD38BC | --- Invalid
VSS entry | Invalid | 00005A78 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Invalid | 00005B4C | 00000093 | C5BEF246 | --- Invalid
VSS entry | Invalid | 00005BE0 | 00000072 | D928D86C | --- Invalid
VSS entry | Invalid | 00005C54 | 00000072 | CB9D7782 | --- Invalid
VSS entry | Invalid | 00005CC8 | 00000072 | 732110E7 | --- Invalid
VSS entry | Invalid | 00005D3C | 00000072 | EEF6285E | --- Invalid
VSS entry | Auth | 00005DB0 | 00000072 | 04421F7A | --- BB2A829F-7943-4691-A03A-F1F48519D7E6 | TestVariable
VSS entry | Auth | 00005E24 | 00000048 | D76F83A5 | --- EB704011-1402-11D3-8E77-00A0C969723B | MTC
VSS entry | Invalid | 00005E6C | 000000FA | 7B7FE195 | --- Invalid
VSS entry | Invalid | 00005F68 | 0000010B | 44135AC8 | --- Invalid
VSS entry | Invalid | 00006074 | 000000DC | A2F145DC | --- Invalid
VSS entry | Invalid | 00006150 | 0000014A | 5F63563F | --- Invalid
VSS entry | Invalid | 0000629C | 00000139 | 33BCF69E | --- Invalid
VSS entry | Invalid | 000063D8 | 0000011B | 82C266DE | --- Invalid
VSS entry | Invalid | 000064F4 | 00000159 | 7E8D1B57 | --- Invalid
VSS entry | Invalid | 00006650 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00006760 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Auth | 00006834 | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 000068E8 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 000069EC | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00006AB0 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00006B84 | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
Free space | | 00006C18 | 000393E8 | 1B524AFE | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
Free space | | 00042000 | 00042000 | 4BC9AB8D | -- Free space
Padding | Non-empty | 00084000 | 00001000 | 306E7D61 | - Padding
```
From this output you can see that `MTC`/`ConOut`/`ConIn`/`ErrOut` variables are updated on every boot. You can see that new records for them were added to the database. As for the new `Invalid` records they can be:
- some new variables, that were later deleted on boot
- the same `ConOut`/`ConIn`/`ErrOut` variables that were later updated on boot
As `UEFIExtract` tool prints start addresses for the records, we can easily check the `Invalid` records content.
For example to check this record:
```
VSS entry | Invalid | 00005E6C | 000000FA | 7B7FE195 | --- Invalid
```
You can use:
```
$ hexdump Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd -s 0x5E6C -n 250 -C
```
(it is not possible to use hex values with the `-n` argument, so I had to convert 0x000000FA to decimal number)
This output would look like this:
```
$ hexdump Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd -s 0x5E6C -n 250 -C
00005e6c aa 55 3c 00 07 00 00 00 00 00 00 00 00 00 00 00 |.U<.............|
00005e7c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00005e8c 00 00 00 00 0e 00 00 00 b0 00 00 00 61 df e4 8b |............a...|
00005e9c ca 93 d2 11 aa 0d 00 e0 98 03 2b 8c 43 00 6f 00 |..........+.C.o.| <---- ConOut
00005eac 6e 00 4f 00 75 00 74 00 00 00 02 01 0c 00 d0 41 |n.O.u.t........A|
00005ebc 03 0a 00 00 00 00 01 01 06 00 00 01 02 01 0c 00 |................|
00005ecc d0 41 01 05 00 00 00 00 03 0e 13 00 00 00 00 00 |.A..............|
00005edc 00 c2 01 00 00 00 00 00 08 01 01 03 0a 14 00 53 |...............S|
00005eec 47 c1 e0 be f9 d2 11 9a 0c 00 90 27 3f c1 4d 7f |G..........'?.M.|
00005efc 01 04 00 02 01 0c 00 d0 41 03 0a 00 00 00 00 01 |........A.......|
00005f0c 01 06 00 00 02 02 03 08 00 00 01 01 80 7f 01 04 |................|
00005f1c 00 02 01 0c 00 d0 41 03 0a 00 00 00 00 01 01 06 |......A.........|
00005f2c 00 00 01 02 01 0c 00 d0 41 01 05 01 00 00 00 03 |........A.......|
00005f3c 0e 13 00 00 00 00 00 00 c2 01 00 00 00 00 00 08 |................|
00005f4c 01 01 03 0a 14 00 53 47 c1 e0 be f9 d2 11 9a 0c |......SG........|
00005f5c 00 90 27 3f c1 4d 7f ff 04 00 |..'?.M....|
00005f66
```
As you can see this record was for the `ConOut` variable.
You can repeat the process for the rest of the `Invalid` records
In my case the situation looked like this:
```
VSS entry | Auth | 00005DB0 | 00000072 | 04421F7A | --- BB2A829F-7943-4691-A03A-F1F48519D7E6 | TestVariable
VSS entry | Auth | 00005E24 | 00000048 | D76F83A5 | --- EB704011-1402-11D3-8E77-00A0C969723B | MTC
VSS entry | Invalid | 00005E6C | 000000FA | 7B7FE195 | --- Invalid <--- ConOut
VSS entry | Invalid | 00005F68 | 0000010B | 44135AC8 | --- Invalid <--- ConIn
VSS entry | Invalid | 00006074 | 000000DC | A2F145DC | --- Invalid <--- ErrOut
VSS entry | Invalid | 00006150 | 0000014A | 5F63563F | --- Invalid <--- ConIn
VSS entry | Invalid | 0000629C | 00000139 | 33BCF69E | --- Invalid <--- ConOut
VSS entry | Invalid | 000063D8 | 0000011B | 82C266DE | --- Invalid <--- ErrOut
VSS entry | Invalid | 000064F4 | 00000159 | 7E8D1B57 | --- Invalid <--- ConOut
VSS entry | Invalid | 00006650 | 00000110 | 1C10333C | --- Invalid <--- ConOut
VSS entry | Invalid | 00006760 | 000000D1 | 40EA30C6 | --- Invalid <--- ConOut
VSS entry | Auth | 00006834 | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 000068E8 | 00000101 | 3EF9B03F | --- Invalid <--- ConIn
VSS entry | Auth | 000069EC | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00006AB0 | 000000D2 | B03D07E3 | --- Invalid <--- ErrOut
VSS entry | Auth | 00006B84 | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
Free space | | 00006C18 | 000393E8 | 1B524AFE | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
Free space | | 00042000 | 00042000 | 4BC9AB8D | -- Free space
Padding | Non-empty | 00084000 | 00001000 | 306E7D61 | - Padding
```
So as you see all these `Invalid` records come from 3 variables: `ConIn`/`ConOut`/`ErrOut`. Now think about how many times the NV driver would have to erase the same flash block on every boot if these variables would be modified in-place.
For the final test let's delete our variable
```
FS0:\> SetVariableExample.efi TestVariable
Variable TestVariable was successfully deleted
```
As you can guess this operation simply would mark the variable record as invalid:
```
VSS entry | Invalid | 000058B0 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Invalid | 000059B4 | 000000C2 | B7FD38BC | --- Invalid
VSS entry | Invalid | 00005A78 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Invalid | 00005B4C | 00000093 | C5BEF246 | --- Invalid
VSS entry | Invalid | 00005BE0 | 00000072 | D928D86C | --- Invalid
VSS entry | Invalid | 00005C54 | 00000072 | CB9D7782 | --- Invalid
VSS entry | Invalid | 00005CC8 | 00000072 | 732110E7 | --- Invalid
VSS entry | Invalid | 00005D3C | 00000072 | EEF6285E | --- Invalid
VSS entry | Invalid | 00005DB0 | 00000072 | 67B27F04 | --- Invalid <------
VSS entry | Auth | 00005E24 | 00000048 | D76F83A5 | --- EB704011-1402-11D3-8E77-00A0C969723B | MTC
VSS entry | Invalid | 00005E6C | 000000FA | 7B7FE195 | --- Invalid
VSS entry | Invalid | 00005F68 | 0000010B | 44135AC8 | --- Invalid
VSS entry | Invalid | 00006074 | 000000DC | A2F145DC | --- Invalid
VSS entry | Invalid | 00006150 | 0000014A | 5F63563F | --- Invalid
VSS entry | Invalid | 0000629C | 00000139 | 33BCF69E | --- Invalid
VSS entry | Invalid | 000063D8 | 0000011B | 82C266DE | --- Invalid
VSS entry | Invalid | 000064F4 | 00000159 | 7E8D1B57 | --- Invalid
VSS entry | Invalid | 00006650 | 00000110 | 1C10333C | --- Invalid
VSS entry | Invalid | 00006760 | 000000D1 | 40EA30C6 | --- Invalid
VSS entry | Auth | 00006834 | 000000B1 | 3E31A587 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConOut
VSS entry | Invalid | 000068E8 | 00000101 | 3EF9B03F | --- Invalid
VSS entry | Auth | 000069EC | 000000C2 | 5C48AE3F | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ConIn
VSS entry | Invalid | 00006AB0 | 000000D2 | B03D07E3 | --- Invalid
VSS entry | Auth | 00006B84 | 00000093 | 2D6DFF61 | --- 8BE4DF61-93CA-11D2-AA0D-00E098032B8C | ErrOut
Free space | | 00006C18 | 000393E8 | 1B524AFE | --- Free space
Padding | Empty (0xFF) | 00040000 | 00001000 | F154670A | -- Padding
FTW store | | 00041000 | 00001000 | FE3E7280 | -- FTW store
Free space | | 00042000 | 00042000 | 4BC9AB8D | -- Free space
Padding | Non-empty | 00084000 | 00001000 | 306E7D61 | - Padding
```
|