Patchworkβ [PATCH/bios_extract] Add support for Phoenix FFV layouts

login
register
about
Submitter Joshua Roys
Date 2011-10-26 16:40:15
Message ID <4EA837EF.1020805@gmail.com>
Download mbox | patch
Permalink /patch/3444/
State Not Applicable
Headers show

Comments

Joshua Roys - 2011-10-26 16:40:15
Hello,

This patch was developed through some guesswork and by examining the 
debug output of "official" tools.  Expected output looks like:

Using file "../882.bin" (1024kB)
Found Phoenix BIOS "PhoenixBIOS 4.0 Release 6.1     "
Version "DEVEL972", created on 07/07/08 at 15:37:46.
BCPSYS module offset is NULL.
FFV modules: 5
[ 0]: (00000000-00080000) FED91FBA-D37B-4EEA-8729-2EF29FB37A78
   _R00            (00000000-0000819A) 0000819A 00 02 oprom_00.rom
   _R01            (0000819A-0000FA31) 00007897 00 02 oprom_01.rom
   _H00            (0000FA31-00014EC3) 00005492 00 02 tcpa_H_00.rom
   _Q00            (00014EC3-000193B3) 000044F0 00 02 tcpa_Q_00.rom
   _A00            (000193B3-0001B034) 00001C81 00 02 acpi_00.rom
   _C00            (0001B038-0001C860) 00001828 02 01 update_00.rom
   _B05            (0001C860-00027F12) 0000B6B2 00 02 bioscode_05.rom
   _B02            (00027F12-00031351) 0000943F 00 02 bioscode_02.rom
   _B03            (00031351-00039238) 00007EE7 00 02 bioscode_03.rom
   _B00            (00039238-0003E135) 00004EFD 00 02 bioscode_00.rom
   _K00            (0003E135-00042B33) 000049FE 00 02 tcpa_K_00.rom
   _T00            (00042B33-000471A2) 0000466F 00 02 template_00.rom
   _E00            (000471A2-0004B1F0) 0000404E 00 02 setup_00.rom
   _S00            (0004B1F0-0004EE85) 00003C95 00 02 strings_00.rom
   _L01            (0004EE85-0005212D) 000032A8 00 02 logo_01.rom
   _B04            (0005212D-00054DDE) 00002CB1 00 02 bioscode_04.rom
   _B06            (00054DDE-00057826) 00002A48 00 02 bioscode_06.rom
   _B01            (00057826-000598EE) 000020C8 00 02 bioscode_01.rom
   _M00            (000598EE-0005AC93) 000013A5 00 02 miser_00.rom
   _D00            (0005AC93-0005B82E) 00000B9B 00 02 display_00.rom
   _Y00            (0005B82E-0005C1E2) 000009B4 00 02 _Y00.rom
   _L00            (0005C1E2-0005CAA3) 000008C1 00 02 logo_00.rom
   _G00            (0005CAA3-0005CF07) 00000464 00 02 decompcode_00.rom
   _A01            (0005CF07-0005CF7F) 00000078 00 02 acpi_01.rom
   _A02            (0005CF7F-0005CFD1) 00000052 00 02 acpi_02.rom
   volumeinfo.bin  (0005CFD1-0005D031) 00000060 00 01 volumeinfo.bin
   GAP             (0005D031-00080000) 00022FCF 00 F0
[ 1]: (00080000-00082000) FD21E8FD-2525-4A95-BB90-47EC5763FF9E
   ESCD
[ 2]: (00082000-00090000) F6AE0F63-5F8C-4316-A2EA-76B9AF762756
   Hole (raw code)
[ 3]: (00090000-000F7000) FED91FBA-D37B-4EEA-8729-2EF29FB37A78
   volumedir.bin2  (00090008-00090190) 00000188 42 01 volumedir.bin2
   volumeinfo.bin  (00090190-000901F0) 00000060 00 01 volumeinfo.bin
   GAP             (000901F0-000DFFE8) 0004FDF8 00 F0
   _$00            (000DFFE8-000E0004) 0000001C 40 01 _$00.rom
   _X01            (000E0008-000E9750) 00009748 40 01 romexec_01.rom
   GAP             (000E9750-000ED6E8) 00003F98 00 F0
   _X00            (000ED6E8-000F6000) 00008918 40 01 romexec_00.rom
   GAP             (000F6000-000F7000) 00001000 00 F0
[ 4]: (000F7000-00100000) F6AE0F63-5F8C-4316-A2EA-76B9AF762756
   Hole (raw code)

Note that many newer Phoenix BIOSes have a trailer containing flash 
enable information, etc. which must be stripped off for this code to work.

ESCD seems to be "Extended System Configuration Data" (dumped to 
ESCD.bin) and all holes are dumped to hole_%02x.bin.

Signed-off-by: Joshua Roys <roysjosh@gmail.com>
From 952b21e8da57938da65596a5aec047375b60dd87 Mon Sep 17 00:00:00 2001
From: Joshua Roys <roysjosh@gmail.com>
Date: Wed, 26 Oct 2011 12:30:06 -0400
Subject: [PATCH] Add support for Phoenix FFV layouts

Signed-off-by: Joshua Roys <roysjosh@gmail.com>
---
 phoenix.c |  281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 268 insertions(+), 13 deletions(-)

Patch

diff --git a/phoenix.c b/phoenix.c
index 2da84b7..b1dedf1 100644
--- a/phoenix.c
+++ b/phoenix.c
@@ -66,6 +66,22 @@  PhoenixModuleNames[] = {
     {'J', "SmartCardPAS"},
 };
 
+struct PhoenixID {
+    char Name[6];
+    uint16_t Flags;
+    uint16_t Length;
+};
+
+struct PhoenixFFVModule {
+    uint8_t Signature;
+    uint8_t Flags;
+    uint16_t Checksum;
+    uint16_t LengthLo;
+    uint8_t LengthHi;
+    uint8_t Compression;
+    char Name[16];
+};
+
 static char *
 PhoenixModuleNameGet(char Id)
 {
@@ -238,6 +254,244 @@  PhoenixModule(unsigned char *BIOSImage, int BIOSLength, int Offset)
     return le32toh(Module->Previous);
 }
 
+static int
+PhoenixExtractFFV(unsigned char *BIOSImage, int BIOSLength, int Offset)
+{
+    struct PhoenixFFVCompressionHeader {
+	uint16_t TotalLengthLo;
+	uint8_t TotalLengthHi;
+	uint8_t Unk1;
+
+	uint16_t PackedLenLo;
+	uint8_t PackedLenHi;
+	uint8_t Unk2;
+
+	uint16_t RealLenLo;
+	uint8_t RealLenHi;
+	uint8_t Unk3;
+    } *CompHeader;
+    struct PhoenixFFVModule *Module;
+    char Name[16], filename[24];
+    char *ModuleName;
+    int fd;
+    uint32_t Length, PackedLen, RealLen;
+    unsigned char *RealData;
+
+    Module = (struct PhoenixFFVModule *) (BIOSImage + Offset);
+
+    if (Module->Signature != 0xF8) {
+	/* ignore and move on to the next byte... */
+	return 1;
+    }
+
+    Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo;
+    if ((Offset + ((le16toh(Module->LengthHi) << 16) | Module->LengthLo)) > BIOSLength) {
+	fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n",
+		Offset);
+	return 1;
+    }
+
+    if (Module->Compression == 0xF0) {
+	strcpy(Name, "GAP");
+	filename[0] = '\0';
+    }
+    else {
+	/* get rid of the pesky 0xFF in the middle of the name */
+	memcpy(Name, Module->Name, 8);
+	memcpy(Name + 8, Module->Name + 9, 7);
+	Name[15] = '\0';
+
+	if (Name[0] == '_') {
+	    ModuleName = PhoenixModuleNameGet(Name[1]);
+	    if (ModuleName) {
+		snprintf(filename, sizeof(filename), "%s_%c%c.rom", ModuleName, Name[2], Name[3]);
+	    }
+	    else {
+		snprintf(filename, sizeof(filename), "%s.rom", Name);
+	    }
+	}
+	else {
+	    strncpy(filename, Name, sizeof(filename));
+	}
+    }
+
+    printf("\t%-15s (%08X-%08X) %08X %02X %02X %s\n",
+	   Name, Offset, Offset + Length, Length, Module->Flags, Module->Compression, filename
+    );
+
+    switch (Module->Compression) {
+    case 0xF0:
+	break;
+
+    case 0x02:
+	if (Name[1] == 'G') {
+	    break;
+	}
+	else {
+	    CompHeader = (struct PhoenixFFVCompressionHeader *) (BIOSImage + Offset + 0x18);
+	}
+	/* some blocks have a (8 byte?) header we need to skip */
+	if (CompHeader->TotalLengthLo != Length - 0x18) {
+	    CompHeader = (struct PhoenixFFVCompressionHeader *)
+		((unsigned char *)CompHeader + CompHeader->TotalLengthLo);
+	}
+	PackedLen = (CompHeader->PackedLenHi << 16) | CompHeader->PackedLenLo;
+	RealLen = (CompHeader->RealLenHi << 16) | CompHeader->RealLenLo;
+	RealData = MMapOutputFile(filename, RealLen);
+	if (!RealData) {
+	    fprintf(stderr, "Failed to mmap file for uncompressed data.\n");
+	    break;
+	}
+	LH5Decode((unsigned char *)CompHeader + sizeof(struct PhoenixFFVCompressionHeader),
+		  PackedLen, RealData, RealLen);
+	munmap(RealData, RealLen);
+	break;
+
+    case 0x01:
+	fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	if (fd < 0) {
+	    fprintf(stderr, "Error: unable to open %s: %s\n\n", filename, strerror(errno));
+	    break;
+	}
+	write(fd, BIOSImage + Offset + 0x18, Length - 0x18);
+	close(fd);
+	break;
+
+    default:
+	fprintf(stderr, "\t\tUnsupported compression type!\n");
+	break;
+    }
+
+    return Length;
+}
+
+void
+PhoenixFFVDirectory(unsigned char *BIOSImage, int BIOSLength, int Offset)
+{
+    struct PhoenixVolumeDirEntry {
+	/* these are stored little endian */
+	uint32_t guid1;
+	uint16_t guid2;
+	uint16_t guid3;
+	/* these are big endian */
+	uint16_t guid4;
+	/*uint48_t guid5;*/
+	uint16_t guid5;
+	uint32_t guid6;
+
+	uint32_t Base;
+	uint32_t Length;
+    };
+    struct PhoenixVolumeDir {
+	uint16_t Unk1;
+	uint16_t Unk2;
+	uint32_t Length;
+	struct PhoenixVolumeDirEntry Modules[];
+    } *Volume;
+    struct PhoenixFFVModule *Module;
+    char Name[16], guid[37];
+    int fd, HoleNum = 0;
+    uint32_t Base, Length, NumModules, ModNum;
+
+    Module = (struct PhoenixFFVModule *) (BIOSImage + Offset);
+    Volume = (struct PhoenixVolumeDir *) (BIOSImage + Offset + 0x18);
+
+    if (Module->Signature != 0xF8) {
+	fprintf(stderr, "Error: Invalid module signature at 0x%05X\n",
+		Offset);
+	return;
+    }
+
+    Length = (le16toh(Module->LengthHi) << 16) | Module->LengthLo;
+    if ((Offset + Length) > BIOSLength) {
+	fprintf(stderr, "Error: Module overruns buffer at 0x%05X\n",
+		Offset);
+	return;
+    }
+
+    /* get rid of the pesky 0xFF in the middle of the name */
+    memcpy(Name, Module->Name, 8);
+    memcpy(Name + 8, Module->Name + 9, 7);
+    Name[15] = '\0';
+    if (strcmp(Name, "volumedir.bin2")) {
+	fprintf(stderr, "FFV points to something other than volumedir.bin2: %s\n", Name);
+	return;
+    }
+
+    NumModules = (Volume->Length - 8) / sizeof(struct PhoenixVolumeDirEntry);
+    printf("FFV modules: %u\n", NumModules);
+
+    for (ModNum = 0; ModNum < NumModules; ModNum++)
+    {
+	sprintf(guid, "%08X-%04X-%04X-%04X-%04X%08X",
+		le32toh(Volume->Modules[ModNum].guid1),
+		le16toh(Volume->Modules[ModNum].guid2),
+		le16toh(Volume->Modules[ModNum].guid3),
+		be16toh(Volume->Modules[ModNum].guid4),
+		be16toh(Volume->Modules[ModNum].guid5),
+		be32toh(Volume->Modules[ModNum].guid6)
+	);
+	Base = Volume->Modules[ModNum].Base & (BIOSLength - 1);
+	Length = Volume->Modules[ModNum].Length;
+	printf("[%2u]: (%08X-%08X) %s\n",
+		ModNum, Base, Base + Length, guid
+	);
+
+	if (!strcmp(guid, "FED91FBA-D37B-4EEA-8729-2EF29FB37A78")) {
+	    /* FFV modules */
+	    Offset = Base;
+	    while (Offset < Base + Length) {
+		Offset += PhoenixExtractFFV(BIOSImage, BIOSLength, Offset);
+	    }
+	}
+	else if (!strcmp(guid, "FD21E8FD-2525-4A95-BB90-47EC5763FF9E")) {
+	    /* Extended System Configuration Data (and similar?) */
+	    printf("\tESCD\n");
+	    fd = open("ESCD.bin", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	    if (fd < 0) {
+		fprintf(stderr, "Error: unable to open ESCD.bin: %s\n\n", strerror(errno));
+		continue;
+	    }
+	    write(fd, BIOSImage + Base, Length);
+	    close(fd);
+	}
+	else if (!strcmp(guid, "F6AE0F63-5F8C-4316-A2EA-76B9AF762756")) {
+	    /* Raw BIOS code */
+	    printf("\tHole (raw code)\n");
+	    snprintf(Name, sizeof(Name), "hole_%02x.bin", HoleNum++);
+	    fd = open(Name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	    if (fd < 0) {
+		fprintf(stderr, "Error: unable to open %s: %s\n\n", Name, strerror(errno));
+		continue;
+	    }
+	    write(fd, BIOSImage + Base, Length);
+	    close(fd);
+	}
+	else {
+	    fprintf(stderr, "\tUnknown FFV module GUID!\n");
+	}
+    }
+
+    return;
+}
+
+Bool
+PhoenixFFV(unsigned char *BIOSImage, int BIOSLength, struct PhoenixID *FFV)
+{
+    uint32_t Offset;
+
+    Offset = le32toh(*((uint32_t *) (((char *) FFV) + 0xA))) & (BIOSLength - 1);
+
+    if (!Offset) {
+	fprintf(stderr, "BCPFFV module offset is NULL.\n");
+	return FALSE;
+    }
+
+    PhoenixFFVDirectory(BIOSImage, BIOSLength, Offset);
+
+    return TRUE;
+}
+
 /*
  *
  */
@@ -245,11 +499,7 @@  Bool
 PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset,
 	       uint32_t Offset1, uint32_t BCPSegmentOffset)
 {
-    struct PhoenixID {
-	char Name[6];
-	uint16_t Flags;
-	uint16_t Length;
-    } *ID;
+    struct PhoenixID *ID, *SYS = NULL, *FFV = NULL;
     uint32_t Offset;
 
     printf("Found Phoenix BIOS \"%s\"\n", (char *) (BIOSImage + Offset1));
@@ -263,10 +513,12 @@  PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset,
 	       ID->Name[4],  ID->Name[5], le16toh(ID->Flags), le16toh(ID->Length));
 #endif
 	if (!strncmp(ID->Name, "BCPSYS", 6))
-	    break;
+	    SYS = ID;
+	else if (!strncmp(ID->Name, "BCPFFV", 6))
+	    FFV = ID;
     }
 
-    if (!ID->Name[0] || ((void *) ID >= (void *) (BIOSImage + BIOSLength))) {
+    if (!SYS) {
 	fprintf(stderr, "Error: Failed to locate BCPSYS offset.\n");
 	return FALSE;
     }
@@ -275,21 +527,24 @@  PhoenixExtract(unsigned char *BIOSImage, int BIOSLength, int BIOSOffset,
     {
 	char Date[9], Time[9], Version[9];
 
-	strncpy(Date, ((char *) ID) + 0x0F, 8);
+	strncpy(Date, ((char *) SYS) + 0x0F, 8);
 	Date[8] = 0;
-	strncpy(Time, ((char *) ID) + 0x18, 8);
+	strncpy(Time, ((char *) SYS) + 0x18, 8);
 	Time[8] = 0;
-	strncpy(Version, ((char *) ID) + 0x37, 8);
+	strncpy(Version, ((char *) SYS) + 0x37, 8);
 	Version[8] = 0;
 
 	printf("Version \"%s\", created on %s at %s.\n", Version, Date, Time);
     }
 
-    Offset = le32toh(*((uint32_t *) (((char *) ID) + 0x77)));
+    Offset = le32toh(*((uint32_t *) (((char *) SYS) + 0x77)));
     Offset &= (BIOSLength - 1);
     if (!Offset) {
-	fprintf(stderr, "Error: retrieved invalid Modules offset.\n");
-	return FALSE;
+	fprintf(stderr, "BCPSYS module offset is NULL.\n");
+	if (!FFV) {
+	    return FALSE;
+	}
+	return PhoenixFFV(BIOSImage, BIOSLength, FFV);
     }
 
     while (Offset) {