📜 ⬆️ ⬇️

NVRAM device in UEFI-compatible firmware, part two

We continue to talk about the NVRAM formats in UEFI-compatible firmware, which was started in the first part . This time, the agenda includes the Fsys block from Apple’s firmware, the FTW block from the firmware following the precepts of the TianoCore project, and the FDC block, which can be found in firmware based on the Insyde code base.
If you are wondering why you need and what non-NVRAM data looks like that can be found next to NVRAM in firmware from various manufacturers - welcome to Cat.

Disclaimer # 2


As always, the author is not responsible for anything except possible ochepyakok, all bugs are automatically recognized features, you use the information at your own risk, in short, you already know. If you still do not understand what is happening in this article, where to run, what to do, and who is to blame - read here and here , and come back. Returned or did not leave? Well, go on.

Fsys block


Let's start with the block format Fsys, in which Apple stores the settings for a specific model of iron. These settings are then converted into SMBIOS data by using a special DXE driver (the ones that can be read from the OS using the dmidecode utility).

The format, of course, is specific to Apple’s firmware, and “was always”, i.e. It is found in both the earliest and the newest firmware. A data block in this format is usually located right behind the first two VSS storages (main and backup), and, in theory, should not be changed by the user, and the data from it is not accessible through UEFI runtime services, so I do not consider them to be NVRAM , but if they are (not) lucky to lie with NVRAM in one volume - they had to deal with them, especially since the format turned out to be trivial, and it can be almost all shown in one screenshot without any C-structures. The block header and variables look like this:

The block starts with a four-byte signature , usually it is Fsys (on relatively old machines there was still a second block of the same format with a Gaid signature, on more modern ones everything is put into one Fsys block). The signature is followed by 5 unknown bytes , in all the dumps that I have, they are equal to 0x01 0x0E 0x00 0x00 0x00, but you understand that they may differ. They are followed by a two-byte total block size , immediately after which the variables begin, without any alignment and with maximum packing. The variable (better to call this entity “record” because Apple does not allow the end user to change this data) is stored as follows: single-byte name length , ASCII name , two-byte data length , and the data itself . It turns out that the screenshot shows, in addition to the title, 3 and a half records - dckt , dckh , dck_ and overrides .
Pay attention to the beginning of the latest data: BZ is the signature , h is an indication of the use of a Huffman code , 1 is an indication of the size of the block , and then the number Pi ... Ba, the old acquaintance, is Bzip2 format ! We get, unpack, and we get this:
overrides.txt
ADD_DEVICE () [class = "USBPort", type = "USB 2.0", location = "right", speed = "480", uhci-id = "0xFA133000", ehci-id = "0xFA130000"]
ADD_DEVICE () [class = "USBPort", type = "USB 2.0", location = "left", speed = "480", uhci-id = "0xFD113000", ehci-id = "0xFD110000"]
ADD_DEVICE () [class = "SensorController", location = "U5510", model = "EMC1413", device-key = "SensorController @ U5510"]
ADD_DEVICE () [class = "SensorController", location = "U5530", model = "EMC1704", device-key = "SensorController @ U5530"]
ADD_DEVICE () [class = "ThunderboltPort", location = "Left", port1 = "1", port2 = "2", mcuaddr = "0x26"]
SET_PROPERTY (class = "Processor") [ptype = "iCore"]
SET_PROPERTY (class = "Battery") [cell-count = "2"]
SET_PROPERTY (class = "Sensor" & location = "VC0C") [low-limit = "0.0", high-limit = "1.23", type = "Voltage", description = "VOLTAGE Sensor CPU 0 VCore"]
SET_PROPERTY (class = "Sensor" & location = "VP0R") [low-limit = "7.2", high-limit = "8.9", type = "Voltage", description = "VOLTAGE Sensor PBus 0 Rail"]
SET_PROPERTY (class = "Sensor" & location = "VN0C") [low-limit = "0.0", high-limit = "1.23", type = "Voltage", description = "VOLTAGE Sensor AGX 0 VCore"]
SET_PROPERTY (class = "Sensor" & location = "VD0R") [low-limit = "13.5", high-limit = "15.5", type = "Voltage", description = "VOLTAGE Sensor DCIN"]
SET_PROPERTY (class = "Sensor" & location = "VC1R") [low-limit = "7.2", high-limit = "8.9", type = "Voltage", description = "VOLTAGE Sensor CPU highside"]
SET_PROPERTY (class = "Sensor" & location = "ID0R") [low-limit = "0.0", high-limit = "3.5", type = "Current", description = "CURRENT Sensor DC IN 0 Rail AMON"]
SET_PROPERTY (class = "Sensor" & location = "IB0R") [low-limit = "0.0", high-limit = "10.0", type = "Current", description = "CURRENT Sensor CHGR 0 Rail BMON"]
SET_PROPERTY (class = "Sensor" & location = "IC0R") [low-limit = "0.0", high-limit = "12.0", type = "Current", description = "CURRENT Sensor Chipset 0 INA Highside"]
SET_PROPERTY (class = "Sensor" & location = "IC1R") [low-limit = "0.0", high-limit = "12.0", type = "Current", description = "CURRENT Sensor Chipset 0 SMBUS Highside"]
SET_PROPERTY (class = "Sensor" & location = "IC0C") [low-limit = "0.0", high-limit = "25.0", type = "Current", description = "CURRENT Sensor CPU 0 VCore"]
SET_PROPERTY (class = "Sensor" & location = "IN0C") [low-limit = "0.0", high-limit = "10.0", type = "Current", description = "CURRENT Sensor IG GFX VCore"]
SET_PROPERTY (class = "Sensor" & location = "IM0R") [low-limit = "0.0", high-limit = "10.0", type = "Current", description = "CURRENT Sensor Memory Power"]
SET_PROPERTY (class = "Sensor" & location = "Ts0P") [noise-tolerance = "3.0", low-limit = "10", high-limit = "50", type = "Temperature", description = "TEMP Sensor MLB "]
SET_PROPERTY (class = "Sensor" & location = "TPCD") [noise-tolerance = "3.0", low-limit = "15", high-limit = "100", type = "Temperature", description = "TEMP Sensor PCH "]
SET_PROPERTY (class = "Sensor" & location = "TC0D") [noise-tolerance = "3.0", low-limit = "10", high-limit = "110", type = "Temperature", description = "TEMP Sensor CPU 0 Die "]
SET_PROPERTY (class = "Sensor" & location = "TC0P") [noise-tolerance = "3.0", low-limit = "20", high-limit = "87", type = "Temperature", description = "TEMP Sensor CPU 0 Proximity »]
SET_PROPERTY (class = "Sensor" & location = "TM0P") [noise-tolerance = "3.0", low-limit = "20", high-limit = "75", type = "Temperature", description = "TEMP Sensor Inlet "]
SET_PROPERTY (class = "Sensor" & location = "Ta0P") [noise-tolerance = "3.0", low-limit = "20", high-limit = "80", type = "Temperature", description = "TEMP Sensor Inlet "]
SET_PROPERTY (class = "Sensor" & location = "Tm1P") [noise-tolerance = "3.0", low-limit = "10", high-limit = "65", type = "Temperature", description = "TEMP Sensor Inlet "]
SET_PROPERTY (class = "Sensor" & location = "Tm0P") [noise-tolerance = "3.0", low-limit = "10", high-limit = "65", type = "Temperature", description = "TEMP Sensor Inlet "]
SET_PROPERTY (class = "Sensor" & location = "THSP") [noise-tolerance = "3.0", low-limit = "10", high-limit = "65", type = "Temperature", description = "TEMP Sensor PCH Proximity »]
SET_PROPERTY (class = "Sensor" & location = "Th1H") [noise-tolerance = "3.0", low-limit = "10", high-limit = "65", type = "Temperature", description = "TEMP Sensor Fin Stack "]
SET_PROPERTY (class = "Sensor" & location = "TB1T") [noise-tolerance = "1.0", low-limit = "10", high-limit = "50", type = "Temperature", description = "TEMP Sensor BMU one"]
SET_PROPERTY (class = "Sensor" & location = "TB2T") [noise-tolerance = "1.0", low-limit = "10", high-limit = "50", type = "Temperature", description = "TEMP Sensor BMU 2 "]
SET_PROPERTY (class = "Sensor" & location = "TB0T") [noise-tolerance = "1.0", low-limit = "10", high-limit = "50", type = "Temperature", description = "TEMP Sensor Battery "]
SET_PROPERTY (class = "Sensor" & location = "TC0C") [noise-tolerance = "1.0", low-limit = "15", high-limit = "105", type = "Temperature", description = "TEMP Sensor CPU Die - Digital Core 0 "]
SET_PROPERTY (class = "Sensor" & location = "TC1C") [noise-tolerance = "1.0", low-limit = "15", high-limit = "105", type = "Temperature", description = "TEMP Sensor CPU Die - Digital Core 1 "]
SET_PROPERTY (class = "Sensor" & location = "PCPT") [noise-tolerance = "1.0", low-limit = "0", high-limit = "55", type = "Power", description = "POWER Sensor CPU Package Total Power "]
SET_PROPERTY (class = "Sensor" & location = "PCPG") [noise-tolerance = "1.0", low-limit = "0", high-limit = "22", type = "Power", description = "POWER Sensor CPU Package Gfx Power »]
SET_PROPERTY (class = "Sensor" & location = "PCPC") [noise-tolerance = "1.0", low-limit = "0", high-limit = "33", type = "Power", description = "POWER Sensor CPU Package Core Power "]
SET_PROPERTY (class = "Sensor" & location = "MO_X") [type = "Accelerometer", description = "Motion Sensor"]
SET_PROPERTY (class = "Sensor" & location = "MSC0") [low-limit = "9750", high-limit = "14500", type = "CalibrationKeys", description = "Calibration Key 0"]
SET_PROPERTY (class = "Sensor" & location = "MSLD") [type = "Magnetometer", description = "Magnetometer"]
SET_PROPERTY (class = "HardDrive" & type = "SSD") [throttling-support = "TRUE"]
REMOVE_DEVICE (class = "Sensor") (class = "Sensor" & type = "?")

')
The records in the block follow each other until there is a record with the speaker name EOF , followed by zeros to the very end of the block, and in the last four bytes the CRC32 checksum of the entire block contents is written except for those last four bytes. Apple generally loves CRC32, and they consider it literally for everything - for Fsys records, for VSS NVRAM variables, for EFI executable files, for volumes and for the entire image as well. Integrity to god of integrity, control to the throne of control!

If you manually disassemble the mood, UEFITool NE comes to the rescue , in which the Fsys block with the screenshot above looks like this:


FTW block


The next block in our preparation list is FTW , which is used to support transaction recording in NVRAM, and helps restore its integrity after a power outage during recording. Unfortunately (or, perhaps, fortunately), I haven’t yet come across firmware dumps with any entries in this block, so only the header can be made out, and the content format will have to go to the TianoCore project code . However, the theory is theory, but in practice, instead of one beautiful and pleasant headline, in the firmware there are suddenly two almost identical ones, like this:
struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32 { EFI_GUID Signature; // EFI_SYSTEM_NV_DATA_FV_GUID UINT32 Crc; // CRC32      Crc  State. //      ErasePolarity  NVRAM UINT8 State; //  ,  (0xFE  0x01,    ErasePolarity)   ( ) UINT8 Reserved[3]; //   UINT32 WriteQueueSize; //   ,  UINT32 //UINT8 WriteQueue[WriteQueueSize]; //  }; 

And this:
 struct EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64 { EFI_GUID Signature; // EFI_SYSTEM_NV_DATA_FV_GUID  EDKII_WORKING_BLOCK_SIGNATURE_GUID UINT32 Crc; // ~~~ UINT8 State; // ~~~ UINT8 Reserved[3]; // ~~~ UINT64 WriteQueueSize; //  UINT64,     //UINT8 WriteQueue[WriteQueueSize]; // ~~~ }; 

Such an unexpected variety creates certain difficulties when trying to guess exactly which version of the structure in front of us. Fortunately, most often, the total block size of FTW is multiple to 16 bytes, and therefore it suffices to check WriteQueueSize for divisibility by 16, if divisible - we have the second option, if in the remainder 4 - the first, if in the remainder something else - we found another version of this structure, hooray.

In the screenshot I will show only the second type of title, since the first is found only in some old firmware from the time of the King of Peas:

All specifications, GUID - FFF12B8D-7696-4C8B-A985-2747075B4F50, CRC32 - 0xB0458FB9, the state of the block is valid, the data size is 0xFE0, which is perfectly divided into 16, so the last 4 bytes of the header are still a header, and not already a piece of data.

In UEFITool NE, the same block looks like this:


FDC block


After the UEFI Forum decided to store keys for SecureBoot in NVRAM, it was necessary not only to seriously alter the VSS format (which I mentioned in the first part ), but also to resolve the issue of storing defaults for these variables, and the vendors were again allowed to solve it on their own. One of these solutions from the company Insyde, namely the FDC unit, we now analyze.
The format there is very simple, but it is not at all clear to me what its developer was guided by. The block title is like this:
 struct FDC_VOLUME_HEADER { UINT32 Signature; //  _FDC UINT32 Size; //       //EFI_FIRMWARE_VOLUME_HEADER VolumeHeader; //  NVRAM-,    -   //VSS_VARIABLE_STORE_HEADER VssHeader; //   VSS,       //       ,   }; 

In the screenshot, this whole nightmare looks like this:

Total: the signature is _FDC, the total block size is 0x4000, the header of the NVRAM volume from which nothing is used at all, the VSS storage signature with an unfilled size in a formatted and healthy state, and a region with variables . It turns out that as much as 88 bytes are spent on headers that are not needed for anything at all, my internal optimizer is indignant.

In UEFITool NE, I decided not to display all these unnecessary headers at all, and therefore the same FDC block in it looks like this:


Conclusion


Well, we decided on the format of any strange blocks stored in the middle of the NVRAM volume, EVSA and NVAR, which we will discuss in the third part, remained for dessert. Thanks for attention.

Source: https://habr.com/ru/post/281412/


All Articles