diff --git a/UEFITool/uefitool.cpp b/UEFITool/uefitool.cpp index a80e6e2..ac178be 100644 --- a/UEFITool/uefitool.cpp +++ b/UEFITool/uefitool.cpp @@ -176,7 +176,7 @@ void UEFITool::populateUi(const QModelIndex ¤t) ui->menuFileActions->setEnabled(type == Types::File); ui->menuSectionActions->setEnabled(type == Types::Section); ui->menuVariableActions->setEnabled(type == Types::NvramVariableNvar || type == Types::NvramVariableVss || type == Types::NvramVariableFsys); - ui->menuStoreActions->setEnabled(type == Types::NvramStoreVss || type == Types::NvramStoreFdc || type == Types::NvramStoreFsys || type == Types::NvramStoreEvsa); + ui->menuStoreActions->setEnabled(type == Types::NvramStoreVss || type == Types::NvramStoreFdc || type == Types::NvramStoreFsys || type == Types::NvramStoreEvsa || type == Types::NvramFtwBlock); // Enable actions ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current)); @@ -553,6 +553,9 @@ void UEFITool::extract(const UINT8 mode) case Types::NvramStoreEvsa: path = QFileDialog::getSaveFileName(this, tr("Save EVSA store to file"), name + ".evsa", "EVSA store files (*.evsa *.bin);;All files (*)"); break; + case Types::NvramFtwBlock: + path = QFileDialog::getSaveFileName(this, tr("Save FTW block to file"), name + ".ftw", "FTW block files (*.ftw *.bin);;All files (*)"); + break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } @@ -599,6 +602,9 @@ void UEFITool::extract(const UINT8 mode) case Types::NvramStoreEvsa: path = QFileDialog::getSaveFileName(this, tr("Save EVSA store body to file"), name + ".esb", "EVSA store body files (*.esb *.bin);;All files (*)"); break; + case Types::NvramFtwBlock: + path = QFileDialog::getSaveFileName(this, tr("Save FTW block body to file"), name + ".ftb", "FTW block body files (*.ftb *.bin);;All files (*)"); + break; default: path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)"); } @@ -969,6 +975,7 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event) case Types::NvramStoreFdc: case Types::NvramStoreFsys: case Types::NvramStoreEvsa: + case Types::NvramFtwBlock: ui->menuStoreActions->exec(event->globalPos()); break; } diff --git a/common/ffsops.cpp b/common/ffsops.cpp index 5cf8375..aabe202 100644 --- a/common/ffsops.cpp +++ b/common/ffsops.cpp @@ -76,6 +76,8 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr case Types::NvramStoreVss: case Types::NvramStoreFdc: case Types::NvramStoreFsys: + case Types::NvramStoreEvsa: + case Types::NvramFtwBlock: default: name = itemName.replace(' ', '_').replace('/', '_'); } diff --git a/common/ffsparser.cpp b/common/ffsparser.cpp index 68a4904..2c7bb4e 100644 --- a/common/ffsparser.cpp +++ b/common/ffsparser.cpp @@ -1046,7 +1046,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare } // Check for VSS NVRAM volume - if (guid == NVRAM_VSS_STORE_VOLUME_GUID || guid == NVRAM_ADDITIONAL_STORE_VOLUME_GUID) { + if (guid == NVRAM_MAIN_STORE_VOLUME_GUID || guid == NVRAM_ADDITIONAL_STORE_VOLUME_GUID) { isUnknown = false; isNvramVolume = true; } @@ -3200,13 +3200,11 @@ parsing_done: msg(QObject::tr("parseNvarStore: extended data size (%1h) is smaller than required for timestamp and hash (0x28)") .hexarg(extendedData.size()), varIndex); - // Check variable name to be in the list of nesting variables - if (text == QString("StdDefaults") || text == QString("MfgDefaults")) { - STATUS result = parseNvarStore(body, varIndex); - if (result) - msg(QObject::tr("parseNvarStore: parsing of nested NVAR store failed with error \"%1\"").arg(errorCodeToQString(result)), varIndex); - } - + // Try parsing the variable data as NVAR storage if it begins with NVAR signature + if ((subtype == Subtypes::DataNvarVariable || subtype == Subtypes::FullNvarVariable) && + *(const UINT32*)body.constData() == NVRAM_NVAR_VARIABLE_SIGNATURE) + parseNvarStore(body, varIndex); + // Move to next variable offset += variableHeader->Size; } @@ -3281,16 +3279,10 @@ STATUS FfsParser::parseStoreArea(const QByteArray & data, const QModelIndex & in return result; } - // Check that store is fully present in input + // Check that current store is fully present in input if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) { - msg(QObject::tr("parseVssStoreArea: one of stores inside overlaps the end of data"), index); - return ERR_INVALID_VOLUME; - } - - QByteArray store = data.mid(storeOffset, storeSize); - if (storeSize > (UINT32)store.size()) { // Mark the rest as padding and finish the parsing - QByteArray padding = data.right(store.size()); + QByteArray padding = data.mid(storeOffset); // Get info name = QObject::tr("Padding"); @@ -3310,6 +3302,7 @@ STATUS FfsParser::parseStoreArea(const QByteArray & data, const QModelIndex & in break; } + QByteArray store = data.mid(storeOffset, storeSize); // Parse current volume's header QModelIndex storeIndex; result = parseStoreHeader(store, parentOffset + storeOffset, index, storeIndex); @@ -3373,6 +3366,7 @@ STATUS FfsParser::parseStoreArea(const QByteArray & data, const QModelIndex & in case Types::NvramStoreEvsa: parseEvsaStoreBody(current); break; + case Types::NvramFtwBlock: case Types::Padding: // No parsing required break; @@ -3404,34 +3398,70 @@ STATUS FfsParser::findNextStore(const QModelIndex & index, const QByteArray & da msg(QObject::tr("findNextStore: VSS store candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Size, 8), index); continue; } - // All checks passed, store found break; } else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found - // No checks needed + const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos; + if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) { + msg(QObject::tr("findNextStore: FDC store candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(fdcHeader->Size, 8), index); + continue; + } + // All checks passed, store found break; } else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found - // No checks needed + const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos; + if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) { + msg(QObject::tr("findNextStore: Fsys store candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(fsysHeader->Size, 4), index); + continue; + } + // All checks passed, store found break; } else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found if (offset < 4) continue; - const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1); if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) { msg(QObject::tr("findNextStore: EVSA store candidate at offset %1h skipped, has invalid type %2h").hexarg(parentOffset + offset - 4).hexarg2(evsaHeader->Header.Type, 2), index); continue; } - + if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) { + msg(QObject::tr("findNextStore: EVSA store candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(evsaHeader->StoreSize, 8), index); + continue; + } // All checks passed, store found offset -= 4; break; } + else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1) { //Possible FTW block signature found + if (QByteArray(data.constData() + offset, sizeof(EFI_GUID)) != NVRAM_MAIN_STORE_VOLUME_GUID) // Check the whole signature + continue; + + // Detect header variant based on WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)currentPos; + if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + if (ftwHeader->WriteQueueSize == 0 || ftwHeader->WriteQueueSize == 0xFFFFFFFF) { + msg(QObject::tr("findNextStore: FTW block candidate at offset %1h skipped, has invalid body size %2h").hexarg(parentOffset + offset).hexarg2(ftwHeader->WriteQueueSize, 8), index); + continue; + } + } + else if (ftwHeader->WriteQueueSize % 0x10 == 0x00) { // Header with 64 bit WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)currentPos; + if (ftw64Header->WriteQueueSize == 0 || ftw64Header->WriteQueueSize >= 0xFFFFFFFF) { + msg(QObject::tr("findNextStore: FTW block candidate at offset %1h skipped, has invalid body size %2h").hexarg(parentOffset + offset).hexarg2(ftw64Header->WriteQueueSize, 16), index); + continue; + } + } + else // Unknown header + continue; + + // All checks passed, store found + break; + } } - // No more storas found + // No more stores found if (offset >= dataSize - sizeof(UINT32)) return ERR_STORES_NOT_FOUND; @@ -3442,7 +3472,6 @@ STATUS FfsParser::findNextStore(const QModelIndex & index, const QByteArray & da STATUS FfsParser::getStoreSize(const QByteArray & data, const UINT32 storeOffset, UINT32 & storeSize) { - //TODO: add GUID support const UINT32* signature = (const UINT32*)(data.constData() + storeOffset); if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) { const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature; @@ -3460,13 +3489,23 @@ STATUS FfsParser::getStoreSize(const QByteArray & data, const UINT32 storeOffset const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature; storeSize = evsaHeader->StoreSize; } + else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1) { + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature; + if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftwHeader->WriteQueueSize; + } + else { // Header with 64 bit WriteQueueSize + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature; + storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + ftw64Header->WriteQueueSize; + } + } return ERR_SUCCESS; } STATUS FfsParser::parseStoreHeader(const QByteArray & store, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index) { // Parse VSS volume like raw area - //TODO: seen for now - $VSS, $SVS, Fsys, full volume GUID, _FDC, EVSA and paddings + //Seen for now: $VSS, $SVS, Fsys, FTW block, _FDC, EVSA, _FLASH_MAP and paddings const UINT32 dataSize = (UINT32)store.size(); const UINT32* signature = (const UINT32*)store.constData(); @@ -3685,6 +3724,68 @@ STATUS FfsParser::parseStoreHeader(const QByteArray & store, const UINT32 parent // Add tree item index = model->addItem(Types::NvramStoreEvsa, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); } + else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1) { + // The volume must begin with a store to be valid, but after the first one, there can be many variants + if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) { + msg(QObject::tr("parseStoreHeader: volume body is too small even for FTW block header"), parent); + return ERR_SUCCESS; + } + + // Get FTW block headers + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature; + const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature; + + // Check store size + UINT32 ftwBlockSize; + bool has32bitHeader; + if (ftw32BlockHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize + ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftw32BlockHeader->WriteQueueSize; + has32bitHeader = true; + } + else { // Header with 64 bit WriteQueueSize + ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + ftw64BlockHeader->WriteQueueSize; + has32bitHeader = false; + } + if (dataSize < ftwBlockSize) { + msg(QObject::tr("parseStoreHeader: FTW block size %1h (%2) is greater than volume body size %3h (%4)") + .hexarg2(ftwBlockSize, 4).arg(ftwBlockSize) + .hexarg2(dataSize, 8).arg(dataSize), parent); + return ERR_SUCCESS; + } + + // Get parsing data + PARSING_DATA pdata = parsingDataFromQModelIndex(parent); + + // Construct header and body + UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64); + QByteArray header = store.left(headerSize); + QByteArray body = store.mid(headerSize, ftwBlockSize - headerSize); + + // Check block header checksum + QByteArray crcHeader = header; + EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data(); + crcFtwBlockHeader->Crc = pdata.emptyByte ? 0xFFFFFFFF : 0; + crcFtwBlockHeader->State = pdata.emptyByte ? 0xFF : 0; + UINT32 calculatedCrc = crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize); + + // Add info + QString name = QObject::tr("FTW block"); + QString info = QObject::tr("Signature: %1\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nState: %8h\nHeader CRC32: %9") + .arg(guidToQString(ftw32BlockHeader->Signature)) + .hexarg(ftwBlockSize).arg(ftwBlockSize) + .hexarg(headerSize).arg(headerSize) + .hexarg(body.size()).arg(body.size()) + .hexarg2(ftw32BlockHeader->State, 2) + .arg(ftw32BlockHeader->Crc == calculatedCrc ? + QObject::tr("%1h, valid").hexarg2(ftw32BlockHeader->Crc, 8) : + QObject::tr("%1h, invalid, should be %2h").hexarg2(ftw32BlockHeader->Crc, 8).hexarg2(calculatedCrc, 8)); + + // Add correct offset + pdata.offset = parentOffset; + + // Add tree item + index = model->addItem(Types::NvramFtwBlock, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent); + } return ERR_SUCCESS; } @@ -3972,7 +4073,7 @@ STATUS FfsParser::parseFsysStoreBody(const QModelIndex & index) model->addItem(Types::Padding, getPaddingType(body), QObject::tr("Padding"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index); // Show message - msg(QObject::tr("parseFsysStoreBody: variable appears too big, added as padding"), index); + msg(QObject::tr("parseFsysStoreBody: next variable appears too big, added as padding"), index); return ERR_SUCCESS; } @@ -4015,11 +4116,13 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) const UINT32 dataSize = (UINT32)data.size(); UINT32 offset = 0; - // Parse all variables - while (1) { - UINT32 unparsedSize = dataSize - offset; - UINT32 variableSize = 0; + std::map guidMap; + std::map nameMap; + // Parse all variables + UINT32 unparsedSize = dataSize; + while (unparsedSize) { + UINT32 variableSize = 0; QString name; QString info; QByteArray header; @@ -4032,8 +4135,22 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) // Check variable size variableSize = sizeof(EVSA_ENTRY_HEADER); if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) { - //Add the rest as padding and break from cycle - return ERR_SUCCESS; + // Last variable is bad, add the rest as padding and return + QByteArray body = data.mid(offset); + QString info = QObject::tr("Full size: %1h (%2)") + .hexarg(body.size()).arg(body.size()); + + // Add correct offset to parsing data + pdata.offset = parentOffset + offset; + + // Add free space tree item + model->addItem(Types::Padding, getPaddingType(body), QObject::tr("Padding"), QString(), info, QByteArray(), body, FALSE, parsingDataToQByteArray(pdata), index); + + // Show message + if (unparsedSize < entryHeader->Size) + msg(QObject::tr("parseEvsaStoreBody: next variable appears too big, added as padding"), index); + + break; } variableSize = entryHeader->Size; @@ -4056,6 +4173,7 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) QObject::tr("%1h, invalid, should be %2h").hexarg2(guidHeader->Header.Checksum, 2).hexarg2(calculated, 2)) .hexarg2(guidHeader->GuidId, 4); subtype = Subtypes::GuidEvsaEntry; + guidMap.insert_or_assign(guidHeader->GuidId, guidHeader->Guid); } // Name entry else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 || @@ -4074,6 +4192,7 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) QObject::tr("%1h, invalid, should be %2h").hexarg2(nameHeader->Header.Checksum, 2).hexarg2(calculated, 2)) .hexarg2(nameHeader->VarId, 4); subtype = Subtypes::NameEvsaEntry; + nameMap.insert_or_assign(nameHeader->VarId, name); } // Data entry else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 || @@ -4098,7 +4217,18 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) } // Unknown entry else { - ; + header = data.mid(offset, sizeof(EVSA_ENTRY_HEADER)); + body = data.mid(offset + sizeof(EVSA_ENTRY_HEADER), entryHeader->Size - sizeof(EVSA_ENTRY_HEADER)); + name = QObject::tr("Unknown"); + info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)\nType: %7h\nChecksum: %8") + .hexarg(variableSize).arg(variableSize) + .hexarg(header.size()).arg(header.size()) + .hexarg(body.size()).arg(body.size()) + .hexarg2(entryHeader->Type, 2) + .arg(entryHeader->Checksum == calculated ? + QObject::tr("%1h, valid").hexarg2(calculated, 2) : + QObject::tr("%1h, invalid, should be %2h").hexarg2(entryHeader->Checksum, 2).hexarg2(calculated, 2)); + subtype = Subtypes::UnknownEvsaEntry; } // Add correct offset to parsing data @@ -4109,9 +4239,44 @@ STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index) // Move to next variable offset += variableSize; + unparsedSize = dataSize - offset; } - + // Reparse all data variables to detect invalid ones and assign name and test to valid ones + for (int i = 0; i < model->rowCount(index); i++) { + QModelIndex current = index.child(i, 0); + if (model->subtype(current) == Subtypes::DataEvsaEntry) { + QByteArray header = model->header(current); + const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData(); + QString guid; + if (guidMap.count(dataHeader->GuidId)) + guid = guidToQString(guidMap[dataHeader->GuidId]); + QString name; + if (nameMap.count(dataHeader->VarId)) + name = nameMap[dataHeader->VarId]; + + // Check for variable validity + if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, QObject::tr("Invalid")); + msg(QObject::tr("parseEvsaStoreBody: data variable with invalid GuidId and invalid VarId"), current); + } + else if (guid.isEmpty()) { // Guid not found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, QObject::tr("Invalid")); + msg(QObject::tr("parseEvsaStoreBody: data variable with invalid GuidId"), current); + } + else if (name.isEmpty()) { // Name not found + model->setSubtype(current, Subtypes::InvalidEvsaEntry); + model->setName(current, QObject::tr("Invalid")); + msg(QObject::tr("parseEvsaStoreBody: data variable with invalid VarId"), current); + } + else { // Variable is OK, rename it + model->setName(current, guid); + model->setText(current, name); + } + } + } return ERR_SUCCESS; } diff --git a/common/nvram.h b/common/nvram.h index 60f0b1e..4d650ab 100644 --- a/common/nvram.h +++ b/common/nvram.h @@ -40,7 +40,7 @@ extern QString efiTimeToQString(const EFI_TIME & time); // Variable header typedef struct _NVAR_VARIABLE_HEADER { - UINT32 Signature; // NVAR signature + UINT32 Signature; // NVAR UINT16 Size; // Size of the variable including header UINT32 Next : 24; // Offset to the next variable in a list, or empty if latest in the list UINT32 Attributes : 8; // Attributes @@ -64,13 +64,16 @@ typedef struct _NVAR_VARIABLE_HEADER { #define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_AUTH_WRITE 0x10 #define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_TIME_BASED 0x20 + + // // TianoCore VSS and it's variations // // FFF12B8D-7696-4C8B-A985-2747075B4F50 -const QByteArray NVRAM_VSS_STORE_VOLUME_GUID +const QByteArray NVRAM_MAIN_STORE_VOLUME_GUID ("\x8D\x2B\xF1\xFF\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 16); +#define NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 0xFFF12B8D // 00504624-8A59-4EEB-BD0F-6B36E96128E0 const QByteArray NVRAM_ADDITIONAL_STORE_VOLUME_GUID @@ -168,6 +171,28 @@ typedef struct _FDC_VOLUME_HEADER { //VSS_VARIABLE_STORE_HEADER VssHeader; } FDC_VOLUME_HEADER; +// FTW block +// EFI Fault tolerant working block header +#define EFI_FAULT_TOLERANT_WORKING_BLOCK_VALID 0x1 +#define EFI_FAULT_TOLERANT_WORKING_BLOCK_INVALID 0x2 +typedef struct { + EFI_GUID Signature; // NVRAM_MAIN_STORE_VOLUME_GUID + UINT32 Crc; // Crc32 of the header with empty Crc and State fields + UINT8 State; + UINT8 Reserved3[3]; + UINT32 WriteQueueSize; // Size of the FTW block without the header + //UINT8 WriteQueue[WriteQueueSize]; +} EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32; + +typedef struct { + EFI_GUID Signature; // NVRAM_MAIN_STORE_VOLUME_GUID + UINT32 Crc; // Crc32 of the header with empty Crc and State fields + UINT8 State; + UINT8 Reserved3[3]; + UINT64 WriteQueueSize; // Size of the FTW block without the header + //UINT8 WriteQueue[WriteQueueSize]; +} EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64; + // // Apple Fsys // diff --git a/common/types.cpp b/common/types.cpp index b8bb425..e18e5ef 100644 --- a/common/types.cpp +++ b/common/types.cpp @@ -73,6 +73,8 @@ QString itemTypeToQString(const UINT8 type) return QObject::tr("Fsys store"); case Types::NvramStoreEvsa: return QObject::tr("EVSA store"); + case Types::NvramFtwBlock: + return QObject::tr("FTW block"); case Types::NvramVariableNvar: return QObject::tr("NVAR variable"); case Types::NvramVariableVss: @@ -140,6 +142,7 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype) case Types::NvramStoreFdc: case Types::NvramStoreFsys: case Types::NvramStoreEvsa: + case Types::NvramFtwBlock: case Types::NvramVariableFsys: return QString(); case Types::NvramVariableNvar: diff --git a/common/types.h b/common/types.h index 2ee4e9a..0348b61 100644 --- a/common/types.h +++ b/common/types.h @@ -47,6 +47,7 @@ namespace Types { NvramStoreFdc, NvramStoreFsys, NvramStoreEvsa, + NvramFtwBlock, NvramVariableNvar, NvramVariableVss, NvramVariableFsys,