
Hello again, dear readers.
Started in the
previous three parts, the conversation about the storage formats of NVRAM used by different implementations of UEFI comes to its logical end. Only one format remained unreviewed - NVAR, which is used in firmware based on the AMI Aptio code base. At one time, AMI was able to “straddle” almost the entire firmware market for desktop and server motherboards, so the NVAR format turned out to be almost more common than the original and “standard” VSS.
If you are wondering what makes AMI's NVRAM storage good and bad, welcome to Cat.
Disclaimer # 4
Repetition - the
mother of stuttering is the basis of memorization, so the author does not leave attempts to convince the reader that picking the firmware is dangerous, and before any changes you should make a backup on the programmer so that it will not be excruciatingly painful for a couple of tasks days (or weeks). The author is still not responsible for anything other than ochepyakok, information about which can be sent to the L / S, you use this knowledge obtained reverse engineering at your own peril and risk.
AMI NVAR
Well, finally managed to get to the last in my list of storage format NVRAM, which I will call NVAR by the signature used in its header. Unlike all the other formats described in the previous parts, NVAR data format are not stored with the volume
GUID FFF12B8D-7696-4C8B-A985-2747075B4F50 (EFI_SYSTEM_NV_DATA_FV_GUID ), and in the usual FFS-file GUID
CEF5B9A3-476D-497F-9FDC -E98143E0422C (NVAR_STORE_FILE_GUID) or
9221315B-30BB-46B5-813E-1B1BF4712BD3 (NVAR_EXTERNAL_DEFAULTS_FILE_GUID).
The file with the first GUID is stored in a separate volume specially designed for NVRAM, most often there are two such volumes - primary and backup, and if something happens with the data or the primary format and the NVRAM driver can determine this, it switches to using backup storage . Sometimes backup storage is filled at the assembly stage of the firmware, but more often space is simply left for it, and it is created during the first start (therefore, the first start after updating the firmware can be quite long). The second file is stored in the DXE volume, has a slightly different platform-specific format, and is used to restore the defaults of some variables if both the main and the additional storage are corrupted unrecoverable.
Since the data in NVAR format is stored inside the file, information about the maximum storage size and where to find it is already available to the firmware due to the UEFI
FFS services, therefore the AMI developers did not invent any additional headers immediately after the header with maximum packaging and without alignment, NVAR recordings begin.
')
The title of this entry looks like this:
struct NVAR_ENTRY_HEADER { UINT32 Signature;
He is on the screenshot:

Everything looks very simple at first; the first
signature is NVAR, then
the record size is 0x5D3, the blank
field is Next , the
attributes are 0x83, the incomprehensible
eight-bit field is 0x00, and
the variable name in ASCII is
StdDefaults .
It turns out that the data format of the data strongly depends on the bits of the
Attributes field, which can be represented as follows:
enum NVAR_ENTRY_ATTRIBUTES { RuntimeVariable = 0x01,
Thus, our 0x83
attributes are in fact
EntryValid +
AsciiName +
RuntimeVariable , and the previously unknown
eight-bit field is an
index in the GUID database . I also note that the length of the name is not stored anywhere, and in order to find the beginning of the data, you need to call
strlen () every time. If the LocalGuid attribute were set, instead of a 1-byte index, the entire GUID would be present at 16. It turns out that the GUID database is in the database (I’ll reveal a secret, it is at the very end of the file and grows up, that is, our null GUID is the last 16 a byte of the file with NVRAM storage, the first is the penultimate 16 bytes and so on) no more than 256 different GUIDs can be stored, but this is enough for any possible NVRAM applications at the moment, and it saves a lot of space.
The same from the
UEFITool NE window:

The attribute values ​​show how the format has evolved over time. Prior to UEFI 2.1, variable NVRAM had only 3 possible attributes:
NV ,
BS ,
RT . The NV attribute is meaningless to store, because Only such variables in the NVRAM repository both fall, and BS and RT are not mutually exclusive, and a healthy change can have either BS or BS + RT, so only one bit was used for these attributes —
RuntimeVariable . Great, it turned out to save as much as 24 bits per variable.
Then it turned out that the physical level of NVRAM is not always reliable, and it would be necessary to read the checksum from the data in order to distinguish the damaged variables from the normal ones, so the
ExtendedHeader bit was
entered , and the checksum was stored at the very end of the record, after the data.
It took some time, and under pressure from Microsoft, another attribute was added to UEFI 2.1 -
HW , used for
WHEA variables. Well, under it brought a bit
HwErrorRecord , it is necessary so it is necessary.
Then, in UEFI 2.3.1C, SecureBoot was unexpectedly added along with two new attributes for variables -
AV and
AW . Fortunately, storing the latter is not very necessary (because there is only one such variable,
dbx ), and the last free bit of
AuthWrite had to be allocated to the first
one .
It turned out to be quite short-lived, already in UEFI 2.4 another attribute was added -
TA , which, suddenly, turned out to have nowhere to pop, because at one time saved as much as 24 bits. As a result, I had to start an additional field in the extended header, which is stored after the data. There also had to store a timestamp and a hash for AV / TA variables.
After all these improvements, the extended title turned out like this:
struct NVAR_EXTENDED_HEADER { UINT8 ExtendedAttributes;
He is on the screenshot:

Total, the
size of the extended header is 0x2C, the
checksum is 0x10, the zero
hash , the
timestamp is 0x5537BB5D and the
attributes are 0x21 (
ExtChecksum +
ExtTimeBased ).
It turns out that in order to get the value of attributes for any variable, it must be disassembled all in its entirety, calculating displacements dynamically and collecting values ​​from several different places in the file. And all this is exactly because once a long time ago they saved as much as 24 bytes. You will develop your format - do not save on matches, do yourself a favor from the future!
But that's not all, because we still have not considered the
DataOnly attribute and the
Next field in the header. They are used to save on GUIDs, names and attributes, if the variable to which you are writing to already exists. Instead of removing the
EntryValid attribute from the old record and writing a new one entirely, the Next field is filled in the header of the old record, and an entry is created in the empty space of the file with the
DataOnly attribute to which this
Next itself refers, and there is no GUID anymore No name, but there is an extended header. Moreover, when the value of a variable is rewritten next time, the
Next field is not corrected in the first record in this unique, simply-connected list, but in the last one, by extending the list. And since there are variables that are updated each time you reboot (yes the same
MonotonicCounter ), very soon NVRAM is filled with data of this variable to the edges, and access to it slows down with each reboot until it turns out that there is no space at all and the NVRAM driver needs to be assembled garbage. Why this is done is another great mystery; I cannot think of a good reason for such behavior.
In UEFITool NE, we had to add the
Go to data action, which works on variables of type
Link (that is, those with a
Next field that is not empty) and selects the last element in a simply linked list, which stores the current data of the variable, and not those were there the devil knows when before:

Access to variables NVRAM works
like this on 95% of desktops and servers of the last 5 years. Look, dear readers, to what the savings in bytes and the hanging of the old format with new crutches bring in a desperate attempt not to rewrite the NVRAM driver again, and do not do so, please.
Conclusion
I do not know what to say about NVAR format. In pursuit of AMI compactness, they managed to sacrifice everything else, and if at first it seemed that this sacrifice was small and imperceptible, with the development of the UEFI specification, the format turned into a local analogue of
Abomination ', assembled from pieces of unclearly what, stitched together, it is unclear how. We were all lucky that AMI's NVRAM driver is good enough to clean up garbage in time and quietly, switch to backup storage when the main one is damaged, start with a destroyed NVRAM, relive the recording “right up to the cover”, etc., but this has been achieved all rather not thanks, but in spite of.
The story of NVRAM formats, I hope, has come to an end, now you know about them almost as much as I myself. Thank you very much for your attention, successful firmware, chips and NVRAMs.