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:
[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.
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:
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:
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:
#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
:
///
/// 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:
///
/// 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:
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 *[email protected];*
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 |[email protected];|
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. Load the OVMF.fd
file to the UEFITool
program. Here you can see that firmware indeed starts from the Firmware Volume with the gEfiSystemNvDataFvGuid
:
If you unravel the variable storage, you could see that the storage is filled with Invalid
entries:
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
:
Although you can find it in the end of the storage:
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 |[email protected];|
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 |[email protected];|
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 |[email protected];|
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 file there are some descriptions for the State field flags:
//
/// 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.
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. 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