mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-25 01:18:22 +08:00
1915 lines
81 KiB
C++
1915 lines
81 KiB
C++
|
/* nvramparser.cpp
|
||
|
|
||
|
Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
|
||
|
|
||
|
This program and the accompanying materials
|
||
|
are licensed and made available under the terms and conditions of the BSD License
|
||
|
which accompanies this distribution. The full text of the license may be found at
|
||
|
http://opensource.org/licenses/bsd-license.php.
|
||
|
|
||
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include <cinttypes>
|
||
|
|
||
|
#include "nvramparser.h"
|
||
|
#include "parsingdata.h"
|
||
|
#include "utility.h"
|
||
|
#include "nvram.h"
|
||
|
#include "ffs.h"
|
||
|
#include "fit.h"
|
||
|
|
||
|
#ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
|
||
|
USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data for the current item
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
|
||
|
// Get item data
|
||
|
const UByteArray data = model->body(index);
|
||
|
|
||
|
// Rename parent file
|
||
|
model->setText(model->findParentOfType(index, Types::File), UString("NVAR store"));
|
||
|
|
||
|
UINT32 offset = 0;
|
||
|
UINT32 guidsInStore = 0;
|
||
|
const UINT8 emptyByte = pdata.emptyByte;
|
||
|
// Parse all entries
|
||
|
while (1) {
|
||
|
bool msgUnknownExtDataFormat = false;
|
||
|
bool msgExtHeaderTooLong = false;
|
||
|
bool msgExtDataTooShort = false;
|
||
|
|
||
|
bool isInvalid = false;
|
||
|
bool isInvalidLink = false;
|
||
|
//bool isDataOnly = false;
|
||
|
bool hasExtendedHeader = false;
|
||
|
bool hasChecksum = false;
|
||
|
bool hasTimestamp = false;
|
||
|
bool hasHash = false;
|
||
|
bool hasGuidIndex = false;
|
||
|
|
||
|
UINT32 guidIndex = 0;
|
||
|
UINT8 storedChecksum = 0;
|
||
|
UINT8 calculatedChecksum = 0;
|
||
|
UINT32 extendedHeaderSize = 0;
|
||
|
UINT8 extendedAttributes = 0;
|
||
|
UINT64 timestamp = 0;
|
||
|
UByteArray hash;
|
||
|
|
||
|
UINT8 subtype = Subtypes::FullNvarEntry;
|
||
|
UString name;
|
||
|
UString text;
|
||
|
UByteArray header;
|
||
|
UByteArray body;
|
||
|
UByteArray tail;
|
||
|
|
||
|
UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
|
||
|
UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
|
||
|
|
||
|
// Get entry header
|
||
|
const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset);
|
||
|
|
||
|
// Check header size and signature
|
||
|
if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) ||
|
||
|
entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE ||
|
||
|
unparsedSize < entryHeader->Size) {
|
||
|
|
||
|
// Check if the data left is a free space or a padding
|
||
|
UByteArray padding = data.mid(offset, unparsedSize);
|
||
|
UINT8 type;
|
||
|
|
||
|
if ((UINT32)padding.count(emptyByte) == unparsedSize) {
|
||
|
// It's a free space
|
||
|
name = ("Free space");
|
||
|
type = Types::FreeSpace;
|
||
|
subtype = 0;
|
||
|
}
|
||
|
else {
|
||
|
// Nothing is parsed yet, but the file is not empty
|
||
|
if (!offset) {
|
||
|
msg(UString("parseNvarStore: file can't be parsed as NVAR variables store"), index);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// It's a padding
|
||
|
name = UString("Padding");
|
||
|
type = Types::Padding;
|
||
|
subtype = getPaddingType(padding);
|
||
|
}
|
||
|
// Get info
|
||
|
UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
// Add tree item
|
||
|
model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Add GUID store area
|
||
|
UByteArray guidArea = data.right(guidAreaSize);
|
||
|
// Get info
|
||
|
name = UString("GUID store area");
|
||
|
info = usprintf("Full size: %Xh (%u)\nGUIDs in store: %u",
|
||
|
guidArea.size(), guidArea.size(),
|
||
|
guidsInStore);
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + offset + padding.size();
|
||
|
// Add tree item
|
||
|
model->addItem(Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Contruct generic header and body
|
||
|
header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER));
|
||
|
body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER));
|
||
|
|
||
|
UINT32 lastVariableFlag = pdata.emptyByte ? 0xFFFFFF : 0;
|
||
|
|
||
|
// Set default next to predefined last value
|
||
|
pdata.nvar.next = lastVariableFlag;
|
||
|
|
||
|
// Entry is marked as invalid
|
||
|
if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set
|
||
|
isInvalid = true;
|
||
|
// Do not parse further
|
||
|
goto parsing_done;
|
||
|
}
|
||
|
|
||
|
// Add next node information to parsing data
|
||
|
if (entryHeader->Next != lastVariableFlag) {
|
||
|
subtype = Subtypes::LinkNvarEntry;
|
||
|
pdata.nvar.next = entryHeader->Next;
|
||
|
}
|
||
|
|
||
|
// Entry with extended header
|
||
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) {
|
||
|
hasExtendedHeader = true;
|
||
|
msgUnknownExtDataFormat = true;
|
||
|
|
||
|
extendedHeaderSize = *(UINT16*)(body.constData() + body.size() - sizeof(UINT16));
|
||
|
if (extendedHeaderSize > (UINT32)body.size()) {
|
||
|
msgExtHeaderTooLong = true;
|
||
|
isInvalid = true;
|
||
|
// Do not parse further
|
||
|
goto parsing_done;
|
||
|
}
|
||
|
|
||
|
extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize);
|
||
|
|
||
|
// Variable with checksum
|
||
|
if (extendedAttributes & NVRAM_NVAR_ENTRY_EXT_CHECKSUM) {
|
||
|
// Get stored checksum
|
||
|
storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8));
|
||
|
|
||
|
// Recalculate checksum for the variable
|
||
|
calculatedChecksum = 0;
|
||
|
// Include entry data
|
||
|
UINT8* start = (UINT8*)(entryHeader + 1);
|
||
|
for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) {
|
||
|
calculatedChecksum += *p;
|
||
|
}
|
||
|
// Include entry size and flags
|
||
|
start = (UINT8*)&entryHeader->Size;
|
||
|
for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
|
||
|
calculatedChecksum += *p;
|
||
|
}
|
||
|
// Include entry attributes
|
||
|
calculatedChecksum += entryHeader->Attributes;
|
||
|
|
||
|
hasChecksum = true;
|
||
|
msgUnknownExtDataFormat = false;
|
||
|
}
|
||
|
|
||
|
tail = body.mid(body.size() - extendedHeaderSize);
|
||
|
body = body.left(body.size() - extendedHeaderSize);
|
||
|
|
||
|
// Entry with authenticated write (for SecureBoot)
|
||
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) {
|
||
|
if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash
|
||
|
if ((UINT32)tail.size() < sizeof(UINT64)) {
|
||
|
msgExtDataTooShort = true;
|
||
|
isInvalid = true;
|
||
|
// Do not parse further
|
||
|
goto parsing_done;
|
||
|
}
|
||
|
|
||
|
timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8));
|
||
|
hasTimestamp = true;
|
||
|
msgUnknownExtDataFormat = false;
|
||
|
}
|
||
|
else { // Full or link variable have hash
|
||
|
if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) {
|
||
|
msgExtDataTooShort = true;
|
||
|
isInvalid = true;
|
||
|
// Do not parse further
|
||
|
goto parsing_done;
|
||
|
}
|
||
|
|
||
|
timestamp = *(UINT64*)(tail.constData() + sizeof(UINT8));
|
||
|
hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE);
|
||
|
hasTimestamp = true;
|
||
|
hasHash = true;
|
||
|
msgUnknownExtDataFormat = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Entry is data-only (nameless and GUIDless entry or link)
|
||
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set
|
||
|
isInvalidLink = true;
|
||
|
UModelIndex nvarIndex;
|
||
|
// Search prevously added entries for a link to this variable
|
||
|
for (int i = 0; i < model->rowCount(index); i++) {
|
||
|
nvarIndex = index.child(i, 0);
|
||
|
PARSING_DATA nvarPdata = parsingDataFromUModelIndex(nvarIndex);
|
||
|
if (nvarPdata.nvar.isValid && nvarPdata.nvar.next + nvarPdata.offset - parentOffset == offset) { // Previous link is present and valid
|
||
|
isInvalidLink = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// Check if the link is valid
|
||
|
if (!isInvalidLink) {
|
||
|
// Use the name and text of the previous link
|
||
|
name = model->name(nvarIndex);
|
||
|
text = model->text(nvarIndex);
|
||
|
|
||
|
if (entryHeader->Next == lastVariableFlag)
|
||
|
subtype = Subtypes::DataNvarEntry;
|
||
|
}
|
||
|
|
||
|
//isDataOnly = true;
|
||
|
// Do not parse further
|
||
|
goto parsing_done;
|
||
|
}
|
||
|
|
||
|
// Get entry name
|
||
|
{
|
||
|
UINT32 nameOffset = (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) ? sizeof(EFI_GUID) : sizeof(UINT8); // GUID can be stored with the variable or in a separate store, so there will only be an index of it
|
||
|
CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset;
|
||
|
UINT32 nameSize = 0;
|
||
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
|
||
|
text = UString(namePtr);
|
||
|
nameSize = text.length() + 1;
|
||
|
}
|
||
|
else { // Name is stored as UCS2 string of CHAR16s
|
||
|
text = UString::fromUtf16((CHAR16*)namePtr);
|
||
|
nameSize = (text.length() + 1) * 2;
|
||
|
}
|
||
|
|
||
|
// Get entry GUID
|
||
|
if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself
|
||
|
name = guidToUString(*(EFI_GUID*)(entryHeader + 1));
|
||
|
}
|
||
|
// GUID is stored in GUID list at the end of the store
|
||
|
else {
|
||
|
guidIndex = *(UINT8*)(entryHeader + 1);
|
||
|
if (guidsInStore < guidIndex + 1)
|
||
|
guidsInStore = guidIndex + 1;
|
||
|
|
||
|
// 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 = guidToUString(*guidPtr);
|
||
|
hasGuidIndex = true;
|
||
|
}
|
||
|
|
||
|
// Include name and GUID into the header and remove them from body
|
||
|
header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize);
|
||
|
body = body.mid(nameOffset + nameSize);
|
||
|
}
|
||
|
parsing_done:
|
||
|
UString info;
|
||
|
|
||
|
// Rename invalid entries according to their types
|
||
|
pdata.nvar.isValid = TRUE;
|
||
|
if (isInvalid) {
|
||
|
name = UString("Invalid");
|
||
|
subtype = Subtypes::InvalidNvarEntry;
|
||
|
pdata.nvar.isValid = FALSE;
|
||
|
}
|
||
|
else if (isInvalidLink) {
|
||
|
name = UString("Invalid link");
|
||
|
subtype = Subtypes::InvalidLinkNvarEntry;
|
||
|
pdata.nvar.isValid = FALSE;
|
||
|
}
|
||
|
else // Add GUID info for valid entries
|
||
|
info += UString("Variable GUID: ") + name + UString("\n");
|
||
|
|
||
|
// Add GUID index information
|
||
|
if (hasGuidIndex)
|
||
|
info += usprintf("GUID index: %u\n", guidIndex);
|
||
|
|
||
|
// Add header, body and extended data info
|
||
|
info += usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
||
|
entryHeader->Size, entryHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size());
|
||
|
|
||
|
// Add attributes info
|
||
|
info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes);
|
||
|
// Translate attributes to text
|
||
|
if (entryHeader->Attributes && entryHeader->Attributes != 0xFF)
|
||
|
info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")");
|
||
|
|
||
|
// Add next node info
|
||
|
if (!isInvalid && entryHeader->Next != lastVariableFlag)
|
||
|
info += usprintf("\nNext node at offset: %Xh", parentOffset + offset + entryHeader->Next);
|
||
|
|
||
|
// Add extended header info
|
||
|
if (hasExtendedHeader) {
|
||
|
info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (",
|
||
|
extendedHeaderSize, extendedHeaderSize,
|
||
|
extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
|
||
|
|
||
|
// Add checksum
|
||
|
if (hasChecksum)
|
||
|
info += usprintf("\nChecksum: %02Xh", storedChecksum) +
|
||
|
(calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
|
||
|
|
||
|
// Add timestamp
|
||
|
if (hasTimestamp)
|
||
|
info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp);
|
||
|
|
||
|
// Add hash
|
||
|
if (hasHash)
|
||
|
info += UString("\nHash: ") + UString(hash.toHex().constData());
|
||
|
}
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add tree item
|
||
|
UModelIndex varIndex = model->addItem(Types::NvarEntry, subtype, name, text, info, header, body, tail, false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Show messages
|
||
|
if (msgUnknownExtDataFormat) msg(UString("parseNvarStore: unknown extended data format"), varIndex);
|
||
|
if (msgExtHeaderTooLong) msg(usprintf("parseNvarStore: extended header size (%Xh) is greater than body size (%Xh)",
|
||
|
extendedHeaderSize, body.size()), varIndex);
|
||
|
if (msgExtDataTooShort) msg(usprintf("parseNvarStore: extended header size (%Xh) is too small for timestamp and hash",
|
||
|
tail.size()), varIndex);
|
||
|
|
||
|
// Try parsing the entry data as NVAR storage if it begins with NVAR signature
|
||
|
if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
|
||
|
&& *(const UINT32*)body.constData() == NVRAM_NVAR_ENTRY_SIGNATURE)
|
||
|
parseNvarStore(varIndex);
|
||
|
|
||
|
// Move to next exntry
|
||
|
offset += entryHeader->Size;
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
|
||
|
// Get item data
|
||
|
UByteArray data = model->body(index);
|
||
|
|
||
|
// Search for first store
|
||
|
USTATUS result;
|
||
|
UINT32 prevStoreOffset;
|
||
|
result = findNextStore(index, data, parentOffset, 0, prevStoreOffset);
|
||
|
if (result)
|
||
|
return result;
|
||
|
|
||
|
// First store is not at the beginning of volume body
|
||
|
UString name;
|
||
|
UString info;
|
||
|
if (prevStoreOffset > 0) {
|
||
|
// Get info
|
||
|
UByteArray padding = data.left(prevStoreOffset);
|
||
|
name = UString("Padding");
|
||
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index);
|
||
|
}
|
||
|
|
||
|
// Search for and parse all stores
|
||
|
UINT32 storeOffset = prevStoreOffset;
|
||
|
UINT32 prevStoreSize = 0;
|
||
|
|
||
|
while (!result)
|
||
|
{
|
||
|
// Padding between stores
|
||
|
if (storeOffset > prevStoreOffset + prevStoreSize) {
|
||
|
UINT32 paddingOffset = prevStoreOffset + prevStoreSize;
|
||
|
UINT32 paddingSize = storeOffset - paddingOffset;
|
||
|
UByteArray padding = data.mid(paddingOffset, paddingSize);
|
||
|
|
||
|
// Get info
|
||
|
name = UString("Padding");
|
||
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + paddingOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index);
|
||
|
}
|
||
|
|
||
|
// Get store size
|
||
|
UINT32 storeSize = 0;
|
||
|
result = getStoreSize(data, storeOffset, storeSize);
|
||
|
if (result) {
|
||
|
msg(UString("parseNvramVolumeBody: getStoreSize failed with error ") + errorCodeToUString(result), index);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Check that current store is fully present in input
|
||
|
if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) {
|
||
|
// Mark the rest as padding and finish parsing
|
||
|
UByteArray padding = data.mid(storeOffset);
|
||
|
|
||
|
// Get info
|
||
|
name = UString("Padding");
|
||
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + storeOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
UModelIndex paddingIndex = model->addItem(Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index);
|
||
|
msg(UString("parseNvramVolumeBody: one of stores inside overlaps the end of data"), paddingIndex);
|
||
|
|
||
|
// Update variables
|
||
|
prevStoreOffset = storeOffset;
|
||
|
prevStoreSize = padding.size();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
UByteArray store = data.mid(storeOffset, storeSize);
|
||
|
// Parse current store header
|
||
|
UModelIndex storeIndex;
|
||
|
result = parseStoreHeader(store, parentOffset + storeOffset, index, storeIndex);
|
||
|
if (result)
|
||
|
msg(UString("parseNvramVolumeBody: store header parsing failed with error ") + errorCodeToUString(result), index);
|
||
|
|
||
|
// Go to next store
|
||
|
prevStoreOffset = storeOffset;
|
||
|
prevStoreSize = storeSize;
|
||
|
result = findNextStore(index, data, parentOffset, storeOffset + prevStoreSize, storeOffset);
|
||
|
}
|
||
|
|
||
|
// Padding/free space at the end
|
||
|
storeOffset = prevStoreOffset + prevStoreSize;
|
||
|
if ((UINT32)data.size() > storeOffset) {
|
||
|
UByteArray padding = data.mid(storeOffset);
|
||
|
UINT8 type;
|
||
|
UINT8 subtype;
|
||
|
if (padding.count(pdata.emptyByte) == padding.size()) {
|
||
|
// It's a free space
|
||
|
name = UString("Free space");
|
||
|
type = Types::FreeSpace;
|
||
|
subtype = 0;
|
||
|
}
|
||
|
else {
|
||
|
// Nothing is parsed yet, but the file is not empty
|
||
|
if (!storeOffset) {
|
||
|
msg(UString("parseNvramVolumeBody: can't be parsed as NVRAM volume"), index);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// It's a padding
|
||
|
name = UString("Padding");
|
||
|
type = Types::Padding;
|
||
|
subtype = getPaddingType(padding);
|
||
|
}
|
||
|
|
||
|
// Add info
|
||
|
info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + storeOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), true, parsingDataToUByteArray(pdata), index);
|
||
|
}
|
||
|
|
||
|
// Parse bodies
|
||
|
for (int i = 0; i < model->rowCount(index); i++) {
|
||
|
UModelIndex current = index.child(i, 0);
|
||
|
switch (model->type(current)) {
|
||
|
case Types::VssStore:
|
||
|
case Types::FdcStore: parseVssStoreBody(current); break;
|
||
|
case Types::FsysStore: parseFsysStoreBody(current); break;
|
||
|
case Types::EvsaStore: parseEvsaStoreBody(current); break;
|
||
|
case Types::FlashMapStore: parseFlashMapBody(current); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 parentOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset)
|
||
|
{
|
||
|
UINT32 dataSize = volume.size();
|
||
|
|
||
|
if (dataSize < sizeof(UINT32))
|
||
|
return U_STORES_NOT_FOUND;
|
||
|
|
||
|
UINT32 offset = storeOffset;
|
||
|
for (; offset < dataSize - sizeof(UINT32); offset++) {
|
||
|
const UINT32* currentPos = (const UINT32*)(volume.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(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", parentOffset + offset, vssHeader->Format), index);
|
||
|
continue;
|
||
|
}
|
||
|
if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
|
||
|
msg(usprintf("findNextStore: VSS store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, vssHeader->Size), index);
|
||
|
continue;
|
||
|
}
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { //FDC signature found
|
||
|
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos;
|
||
|
if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) {
|
||
|
msg(usprintf("findNextStore: FDC store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fdcHeader->Size), index);
|
||
|
continue;
|
||
|
}
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_GAID_STORE_SIGNATURE) { //Fsys or Gaid signature found
|
||
|
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos;
|
||
|
if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) {
|
||
|
msg(usprintf("findNextStore: Fsys store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, fsysHeader->Size), index);
|
||
|
continue;
|
||
|
}
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found
|
||
|
if (offset < sizeof(UINT32))
|
||
|
continue;
|
||
|
|
||
|
const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1);
|
||
|
if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) {
|
||
|
msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid type %02Xh", parentOffset + offset - 4, evsaHeader->Header.Type), index);
|
||
|
continue;
|
||
|
}
|
||
|
if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) {
|
||
|
msg(usprintf("findNextStore: EVSA store candidate at offset %Xh skipped, has invalid size %Xh", parentOffset + offset, evsaHeader->StoreSize), index);
|
||
|
continue;
|
||
|
}
|
||
|
// All checks passed, store found
|
||
|
offset -= sizeof(UINT32);
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *currentPos == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { //Possible FTW block signature found
|
||
|
UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID));
|
||
|
if (guid != NVRAM_MAIN_STORE_VOLUME_GUID && guid != EDKII_WORKING_BLOCK_SIGNATURE_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(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftwHeader->WriteQueueSize), 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(usprintf("findNextStore: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", parentOffset + offset, ftw64Header->WriteQueueSize), index);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
else // Unknown header
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) {// Phoenix SCT flash map
|
||
|
UByteArray signature = UByteArray(volume.constData() + offset, NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH);
|
||
|
if (signature != NVRAM_PHOENIX_FLASH_MAP_SIGNATURE) // Check the whole signature
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
|
||
|
const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)currentPos;
|
||
|
|
||
|
// Check size
|
||
|
if (cmdbHeader->HeaderSize != sizeof(PHOENIX_CMDB_HEADER))
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION) {// Intel microcode
|
||
|
if (!INTEL_MICROCODE_HEADER_SIZES_VALID(currentPos)) // Check header sizes
|
||
|
continue;
|
||
|
|
||
|
// Check reserved bytes
|
||
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos;
|
||
|
bool reservedBytesValid = true;
|
||
|
for (UINT32 i = 0; i < sizeof(ucodeHeader->Reserved); i++)
|
||
|
if (ucodeHeader->Reserved[i] != INTEL_MICROCODE_HEADER_RESERVED_BYTE) {
|
||
|
reservedBytesValid = false;
|
||
|
break;
|
||
|
}
|
||
|
if (!reservedBytesValid)
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
|
||
|
if (offset < 4 * sizeof(UINT32))
|
||
|
continue;
|
||
|
|
||
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)(currentPos - 4);
|
||
|
// Check type
|
||
|
if (pubkeyHeader->Type != OEM_ACTIVATION_PUBKEY_TYPE)
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
offset -= 4 * sizeof(UINT32);
|
||
|
break;
|
||
|
}
|
||
|
else if (*currentPos == OEM_ACTIVATION_MARKER_WINDOWS_FLAG_PART1) { // SLIC marker
|
||
|
if (offset >= dataSize - sizeof(UINT64) ||
|
||
|
*(const UINT64*)currentPos != OEM_ACTIVATION_MARKER_WINDOWS_FLAG ||
|
||
|
offset < 26) // Check full windows flag and structure size
|
||
|
continue;
|
||
|
|
||
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)(volume.constData() + offset - 26);
|
||
|
// Check reserved bytes
|
||
|
bool reservedBytesValid = true;
|
||
|
for (UINT32 i = 0; i < sizeof(markerHeader->Reserved); i++)
|
||
|
if (markerHeader->Reserved[i] != OEM_ACTIVATION_MARKER_RESERVED_BYTE) {
|
||
|
reservedBytesValid = false;
|
||
|
break;
|
||
|
}
|
||
|
if (!reservedBytesValid)
|
||
|
continue;
|
||
|
|
||
|
// All checks passed, store found
|
||
|
offset -= 26;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// No more stores found
|
||
|
if (offset >= dataSize - sizeof(UINT32))
|
||
|
return U_STORES_NOT_FOUND;
|
||
|
|
||
|
nextStoreOffset = offset;
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize)
|
||
|
{
|
||
|
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;
|
||
|
storeSize = vssHeader->Size;
|
||
|
}
|
||
|
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
|
||
|
const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature;
|
||
|
storeSize = fdcHeader->Size;
|
||
|
}
|
||
|
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) {
|
||
|
const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
|
||
|
storeSize = fsysHeader->Size;
|
||
|
}
|
||
|
else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) {
|
||
|
const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature;
|
||
|
storeSize = evsaHeader->StoreSize;
|
||
|
}
|
||
|
else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_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) + (UINT32)ftw64Header->WriteQueueSize;
|
||
|
}
|
||
|
}
|
||
|
else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) { // Phoenix SCT flash map
|
||
|
const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)signature;
|
||
|
storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + sizeof(PHOENIX_FLASH_MAP_ENTRY) * flashMapHeader->NumEntries;
|
||
|
}
|
||
|
else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
|
||
|
storeSize = NVRAM_PHOENIX_CMDB_SIZE; // It's a predefined max size, no need to calculate
|
||
|
}
|
||
|
else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
|
||
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)signature;
|
||
|
storeSize = pubkeyHeader->Size;
|
||
|
}
|
||
|
else if (*(const UINT64*)(data.constData() + storeOffset + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) { // SLIC marker
|
||
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature;
|
||
|
storeSize = markerHeader->Size;
|
||
|
}
|
||
|
else if (*signature == INTEL_MICROCODE_HEADER_VERSION) { // Intel microcode, must be checked after SLIC marker because of the same *signature values
|
||
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature;
|
||
|
storeSize = ucodeHeader->TotalSize;
|
||
|
}
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) {
|
||
|
msg(UString("parseVssStoreHeader: volume body is too small even for VSS store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get VSS store header
|
||
|
const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < vssStoreHeader->Size) {
|
||
|
msg(usprintf("parseVssStoreHeader: VSS store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
vssStoreHeader->Size, vssStoreHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER));
|
||
|
UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), vssStoreHeader->Size - sizeof(VSS_VARIABLE_STORE_HEADER));
|
||
|
|
||
|
// Add info
|
||
|
bool isSvsStore = (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE);
|
||
|
UString name = isSvsStore ? UString("SVS store") : UString("VSS store");
|
||
|
UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh",
|
||
|
isSvsStore ? "$SVS" : "$VSS",
|
||
|
vssStoreHeader->Size, vssStoreHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
vssStoreHeader->Format,
|
||
|
vssStoreHeader->State,
|
||
|
vssStoreHeader->Unknown);
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) {
|
||
|
msg(UString("parseFtwStoreHeader: volume body is too small even for FTW store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get FTW block headers
|
||
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)store.constData();
|
||
|
const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)store.constData();
|
||
|
|
||
|
// 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) + (UINT32)ftw64BlockHeader->WriteQueueSize;
|
||
|
has32bitHeader = false;
|
||
|
}
|
||
|
if (dataSize < ftwBlockSize) {
|
||
|
msg(usprintf("parseFtwStoreHeader: FTW store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
ftwBlockSize, ftwBlockSize,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64);
|
||
|
UByteArray header = store.left(headerSize);
|
||
|
UByteArray body = store.mid(headerSize, ftwBlockSize - headerSize);
|
||
|
|
||
|
// Check block header checksum
|
||
|
UByteArray 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
|
||
|
UString name("FTW store");
|
||
|
UString info = UString("Signature: ") + guidToUString(ftw32BlockHeader->Signature) +
|
||
|
usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nHeader CRC32: %08Xh",
|
||
|
ftwBlockSize, ftwBlockSize,
|
||
|
headerSize, headerSize,
|
||
|
body.size(), body.size(),
|
||
|
ftw32BlockHeader->State,
|
||
|
ftw32BlockHeader->Crc) +
|
||
|
(ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(FDC_VOLUME_HEADER)) {
|
||
|
msg(UString("parseFdcStoreHeader: volume body is too small even for FDC store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get Fdc store header
|
||
|
const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < fdcStoreHeader->Size) {
|
||
|
msg(usprintf("parseFdcStoreHeader: FDC store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
fdcStoreHeader->Size, fdcStoreHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Determine internal volume header size
|
||
|
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);
|
||
|
headerSize = volumeHeader->ExtHeaderOffset + extendedHeader->ExtHeaderSize;
|
||
|
}
|
||
|
else
|
||
|
headerSize = volumeHeader->HeaderLength;
|
||
|
|
||
|
// Extended header end can be unaligned
|
||
|
headerSize = ALIGN8(headerSize);
|
||
|
|
||
|
// Add VSS store header
|
||
|
headerSize += sizeof(VSS_VARIABLE_STORE_HEADER);
|
||
|
|
||
|
// Add FDC header
|
||
|
headerSize += sizeof(FDC_VOLUME_HEADER);
|
||
|
|
||
|
// Check sanity of combined header size
|
||
|
if (dataSize < headerSize) {
|
||
|
msg(usprintf("parseFdcStoreHeader: FDC store header size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
fdcStoreHeader->Size,fdcStoreHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(headerSize);
|
||
|
UByteArray body = store.mid(headerSize, fdcStoreHeader->Size - headerSize);
|
||
|
|
||
|
// Add info
|
||
|
UString name("FDC store");
|
||
|
UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
||
|
fdcStoreHeader->Size, fdcStoreHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size());
|
||
|
|
||
|
// TODO: add internal headers info
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) {
|
||
|
msg(UString("parseFsysStoreHeader: volume body is too small even for Fsys store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get Fsys store header
|
||
|
const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < fsysStoreHeader->Size) {
|
||
|
msg(usprintf("parseFsysStoreHeader: Fsys store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
fsysStoreHeader->Size, fsysStoreHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER));
|
||
|
UByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
|
||
|
|
||
|
// Check store checksum
|
||
|
UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData();
|
||
|
UINT32 calculatedCrc = crc32(0, (const UINT8*)store.constData(), (const UINT32)store.size() - sizeof(UINT32));
|
||
|
|
||
|
// Add info
|
||
|
bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE);
|
||
|
UString name = isGaidStore ? UString("Gaid store") : UString("Fsys store");
|
||
|
UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nUnknown0: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh",
|
||
|
isGaidStore ? "Gaid" : "Fsys",
|
||
|
fsysStoreHeader->Size, fsysStoreHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
fsysStoreHeader->Unknown0,
|
||
|
fsysStoreHeader->Unknown1)
|
||
|
+ (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check dataSize
|
||
|
if (dataSize < sizeof(EVSA_STORE_ENTRY)) {
|
||
|
msg(UString("parseEvsaStoreHeader: volume body is too small even for EVSA store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get EVSA store header
|
||
|
const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < evsaStoreHeader->StoreSize) {
|
||
|
msg(usprintf("parseEvsaStoreHeader: EVSA store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(evsaStoreHeader->Header.Size);
|
||
|
UByteArray 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
|
||
|
UString name("EVSA store");
|
||
|
UString info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nAttributes: %08Xh\nChecksum: %02Xh",
|
||
|
evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
evsaStoreHeader->Header.Type,
|
||
|
evsaStoreHeader->Attributes,
|
||
|
evsaStoreHeader->Header.Checksum) +
|
||
|
(evsaStoreHeader->Header.Checksum != calculated ? usprintf("%, invalid, should be %02Xh", calculated) : UString(", valid"));
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check data size
|
||
|
if (dataSize < sizeof(PHOENIX_FLASH_MAP_HEADER)) {
|
||
|
msg(UString("parseFlashMapStoreHeader: volume body is too small even for FlashMap block header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get FlashMap block header
|
||
|
const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
UINT32 flashMapSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + flashMapHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY);
|
||
|
if (dataSize < flashMapSize) {
|
||
|
msg(usprintf("parseFlashMapStoreHeader: FlashMap block size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
flashMapSize, flashMapSize,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(PHOENIX_FLASH_MAP_HEADER));
|
||
|
UByteArray body = store.mid(sizeof(PHOENIX_FLASH_MAP_HEADER), flashMapSize - sizeof(PHOENIX_FLASH_MAP_HEADER));
|
||
|
|
||
|
// Add info
|
||
|
UString name("Phoenix SCT flash map");
|
||
|
UString info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nNumber of entries: %u",
|
||
|
flashMapSize, flashMapSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
flashMapHeader->NumEntries);
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(PHOENIX_CMDB_HEADER)) {
|
||
|
msg(UString("parseCmdbStoreHeader: volume body is too small even for CMDB store header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
UINT32 cmdbSize = NVRAM_PHOENIX_CMDB_SIZE;
|
||
|
if (dataSize < cmdbSize) {
|
||
|
msg(usprintf("parseCmdbStoreHeader: CMDB store size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
cmdbSize, cmdbSize,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get store header
|
||
|
const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)store.constData();
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(cmdbHeader->TotalSize);
|
||
|
UByteArray body = store.mid(cmdbHeader->TotalSize, cmdbSize - cmdbHeader->TotalSize);
|
||
|
|
||
|
// Add info
|
||
|
UString name("CMDB store");
|
||
|
UString info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)",
|
||
|
cmdbSize, cmdbSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size());
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check data size
|
||
|
if (dataSize < sizeof(OEM_ACTIVATION_PUBKEY)) {
|
||
|
msg(UString("parseSlicPubkeyHeader: volume body is too small even for SLIC pubkey header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get SLIC pubkey header
|
||
|
const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < pubkeyHeader->Size) {
|
||
|
msg(usprintf("parseSlicPubkeyHeader: SLIC pubkey size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
pubkeyHeader->Size, pubkeyHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(OEM_ACTIVATION_PUBKEY));
|
||
|
|
||
|
// Add info
|
||
|
UString name("SLIC pubkey");
|
||
|
UString info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n"
|
||
|
"Key type: %02Xh\nVersion: %02Xh\nAlgorithm: %08Xh\nMagic: RSA1\nBit length: %08Xh\nExponent: %08Xh",
|
||
|
pubkeyHeader->Size, pubkeyHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
pubkeyHeader->KeyType,
|
||
|
pubkeyHeader->Version,
|
||
|
pubkeyHeader->Algorithm,
|
||
|
pubkeyHeader->BitLength,
|
||
|
pubkeyHeader->Exponent);
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check data size
|
||
|
if (dataSize < sizeof(OEM_ACTIVATION_MARKER)) {
|
||
|
msg(UString("parseSlicMarkerHeader: volume body is too small even for SLIC marker header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get SLIC marker header
|
||
|
const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < markerHeader->Size) {
|
||
|
msg(usprintf("parseSlicMarkerHeader: SLIC marker size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
markerHeader->Size, markerHeader->Size,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(OEM_ACTIVATION_MARKER));
|
||
|
|
||
|
// Add info
|
||
|
UString name("SLIC marker");
|
||
|
UString info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: 0h (0)\n"
|
||
|
"Version: %08Xh\nOEM ID: %s\nOEM table ID: %s\nWindows flag: WINDOWS\nSLIC version: %08Xh",
|
||
|
markerHeader->Size, markerHeader->Size,
|
||
|
header.size(), header.size(),
|
||
|
markerHeader->Version,
|
||
|
(const char*)UString((const char*)&(markerHeader->OemId)).left(6).toLocal8Bit(),
|
||
|
(const char*)UString((const char*)&(markerHeader->OemTableId)).left(8).toLocal8Bit(),
|
||
|
markerHeader->SlicVersion);
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseIntelMicrocodeHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
|
||
|
// Check data size
|
||
|
if (dataSize < sizeof(INTEL_MICROCODE_HEADER)) {
|
||
|
msg(UString("parseIntelMicrocodeHeader: volume body is too small even for Intel microcode header"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get Intel microcode header
|
||
|
const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)store.constData();
|
||
|
|
||
|
// Check store size
|
||
|
if (dataSize < ucodeHeader->TotalSize) {
|
||
|
msg(usprintf("parseIntelMicrocodeHeader: Intel microcode size %Xh (%u) is greater than volume body size %Xh (%u)",
|
||
|
ucodeHeader->TotalSize, ucodeHeader->TotalSize,
|
||
|
dataSize, dataSize), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Get parsing data
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(parent);
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = store.left(sizeof(INTEL_MICROCODE_HEADER));
|
||
|
UByteArray body = store.mid(sizeof(INTEL_MICROCODE_HEADER), ucodeHeader->DataSize);
|
||
|
|
||
|
//TODO: recalculate checksum
|
||
|
|
||
|
// Add info
|
||
|
UString name("Intel microcode");
|
||
|
UString info = usprintf("Revision: 1h\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\n"
|
||
|
"Date: %08Xh\nCPU signature: %08Xh\nChecksum: %08Xh\nLoader revision: %08Xh\nCPU flags: %08Xh",
|
||
|
ucodeHeader->TotalSize, ucodeHeader->TotalSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
ucodeHeader->Date,
|
||
|
ucodeHeader->CpuSignature,
|
||
|
ucodeHeader->Checksum,
|
||
|
ucodeHeader->LoaderRevision,
|
||
|
ucodeHeader->CpuFlags);
|
||
|
|
||
|
// Add correct offset
|
||
|
pdata.offset = parentOffset;
|
||
|
|
||
|
// Add tree item
|
||
|
index = model->addItem(Types::Microcode, Subtypes::IntelMicrocode, name, UString(), info, header, body, UByteArray(), true, parsingDataToUByteArray(pdata), parent);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 parentOffset, const UModelIndex & parent, UModelIndex & index)
|
||
|
{
|
||
|
const UINT32 dataSize = (const UINT32)store.size();
|
||
|
const UINT32* signature = (const UINT32*)store.constData();
|
||
|
// Check store size
|
||
|
if (dataSize < sizeof(UINT32)) {
|
||
|
msg(UString("parseStoreHeader: volume body is too small even for store signature"), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Check signature and run parser function needed
|
||
|
// VSS/SVS store
|
||
|
if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE)
|
||
|
return parseVssStoreHeader(store, parentOffset, parent, index);
|
||
|
// FTW store
|
||
|
else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1)
|
||
|
return parseFtwStoreHeader(store, parentOffset, parent, index);
|
||
|
// FDC store
|
||
|
else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE)
|
||
|
return parseFdcStoreHeader(store, parentOffset, parent, index);
|
||
|
// Apple Fsys/Gaid store
|
||
|
else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE)
|
||
|
return parseFsysStoreHeader(store, parentOffset, parent, index);
|
||
|
// EVSA store
|
||
|
else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE)
|
||
|
return parseEvsaStoreHeader(store, parentOffset, parent, index);
|
||
|
// Phoenix SCT flash map
|
||
|
else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1)
|
||
|
return parseFlashMapStoreHeader(store, parentOffset, parent, index);
|
||
|
// Phoenix CMDB store
|
||
|
else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE)
|
||
|
return parseCmdbStoreHeader(store, parentOffset, parent, index);
|
||
|
// SLIC pubkey
|
||
|
else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC)
|
||
|
return parseSlicPubkeyHeader(store, parentOffset, parent, index);
|
||
|
// SLIC marker
|
||
|
else if (*(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG)
|
||
|
return parseSlicMarkerHeader(store, parentOffset, parent, index);
|
||
|
// Intel microcode
|
||
|
// Must be checked after SLIC marker because of the same *signature values
|
||
|
else if (*signature == INTEL_MICROCODE_HEADER_VERSION)
|
||
|
return parseIntelMicrocodeHeader(store, parentOffset, parent, index);
|
||
|
|
||
|
msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data for the current item
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
const UByteArray data = model->body(index);
|
||
|
|
||
|
// Check that the is enough space for variable header
|
||
|
const UINT32 dataSize = (UINT32)data.size();
|
||
|
if (dataSize < sizeof(VSS_VARIABLE_HEADER)) {
|
||
|
msg(UString("parseVssStoreBody: store body is too small even for VSS variable header"), index);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
UINT32 offset = 0;
|
||
|
|
||
|
// Parse all variables
|
||
|
while (1) {
|
||
|
bool isInvalid = false;
|
||
|
bool isAuthenticated = false;
|
||
|
bool isAppleCrc32 = false;
|
||
|
|
||
|
UINT32 storedCrc32 = 0;
|
||
|
UINT32 calculatedCrc32 = 0;
|
||
|
UINT64 monotonicCounter = 0;
|
||
|
EFI_TIME timestamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||
|
UINT32 pubKeyIndex = 0;
|
||
|
|
||
|
UINT8 subtype = 0;
|
||
|
UString name;
|
||
|
UString text;
|
||
|
EFI_GUID* variableGuid;
|
||
|
CHAR16* variableName = (CHAR16*)L"";
|
||
|
UByteArray header;
|
||
|
UByteArray body;
|
||
|
|
||
|
UINT32 unparsedSize = dataSize - offset;
|
||
|
|
||
|
// Get variable header
|
||
|
const VSS_VARIABLE_HEADER* variableHeader = (const VSS_VARIABLE_HEADER*)(data.constData() + offset);
|
||
|
|
||
|
// Check variable header to fit in still unparsed data
|
||
|
UINT32 variableSize = 0;
|
||
|
if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER)
|
||
|
&& variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) {
|
||
|
|
||
|
// Apple VSS variable with CRC32 of the data
|
||
|
if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) {
|
||
|
isAppleCrc32 = true;
|
||
|
if (unparsedSize < sizeof(VSS_APPLE_VARIABLE_HEADER)) {
|
||
|
variableSize = 0;
|
||
|
}
|
||
|
else {
|
||
|
const VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (const VSS_APPLE_VARIABLE_HEADER*)variableHeader;
|
||
|
variableSize = sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize + appleVariableHeader->DataSize;
|
||
|
variableGuid = (EFI_GUID*)&appleVariableHeader->VendorGuid;
|
||
|
variableName = (CHAR16*)(appleVariableHeader + 1);
|
||
|
|
||
|
header = data.mid(offset, sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize);
|
||
|
body = data.mid(offset + header.size(), appleVariableHeader->DataSize);
|
||
|
|
||
|
// Calculate CRC32 of the variable data
|
||
|
storedCrc32 = appleVariableHeader->DataCrc32;
|
||
|
calculatedCrc32 = crc32(0, (const UINT8*)body.constData(), body.size());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Authenticated variable
|
||
|
else if ((variableHeader->Attributes & NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
|
||
|
|| (variableHeader->Attributes & NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
|
||
|
|| (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPEND_WRITE)
|
||
|
|| (variableHeader->NameSize == 0 && variableHeader->DataSize == 0)) { // If both NameSize and DataSize are zeros, it's auth variable with zero montonic counter
|
||
|
isAuthenticated = true;
|
||
|
if (unparsedSize < sizeof(VSS_AUTH_VARIABLE_HEADER)) {
|
||
|
variableSize = 0;
|
||
|
}
|
||
|
else {
|
||
|
const VSS_AUTH_VARIABLE_HEADER* authVariableHeader = (const VSS_AUTH_VARIABLE_HEADER*)variableHeader;
|
||
|
variableSize = sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize + authVariableHeader->DataSize;
|
||
|
variableGuid = (EFI_GUID*)&authVariableHeader->VendorGuid;
|
||
|
variableName = (CHAR16*)(authVariableHeader + 1);
|
||
|
|
||
|
header = data.mid(offset, sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize);
|
||
|
body = data.mid(offset + header.size(), authVariableHeader->DataSize);
|
||
|
|
||
|
monotonicCounter = authVariableHeader->MonotonicCounter;
|
||
|
timestamp = authVariableHeader->Timestamp;
|
||
|
pubKeyIndex = authVariableHeader->PubKeyIndex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Normal VSS variable
|
||
|
if (!isAuthenticated && !isAppleCrc32) {
|
||
|
variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize;
|
||
|
variableGuid = (EFI_GUID*)&variableHeader->VendorGuid;
|
||
|
variableName = (CHAR16*)(variableHeader + 1);
|
||
|
|
||
|
header = data.mid(offset, sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize);
|
||
|
body = data.mid(offset + header.size(), variableHeader->DataSize);
|
||
|
}
|
||
|
|
||
|
// There is also a case of authenticated Apple variables, but I haven't seen one yet
|
||
|
|
||
|
// Check variable state
|
||
|
if (variableHeader->State != NVRAM_VSS_VARIABLE_ADDED && variableHeader->State != NVRAM_VSS_VARIABLE_HEADER_VALID) {
|
||
|
isInvalid = true;
|
||
|
}
|
||
|
|
||
|
// Check variable size
|
||
|
if (variableSize > unparsedSize) {
|
||
|
unparsedSize = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Can't parse further, add the last element and break the loop
|
||
|
if (!variableSize) {
|
||
|
// Check if the data left is a free space or a padding
|
||
|
UByteArray padding = data.mid(offset, unparsedSize);
|
||
|
UINT8 type;
|
||
|
|
||
|
if (padding.count(pdata.emptyByte) == padding.size()) {
|
||
|
// It's a free space
|
||
|
name = UString("Free space");
|
||
|
type = Types::FreeSpace;
|
||
|
subtype = 0;
|
||
|
}
|
||
|
else {
|
||
|
// Nothing is parsed yet, but the store is not empty
|
||
|
if (!offset) {
|
||
|
msg(UString("parseVssStoreBody: store can't be parsed as VSS store"), index);
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// It's a padding
|
||
|
name = UString("Padding");
|
||
|
type = Types::Padding;
|
||
|
subtype = getPaddingType(padding);
|
||
|
}
|
||
|
|
||
|
// Get info
|
||
|
UString info = usprintf("Full size: %Xh (%u)", padding.size(), padding.size());
|
||
|
|
||
|
// Construct parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(type, subtype, name, UString(), info, UByteArray(), padding, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
UString info;
|
||
|
|
||
|
// Rename invalid variables
|
||
|
if (isInvalid) {
|
||
|
name = UString("Invalid");
|
||
|
}
|
||
|
else { // Add GUID and text for valid variables
|
||
|
name = guidToUString(*variableGuid);
|
||
|
info += UString("Variable GUID: ") + name + UString("\n");
|
||
|
text = UString::fromUtf16(variableName);
|
||
|
}
|
||
|
|
||
|
// Add info
|
||
|
info += usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nState: %02Xh\nAttributes: %08Xh (",
|
||
|
variableSize, variableSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
variableHeader->State,
|
||
|
variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")");
|
||
|
|
||
|
// Set subtype and add related info
|
||
|
if (isInvalid)
|
||
|
subtype = Subtypes::InvalidVssEntry;
|
||
|
else if (isAuthenticated) {
|
||
|
subtype = Subtypes::AuthVssEntry;
|
||
|
info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(timestamp)
|
||
|
+ usprintf("\nPubKey index: %u", pubKeyIndex);
|
||
|
}
|
||
|
else if (isAppleCrc32) {
|
||
|
subtype = Subtypes::AppleVssEntry;
|
||
|
info += usprintf("\nData checksum: %08Xh", storedCrc32) +
|
||
|
(storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid"));
|
||
|
}
|
||
|
else
|
||
|
subtype = Subtypes::StandardVssEntry;
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Move to next variable
|
||
|
offset += variableSize;
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseFsysStoreBody(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data for the current item
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
const UByteArray 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;
|
||
|
|
||
|
// Get nameSize and name of the variable
|
||
|
const UINT8 nameSize = *(UINT8*)(data.constData() + offset);
|
||
|
// Check sanity
|
||
|
if (unparsedSize >= nameSize + sizeof(UINT8)) {
|
||
|
variableSize = nameSize + sizeof(UINT8);
|
||
|
}
|
||
|
|
||
|
UByteArray name;
|
||
|
if (variableSize) {
|
||
|
name = data.mid(offset + sizeof(UINT8), nameSize);
|
||
|
// Check for EOF variable
|
||
|
if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') {
|
||
|
// There is no data afterward, add EOF variable and free space and return
|
||
|
UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize);
|
||
|
UString info = usprintf("Full size: %Xh (%u)", header.size(), header.size());
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add EOF tree item
|
||
|
model->addItem(Types::FsysEntry, 0, UString("EOF"), UString(), info, header, UByteArray(), UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Add free space
|
||
|
offset += header.size();
|
||
|
unparsedSize = dataSize - offset;
|
||
|
UByteArray body = data.mid(offset);
|
||
|
info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add free space tree item
|
||
|
model->addItem(Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get dataSize and data of the variable
|
||
|
const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize);
|
||
|
if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) {
|
||
|
variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize;
|
||
|
}
|
||
|
else {
|
||
|
// Last variable is bad, add the rest as padding and return
|
||
|
UByteArray body = data.mid(offset);
|
||
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add free space tree item
|
||
|
model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Show message
|
||
|
msg(UString("parseFsysStoreBody: next variable appears too big, added as padding"), index);
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// Construct header and body
|
||
|
UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16));
|
||
|
UByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize);
|
||
|
|
||
|
// Add info
|
||
|
UString info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)",
|
||
|
variableSize, variableSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size());
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::FsysEntry, 0, UString(name.constData()), UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Move to next variable
|
||
|
offset += variableSize;
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
USTATUS NvramParser::parseEvsaStoreBody(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data for the current item
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
const UByteArray data = model->body(index);
|
||
|
|
||
|
// Check that the is enough space for entry header
|
||
|
const UINT32 dataSize = (UINT32)data.size();
|
||
|
UINT32 offset = 0;
|
||
|
|
||
|
std::map<UINT16, EFI_GUID> guidMap;
|
||
|
std::map<UINT16, UString> nameMap;
|
||
|
|
||
|
// Parse all entries
|
||
|
UINT32 unparsedSize = dataSize;
|
||
|
while (unparsedSize) {
|
||
|
UINT32 variableSize = 0;
|
||
|
UString name;
|
||
|
UString info;
|
||
|
UByteArray header;
|
||
|
UByteArray body;
|
||
|
UINT8 subtype;
|
||
|
UINT8 calculated;
|
||
|
|
||
|
const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset);
|
||
|
|
||
|
// Check entry size
|
||
|
variableSize = sizeof(EVSA_ENTRY_HEADER);
|
||
|
if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) {
|
||
|
UByteArray body = data.mid(offset);
|
||
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
||
|
|
||
|
// Checke type
|
||
|
UString name("Free space");
|
||
|
UINT8 type = Types::FreeSpace;
|
||
|
UINT8 subtype = 0;
|
||
|
if (getPaddingType(body) == Subtypes::DataPadding) {
|
||
|
name = UString("Padding");
|
||
|
type = Types::Padding;
|
||
|
subtype = Subtypes::DataPadding;
|
||
|
}
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add free space tree item
|
||
|
UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Show message
|
||
|
if (type == Types::Padding)
|
||
|
msg(UString("parseEvsaStoreBody: variable parsing failed, the rest of unparsed store added as padding"), itemIndex);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
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));
|
||
|
body = data.mid(offset + sizeof(EVSA_GUID_ENTRY), guidHeader->Header.Size - sizeof(EVSA_GUID_ENTRY));
|
||
|
EFI_GUID guid = *(EFI_GUID*)body.constData();
|
||
|
name = guidToUString(guid);
|
||
|
info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
||
|
variableSize, variableSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
guidHeader->Header.Type,
|
||
|
guidHeader->Header.Checksum)
|
||
|
+ (guidHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
||
|
+ usprintf("\nGuidId: %04Xh", guidHeader->GuidId);
|
||
|
subtype = Subtypes::GuidEvsaEntry;
|
||
|
guidMap.insert(std::pair<UINT16, EFI_GUID>(guidHeader->GuidId, guid));
|
||
|
}
|
||
|
// 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 = UString::fromUtf16((const CHAR16*)body.constData());
|
||
|
info = UString("GUID: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
||
|
variableSize, variableSize,
|
||
|
header.size(), header.size(),
|
||
|
body.size(), body.size(),
|
||
|
nameHeader->Header.Type,
|
||
|
nameHeader->Header.Checksum)
|
||
|
+ (nameHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
||
|
+ usprintf("\nVarId: %04Xh", nameHeader->VarId);
|
||
|
subtype = Subtypes::NameEvsaEntry;
|
||
|
nameMap.insert(std::pair<UINT16, UString>(nameHeader->VarId, name));
|
||
|
}
|
||
|
// Data entry
|
||
|
else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 ||
|
||
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 ||
|
||
|
entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
|
||
|
const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader;
|
||
|
// Check for extended header
|
||
|
UINT32 headerSize = sizeof(EVSA_DATA_ENTRY);
|
||
|
UINT32 dataSize = dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY);
|
||
|
if (dataHeader->Attributes & NVRAM_EVSA_DATA_EXTENDED_HEADER) {
|
||
|
const EVSA_DATA_ENTRY_EXTENDED* dataHeaderExtended = (const EVSA_DATA_ENTRY_EXTENDED*)entryHeader;
|
||
|
headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED);
|
||
|
dataSize = dataHeaderExtended->DataSize;
|
||
|
variableSize = headerSize + dataSize;
|
||
|
}
|
||
|
|
||
|
header = data.mid(offset, headerSize);
|
||
|
body = data.mid(offset + headerSize, dataSize);
|
||
|
name = UString("Data");
|
||
|
info = usprintf("Full size: %Xh (%u)\nHeader size %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
|
||
|
variableSize, variableSize,
|
||
|
headerSize, headerSize,
|
||
|
dataSize, dataSize,
|
||
|
dataHeader->Header.Type,
|
||
|
dataHeader->Header.Checksum)
|
||
|
+ (dataHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
|
||
|
+ usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (",
|
||
|
dataHeader->VarId,
|
||
|
dataHeader->GuidId,
|
||
|
dataHeader->Attributes)
|
||
|
+ evsaAttributesToUString(dataHeader->Attributes) + UString(")");
|
||
|
subtype = Subtypes::DataEvsaEntry;
|
||
|
}
|
||
|
// Unknown entry or free space
|
||
|
else {
|
||
|
UByteArray body = data.mid(offset);
|
||
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
||
|
|
||
|
// Check type
|
||
|
UString name("Free space");
|
||
|
UINT8 type = Types::FreeSpace;
|
||
|
UINT8 subtype = 0;
|
||
|
if (getPaddingType(body) == Subtypes::DataPadding) {
|
||
|
name = UString("Padding");
|
||
|
type = Types::Padding;
|
||
|
subtype = Subtypes::DataPadding;
|
||
|
}
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add free space tree item
|
||
|
UModelIndex itemIndex = model->addItem(type, subtype, name, UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Show message
|
||
|
if (type == Types::Padding)
|
||
|
msg(usprintf("parseEvsaStoreBody: unknown variable of type %02Xh found at offset %Xh, the rest of unparsed store added as padding",entryHeader->Type, offset), itemIndex);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::EvsaEntry, subtype, name, UString(), info, header, body, UByteArray(), false, parsingDataToUByteArray(pdata), 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++) {
|
||
|
UModelIndex current = index.child(i, 0);
|
||
|
if (model->subtype(current) == Subtypes::DataEvsaEntry) {
|
||
|
UByteArray header = model->header(current);
|
||
|
const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData();
|
||
|
UString guid;
|
||
|
if (guidMap.count(dataHeader->GuidId))
|
||
|
guid = guidToUString(guidMap[dataHeader->GuidId]);
|
||
|
UString 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, UString("Invalid"));
|
||
|
msg(UString("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, UString("Invalid"));
|
||
|
msg(UString("parseEvsaStoreBody: data variable with invalid GuidId"), current);
|
||
|
}
|
||
|
else if (name.isEmpty()) { // Name not found
|
||
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
||
|
model->setName(current, UString("Invalid"));
|
||
|
msg(UString("parseEvsaStoreBody: data variable with invalid VarId"), current);
|
||
|
}
|
||
|
else { // Variable is OK, rename it
|
||
|
if (dataHeader->Header.Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
|
||
|
model->setSubtype(current, Subtypes::InvalidEvsaEntry);
|
||
|
model->setName(current, UString("Invalid"));
|
||
|
}
|
||
|
else {
|
||
|
model->setName(current, guid);
|
||
|
}
|
||
|
model->setText(current, name);
|
||
|
model->addInfo(current, UString("GUID: ") + guid + UString("\nName: ") + name + UString("\n"), false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
USTATUS NvramParser::parseFlashMapBody(const UModelIndex & index)
|
||
|
{
|
||
|
// Sanity check
|
||
|
if (!index.isValid())
|
||
|
return U_INVALID_PARAMETER;
|
||
|
|
||
|
// Get parsing data for the current item
|
||
|
PARSING_DATA pdata = parsingDataFromUModelIndex(index);
|
||
|
UINT32 parentOffset = pdata.offset + model->header(index).size();
|
||
|
const UByteArray data = model->body(index);
|
||
|
|
||
|
|
||
|
const UINT32 dataSize = (UINT32)data.size();
|
||
|
UINT32 offset = 0;
|
||
|
UINT32 unparsedSize = dataSize;
|
||
|
// Parse all entries
|
||
|
while (unparsedSize) {
|
||
|
const PHOENIX_FLASH_MAP_ENTRY* entryHeader = (const PHOENIX_FLASH_MAP_ENTRY*)(data.constData() + offset);
|
||
|
|
||
|
// Check entry size
|
||
|
if (unparsedSize < sizeof(PHOENIX_FLASH_MAP_ENTRY)) {
|
||
|
// Last variable is bad, add the rest as padding and return
|
||
|
UByteArray body = data.mid(offset);
|
||
|
UString info = usprintf("Full size: %Xh (%u)", body.size(), body.size());
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Add free space tree item
|
||
|
model->addItem(Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), false, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Show message
|
||
|
if (unparsedSize < entryHeader->Size)
|
||
|
msg(UString("parseFlashMapBody: next entry appears too big, added as padding"), index);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
UString name = guidToUString(entryHeader->Guid);
|
||
|
|
||
|
// Construct header
|
||
|
UByteArray header = data.mid(offset, sizeof(PHOENIX_FLASH_MAP_ENTRY));
|
||
|
|
||
|
// Add info
|
||
|
UString info = UString("Entry GUID: ") + name + usprintf("\nFull size: 24h (36)\nHeader size: 24h (36)\nBody size: 0h (0)\n"
|
||
|
"Entry type: %04Xh\nData type: %04Xh\nMemory address: %08Xh\nSize: %08Xh\nOffset: %08Xh",
|
||
|
entryHeader->EntryType,
|
||
|
entryHeader->DataType,
|
||
|
entryHeader->PhysicalAddress,
|
||
|
entryHeader->Size,
|
||
|
entryHeader->Offset);
|
||
|
|
||
|
// Add correct offset to parsing data
|
||
|
pdata.offset = parentOffset + offset;
|
||
|
|
||
|
// Determine subtype
|
||
|
UINT8 subtype = 0;
|
||
|
switch (entryHeader->DataType) {
|
||
|
case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_VOLUME:
|
||
|
subtype = Subtypes::VolumeFlashMapEntry;
|
||
|
break;
|
||
|
case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_DATA_BLOCK:
|
||
|
subtype = Subtypes::DataFlashMapEntry;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Add tree item
|
||
|
model->addItem(Types::FlashMapEntry, subtype, name, flashMapGuidToUString(entryHeader->Guid), info, header, UByteArray(), UByteArray(), true, parsingDataToUByteArray(pdata), index);
|
||
|
|
||
|
// Move to next variable
|
||
|
offset += sizeof(PHOENIX_FLASH_MAP_ENTRY);
|
||
|
unparsedSize = dataSize - offset;
|
||
|
}
|
||
|
|
||
|
return U_SUCCESS;
|
||
|
}
|
||
|
#endif // U_ENABLE_NVRAM_PARSING_SUPPORT
|