1 /* nvramparser.cpp
2 
3 Copyright (c) 2016, Nikolaj Schlej. All rights reserved.
4 
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 */
14 
15 //TODO: relax fixed restrictions once NVRAM builder is ready
16 
17 #include <map>
18 
19 #include "nvramparser.h"
20 #include "parsingdata.h"
21 #include "utility.h"
22 #include "nvram.h"
23 #include "ffs.h"
24 #include "fit.h"
25 #include "uinttypes.h"
26 
27 #ifdef U_ENABLE_NVRAM_PARSING_SUPPORT
parseNvarStore(const UModelIndex & index)28 USTATUS NvramParser::parseNvarStore(const UModelIndex & index)
29 {
30     // Sanity check
31     if (!index.isValid())
32         return U_INVALID_PARAMETER;
33 
34     // Obtain required information from parent file
35     UINT8 emptyByte = 0xFF;
36     UModelIndex parentFileIndex = model->findParentOfType(index, Types::File);
37     if (parentFileIndex.isValid() && model->hasEmptyParsingData(parentFileIndex) == false) {
38         UByteArray data = model->parsingData(parentFileIndex);
39         const FILE_PARSING_DATA* pdata = (const FILE_PARSING_DATA*)data.constData();
40         emptyByte = readUnaligned(pdata).emptyByte;
41     }
42 
43     // Get local offset
44     UINT32 localOffset = (UINT32)model->header(index).size();
45 
46     // Get item data
47     const UByteArray data = model->body(index);
48 
49     // Parse all entries
50     UINT32 offset = 0;
51     UINT32 guidsInStore = 0;
52     while (1) {
53         bool msgUnknownExtDataFormat = false;
54         bool msgExtHeaderTooLong = false;
55         bool msgExtDataTooShort = false;
56 
57         bool isInvalid = false;
58         bool isInvalidLink = false;
59         bool hasExtendedHeader = false;
60         bool hasChecksum = false;
61         bool hasTimestamp = false;
62         bool hasHash = false;
63         bool hasGuidIndex = false;
64 
65         UINT32 guidIndex = 0;
66         UINT8  storedChecksum = 0;
67         UINT8  calculatedChecksum = 0;
68         UINT32 extendedHeaderSize = 0;
69         UINT8  extendedAttributes = 0;
70         UINT64 timestamp = 0;
71         UByteArray hash;
72 
73         UINT8 subtype = Subtypes::FullNvarEntry;
74         UString name;
75         UString guid;
76         UString text;
77         UByteArray header;
78         UByteArray body;
79         UByteArray tail;
80 
81         UINT32 guidAreaSize = guidsInStore * sizeof(EFI_GUID);
82         UINT32 unparsedSize = (UINT32)data.size() - offset - guidAreaSize;
83 
84         // Get entry header
85         const NVAR_ENTRY_HEADER* entryHeader = (const NVAR_ENTRY_HEADER*)(data.constData() + offset);
86 
87         // Check header size and signature
88         if (unparsedSize < sizeof(NVAR_ENTRY_HEADER) ||
89             entryHeader->Signature != NVRAM_NVAR_ENTRY_SIGNATURE ||
90             unparsedSize < entryHeader->Size) {
91             // Check if the data left is a free space or a padding
92             UByteArray padding = data.mid(offset, unparsedSize);
93 
94             // Get info
95             UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
96 
97             if ((UINT32)padding.count(emptyByte) == unparsedSize) { // Free space
98                 // Add tree item
99                 model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
100             }
101             else {
102                 // Nothing is parsed yet, but the file is not empty
103                 if (!offset) {
104                     msg(usprintf("%s: file can't be parsed as NVAR variables store", __FUNCTION__), index);
105                     return U_SUCCESS;
106                 }
107 
108                 // Add tree item
109                 model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
110             }
111 
112             // Add GUID store area
113             UByteArray guidArea = data.right(guidAreaSize);
114             // Get info
115             name = UString("GUID store");
116             info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")\nGUIDs in store: %u",
117                 guidArea.size(), guidArea.size(),
118                 guidsInStore);
119             // Add tree item
120             model->addItem((UINT32)(localOffset + offset + padding.size()), Types::Padding, getPaddingType(guidArea), name, UString(), info, UByteArray(), guidArea, UByteArray(), Fixed, index);
121 
122             return U_SUCCESS;
123         }
124 
125         // Contruct generic header and body
126         header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER));
127         body = data.mid(offset + sizeof(NVAR_ENTRY_HEADER), entryHeader->Size - sizeof(NVAR_ENTRY_HEADER));
128 
129         UINT32 lastVariableFlag = emptyByte ? 0xFFFFFF : 0;
130 
131         // Set default next to predefined last value
132         NVAR_ENTRY_PARSING_DATA pdata;
133         pdata.emptyByte = emptyByte;
134         pdata.next = lastVariableFlag;
135 
136         // Entry is marked as invalid
137         if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_VALID) == 0) { // Valid attribute is not set
138             isInvalid = true;
139             // Do not parse further
140             goto parsing_done;
141         }
142 
143         // Add next node information to parsing data
144         if (entryHeader->Next != lastVariableFlag) {
145             subtype = Subtypes::LinkNvarEntry;
146             pdata.next = entryHeader->Next;
147         }
148 
149         // Entry with extended header
150         if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_EXT_HEADER) {
151             hasExtendedHeader = true;
152             msgUnknownExtDataFormat = true;
153 
154             extendedHeaderSize = readUnaligned((UINT16*)(body.constData() + body.size() - sizeof(UINT16)));
155             if (extendedHeaderSize > (UINT32)body.size()) {
156                 msgExtHeaderTooLong = true;
157                 isInvalid = true;
158                 // Do not parse further
159                 goto parsing_done;
160             }
161 
162             extendedAttributes = *(UINT8*)(body.constData() + body.size() - extendedHeaderSize);
163 
164             // Variable with checksum
165             if (extendedAttributes & NVRAM_NVAR_ENTRY_EXT_CHECKSUM) {
166                 // Get stored checksum
167                 storedChecksum = *(UINT8*)(body.constData() + body.size() - sizeof(UINT16) - sizeof(UINT8));
168 
169                 // Recalculate checksum for the variable
170                 calculatedChecksum = 0;
171                 // Include entry data
172                 UINT8* start = (UINT8*)(entryHeader + 1);
173                 for (UINT8* p = start; p < start + entryHeader->Size - sizeof(NVAR_ENTRY_HEADER); p++) {
174                     calculatedChecksum += *p;
175                 }
176                 // Include entry size and flags
177                 start = (UINT8*)&entryHeader->Size;
178                 for (UINT8*p = start; p < start + sizeof(UINT16); p++) {
179                     calculatedChecksum += *p;
180                 }
181                 // Include entry attributes
182                 calculatedChecksum += entryHeader->Attributes;
183 
184                 hasChecksum = true;
185                 msgUnknownExtDataFormat = false;
186             }
187 
188             tail = body.mid(body.size() - extendedHeaderSize);
189             body = body.left(body.size() - extendedHeaderSize);
190 
191             // Entry with authenticated write (for SecureBoot)
192             if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_AUTH_WRITE) {
193                 if ((entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY)) {// Data only auth. variables has no hash
194                     if ((UINT32)tail.size() < sizeof(UINT64)) {
195                         msgExtDataTooShort = true;
196                         isInvalid = true;
197                         // Do not parse further
198                         goto parsing_done;
199                     }
200 
201                     timestamp = readUnaligned(tail.constData() + sizeof(UINT8));
202                     hasTimestamp = true;
203                     msgUnknownExtDataFormat = false;
204                 }
205                 else { // Full or link variable have hash
206                     if ((UINT32)tail.size() < sizeof(UINT64) + SHA256_HASH_SIZE) {
207                         msgExtDataTooShort = true;
208                         isInvalid = true;
209                         // Do not parse further
210                         goto parsing_done;
211                     }
212 
213                     timestamp = readUnaligned((UINT64*)(tail.constData() + sizeof(UINT8)));
214                     hash = tail.mid(sizeof(UINT64) + sizeof(UINT8), SHA256_HASH_SIZE);
215                     hasTimestamp = true;
216                     hasHash = true;
217                     msgUnknownExtDataFormat = false;
218                 }
219             }
220         }
221 
222         // Entry is data-only (nameless and GUIDless entry or link)
223         if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_DATA_ONLY) { // Data-only attribute is set
224             isInvalidLink = true;
225             UModelIndex nvarIndex;
226             // Search previously added entries for a link to this variable
227             // WARNING: O(n^2), may be very slow
228             for (int i = model->rowCount(index) - 1; i >= 0; i--) {
229 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
230                 nvarIndex = index.child(i, 0);
231 #else
232                 nvarIndex = index.model()->index(i, 0, index);
233 #endif
234 
235                 if (model->hasEmptyParsingData(nvarIndex) == false) {
236                     UByteArray nvarData = model->parsingData(nvarIndex);
237                     const NVAR_ENTRY_PARSING_DATA nvarPdata = readUnaligned((const NVAR_ENTRY_PARSING_DATA*)nvarData.constData());
238                     if (nvarPdata.isValid && nvarPdata.next + model->offset(nvarIndex) - localOffset == offset) { // Previous link is present and valid
239                         isInvalidLink = false;
240                         break;
241                     }
242                 }
243             }
244             // Check if the link is valid
245             if (!isInvalidLink) {
246                 // Use the name and text of the previous link
247                 name = model->name(nvarIndex);
248                 text = model->text(nvarIndex);
249 
250                 if (entryHeader->Next == lastVariableFlag)
251                     subtype = Subtypes::DataNvarEntry;
252             }
253 
254             // Do not parse further
255             goto parsing_done;
256         }
257 
258         // Get entry name
259         {
260             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
261             CHAR8* namePtr = (CHAR8*)(entryHeader + 1) + nameOffset;
262             UINT32 nameSize = 0;
263             if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_ASCII_NAME) { // Name is stored as ASCII string of CHAR8s
264                 text = UString(namePtr);
265                 nameSize = (UINT32)(text.length() + 1);
266             }
267             else { // Name is stored as UCS2 string of CHAR16s
268 #if QT_VERSION_MAJOR >= 6
269                 text = UString::fromUtf16((char16_t*)namePtr);
270 #else
271                 text = UString::fromUtf16((CHAR16*)namePtr);
272 #endif
273 
274                 nameSize = (UINT32)((text.length() + 1) * 2);
275             }
276 
277             // Get entry GUID
278             if (entryHeader->Attributes & NVRAM_NVAR_ENTRY_GUID) { // GUID is strored in the variable itself
279                 name = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)));
280                 guid = guidToUString(readUnaligned((EFI_GUID*)(entryHeader + 1)), false);
281             }
282             // GUID is stored in GUID list at the end of the store
283             else {
284                 guidIndex = *(UINT8*)(entryHeader + 1);
285                 if (guidsInStore < guidIndex + 1)
286                     guidsInStore = guidIndex + 1;
287 
288                 // The list begins at the end of the store and goes backwards
289                 const EFI_GUID* guidPtr = (const EFI_GUID*)(data.constData() + data.size()) - 1 - guidIndex;
290                 name = guidToUString(readUnaligned(guidPtr));
291                 guid = guidToUString(readUnaligned(guidPtr), false);
292                 hasGuidIndex = true;
293             }
294 
295             // Include name and GUID into the header and remove them from body
296             header = data.mid(offset, sizeof(NVAR_ENTRY_HEADER) + nameOffset + nameSize);
297             body = body.mid(nameOffset + nameSize);
298         }
299     parsing_done:
300         UString info;
301 
302         // Rename invalid entries according to their types
303         pdata.isValid = TRUE;
304         if (isInvalid) {
305             name = UString("Invalid");
306             subtype = Subtypes::InvalidNvarEntry;
307             pdata.isValid = FALSE;
308         }
309         else if (isInvalidLink) {
310             name = UString("Invalid link");
311             subtype = Subtypes::InvalidLinkNvarEntry;
312             pdata.isValid = FALSE;
313         }
314         else // Add GUID info for valid entries
315             info += UString("Variable GUID: ") + guid + UString("\n");
316 
317         // Add GUID index information
318         if (hasGuidIndex)
319             info += usprintf("GUID index: %u\n", guidIndex);
320 
321         // Add header, body and extended data info
322         info += usprintf("Full size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")",
323             entryHeader->Size, entryHeader->Size,
324             header.size(), header.size(),
325             body.size(), body.size());
326 
327         // Add attributes info
328         info += usprintf("\nAttributes: %02Xh", entryHeader->Attributes);
329         // Translate attributes to text
330         if (entryHeader->Attributes && entryHeader->Attributes != 0xFF)
331             info += UString(" (") + nvarAttributesToUString(entryHeader->Attributes) + UString(")");
332 
333         // Add next node info
334         if (!isInvalid && entryHeader->Next != lastVariableFlag)
335             info += usprintf("\nNext node at offset: %Xh", localOffset + offset + entryHeader->Next);
336 
337         // Add extended header info
338         if (hasExtendedHeader) {
339             info += usprintf("\nExtended header size: %Xh (%u)\nExtended attributes: %Xh (",
340                 extendedHeaderSize, extendedHeaderSize,
341                 extendedAttributes) + nvarExtendedAttributesToUString(extendedAttributes) + UString(")");
342 
343             // Add checksum
344             if (hasChecksum)
345                 info += usprintf("\nChecksum: %02Xh", storedChecksum) +
346                 (calculatedChecksum ? usprintf(", invalid, should be %02Xh", 0x100 - calculatedChecksum) : UString(", valid"));
347 
348             // Add timestamp
349             if (hasTimestamp)
350                 info += usprintf("\nTimestamp: %" PRIX64 "h", timestamp);
351 
352             // Add hash
353             if (hasHash)
354                 info += UString("\nHash: ") + UString(hash.toHex().constData());
355         }
356 
357         // Add tree item
358         UModelIndex varIndex = model->addItem(localOffset + offset, Types::NvarEntry, subtype, name, text, info, header, body, tail, Fixed, index);
359 
360         // Set parsing data for created entry
361         model->setParsingData(varIndex, UByteArray((const char*)&pdata, sizeof(pdata)));
362 
363         // Show messages
364         if (msgUnknownExtDataFormat) msg(usprintf("%s: unknown extended data format", __FUNCTION__), varIndex);
365         if (msgExtHeaderTooLong)     msg(usprintf("%s: extended header size (%Xh) is greater than body size (%" PRIXQ "h)", __FUNCTION__,
366             extendedHeaderSize, body.size()), varIndex);
367         if (msgExtDataTooShort)      msg(usprintf("%s: extended header size (%" PRIXQ "h) is too small for timestamp and hash", __FUNCTION__,
368             tail.size()), varIndex);
369 
370         // Try parsing the entry data as NVAR storage if it begins with NVAR signature
371         if ((subtype == Subtypes::DataNvarEntry || subtype == Subtypes::FullNvarEntry)
372             && body.size() >= 4 && readUnaligned((const UINT32*)body.constData()) == NVRAM_NVAR_ENTRY_SIGNATURE)
373             parseNvarStore(varIndex);
374 
375         // Move to next exntry
376         offset += entryHeader->Size;
377     }
378 
379     return U_SUCCESS;
380 }
381 
parseNvramVolumeBody(const UModelIndex & index)382 USTATUS NvramParser::parseNvramVolumeBody(const UModelIndex & index)
383 {
384     // Sanity check
385     if (!index.isValid())
386         return U_INVALID_PARAMETER;
387 
388     // Obtain required fields from parsing data
389     UINT8 emptyByte = 0xFF;
390     if (model->hasEmptyParsingData(index) == false) {
391         UByteArray data = model->parsingData(index);
392         const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
393         emptyByte = pdata->emptyByte;
394     }
395 
396     // Get local offset
397     UINT32 localOffset = (UINT32)model->header(index).size();
398 
399     // Get item data
400     UByteArray data = model->body(index);
401 
402     // Search for first store
403     USTATUS result;
404     UINT32 prevStoreOffset;
405     result = findNextStore(index, data, localOffset, 0, prevStoreOffset);
406     if (result)
407         return result;
408 
409     // First store is not at the beginning of volume body
410     UString name;
411     UString info;
412     if (prevStoreOffset > 0) {
413         // Get info
414         UByteArray padding = data.left(prevStoreOffset);
415         name = UString("Padding");
416         info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
417 
418         // Add tree item
419         model->addItem(localOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
420     }
421 
422     // Search for and parse all stores
423     UINT32 storeOffset = prevStoreOffset;
424     UINT32 prevStoreSize = 0;
425 
426     while (!result) {
427         // Padding between stores
428         if (storeOffset > prevStoreOffset + prevStoreSize) {
429             UINT32 paddingOffset = prevStoreOffset + prevStoreSize;
430             UINT32 paddingSize = storeOffset - paddingOffset;
431             UByteArray padding = data.mid(paddingOffset, paddingSize);
432 
433             // Get info
434             name = UString("Padding");
435             info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
436 
437             // Add tree item
438             model->addItem(localOffset + paddingOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
439         }
440 
441         // Get store size
442         UINT32 storeSize = 0;
443         result = getStoreSize(data, storeOffset, storeSize);
444         if (result) {
445             msg(usprintf("%s: getStoreSize failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
446             return result;
447         }
448 
449         // Check that current store is fully present in input
450         if (storeSize > (UINT32)data.size() || storeOffset + storeSize > (UINT32)data.size()) {
451             // Mark the rest as padding and finish parsing
452             UByteArray padding = data.mid(storeOffset);
453 
454             // Get info
455             name = UString("Padding");
456             info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
457 
458             // Add tree item
459             UModelIndex paddingIndex = model->addItem(localOffset + storeOffset, Types::Padding, getPaddingType(padding), name, UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
460             msg(usprintf("%s: one of stores inside overlaps the end of data", __FUNCTION__), paddingIndex);
461 
462             // Update variables
463             prevStoreOffset = storeOffset;
464             prevStoreSize = (UINT32)padding.size();
465             break;
466         }
467 
468         // Parse current store header
469         UModelIndex storeIndex;
470         UByteArray store = data.mid(storeOffset, storeSize);
471         result = parseStoreHeader(store, localOffset + storeOffset, index, storeIndex);
472         if (result)
473             msg(usprintf("%s: store header parsing failed with error ", __FUNCTION__) + errorCodeToUString(result), index);
474 
475         // Go to next store
476         prevStoreOffset = storeOffset;
477         prevStoreSize = storeSize;
478         result = findNextStore(index, data, localOffset, storeOffset + prevStoreSize, storeOffset);
479     }
480 
481     // Padding/free space at the end
482     storeOffset = prevStoreOffset + prevStoreSize;
483     if ((UINT32)data.size() > storeOffset) {
484         UByteArray padding = data.mid(storeOffset);
485         // Add info
486         info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
487 
488         if (padding.count(emptyByte) == padding.size()) { // Free space
489             // Add tree item
490             model->addItem(localOffset + storeOffset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
491         }
492         else {
493             // Nothing is parsed yet, but the file is not empty
494             if (!storeOffset) {
495                 msg(usprintf("%s: can't be parsed as NVRAM volume", __FUNCTION__), index);
496                 return U_SUCCESS;
497             }
498 
499             // Add tree item
500             model->addItem(localOffset + storeOffset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
501         }
502     }
503 
504     // Parse bodies
505     for (int i = 0; i < model->rowCount(index); i++) {
506 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
507         UModelIndex current = index.child(i, 0);
508 #else
509         UModelIndex current = index.model()->index(i, 0, index);
510 #endif
511 
512         switch (model->type(current)) {
513         case Types::FdcStore:
514             parseFdcStoreBody(current);
515             break;
516         case Types::VssStore:
517             parseVssStoreBody(current, 0);
518             break;
519         case Types::Vss2Store:
520             parseVssStoreBody(current, 4);
521             break;
522         case Types::FsysStore:
523             parseFsysStoreBody(current);
524             break;
525         case Types::EvsaStore:
526             parseEvsaStoreBody(current);
527             break;
528         case Types::FlashMapStore:
529             parseFlashMapBody(current);
530             break;
531         default:
532             // Ignore unknown!
533             break;
534         }
535     }
536 
537     return U_SUCCESS;
538 }
539 
findNextStore(const UModelIndex & index,const UByteArray & volume,const UINT32 localOffset,const UINT32 storeOffset,UINT32 & nextStoreOffset)540 USTATUS NvramParser::findNextStore(const UModelIndex & index, const UByteArray & volume, const UINT32 localOffset, const UINT32 storeOffset, UINT32 & nextStoreOffset)
541 {
542     UINT32 dataSize = (UINT32)volume.size();
543 
544     if (dataSize < sizeof(UINT32))
545         return U_STORES_NOT_FOUND;
546 
547     // TODO: add checks for restSize
548     UINT32 offset = storeOffset;
549     for (; offset < dataSize - sizeof(UINT32); offset++) {
550         const UINT32* currentPos = (const UINT32*)(volume.constData() + offset);
551         if (*currentPos == NVRAM_VSS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_SVS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_NSS_STORE_SIGNATURE) { // $VSS, $SVS or $NSS signatures found, perform checks
552             const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)currentPos;
553             if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) {
554                 msg(usprintf("%s: VSS store candidate at offset %Xh skipped, has invalid format %02Xh", __FUNCTION__, localOffset + offset, vssHeader->Format), index);
555                 continue;
556             }
557             if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
558                 msg(usprintf("%s: VSS store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, vssHeader->Size), index);
559                 continue;
560             }
561             // All checks passed, store found
562             break;
563         }
564         else if (*currentPos == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *currentPos == NVRAM_VSS2_STORE_GUID_PART1) { // VSS2 store signatures found, perform checks
565             UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID));
566             if (guid != NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID && guid != NVRAM_VSS2_STORE_GUID) // Check the whole signature
567                 continue;
568 
569             const VSS2_VARIABLE_STORE_HEADER* vssHeader = (const VSS2_VARIABLE_STORE_HEADER*)currentPos;
570             if (vssHeader->Format != NVRAM_VSS_VARIABLE_STORE_FORMATTED) {
571                 msg(usprintf("%s: VSS2 store candidate at offset %Xh skipped, has invalid format %02Xh", __FUNCTION__, localOffset + offset, vssHeader->Format), index);
572                 continue;
573             }
574             if (vssHeader->Size == 0 || vssHeader->Size == 0xFFFFFFFF) {
575                 msg(usprintf("%s: VSS2 store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, vssHeader->Size), index);
576                 continue;
577             }
578             // All checks passed, store found
579             break;
580         }
581         else if (*currentPos == NVRAM_FDC_VOLUME_SIGNATURE) { // FDC signature found
582             const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)currentPos;
583             if (fdcHeader->Size == 0 || fdcHeader->Size == 0xFFFFFFFF) {
584                 msg(usprintf("%s: FDC store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, fdcHeader->Size), index);
585                 continue;
586             }
587             // All checks passed, store found
588             break;
589         }
590         else if (*currentPos == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *currentPos == NVRAM_APPLE_GAID_STORE_SIGNATURE) { // Fsys or Gaid signature found
591             const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)currentPos;
592             if (fsysHeader->Size == 0 || fsysHeader->Size == 0xFFFF) {
593                 msg(usprintf("%s: Fsys store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, fsysHeader->Size), index);
594                 continue;
595             }
596             // All checks passed, store found
597             break;
598         }
599         else if (*currentPos == NVRAM_EVSA_STORE_SIGNATURE) { //EVSA signature found
600             if (offset < sizeof(UINT32))
601                 continue;
602 
603             const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)(currentPos - 1);
604             if (evsaHeader->Header.Type != NVRAM_EVSA_ENTRY_TYPE_STORE) {
605                 msg(usprintf("%s: EVSA store candidate at offset %Xh skipped, has invalid type %02Xh", __FUNCTION__, localOffset + offset - 4, evsaHeader->Header.Type), index);
606                 continue;
607             }
608             if (evsaHeader->StoreSize == 0 || evsaHeader->StoreSize == 0xFFFFFFFF) {
609                 msg(usprintf("%s: EVSA store candidate at offset %Xh skipped, has invalid size %Xh", __FUNCTION__, localOffset + offset, evsaHeader->StoreSize), index);
610                 continue;
611             }
612             // All checks passed, store found
613             offset -= sizeof(UINT32);
614             break;
615         }
616         else if (*currentPos == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *currentPos == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) { // Possible FTW block signature found
617             UByteArray guid = UByteArray(volume.constData() + offset, sizeof(EFI_GUID));
618             if (guid != NVRAM_MAIN_STORE_VOLUME_GUID && guid != EDKII_WORKING_BLOCK_SIGNATURE_GUID && guid != VSS2_WORKING_BLOCK_SIGNATURE_GUID) // Check the whole signature
619                 continue;
620 
621             // Detect header variant based on WriteQueueSize
622             const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)currentPos;
623             if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
624                 if (ftwHeader->WriteQueueSize == 0 || ftwHeader->WriteQueueSize == 0xFFFFFFFF) {
625                     msg(usprintf("%s: FTW block candidate at offset %Xh skipped, has invalid body size %Xh", __FUNCTION__, localOffset + offset, ftwHeader->WriteQueueSize), index);
626                     continue;
627                 }
628             }
629             else if (ftwHeader->WriteQueueSize % 0x10 == 0x00) { // Header with 64 bit WriteQueueSize
630                 const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)currentPos;
631                 if (ftw64Header->WriteQueueSize == 0 || ftw64Header->WriteQueueSize >= 0xFFFFFFFF) {
632                     msg(usprintf("%s: FTW block candidate at offset %Xh skipped, has invalid body size %llXh", __FUNCTION__, localOffset + offset, (unsigned long long)ftw64Header->WriteQueueSize), index);
633                     continue;
634                 }
635             }
636             else // Unknown header
637                 continue;
638 
639             // All checks passed, store found
640             break;
641         }
642         else if (*currentPos == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) {// Phoenix SCT flash map
643             UByteArray signature = UByteArray(volume.constData() + offset, NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_LENGTH);
644             if (signature != NVRAM_PHOENIX_FLASH_MAP_SIGNATURE) // Check the whole signature
645                 continue;
646 
647             // All checks passed, store found
648             break;
649         }
650         else if (*currentPos == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
651             const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)currentPos;
652 
653             // Check size
654             if (cmdbHeader->HeaderSize != sizeof(PHOENIX_CMDB_HEADER))
655                 continue;
656 
657             // All checks passed, store found
658             break;
659         }
660         else if (*currentPos == INTEL_MICROCODE_HEADER_VERSION_1) {// Intel microcode
661             const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)currentPos;
662 
663             // TotalSize is greater then DataSize and is multiple of 1024
664             if (FALSE == ffsParser->microcodeHeaderValid(ucodeHeader)) {
665                 continue;
666             }
667 
668             // All checks passed, store found
669             break;
670         }
671         else if (*currentPos == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
672             if (offset < 4 * sizeof(UINT32))
673                 continue;
674 
675             const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)(currentPos - 4);
676             // Check type
677             if (pubkeyHeader->Type != OEM_ACTIVATION_PUBKEY_TYPE)
678                 continue;
679 
680             // All checks passed, store found
681             offset -= 4 * sizeof(UINT32);
682             break;
683         }
684         else if (*currentPos == OEM_ACTIVATION_MARKER_WINDOWS_FLAG_PART1) { // SLIC marker
685             if (offset >= dataSize - sizeof(UINT64) ||
686                 *(const UINT64*)currentPos != OEM_ACTIVATION_MARKER_WINDOWS_FLAG ||
687                 offset < 26) // Check full windows flag and structure size
688                 continue;
689 
690             const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)(volume.constData() + offset - 26);
691             // Check reserved bytes
692             bool reservedBytesValid = true;
693             for (UINT32 i = 0; i < sizeof(markerHeader->Reserved); i++)
694                 if (markerHeader->Reserved[i] != OEM_ACTIVATION_MARKER_RESERVED_BYTE) {
695                     reservedBytesValid = false;
696                     break;
697                 }
698             if (!reservedBytesValid)
699                 continue;
700 
701             // All checks passed, store found
702             offset -= 26;
703             break;
704         }
705     }
706     // No more stores found
707     if (offset >= dataSize - sizeof(UINT32))
708         return U_STORES_NOT_FOUND;
709 
710     nextStoreOffset = offset;
711 
712     return U_SUCCESS;
713 }
714 
getStoreSize(const UByteArray & data,const UINT32 storeOffset,UINT32 & storeSize)715 USTATUS NvramParser::getStoreSize(const UByteArray & data, const UINT32 storeOffset, UINT32 & storeSize)
716 {
717     const UINT32* signature = (const UINT32*)(data.constData() + storeOffset);
718     if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE || *signature == NVRAM_APPLE_NSS_STORE_SIGNATURE) {
719         const VSS_VARIABLE_STORE_HEADER* vssHeader = (const VSS_VARIABLE_STORE_HEADER*)signature;
720         storeSize = vssHeader->Size;
721     }
722     else if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1) {
723         const VSS2_VARIABLE_STORE_HEADER* vssHeader = (const VSS2_VARIABLE_STORE_HEADER*)signature;
724         storeSize = vssHeader->Size;
725     }
726     else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE) {
727         const FDC_VOLUME_HEADER* fdcHeader = (const FDC_VOLUME_HEADER*)signature;
728         storeSize = fdcHeader->Size;
729     }
730     else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE) {
731         const APPLE_FSYS_STORE_HEADER* fsysHeader = (const APPLE_FSYS_STORE_HEADER*)signature;
732         storeSize = fsysHeader->Size;
733     }
734     else if (*(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE) {
735         const EVSA_STORE_ENTRY* evsaHeader = (const EVSA_STORE_ENTRY*)signature;
736         storeSize = evsaHeader->StoreSize;
737     }
738     else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1) {
739         const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftwHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)signature;
740         if (ftwHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
741             storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftwHeader->WriteQueueSize;
742         }
743         else { //  Header with 64 bit WriteQueueSize
744             const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64Header = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)signature;
745             storeSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64Header->WriteQueueSize;
746         }
747     }
748     else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1) { // Phoenix SCT flash map
749         const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)signature;
750         storeSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + sizeof(PHOENIX_FLASH_MAP_ENTRY) * flashMapHeader->NumEntries;
751     }
752     else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE) { // Phoenix SCT CMDB store
753         storeSize = NVRAM_PHOENIX_CMDB_SIZE; // It's a predefined max size, no need to calculate
754     }
755     else if (*(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC) { // SLIC pubkey
756         const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)signature;
757         storeSize = pubkeyHeader->Size;
758     }
759     else if (*(const UINT64*)(data.constData() + storeOffset + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG) { // SLIC marker
760         const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)signature;
761         storeSize = markerHeader->Size;
762     }
763     else if (*signature == INTEL_MICROCODE_HEADER_VERSION_1) { // Intel microcode, must be checked after SLIC marker because of the same *signature values
764         const INTEL_MICROCODE_HEADER* ucodeHeader = (const INTEL_MICROCODE_HEADER*)signature;
765         storeSize = ucodeHeader->TotalSize;
766     } else {
767         return U_INVALID_PARAMETER; // Unreachable
768     }
769     return U_SUCCESS;
770 }
771 
parseVssStoreHeader(const UByteArray & store,const UINT32 localOffset,const bool sizeOverride,const UModelIndex & parent,UModelIndex & index)772 USTATUS NvramParser::parseVssStoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index)
773 {
774     const UINT32 dataSize = (UINT32)store.size();
775 
776     // Check store size
777     if (dataSize < sizeof(VSS_VARIABLE_STORE_HEADER)) {
778         msg(usprintf("%s: volume body is too small even for VSS store header", __FUNCTION__), parent);
779         return U_SUCCESS;
780     }
781 
782     // Get VSS store header
783     const VSS_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS_VARIABLE_STORE_HEADER*)store.constData();
784 
785     // Check for size override
786     UINT32 storeSize = vssStoreHeader->Size;
787     if (sizeOverride) {
788         storeSize = dataSize;
789     }
790 
791     // Check store size
792     if (dataSize < storeSize) {
793         msg(usprintf("%s: VSS store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
794             storeSize, storeSize,
795             dataSize, dataSize), parent);
796         return U_SUCCESS;
797     }
798 
799     // Construct header and body
800     UByteArray header = store.left(sizeof(VSS_VARIABLE_STORE_HEADER));
801     UByteArray body = store.mid(sizeof(VSS_VARIABLE_STORE_HEADER), storeSize - sizeof(VSS_VARIABLE_STORE_HEADER));
802 
803     // Add info
804     UString name;
805     if (vssStoreHeader->Signature == NVRAM_APPLE_SVS_STORE_SIGNATURE) {
806         name = UString("SVS store");
807     }
808     else if (vssStoreHeader->Signature == NVRAM_APPLE_NSS_STORE_SIGNATURE) {
809         name = UString("NSS store");
810     }
811     else {
812         name = UString("VSS store");
813     }
814 
815     UString info = usprintf("Signature: %Xh\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh",
816         vssStoreHeader->Signature,
817         storeSize, storeSize,
818         header.size(), header.size(),
819         body.size(), body.size(),
820         vssStoreHeader->Format,
821         vssStoreHeader->State,
822         vssStoreHeader->Unknown);
823 
824     // Add tree item
825     index = model->addItem(localOffset, Types::VssStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
826 
827     return U_SUCCESS;
828 }
829 
parseVss2StoreHeader(const UByteArray & store,const UINT32 localOffset,const bool sizeOverride,const UModelIndex & parent,UModelIndex & index)830 USTATUS NvramParser::parseVss2StoreHeader(const UByteArray & store, const UINT32 localOffset, const bool sizeOverride, const UModelIndex & parent, UModelIndex & index)
831 {
832     const UINT32 dataSize = (UINT32)store.size();
833 
834     // Check store size
835     if (dataSize < sizeof(VSS2_VARIABLE_STORE_HEADER)) {
836         msg(usprintf("%s: volume body is too small even for VSS2 store header", __FUNCTION__), parent);
837         return U_SUCCESS;
838     }
839 
840     // Get VSS2 store header
841     const VSS2_VARIABLE_STORE_HEADER* vssStoreHeader = (const VSS2_VARIABLE_STORE_HEADER*)store.constData();
842 
843     // Check for size override
844     UINT32 storeSize = vssStoreHeader->Size;
845     if (sizeOverride) {
846         storeSize = dataSize;
847     }
848 
849     // Check store size
850     if (dataSize < storeSize) {
851         msg(usprintf("%s: VSS2 store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
852             storeSize, storeSize,
853             dataSize, dataSize), parent);
854         return U_SUCCESS;
855     }
856 
857     // Construct header and body
858     UByteArray header = store.left(sizeof(VSS2_VARIABLE_STORE_HEADER));
859     UByteArray body = store.mid(sizeof(VSS2_VARIABLE_STORE_HEADER), storeSize - sizeof(VSS2_VARIABLE_STORE_HEADER));
860 
861     // Add info
862     UString name = UString("VSS2 store");
863     UString info = UString("Signature: ") + guidToUString(vssStoreHeader->Signature, false) +
864         usprintf("\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nFormat: %02Xh\nState: %02Xh\nUnknown: %04Xh",
865             storeSize, storeSize,
866             header.size(), header.size(),
867             body.size(), body.size(),
868             vssStoreHeader->Format,
869             vssStoreHeader->State,
870             vssStoreHeader->Unknown);
871 
872     // Add tree item
873     index = model->addItem(localOffset, Types::Vss2Store, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
874 
875     return U_SUCCESS;
876 }
877 
parseFtwStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)878 USTATUS NvramParser::parseFtwStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
879 {
880     const UINT32 dataSize = (UINT32)store.size();
881 
882     // Check store size
883     if (dataSize < sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64)) {
884         msg(usprintf("%s: volume body is too small even for FTW store header", __FUNCTION__), parent);
885         return U_SUCCESS;
886     }
887 
888     // Obtain required information from parent volume
889     UINT8 emptyByte = 0xFF;
890     UModelIndex parentVolumeIndex = model->findParentOfType(parent, Types::Volume);
891     if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
892         UByteArray data = model->parsingData(parentVolumeIndex);
893         const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
894         emptyByte = pdata->emptyByte;
895     }
896 
897     // Get FTW block headers
898     const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* ftw32BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)store.constData();
899     const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64* ftw64BlockHeader = (const EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64*)store.constData();
900 
901     // Check store size
902     UINT32 ftwBlockSize;
903     bool has32bitHeader;
904     if (ftw32BlockHeader->WriteQueueSize % 0x10 == 0x04) { // Header with 32 bit WriteQueueSize
905         ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) + ftw32BlockHeader->WriteQueueSize;
906         has32bitHeader = true;
907     }
908     else { // Header with 64 bit WriteQueueSize
909         ftwBlockSize = sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64) + (UINT32)ftw64BlockHeader->WriteQueueSize;
910         has32bitHeader = false;
911     }
912     if (dataSize < ftwBlockSize) {
913         msg(usprintf("%s: FTW store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
914             ftwBlockSize, ftwBlockSize,
915             dataSize, dataSize), parent);
916         return U_SUCCESS;
917     }
918 
919     // Construct header and body
920     UINT32 headerSize = has32bitHeader ? sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32) : sizeof(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER64);
921     UByteArray header = store.left(headerSize);
922     UByteArray body = store.mid(headerSize, ftwBlockSize - headerSize);
923 
924     // Check block header checksum
925     UByteArray crcHeader = header;
926     EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32* crcFtwBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER32*)header.data();
927     crcFtwBlockHeader->Crc = emptyByte ? 0xFFFFFFFF : 0;
928     crcFtwBlockHeader->State = emptyByte ? 0xFF : 0;
929     UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)crcFtwBlockHeader, headerSize);
930 
931     // Add info
932     UString name("FTW store");
933     UString info = UString("Signature: ") + guidToUString(ftw32BlockHeader->Signature, false) +
934         usprintf("\nFull size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %" PRIXQ "h (%" PRIuQ ")\nState: %02Xh\nHeader CRC32: %08Xh",
935             ftwBlockSize, ftwBlockSize,
936             headerSize, headerSize,
937             body.size(), body.size(),
938             ftw32BlockHeader->State,
939             ftw32BlockHeader->Crc) +
940             (ftw32BlockHeader->Crc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
941 
942     // Add tree item
943     index = model->addItem(localOffset, Types::FtwStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
944 
945     return U_SUCCESS;
946 }
947 
parseFdcStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)948 USTATUS NvramParser::parseFdcStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
949 {
950     const UINT32 dataSize = (UINT32)store.size();
951 
952     // Check store size
953     if (dataSize < sizeof(FDC_VOLUME_HEADER)) {
954         msg(usprintf("%s: volume body is too small even for FDC store header", __FUNCTION__), parent);
955         return U_SUCCESS;
956     }
957 
958     // Get Fdc store header
959     const FDC_VOLUME_HEADER* fdcStoreHeader = (const FDC_VOLUME_HEADER*)store.constData();
960 
961     // Check store size
962     if (dataSize < fdcStoreHeader->Size) {
963         msg(usprintf("%s: FDC store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
964             fdcStoreHeader->Size, fdcStoreHeader->Size,
965             dataSize, dataSize), parent);
966         return U_SUCCESS;
967     }
968 
969     // Construct header and body
970     UByteArray header = store.left(sizeof(FDC_VOLUME_HEADER));
971     UByteArray body = store.mid(sizeof(FDC_VOLUME_HEADER), fdcStoreHeader->Size - sizeof(FDC_VOLUME_HEADER));
972 
973     // Add info
974     UString name("FDC store");
975     UString info = usprintf("Signature: _FDC\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")",
976         fdcStoreHeader->Size, fdcStoreHeader->Size,
977         header.size(), header.size(),
978         body.size(), body.size());
979 
980     // Add tree item
981     index = model->addItem(localOffset, Types::FdcStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
982 
983     return U_SUCCESS;
984 }
985 
parseFsysStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)986 USTATUS NvramParser::parseFsysStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
987 {
988     const UINT32 dataSize = (UINT32)store.size();
989 
990     // Check store size
991     if (dataSize < sizeof(APPLE_FSYS_STORE_HEADER)) {
992         msg(usprintf("%s: volume body is too small even for Fsys store header", __FUNCTION__), parent);
993         return U_SUCCESS;
994     }
995 
996     // Get Fsys store header
997     const APPLE_FSYS_STORE_HEADER* fsysStoreHeader = (const APPLE_FSYS_STORE_HEADER*)store.constData();
998 
999     // Check store size
1000     if (dataSize < fsysStoreHeader->Size) {
1001         msg(usprintf("%s: Fsys store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1002             fsysStoreHeader->Size, fsysStoreHeader->Size,
1003             dataSize, dataSize), parent);
1004         return U_SUCCESS;
1005     }
1006 
1007     // Construct header and body
1008     UByteArray header = store.left(sizeof(APPLE_FSYS_STORE_HEADER));
1009     UByteArray body = store.mid(sizeof(APPLE_FSYS_STORE_HEADER), fsysStoreHeader->Size - sizeof(APPLE_FSYS_STORE_HEADER) - sizeof(UINT32));
1010 
1011     // Check store checksum
1012     UINT32 storedCrc = *(UINT32*)store.right(sizeof(UINT32)).constData();
1013     UINT32 calculatedCrc = (UINT32)crc32(0, (const UINT8*)store.constData(), (UINT32)store.size() - sizeof(UINT32));
1014 
1015     // Add info
1016     bool isGaidStore = (fsysStoreHeader->Signature == NVRAM_APPLE_GAID_STORE_SIGNATURE);
1017     UString name = isGaidStore ? UString("Gaid store") : UString("Fsys store");
1018     UString info = usprintf("Signature: %s\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nUnknown0: %02Xh\nUnknown1: %08Xh\nCRC32: %08Xh",
1019         isGaidStore ? "Gaid" : "Fsys",
1020         fsysStoreHeader->Size, fsysStoreHeader->Size,
1021         header.size(), header.size(),
1022         body.size(), body.size(),
1023         fsysStoreHeader->Unknown0,
1024         fsysStoreHeader->Unknown1,
1025         storedCrc)
1026         + (storedCrc != calculatedCrc ? usprintf(", invalid, should be %08Xh", calculatedCrc) : UString(", valid"));
1027 
1028     // Add tree item
1029     index = model->addItem(localOffset, Types::FsysStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
1030 
1031     return U_SUCCESS;
1032 }
1033 
parseEvsaStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1034 USTATUS NvramParser::parseEvsaStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1035 {
1036     const UINT32 dataSize = (UINT32)store.size();
1037 
1038     // Check dataSize
1039     if (dataSize < sizeof(EVSA_STORE_ENTRY)) {
1040         msg(usprintf("%s: volume body is too small even for EVSA store header", __FUNCTION__), parent);
1041         return U_SUCCESS;
1042     }
1043 
1044     // Get EVSA store header
1045     const EVSA_STORE_ENTRY* evsaStoreHeader = (const EVSA_STORE_ENTRY*)store.constData();
1046 
1047     // Check store size
1048     if (dataSize < evsaStoreHeader->StoreSize) {
1049         msg(usprintf("%s: EVSA store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1050             evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
1051             dataSize, dataSize), parent);
1052         return U_SUCCESS;
1053     }
1054 
1055     // Construct header and body
1056     UByteArray header = store.left(evsaStoreHeader->Header.Size);
1057     UByteArray body = store.mid(evsaStoreHeader->Header.Size, evsaStoreHeader->StoreSize - evsaStoreHeader->Header.Size);
1058 
1059     // Recalculate checksum
1060     UINT8 calculated = calculateChecksum8(((const UINT8*)evsaStoreHeader) + 2, evsaStoreHeader->Header.Size - 2);
1061 
1062     // Add info
1063     UString name("EVSA store");
1064     UString info = usprintf("Signature: EVSA\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nType: %02Xh\nAttributes: %08Xh\nChecksum: %02Xh",
1065         evsaStoreHeader->StoreSize, evsaStoreHeader->StoreSize,
1066         header.size(), header.size(),
1067         body.size(), body.size(),
1068         evsaStoreHeader->Header.Type,
1069         evsaStoreHeader->Attributes,
1070         evsaStoreHeader->Header.Checksum) +
1071         (evsaStoreHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"));
1072 
1073     // Add tree item
1074     index = model->addItem(localOffset, Types::EvsaStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
1075 
1076     return U_SUCCESS;
1077 }
1078 
parseFlashMapStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1079 USTATUS NvramParser::parseFlashMapStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1080 {
1081     const UINT32 dataSize = (UINT32)store.size();
1082 
1083     // Check data size
1084     if (dataSize < sizeof(PHOENIX_FLASH_MAP_HEADER)) {
1085         msg(usprintf("%s: volume body is too small even for FlashMap block header", __FUNCTION__), parent);
1086         return U_SUCCESS;
1087     }
1088 
1089     // Get FlashMap block header
1090     const PHOENIX_FLASH_MAP_HEADER* flashMapHeader = (const PHOENIX_FLASH_MAP_HEADER*)store.constData();
1091 
1092     // Check store size
1093     UINT32 flashMapSize = sizeof(PHOENIX_FLASH_MAP_HEADER) + flashMapHeader->NumEntries * sizeof(PHOENIX_FLASH_MAP_ENTRY);
1094     if (dataSize < flashMapSize) {
1095         msg(usprintf("%s: FlashMap block size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1096             flashMapSize, flashMapSize,
1097             dataSize, dataSize), parent);
1098         return U_SUCCESS;
1099     }
1100 
1101     // Construct header and body
1102     UByteArray header = store.left(sizeof(PHOENIX_FLASH_MAP_HEADER));
1103     UByteArray body = store.mid(sizeof(PHOENIX_FLASH_MAP_HEADER), flashMapSize - sizeof(PHOENIX_FLASH_MAP_HEADER));
1104 
1105     // Add info
1106     UString name("Phoenix SCT flash map");
1107     UString info = usprintf("Signature: _FLASH_MAP\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nNumber of entries: %u",
1108         flashMapSize, flashMapSize,
1109         header.size(), header.size(),
1110         body.size(), body.size(),
1111         flashMapHeader->NumEntries);
1112 
1113     // Add tree item
1114     index = model->addItem(localOffset, Types::FlashMapStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
1115 
1116     return U_SUCCESS;
1117 }
1118 
parseCmdbStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1119 USTATUS NvramParser::parseCmdbStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1120 {
1121     const UINT32 dataSize = (UINT32)store.size();
1122 
1123     // Check store size
1124     if (dataSize < sizeof(PHOENIX_CMDB_HEADER)) {
1125         msg(usprintf("%s: volume body is too small even for CMDB store header", __FUNCTION__), parent);
1126         return U_SUCCESS;
1127     }
1128 
1129     UINT32 cmdbSize = NVRAM_PHOENIX_CMDB_SIZE;
1130     if (dataSize < cmdbSize) {
1131         msg(usprintf("%s: CMDB store size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1132             cmdbSize, cmdbSize,
1133             dataSize, dataSize), parent);
1134         return U_SUCCESS;
1135     }
1136 
1137     // Get store header
1138     const PHOENIX_CMDB_HEADER* cmdbHeader = (const PHOENIX_CMDB_HEADER*)store.constData();
1139 
1140     // Construct header and body
1141     UByteArray header = store.left(cmdbHeader->TotalSize);
1142     UByteArray body = store.mid(cmdbHeader->TotalSize, cmdbSize - cmdbHeader->TotalSize);
1143 
1144     // Add info
1145     UString name("CMDB store");
1146     UString info = usprintf("Signature: CMDB\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")",
1147         cmdbSize, cmdbSize,
1148         header.size(), header.size(),
1149         body.size(), body.size());
1150 
1151     // Add tree item
1152     index = model->addItem(localOffset, Types::CmdbStore, 0, name, UString(), info, header, body, UByteArray(), Fixed, parent);
1153 
1154     return U_SUCCESS;
1155 }
1156 
parseSlicPubkeyHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1157 USTATUS NvramParser::parseSlicPubkeyHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1158 {
1159     const UINT32 dataSize = (UINT32)store.size();
1160 
1161     // Check data size
1162     if (dataSize < sizeof(OEM_ACTIVATION_PUBKEY)) {
1163         msg(usprintf("%s: volume body is too small even for SLIC pubkey header", __FUNCTION__), parent);
1164         return U_SUCCESS;
1165     }
1166 
1167     // Get SLIC pubkey header
1168     const OEM_ACTIVATION_PUBKEY* pubkeyHeader = (const OEM_ACTIVATION_PUBKEY*)store.constData();
1169 
1170     // Check store size
1171     if (dataSize < pubkeyHeader->Size) {
1172         msg(usprintf("%s: SLIC pubkey size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1173             pubkeyHeader->Size, pubkeyHeader->Size,
1174             dataSize, dataSize), parent);
1175         return U_SUCCESS;
1176     }
1177 
1178     // Construct header and body
1179     UByteArray header = store.left(sizeof(OEM_ACTIVATION_PUBKEY));
1180 
1181     // Add info
1182     UString name("SLIC pubkey");
1183     UString info = usprintf("Type: 0h\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: 0h (0)\n"
1184         "Key type: %02Xh\nVersion: %02Xh\nAlgorithm: %08Xh\nMagic: RSA1\nBit length: %08Xh\nExponent: %08Xh",
1185         pubkeyHeader->Size, pubkeyHeader->Size,
1186         header.size(), header.size(),
1187         pubkeyHeader->KeyType,
1188         pubkeyHeader->Version,
1189         pubkeyHeader->Algorithm,
1190         pubkeyHeader->BitLength,
1191         pubkeyHeader->Exponent);
1192 
1193     // Add tree item
1194     index = model->addItem(localOffset, Types::SlicData, Subtypes::PubkeySlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent);
1195 
1196     return U_SUCCESS;
1197 }
1198 
parseSlicMarkerHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1199 USTATUS NvramParser::parseSlicMarkerHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1200 {
1201     const UINT32 dataSize = (UINT32)store.size();
1202 
1203     // Check data size
1204     if (dataSize < sizeof(OEM_ACTIVATION_MARKER)) {
1205         msg(usprintf("%s: volume body is too small even for SLIC marker header", __FUNCTION__), parent);
1206         return U_SUCCESS;
1207     }
1208 
1209     // Get SLIC marker header
1210     const OEM_ACTIVATION_MARKER* markerHeader = (const OEM_ACTIVATION_MARKER*)store.constData();
1211 
1212     // Check store size
1213     if (dataSize < markerHeader->Size) {
1214         msg(usprintf("%s: SLIC marker size %Xh (%u) is greater than volume body size %Xh (%u)", __FUNCTION__,
1215             markerHeader->Size, markerHeader->Size,
1216             dataSize, dataSize), parent);
1217         return U_SUCCESS;
1218     }
1219 
1220     // Construct header and body
1221     UByteArray header = store.left(sizeof(OEM_ACTIVATION_MARKER));
1222 
1223     // Add info
1224     UString name("SLIC marker");
1225     UString info = usprintf("Type: 1h\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: 0h (0)\n"
1226         "Version: %08Xh\nOEM ID: %s\nOEM table ID: %s\nWindows flag: WINDOWS\nSLIC version: %08Xh",
1227         markerHeader->Size, markerHeader->Size,
1228         header.size(), header.size(),
1229         markerHeader->Version,
1230         (const char*)UString((const char*)&(markerHeader->OemId)).left(6).toLocal8Bit(),
1231         (const char*)UString((const char*)&(markerHeader->OemTableId)).left(8).toLocal8Bit(),
1232         markerHeader->SlicVersion);
1233 
1234 
1235     // Add tree item
1236     index = model->addItem(localOffset, Types::SlicData, Subtypes::MarkerSlicData, name, UString(), info, header, UByteArray(), UByteArray(), Fixed, parent);
1237 
1238     return U_SUCCESS;
1239 }
1240 
parseStoreHeader(const UByteArray & store,const UINT32 localOffset,const UModelIndex & parent,UModelIndex & index)1241 USTATUS NvramParser::parseStoreHeader(const UByteArray & store, const UINT32 localOffset, const UModelIndex & parent, UModelIndex & index)
1242 {
1243     const UINT32 dataSize = (UINT32)store.size();
1244     const UINT32* signature = (const UINT32*)store.constData();
1245     // Check store size
1246     if (dataSize < sizeof(UINT32)) {
1247         msg(usprintf("%s: volume body is too small even for a store signature", __FUNCTION__), parent);
1248         return U_SUCCESS;
1249     }
1250 
1251     // Check signature and run parser function needed
1252     // VSS/SVS/NSS store
1253     if (*signature == NVRAM_VSS_STORE_SIGNATURE || *signature == NVRAM_APPLE_SVS_STORE_SIGNATURE || *signature == NVRAM_APPLE_NSS_STORE_SIGNATURE)
1254         return parseVssStoreHeader(store, localOffset, false, parent, index);
1255     // VSS2 store
1256     if (*signature == NVRAM_VSS2_AUTH_VAR_KEY_DATABASE_GUID_PART1 || *signature == NVRAM_VSS2_STORE_GUID_PART1)
1257         return parseVss2StoreHeader(store, localOffset, false, parent, index);
1258     // FTW store
1259     else if (*signature == NVRAM_MAIN_STORE_VOLUME_GUID_DATA1 || *signature == EDKII_WORKING_BLOCK_SIGNATURE_GUID_DATA1)
1260         return parseFtwStoreHeader(store, localOffset, parent, index);
1261     // FDC store
1262     else if (*signature == NVRAM_FDC_VOLUME_SIGNATURE)
1263         return parseFdcStoreHeader(store, localOffset, parent, index);
1264     // Apple Fsys/Gaid store
1265     else if (*signature == NVRAM_APPLE_FSYS_STORE_SIGNATURE || *signature == NVRAM_APPLE_GAID_STORE_SIGNATURE)
1266         return parseFsysStoreHeader(store, localOffset, parent, index);
1267     // EVSA store
1268     else if (dataSize >= 2 * sizeof(UINT32) && *(signature + 1) == NVRAM_EVSA_STORE_SIGNATURE)
1269         return parseEvsaStoreHeader(store, localOffset, parent, index);
1270     // Phoenix SCT flash map
1271     else if (*signature == NVRAM_PHOENIX_FLASH_MAP_SIGNATURE_PART1)
1272         return parseFlashMapStoreHeader(store, localOffset, parent, index);
1273     // Phoenix CMDB store
1274     else if (*signature == NVRAM_PHOENIX_CMDB_HEADER_SIGNATURE)
1275         return parseCmdbStoreHeader(store, localOffset, parent, index);
1276     // SLIC pubkey
1277     else if (dataSize >= 5 * sizeof(UINT32) && *(signature + 4) == OEM_ACTIVATION_PUBKEY_MAGIC)
1278         return parseSlicPubkeyHeader(store, localOffset, parent, index);
1279     // SLIC marker
1280     else if (dataSize >= 34 && *(const UINT64*)(store.constData() + 26) == OEM_ACTIVATION_MARKER_WINDOWS_FLAG)
1281         return parseSlicMarkerHeader(store, localOffset, parent, index);
1282     // Intel microcode
1283     // Must be checked after SLIC marker because of the same *signature values
1284     else if (*signature == INTEL_MICROCODE_HEADER_VERSION_1)
1285         return ffsParser->parseIntelMicrocodeHeader(store, localOffset, parent, index);
1286 
1287     msg(usprintf("parseStoreHeader: don't know how to parse a header with signature %08Xh", *signature), parent);
1288     return U_SUCCESS;
1289 }
1290 
parseFdcStoreBody(const UModelIndex & index)1291 USTATUS NvramParser::parseFdcStoreBody(const UModelIndex & index)
1292 {
1293     // Sanity check
1294     if (!index.isValid())
1295         return U_INVALID_PARAMETER;
1296 
1297     // Get item data
1298     const UByteArray data = model->body(index);
1299 
1300     // Get local offset
1301     UINT32 localOffset = (UINT32)model->header(index).size();
1302 
1303     // The body is a firmware volume with either a VSS or VSS2 store
1304     UModelIndex volumeIndex;
1305     USTATUS status = ffsParser->parseVolumeHeader(data, localOffset, index, volumeIndex);
1306     if (status || !volumeIndex.isValid()) {
1307         msg(usprintf("%s: store can't be parsed as FDC store", __FUNCTION__), index);
1308         return U_SUCCESS;
1309     }
1310 
1311     // Determine if it's a VSS or VSS2 store inside
1312     UByteArray store = model->body(volumeIndex);
1313     if ((UINT32)store.size() >= sizeof(UINT32) && *(const UINT32*)store.constData() == NVRAM_VSS_STORE_SIGNATURE) {
1314         UModelIndex vssIndex;
1315         status = parseVssStoreHeader(store, (UINT32)(localOffset + model->header(volumeIndex).size()), true, volumeIndex, vssIndex);
1316         if (status)
1317             return status;
1318         return parseVssStoreBody(vssIndex, 0);
1319     }
1320     else if ((UINT32)store.size() >= sizeof(EFI_GUID) && store.left(sizeof(EFI_GUID)) == NVRAM_FDC_STORE_GUID) {
1321         UModelIndex vss2Index;
1322         status = parseVss2StoreHeader(store, (UINT32)(localOffset + model->header(volumeIndex).size()), true, volumeIndex, vss2Index);
1323         if (status)
1324             return status;
1325         return parseVssStoreBody(vss2Index, 0);
1326     }
1327     else {
1328         msg(usprintf("%s: internal volume can't be parsed as VSS/VSS2 store", __FUNCTION__), index);
1329         return U_SUCCESS;
1330     }
1331 
1332 }
1333 
parseVssStoreBody(const UModelIndex & index,UINT8 alignment)1334 USTATUS NvramParser::parseVssStoreBody(const UModelIndex & index, UINT8 alignment)
1335 {
1336     // Sanity check
1337     if (!index.isValid())
1338         return U_INVALID_PARAMETER;
1339 
1340     // Obtain required information from parent volume
1341     UINT8 emptyByte = 0xFF;
1342     UModelIndex parentVolumeIndex = model->findParentOfType(index, Types::Volume);
1343     if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
1344         UByteArray data = model->parsingData(parentVolumeIndex);
1345         const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
1346         emptyByte = pdata->emptyByte;
1347     }
1348 
1349     // Get local offset
1350     UINT32 localOffset = (UINT32)model->header(index).size();
1351 
1352     // Get item data
1353     const UByteArray data = model->body(index);
1354 
1355     // Check that the is enough space for variable header
1356     const UINT32 dataSize = (UINT32)data.size();
1357     if (dataSize < sizeof(VSS_VARIABLE_HEADER)) {
1358         msg(usprintf("%s: store body is too small even for VSS variable header", __FUNCTION__), index);
1359         return U_SUCCESS;
1360     }
1361 
1362     UINT32 offset = 0;
1363 
1364     // Parse all variables
1365     while (1) {
1366         bool isInvalid = true;
1367         bool isAuthenticated = false;
1368         bool isAppleCrc32 = false;
1369         bool isIntelSpecial = false;
1370 
1371         UINT32 storedCrc32 = 0;
1372         UINT32 calculatedCrc32 = 0;
1373         UINT64 monotonicCounter = 0;
1374         EFI_TIME timestamp = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1375         UINT32 pubKeyIndex = 0;
1376 
1377         UINT8 subtype = 0;
1378         UString name;
1379         UString text;
1380         EFI_GUID* variableGuid = NULL;
1381         CHAR16*   variableName = (CHAR16*)L"";
1382         UByteArray header;
1383         UByteArray body;
1384 
1385         UINT32 unparsedSize = dataSize - offset;
1386 
1387         // Get variable header
1388         const VSS_VARIABLE_HEADER* variableHeader = (const VSS_VARIABLE_HEADER*)(data.constData() + offset);
1389 
1390         // Check variable header to fit in still unparsed data
1391         UINT32 variableSize = 0;
1392         if (unparsedSize >= sizeof(VSS_VARIABLE_HEADER)
1393             && variableHeader->StartId == NVRAM_VSS_VARIABLE_START_ID) {
1394             // Apple VSS variable with CRC32 of the data
1395             if (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPLE_DATA_CHECKSUM) {
1396                 isAppleCrc32 = true;
1397                 if (unparsedSize < sizeof(VSS_APPLE_VARIABLE_HEADER)) {
1398                     variableSize = 0;
1399                 }
1400                 else {
1401                     const VSS_APPLE_VARIABLE_HEADER* appleVariableHeader = (const VSS_APPLE_VARIABLE_HEADER*)variableHeader;
1402                     variableSize = sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize + appleVariableHeader->DataSize;
1403                     variableGuid = (EFI_GUID*)&appleVariableHeader->VendorGuid;
1404                     variableName = (CHAR16*)(appleVariableHeader + 1);
1405 
1406                     header = data.mid(offset, sizeof(VSS_APPLE_VARIABLE_HEADER) + appleVariableHeader->NameSize);
1407                     body = data.mid(offset + header.size(), appleVariableHeader->DataSize);
1408 
1409                     // Calculate CRC32 of the variable data
1410                     storedCrc32 = appleVariableHeader->DataCrc32;
1411                     calculatedCrc32 = (UINT32)crc32(0, (const UINT8*)body.constData(), (uInt)body.size());
1412                 }
1413             }
1414 
1415             // Authenticated variable
1416             else if ((variableHeader->Attributes & NVRAM_VSS_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
1417                 || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
1418                 || (variableHeader->Attributes & NVRAM_VSS_VARIABLE_APPEND_WRITE)
1419                 || (variableHeader->NameSize == 0 && variableHeader->DataSize == 0)) { // If both NameSize and DataSize are zeros, it's auth variable with zero montonic counter
1420                 isAuthenticated = true;
1421                 if (unparsedSize < sizeof(VSS_AUTH_VARIABLE_HEADER)) {
1422                     variableSize = 0;
1423                 }
1424                 else {
1425                     const VSS_AUTH_VARIABLE_HEADER* authVariableHeader = (const VSS_AUTH_VARIABLE_HEADER*)variableHeader;
1426                     variableSize = sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize + authVariableHeader->DataSize;
1427                     variableGuid = (EFI_GUID*)&authVariableHeader->VendorGuid;
1428                     variableName = (CHAR16*)(authVariableHeader + 1);
1429 
1430                     header = data.mid(offset, sizeof(VSS_AUTH_VARIABLE_HEADER) + authVariableHeader->NameSize);
1431                     body = data.mid(offset + header.size(), authVariableHeader->DataSize);
1432 
1433                     monotonicCounter = authVariableHeader->MonotonicCounter;
1434                     timestamp = authVariableHeader->Timestamp;
1435                     pubKeyIndex = authVariableHeader->PubKeyIndex;
1436                 }
1437             }
1438 
1439             // Intel special variable
1440             else if (variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_VALID
1441                 || variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_INVALID) {
1442                 isIntelSpecial = true;
1443                 const VSS_INTEL_VARIABLE_HEADER* intelVariableHeader = (const VSS_INTEL_VARIABLE_HEADER*)variableHeader;
1444                 variableSize = intelVariableHeader->TotalSize;
1445                 variableGuid = (EFI_GUID*)&intelVariableHeader->VendorGuid;
1446                 variableName = (CHAR16*)(intelVariableHeader + 1);
1447 
1448                 UINT32 i = 0;
1449                 while (variableName[i] != 0) ++i;
1450 
1451                 i = sizeof(VSS_INTEL_VARIABLE_HEADER) + 2 * (i + 1);
1452                 i = i < variableSize ? i : variableSize;
1453 
1454                 header = data.mid(offset, i);
1455                 body = data.mid(offset + header.size(), variableSize - i);
1456             }
1457 
1458             // Normal VSS variable
1459             else {
1460                 variableSize = sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize + variableHeader->DataSize;
1461                 variableGuid = (EFI_GUID*)&variableHeader->VendorGuid;
1462                 variableName = (CHAR16*)(variableHeader + 1);
1463 
1464                 header = data.mid(offset, sizeof(VSS_VARIABLE_HEADER) + variableHeader->NameSize);
1465                 body = data.mid(offset + header.size(), variableHeader->DataSize);
1466             }
1467 
1468             // Check variable state
1469             if (variableHeader->State == NVRAM_VSS_INTEL_VARIABLE_VALID
1470                 || variableHeader->State == NVRAM_VSS_VARIABLE_ADDED
1471                 || variableHeader->State == NVRAM_VSS_VARIABLE_HEADER_VALID) {
1472                 isInvalid = false;
1473             }
1474 
1475             // Check variable size
1476             if (variableSize > unparsedSize) {
1477                 variableSize = 0;
1478             }
1479         }
1480 
1481         // Can't parse further, add the last element and break the loop
1482         if (!variableSize) {
1483             // Check if the data left is a free space or a padding
1484             UByteArray padding = data.mid(offset, unparsedSize);
1485             // Get info
1486             UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", padding.size(), padding.size());
1487 
1488             if (padding.count(emptyByte) == padding.size()) { // Free space
1489                 // Add tree item
1490                 model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
1491             }
1492             else { // Padding
1493                 // Nothing is parsed yet, but the store is not empty
1494                 if (!offset) {
1495                     msg(usprintf("%s: store can't be parsed as VSS store", __FUNCTION__), index);
1496                     return U_SUCCESS;
1497                 }
1498 
1499                 // Add tree item
1500                 model->addItem(localOffset + offset, Types::Padding, getPaddingType(padding), UString("Padding"), UString(), info, UByteArray(), padding, UByteArray(), Fixed, index);
1501             }
1502 
1503             return U_SUCCESS;
1504         }
1505 
1506         UString info;
1507 
1508         // Rename invalid variables
1509         if (isInvalid || !variableGuid) {
1510             isInvalid = true;
1511             name = UString("Invalid");
1512         }
1513         else { // Add GUID and text for valid variables
1514             name = guidToUString(readUnaligned(variableGuid));
1515             info += UString("Variable GUID: ") + guidToUString(readUnaligned(variableGuid), false) + UString("\n");
1516 
1517 #if QT_VERSION_MAJOR >= 6
1518             text = UString::fromUtf16((char16_t *)variableName);
1519 #else
1520             text = UString::fromUtf16(variableName);
1521 #endif
1522         }
1523 
1524         // Add info
1525         info += usprintf("Full size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nState: %02Xh\nReserved: %02Xh\nAttributes: %08Xh (",
1526             variableSize, variableSize,
1527             header.size(), header.size(),
1528             body.size(), body.size(),
1529             variableHeader->State,
1530             variableHeader->Reserved,
1531             variableHeader->Attributes) + vssAttributesToUString(variableHeader->Attributes) + UString(")");
1532 
1533         // Set subtype and add related info
1534         if (isInvalid)
1535             subtype = Subtypes::InvalidVssEntry;
1536         else if (isAuthenticated) {
1537             subtype = Subtypes::AuthVssEntry;
1538             info += usprintf("\nMonotonic counter: %" PRIX64 "h\nTimestamp: ", monotonicCounter) + efiTimeToUString(timestamp)
1539                 + usprintf("\nPubKey index: %u", pubKeyIndex);
1540         }
1541         else if (isAppleCrc32) {
1542             subtype = Subtypes::AppleVssEntry;
1543             info += usprintf("\nData checksum: %08Xh", storedCrc32) +
1544                 (storedCrc32 != calculatedCrc32 ? usprintf(", invalid, should be %08Xh", calculatedCrc32) : UString(", valid"));
1545         }
1546         else if (isIntelSpecial) {
1547             subtype = Subtypes::IntelVssEntry;
1548         }
1549         else {
1550             subtype = Subtypes::StandardVssEntry;
1551         }
1552 
1553         // Add tree item
1554         model->addItem(localOffset + offset, Types::VssEntry, subtype, name, text, info, header, body, UByteArray(), Fixed, index);
1555 
1556         // Apply alignment, if needed
1557         if (alignment) {
1558             variableSize = ((variableSize + alignment - 1) & (~(alignment - 1)));
1559         }
1560 
1561         // Move to next variable
1562         offset += variableSize;
1563     }
1564 
1565     return U_SUCCESS;
1566 }
1567 
parseFsysStoreBody(const UModelIndex & index)1568 USTATUS NvramParser::parseFsysStoreBody(const UModelIndex & index)
1569 {
1570     // Sanity check
1571     if (!index.isValid())
1572         return U_INVALID_PARAMETER;
1573 
1574     // Get local offset
1575     UINT32 localOffset = (UINT32)model->header(index).size();
1576 
1577     // Get item data
1578     const UByteArray data = model->body(index);
1579 
1580     // Check that the is enough space for variable header
1581     const UINT32 storeDataSize = (UINT32)data.size();
1582     UINT32 offset = 0;
1583 
1584     // Parse all variables
1585     while (1) {
1586         UINT32 unparsedSize = storeDataSize - offset;
1587         UINT32 variableSize = 0;
1588 
1589         // Get nameSize and name of the variable
1590         UINT8 nameSize = *(UINT8*)(data.constData() + offset);
1591         bool valid = !(nameSize & 0x80); // Last bit is a validity bit, 0 means valid
1592         nameSize &= 0x7F;
1593 
1594         // Check sanity
1595         if (unparsedSize >= nameSize + sizeof(UINT8)) {
1596             variableSize = nameSize + sizeof(UINT8);
1597         }
1598 
1599         UByteArray name;
1600         if (variableSize) {
1601             name = data.mid(offset + sizeof(UINT8), nameSize);
1602             // Check for EOF variable
1603             if (nameSize == 3 && name[0] == 'E' && name[1] == 'O' && name[2] == 'F') {
1604                 // There is no data afterward, add EOF variable and free space and return
1605                 UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize);
1606                 UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", header.size(), header.size());
1607 
1608                 // Add EOF tree item
1609                 model->addItem(localOffset + offset, Types::FsysEntry, Subtypes::NormalFsysEntry, UString("EOF"), UString(), info, header, UByteArray(), UByteArray(), Fixed, index);
1610 
1611                 // Add free space
1612                 offset += header.size();
1613                 UByteArray body = data.mid(offset);
1614                 info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", body.size(), body.size());
1615 
1616                 // Add free space tree item
1617                 model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1618 
1619                 return U_SUCCESS;
1620             }
1621         }
1622 
1623         // Get dataSize and data of the variable
1624         const UINT16 dataSize = *(UINT16*)(data.constData() + offset + sizeof(UINT8) + nameSize);
1625         if (unparsedSize >= sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize) {
1626             variableSize = sizeof(UINT8) + nameSize + sizeof(UINT16) + dataSize;
1627         }
1628         else {
1629             // Last variable is bad, add the rest as padding and return
1630             UByteArray body = data.mid(offset);
1631             UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", body.size(), body.size());
1632 
1633             // Add padding tree item
1634             model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1635 
1636             // Show message
1637             msg(usprintf("%s: next variable appears too big, added as padding", __FUNCTION__), index);
1638 
1639             return U_SUCCESS;
1640         }
1641 
1642         // Construct header and body
1643         UByteArray header = data.mid(offset, sizeof(UINT8) + nameSize + sizeof(UINT16));
1644         UByteArray body = data.mid(offset + sizeof(UINT8) + nameSize + sizeof(UINT16), dataSize);
1645 
1646         // Add info
1647         UString info = usprintf("Full size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")",
1648             variableSize, variableSize,
1649             header.size(), header.size(),
1650             body.size(), body.size());
1651 
1652         // Add tree item
1653         model->addItem(localOffset + offset, Types::FsysEntry, valid ? Subtypes::NormalFsysEntry : Subtypes::InvalidFsysEntry, UString(name.constData()), UString(), info, header, body, UByteArray(), Fixed, index);
1654 
1655         // Move to next variable
1656         offset += variableSize;
1657     }
1658 
1659     return U_SUCCESS;
1660 }
1661 
parseEvsaStoreBody(const UModelIndex & index)1662 USTATUS NvramParser::parseEvsaStoreBody(const UModelIndex & index)
1663 {
1664     // Sanity check
1665     if (!index.isValid())
1666         return U_INVALID_PARAMETER;
1667 
1668     // Obtain required information from parent volume
1669     UINT8 emptyByte = 0xFF;
1670     UModelIndex parentVolumeIndex = model->findParentOfType(index, Types::Volume);
1671     if (parentVolumeIndex.isValid() && model->hasEmptyParsingData(parentVolumeIndex) == false) {
1672         UByteArray data = model->parsingData(parentVolumeIndex);
1673         const VOLUME_PARSING_DATA* pdata = (const VOLUME_PARSING_DATA*)data.constData();
1674         emptyByte = pdata->emptyByte;
1675     }
1676 
1677     // Get local offset
1678     UINT32 localOffset = (UINT32)model->header(index).size();
1679 
1680     // Get item data
1681     const UByteArray data = model->body(index);
1682 
1683     // Check that the is enough space for entry header
1684     const UINT32 storeDataSize = (UINT32)data.size();
1685     UINT32 offset = 0;
1686 
1687     std::map<UINT16, EFI_GUID> guidMap;
1688     std::map<UINT16, UString> nameMap;
1689 
1690     // Parse all entries
1691     UINT32 unparsedSize = storeDataSize;
1692     while (unparsedSize) {
1693         UINT32 variableSize = 0;
1694         UString name;
1695         UString info;
1696         UByteArray header;
1697         UByteArray body;
1698         UINT8 subtype;
1699         UINT8 calculated;
1700 
1701         const EVSA_ENTRY_HEADER* entryHeader = (const EVSA_ENTRY_HEADER*)(data.constData() + offset);
1702 
1703         // Check entry size
1704         variableSize = sizeof(EVSA_ENTRY_HEADER);
1705         if (unparsedSize < variableSize || unparsedSize < entryHeader->Size) {
1706             body = data.mid(offset);
1707             info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", body.size(), body.size());
1708 
1709             if (body.count(emptyByte) == body.size()) { // Free space
1710                 // Add free space tree item
1711                 model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1712             }
1713             else {
1714                 // Add padding tree item
1715                 UModelIndex itemIndex = model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1716 
1717                 // Show message
1718                 msg(usprintf("%s: variable parsing failed, the rest of unparsed store added as padding", __FUNCTION__), itemIndex);
1719             }
1720             break;
1721         }
1722         variableSize = entryHeader->Size;
1723 
1724         // Recalculate entry checksum
1725         calculated = calculateChecksum8(((const UINT8*)entryHeader) + 2, entryHeader->Size - 2);
1726 
1727         // GUID entry
1728         if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID1 ||
1729             entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_GUID2) {
1730             const EVSA_GUID_ENTRY* guidHeader = (const EVSA_GUID_ENTRY*)entryHeader;
1731             header = data.mid(offset, sizeof(EVSA_GUID_ENTRY));
1732             body = data.mid(offset + sizeof(EVSA_GUID_ENTRY), guidHeader->Header.Size - sizeof(EVSA_GUID_ENTRY));
1733             EFI_GUID guid = *(EFI_GUID*)body.constData();
1734             name = guidToUString(guid);
1735             info = UString("GUID: ") + guidToUString(guid, false) + usprintf("\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nType: %02Xh\nChecksum: %02Xh",
1736                 variableSize, variableSize,
1737                 header.size(), header.size(),
1738                 body.size(), body.size(),
1739                 guidHeader->Header.Type,
1740                 guidHeader->Header.Checksum)
1741                 + (guidHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
1742                 + usprintf("\nGuidId: %04Xh", guidHeader->GuidId);
1743             subtype = Subtypes::GuidEvsaEntry;
1744             guidMap.insert(std::pair<UINT16, EFI_GUID>(guidHeader->GuidId, guid));
1745         }
1746         // Name entry
1747         else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME1 ||
1748             entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_NAME2) {
1749             const EVSA_NAME_ENTRY* nameHeader = (const EVSA_NAME_ENTRY*)entryHeader;
1750             header = data.mid(offset, sizeof(EVSA_NAME_ENTRY));
1751             body = data.mid(offset + sizeof(EVSA_NAME_ENTRY), nameHeader->Header.Size - sizeof(EVSA_NAME_ENTRY));
1752 
1753 #if QT_VERSION_MAJOR >= 6
1754             name = UString::fromUtf16((const char16_t *)body.constData());
1755 #else
1756             name = UString::fromUtf16((const CHAR16*)body.constData());
1757 #endif
1758 
1759             info = UString("Name: ") + name + usprintf("\nFull size: %Xh (%u)\nHeader size: %" PRIXQ "h (%" PRIuQ ")\nBody size: %" PRIXQ "h (%" PRIuQ ")\nType: %02Xh\nChecksum: %02Xh",
1760                 variableSize, variableSize,
1761                 header.size(), header.size(),
1762                 body.size(), body.size(),
1763                 nameHeader->Header.Type,
1764                 nameHeader->Header.Checksum)
1765                 + (nameHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
1766                 + usprintf("\nVarId: %04Xh", nameHeader->VarId);
1767             subtype = Subtypes::NameEvsaEntry;
1768             nameMap.insert(std::pair<UINT16, UString>(nameHeader->VarId, name));
1769         }
1770         // Data entry
1771         else if (entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA1 ||
1772             entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA2 ||
1773             entryHeader->Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
1774             const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)entryHeader;
1775             // Check for extended header
1776             UINT32 headerSize = sizeof(EVSA_DATA_ENTRY);
1777             UINT32 dataSize = dataHeader->Header.Size - sizeof(EVSA_DATA_ENTRY);
1778             if (dataHeader->Attributes & NVRAM_EVSA_DATA_EXTENDED_HEADER) {
1779                 const EVSA_DATA_ENTRY_EXTENDED* dataHeaderExtended = (const EVSA_DATA_ENTRY_EXTENDED*)entryHeader;
1780                 headerSize = sizeof(EVSA_DATA_ENTRY_EXTENDED);
1781                 dataSize = dataHeaderExtended->DataSize;
1782                 variableSize = headerSize + dataSize;
1783             }
1784 
1785             header = data.mid(offset, headerSize);
1786             body = data.mid(offset + headerSize, dataSize);
1787             name = UString("Data");
1788             info = usprintf("Full size: %Xh (%u)\nHeader size: %Xh (%u)\nBody size: %Xh (%u)\nType: %02Xh\nChecksum: %02Xh",
1789                 variableSize, variableSize,
1790                 headerSize, headerSize,
1791                 dataSize, dataSize,
1792                 dataHeader->Header.Type,
1793                 dataHeader->Header.Checksum)
1794                 + (dataHeader->Header.Checksum != calculated ? usprintf(", invalid, should be %02Xh", calculated) : UString(", valid"))
1795                 + usprintf("\nVarId: %04Xh\nGuidId: %04Xh\nAttributes: %08Xh (",
1796                     dataHeader->VarId,
1797                     dataHeader->GuidId,
1798                     dataHeader->Attributes)
1799                 + evsaAttributesToUString(dataHeader->Attributes) + UString(")");
1800             subtype = Subtypes::DataEvsaEntry;
1801         }
1802         // Unknown entry or free space
1803         else {
1804             body = data.mid(offset);
1805             info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", body.size(), body.size());
1806 
1807             if (body.count(emptyByte) == body.size()) { // Free space
1808                 // Add free space tree item
1809                 model->addItem(localOffset + offset, Types::FreeSpace, 0, UString("Free space"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1810             }
1811             else {
1812                 // Add padding tree item
1813                 UModelIndex itemIndex = model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1814 
1815                 // Show message
1816                 msg(usprintf("%s: unknown variable of type %02Xh found at offset %Xh, the rest of unparsed store added as padding", __FUNCTION__, entryHeader->Type, offset), itemIndex);
1817             }
1818             break;
1819         }
1820 
1821         // Add tree item
1822         model->addItem(localOffset + offset, Types::EvsaEntry, subtype, name, UString(), info, header, body, UByteArray(), Fixed, index);
1823 
1824         // Move to next variable
1825         offset += variableSize;
1826         unparsedSize = storeDataSize - offset;
1827     }
1828 
1829     // Reparse all data variables to detect invalid ones and assign name and test to valid ones
1830     for (int i = 0; i < model->rowCount(index); i++) {
1831 #if ((QT_VERSION_MAJOR == 5) && (QT_VERSION_MINOR < 6)) || (QT_VERSION_MAJOR < 5)
1832         UModelIndex current = index.child(i, 0);
1833 #else
1834         UModelIndex current = index.model()->index(i, 0, index);
1835 #endif
1836 
1837         if (model->subtype(current) == Subtypes::DataEvsaEntry) {
1838             UByteArray header = model->header(current);
1839             const EVSA_DATA_ENTRY* dataHeader = (const EVSA_DATA_ENTRY*)header.constData();
1840             UString guid;
1841             if (guidMap.count(dataHeader->GuidId))
1842                 guid = guidToUString(guidMap[dataHeader->GuidId], false);
1843             UString name;
1844             if (nameMap.count(dataHeader->VarId))
1845                 name = nameMap[dataHeader->VarId];
1846 
1847             // Check for variable validity
1848             if (guid.isEmpty() && name.isEmpty()) { // Both name and guid aren't found
1849                 model->setSubtype(current, Subtypes::InvalidEvsaEntry);
1850                 model->setName(current, UString("Invalid"));
1851                 msg(usprintf("%s: data variable with invalid GuidId and invalid VarId", __FUNCTION__), current);
1852             }
1853             else if (guid.isEmpty()) { // Guid not found
1854                 model->setSubtype(current, Subtypes::InvalidEvsaEntry);
1855                 model->setName(current, UString("Invalid"));
1856                 msg(usprintf("%s: data variable with invalid GuidId", __FUNCTION__), current);
1857             }
1858             else if (name.isEmpty()) { // Name not found
1859                 model->setSubtype(current, Subtypes::InvalidEvsaEntry);
1860                 model->setName(current, UString("Invalid"));
1861                 msg(usprintf("%s: data variable with invalid VarId", __FUNCTION__), current);
1862             }
1863             else { // Variable is OK, rename it
1864                 if (dataHeader->Header.Type == NVRAM_EVSA_ENTRY_TYPE_DATA_INVALID) {
1865                     model->setSubtype(current, Subtypes::InvalidEvsaEntry);
1866                     model->setName(current, UString("Invalid"));
1867                 }
1868                 else {
1869                     model->setName(current, guid);
1870                 }
1871                 model->setText(current, name);
1872                 model->addInfo(current, UString("GUID: ") + guid + UString("\nName: ") + name + UString("\n"), false);
1873             }
1874         }
1875     }
1876 
1877     return U_SUCCESS;
1878 }
1879 
1880 
parseFlashMapBody(const UModelIndex & index)1881 USTATUS NvramParser::parseFlashMapBody(const UModelIndex & index)
1882 {
1883     // Sanity check
1884     if (!index.isValid())
1885         return U_INVALID_PARAMETER;
1886 
1887     // Get parsing data for the current item
1888     UINT32 localOffset = (UINT32)model->header(index).size();
1889     const UByteArray data = model->body(index);
1890 
1891 
1892     const UINT32 dataSize = (UINT32)data.size();
1893     UINT32 offset = 0;
1894     UINT32 unparsedSize = dataSize;
1895     // Parse all entries
1896     while (unparsedSize) {
1897         const PHOENIX_FLASH_MAP_ENTRY* entryHeader = (const PHOENIX_FLASH_MAP_ENTRY*)(data.constData() + offset);
1898 
1899         // Check entry size
1900         if (unparsedSize < sizeof(PHOENIX_FLASH_MAP_ENTRY)) {
1901             // Last variable is bad, add the rest as padding and return
1902             UByteArray body = data.mid(offset);
1903             UString info = usprintf("Full size: %" PRIXQ "h (%" PRIuQ ")", body.size(), body.size());
1904 
1905             // Add padding tree item
1906             model->addItem(localOffset + offset, Types::Padding, getPaddingType(body), UString("Padding"), UString(), info, UByteArray(), body, UByteArray(), Fixed, index);
1907 
1908             // Show message
1909             if (unparsedSize < entryHeader->Size)
1910                 msg(usprintf("%s: next entry appears too big, added as padding", __FUNCTION__), index);
1911 
1912             break;
1913         }
1914 
1915         UString name = guidToUString(entryHeader->Guid);
1916 
1917         // Construct header
1918         UByteArray header = data.mid(offset, sizeof(PHOENIX_FLASH_MAP_ENTRY));
1919 
1920         // Add info
1921         UString info = UString("Entry GUID: ") + guidToUString(entryHeader->Guid, false) +
1922             usprintf("\nFull size: 24h (36)\nHeader size: 24h (36)\nBody size: 0h (0)\n"
1923                 "Entry type: %04Xh\nData type: %04Xh\nMemory address: %08llXh\nSize: %08Xh\nOffset: %08Xh",
1924                 entryHeader->EntryType,
1925                 entryHeader->DataType,
1926                 (unsigned long long)entryHeader->PhysicalAddress,
1927                 entryHeader->Size,
1928                 entryHeader->Offset);
1929 
1930         // Determine subtype
1931         UINT8 subtype = 0;
1932         switch (entryHeader->DataType) {
1933         case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_VOLUME:
1934             subtype = Subtypes::VolumeFlashMapEntry;
1935             break;
1936         case NVRAM_PHOENIX_FLASH_MAP_ENTRY_TYPE_DATA_BLOCK:
1937             subtype = Subtypes::DataFlashMapEntry;
1938             break;
1939         }
1940 
1941         // Add tree item
1942         model->addItem(localOffset + offset, Types::FlashMapEntry, subtype, name, flashMapGuidToUString(entryHeader->Guid), info, header, UByteArray(), UByteArray(), Fixed, index);
1943 
1944         // Move to next variable
1945         offset += sizeof(PHOENIX_FLASH_MAP_ENTRY);
1946         unparsedSize = dataSize - offset;
1947     }
1948 
1949     return U_SUCCESS;
1950 }
1951 #endif // U_ENABLE_NVRAM_PARSING_SUPPORT
1952