blob: 6598dcf8138d24f31ed4a913b2a68df49a5020dd [file] [log] [blame]
Luc Verhaegen038e6a52009-06-24 14:18:29 +02001/*
2 * Copyright 2009 Luc Verhaegen <libv@skynet.be>
3 * Copyright 2000-2003 Anthony Borisow
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include <stdio.h>
21#include <inttypes.h>
22#include <string.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <errno.h>
Luc Verhaegen038e6a52009-06-24 14:18:29 +020027#include <sys/mman.h>
28
Idwer Vollering0ab44552012-09-23 01:22:20 +020029#include "compat.h"
Luc Verhaegen038e6a52009-06-24 14:18:29 +020030#include "bios_extract.h"
31#include "lh5_extract.h"
32
Anton Kochkovb09d0ef2012-12-21 07:17:56 +040033struct bcpHeader {
34 char signature[6];
35 uint8_t major_revision;
36 uint8_t minor_revision;
37 uint16_t length;
38};
39
40/* Our own structure just to store important parameters */
41struct Phoenix {
42 uint8_t version;
43 uint8_t type;
44 uint8_t compression;
45};
46
Anton Kochkov8f9427f2013-04-07 13:20:30 +040047static struct Phoenix phx = { 0, 0, 0 };
Anton Kochkovb09d0ef2012-12-21 07:17:56 +040048
49#define COMP_LZSS 0
50#define COMP_LZARI 1
51#define COMP_LZHUF 2
52#define COMP_LZINT 3
53
54struct bcpCompress {
55 struct bcpHeader head;
56 uint8_t flags;
57 uint8_t alg;
58 uint16_t unc_start_offset;
59 uint32_t size_comp_data;
60 uint16_t bcpiRamBiosStart;
61 uint16_t bcpiWorkAreaStart;
62 uint16_t bcpiLowMemStart;
63 uint16_t bcpiLowMemSize;
64 uint8_t commonCharacterLZSS;
65 uint16_t oldRamBiosStart;
66 uint16_t oldSetupScanStart;
67 uint16_t oldSetupScanSize;
68};
69
Anton Kochkovb09d0ef2012-12-21 07:17:56 +040070#define GUID_FFVMODULE "FED91FBA-D37B-4EEA-8729-2EF29FB37A78"
71#define GUID_ESCD "FD21E8FD-2525-4A95-BB90-47EC5763FF9E"
72#define GUID_RAWCODE "F6AE0F63-5F8C-4316-A2EA-76B9AF762756"
73
74/* -------------- Phoenix module file type parsing -------------- */
75
76/* See http://wiki.phoenix.com/wiki/index.php/EFI_FV_FILETYPE for
77 * additional information */
78
Anton Kochkov8f9427f2013-04-07 13:20:30 +040079struct PhoenixFFVFileType {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +040080 uint8_t Id;
81 char *Name;
82};
83
84static struct PhoenixFFVFileType
Anton Kochkov8f9427f2013-04-07 13:20:30 +040085 PhoenixFFVFileTypes[] = {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +040086 {0x00, "ALL"},
87 {0x01, "BIN"},
88 {0x02, "SECTION"},
89 {0x03, "CEIMAIN"},
90 {0x04, "PEIMAIN"},
91 {0x05, "DXEMAIN"},
92 {0x06, "PEI"},
93 {0x07, "DXE"},
94 {0x08, "COMBINED_PEIM_DRIVER"},
95 {0x09, "APP"},
96 {0x0B, "FFV"},
97 {0xC2, "CEI"},
98 {0xC3, "XIP"},
99 {0xC4, "BB"},
100 {0xD0, "SDXE"},
101 {0xD1, "DXESDXE"},
102 {0xF0, "GAP"},
103 {0, NULL},
104};
105
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400106static char *get_file_type(uint8_t id)
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400107{
108 short i = 0;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400109 while (PhoenixFFVFileTypes[i].Name != NULL) {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400110 if (PhoenixFFVFileTypes[i].Id == id)
111 return PhoenixFFVFileTypes[i].Name;
112 i++;
113 }
114 return "UNKNOWN";
115}
116
117/* -------------- Phoenix section file type parsing -------------- */
118
119/* See http://wiki.phoenix.com/wiki/index.php/EFI_SECTION_TYPE for
120 * additional information */
121
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400122struct PhoenixFFVSectionType {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400123 uint8_t Id;
124 char *Name;
125};
126
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400127static struct PhoenixFFVSectionType PhoenixFFVSectionTypes[] = {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400128 {0x01, "COMPRESSION"},
129 {0x02, "GUID_DEFINED"},
130 {0x10, "PE32"},
131 {0x11, "PIC"},
132 {0x12, "TE"},
133 {0x13, "DXE_DEPEX"},
134 {0x14, "VERSION"},
135 {0x15, "USER_INTERFACE"},
136 {0x16, "COMPATIBILITY16"},
137 {0x17, "FIRMWARE_VOLUME_IMAGE"},
138 {0x18, "FREEFORM_SUBTYPE_GUID"},
139 {0x19, "BIN"},
140 {0x1A, "PE64"},
141 {0x1B, "PEI_DEPEX"},
142 {0xC0, "SOURCECODE"},
143 {0xC1, "FFV"},
144 {0xC2, "RE32"},
145 {0xC3, "XIP16"},
146 {0xC4, "XIP32"},
147 {0xC5, "XIP64"},
148 {0xC6, "PLACE16"},
149 {0xC7, "PLACE32"},
150 {0xC8, "PLACE64"},
151 {0xCF, "PCI_DEVICE"},
152 {0xD0, "PDB"},
153 {0, NULL},
154};
155
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400156static char *get_section_type(uint8_t id)
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400157{
158 short i = 0;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400159 while (PhoenixFFVSectionTypes[i].Name != NULL) {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400160 if (PhoenixFFVSectionTypes[i].Id == id)
161 return PhoenixFFVSectionTypes[i].Name;
162 i++;
163 }
164 return "UNKNOWN";
165}
166
167/* -------------- Phoenix module name parsing -------------- */
168
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400169struct PhoenixModuleName {
170 char Id;
171 char *Name;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200172};
173
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400174static struct PhoenixModuleName PhoenixModuleNames[] = {
175 {'A', "acpi"},
176 {'B', "bioscode"},
177 {'C', "update"},
178 {'D', "display"},
179 {'E', "setup"},
180 {'F', "font"},
181 {'G', "decompcode"},
182 {'I', "bootblock"},
183 {'L', "logo"},
184 {'M', "miser"},
185 {'N', "rompilotload"},
186 {'O', "network"},
187 {'P', "rompilotinit"},
188 {'R', "oprom"},
189 {'S', "strings"},
190 {'T', "template"},
191 {'U', "user"},
192 {'X', "romexec"},
193 {'W', "wav"},
194 {'H', "tcpa_H"}, /* TCPA (Trusted Computing), USBKCLIB? */
195 {'K', "tcpa_K"}, /* TCPA (Trusted Computing), "AUTH"? */
196 {'Q', "tcpa_Q"}, /* TCPA (Trusted Computing), "SROM"? */
197 {'<', "tcpa_<"},
198 {'*', "tcpa_*"},
199 {'?', "tcpa_?"},
200 {'$', "biosentry"},
201 {'J', "SmartCardPAS"},
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200202};
203
Joshua Roys563d19c2011-10-26 12:30:06 -0400204struct PhoenixID {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400205 char Name[6];
206 uint16_t Flags;
207 uint16_t Length;
Joshua Roys563d19c2011-10-26 12:30:06 -0400208};
209
210struct PhoenixFFVModule {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400211 uint8_t Signature;
212 uint8_t Flags;
213 uint16_t Checksum; /* Can be splitted to header and data checksums */
214 uint16_t LengthLo;
215 uint8_t LengthHi;
216 uint8_t FileType;
217 char Name[16]; /* GUID name */
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400218};
219
220struct PhoenixFFVSectionHeader {
221 uint16_t SizeLo;
222 uint8_t SizeHi;
223 uint8_t Type;
224};
225
226struct PhoenixFFVCompressionHeader {
227 uint16_t TotalLengthLo;
228 uint8_t TotalLengthHi;
229 uint8_t CompType;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400230 uint16_t PackedLenLo;
231 uint8_t PackedLenHi;
232 uint8_t Unk2;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400233 uint16_t RealLenLo;
234 uint8_t RealLenHi;
235 uint8_t Unk3;
Joshua Roys563d19c2011-10-26 12:30:06 -0400236};
237
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400238static char *PhoenixModuleNameGet(char Id)
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200239{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400240 int i;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200241
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400242 for (i = 0; PhoenixModuleNames[i].Name; i++)
243 if (PhoenixModuleNames[i].Id == Id)
244 return PhoenixModuleNames[i].Name;
245 return NULL;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200246}
247
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400248static void
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400249phx_write_file(unsigned char *BIOSImage, char *filename, short filetype,
250 int offset, uint32_t length)
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400251{
252 int fd;
253
254 if (filename[0] == '\0') {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400255 sprintf(filename, "%s_0x%08x-0x%08x", get_file_type(filetype),
256 offset, offset + length);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400257 }
258 fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
259 if (fd < 0) {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400260 fprintf(stderr, "Error: unable to open %s: %s\n\n", filename,
261 strerror(errno));
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400262 return;
263 }
264 write(fd, BIOSImage + offset + 0x18, length - 0x18);
265 close(fd);
266}
267
268/* ---------- Extraction code ---------- */
269
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400270static int PhoenixModule(unsigned char *BIOSImage, int BIOSLength, int Offset)
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200271{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400272 struct PhoenixModule {
273 uint32_t Previous;
274 uint8_t Signature[3];
275 uint8_t Id;
276 uint8_t Type;
277 uint8_t HeadLen;
278 uint8_t Compression;
279 uint16_t Offset;
280 uint16_t Segment;
281 uint32_t ExpLen;
282 uint32_t FragLength;
283 uint32_t NextFrag;
284 } *Module;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200285
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400286 char *filename, *ModuleName;
287 unsigned char *Buffer;
288 unsigned char *ModuleData;
289 uint32_t Packed;
290 int fd;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200291
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400292 Module = (struct PhoenixModule *)(BIOSImage + Offset);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200293
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400294 if (Module->Signature[0] || (Module->Signature[1] != 0x31)
295 || (Module->Signature[2] != 0x31)) {
296 fprintf(stderr, "Error: Invalid module signature at 0x%05X\n",
297 Offset);
298 return 0;
Michael Karchere32568e2010-12-07 10:42:37 +0100299 }
300
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400301 if ((Offset + Module->HeadLen + 4 + le32toh(Module->FragLength)) >
302 BIOSLength) {
303 fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n",
304 Offset);
Michael Karchere32568e2010-12-07 10:42:37 +0100305 return le32toh(Module->Previous);
Michael Karchere32568e2010-12-07 10:42:37 +0100306 }
Michael Karchere32568e2010-12-07 10:42:37 +0100307
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400308 /* NextFrag is either the unpacked length again *or* the virtual address
309 of the next fragment. As long as BIOSses stay below 256MB, this works */
310 if ((le32toh(Module->NextFrag) & 0xF0000000) == 0xF0000000) {
311 struct PhoenixFragment {
312 uint32_t NextFrag;
313 uint8_t NextBank;
314 uint32_t FragLength;
315 } *Fragment;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200316
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400317 int FragOffset;
318 uint32_t FragLength = le32toh(Module->FragLength);
319
320 if (FragLength > le32toh(Module->ExpLen)) {
321 fprintf(stderr,
322 "Error: First fragment exceeds total size at %05X\n",
323 Offset);
324 return le32toh(Module->Previous);
325 }
326
327 /* This over-allocates, but the total compressed size is not available here */
328 ModuleData = malloc(le32toh(Module->ExpLen));
329 if (!ModuleData) {
330 fprintf(stderr,
331 "Error: Can't reassemble fragments, no memory for %d bytes\n",
332 le32toh(Module->ExpLen));
333 return le32toh(Module->Previous);
334 }
335
336 memcpy(ModuleData, BIOSImage + Offset + Module->HeadLen,
337 FragLength);
338
339 Packed = FragLength;
340 FragOffset = le32toh(Module->NextFrag) & (BIOSLength - 1);
341
342 printf("extra fragments: ");
343 while (FragOffset) {
344 Fragment =
345 (struct PhoenixFragment *)(BIOSImage + FragOffset);
346 FragLength = le32toh(Fragment->FragLength);
347 printf("(%05X, %d bytes) ", FragOffset, FragLength);
348
349 if (Packed + FragLength > le32toh(Module->ExpLen)) {
350 fprintf(stderr,
351 "\nFragment too big at %05X for %05X\n",
352 FragOffset, Offset);
353 free(ModuleData);
354 return le32toh(Module->Previous);
355 }
356 memcpy(ModuleData + Packed, BIOSImage + FragOffset + 9,
357 FragLength);
358 Packed += FragLength;
359 FragOffset =
360 le32toh(Fragment->NextFrag) & (BIOSLength - 1);
361 }
362 printf("\n");
363
364 } else {
365 ModuleData = BIOSImage + Offset + Module->HeadLen;
366 Packed = le32toh(Module->FragLength);
367 }
368
369 ModuleName = PhoenixModuleNameGet(Module->Type);
370 if (ModuleName) {
371 filename = malloc(strlen(ModuleName) + 7);
372 sprintf(filename, "%s_%1d.rom", ModuleName, Module->Id);
373 } else {
374 filename = malloc(9);
375 sprintf(filename, "%02X_%1d.rom", Module->Type, Module->Id);
376 }
377
378 fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
379 if (fd < 0) {
380 fprintf(stderr, "Error: unable to open %s: %s\n\n", filename,
381 strerror(errno));
382 free(filename);
383 if ((le32toh(Module->NextFrag) & 0xF0000000) == 0xF0000000)
384 free(ModuleData);
385 return le32toh(Module->Previous);
386 }
387
388 switch (Module->Compression) {
389 case 5: /* LH5 */
390 printf("0x%05X (%6d bytes) -> %s\t(%d bytes)",
391 Offset + Module->HeadLen + 4, Packed, filename,
392 le32toh(Module->ExpLen));
393 Buffer = MMapOutputFile(filename, le32toh(Module->ExpLen));
394 if (!Buffer)
395 break;
396
397 /* The first 4 bytes of the LH5 packing method is just the total
398 * expanded length; skip them */
399 LH5Decode(ModuleData + 4, Packed - 4, Buffer,
400 le32toh(Module->ExpLen));
401 munmap(Buffer, le32toh(Module->ExpLen));
402 break;
403
404 /* case 3 *//* LZSS */
405 case 0: /* not compressed at all */
406 printf("0x%05X (%6d bytes) -> %s", Offset + Module->HeadLen,
407 Packed, filename);
408 write(fd, ModuleData, Packed);
409 break;
410
411 default:
412 fprintf(stderr, "Unsupported compression type for %s: %d\n",
413 filename, Module->Compression);
414 printf("0x%05X (%6d bytes) -> %s\t(%d bytes)",
415 Offset + Module->HeadLen, Packed, filename,
416 le32toh(Module->ExpLen));
417 write(fd, ModuleData, Packed);
418 break;
419 }
420
421 close(fd);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200422 free(filename);
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400423
Michael Karchere32568e2010-12-07 10:42:37 +0100424 if ((le32toh(Module->NextFrag) & 0xF0000000) == 0xF0000000)
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400425 free(ModuleData);
426
427 if (le16toh(Module->Offset) || le16toh(Module->Segment)) {
428 if (!Module->Compression)
429 printf("\t\t");
430 printf("\t [0x%04X:0x%04X]\n", le16toh(Module->Segment) << 12,
431 le16toh(Module->Offset));
432 } else
433 printf("\n");
Michael Karchere32568e2010-12-07 10:42:37 +0100434
435 return le32toh(Module->Previous);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200436}
437
Joshua Roys563d19c2011-10-26 12:30:06 -0400438static int
439PhoenixExtractFFV(unsigned char *BIOSImage, int BIOSLength, int Offset)
440{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400441 struct PhoenixFFVSectionHeader *SectionHeader;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400442 struct PhoenixFFVCompressionHeader *CompHeader;
443 struct PhoenixFFVModule *Module;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400444 char Name[16], filename[24];
445 char *ModuleName;
446 uint32_t Length, PackedLen, RealLen;
447 unsigned char *RealData;
Joshua Roys563d19c2011-10-26 12:30:06 -0400448
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400449 Module = (struct PhoenixFFVModule *)(BIOSImage + Offset);
Joshua Roys563d19c2011-10-26 12:30:06 -0400450
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400451 if (Module->Signature != 0xF8) {
452 /* ignore and move on to the next byte... */
453 return 1;
454 }
Joshua Roys563d19c2011-10-26 12:30:06 -0400455
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400456 Length = ((le16toh(Module->LengthHi) << 16) | Module->LengthLo) - 1;
457 if ((Offset + Length) >= BIOSLength) {
458 fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n",
459 Offset);
460 return 1;
461 }
Joshua Roys563d19c2011-10-26 12:30:06 -0400462
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400463 /* TODO: Improve module name parsing */
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400464 if (Module->FileType == 0xF0) {
465 strcpy(Name, "GAP");
466 filename[0] = '\0';
467 } else if ((uint8_t) Module->Name[8] != 0xFF) {
468 strcpy(Name, "GUID?");
469 filename[0] = '\0';
470 } else {
471 /* get rid of the pesky 0xFF in the middle of the name */
472 memcpy(Name, Module->Name, 8);
473 memcpy(Name + 8, Module->Name + 9, 7);
474 Name[15] = '\0';
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400475
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400476 if (Name[0] == '_' && strlen(Name) == 4) {
477 ModuleName = PhoenixModuleNameGet(Name[1]);
478 if (ModuleName) {
479 snprintf(filename, sizeof(filename),
480 "%s_%c%c.rom", ModuleName, Name[2],
481 Name[3]);
482 } else {
483 snprintf(filename, sizeof(filename), "%s.rom",
484 Name);
485 }
486 } else {
487 strncpy(filename, Name, sizeof(filename));
488 }
Joshua Roys563d19c2011-10-26 12:30:06 -0400489 }
Joshua Roys563d19c2011-10-26 12:30:06 -0400490
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400491 printf("\t%-15s (%08X-%08X) %08X %02X %02X %s [%s]\n",
492 Name, Offset, Offset + Length, Length, Module->Flags,
493 Module->FileType, filename, get_file_type(Module->FileType));
Joshua Roys563d19c2011-10-26 12:30:06 -0400494
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400495 switch (Module->FileType) {
496 case 0xF0:
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400497 break;
Joshua Roys563d19c2011-10-26 12:30:06 -0400498
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400499 /* ---------- SECTION file type ---------- */
500 case 0x02:
501 SectionHeader =
502 (struct PhoenixFFVSectionHeader *)(BIOSImage + Offset +
503 0x18);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400504 if (Name[1] == 'G' || !*filename) {
505 break;
506 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400507
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400508 /* COMPRESSION section */
509 if (SectionHeader->Type == 0x01) {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400510 CompHeader =
511 (struct PhoenixFFVCompressionHeader *)(BIOSImage +
512 Offset +
513 0x18);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400514 /* some blocks have a (8 byte?) header we need to skip */
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400515 if (CompHeader->TotalLengthLo != Length - 0x18
516 && CompHeader->Unk3) {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400517 /* FIXME more advanced parsing of sections */
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400518 CompHeader =
519 (struct PhoenixFFVCompressionHeader *)
520 ((unsigned char *)CompHeader +
521 CompHeader->TotalLengthLo);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400522 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400523 PackedLen =
524 (CompHeader->
525 PackedLenHi << 16) | CompHeader->PackedLenLo;
526 RealLen =
527 (CompHeader->
528 RealLenHi << 16) | CompHeader->RealLenLo;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400529 //printf("CompHeader->Type = %d\n", CompHeader->CompType);
Joshua Roys563d19c2011-10-26 12:30:06 -0400530
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400531 if (CompHeader->CompType == 0) /* Not compressed at all */
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400532 break;
Joshua Roys563d19c2011-10-26 12:30:06 -0400533
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400534 if (!RealLen) /* FIXME temporary hack */
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400535 break;
Joshua Roys563d19c2011-10-26 12:30:06 -0400536
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400537 RealData = MMapOutputFile(filename, RealLen);
538 if (!RealData) {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400539 fprintf(stderr,
540 "Failed to mmap file for uncompressed data.\n");
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400541 break;
542 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400543 if ((phx.compression == COMP_LZHUF)
544 || (phx.compression == COMP_LZINT)) {
545 if (LH5Decode
546 ((unsigned char *)CompHeader +
547 sizeof(struct PhoenixFFVCompressionHeader),
548 PackedLen, RealData, RealLen) == -1) {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400549 munmap(RealData, RealLen);
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400550 fprintf(stderr,
551 "Failed to uncompress section with LHA5.\n");
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400552 /* dump original section in this case */
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400553 phx_write_file(BIOSImage, filename,
554 Module->FileType, Offset,
555 Length);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400556 } else
557 printf("COMPRESSED\n");
558 } else
559 printf("Unsupported compression!\n");
560 munmap(RealData, RealLen);
561 break;
562 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400563 printf("\t\tSECTION: %s\n",
564 get_section_type(SectionHeader->Type));
565 phx_write_file(BIOSImage, filename, Module->FileType, Offset,
566 Length);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400567 break;
568
569 default:
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400570 phx_write_file(BIOSImage, filename, Module->FileType, Offset,
571 Length);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400572 break;
573 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400574 return Length;
Joshua Roys563d19c2011-10-26 12:30:06 -0400575}
576
Joshua Roysa52d11f2012-09-12 12:58:20 -0400577/* Parse initial volumedir layout:
578 * - 1 byte Type indicates either raw code or an FFV module
579 * - 4 byte Base provides the offset into the image to find the specified volume
580 * - 4 byte Length
581 */
Joshua Roys563d19c2011-10-26 12:30:06 -0400582void
Joshua Roysa52d11f2012-09-12 12:58:20 -0400583PhoenixVolume1(unsigned char *BIOSImage, int BIOSLength, int Offset, int ModLen)
Joshua Roys563d19c2011-10-26 12:30:06 -0400584{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400585 struct PhoenixVolumeDirEntry {
586 uint8_t Type;
587 uint32_t Base;
588 uint32_t Length;
589 } *Modules;
590
591 char Name[16];
592 int fd, HoleNum = 0;
Joshua Roysa52d11f2012-09-12 12:58:20 -0400593 uint8_t Type;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400594 uint32_t Base, Length, NumModules, ModNum;
Joshua Roysa52d11f2012-09-12 12:58:20 -0400595
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400596 Modules = (struct PhoenixVolumeDirEntry *)(BIOSImage + Offset + 0x18);
597 NumModules = (ModLen - 0x18) / sizeof(struct PhoenixVolumeDirEntry);
Joshua Roysa52d11f2012-09-12 12:58:20 -0400598
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400599 printf("FFV modules: %u\n", NumModules);
Joshua Roysa52d11f2012-09-12 12:58:20 -0400600
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400601 for (ModNum = 0; ModNum < NumModules; ModNum++) {
602 Type = Modules[ModNum].Type;
603 Base = Modules[ModNum].Base & (BIOSLength - 1);
604 Length = Modules[ModNum].Length - 1;
605 printf("[%2u]: (%08X-%08X) %02x\n", ModNum, Base, Base + Length,
606 Type);
Joshua Roysa52d11f2012-09-12 12:58:20 -0400607
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400608 switch (Type) {
609 case 0x01:
610 printf("\tHole (raw code)\n");
611 snprintf(Name, sizeof(Name), "hole_%02x.bin",
612 HoleNum++);
613 fd = open(Name, O_RDWR | O_CREAT | O_TRUNC,
614 S_IRUSR | S_IWUSR);
615 if (fd < 0) {
616 fprintf(stderr,
617 "Error: unable to open %s: %s\n\n",
618 Name, strerror(errno));
619 continue;
620 }
621 write(fd, BIOSImage + Base, Length);
622 close(fd);
623 break;
Joshua Roysa52d11f2012-09-12 12:58:20 -0400624
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400625 case 0x02:
626 /* FFV modules */
627 Offset = Base;
628 while (Offset < Base + Length) {
629 Offset +=
630 PhoenixExtractFFV(BIOSImage, BIOSLength,
631 Offset);
632 }
633 break;
634 }
Joshua Roysa52d11f2012-09-12 12:58:20 -0400635 }
Joshua Roysa52d11f2012-09-12 12:58:20 -0400636}
637
638/* Parse GUID-based volumedir layout:
639 * - 4 bytes of unknown data
640 * - 4 byte Length
641 * - array of module entries:
642 * - 16 byte GUID indicating module type
643 * - 4 byte Base
644 * - 4 byte Length
645 */
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400646void PhoenixVolume2(unsigned char *BIOSImage, int BIOSLength, int Offset)
Joshua Roysa52d11f2012-09-12 12:58:20 -0400647{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400648 struct PhoenixVolumeDirEntry2 {
649 /* these are stored little endian */
650 uint32_t guid1;
651 uint16_t guid2;
652 uint16_t guid3;
653 /* these are big endian */
654 uint16_t guid4;
655 /*uint48_t guid5; */
656 uint16_t guid5;
657 uint32_t guid6;
658 uint32_t Base;
659 uint32_t Length;
660 };
Joshua Roys563d19c2011-10-26 12:30:06 -0400661
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400662 struct PhoenixVolumeDir2 {
663 uint16_t Unk1;
664 uint16_t Unk2;
665 uint32_t Length;
666 struct PhoenixVolumeDirEntry2 Modules[];
667 } *Volume;
668
669 char Name[16], guid[37];
670 int fd, HoleNum = 0;
671 uint32_t Base, Length, NumModules, ModNum;
672
673 Volume = (struct PhoenixVolumeDir2 *)(BIOSImage + Offset + 0x18);
674 NumModules =
675 (Volume->Length - 8) / sizeof(struct PhoenixVolumeDirEntry2);
676
677 printf("FFV modules: %u\n", NumModules);
678
679 for (ModNum = 0; ModNum < NumModules; ModNum++) {
680 sprintf(guid, "%08X-%04X-%04X-%04X-%04X%08X",
681 le32toh(Volume->Modules[ModNum].guid1),
682 le16toh(Volume->Modules[ModNum].guid2),
683 le16toh(Volume->Modules[ModNum].guid3),
684 be16toh(Volume->Modules[ModNum].guid4),
685 be16toh(Volume->Modules[ModNum].guid5),
686 be32toh(Volume->Modules[ModNum].guid6)
687 );
688 Base = Volume->Modules[ModNum].Base & (BIOSLength - 1);
689 Length = Volume->Modules[ModNum].Length - 1;
690 printf("[%2u]: (%08X-%08X) %s\n", ModNum, Base, Base + Length,
691 guid);
692
693 if (!strcmp(guid, GUID_FFVMODULE)) {
694 /* FFV modules */
695 Offset = Base;
696 while (Offset < Base + Length) {
697 Offset +=
698 PhoenixExtractFFV(BIOSImage, BIOSLength,
699 Offset);
700 }
701 } else if (!strcmp(guid, GUID_ESCD)) {
702 /* Extended System Configuration Data (and similar?) */
703 printf("\tESCD\n");
704 fd = open("ESCD.bin", O_RDWR | O_CREAT | O_TRUNC,
705 S_IRUSR | S_IWUSR);
706 if (fd < 0) {
707 fprintf(stderr,
708 "Error: unable to open ESCD.bin: %s\n\n",
709 strerror(errno));
710 continue;
711 }
712 write(fd, BIOSImage + Base, Length);
713 close(fd);
714 } else if (!strcmp(guid, GUID_RAWCODE)) {
715 /* Raw BIOS code */
716 printf("\tHole (raw code)\n");
717 snprintf(Name, sizeof(Name), "hole_%02x.bin",
718 HoleNum++);
719 fd = open(Name, O_RDWR | O_CREAT | O_TRUNC,
720 S_IRUSR | S_IWUSR);
721 if (fd < 0) {
722 fprintf(stderr,
723 "Error: unable to open %s: %s\n\n",
724 Name, strerror(errno));
725 continue;
726 }
727 write(fd, BIOSImage + Base, Length);
728 close(fd);
729 } else {
730 fprintf(stderr, "\tUnknown FFV module GUID: %s\n",
731 guid);
732 }
733 }
734}
735
736void PhoenixFFVDirectory(unsigned char *BIOSImage, int BIOSLength, int Offset)
737{
738 char Name[16];
Joshua Roys563d19c2011-10-26 12:30:06 -0400739 uint32_t Length;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400740 struct PhoenixFFVModule *Module;
Joshua Roys563d19c2011-10-26 12:30:06 -0400741
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400742 Module = (struct PhoenixFFVModule *)(BIOSImage + Offset);
Joshua Roys563d19c2011-10-26 12:30:06 -0400743
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400744 if (Module->Signature != 0xF8) {
745 fprintf(stderr, "Error: Invalid module signature at 0x%05X\n",
746 Offset);
747 return;
748 }
Joshua Roys563d19c2011-10-26 12:30:06 -0400749
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400750 Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo;
Joshua Roys563d19c2011-10-26 12:30:06 -0400751
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400752 if ((Offset + Length) > BIOSLength) {
753 fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n",
754 Offset);
755 return;
Joshua Roys563d19c2011-10-26 12:30:06 -0400756 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400757
758 /* get rid of the pesky 0xFF in the middle of the name */
759 memcpy(Name, Module->Name, 8);
760 memcpy(Name + 8, Module->Name + 9, 7);
761 Name[15] = '\0';
762 if (!strcmp(Name, "volumedir.bin")) {
763 PhoenixVolume1(BIOSImage, BIOSLength, Offset, Length);
764 } else if (!strcmp(Name, "volumedir.bin2")) {
765 PhoenixVolume2(BIOSImage, BIOSLength, Offset);
766 } else {
767 fprintf(stderr,
768 "FFV points to something other than the volumedir: %s\n",
769 Name);
Joshua Roys563d19c2011-10-26 12:30:06 -0400770 }
Joshua Roysa52d11f2012-09-12 12:58:20 -0400771}
Joshua Roys563d19c2011-10-26 12:30:06 -0400772
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400773Bool PhoenixFFV(unsigned char *BIOSImage, int BIOSLength, struct PhoenixID *FFV)
Joshua Roysa52d11f2012-09-12 12:58:20 -0400774{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400775 uint32_t Offset;
Joshua Roysa52d11f2012-09-12 12:58:20 -0400776
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400777 Offset =
778 le32toh(*((uint32_t *) (((char *)FFV) + 0xA))) & (BIOSLength - 1);
Joshua Roysa52d11f2012-09-12 12:58:20 -0400779
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400780 if (!Offset) {
781 fprintf(stderr, "BCPFFV module offset is NULL.\n");
782 return FALSE;
783 }
Joshua Roysa52d11f2012-09-12 12:58:20 -0400784
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400785 PhoenixFFVDirectory(BIOSImage, BIOSLength, Offset);
Joshua Roysa52d11f2012-09-12 12:58:20 -0400786
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400787 return TRUE;
Joshua Roys563d19c2011-10-26 12:30:06 -0400788}
789
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200790/*
791 *
792 */
793Bool
794PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset,
Luc Verhaegen15587352009-06-26 10:00:29 +0200795 uint32_t Offset1, uint32_t BCPSegmentOffset)
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200796{
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400797 struct PhoenixID *ID, *SYS = NULL, *FFV = NULL;
798 uint32_t Offset;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200799
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400800 printf("Found Phoenix BIOS \"%s\"\n", (char *)(BIOSImage + Offset1));
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200801
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400802 /* TODO: Print more information about image */
803 /* TODO: Group modules by firmware volumes */
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400804
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400805 /*
806 * For newer Phoenix BIOSes, the BIOS has a trailing block that does not
807 * match the signature as tested in PhoenixModule. We adjust the length
808 * variable to handle that scenario. For example try the new BIOSes for the
809 * SuperMicro motherboards X7DA8 and X7DB8. X7DB8 is supported by Coreboot.
810 */
811 if (BIOSLength > 0x100000 && BIOSOffset > 0) {
812 BIOSLength = BIOSLength + BIOSOffset - 0x100000;
813 }
Vikas N Kumar8380f0c2012-11-13 01:11:37 -0500814
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400815 for (ID = (struct PhoenixID *)(BIOSImage + BCPSegmentOffset + 10);
816 ((void *)ID < (void *)(BIOSImage + BIOSLength)) && ID->Name[0];
817 ID =
818 (struct PhoenixID *)(((unsigned char *)ID) +
819 le16toh(ID->Length))) {
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200820#if 0
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400821 printf
822 ("PhoenixID: Name %c%c%c%c%c%c, Flags 0x%04X, Length %d\n",
823 ID->Name[0], ID->Name[1], ID->Name[2], ID->Name[3],
824 ID->Name[4], ID->Name[5], le16toh(ID->Flags),
825 le16toh(ID->Length));
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200826#endif
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400827 if (!strncmp(ID->Name, "BCPSYS", 6)) {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400828 SYS = ID;
Uwe Kleine-König3c911f72015-06-09 19:59:38 +0200829 if (FFV)
830 break;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400831 } else if (!strncmp(ID->Name, "BCPFFV", 6)) {
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400832 FFV = ID;
Uwe Kleine-König3c911f72015-06-09 19:59:38 +0200833 if (SYS)
834 break;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400835 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400836 }
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200837
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400838 if (!SYS) {
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400839 fprintf(stderr, "Error: Failed to locate BCPSYS offset.\n");
840 return FALSE;
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400841 }
842
843 /* BCPCMP parsing */
844
845 unsigned char *bcpcmp = memmem(BIOSImage, BIOSLength - 6, "BCPCMP", 6);
846 if (!bcpcmp) {
847 fprintf(stderr, "Error: Failed to locate BCPCMP offset.\n");
848 return FALSE;
849 }
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400850
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400851 uint32_t bcpoff = bcpcmp - BIOSImage;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400852 struct bcpCompress *bcpComp =
853 (struct bcpCompress *)(BIOSImage + bcpoff);
Anton Kochkovb09d0ef2012-12-21 07:17:56 +0400854 phx.compression = bcpComp->alg;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200855
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400856 /* Get some info */
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200857 char Date[9], Time[9], Version[9];
858
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400859 strncpy(Date, ((char *)SYS) + 0x0F, 8);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200860 Date[8] = 0;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400861 strncpy(Time, ((char *)SYS) + 0x18, 8);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200862 Time[8] = 0;
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400863 strncpy(Version, ((char *)SYS) + 0x37, 8);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200864 Version[8] = 0;
865
866 printf("Version \"%s\", created on %s at %s.\n", Version, Date, Time);
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200867
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400868 Offset = le32toh(*((uint32_t *) (((char *)SYS) + 0x77)));
869 Offset &= (BIOSLength - 1);
870 if (!Offset) {
871 fprintf(stderr, "BCPSYS module offset is NULL.\n");
872 if (!FFV) {
873 return FALSE;
874 }
875 return PhoenixFFV(BIOSImage, BIOSLength, FFV);
Joshua Roys563d19c2011-10-26 12:30:06 -0400876 }
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200877
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400878 while (Offset) {
879 Offset = PhoenixModule(BIOSImage, BIOSLength, Offset);
880 Offset &= BIOSLength - 1;
881 }
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200882
Anton Kochkov8f9427f2013-04-07 13:20:30 +0400883 return TRUE;
Luc Verhaegen038e6a52009-06-24 14:18:29 +0200884}