Initial EVSA format support

- better UI and parsing upcoming
- "storage" replaced with "store" everywhere
This commit is contained in:
Nikolaj Schlej 2016-04-07 08:23:37 +02:00
parent 40200bca12
commit 57e24c7465
12 changed files with 559 additions and 289 deletions

View File

@ -176,11 +176,11 @@ void UEFITool::populateUi(const QModelIndex &current)
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->menuStorageActions->setEnabled(type == Types::NvramStorageVss || type == Types::NvramStorageFdc || type == Types::NvramStorageFsys);
ui->menuStoreActions->setEnabled(type == Types::NvramStoreVss || type == Types::NvramStoreFdc || type == Types::NvramStoreFsys || type == Types::NvramStoreEvsa);
// Enable actions
ui->actionExtract->setDisabled(model->hasEmptyHeader(current) && model->hasEmptyBody(current));
ui->actionGoToData->setEnabled(type == Types::NvramVariableNvar && subtype == Subtypes::LinkNvar);
ui->actionGoToData->setEnabled(type == Types::NvramVariableNvar && subtype == Subtypes::LinkNvarVariable);
// Disable rebuild for now
//ui->actionRebuild->setDisabled(type == Types::Region && subtype == Subtypes::DescriptorRegion);
@ -284,7 +284,7 @@ void UEFITool::search()
void UEFITool::goToData()
{
QModelIndex index = ui->structureTreeView->selectionModel()->currentIndex();
if (!index.isValid() || model->type(index) != Types::NvramVariableNvar || model->subtype(index) != Subtypes::LinkNvar)
if (!index.isValid() || model->type(index) != Types::NvramVariableNvar || model->subtype(index) != Subtypes::LinkNvarVariable)
return;
// Get parent
@ -543,12 +543,15 @@ void UEFITool::extract(const UINT8 mode)
case Types::NvramVariableVss:
path = QFileDialog::getSaveFileName(this, tr("Save variable to file"), name + ".var", "Variable files (*.var *.bin);;All files (*)");
break;
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable storage to file"), name + ".vss", "Variable storage files (*.vss *.bin);;All files (*)");
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable store to file"), name + ".vss", "Variable store files (*.vss *.bin);;All files (*)");
break;
case Types::NvramStorageFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage to file"), name + ".fsys", "Fsys storage files (*.fsys *.bin);;All files (*)");
case Types::NvramStoreFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys store to file"), name + ".fsys", "Fsys store files (*.fsys *.bin);;All files (*)");
break;
case Types::NvramStoreEvsa:
path = QFileDialog::getSaveFileName(this, tr("Save EVSA store to file"), name + ".evsa", "EVSA store files (*.evsa *.bin);;All files (*)");
break;
default:
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
@ -586,12 +589,15 @@ void UEFITool::extract(const UINT8 mode)
case Types::NvramVariableVss:
path = QFileDialog::getSaveFileName(this, tr("Save variable body to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
break;
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable storage body to file"), name + ".vsb", "Variable storage body files (*.vsb *.bin);;All files (*)");
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
path = QFileDialog::getSaveFileName(this, tr("Save variable store body to file"), name + ".vsb", "Variable store body files (*.vsb *.bin);;All files (*)");
break;
case Types::NvramStorageFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys storage body to file"), name + ".fsb", "Fsys storage body files (*.fsb *.bin);;All files (*)");
case Types::NvramStoreFsys:
path = QFileDialog::getSaveFileName(this, tr("Save Fsys store body to file"), name + ".fsb", "Fsys store body files (*.fsb *.bin);;All files (*)");
break;
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;
default:
path = QFileDialog::getSaveFileName(this, tr("Save object to file"), name + ".bin", "Binary files (*.bin);;All files (*)");
@ -959,10 +965,11 @@ void UEFITool::contextMenuEvent(QContextMenuEvent* event)
case Types::NvramVariableFsys:
ui->menuVariableActions->exec(event->globalPos());
break;
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
ui->menuStorageActions->exec(event->globalPos());
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
case Types::NvramStoreFsys:
case Types::NvramStoreEvsa:
ui->menuStoreActions->exec(event->globalPos());
break;
}
}

View File

@ -413,9 +413,9 @@
<addaction name="separator"/>
<addaction name="actionRemove"/>
</widget>
<widget class="QMenu" name="menuStorageActions">
<widget class="QMenu" name="menuStoreActions">
<property name="title">
<string>S&amp;torage</string>
<string>S&amp;tore</string>
</property>
<addaction name="actionExtract"/>
<addaction name="actionExtractBody"/>
@ -436,7 +436,7 @@
<addaction name="menuVolumeActions"/>
<addaction name="menuFileActions"/>
<addaction name="menuSectionActions"/>
<addaction name="menuStorageActions"/>
<addaction name="menuStoreActions"/>
<addaction name="menuVariableActions"/>
<addaction name="separator"/>
<addaction name="menuMessages"/>

View File

@ -80,7 +80,7 @@ typedef UINT8 STATUS;
#define ERR_DIR_CREATE 34
#define ERR_TRUNCATED_IMAGE 35
#define ERR_INVALID_CAPSULE 36
#define ERR_STORAGES_NOT_FOUND 37
#define ERR_STORES_NOT_FOUND 37
#define ERR_NOT_IMPLEMENTED 0xFF
// UDK porting definitions

View File

@ -73,9 +73,9 @@ STATUS FfsOperations::extract(const QModelIndex & index, QString & name, QByteAr
case Types::Image:
case Types::Region:
case Types::Padding:
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
case Types::NvramStoreFsys:
default:
name = itemName.replace(' ', '_').replace('/', '_');
}

View File

@ -1029,7 +1029,7 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
// Check for volume structure to be known
bool isUnknown = true;
bool isVssNvramVolume = false;
bool isNvramVolume = false;
UINT8 ffsVersion = 0;
// Check for FFS v2 volume
@ -1046,9 +1046,9 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
}
// Check for VSS NVRAM volume
if (guid == NVRAM_VSS_STORAGE_VOLUME_GUID) {
if (guid == NVRAM_VSS_STORE_VOLUME_GUID || guid == NVRAM_ADDITIONAL_STORE_VOLUME_GUID) {
isUnknown = false;
isVssNvramVolume = true;
isNvramVolume = true;
}
// Check volume revision and alignment
@ -1179,8 +1179,8 @@ STATUS FfsParser::parseVolumeHeader(const QByteArray & volume, const UINT32 pare
subtype = Subtypes::Ffs2Volume;
else if (ffsVersion == 3)
subtype = Subtypes::Ffs3Volume;
else if (isVssNvramVolume)
subtype = Subtypes::VssNvramVolume;
else if (isNvramVolume)
subtype = Subtypes::NvramVolume;
}
index = model->addItem(Types::Volume, subtype, name, text, info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
@ -1345,8 +1345,8 @@ STATUS FfsParser::parseVolumeBody(const QModelIndex & index)
UINT32 offset = pdata.offset;
// Parse VSS NVRAM volumes with a dedicated function
if (model->subtype(index) == Subtypes::VssNvramVolume)
return parseStorageArea(volumeBody, index);
if (model->subtype(index) == Subtypes::NvramVolume)
return parseStoreArea(volumeBody, index);
if (pdata.ffsVersion != 2 && pdata.ffsVersion != 3) // Don't parse unknown volumes
return ERR_SUCCESS;
@ -1622,10 +1622,10 @@ STATUS FfsParser::parseFileHeader(const QByteArray & file, const UINT32 parentOf
isVtf = true;
text = QObject::tr("Volume Top File");
}
// Check if the file is NVRAM storage with NVAR format
else if (guid == NVRAM_NVAR_STORAGE_FILE_GUID || guid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) {
// Mark the file as NVAR storage
pdata.file.format = RAW_FILE_FORMAT_NVAR_STORAGE;
// Check if the file is NVRAM store with NVAR format
else if (guid == NVRAM_NVAR_STORE_FILE_GUID || guid == NVRAM_NVAR_EXTERNAL_DEFAULTS_FILE_GUID) {
// Mark the file as NVAR store
pdata.file.format = RAW_FILE_FORMAT_NVAR_STORE;
}
// Construct parsing data
@ -1700,9 +1700,9 @@ STATUS FfsParser::parseFileBody(const QModelIndex & index)
// Get data from parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
// Parse NVAR storage
if (pdata.file.format == RAW_FILE_FORMAT_NVAR_STORAGE)
return parseNvarStorage(model->body(index), index);
// Parse NVAR store
if (pdata.file.format == RAW_FILE_FORMAT_NVAR_STORE)
return parseNvarStore(model->body(index), index);
return parseRawArea(model->body(index), index);
}
@ -2440,7 +2440,7 @@ STATUS FfsParser::parseVersionSectionBody(const QModelIndex & index)
return ERR_INVALID_PARAMETER;
// Add info
model->addInfo(index, QObject::tr("\nVersion string: %1").arg(QString::fromUtf16((const ushort*)model->body(index).constData())));
model->addInfo(index, QObject::tr("\nVersion string: %1").arg(QString::fromUtf16((const CHAR16*)model->body(index).constData())));
return ERR_SUCCESS;
}
@ -2575,7 +2575,7 @@ STATUS FfsParser::parseUiSectionBody(const QModelIndex & index)
if (!index.isValid())
return ERR_INVALID_PARAMETER;
QString text = QString::fromUtf16((const ushort*)model->body(index).constData());
QString text = QString::fromUtf16((const CHAR16*)model->body(index).constData());
// Add info
model->addInfo(index, QObject::tr("\nText: %1").arg(text));
@ -2881,7 +2881,7 @@ STATUS FfsParser::addOffsetsRecursive(const QModelIndex & index)
return ERR_SUCCESS;
}
STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex & index)
STATUS FfsParser::parseNvarStore(const QByteArray & data, const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
@ -2892,10 +2892,10 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
UINT32 parentOffset = pdata.offset + model->header(index).size();
// Rename parent file
model->setText(model->findParentOfType(index, Types::File), QObject::tr("NVAR storage"));
model->setText(model->findParentOfType(index, Types::File), QObject::tr("NVAR store"));
UINT32 offset = 0;
UINT32 guidsInStorage = 0;
UINT32 guidsInStore = 0;
// Parse all variables
while (1) {
@ -2918,14 +2918,14 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
UINT64 timestamp = 0;
QByteArray hash;
UINT8 subtype = Subtypes::FullNvar;
UINT8 subtype = Subtypes::FullNvarVariable;
QString name;
QString text;
QByteArray header;
QByteArray body;
QByteArray extendedData;
UINT32 guidAreaSize = guidsInStorage * sizeof(EFI_GUID);
UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
// Get variable header
@ -2949,7 +2949,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
else {
// Nothing is parsed yet, but the file is not empty
if (!offset) {
msg(QObject::tr("parseNvarStorage: file can't be parsed as NVAR variables storage"), index);
msg(QObject::tr("parseNvarStore: file can't be parsed as NVAR variables store"), index);
return ERR_SUCCESS;
}
@ -2966,13 +2966,13 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
// Add tree item
model->addItem(type, subtype, name, QString(), info, QByteArray(), padding, FALSE, parsingDataToQByteArray(pdata), index);
// Add GUID storage area
// Add GUID store area
QByteArray guidArea = data.right(guidAreaSize);
// Get info
name = QObject::tr("GUID storage area");
info = QObject::tr("Full size: %1h (%2)\nGUIDs in storage: %3")
name = QObject::tr("GUID store area");
info = QObject::tr("Full size: %1h (%2)\nGUIDs in store: %3")
.hexarg(guidArea.size()).arg(guidArea.size())
.arg(guidsInStorage);
.arg(guidsInStore);
// Construct parsing data
pdata.offset = parentOffset + offset + padding.size();
// Add tree item
@ -2999,7 +2999,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
// Add next node information to parsing data
if (variableHeader->Next != lastVariableFlag) {
subtype = Subtypes::LinkNvar;
subtype = Subtypes::LinkNvarVariable;
pdata.nvram.nvar.next = variableHeader->Next;
}
@ -3081,7 +3081,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
text = model->text(nvarIndex);
if (variableHeader->Next == lastVariableFlag)
subtype = Subtypes::DataNvar;
subtype = Subtypes::DataNvarVariable;
}
isDataOnly = true;
@ -3091,7 +3091,7 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
// Get variable name
{
UINT32 nameOffset = (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) ? sizeof(EFI_GUID) : 1; // GUID can be stored with the variable or in a separate storage, so there will only be an index of it
UINT32 nameOffset = (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) ? sizeof(EFI_GUID) : 1; // GUID can be stored with the variable or in a separate store, so there will only be an index of it
CHAR8* namePtr = (CHAR8*)(variableHeader + 1) + nameOffset;
UINT32 nameSize = 0;
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
@ -3107,13 +3107,13 @@ STATUS FfsParser::parseNvarStorage(const QByteArray & data, const QModelIndex &
if (variableHeader->Attributes & NVRAM_NVAR_VARIABLE_ATTRIB_GUID) { // GUID is strored in the variable itself
name = guidToQString(*(EFI_GUID*)(variableHeader + 1));
}
// GUID is stored in GUID list at the end of the storage
// GUID is stored in GUID list at the end of the store
else {
guidIndex = *(UINT8*)(variableHeader + 1);
if (guidsInStorage < guidIndex + 1)
guidsInStorage = guidIndex + 1;
if (guidsInStore < guidIndex + 1)
guidsInStore = guidIndex + 1;
// The list begins at the end of the storage and goes backwards
// The list begins at the end of the store and goes backwards
const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
name = guidToQString(*guidPtr);
hasGuidIndex = true;
@ -3129,11 +3129,11 @@ parsing_done:
if (isInvalid) {
if (variableHeader->Next != lastVariableFlag) {
name = QObject::tr("Invalid link");
subtype = Subtypes::InvalidLinkNvar;
subtype = Subtypes::InvalidLinkNvarVariable;
}
else {
name = QObject::tr("Invalid");
subtype = Subtypes::InvalidNvar;
subtype = Subtypes::InvalidNvarVariable;
}
}
else // Add GUID info for valid variables
@ -3192,19 +3192,19 @@ parsing_done:
// Show messages
if (msgUnknownExtDataFormat)
msg(QObject::tr("parseNvarStorage: unknown extended data format"), varIndex);
msg(QObject::tr("parseNvarStore: unknown extended data format"), varIndex);
if (msgExtHeaderTooLong)
msg(QObject::tr("parseNvarStorage: extended header size (%1h) is greater than body size (%2h)")
msg(QObject::tr("parseNvarStore: extended header size (%1h) is greater than body size (%2h)")
.hexarg(extendedHeaderSize).hexarg(body.size()), varIndex);
if (msgExtDataTooShort)
msg(QObject::tr("parseNvarStorage: extended data size (%1h) is smaller than required for timestamp and hash (0x28)")
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 = parseNvarStorage(body, varIndex);
STATUS result = parseNvarStore(body, varIndex);
if (result)
msg(QObject::tr("parseNvarStorage: parsing of nested NVAR storage failed with error \"%1\"").arg(errorCodeToQString(result)), varIndex);
msg(QObject::tr("parseNvarStore: parsing of nested NVAR store failed with error \"%1\"").arg(errorCodeToQString(result)), varIndex);
}
// Move to next variable
@ -3214,7 +3214,7 @@ parsing_done:
return ERR_SUCCESS;
}
STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex & index)
STATUS FfsParser::parseStoreArea(const QByteArray & data, const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
@ -3226,18 +3226,18 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
// Search for first volume
STATUS result;
UINT32 prevStorageOffset;
UINT32 prevStoreOffset;
result = findNextStorage(index, data, parentOffset, 0, prevStorageOffset);
result = findNextStore(index, data, parentOffset, 0, prevStoreOffset);
if (result)
return result;
// First storage is not at the beginning of volume body
// First store is not at the beginning of volume body
QString name;
QString info;
if (prevStorageOffset > 0) {
if (prevStoreOffset > 0) {
// Get info
QByteArray padding = data.left(prevStorageOffset);
QByteArray padding = data.left(prevStoreOffset);
name = QObject::tr("Padding");
info = QObject::tr("Full size: %1h (%2)")
.hexarg(padding.size()).arg(padding.size());
@ -3249,16 +3249,16 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
}
// Search for and parse all storages
UINT32 storageOffset = prevStorageOffset;
UINT32 prevStorageSize = 0;
// Search for and parse all stores
UINT32 storeOffset = prevStoreOffset;
UINT32 prevStoreSize = 0;
while (!result)
{
// Padding between storages
if (storageOffset > prevStorageOffset + prevStorageSize) {
UINT32 paddingOffset = prevStorageOffset + prevStorageSize;
UINT32 paddingSize = storageOffset - paddingOffset;
// Padding between stores
if (storeOffset > prevStoreOffset + prevStoreSize) {
UINT32 paddingOffset = prevStoreOffset + prevStoreSize;
UINT32 paddingSize = storeOffset - paddingOffset;
QByteArray padding = data.mid(paddingOffset, paddingSize);
// Get info
@ -3273,24 +3273,24 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
}
// Get storage size
UINT32 storageSize = 0;
result = getStorageSize(data, storageOffset, storageSize);
// Get store size
UINT32 storeSize = 0;
result = getStoreSize(data, storeOffset, storeSize);
if (result) {
msg(QObject::tr("parseStorageArea: getVssStorageSize failed with error \"%1\"").arg(errorCodeToQString(result)), index);
msg(QObject::tr("parseStoreArea: getVssStoreSize failed with error \"%1\"").arg(errorCodeToQString(result)), index);
return result;
}
// Check that storage is fully present in input
if (storageSize > (UINT32)data.size() || storageOffset + storageSize > (UINT32)data.size()) {
msg(QObject::tr("parseVssStorageArea: one of storages inside overlaps the end of data"), index);
// Check that 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 storage = data.mid(storageOffset, storageSize);
if (storageSize > (UINT32)storage.size()) {
QByteArray store = data.mid(storeOffset, storeSize);
if (storeSize > (UINT32)store.size()) {
// Mark the rest as padding and finish the parsing
QByteArray padding = data.right(storage.size());
QByteArray padding = data.right(store.size());
// Get info
name = QObject::tr("Padding");
@ -3298,35 +3298,35 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
.hexarg(padding.size()).arg(padding.size());
// Construct parsing data
pdata.offset = parentOffset + storageOffset;
pdata.offset = parentOffset + storeOffset;
// Add tree item
QModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
msg(QObject::tr("parseStorageArea: one of storages inside overlaps the end of data"), paddingIndex);
msg(QObject::tr("parseStoreArea: one of stores inside overlaps the end of data"), paddingIndex);
// Update variables
prevStorageOffset = storageOffset;
prevStorageSize = padding.size();
prevStoreOffset = storeOffset;
prevStoreSize = padding.size();
break;
}
// Parse current volume's header
QModelIndex storageIndex;
result = parseStorageHeader(storage, parentOffset + storageOffset, index, storageIndex);
QModelIndex storeIndex;
result = parseStoreHeader(store, parentOffset + storeOffset, index, storeIndex);
if (result) {
msg(QObject::tr("parseStorageArea: storage header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
msg(QObject::tr("parseStoreArea: store header parsing failed with error \"%1\"").arg(errorCodeToQString(result)), index);
}
// Go to next volume
prevStorageOffset = storageOffset;
prevStorageSize = storageSize;
result = findNextStorage(index, data, parentOffset, storageOffset + prevStorageSize, storageOffset);
prevStoreOffset = storeOffset;
prevStoreSize = storeSize;
result = findNextStore(index, data, parentOffset, storeOffset + prevStoreSize, storeOffset);
}
// Padding/free space at the end of volume
storageOffset = prevStorageOffset + prevStorageSize;
if ((UINT32)data.size() > storageOffset) {
QByteArray padding = data.mid(storageOffset);
storeOffset = prevStoreOffset + prevStoreSize;
if ((UINT32)data.size() > storeOffset) {
QByteArray padding = data.mid(storeOffset);
UINT8 type;
UINT8 subtype;
if (padding.count(pdata.emptyByte) == padding.size()) {
@ -3337,8 +3337,8 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
}
else {
// Nothing is parsed yet, but the file is not empty
if (!storageOffset) {
msg(QObject::tr("parseStorageArea: area can't be parsed as storage"), index);
if (!storeOffset) {
msg(QObject::tr("parseStoreArea: area can't be parsed as variable store"), index);
return ERR_SUCCESS;
}
@ -3353,7 +3353,7 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
.hexarg(padding.size()).arg(padding.size());
// Construct parsing data
pdata.offset = parentOffset + storageOffset;
pdata.offset = parentOffset + storeOffset;
// Add tree item
model->addItem(Types::Padding, getPaddingType(padding), name, QString(), info, QByteArray(), padding, TRUE, parsingDataToQByteArray(pdata), index);
@ -3363,12 +3363,15 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
for (int i = 0; i < model->rowCount(index); i++) {
QModelIndex current = index.child(i, 0);
switch (model->type(current)) {
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
parseVssStorageBody(current);
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
parseVssStoreBody(current);
break;
case Types::NvramStorageFsys:
parseFsysStorageBody(current);
case Types::NvramStoreFsys:
parseFsysStoreBody(current);
break;
case Types::NvramStoreEvsa:
parseEvsaStoreBody(current);
break;
case Types::Padding:
// No parsing required
@ -3381,99 +3384,112 @@ STATUS FfsParser::parseStorageArea(const QByteArray & data, const QModelIndex &
return ERR_SUCCESS;
}
STATUS FfsParser::findNextStorage(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storageOffset, UINT32 & nextStorageOffset)
STATUS FfsParser::findNextStore(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset)
{
UINT32 dataSize = data.size();
if (dataSize < sizeof(UINT32))
return ERR_STORAGES_NOT_FOUND;
return ERR_STORES_NOT_FOUND;
UINT32 offset = storageOffset;
UINT32 offset = storeOffset;
for (; offset < dataSize - sizeof(UINT32); offset++) {
const UINT32* currentPos = (const UINT32*)(data.constData() + offset);
if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE) { //$VSS or $SVS signatures found, perform checks
const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos;
if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) {
msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid format %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Format, 2), index);
msg(QObject::tr("findNextStore: VSS store candidate at offset %1h skipped, has invalid format %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Format, 2), index);
continue;
}
if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Size, 8), index);
msg(QObject::tr("findNextStore: VSS store candidate at offset %1h skipped, has invalid size %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->Size, 8), index);
continue;
}
//if (vssHeader->State != NVRAM_VSS_VARIABLE_STORE_HEALTHY) {
// msg(QObject::tr("findNextStorage: VSS storage candidate at offset %1h skipped, has invalid state %2h").hexarg(parentOffset + offset).hexarg2(vssHeader->State, 2), index);
// continue;
//}
// All checks passed, storage found
// All checks passed, store found
break;
}
else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found
// No checks needed
break;
// No checks needed
break;
}
else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE) { //Fsys signature found
// No checks needed
break;
// No checks needed
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;
}
// All checks passed, store found
offset -= 4;
break;
}
}
// No more storages found
// No more storas found
if (offset >= dataSize - sizeof(UINT32))
return ERR_STORAGES_NOT_FOUND;
return ERR_STORES_NOT_FOUND;
nextStorageOffset = offset;
nextStoreOffset = offset;
return ERR_SUCCESS;
}
STATUS FfsParser::getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize)
STATUS FfsParser::getStoreSize(const QByteArray & data, const UINT32 storeOffset, UINT32 & storeSize)
{
//TODO: add GUID support
const UINT32* signature = (const UINT32*)(data.constData() + storageOffset);
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;
storageSize = vssHeader->Size;
storeSize = vssHeader->Size;
}
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature;
storageSize = fdcHeader->Size;
storeSize = fdcHeader->Size;
}
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) {
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
storageSize = fsysHeader->Size;
storeSize = fsysHeader->Size;
}
else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) {
const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature;
storeSize = evsaHeader->StoreSize;
}
return ERR_SUCCESS;
}
STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index)
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 and paddings
//TODO: seen for now - $VSS, $SVS, Fsys, full volume GUID, _FDC, EVSA and paddings
const UINT32 dataSize = (UINT32)storage.size();
const UINT32* signature = (const UINT32*)storage.constData();
const UINT32 dataSize = (UINT32)store.size();
const UINT32* signature = (const UINT32*)store.constData();
if (dataSize < sizeof(UINT32)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for storage signature"), parent);
msg(QObject::tr("parseStoreHeader: volume body is too small even for store signature"), parent);
return ERR_SUCCESS;
}
// VSS variable storages
// VSS variable stores
if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
// The volume must begin with a store to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for VSS storage header"), parent);
msg(QObject::tr("parseStoreHeader: volume body is too small even for VSS store header"), parent);
return ERR_SUCCESS;
}
// Get VSS storage header
const VSS_VARIABLE_STORE_HEADER* vssStorageHeader = (const VSS_VARIABLE_STORE_HEADER*)signature;
// Get VSS store header
const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)signature;
// Check storage size
if (dataSize < vssStorageHeader->Size) {
msg(QObject::tr("parseStorageHeader: VSS storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(vssStorageHeader->Size, 8).arg(vssStorageHeader->Size)
// Check store size
if (dataSize < vssStoreHeader->Size) {
msg(QObject::tr("parseStoreHeader: VSS store size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(vssStoreHeader->Size, 8).arg(vssStoreHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
@ -3482,49 +3498,49 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = storage.left(sizeof(VSS_VARIABLE_STORE_HEADER));
QByteArray body = storage.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStorageHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER));
QByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER));
QByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStoreHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER));
// Add info
QString name = QObject::tr("VSS storage");
QString name = QObject::tr("VSS store");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nFormat: %8h\nState: %9h")
.hexarg2(vssStorageHeader->Signature, 8)
.hexarg(vssStorageHeader->Size).arg(vssStorageHeader->Size)
.hexarg2(vssStoreHeader->Signature, 8)
.hexarg(vssStoreHeader->Size).arg(vssStoreHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(vssStorageHeader->Format, 2)
.hexarg2(vssStorageHeader->State, 2);
.hexarg2(vssStoreHeader->Format, 2)
.hexarg2(vssStoreHeader->State, 2);
// Add unknown field for $SVS storages
// Add unknown field for $SVS stores
if (*signature == NVRAM_APPLE_SVS_STORE_SIGNATURE)
info += QObject::tr("\nUnknown: %1h").hexarg2(vssStorageHeader->Unknown, 4);
info += QObject::tr("\nUnknown: %1h").hexarg2(vssStoreHeader->Unknown, 4);
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
index = model->addItem(Types::NvramStoreVss, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
}
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
// The volume must begin with a store to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(FDC_VOLUME_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for FDC storage header"), parent);
msg(QObject::tr("parseStoreHeader: volume body is too small even for FDC store header"), parent);
return ERR_SUCCESS;
}
// Get VSS storage header
const FDC_VOLUME_HEADER* fdcStorageHeader = (const FDC_VOLUME_HEADER*)signature;
// Get VSS store header
const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)signature;
// Check storage size
if (dataSize < fdcStorageHeader->Size) {
msg(QObject::tr("parseStorageHeader: FDC storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size)
// Check store size
if (dataSize < fdcStoreHeader->Size) {
msg(QObject::tr("parseStoreHeader: FDC store size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStoreHeader->Size, 8).arg(fdcStoreHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Determine internal volume header size
const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStorageHeader + 1);
const EFI_FIRMWARE_VOLUME_HEADER* volumeHeader = (const EFI_FIRMWARE_VOLUME_HEADER*)(fdcStoreHeader + 1);
UINT32 headerSize;
if (volumeHeader->Revision > 1 && volumeHeader->ExtHeaderOffset) {
const EFI_FIRMWARE_VOLUME_EXT_HEADER* extendedHeader = (const EFI_FIRMWARE_VOLUME_EXT_HEADER*)((const UINT8*)volumeHeader + volumeHeader->ExtHeaderOffset);
@ -3536,7 +3552,7 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
// Extended header end can be unaligned
headerSize = ALIGN8(headerSize);
// Add VSS storage header
// Add VSS store header
headerSize += sizeof(VSS_VARIABLE_STORE_HEADER);
// Add FDC header
@ -3544,8 +3560,8 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
// Check sanity of combined header size
if (dataSize < headerSize) {
msg(QObject::tr("parseStorageHeader: FDC storage header size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStorageHeader->Size, 8).arg(fdcStorageHeader->Size)
msg(QObject::tr("parseStoreHeader: FDC store header size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fdcStoreHeader->Size, 8).arg(fdcStoreHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
@ -3554,14 +3570,14 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = storage.left(headerSize);
QByteArray body = storage.mid(headerSize, fdcStorageHeader->Size - headerSize);
QByteArray header = store.left(headerSize);
QByteArray body = store.mid(headerSize, fdcStoreHeader->Size - headerSize);
// Add info
QString name = QObject::tr("FDC storage");
QString name = QObject::tr("FDC store");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)")
.hexarg2(fdcStorageHeader->Signature, 8)
.hexarg(fdcStorageHeader->Size).arg(fdcStorageHeader->Size)
.hexarg2(fdcStoreHeader->Signature, 8)
.hexarg(fdcStoreHeader->Size).arg(fdcStoreHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size());
@ -3571,22 +3587,22 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageFdc, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
index = model->addItem(Types::NvramStoreFdc, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
}
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE) {
// The volume must begin with a storage to be valid, but after the first one, there can be many variants
// The volume must begin with a store to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) {
msg(QObject::tr("parseStorageHeader: volume body is too small even for Fsys storage header"), parent);
msg(QObject::tr("parseStoreHeader: volume body is too small even for Fsys store header"), parent);
return ERR_SUCCESS;
}
// Get Fsys storage header
const APPLE_FSYS_STORE_HEADER* fsysStorageHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
// Get Fsys store header
const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
// Check storage size
if (dataSize < fsysStorageHeader->Size) {
msg(QObject::tr("parseStorageHeader: Fsys storage size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fsysStorageHeader->Size, 4).arg(fsysStorageHeader->Size)
// Check store size
if (dataSize < fsysStoreHeader->Size) {
msg(QObject::tr("parseStoreHeader: Fsys store size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(fsysStoreHeader->Size, 4).arg(fsysStoreHeader->Size)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
@ -3595,38 +3611,85 @@ STATUS FfsParser::parseStorageHeader(const QByteArray & storage, const UINT32 pa
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = storage.left(sizeof(APPLE_FSYS_STORE_HEADER));
QByteArray body = storage.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStorageHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
QByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER));
QByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
// Check storage checksum
UINT32 storedCrc = *(UINT32*)storage.right(sizeof(UINT32)).constBegin();
UINT32 calculatedCrc = calculatedCrc = crc32(0, (const UINT8*)storage.constData(), (const UINT32)storage.size() - sizeof(UINT32));
// Check store checksum
UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constBegin();
UINT32 calculatedCrc = calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32));
// Add info
QString name = QObject::tr("Fsys storage");
QString name = QObject::tr("Fsys store");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nUnknown: %9 %10 %11 %12 %13\nCRC32: %14")
.hexarg2(fsysStorageHeader->Signature, 8)
.hexarg(fsysStorageHeader->Size).arg(fsysStorageHeader->Size)
.hexarg2(fsysStoreHeader->Signature, 8)
.hexarg(fsysStoreHeader->Size).arg(fsysStoreHeader->Size)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(fsysStorageHeader->Unknown[0], 2)
.hexarg2(fsysStorageHeader->Unknown[1], 2)
.hexarg2(fsysStorageHeader->Unknown[2], 2)
.hexarg2(fsysStorageHeader->Unknown[3], 2)
.hexarg2(fsysStorageHeader->Unknown[4], 2)
.hexarg2(fsysStoreHeader->Unknown[0], 2)
.hexarg2(fsysStoreHeader->Unknown[1], 2)
.hexarg2(fsysStoreHeader->Unknown[2], 2)
.hexarg2(fsysStoreHeader->Unknown[3], 2)
.hexarg2(fsysStoreHeader->Unknown[4], 2)
.arg(storedCrc == calculatedCrc ? QObject::tr("%1h, valid").hexarg2(storedCrc, 8) : QObject::tr("%1h, invalid, should be %2h").hexarg2(storedCrc, 8).hexarg2(calculatedCrc, 8));
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStorageFsys, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
index = model->addItem(Types::NvramStoreFsys, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
}
else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) {
// The volume must begin with a store to be valid, but after the first one, there can be many variants
if (dataSize < sizeof(EVSA_STORE_ENTRY)) {
msg(QObject::tr("parseStoreHeader: volume body is too small even for EVSA store header"), parent);
return ERR_SUCCESS;
}
// Get EVSA store header
const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)signature;
// Check store size
if (dataSize < evsaStoreHeader->StoreSize) {
msg(QObject::tr("parseStoreHeader: EVSA store size %1h (%2) is greater than volume body size %3h (%4)")
.hexarg2(evsaStoreHeader->StoreSize, 4).arg(evsaStoreHeader->StoreSize)
.hexarg2(dataSize, 8).arg(dataSize), parent);
return ERR_SUCCESS;
}
// Get parsing data
PARSING_DATA pdata = parsingDataFromQModelIndex(parent);
// Construct header and body
QByteArray header = store.left(evsaStoreHeader->Header.Size);
QByteArray body = store.mid(evsaStoreHeader->Header.Size, evsaStoreHeader->StoreSize - evsaStoreHeader->Header.Size);
// Recalculate checksum
UINT8 calculated = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2);
// Add info
QString name = QObject::tr("EVSA store");
QString info = QObject::tr("Signature: %1h\nFull size: %2h (%3)\nHeader size: %4h (%5)\nBody size: %6h (%7)\nType: %9h\nChecksum: %10\nAttributes: %11h")
.hexarg2(evsaStoreHeader->Signature, 8)
.hexarg(evsaStoreHeader->StoreSize).arg(evsaStoreHeader->StoreSize)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(evsaStoreHeader->Header.Type, 2)
.arg(evsaStoreHeader->Header.Checksum == calculated ?
QObject::tr("%1h, valid").hexarg2(calculated,2) :
QObject::tr("%1h, invalid, should be %2h").hexarg2(evsaStoreHeader->Header.Checksum, 2).hexarg2(calculated,2))
.hexarg2(evsaStoreHeader->Attributes, 8);
// Add correct offset
pdata.offset = parentOffset;
// Add tree item
index = model->addItem(Types::NvramStoreEvsa, 0, name, QString(), info, header, body, TRUE, parsingDataToQByteArray(pdata), parent);
}
return ERR_SUCCESS;
}
STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
STATUS FfsParser::parseVssStoreBody(const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
@ -3640,7 +3703,7 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
// Check that the is enough space for variable header
const UINT32 dataSize = (UINT32)data.size();
if (dataSize < sizeof(VSS_VARIABLE_HEADER)) {
msg(QObject::tr("parseVssStorageBody: storage body is too small even for VSS variable header"), index);
msg(QObject::tr("parseVssStoreBody: store body is too small even for VSS variable header"), index);
return ERR_SUCCESS;
}
@ -3752,9 +3815,9 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
subtype = 0;
}
else {
// Nothing is parsed yet, but the storage is not empty
// Nothing is parsed yet, but the store is not empty
if (!offset) {
msg(QObject::tr("parseVssStorageBody: storage can't be parsed as VSS storage"), index);
msg(QObject::tr("parseVssStoreBody: store can't be parsed as VSS store"), index);
return ERR_SUCCESS;
}
@ -3803,20 +3866,20 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
// Set subtype and add related info
if (isInvalid)
subtype = Subtypes::InvalidVss;
subtype = Subtypes::InvalidVssVariable;
else if (isAuthenticated) {
subtype = Subtypes::AuthVss;
subtype = Subtypes::AuthVssVariable;
info += QObject::tr("\nMonotonic counter: %1h\nTimestamp: %2\nPubKey index: %3")
.hexarg(monotonicCounter).arg(efiTimeToQString(timestamp)).arg(pubKeyIndex);
}
else if (isAppleCrc32) {
subtype = Subtypes::AppleCrc32Vss;
subtype = Subtypes::Crc32VssVariable;
info += QObject::tr("\nCRC32: %1h%2").hexarg2(storedCrc32, 8)
.arg(storedCrc32 == calculatedCrc32 ? QObject::tr(", valid") : QObject::tr(", invalid, should be %1h").hexarg2(calculatedCrc32,8));
}
else
subtype = Subtypes::StandardVss;
subtype = Subtypes::StandardVssVariable;
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
@ -3831,7 +3894,7 @@ STATUS FfsParser::parseVssStorageBody(const QModelIndex & index)
return ERR_SUCCESS;
}
STATUS FfsParser::parseFsysStorageBody(const QModelIndex & index)
STATUS FfsParser::parseFsysStoreBody(const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
@ -3909,7 +3972,7 @@ STATUS FfsParser::parseFsysStorageBody(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("parseFsysStorageBody: variable appears too big, added as padding"), index);
msg(QObject::tr("parseFsysStoreBody: variable appears too big, added as padding"), index);
return ERR_SUCCESS;
}
@ -3937,4 +4000,120 @@ STATUS FfsParser::parseFsysStorageBody(const QModelIndex & index)
return ERR_SUCCESS;
}
STATUS FfsParser::parseEvsaStoreBody(const QModelIndex & index)
{
// Sanity check
if (!index.isValid())
return ERR_INVALID_PARAMETER;
// Get parsing data for the current item
PARSING_DATA pdata = parsingDataFromQModelIndex(index);
UINT32 parentOffset = pdata.offset + model->header(index).size();
const QByteArray data = model->body(index);
// Check that the is enough space for variable header
const UINT32 dataSize = (UINT32)data.size();
UINT32 offset = 0;
// Parse all variables
while (1) {
UINT32 unparsedSize = dataSize - offset;
UINT32 variableSize = 0;
QString name;
QString info;
QByteArray header;
QByteArray body;
UINT8 subtype;
UINT8 calculated;
const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset);
// 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;
}
variableSize = entryHeader->Size;
// Recalculate entry checksum
calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2);
// GUID entry
if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID1 ||
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID2) {
const EVSA_GUID_ENTRY* guidHeader = (const EVSA_GUID_ENTRY*)entryHeader;
header = data.mid(offset, sizeof(EVSA_GUID_ENTRY));
name = guidToQString(guidHeader->Guid);
info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)\nType: %7h\nChecksum: %8\nGuidId: %9h")
.hexarg(variableSize).arg(variableSize)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(guidHeader->Header.Type, 2)
.arg(guidHeader->Header.Checksum == calculated ?
QObject::tr("%1h, valid").hexarg2(calculated, 2) :
QObject::tr("%1h, invalid, should be %2h").hexarg2(guidHeader->Header.Checksum, 2).hexarg2(calculated, 2))
.hexarg2(guidHeader->GuidId, 4);
subtype = Subtypes::GuidEvsaEntry;
}
// Name entry
else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 ||
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME2) {
const EVSA_NAME_ENTRY* nameHeader = (const EVSA_NAME_ENTRY*)entryHeader;
header = data.mid(offset, sizeof(EVSA_NAME_ENTRY));
body = data.mid(offset + sizeof(EVSA_NAME_ENTRY), nameHeader->Header.Size - sizeof(EVSA_NAME_ENTRY));
name = QString::fromUtf16((const CHAR16*)body.constData());
info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)\nType: %7h\nChecksum: %8\nVarId: %9h")
.hexarg(variableSize).arg(variableSize)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(nameHeader->Header.Type, 2)
.arg(nameHeader->Header.Checksum == calculated ?
QObject::tr("%1h, valid").hexarg2(calculated, 2) :
QObject::tr("%1h, invalid, should be %2h").hexarg2(nameHeader->Header.Checksum, 2).hexarg2(calculated, 2))
.hexarg2(nameHeader->VarId, 4);
subtype = Subtypes::NameEvsaEntry;
}
// Data entry
else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 ||
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 ||
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA3) {
const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader;
header = data.mid(offset, sizeof(EVSA_DATA_ENTRY));
body = data.mid(offset + sizeof(EVSA_DATA_ENTRY), dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY));
name = QObject::tr("Data");
info = QObject::tr("Full size: %1h (%2)\nHeader size %3h (%4)\nBody size: %5h (%6)\nType: %7h\nChecksum: %8\nVarId: %9h\nGuidId: %10h\nAttributes: %11h")
.hexarg(variableSize).arg(variableSize)
.hexarg(header.size()).arg(header.size())
.hexarg(body.size()).arg(body.size())
.hexarg2(dataHeader->Header.Type, 2)
.arg(dataHeader->Header.Checksum == calculated ?
QObject::tr("%1h, valid").hexarg2(calculated, 2) :
QObject::tr("%1h, invalid, should be %2h").hexarg2(dataHeader->Header.Checksum, 2).hexarg2(calculated, 2))
.hexarg2(dataHeader->VarId, 4)
.hexarg2(dataHeader->GuidId, 4)
.hexarg2(dataHeader->Attributes, 8);
subtype = Subtypes::DataEvsaEntry;
}
// Unknown entry
else {
;
}
// Add correct offset to parsing data
pdata.offset = parentOffset + offset;
// Add tree item
model->addItem(Types::NvramEntryEvsa, subtype, name, QString(), info, header, body, FALSE, parsingDataToQByteArray(pdata), index);
// Move to next variable
offset += variableSize;
}
return ERR_SUCCESS;
}

View File

@ -107,14 +107,15 @@ private:
STATUS addMemoryAddressesRecursive(const QModelIndex & index, const UINT32 diff);
// NVRAM parsing
STATUS parseNvarStorage(const QByteArray & data, const QModelIndex & index);
STATUS parseNvarStore(const QByteArray & data, const QModelIndex & index);
STATUS parseStorageArea(const QByteArray & data, const QModelIndex & index);
STATUS findNextStorage(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storageOffset, UINT32 & nextStorageOffset);
STATUS getStorageSize(const QByteArray & data, const UINT32 storageOffset, UINT32 & storageSize);
STATUS parseStorageHeader(const QByteArray & storage, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseVssStorageBody(const QModelIndex & index);
STATUS parseFsysStorageBody(const QModelIndex & index);
STATUS parseStoreArea(const QByteArray & data, const QModelIndex & index);
STATUS findNextStore(const QModelIndex & index, const QByteArray & data, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset);
STATUS getStoreSize(const QByteArray & data, const UINT32 storeOffset, UINT32 & storeSize);
STATUS parseStoreHeader(const QByteArray & store, const UINT32 parentOffset, const QModelIndex & parent, QModelIndex & index);
STATUS parseVssStoreBody(const QModelIndex & index);
STATUS parseFsysStoreBody(const QModelIndex & index);
STATUS parseEvsaStoreBody(const QModelIndex & index);
// Message helper
void msg(const QString & message, const QModelIndex &index = QModelIndex());

View File

@ -153,13 +153,13 @@ STATUS FitParser::findFitRecursive(const QModelIndex & index, QModelIndex & foun
{
// Sanity check
if (!index.isValid())
return EFI_SUCCESS;
return ERR_SUCCESS;
// Process child items
for (int i = 0; i < model->rowCount(index); i++) {
findFitRecursive(index.child(i, 0), found, fitOffset);
if (found.isValid())
return EFI_SUCCESS;
return ERR_SUCCESS;
}
// Get parsing data for the current item

View File

@ -20,11 +20,11 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include "basetypes.h"
//
// Let's start with NVAR storage, as the most difficult one
// NVAR store
//
// CEF5B9A3-476D-497F-9FDC-E98143E0422C
const QByteArray NVRAM_NVAR_STORAGE_FILE_GUID
const QByteArray NVRAM_NVAR_STORE_FILE_GUID
("\xA3\xB9\xF5\xCE\x6D\x47\x7F\x49\x9F\xDC\xE9\x81\x43\xE0\x42\x2C", 16);
// 9221315B-30BB-46B5-813E-1B1BF4712BD3
@ -65,13 +65,17 @@ typedef struct _NVAR_VARIABLE_HEADER {
#define NVRAM_NVAR_VARIABLE_EXT_ATTRIB_TIME_BASED 0x20
//
// Next format is TianoCore VSS and it's variations
// TianoCore VSS and it's variations
//
// FFF12B8D-7696-4C8B-A985-2747075B4F50
const QByteArray NVRAM_VSS_STORAGE_VOLUME_GUID
const QByteArray NVRAM_VSS_STORE_VOLUME_GUID
("\x8D\x2B\xF1\xFF\x96\x76\x8B\x4C\xA9\x85\x27\x47\x07\x5B\x4F\x50", 16);
// 00504624-8A59-4EEB-BD0F-6B36E96128E0
const QByteArray NVRAM_ADDITIONAL_STORE_VOLUME_GUID
("\x24\x46\x50\x00\x59\x8A\xEB\x4E\xBD\x0F\x6B\x36\xE9\x61\x28\xE0", 16);
#define NVRAM_VSS_STORE_SIGNATURE 0x53535624 // $VSS
#define NVRAM_APPLE_SVS_STORE_SIGNATURE 0x53565324 // $SVS
#define NVRAM_APPLE_FSYS_STORE_SIGNATURE 0x73797346 // Fsys
@ -90,29 +94,13 @@ const QByteArray NVRAM_VSS_STORAGE_VOLUME_GUID
// Variable store header
typedef struct _VSS_VARIABLE_STORE_HEADER {
UINT32 Signature; // $VSS signature
UINT32 Size; // Size of variable storage, including storage header
UINT8 Format; // Storage format state
UINT8 State; // Storage health state
UINT32 Size; // Size of variable store, including store header
UINT8 Format; // Store format state
UINT8 State; // Store health state
UINT16 Unknown; // Used in Apple $SVS varstores
UINT32 : 32;
} VSS_VARIABLE_STORE_HEADER;
// Apple Fsys store header
typedef struct _APPLE_FSYS_STORE_HEADER {
UINT32 Signature; // Fsys signature
UINT8 Unknown[5]; // Still unknown
UINT16 Size; // Size of variable storage
} APPLE_FSYS_STORE_HEADER;
// Apple Fsys variable format
// UINT8 NameLength;
// CHAR8 Name[];
// UINT16 DataLength;
// UINT8 Data[]
// Storage ends with a chunk named "EOF" without data
// All free bytes in storage are zeroed
// Has CRC32 of the whole store without checksum field at the end
// Normal variable header
typedef struct _VSS_VARIABLE_HEADER {
UINT16 StartId; // Variable start marker AA55
@ -170,17 +158,84 @@ typedef struct _VSS_AUTH_VARIABLE_HEADER {
// FDC region can be found in some VSS volumes
// It has another VSS volume inside
// _FDC header structure
#define NVRAM_FDC_VOLUME_SIGNATURE 0x4344465F
typedef struct _FDC_VOLUME_HEADER {
UINT32 Signature;
UINT32 Signature; //_FDC
UINT32 Size;
//EFI_FIRMWARE_VOLUME_HEADER VolumeHeader;
//EFI_FV_BLOCK_MAP_ENTRY FvBlockMap[2];
//VSS_VARIABLE_STORE_HEADER VssHeader;
} FDC_VOLUME_HEADER;
#define NVRAM_FDC_VOLUME_SIGNATURE 0x4344465F
//
// Apple Fsys
//
typedef struct _APPLE_FSYS_STORE_HEADER {
UINT32 Signature; // Fsys signature
UINT8 Unknown[5]; // Still unknown
UINT16 Size; // Size of variable store
} APPLE_FSYS_STORE_HEADER;
// Apple Fsys variable format
// UINT8 NameLength;
// CHAR8 Name[];
// UINT16 DataLength;
// UINT8 Data[]
// Store ends with a chunk named "EOF" without data
// All free bytes in store are zeroed
// Has CRC32 of the whole store without checksum field at the end
//
// EVSA
//
#define NVRAM_EVSA_STORE_SIGNATURE 0x41535645
#define NVRAM_EVSA_ENTRY_TYPE_STORE 0xEC
#define NVRAM_EVSA_ENTRY_TYPE_GUID1 0xED
#define NVRAM_EVSA_ENTRY_TYPE_GUID2 0xE1
#define NVRAM_EVSA_ENTRY_TYPE_NAME1 0xEE
#define NVRAM_EVSA_ENTRY_TYPE_NAME2 0xE2
#define NVRAM_EVSA_ENTRY_TYPE_DATA1 0xEF
#define NVRAM_EVSA_ENTRY_TYPE_DATA2 0xE3
#define NVRAM_EVSA_ENTRY_TYPE_DATA3 0x83
typedef struct _EVSA_ENTRY_HEADER {
UINT8 Type;
UINT8 Checksum;
UINT16 Size;
} EVSA_ENTRY_HEADER;
typedef struct _EVSA_STORE_ENTRY {
EVSA_ENTRY_HEADER Header;
UINT32 Signature; // EVSA
UINT32 Attributes;
UINT32 StoreSize;
UINT32 : 32;
} EVSA_STORE_ENTRY;
typedef struct _EVSA_GUID_ENTRY {
EVSA_ENTRY_HEADER Header;
UINT16 GuidId;
EFI_GUID Guid;
} EVSA_GUID_ENTRY;
typedef struct _EVSA_NAME_ENTRY {
EVSA_ENTRY_HEADER Header;
UINT16 VarId;
//CHAR16 Name[];
} EVSA_NAME_ENTRY;
typedef struct _EVSA_DATA_ENTRY {
EVSA_ENTRY_HEADER Header;
UINT16 GuidId;
UINT16 VarId;
UINT32 Attributes;
//UINT8 Data[];
} EVSA_DATA_ENTRY;
// Restore previous packing rules
#pragma pack(pop)
#endif

View File

@ -51,7 +51,7 @@ typedef struct _FILE_PARSING_DATA {
} FILE_PARSING_DATA;
#define RAW_FILE_FORMAT_UNKNOWN 0
#define RAW_FILE_FORMAT_NVAR_STORAGE 1
#define RAW_FILE_FORMAT_NVAR_STORE 1
typedef struct _COMPRESSED_SECTION_PARSING_DATA {
UINT32 uncompressedSize;

View File

@ -65,18 +65,22 @@ QString itemTypeToQString(const UINT8 type)
return QObject::tr("Section");
case Types::FreeSpace:
return QObject::tr("Free space");
case Types::NvramStoreVss:
return QObject::tr("VSS store");
case Types::NvramStoreFdc:
return QObject::tr("FDC store");
case Types::NvramStoreFsys:
return QObject::tr("Fsys store");
case Types::NvramStoreEvsa:
return QObject::tr("EVSA store");
case Types::NvramVariableNvar:
return QObject::tr("NVAR variable");
case Types::NvramStorageVss:
return QObject::tr("VSS storage");
case Types::NvramStorageFdc:
return QObject::tr("FDC storage");
case Types::NvramStorageFsys:
return QObject::tr("Fsys storage");
case Types::NvramVariableVss:
return QObject::tr("VSS variable");
case Types::NvramVariableFsys:
return QObject::tr("Fsys variable");
case Types::NvramEntryEvsa:
return QObject::tr("EVSA entry");
default:
return QObject::tr("Unknown");
}
@ -109,8 +113,8 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
return QObject::tr("FFSv2");
else if (subtype == Subtypes::Ffs3Volume)
return QObject::tr("FFSv3");
else if (subtype == Subtypes::VssNvramVolume)
return QObject::tr("VSS NVRAM");
else if (subtype == Subtypes::NvramVolume)
return QObject::tr("NVRAM");
else
return QObject::tr("Unknown subtype");
case Types::Capsule:
@ -132,35 +136,49 @@ QString itemSubtypeToQString(const UINT8 type, const UINT8 subtype)
return sectionTypeToQString(subtype);
case Types::FreeSpace:
return QString();
case Types::NvramVariableNvar:
if (subtype == Subtypes::InvalidNvar)
return QObject::tr("Invalid");
if (subtype == Subtypes::InvalidLinkNvar)
return QObject::tr("Invalid link");
if (subtype == Subtypes::LinkNvar)
return QObject::tr("Link");
if (subtype == Subtypes::DataNvar)
return QObject::tr("Data");
if (subtype == Subtypes::FullNvar)
return QObject::tr("Full");
else
return QObject::tr("Unknown subtype");
case Types::NvramStorageVss:
case Types::NvramStorageFdc:
case Types::NvramStorageFsys:
case Types::NvramStoreVss:
case Types::NvramStoreFdc:
case Types::NvramStoreFsys:
case Types::NvramStoreEvsa:
case Types::NvramVariableFsys:
return QString();
case Types::NvramVariableVss:
if (subtype == Subtypes::InvalidVss)
case Types::NvramVariableNvar:
if (subtype == Subtypes::InvalidNvarVariable)
return QObject::tr("Invalid");
if (subtype == Subtypes::StandardVss)
if (subtype == Subtypes::InvalidLinkNvarVariable)
return QObject::tr("Invalid link");
if (subtype == Subtypes::LinkNvarVariable)
return QObject::tr("Link");
if (subtype == Subtypes::DataNvarVariable)
return QObject::tr("Data");
if (subtype == Subtypes::FullNvarVariable)
return QObject::tr("Full");
else
return QObject::tr("Unknown subtype");
case Types::NvramVariableVss:
if (subtype == Subtypes::InvalidVssVariable)
return QObject::tr("Invalid");
if (subtype == Subtypes::StandardVssVariable)
return QObject::tr("Standard");
if (subtype == Subtypes::AppleCrc32Vss)
if (subtype == Subtypes::Crc32VssVariable)
return QObject::tr("Apple CRC32");
if (subtype == Subtypes::AuthVss)
if (subtype == Subtypes::AuthVssVariable)
return QObject::tr("Auth");
else
return QObject::tr("Unknown subtype");
case Types::NvramEntryEvsa:
if (subtype == Subtypes::InvalidEvsaEntry)
return QObject::tr("Invalid");
if (subtype == Subtypes::UnknownEvsaEntry)
return QObject::tr("Unknown");
if (subtype == Subtypes::GuidEvsaEntry)
return QObject::tr("GUID");
if (subtype == Subtypes::NameEvsaEntry)
return QObject::tr("Name");
if (subtype == Subtypes::DataEvsaEntry)
return QObject::tr("Data");
else
return QObject::tr("Unknown subtype");
default:
return QObject::tr("Unknown subtype");
}

View File

@ -43,33 +43,35 @@ namespace Types {
File,
Section,
FreeSpace,
NvramStoreVss,
NvramStoreFdc,
NvramStoreFsys,
NvramStoreEvsa,
NvramVariableNvar,
NvramStorageVss,
NvramStorageFdc,
NvramStorageFsys,
NvramVariableVss,
NvramVariableFsys
NvramVariableFsys,
NvramEntryEvsa
};
}
namespace Subtypes {
enum ImageSubtypes{
IntelImage = 70,
IntelImage = 90,
UefiImage
};
enum CapsuleSubtypes {
AptioSignedCapsule = 80,
AptioSignedCapsule = 100,
AptioUnsignedCapsule,
UefiCapsule,
ToshibaCapsule
};
enum VolumeSubtypes {
UnknownVolume = 90,
UnknownVolume = 110,
Ffs2Volume,
Ffs3Volume,
VssNvramVolume
NvramVolume
};
enum RegionSubtypes {
@ -86,24 +88,32 @@ namespace Subtypes {
};
enum PaddingSubtypes {
ZeroPadding = 110,
ZeroPadding = 120,
OnePadding,
DataPadding
};
enum NvarVariableSubtypes {
InvalidNvar = 120,
InvalidLinkNvar,
LinkNvar,
DataNvar,
FullNvar
InvalidNvarVariable = 130,
InvalidLinkNvarVariable,
LinkNvarVariable,
DataNvarVariable,
FullNvarVariable
};
enum VssVariableSubtypes {
InvalidVss = 130,
StandardVss,
AppleCrc32Vss,
AuthVss
InvalidVssVariable = 140,
StandardVssVariable,
Crc32VssVariable,
AuthVssVariable
};
enum EvsaVariableSubtypes {
InvalidEvsaEntry = 150,
UnknownEvsaEntry,
GuidEvsaEntry,
NameEvsaEntry,
DataEvsaEntry,
};
};

View File

@ -95,7 +95,7 @@ QString errorCodeToQString(UINT8 errorCode)
case ERR_DEPEX_PARSE_FAILED: return QObject::tr("Dependency expression parsing failed");
case ERR_TRUNCATED_IMAGE: return QObject::tr("Image is truncated");
case ERR_INVALID_CAPSULE: return QObject::tr("Invalid capsule");
case ERR_STORAGES_NOT_FOUND: return QObject::tr("Storages not found");
case ERR_STORES_NOT_FOUND: return QObject::tr("Stores not found");
default: return QObject::tr("Unknown error %1").arg(errorCode);
}
}