>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(-)

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) {
-- 
1.7.1

