1 // UefiHandler.cpp
2
3 #include "StdAfx.h"
4
5 // #define SHOW_DEBUG_INFO
6
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #endif
10
11 #include "../../../C/7zCrc.h"
12 #include "../../../C/Alloc.h"
13 #include "../../../C/LzmaDec.h"
14 #include "../../../C/CpuArch.h"
15
16 #include "../../Common/AutoPtr.h"
17 #include "../../Common/ComTry.h"
18 #include "../../Common/IntToString.h"
19 #include "../../Common/MyBuffer.h"
20 #include "../../Common/StringConvert.h"
21
22 #include "../../Windows/PropVariantUtils.h"
23
24 #include "../Common/ProgressUtils.h"
25 #include "../Common/RegisterArc.h"
26 #include "../Common/StreamObjects.h"
27 #include "../Common/StreamUtils.h"
28
29 #include "../Compress/CopyCoder.h"
30 #include "../Compress/LzhDecoder.h"
31
32 #ifdef SHOW_DEBUG_INFO
33 #define PRF(x) x
34 #else
35 #define PRF(x)
36 #endif
37
38 #define Get16(p) GetUi16(p)
39 #define Get32(p) GetUi32(p)
40 #define Get64(p) GetUi64(p)
41 #define Get24(p) (Get32(p) & 0xFFFFFF)
42
43 namespace NArchive {
44 namespace NUefi {
45
46 static const size_t kBufTotalSizeMax = 1 << 29;
47 static const unsigned kNumFilesMax = 1 << 18;
48 static const unsigned kLevelMax = 64;
49
50 static const Byte k_IntelMeSignature[] =
51 {
52 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
53 0x5A, 0xA5, 0xF0, 0x0F
54 };
55
IsIntelMe(const Byte * p)56 static bool IsIntelMe(const Byte *p)
57 {
58 return memcmp(p, k_IntelMeSignature, sizeof(k_IntelMeSignature)) == 0;
59 }
60
61 static const unsigned kFvHeaderSize = 0x38;
62
63 static const unsigned kGuidSize = 16;
64
65 #define CAPSULE_SIGNATURE 0xBD,0x86,0x66,0x3B,0x76,0x0D,0x30,0x40,0xB7,0x0E,0xB5,0x51,0x9E,0x2F,0xC5,0xA0
66 #define CAPSULE2_SIGNATURE 0x8B,0xA6,0x3C,0x4A,0x23,0x77,0xFB,0x48,0x80,0x3D,0x57,0x8C,0xC1,0xFE,0xC4,0x4D
67 #define CAPSULE_UEFI_SIGNATURE 0xB9,0x82,0x91,0x53,0xB5,0xAB,0x91,0x43,0xB6,0x9A,0xE3,0xA9,0x43,0xF7,0x2F,0xCC
68 /*
69 6dcbd5ed-e82d-4c44-bda1-7194199ad92a : Firmware Management `
70 */
71
72 static const Byte k_Guids_Capsules[][kGuidSize] =
73 {
74 { CAPSULE_SIGNATURE },
75 { CAPSULE2_SIGNATURE },
76 { CAPSULE_UEFI_SIGNATURE }
77 };
78
79
80 static const unsigned kFfsGuidOffset = 16;
81
82 #define FFS1_SIGNATURE 0xD9,0x54,0x93,0x7A,0x68,0x04,0x4A,0x44,0x81,0xCE,0x0B,0xF6,0x17,0xD8,0x90,0xDF
83 #define FFS2_SIGNATURE 0x78,0xE5,0x8C,0x8C,0x3D,0x8A,0x1C,0x4F,0x99,0x35,0x89,0x61,0x85,0xC3,0x2D,0xD3
84 #define MACFS_SIGNATURE 0xAD,0xEE,0xAD,0x04,0xFF,0x61,0x31,0x4D,0xB6,0xBA,0x64,0xF8,0xBF,0x90,0x1F,0x5A
85 // APPLE_BOOT
86 /*
87 "FFS3": "5473c07a-3dcb-4dca-bd6f-1e9689e7349a",
88 "NVRAM_EVSA": "fff12b8d-7696-4c8b-a985-2747075b4f50",
89 "NVRAM_NVAR": "cef5b9a3-476d-497f-9fdc-e98143e0422c",
90 "NVRAM_EVSA2": "00504624-8a59-4eeb-bd0f-6b36e96128e0",
91 static const Byte k_NVRAM_NVAR_Guid[kGuidSize] =
92 { 0xA3,0xB9,0xF5,0xCE,0x6D,0x47,0x7F,0x49,0x9F,0xDC,0xE9,0x81,0x43,0xE0,0x42,0x2C };
93 */
94
95 static const Byte k_Guids_FS[][kGuidSize] =
96 {
97 { FFS1_SIGNATURE },
98 { FFS2_SIGNATURE },
99 { MACFS_SIGNATURE }
100 };
101
102
103 static const UInt32 kFvSignature = 0x4856465F; // "_FVH"
104
105 static const Byte kGuids[][kGuidSize] =
106 {
107 { 0xB0,0xCD,0x1B,0xFC,0x31,0x7D,0xAA,0x49,0x93,0x6A,0xA4,0x60,0x0D,0x9D,0xD0,0x83 },
108 { 0x2E,0x06,0xA0,0x1B,0x79,0xC7,0x82,0x45,0x85,0x66,0x33,0x6A,0xE8,0xF7,0x8F,0x09 },
109 { 0x25,0x4E,0x37,0x7E,0x01,0x8E,0xEE,0x4F,0x87,0xf2,0x39,0x0C,0x23,0xC6,0x06,0xCD },
110 { 0x97,0xE5,0x1B,0x16,0xC5,0xE9,0xDB,0x49,0xAE,0x50,0xC4,0x62,0xAB,0x54,0xEE,0xDA },
111 { 0xDB,0x7F,0xAD,0x77,0x2A,0xDF,0x02,0x43,0x88,0x98,0xC7,0x2E,0x4C,0xDB,0xD0,0xF4 },
112 { 0xAB,0x71,0xCF,0xF5,0x4B,0xB0,0x7E,0x4B,0x98,0x8A,0xD8,0xA0,0xD4,0x98,0xE6,0x92 },
113 { 0x91,0x45,0x53,0x7A,0xCE,0x37,0x81,0x48,0xB3,0xC9,0x71,0x38,0x14,0xF4,0x5D,0x6B },
114 { 0x84,0xE6,0x7A,0x36,0x5D,0x33,0x71,0x46,0xA1,0x6D,0x89,0x9D,0xBF,0xEA,0x6B,0x88 },
115 { 0x98,0x07,0x40,0x24,0x07,0x38,0x42,0x4A,0xB4,0x13,0xA1,0xEC,0xEE,0x20,0x5D,0xD8 },
116 { 0xEE,0xA2,0x3F,0x28,0x2C,0x53,0x4D,0x48,0x93,0x83,0x9F,0x93,0xB3,0x6F,0x0B,0x7E },
117 { 0x9B,0xD5,0xB8,0x98,0xBA,0xE8,0xEE,0x48,0x98,0xDD,0xC2,0x95,0x39,0x2F,0x1E,0xDB },
118 { 0x09,0x6D,0xE3,0xC3,0x94,0x82,0x97,0x4B,0xA8,0x57,0xD5,0x28,0x8F,0xE3,0x3E,0x28 },
119 { 0x18,0x88,0x53,0x4A,0xE0,0x5A,0xB2,0x4E,0xB2,0xEB,0x48,0x8B,0x23,0x65,0x70,0x22 }
120 };
121
122 static const Byte k_Guid_LZMA_COMPRESSED[kGuidSize] =
123 { 0x98,0x58,0x4E,0xEE,0x14,0x39,0x59,0x42,0x9D,0x6E,0xDC,0x7B,0xD7,0x94,0x03,0xCF };
124
125 static const char * const kGuidNames[] =
126 {
127 "CRC"
128 , "VolumeTopFile"
129 , "ACPI"
130 , "ACPI2"
131 , "Main"
132 , "Intel32"
133 , "Intel64"
134 , "Intel32c"
135 , "Intel64c"
136 , "MacVolume"
137 , "MacUpdate.txt"
138 , "MacName"
139 , "Insyde"
140 };
141
142 enum
143 {
144 kGuidIndex_CRC = 0
145 };
146
147 struct CSigExtPair
148 {
149 const char *ext;
150 unsigned sigSize;
151 Byte sig[16];
152 };
153
154 static const CSigExtPair g_Sigs[] =
155 {
156 { "bmp", 2, { 'B','M' } },
157 { "riff", 4, { 'R','I','F','F' } },
158 { "pe", 2, { 'M','Z'} },
159 { "gif", 6, { 'G','I','F','8','9', 'a' } },
160 { "png", 8, { 0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A } },
161 { "jpg", 10, { 0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46 } },
162 { "rom", 2, { 0x55,0xAA } }
163 };
164
165 enum
166 {
167 kSig_BMP,
168 kSig_RIFF,
169 kSig_PE
170 };
171
FindExt(const Byte * p,size_t size)172 static const char *FindExt(const Byte *p, size_t size)
173 {
174 unsigned i;
175 for (i = 0; i < Z7_ARRAY_SIZE(g_Sigs); i++)
176 {
177 const CSigExtPair &pair = g_Sigs[i];
178 if (size >= pair.sigSize)
179 if (memcmp(p, pair.sig, pair.sigSize) == 0)
180 break;
181 }
182 if (i == Z7_ARRAY_SIZE(g_Sigs))
183 return NULL;
184 switch (i)
185 {
186 case kSig_BMP:
187 if (GetUi32(p + 2) > size || GetUi32(p + 0xA) > size)
188 return NULL;
189 break;
190 case kSig_RIFF:
191 if (GetUi32(p + 8) == 0x45564157 || GetUi32(p + 0xC) == 0x20746D66 )
192 return "wav";
193 break;
194 case kSig_PE:
195 {
196 if (size < 512)
197 return NULL;
198 UInt32 peOffset = GetUi32(p + 0x3C);
199 if (peOffset >= 0x1000 || peOffset + 512 > size || (peOffset & 7) != 0)
200 return NULL;
201 if (GetUi32(p + peOffset) != 0x00004550)
202 return NULL;
203 break;
204 }
205 }
206 return g_Sigs[i].ext;
207 }
208
AreGuidsEq(const Byte * p1,const Byte * p2)209 static bool AreGuidsEq(const Byte *p1, const Byte *p2)
210 {
211 return memcmp(p1, p2, kGuidSize) == 0;
212 }
213
FindGuid(const Byte * p)214 static int FindGuid(const Byte *p)
215 {
216 for (unsigned i = 0; i < Z7_ARRAY_SIZE(kGuids); i++)
217 if (AreGuidsEq(p, kGuids[i]))
218 return (int)i;
219 return -1;
220 }
221
IsFfs(const Byte * p)222 static bool IsFfs(const Byte *p)
223 {
224 if (Get32(p + 0x28) != kFvSignature)
225 return false;
226 for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_Guids_FS); i++)
227 if (AreGuidsEq(p + kFfsGuidOffset, k_Guids_FS[i]))
228 return true;
229 return false;
230 }
231
232 #define FVB_ERASE_POLARITY (1 << 11)
233
234 /*
235 static const CUInt32PCharPair g_FV_Attribs[] =
236 {
237 { 0, "ReadDisabledCap" },
238 { 1, "ReadEnabledCap" },
239 { 2, "ReadEnabled" },
240 { 3, "WriteDisabledCap" },
241 { 4, "WriteEnabledCap" },
242 { 5, "WriteEnabled" },
243 { 6, "LockCap" },
244 { 7, "Locked" },
245
246 { 9, "StickyWrite" },
247 { 10, "MemoryMapped" },
248 { 11, "ErasePolarity" },
249
250 { 12, "ReadLockCap" },
251 { 13, "WriteLockCap" },
252 { 14, "WriteLockCap" }
253 };
254 */
255
256 enum
257 {
258 FV_FILETYPE_ALL,
259 FV_FILETYPE_RAW,
260 FV_FILETYPE_FREEFORM,
261 FV_FILETYPE_SECURITY_CORE,
262 FV_FILETYPE_PEI_CORE,
263 FV_FILETYPE_DXE_CORE,
264 FV_FILETYPE_PEIM,
265 FV_FILETYPE_DRIVER,
266 FV_FILETYPE_COMBINED_PEIM_DRIVER,
267 FV_FILETYPE_APPLICATION,
268 // The value 0x0A is reserved and should not be used
269 FV_FILETYPE_FIRMWARE_VOLUME_IMAGE = 0x0B,
270 // types 0xF0 - 0xFF are FFS file types
271 FV_FILETYPE_FFS_PAD = 0xF0
272 };
273
274 static const char * const g_FileTypes[] =
275 {
276 "ALL"
277 , "RAW"
278 , "FREEFORM"
279 , "SECURITY_CORE"
280 , "PEI_CORE"
281 , "DXE_CORE"
282 , "PEIM"
283 , "DRIVER"
284 , "COMBINED_PEIM_DRIVER"
285 , "APPLICATION"
286 , "0xA"
287 , "VOLUME"
288 };
289
290 // typedef Byte FFS_FILE_ATTRIBUTES;
291 // FFS File Attributes
292 #define FFS_ATTRIB_TAIL_PRESENT 0x01
293 // #define FFS_ATTRIB_RECOVERY 0x02
294 // #define FFS_ATTRIB_HEADER_EXTENSION 0x04
295 // #define FFS_ATTRIB_DATA_ALIGNMENT 0x38
296 #define FFS_ATTRIB_CHECKSUM 0x40
297
298 static const CUInt32PCharPair g_FFS_FILE_ATTRIBUTES[] =
299 {
300 { 0, "" /* "TAIL" */ },
301 { 1, "RECOVERY" },
302 // { 2, "HEADER_EXTENSION" }, // reserved for future
303 { 6, "" /* "CHECKSUM" */ }
304 };
305
306 // static const Byte g_Allignment[8] = { 3, 4, 7, 9, 10, 12, 15, 16 };
307
308 // typedef Byte FFS_FILE_STATE;
309
310 // Look also FVB_ERASE_POLARITY.
311 // Lower-order State bits are superceded by higher-order State bits.
312
313 // #define FILE_HEADER_CONSTRUCTION 0x01
314 // #define FILE_HEADER_VALID 0x02
315 #define FILE_DATA_VALID 0x04
316 // #define FILE_MARKED_FOR_UPDATE 0x08
317 // #define FILE_DELETED 0x10
318 // #define FILE_HEADER_INVALID 0x20
319
320 // SECTION_TYPE
321
322 // #define SECTION_ALL 0x00
323
324 #define SECTION_COMPRESSION 0x01
325 #define SECTION_GUID_DEFINED 0x02
326
327 // Leaf section Type values
328 // #define SECTION_PE32 0x10
329 // #define SECTION_PIC 0x11
330 // #define SECTION_TE 0x12
331 #define SECTION_DXE_DEPEX 0x13
332 #define SECTION_VERSION 0x14
333 #define SECTION_USER_INTERFACE 0x15
334 // #define SECTION_COMPATIBILITY16 0x16
335 #define SECTION_FIRMWARE_VOLUME_IMAGE 0x17
336 #define SECTION_FREEFORM_SUBTYPE_GUID 0x18
337 #define SECTION_RAW 0x19
338 #define SECTION_PEI_DEPEX 0x1B
339
340
341 // #define GUIDED_SECTION_PROCESSING_REQUIRED 0x01
342 // #define GUIDED_SECTION_AUTH_STATUS_VALID 0x02
343
344 static const CUInt32PCharPair g_GUIDED_SECTION_ATTRIBUTES[] =
345 {
346 { 0, "PROCESSING_REQUIRED" },
347 { 1, "AUTH" }
348 };
349
350 static const CUInt32PCharPair g_SECTION_TYPE[] =
351 {
352 { 0x01, "COMPRESSION" },
353 { 0x02, "GUID" },
354 { 0x10, "efi" },
355 { 0x11, "PIC" },
356 { 0x12, "te" },
357 { 0x13, "DXE_DEPEX" },
358 { 0x14, "VERSION" },
359 { 0x15, "USER_INTERFACE" },
360 { 0x16, "COMPATIBILITY16" },
361 { 0x17, "VOLUME" },
362 { 0x18, "FREEFORM_SUBTYPE_GUID" },
363 { 0x19, "raw" },
364 { 0x1B, "PEI_DEPEX" }
365 };
366
367 #define COMPRESSION_TYPE_NONE 0
368 #define COMPRESSION_TYPE_LZH 1
369 #define COMPRESSION_TYPE_LZMA 2
370
371 static const char * const g_Methods[] =
372 {
373 "COPY"
374 , "LZH"
375 , "LZMA"
376 };
377
378
AddGuid(AString & dest,const Byte * p,bool full)379 static void AddGuid(AString &dest, const Byte *p, bool full)
380 {
381 char s[64];
382 ::RawLeGuidToString(p, s);
383 // MyStringUpper_Ascii(s);
384 if (!full)
385 s[8] = 0;
386 dest += s;
387 }
388
389 static const char * const kExpressionCommands[] =
390 {
391 "BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"
392 };
393
ParseDepedencyExpression(const Byte * p,UInt32 size,AString & res)394 static bool ParseDepedencyExpression(const Byte *p, UInt32 size, AString &res)
395 {
396 res.Empty();
397 for (UInt32 i = 0; i < size;)
398 {
399 unsigned command = p[i++];
400 if (command > Z7_ARRAY_SIZE(kExpressionCommands))
401 return false;
402 res += kExpressionCommands[command];
403 if (command < 3)
404 {
405 if (i + kGuidSize > size)
406 return false;
407 res.Add_Space();
408 AddGuid(res, p + i, false);
409 i += kGuidSize;
410 }
411 res += "; ";
412 }
413 return true;
414 }
415
ParseUtf16zString(const Byte * p,UInt32 size,UString & res)416 static bool ParseUtf16zString(const Byte *p, UInt32 size, UString &res)
417 {
418 if ((size & 1) != 0)
419 return false;
420 res.Empty();
421 UInt32 i;
422 for (i = 0; i < size; i += 2)
423 {
424 wchar_t c = Get16(p + i);
425 if (c == 0)
426 break;
427 res += c;
428 }
429 return (i == size - 2);
430 }
431
ParseUtf16zString2(const Byte * p,UInt32 size,AString & res)432 static bool ParseUtf16zString2(const Byte *p, UInt32 size, AString &res)
433 {
434 UString s;
435 if (!ParseUtf16zString(p, size, s))
436 return false;
437 res = UnicodeStringToMultiByte(s);
438 return true;
439 }
440
441 #define FLAGS_TO_STRING(pairs, value) FlagsToString(pairs, Z7_ARRAY_SIZE(pairs), value)
442 #define TYPE_TO_STRING(table, value) TypeToString(table, Z7_ARRAY_SIZE(table), value)
443 #define TYPE_PAIR_TO_STRING(table, value) TypePairToString(table, Z7_ARRAY_SIZE(table), value)
444
445 static const UInt32 kFileHeaderSize = 24;
446
AddSpaceAndString(AString & res,const AString & newString)447 static void AddSpaceAndString(AString &res, const AString &newString)
448 {
449 if (!newString.IsEmpty())
450 {
451 res.Add_Space_if_NotEmpty();
452 res += newString;
453 }
454 }
455
456 class CFfsFileHeader
457 {
458 PRF(public:)
459 Byte CheckHeader;
460 Byte CheckFile;
461 Byte Attrib;
462 Byte State;
463
GetTailReference() const464 UInt16 GetTailReference() const { return (UInt16)(CheckHeader | ((UInt16)CheckFile << 8)); }
GetTailSize() const465 UInt32 GetTailSize() const { return IsThereTail() ? 2 : 0; }
IsThereFileChecksum() const466 bool IsThereFileChecksum() const { return (Attrib & FFS_ATTRIB_CHECKSUM) != 0; }
IsThereTail() const467 bool IsThereTail() const { return (Attrib & FFS_ATTRIB_TAIL_PRESENT) != 0; }
468 public:
469 Byte GuidName[kGuidSize];
470 Byte Type;
471 UInt32 Size;
472
Parse(const Byte * p)473 bool Parse(const Byte *p)
474 {
475 unsigned i;
476 for (i = 0; i < kFileHeaderSize; i++)
477 if (p[i] != 0xFF)
478 break;
479 if (i == kFileHeaderSize)
480 return false;
481 memcpy(GuidName, p, kGuidSize);
482 CheckHeader = p[0x10];
483 CheckFile = p[0x11];
484 Type = p[0x12];
485 Attrib = p[0x13];
486 Size = Get24(p + 0x14);
487 State = p[0x17];
488 return true;
489 }
490
GetDataSize() const491 UInt32 GetDataSize() const { return Size - kFileHeaderSize - GetTailSize(); }
GetDataSize2(UInt32 rem) const492 UInt32 GetDataSize2(UInt32 rem) const { return rem - kFileHeaderSize - GetTailSize(); }
493
Check(const Byte * p,UInt32 size)494 bool Check(const Byte *p, UInt32 size)
495 {
496 if (Size > size)
497 return false;
498 UInt32 tailSize = GetTailSize();
499 if (Size < kFileHeaderSize + tailSize)
500 return false;
501
502 {
503 unsigned checkSum = 0;
504 for (UInt32 i = 0; i < kFileHeaderSize; i++)
505 checkSum += p[i];
506 checkSum -= p[0x17];
507 checkSum -= p[0x11];
508 if ((Byte)checkSum != 0)
509 return false;
510 }
511
512 if (IsThereFileChecksum())
513 {
514 unsigned checkSum = 0;
515 UInt32 checkSize = Size - tailSize;
516 for (UInt32 i = 0; i < checkSize; i++)
517 checkSum += p[i];
518 checkSum -= p[0x17];
519 if ((Byte)checkSum != 0)
520 return false;
521 }
522
523 if (IsThereTail())
524 if (GetTailReference() != (UInt16)~Get16(p + Size - 2))
525 return false;
526
527 int polarity = 0;
528 int i;
529 for (i = 5; i >= 0; i--)
530 if (((State >> i) & 1) == polarity)
531 {
532 // AddSpaceAndString(s, g_FFS_FILE_STATE_Flags[i]);
533 if ((1 << i) != FILE_DATA_VALID)
534 return false;
535 break;
536 }
537 if (i < 0)
538 return false;
539
540 return true;
541 }
542
GetCharacts() const543 AString GetCharacts() const
544 {
545 AString s;
546 if (Type == FV_FILETYPE_FFS_PAD)
547 s += "PAD";
548 else
549 s += TYPE_TO_STRING(g_FileTypes, Type);
550 AddSpaceAndString(s, FLAGS_TO_STRING(g_FFS_FILE_ATTRIBUTES, Attrib & 0xC7));
551 /*
552 int align = (Attrib >> 3) & 7;
553 if (align != 0)
554 {
555 s += " Align:";
556 s.Add_UInt32((UInt32)1 << g_Allignment[align]);
557 }
558 */
559 return s;
560 }
561 };
562
563 #define G32(_offs_, dest) dest = Get32(p + (_offs_))
564 #define G16(_offs_, dest) dest = Get16(p + (_offs_))
565
566 struct CCapsuleHeader
567 {
568 UInt32 HeaderSize;
569 UInt32 Flags;
570 UInt32 CapsuleImageSize;
571 UInt32 SequenceNumber;
572 // Guid InstanceId;
573 UInt32 OffsetToSplitInformation;
574 UInt32 OffsetToCapsuleBody;
575 UInt32 OffsetToOemDefinedHeader;
576 UInt32 OffsetToAuthorInformation;
577 UInt32 OffsetToRevisionInformation;
578 UInt32 OffsetToShortDescription;
579 UInt32 OffsetToLongDescription;
580 UInt32 OffsetToApplicableDevices;
581
ClearNArchive::NUefi::CCapsuleHeader582 void Clear() { memset(this, 0, sizeof(*this)); }
583
ParseNArchive::NUefi::CCapsuleHeader584 bool Parse(const Byte *p)
585 {
586 Clear();
587 G32(0x10, HeaderSize);
588 G32(0x14, Flags);
589 G32(0x18, CapsuleImageSize);
590 if (HeaderSize < 0x1C)
591 return false;
592 if (AreGuidsEq(p, k_Guids_Capsules[0]))
593 {
594 const unsigned kHeaderSize = 80;
595 if (HeaderSize != kHeaderSize)
596 return false;
597 G32(0x1C, SequenceNumber);
598 G32(0x30, OffsetToSplitInformation);
599 G32(0x34, OffsetToCapsuleBody);
600 G32(0x38, OffsetToOemDefinedHeader);
601 G32(0x3C, OffsetToAuthorInformation);
602 G32(0x40, OffsetToRevisionInformation);
603 G32(0x44, OffsetToShortDescription);
604 G32(0x48, OffsetToLongDescription);
605 G32(0x4C, OffsetToApplicableDevices);
606 return true;
607 }
608 else if (AreGuidsEq(p, k_Guids_Capsules[1]))
609 {
610 // capsule v2
611 G16(0x1C, OffsetToCapsuleBody);
612 G16(0x1E, OffsetToOemDefinedHeader);
613 return true;
614 }
615 else if (AreGuidsEq(p, k_Guids_Capsules[2]))
616 {
617 OffsetToCapsuleBody = HeaderSize;
618 return true;
619 }
620 else
621 {
622 // here we must check for another capsule types
623 return false;
624 }
625 }
626 };
627
628
629 struct CItem
630 {
631 AString Name;
632 AString Characts;
633 int Parent;
634 int Method;
635 int NameIndex;
636 unsigned NumChilds;
637 bool IsDir;
638 bool Skip;
639 bool ThereAreSubDirs;
640 bool ThereIsUniqueName;
641 bool KeepName;
642
643 unsigned BufIndex;
644 UInt32 Offset;
645 UInt32 Size;
646
CItemNArchive::NUefi::CItem647 CItem(): Parent(-1), Method(-1), NameIndex(-1), NumChilds(0),
648 IsDir(false), Skip(false), ThereAreSubDirs(false), ThereIsUniqueName(false), KeepName(true) {}
649 void SetGuid(const Byte *guidName, bool full = false);
650 AString GetName(int numChildsInParent) const;
651 };
652
SetGuid(const Byte * guidName,bool full)653 void CItem::SetGuid(const Byte *guidName, bool full)
654 {
655 ThereIsUniqueName = true;
656 int index = FindGuid(guidName);
657 if (index >= 0)
658 Name = kGuidNames[(unsigned)index];
659 else
660 {
661 Name.Empty();
662 AddGuid(Name, guidName, full);
663 }
664 }
665
GetName(int numChildsInParent) const666 AString CItem::GetName(int numChildsInParent) const
667 {
668 if (numChildsInParent <= 1 || NameIndex < 0)
669 return Name;
670 char sz[32];
671 char sz2[32];
672 ConvertUInt32ToString((unsigned)NameIndex, sz);
673 ConvertUInt32ToString((unsigned)numChildsInParent - 1, sz2);
674 const int numZeros = (int)strlen(sz2) - (int)strlen(sz);
675 AString res;
676 for (int i = 0; i < numZeros; i++)
677 res += '0';
678 res += sz;
679 res.Add_Dot();
680 res += Name;
681 return res;
682 }
683
684 struct CItem2
685 {
686 AString Name;
687 AString Characts;
688 unsigned MainIndex;
689 int Parent;
690
CItem2NArchive::NUefi::CItem2691 CItem2(): Parent(-1) {}
692 };
693
694
695 Z7_CLASS_IMP_CHandler_IInArchive_1(
696 IInArchiveGetStream
697 )
698 CObjectVector<CItem> _items;
699 CObjectVector<CItem2> _items2;
700 CObjectVector<CByteBuffer> _bufs;
701 UString _comment;
702 UInt32 _methodsMask;
703 bool _capsuleMode;
704 bool _headersError;
705
706 size_t _totalBufsSize;
707 CCapsuleHeader _h;
708 UInt64 _phySize;
709
710 void AddCommentString(const char *name, UInt32 pos);
711 unsigned AddItem(const CItem &item);
712 unsigned AddFileItemWithIndex(CItem &item);
713 unsigned AddDirItem(CItem &item);
714 unsigned AddBuf(size_t size);
715
716 HRESULT DecodeLzma(const Byte *data, size_t inputSize);
717
718 HRESULT ParseSections(unsigned bufIndex, UInt32 pos,
719 UInt32 size,
720 int parent, int method, unsigned level,
721 bool &error);
722
723 HRESULT ParseIntelMe(unsigned bufIndex, UInt32 posBase,
724 UInt32 exactSize, UInt32 limitSize,
725 int parent, int method, unsigned level);
726
727 HRESULT ParseVolume(unsigned bufIndex, UInt32 posBase,
728 UInt32 exactSize, UInt32 limitSize,
729 int parent, int method, unsigned level);
730
731 HRESULT OpenCapsule(IInStream *stream);
732 HRESULT OpenFv(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
733 HRESULT Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback);
734 public:
735 CHandler(bool capsuleMode): _capsuleMode(capsuleMode) {}
736 };
737
738
739 static const Byte kProps[] =
740 {
741 kpidPath,
742 kpidIsDir,
743 kpidSize,
744 // kpidOffset,
745 kpidMethod,
746 kpidCharacts
747 };
748
749 static const Byte kArcProps[] =
750 {
751 kpidComment,
752 kpidMethod,
753 kpidCharacts
754 };
755
756 IMP_IInArchive_Props
757 IMP_IInArchive_ArcProps
758
759 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
760 {
761 COM_TRY_BEGIN
762 NWindows::NCOM::CPropVariant prop;
763 const CItem2 &item2 = _items2[index];
764 const CItem &item = _items[item2.MainIndex];
765 switch (propID)
766 {
767 case kpidPath:
768 {
769 AString path (item2.Name);
770 int cur = item2.Parent;
771 while (cur >= 0)
772 {
773 const CItem2 &item3 = _items2[cur];
774 path.InsertAtFront(CHAR_PATH_SEPARATOR);
775 path.Insert(0, item3.Name);
776 cur = item3.Parent;
777 }
778 prop = path;
779 break;
780 }
781 case kpidIsDir: prop = item.IsDir; break;
782 case kpidMethod: if (item.Method >= 0) prop = g_Methods[(unsigned)item.Method]; break;
783 case kpidCharacts: if (!item2.Characts.IsEmpty()) prop = item2.Characts; break;
784 case kpidSize: if (!item.IsDir) prop = (UInt64)item.Size; break;
785 // case kpidOffset: if (!item.IsDir) prop = item.Offset; break;
786 }
787 prop.Detach(value);
788 return S_OK;
789 COM_TRY_END
790 }
791
792 void CHandler::AddCommentString(const char *name, UInt32 pos)
793 {
794 UString s;
795 if (pos < _h.HeaderSize)
796 return;
797 if (pos >= _h.OffsetToCapsuleBody)
798 return;
799 UInt32 limit = (_h.OffsetToCapsuleBody - pos) & ~(UInt32)1;
800 const Byte *buf = _bufs[0] + pos;
801 for (UInt32 i = 0;;)
802 {
803 if (s.Len() > (1 << 16) || i >= limit)
804 return;
805 wchar_t c = Get16(buf + i);
806 i += 2;
807 if (c == 0)
808 {
809 if (i >= limit)
810 return;
811 c = Get16(buf + i);
812 i += 2;
813 if (c == 0)
814 break;
815 s.Add_LF();
816 }
817 s += c;
818 }
819 if (s.IsEmpty())
820 return;
821 _comment.Add_LF();
822 _comment += name;
823 _comment += ": ";
824 _comment += s;
825 }
826
827 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
828 {
829 COM_TRY_BEGIN
830 NWindows::NCOM::CPropVariant prop;
831 switch (propID)
832 {
833 case kpidMethod:
834 {
835 AString s;
836 for (unsigned i = 0; i < 32; i++)
837 if ((_methodsMask & ((UInt32)1 << i)) != 0)
838 AddSpaceAndString(s, (AString)g_Methods[i]);
839 if (!s.IsEmpty())
840 prop = s;
841 break;
842 }
843 case kpidComment: if (!_comment.IsEmpty()) prop = _comment; break;
844 case kpidPhySize: prop = (UInt64)_phySize; break;
845
846 case kpidErrorFlags:
847 {
848 UInt32 v = 0;
849 if (_headersError) v |= kpv_ErrorFlags_HeadersError;
850 if (v != 0)
851 prop = v;
852 break;
853 }
854 }
855 prop.Detach(value);
856 return S_OK;
857 COM_TRY_END
858 }
859
860 #ifdef SHOW_DEBUG_INFO
861 static void PrintLevel(unsigned level)
862 {
863 PRF(printf("\n"));
864 for (unsigned i = 0; i < level; i++)
865 PRF(printf(" "));
866 }
867 static void MyPrint(UInt32 posBase, UInt32 size, unsigned level, const char *name)
868 {
869 PrintLevel(level);
870 PRF(printf("%s, pos = %6x, size = %6x", name, posBase, size));
871 }
872 #else
873 #define PrintLevel(level)
874 #define MyPrint(posBase, size, level, name)
875 #endif
876
877
878
879 unsigned CHandler::AddItem(const CItem &item)
880 {
881 if (_items.Size() >= kNumFilesMax)
882 throw 2;
883 return _items.Add(item);
884 }
885
886 unsigned CHandler::AddFileItemWithIndex(CItem &item)
887 {
888 unsigned nameIndex = _items.Size();
889 if (item.Parent >= 0)
890 nameIndex = _items[item.Parent].NumChilds++;
891 item.NameIndex = (int)nameIndex;
892 return AddItem(item);
893 }
894
895 unsigned CHandler::AddDirItem(CItem &item)
896 {
897 if (item.Parent >= 0)
898 _items[item.Parent].ThereAreSubDirs = true;
899 item.IsDir = true;
900 item.Size = 0;
901 return AddItem(item);
902 }
903
904 unsigned CHandler::AddBuf(size_t size)
905 {
906 if (size > kBufTotalSizeMax - _totalBufsSize)
907 throw 1;
908 _totalBufsSize += size;
909 unsigned index = _bufs.Size();
910 _bufs.AddNew().Alloc(size);
911 return index;
912 }
913
914
915 HRESULT CHandler::DecodeLzma(const Byte *data, size_t inputSize)
916 {
917 if (inputSize < 5 + 8)
918 return S_FALSE;
919 const UInt64 unpackSize = Get64(data + 5);
920 if (unpackSize > ((UInt32)1 << 30))
921 return S_FALSE;
922 SizeT destLen = (SizeT)unpackSize;
923 const unsigned newBufIndex = AddBuf((size_t)unpackSize);
924 CByteBuffer &buf = _bufs[newBufIndex];
925 ELzmaStatus status;
926 SizeT srcLen = inputSize - (5 + 8);
927 const SizeT srcLen2 = srcLen;
928 SRes res = LzmaDecode(buf, &destLen, data + 13, &srcLen,
929 data, 5, LZMA_FINISH_END, &status, &g_Alloc);
930 if (res != 0)
931 return S_FALSE;
932 if (srcLen != srcLen2 || destLen != unpackSize || (
933 status != LZMA_STATUS_FINISHED_WITH_MARK &&
934 status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
935 return S_FALSE;
936 return S_OK;
937 }
938
939
940 HRESULT CHandler::ParseSections(unsigned bufIndex, UInt32 posBase, UInt32 size, int parent, int method, unsigned level, bool &error)
941 {
942 error = false;
943
944 if (level > kLevelMax)
945 return S_FALSE;
946 MyPrint(posBase, size, level, "Sections");
947 level++;
948 const Byte *bufData = _bufs[bufIndex];
949 UInt32 pos = 0;
950 for (;;)
951 {
952 if (size == pos)
953 return S_OK;
954 PrintLevel(level);
955 PRF(printf("%s, abs = %6x, relat = %6x", "Sect", posBase + pos, pos));
956 pos = (pos + 3) & ~(UInt32)3;
957 if (pos > size)
958 return S_FALSE;
959 UInt32 rem = size - pos;
960 if (rem == 0)
961 return S_OK;
962 if (rem < 4)
963 return S_FALSE;
964
965 const Byte *p = bufData + posBase + pos;
966
967 const UInt32 sectSize = Get24(p);
968 const Byte type = p[3];
969
970 // PrintLevel(level);
971 PRF(printf(" type = %2x, sectSize = %6x", type, sectSize));
972
973 if (sectSize > rem || sectSize < 4)
974 {
975 _headersError = true;
976 error = true;
977 return S_OK;
978 // return S_FALSE;
979 }
980
981 CItem item;
982 item.Method = method;
983 item.BufIndex = bufIndex;
984 item.Parent = parent;
985 item.Offset = posBase + pos + 4;
986 UInt32 sectDataSize = sectSize - 4;
987 item.Size = sectDataSize;
988 item.Name = TYPE_PAIR_TO_STRING(g_SECTION_TYPE, type);
989
990 if (type == SECTION_COMPRESSION)
991 {
992 if (sectSize < 4 + 5)
993 return S_FALSE;
994 const UInt32 uncompressedSize = Get32(p + 4);
995 const Byte compressionType = p[8];
996
997 UInt32 newSectSize = sectSize - 9;
998 UInt32 newOffset = posBase + pos + 9;
999 const Byte *pStart = p + 9;
1000
1001 item.KeepName = false;
1002 if (compressionType > 2)
1003 {
1004 // AddFileItemWithIndex(item);
1005 return S_FALSE;
1006 }
1007 else
1008 {
1009 item.Name = g_Methods[compressionType];
1010 // int parent = AddDirItem(item);
1011 if (compressionType == COMPRESSION_TYPE_NONE)
1012 {
1013 bool error2;
1014 RINOK(ParseSections(bufIndex, newOffset, newSectSize, parent, method, level, error2))
1015 }
1016 else if (compressionType == COMPRESSION_TYPE_LZH)
1017 {
1018 const unsigned newBufIndex = AddBuf(uncompressedSize);
1019 CByteBuffer &buf = _bufs[newBufIndex];
1020 CMyUniquePtr<NCompress::NLzh::NDecoder::CCoder> lzhDecoder;
1021 lzhDecoder.Create_if_Empty();
1022 {
1023 const Byte *src = pStart;
1024 if (newSectSize < 8)
1025 return S_FALSE;
1026 UInt32 packSize = Get32(src);
1027 const UInt32 unpackSize = Get32(src + 4);
1028
1029 PRF(printf(" LZH packSize = %6x, unpackSize = %6x", packSize, unpackSize));
1030
1031 if (uncompressedSize != unpackSize || newSectSize - 8 != packSize)
1032 return S_FALSE;
1033 if (packSize < 1)
1034 return S_FALSE;
1035 packSize--;
1036 src += 8;
1037 if (src[packSize] != 0)
1038 return S_FALSE;
1039
1040 CMyComPtr2_Create<IInStream, CBufInStream> inStream;
1041 CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> outStream;
1042 // const UInt64 uncompressedSize64 = uncompressedSize;
1043 // lzhDecoder->FinishMode = true;
1044 /*
1045 EFI 1.1 probably used LZH with small dictionary and (pbit = 4). It was named "Efi compression".
1046 New version of compression code (named Tiano) uses LZH with (1 << 19) dictionary.
1047 But maybe LZH decoder in UEFI decoder supports larger than (1 << 19) dictionary.
1048 We check both LZH versions: Tiano and then Efi.
1049 */
1050 HRESULT res = S_FALSE;
1051 for (unsigned m = 0 ; m < 2; m++)
1052 {
1053 inStream->Init(src, packSize);
1054 outStream->Init(buf, uncompressedSize);
1055 lzhDecoder->SetDictSize(m == 0 ? ((UInt32)1 << 19) : ((UInt32)1 << 14));
1056 res = lzhDecoder->Code(inStream, outStream, uncompressedSize, NULL);
1057 if (res == S_OK)
1058 break;
1059 }
1060 RINOK(res)
1061 }
1062 bool error2;
1063 RINOK(ParseSections(newBufIndex, 0, uncompressedSize, parent, compressionType, level, error2))
1064 }
1065 else
1066 {
1067 if (newSectSize < 4 + 5 + 8)
1068 return S_FALSE;
1069 unsigned addSize = 4;
1070 if (pStart[0] == 0x5d && pStart[1] == 0 && pStart[2] == 0 && pStart[3] == 0x80 && pStart[4] == 0)
1071 {
1072 addSize = 0;
1073 // some archives have such header
1074 }
1075 else
1076 {
1077 // normal BIOS contains uncompressed size here
1078 // UInt32 uncompressedSize2 = Get24(pStart);
1079 // Byte firstSectType = p[9 + 3];
1080 // firstSectType can be 0 in some archives
1081 }
1082 pStart += addSize;
1083
1084 RINOK(DecodeLzma(pStart, newSectSize - addSize))
1085 const size_t lzmaUncompressedSize = _bufs.Back().Size();
1086 // if (lzmaUncompressedSize != uncompressedSize)
1087 if (lzmaUncompressedSize < uncompressedSize)
1088 return S_FALSE;
1089 bool error2;
1090 RINOK(ParseSections(_bufs.Size() - 1, 0, (UInt32)lzmaUncompressedSize, parent, compressionType, level, error2))
1091 }
1092 _methodsMask |= (1 << compressionType);
1093 }
1094 }
1095 else if (type == SECTION_GUID_DEFINED)
1096 {
1097 const unsigned kHeaderSize = 4 + kGuidSize + 4;
1098 if (sectSize < kHeaderSize)
1099 return S_FALSE;
1100 item.SetGuid(p + 4);
1101 const UInt32 dataOffset = Get16(p + 4 + kGuidSize);
1102 const UInt32 attrib = Get16(p + 4 + kGuidSize + 2);
1103 if (dataOffset > sectSize || dataOffset < kHeaderSize)
1104 return S_FALSE;
1105 UInt32 newSectSize = sectSize - dataOffset;
1106 item.Size = newSectSize;
1107 UInt32 newOffset = posBase + pos + dataOffset;
1108 item.Offset = newOffset;
1109 const UInt32 propsSize = dataOffset - kHeaderSize;
1110 AddSpaceAndString(item.Characts, FLAGS_TO_STRING(g_GUIDED_SECTION_ATTRIBUTES, attrib));
1111
1112 bool needDir = true;
1113 unsigned newBufIndex = bufIndex;
1114 int newMethod = method;
1115
1116 if (AreGuidsEq(p + 0x4, k_Guid_LZMA_COMPRESSED))
1117 {
1118 // item.Name = "guid.lzma";
1119 // AddItem(item);
1120 const Byte *pStart = bufData + newOffset;
1121 // do we need correct pStart here for lzma steram offset?
1122 RINOK(DecodeLzma(pStart, newSectSize))
1123 _methodsMask |= (1 << COMPRESSION_TYPE_LZMA);
1124 newBufIndex = _bufs.Size() - 1;
1125 newOffset = 0;
1126 newSectSize = (UInt32)_bufs.Back().Size();
1127 newMethod = COMPRESSION_TYPE_LZMA;
1128 }
1129 else if (AreGuidsEq(p + 0x4, kGuids[kGuidIndex_CRC]) && propsSize == 4)
1130 {
1131 needDir = false;
1132 item.KeepName = false;
1133 if (CrcCalc(bufData + newOffset, newSectSize) != Get32(p + kHeaderSize))
1134 return S_FALSE;
1135 }
1136 else
1137 {
1138 if (propsSize != 0)
1139 {
1140 CItem item2 = item;
1141 item2.Name += ".prop";
1142 item2.Size = propsSize;
1143 item2.Offset = posBase + pos + kHeaderSize;
1144 AddItem(item2);
1145 }
1146 }
1147
1148 int newParent = parent;
1149 if (needDir)
1150 newParent = (int)AddDirItem(item);
1151 bool error2;
1152 RINOK(ParseSections(newBufIndex, newOffset, newSectSize, newParent, newMethod, level, error2))
1153 }
1154 else if (type == SECTION_FIRMWARE_VOLUME_IMAGE)
1155 {
1156 item.KeepName = false;
1157 const int newParent = (int)AddDirItem(item);
1158 RINOK(ParseVolume(bufIndex, posBase + pos + 4,
1159 sectSize - 4,
1160 sectSize - 4,
1161 newParent, method, level))
1162 }
1163 else
1164 {
1165 bool needAdd = true;
1166 switch (type)
1167 {
1168 case SECTION_RAW:
1169 {
1170 const UInt32 kInsydeOffset = 12;
1171 if (sectDataSize >= kFvHeaderSize + kInsydeOffset)
1172 {
1173 if (IsFfs(p + 4 + kInsydeOffset) &&
1174 sectDataSize - kInsydeOffset == Get64(p + 4 + kInsydeOffset + 0x20))
1175 {
1176 needAdd = false;
1177 item.Name = "vol";
1178 const unsigned newParent = AddDirItem(item);
1179 RINOK(ParseVolume(bufIndex, posBase + pos + 4 + kInsydeOffset,
1180 sectDataSize - kInsydeOffset,
1181 sectDataSize - kInsydeOffset,
1182 (int)newParent, method, level))
1183 }
1184
1185 if (needAdd)
1186 {
1187 const char *ext = FindExt(p + 4, sectDataSize);
1188 if (ext)
1189 item.Name = ext;
1190 }
1191 }
1192 break;
1193 }
1194 case SECTION_DXE_DEPEX:
1195 case SECTION_PEI_DEPEX:
1196 {
1197 AString s;
1198 if (ParseDepedencyExpression(p + 4, sectDataSize, s))
1199 {
1200 if (s.Len() < (1 << 9))
1201 {
1202 s.InsertAtFront('[');
1203 s += ']';
1204 AddSpaceAndString(_items[item.Parent].Characts, s);
1205 needAdd = false;
1206 }
1207 else
1208 {
1209 item.BufIndex = AddBuf(s.Len());
1210 CByteBuffer &buf0 = _bufs[item.BufIndex];
1211 if (s.Len() != 0)
1212 memcpy(buf0, s, s.Len());
1213 item.Offset = 0;
1214 item.Size = s.Len();
1215 }
1216 }
1217 break;
1218 }
1219 case SECTION_VERSION:
1220 {
1221 if (sectDataSize > 2)
1222 {
1223 AString s;
1224 if (ParseUtf16zString2(p + 6, sectDataSize - 2, s))
1225 {
1226 AString s2 ("ver:");
1227 s2.Add_UInt32(Get16(p + 4));
1228 s2.Add_Space();
1229 s2 += s;
1230 AddSpaceAndString(_items[item.Parent].Characts, s2);
1231 needAdd = false;
1232 }
1233 }
1234 break;
1235 }
1236 case SECTION_USER_INTERFACE:
1237 {
1238 AString s;
1239 if (ParseUtf16zString2(p + 4, sectDataSize, s))
1240 {
1241 _items[parent].Name = s;
1242 needAdd = false;
1243 }
1244 break;
1245 }
1246 case SECTION_FREEFORM_SUBTYPE_GUID:
1247 {
1248 if (sectDataSize >= kGuidSize)
1249 {
1250 item.SetGuid(p + 4);
1251 item.Size = sectDataSize - kGuidSize;
1252 item.Offset = posBase + pos + 4 + kGuidSize;
1253 }
1254 break;
1255 }
1256 }
1257
1258 if (needAdd)
1259 AddFileItemWithIndex(item);
1260 }
1261
1262 pos += sectSize;
1263 }
1264 }
1265
1266 static UInt32 Count_FF_Bytes(const Byte *p, UInt32 size)
1267 {
1268 UInt32 i;
1269 for (i = 0; i < size && p[i] == 0xFF; i++);
1270 return i;
1271 }
1272
1273 static bool Is_FF_Stream(const Byte *p, UInt32 size)
1274 {
1275 return (Count_FF_Bytes(p, size) == size);
1276 }
1277
1278 struct CVolFfsHeader
1279 {
1280 UInt32 HeaderLen;
1281 UInt64 VolSize;
1282
1283 bool Parse(const Byte *p);
1284 };
1285
1286 bool CVolFfsHeader::Parse(const Byte *p)
1287 {
1288 if (Get32(p + 0x28) != kFvSignature)
1289 return false;
1290
1291 UInt32 attribs = Get32(p + 0x2C);
1292 if ((attribs & FVB_ERASE_POLARITY) == 0)
1293 return false;
1294 VolSize = Get64(p + 0x20);
1295 HeaderLen = Get16(p + 0x30);
1296 if (HeaderLen < kFvHeaderSize || (HeaderLen & 0x7) != 0 || VolSize < HeaderLen)
1297 return false;
1298 return true;
1299 }
1300
1301
1302 HRESULT CHandler::ParseVolume(
1303 unsigned bufIndex, UInt32 posBase,
1304 UInt32 exactSize, UInt32 limitSize,
1305 int parent, int method, unsigned level)
1306 {
1307 if (level > kLevelMax)
1308 return S_FALSE;
1309 MyPrint(posBase, exactSize, level, "Volume");
1310 level++;
1311 if (exactSize < kFvHeaderSize)
1312 return S_FALSE;
1313 const Byte *p = _bufs[bufIndex] + posBase;
1314 // first 16 bytes must be zeros, but they are not zeros sometimes.
1315 if (!IsFfs(p))
1316 {
1317 CItem item;
1318 item.Method = method;
1319 item.BufIndex = bufIndex;
1320 item.Parent = parent;
1321 item.Offset = posBase;
1322 item.Size = exactSize;
1323 if (!Is_FF_Stream(p + kFfsGuidOffset, 16))
1324 item.SetGuid(p + kFfsGuidOffset);
1325 // if (item.Name.IsEmpty())
1326 item.Name += "[VOL]";
1327 AddItem(item);
1328 return S_OK;
1329 }
1330
1331 CVolFfsHeader ffsHeader;
1332 if (!ffsHeader.Parse(p))
1333 return S_FALSE;
1334 // if (parent >= 0) AddSpaceAndString(_items[parent].Characts, FLAGS_TO_STRING(g_FV_Attribs, attribs));
1335
1336 // VolSize > exactSize (fh.Size) for some UEFI archives (is it correct UEFI?)
1337 // so we check VolSize for limitSize instead.
1338
1339 if (ffsHeader.HeaderLen > limitSize || ffsHeader.VolSize > limitSize)
1340 return S_FALSE;
1341
1342 {
1343 UInt32 checkCalc = 0;
1344 for (UInt32 i = 0; i < ffsHeader.HeaderLen; i += 2)
1345 checkCalc += Get16(p + i);
1346 if ((checkCalc & 0xFFFF) != 0)
1347 return S_FALSE;
1348 }
1349
1350 // 3 reserved bytes are not zeros sometimes.
1351 // UInt16 ExtHeaderOffset; // in new SPECIFICATION?
1352 // Byte revision = p[0x37];
1353
1354 UInt32 pos = kFvHeaderSize;
1355 for (;;)
1356 {
1357 if (pos >= ffsHeader.HeaderLen)
1358 return S_FALSE;
1359 UInt32 numBlocks = Get32(p + pos);
1360 UInt32 length = Get32(p + pos + 4);
1361 pos += 8;
1362 if (numBlocks == 0 && length == 0)
1363 break;
1364 }
1365 if (pos != ffsHeader.HeaderLen)
1366 return S_FALSE;
1367
1368 CRecordVector<UInt32> guidsVector;
1369
1370 for (;;)
1371 {
1372 UInt32 rem = (UInt32)ffsHeader.VolSize - pos;
1373 if (rem < kFileHeaderSize)
1374 break;
1375 pos = (pos + 7) & ~7u;
1376 rem = (UInt32)ffsHeader.VolSize - pos;
1377 if (rem < kFileHeaderSize)
1378 break;
1379
1380 CItem item;
1381 item.Method = method;
1382 item.BufIndex = bufIndex;
1383 item.Parent = parent;
1384
1385 const Byte *pFile = p + pos;
1386 CFfsFileHeader fh;
1387 if (!fh.Parse(pFile))
1388 {
1389 UInt32 num_FF_bytes = Count_FF_Bytes(pFile, rem);
1390 if (num_FF_bytes != rem)
1391 {
1392 item.Name = "[junk]";
1393 item.Offset = posBase + pos + num_FF_bytes;
1394 item.Size = rem - num_FF_bytes;
1395 AddItem(item);
1396 }
1397 PrintLevel(level); PRF(printf("== FF FF reminder"));
1398
1399 break;
1400 }
1401
1402 PrintLevel(level); PRF(printf("%s, type = %3d, pos = %7x, size = %7x", "FILE", fh.Type, posBase + pos, fh.Size));
1403
1404 if (!fh.Check(pFile, rem))
1405 return S_FALSE;
1406
1407 UInt32 offset = posBase + pos + kFileHeaderSize;
1408 UInt32 sectSize = fh.GetDataSize();
1409 item.Offset = offset;
1410 item.Size = sectSize;
1411
1412 pos += fh.Size;
1413
1414 if (fh.Type == FV_FILETYPE_FFS_PAD)
1415 if (Is_FF_Stream(pFile + kFileHeaderSize, sectSize))
1416 continue;
1417
1418 UInt32 guid32 = Get32(fh.GuidName);
1419 bool full = true;
1420 if (guidsVector.FindInSorted(guid32) < 0)
1421 {
1422 guidsVector.AddToUniqueSorted(guid32);
1423 full = false;
1424 }
1425 item.SetGuid(fh.GuidName, full);
1426
1427 item.Characts = fh.GetCharacts();
1428 // PrintLevel(level);
1429 PRF(printf(" : %s", item.Characts));
1430
1431 {
1432 PRF(printf(" attrib = %2d State = %3d ", (unsigned)fh.Attrib, (unsigned)fh.State));
1433 PRF(char s[64]);
1434 PRF(RawLeGuidToString(fh.GuidName, s));
1435 PRF(printf(" : %s ", s));
1436 }
1437
1438 if (fh.Type == FV_FILETYPE_FFS_PAD ||
1439 fh.Type == FV_FILETYPE_RAW)
1440 {
1441 bool isVolume = false;
1442 if (fh.Type == FV_FILETYPE_RAW)
1443 {
1444 if (sectSize >= kFvHeaderSize)
1445 if (IsFfs(pFile + kFileHeaderSize))
1446 isVolume = true;
1447 }
1448 if (isVolume)
1449 {
1450 const unsigned newParent = AddDirItem(item);
1451 const UInt32 limSize = fh.GetDataSize2(rem);
1452 // volume.VolSize > fh.Size for some UEFI archives (is it correct UEFI?)
1453 // so we will check VolSize for limitSize instead.
1454 RINOK(ParseVolume(bufIndex, offset, sectSize, limSize, (int)newParent, method, level))
1455 }
1456 else
1457 AddItem(item);
1458 }
1459 else
1460 {
1461 /*
1462 if (fh.Type == FV_FILETYPE_FREEFORM)
1463 {
1464 // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1465 // AddItem(item);
1466 }
1467 else
1468 */
1469 {
1470 const unsigned newParent = AddDirItem(item);
1471 bool error2;
1472 RINOK(ParseSections(bufIndex, offset, sectSize, (int)newParent, method, level + 1, error2))
1473 if (error2)
1474 {
1475 // in intel bio example: one FV_FILETYPE_FREEFORM file is wav file (not sections)
1476 item.IsDir = false;
1477 item.Size = sectSize;
1478 item.Name.Insert(0, "[ERROR]");
1479 AddItem(item);
1480 }
1481 }
1482 }
1483 }
1484
1485 return S_OK;
1486 }
1487
1488
1489 static const char * const kRegionName[] =
1490 {
1491 "Descriptor"
1492 , "BIOS"
1493 , "ME"
1494 , "GbE"
1495 , "PDR"
1496 , "Region5"
1497 , "Region6"
1498 , "Region7"
1499 };
1500
1501
1502 HRESULT CHandler::ParseIntelMe(
1503 unsigned bufIndex, UInt32 posBase,
1504 UInt32 exactSize, UInt32 limitSize,
1505 int parent, int method, unsigned /* level */)
1506 {
1507 UNUSED_VAR(limitSize)
1508 // level++;
1509
1510 const Byte *p = _bufs[bufIndex] + posBase;
1511 if (exactSize < 16 + 16)
1512 return S_FALSE;
1513 if (!IsIntelMe(p))
1514 return S_FALSE;
1515
1516 UInt32 v0 = GetUi32(p + 20);
1517 // UInt32 numRegions = (v0 >> 24) & 0x7;
1518 UInt32 regAddr = (v0 >> 12) & 0xFF0;
1519 // UInt32 numComps = (v0 >> 8) & 0x3;
1520 // UInt32 fcba = (v0 << 4) & 0xFF0;
1521
1522 // (numRegions == 0) in header in some new images.
1523 // So we don't use the value from header
1524 UInt32 numRegions = 7;
1525
1526 for (unsigned i = 0; i <= numRegions; i++)
1527 {
1528 UInt32 offset = regAddr + i * 4;
1529 if (offset + 4 > exactSize)
1530 break;
1531 UInt32 val = GetUi32(p + offset);
1532
1533 // only 12 bits probably are OK.
1534 // How does it work for files larger than 16 MB?
1535 const UInt32 kMask = 0xFFF;
1536 // const UInt32 kMask = 0xFFFF; // 16-bit is more logical
1537 const UInt32 lim = (val >> 16) & kMask;
1538 const UInt32 base = (val & kMask);
1539
1540 /*
1541 strange combinations:
1542 PDR: base = 0x1FFF lim = 0;
1543 empty: base = 0xFFFF lim = 0xFFFF;
1544
1545 PDR: base = 0x7FFF lim = 0;
1546 empty: base = 0x7FFF lim = 0;
1547 */
1548 if (base == kMask && lim == 0)
1549 continue; // unused
1550
1551 if (lim < base)
1552 continue; // unused
1553
1554 CItem item;
1555 item.Name = kRegionName[i];
1556 item.Method = method;
1557 item.BufIndex = bufIndex;
1558 item.Parent = parent;
1559 item.Offset = posBase + (base << 12);
1560 if (item.Offset > exactSize)
1561 continue;
1562 item.Size = (lim + 1 - base) << 12;
1563 // item.SetGuid(p + kFfsGuidOffset);
1564 // item.Name += " [VOLUME]";
1565 AddItem(item);
1566 }
1567 return S_OK;
1568 }
1569
1570
1571 HRESULT CHandler::OpenCapsule(IInStream *stream)
1572 {
1573 const unsigned kHeaderSize = 80;
1574 Byte buf[kHeaderSize];
1575 RINOK(ReadStream_FALSE(stream, buf, kHeaderSize))
1576 if (!_h.Parse(buf))
1577 return S_FALSE;
1578 if (_h.CapsuleImageSize < kHeaderSize
1579 || _h.CapsuleImageSize < _h.HeaderSize
1580 || _h.OffsetToCapsuleBody < _h.HeaderSize
1581 || _h.OffsetToCapsuleBody > _h.CapsuleImageSize
1582 || _h.CapsuleImageSize > (1u << 30) // to reduce false detection
1583 || _h.HeaderSize > (1u << 28) // to reduce false detection
1584 )
1585 return S_FALSE;
1586 _phySize = _h.CapsuleImageSize;
1587
1588 if (_h.SequenceNumber != 0 ||
1589 _h.OffsetToSplitInformation != 0 )
1590 return E_NOTIMPL;
1591
1592 const unsigned bufIndex = AddBuf(_h.CapsuleImageSize);
1593 CByteBuffer &buf0 = _bufs[bufIndex];
1594 memcpy(buf0, buf, kHeaderSize);
1595 ReadStream_FALSE(stream, buf0 + kHeaderSize, _h.CapsuleImageSize - kHeaderSize);
1596
1597 AddCommentString("Author", _h.OffsetToAuthorInformation);
1598 AddCommentString("Revision", _h.OffsetToRevisionInformation);
1599 AddCommentString("Short Description", _h.OffsetToShortDescription);
1600 AddCommentString("Long Description", _h.OffsetToLongDescription);
1601
1602
1603 const UInt32 size = _h.CapsuleImageSize - _h.OffsetToCapsuleBody;
1604
1605 if (size >= 32 && IsIntelMe(buf0 + _h.OffsetToCapsuleBody))
1606 return ParseIntelMe(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1607
1608 return ParseVolume(bufIndex, _h.OffsetToCapsuleBody, size, size, -1, -1, 0);
1609 }
1610
1611
1612 HRESULT CHandler::OpenFv(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback * /* callback */)
1613 {
1614 Byte buf[kFvHeaderSize];
1615 RINOK(ReadStream_FALSE(stream, buf, kFvHeaderSize))
1616 if (!IsFfs(buf))
1617 return S_FALSE;
1618 CVolFfsHeader ffsHeader;
1619 if (!ffsHeader.Parse(buf))
1620 return S_FALSE;
1621 if (ffsHeader.VolSize > ((UInt32)1 << 30))
1622 return S_FALSE;
1623 _phySize = ffsHeader.VolSize;
1624 RINOK(InStream_SeekToBegin(stream))
1625 UInt32 fvSize32 = (UInt32)ffsHeader.VolSize;
1626 unsigned bufIndex = AddBuf(fvSize32);
1627 RINOK(ReadStream_FALSE(stream, _bufs[bufIndex], fvSize32))
1628 return ParseVolume(bufIndex, 0, fvSize32, fvSize32, -1, -1, 0);
1629 }
1630
1631
1632 HRESULT CHandler::Open2(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback)
1633 {
1634 if (_capsuleMode)
1635 {
1636 RINOK(OpenCapsule(stream))
1637 }
1638 else
1639 {
1640 RINOK(OpenFv(stream, maxCheckStartPosition, callback))
1641 }
1642
1643 const unsigned num = _items.Size();
1644 CIntArr numChilds(num);
1645
1646 unsigned i;
1647
1648 for (i = 0; i < num; i++)
1649 numChilds[i] = 0;
1650
1651 for (i = 0; i < num; i++)
1652 {
1653 const int parent = _items[i].Parent;
1654 if (parent >= 0)
1655 numChilds[(unsigned)parent]++;
1656 }
1657
1658 for (i = 0; i < num; i++)
1659 {
1660 const CItem &item = _items[i];
1661 const int parent = item.Parent;
1662 if (parent >= 0)
1663 {
1664 CItem &parentItem = _items[(unsigned)parent];
1665 if (numChilds[(unsigned)parent] == 1)
1666 if (!item.ThereIsUniqueName || !parentItem.ThereIsUniqueName || !parentItem.ThereAreSubDirs)
1667 parentItem.Skip = true;
1668 }
1669 }
1670
1671 CUIntVector mainToReduced;
1672
1673 for (i = 0; i < _items.Size(); i++)
1674 {
1675 mainToReduced.Add(_items2.Size());
1676 const CItem &item = _items[i];
1677 if (item.Skip)
1678 continue;
1679 AString name;
1680 int numItems = -1;
1681 int parent = item.Parent;
1682 if (parent >= 0)
1683 numItems = numChilds[(unsigned)parent];
1684 AString name2 (item.GetName(numItems));
1685 AString characts2 (item.Characts);
1686 if (item.KeepName)
1687 name = name2;
1688
1689 while (parent >= 0)
1690 {
1691 const CItem &item3 = _items[(unsigned)parent];
1692 if (!item3.Skip)
1693 break;
1694 if (item3.KeepName)
1695 {
1696 AString name3 (item3.GetName(-1));
1697 if (name.IsEmpty())
1698 name = name3;
1699 else
1700 name = name3 + '.' + name;
1701 }
1702 AddSpaceAndString(characts2, item3.Characts);
1703 parent = item3.Parent;
1704 }
1705
1706 if (name.IsEmpty())
1707 name = name2;
1708
1709 CItem2 item2;
1710 item2.MainIndex = i;
1711 item2.Name = name;
1712 item2.Characts = characts2;
1713 if (parent >= 0)
1714 item2.Parent = (int)mainToReduced[(unsigned)parent];
1715 _items2.Add(item2);
1716 /*
1717 CItem2 item2;
1718 item2.MainIndex = i;
1719 item2.Name = item.Name;
1720 item2.Parent = item.Parent;
1721 _items2.Add(item2);
1722 */
1723 }
1724
1725 return S_OK;
1726 }
1727
1728 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
1729 const UInt64 *maxCheckStartPosition,
1730 IArchiveOpenCallback *callback))
1731 {
1732 COM_TRY_BEGIN
1733 Close();
1734 {
1735 HRESULT res = Open2(inStream, maxCheckStartPosition, callback);
1736 if (res == E_NOTIMPL)
1737 res = S_FALSE;
1738 return res;
1739 }
1740 COM_TRY_END
1741 }
1742
1743 Z7_COM7F_IMF(CHandler::Close())
1744 {
1745 _phySize = 0;
1746 _totalBufsSize = 0;
1747 _methodsMask = 0;
1748 _items.Clear();
1749 _items2.Clear();
1750 _bufs.Clear();
1751 _comment.Empty();
1752 _headersError = false;
1753 _h.Clear();
1754 return S_OK;
1755 }
1756
1757 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1758 {
1759 *numItems = _items2.Size();
1760 return S_OK;
1761 }
1762
1763 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1764 Int32 testMode, IArchiveExtractCallback *extractCallback))
1765 {
1766 COM_TRY_BEGIN
1767 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1768 if (allFilesMode)
1769 numItems = _items2.Size();
1770 if (numItems == 0)
1771 return S_OK;
1772 UInt64 totalSize = 0;
1773 UInt32 i;
1774 for (i = 0; i < numItems; i++)
1775 totalSize += _items[_items2[allFilesMode ? i : indices[i]].MainIndex].Size;
1776 RINOK(extractCallback->SetTotal(totalSize))
1777 totalSize = 0;
1778
1779 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1780 lps->Init(extractCallback, false);
1781 CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1782
1783 for (i = 0;; i++)
1784 {
1785 lps->InSize = lps->OutSize = totalSize;
1786 RINOK(lps->SetCur())
1787 if (i >= numItems)
1788 break;
1789 Int32 opRes;
1790 {
1791 CMyComPtr<ISequentialOutStream> realOutStream;
1792 const Int32 askMode = testMode ?
1793 NExtract::NAskMode::kTest :
1794 NExtract::NAskMode::kExtract;
1795 const UInt32 index = allFilesMode ? i : indices[i];
1796 const CItem &item = _items[_items2[index].MainIndex];
1797 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1798 totalSize += item.Size;
1799 if (!testMode && !realOutStream)
1800 continue;
1801 RINOK(extractCallback->PrepareOperation(askMode))
1802 if (testMode || item.IsDir)
1803 opRes = NExtract::NOperationResult::kOK;
1804 else
1805 {
1806 opRes = NExtract::NOperationResult::kDataError;
1807 CMyComPtr<ISequentialInStream> inStream;
1808 GetStream(index, &inStream);
1809 if (inStream)
1810 {
1811 RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps))
1812 if (copyCoder->TotalSize == item.Size)
1813 opRes = NExtract::NOperationResult::kOK;
1814 }
1815 }
1816 }
1817 RINOK(extractCallback->SetOperationResult(opRes))
1818 }
1819 return S_OK;
1820 COM_TRY_END
1821 }
1822
1823 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
1824 {
1825 COM_TRY_BEGIN
1826 const CItem &item = _items[_items2[index].MainIndex];
1827 if (item.IsDir)
1828 return S_FALSE;
1829 CBufInStream *streamSpec = new CBufInStream;
1830 CMyComPtr<IInStream> streamTemp = streamSpec;
1831 const CByteBuffer &buf = _bufs[item.BufIndex];
1832 if (item.Offset > buf.Size())
1833 return S_FALSE;
1834 size_t size = buf.Size() - item.Offset;
1835 if (size > item.Size)
1836 size = item.Size;
1837 streamSpec->Init(buf + item.Offset, size, (IInArchive *)this);
1838 *stream = streamTemp.Detach();
1839 return S_OK;
1840 COM_TRY_END
1841 }
1842
1843
1844 namespace UEFIc {
1845
1846 static const Byte k_Capsule_Signatures[] =
1847 {
1848 16, CAPSULE_SIGNATURE,
1849 16, CAPSULE2_SIGNATURE,
1850 16, CAPSULE_UEFI_SIGNATURE
1851 };
1852
1853 REGISTER_ARC_I_CLS(
1854 CHandler(true),
1855 "UEFIc", "scap", NULL, 0xD0,
1856 k_Capsule_Signatures,
1857 0,
1858 NArcInfoFlags::kMultiSignature |
1859 NArcInfoFlags::kFindSignature,
1860 NULL)
1861
1862 }
1863
1864 namespace UEFIf {
1865
1866 static const Byte k_FFS_Signatures[] =
1867 {
1868 16, FFS1_SIGNATURE,
1869 16, FFS2_SIGNATURE
1870 };
1871
1872
1873 REGISTER_ARC_I_CLS(
1874 CHandler(false),
1875 "UEFIf", "uefif", NULL, 0xD1,
1876 k_FFS_Signatures,
1877 kFfsGuidOffset,
1878 NArcInfoFlags::kMultiSignature |
1879 NArcInfoFlags::kFindSignature,
1880 NULL)
1881
1882 }
1883
1884 }}
1885