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