Add ich_descriptor_tool to decode all flash descriptors stored in a flash dump file

This patch adds an external utility that shares most of the existing descriptor
decoding source code. Additionally to what is available via FDOC/FDOD this
allows to access:
 - the softstraps which are used to configure the chipset by flash content
   without the need for BIOS routines. on ICH8 it is possible to read those
   with FDOC/FDOC too, but this was removed in later chipsets.
 - the ME VSCC (Vendor Specific Component Capabilities) table. simply put,
   this is an SPI chip database used to figure out the flash's capabilities.
 - the MAC address stored in the GbE image.

Intel thinks this information should be confidential for ICH9 and up, but
references some tidbits in their public documentation.
This patch includes the human-readable information for ICH8, Ibex Peak
(5 series) and Cougar Point (6 series); the latter two were obtained from
leaked "SPI Flash Programming Guides" found by google. Data regarding ICH9
and 10 is unknown to us yet. It can probably found in:
"Intel® ICH7, ICH8, ICH9 and ICH10 — SPI Family Flash Programming Guide"
Information regarding the upcoming Panther Point chipset is also not included.

Corresponding to flashrom svn r1480.

Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Matthias Wenzel <bios@mazzoo.de>
diff --git a/ich_descriptors.c b/ich_descriptors.c
index 125077c..726f5e0 100644
--- a/ich_descriptors.c
+++ b/ich_descriptors.c
@@ -22,9 +22,29 @@
 #if defined(__i386__) || defined(__x86_64__)
 
 #include "ich_descriptors.h"
+
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+#include <stdio.h>
+#define print(t, ...) printf(__VA_ARGS__)
+#define DESCRIPTOR_MODE_SIGNATURE 0x0ff0a55a
+/* The upper map is located in the word before the 256B-long OEM section at the
+ * end of the 4kB-long flash descriptor.
+ */
+#define UPPER_MAP_OFFSET (4096 - 256 - 4)
+#define getVTBA(flumap)	(((flumap)->FLUMAP1 << 4) & 0x00000ff0)
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
 #include "flash.h" /* for msg_* */
 #include "programmer.h"
 
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+
+#ifndef min
+#define min(a, b) (a < b) ? a : b
+#endif
+
 void prettyprint_ich_reg_vscc(uint32_t reg_val, int verbosity)
 {
 	print(verbosity, "BES=0x%x, ",	(reg_val & VSCC_BES)  >> VSCC_BES_OFF);
@@ -47,6 +67,12 @@
 	prettyprint_ich_descriptor_component(desc);
 	prettyprint_ich_descriptor_region(desc);
 	prettyprint_ich_descriptor_master(&desc->master);
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+	if (cs >= CHIPSET_ICH8) {
+		prettyprint_ich_descriptor_upper_map(&desc->upper);
+		prettyprint_ich_descriptor_straps(cs, desc);
+	}
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
 }
 
 void prettyprint_ich_descriptor_content(const struct ich_desc_content *cont)
@@ -213,6 +239,506 @@
 	msg_pdbg2("\n");
 }
 
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+void prettyprint_ich_descriptor_straps_ich8(const struct ich_descriptors *desc)
+{
+	static const char * const str_GPIO12[4] = {
+		"GPIO12",
+		"LAN PHY Power Control Function (Native Output)",
+		"GLAN_DOCK# (Native Input)",
+		"invalid configuration",
+	};
+
+	msg_pdbg2("--- MCH details ---\n");
+	msg_pdbg2("ME B is %sabled.\n", desc->north.ich8.MDB ? "dis" : "en");
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- ICH details ---\n");
+	msg_pdbg2("ME SMBus Address 1: 0x%02x\n", desc->south.ich8.ASD);
+	msg_pdbg2("ME SMBus Address 2: 0x%02x\n", desc->south.ich8.ASD2);
+	msg_pdbg2("ME SMBus Controller is connected to the %s.\n",
+		  desc->south.ich8.MESM2SEL ? "SMLink pins" : "SMBus pins");
+	msg_pdbg2("SPI CS1 is used for %s.\n",
+		  desc->south.ich8.SPICS1_LANPHYPC_SEL ?
+		  "LAN PHY Power Control Function" :
+		  "SPI Chip Select");
+	msg_pdbg2("GPIO12 is used as %s.\n",
+		  str_GPIO12[desc->south.ich8.GPIO12_SEL]);
+	msg_pdbg2("PCIe Port 6 is used for %s.\n",
+	     desc->south.ich8.GLAN_PCIE_SEL ? "integrated LAN" : "PCI Express");
+	msg_pdbg2("%sn BMC Mode: "
+		  "Intel AMT SMBus Controller 1 is connected to %s.\n",
+		  desc->south.ich8.BMCMODE ? "I" : "Not i",
+		  desc->south.ich8.BMCMODE ? "SMLink" : "SMBus");
+	msg_pdbg2("TCO is in %s Mode.\n",
+	       desc->south.ich8.TCOMODE ? "Advanced TCO" : "Legacy/Compatible");
+	msg_pdbg2("ME A is %sabled.\n",
+		  desc->south.ich8.ME_DISABLE ? "dis" : "en");
+	msg_pdbg2("\n");
+}
+
+static void prettyprint_ich_descriptor_straps_56_pciecs(uint8_t conf, uint8_t off)
+{
+	msg_pdbg2("PCI Express Port Configuration Strap %d: ", off+1);
+
+	off *= 4;
+	switch(conf){
+	case 0:
+		msg_pdbg2("4x1 Ports %d-%d (x1)", 1+off, 4+off);
+		break;
+	case 1:
+		msg_pdbg2("1x2, 2x1 Port %d (x2), Port %d (disabled), "
+			  "Ports %d, %d (x1)", 1+off, 2+off, 3+off, 4+off);
+		break;
+	case 2:
+		msg_pdbg2("2x2 Port %d (x2), Port %d (x2), Ports "
+			  "%d, %d (disabled)", 1+off, 3+off, 2+off, 4+off);
+		break;
+	case 3:
+		msg_pdbg2("1x4 Port %d (x4), Ports %d-%d (disabled)",
+			  1+off, 2+off, 4+off);
+		break;
+	}
+	msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_pchstraps45678_56(const struct ich_desc_south_strap *s)
+{
+	/* PCHSTRP4 */
+	msg_pdbg2("Intel PHY is %s.\n",
+		  (s->ibex.PHYCON == 2) ? "connected" :
+			  (s->ibex.PHYCON == 0) ? "disconnected" : "reserved");
+	msg_pdbg2("GbE MAC SMBus address is %sabled.\n",
+		  s->ibex.GBEMAC_SMBUS_ADDR_EN ? "en" : "dis");
+	msg_pdbg2("GbE MAC SMBus address: 0x%02x\n",
+		  s->ibex.GBEMAC_SMBUS_ADDR);
+	msg_pdbg2("GbE PHY SMBus address: 0x%02x\n",
+		  s->ibex.GBEPHY_SMBUS_ADDR);
+
+	/* PCHSTRP5 */
+	/* PCHSTRP6 */
+	/* PCHSTRP7 */
+	msg_pdbg2("Intel ME SMBus Subsystem Vendor ID: 0x%04x\n",
+		  s->ibex.MESMA2UDID_VENDOR);
+	msg_pdbg2("Intel ME SMBus Subsystem Device ID: 0x%04x\n",
+		  s->ibex.MESMA2UDID_VENDOR);
+
+	/* PCHSTRP8 */
+}
+
+void prettyprint_ich_descriptor_pchstraps111213_56(const struct ich_desc_south_strap *s)
+{
+	/* PCHSTRP11 */
+	msg_pdbg2("SMLink1 GP Address is %sabled.\n",
+		  s->ibex.SML1GPAEN ? "en" : "dis");
+	msg_pdbg2("SMLink1 controller General Purpose Target address: 0x%02x\n",
+		  s->ibex.SML1GPA);
+	msg_pdbg2("SMLink1 I2C Target address is %sabled.\n",
+		  s->ibex.SML1I2CAEN ? "en" : "dis");
+	msg_pdbg2("SMLink1 I2C Target address: 0x%02x\n",
+		  s->ibex.SML1I2CA);
+
+	/* PCHSTRP12 */
+	/* PCHSTRP13 */
+}
+
+void prettyprint_ich_descriptor_straps_ibex(const struct ich_desc_south_strap *s)
+{
+	static const uint8_t const dec_t209min[4] = {
+		100,
+		50,
+		5,
+		1
+	};
+
+	msg_pdbg2("--- PCH ---\n");
+
+	/* PCHSTRP0 */
+	msg_pdbg2("Chipset configuration Softstrap 2: %d\n", s->ibex.cs_ss2);
+	msg_pdbg2("Intel ME SMBus Select is %sabled.\n",
+		  s->ibex.SMB_EN ? "en" : "dis");
+	msg_pdbg2("SMLink0 segment is %sabled.\n",
+		  s->ibex.SML0_EN ? "en" : "dis");
+	msg_pdbg2("SMLink1 segment is %sabled.\n",
+		  s->ibex.SML1_EN ? "en" : "dis");
+	msg_pdbg2("SMLink1 Frequency: %s\n",
+		  (s->ibex.SML1FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("Intel ME SMBus Frequency: %s\n",
+		  (s->ibex.SMB0FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("SMLink0 Frequency: %s\n",
+		  (s->ibex.SML0FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("GPIO12 is used as %s.\n", s->ibex.LANPHYPC_GP12_SEL ?
+		  "LAN_PHY_PWR_CTRL" : "general purpose output");
+	msg_pdbg2("Chipset configuration Softstrap 1: %d\n", s->ibex.cs_ss1);
+	msg_pdbg2("DMI RequesterID Checks are %sabled.\n",
+		  s->ibex.DMI_REQID_DIS ? "en" : "dis");
+	msg_pdbg2("BIOS Boot-Block size (BBBS): %d kB.\n",
+		  1 << (6 + s->ibex.BBBS));
+
+	/* PCHSTRP1 */
+	msg_pdbg2("Chipset configuration Softstrap 3: 0x%x\n", s->ibex.cs_ss3);
+
+	/* PCHSTRP2 */
+	msg_pdbg2("ME SMBus ASD address is %sabled.\n",
+		  s->ibex.MESMASDEN ? "en" : "dis");
+	msg_pdbg2("ME SMBus Controller ASD Target address: 0x%02x\n",
+		  s->ibex.MESMASDA);
+	msg_pdbg2("ME SMBus I2C address is %sabled.\n",
+		  s->ibex.MESMI2CEN ? "en" : "dis");
+	msg_pdbg2("ME SMBus I2C target address: 0x%02x\n",
+		  s->ibex.MESMI2CA);
+
+	/* PCHSTRP3 */
+	prettyprint_ich_descriptor_pchstraps45678_56(s);
+	/* PCHSTRP9 */
+	prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 0);
+	prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 1);
+	msg_pdbg2("PCIe Lane Reversal 1: PCIe Lanes 0-3 are %sreserved.\n",
+		  s->ibex.PCIELR1 ? "" : "not ");
+	msg_pdbg2("PCIe Lane Reversal 2: PCIe Lanes 4-7 are %sreserved.\n",
+		  s->ibex.PCIELR2 ? "" : "not ");
+	msg_pdbg2("DMI Lane Reversal: DMI Lanes 0-3 are %sreserved.\n",
+		  s->ibex.DMILR ? "" : "not ");
+	msg_pdbg2("Default PHY PCIe Port is %d.\n", s->ibex.PHY_PCIEPORTSEL+1);
+	msg_pdbg2("Integrated MAC/PHY communication over PCIe is %sabled.\n",
+		  s->ibex.PHY_PCIE_EN ? "en" : "dis");
+
+	/* PCHSTRP10 */
+	msg_pdbg2("Management Engine will boot from %sflash.\n",
+		  s->ibex.ME_BOOT_FLASH ? "" : "ROM, then ");
+	msg_pdbg2("Chipset configuration Softstrap 5: %d\n", s->ibex.cs_ss5);
+	msg_pdbg2("Virtualization Engine Enable 1 is %sabled.\n",
+		  s->ibex.VE_EN ? "en" : "dis");
+	msg_pdbg2("ME Memory-attached Debug Display Device is %sabled.\n",
+		  s->ibex.MMDDE ? "en" : "dis");
+	msg_pdbg2("ME Memory-attached Debug Display Device address: 0x%02x\n",
+		  s->ibex.MMADDR);
+	msg_pdbg2("Chipset configuration Softstrap 7: %d\n", s->ibex.cs_ss7);
+	msg_pdbg2("Integrated Clocking Configuration is %d.\n",
+		  (s->ibex.ICC_SEL == 7) ? 0 : s->ibex.ICC_SEL);
+	msg_pdbg2("PCH Signal CL_RST1# does %sassert when Intel ME performs a "
+		  "reset.\n", s->ibex.MER_CL1 ? "" : "not ");
+
+	prettyprint_ich_descriptor_pchstraps111213_56(s);
+
+	/* PCHSTRP14 */
+	msg_pdbg2("Virtualization Engine Enable 2 is %sabled.\n",
+		  s->ibex.VE_EN2 ? "en" : "dis");
+	msg_pdbg2("Virtualization Engine will boot from %sflash.\n",
+		  s->ibex.VE_BOOT_FLASH ? "" : "ROM, then ");
+	msg_pdbg2("Braidwood SSD functionality is %sabled.\n",
+		  s->ibex.BW_SSD ? "en" : "dis");
+	msg_pdbg2("Braidwood NVMHCI functionality is %sabled.\n",
+		  s->ibex.NVMHCI_EN ? "en" : "dis");
+
+	/* PCHSTRP15 */
+	msg_pdbg2("Chipset configuration Softstrap 6: %d\n", s->ibex.cs_ss6);
+	msg_pdbg2("Integrated wired LAN Solution is %sabled.\n",
+		  s->ibex.IWL_EN ? "en" : "dis");
+	msg_pdbg2("t209 min Timing: %d ms\n",
+		  dec_t209min[s->ibex.t209min]);
+	msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_straps_cougar(const struct ich_desc_south_strap *s)
+{
+	msg_pdbg2("--- PCH ---\n");
+
+	/* PCHSTRP0 */
+	msg_pdbg2("Chipset configuration Softstrap 1: %d\n", s->cougar.cs_ss1);
+	msg_pdbg2("Intel ME SMBus Select is %sabled.\n",
+		  s->ibex.SMB_EN ? "en" : "dis");
+	msg_pdbg2("SMLink0 segment is %sabled.\n",
+		  s->ibex.SML0_EN ? "en" : "dis");
+	msg_pdbg2("SMLink1 segment is %sabled.\n",
+		  s->ibex.SML1_EN ? "en" : "dis");
+	msg_pdbg2("SMLink1 Frequency: %s\n",
+		  (s->ibex.SML1FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("Intel ME SMBus Frequency: %s\n",
+		  (s->ibex.SMB0FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("SMLink0 Frequency: %s\n",
+		  (s->ibex.SML0FRQ == 1) ? "100 kHz" : "reserved");
+	msg_pdbg2("GPIO12 is used as %s.\n", s->ibex.LANPHYPC_GP12_SEL ?
+		  "LAN_PHY_PWR_CTRL" : "general purpose output");
+	msg_pdbg2("LinkSec is %sabled.\n",
+		  s->cougar.LINKSEC_DIS ? "en" : "dis");
+	msg_pdbg2("DMI RequesterID Checks are %sabled.\n",
+		  s->ibex.DMI_REQID_DIS ? "en" : "dis");
+	msg_pdbg2("BIOS Boot-Block size (BBBS): %d kB.\n",
+		  1 << (6 + s->ibex.BBBS));
+
+	/* PCHSTRP1 */
+	msg_pdbg2("Chipset configuration Softstrap 3: 0x%x\n", s->ibex.cs_ss3);
+	msg_pdbg2("Chipset configuration Softstrap 2: 0x%x\n", s->ibex.cs_ss2);
+
+	/* PCHSTRP2 */
+	msg_pdbg2("ME SMBus ASD address is %sabled.\n",
+		  s->ibex.MESMASDEN ? "en" : "dis");
+	msg_pdbg2("ME SMBus Controller ASD Target address: 0x%02x\n",
+		  s->ibex.MESMASDA);
+	msg_pdbg2("ME SMBus MCTP Address is %sabled.\n",
+		  s->cougar.MESMMCTPAEN ? "en" : "dis");
+	msg_pdbg2("ME SMBus MCTP target address: 0x%02x\n",
+		  s->cougar.MESMMCTPA);
+	msg_pdbg2("ME SMBus I2C address is %sabled.\n",
+		  s->ibex.MESMI2CEN ? "en" : "dis");
+	msg_pdbg2("ME SMBus I2C target address: 0x%02x\n",
+		  s->ibex.MESMI2CA);
+
+	/* PCHSTRP3 */
+	prettyprint_ich_descriptor_pchstraps45678_56(s);
+	/* PCHSTRP9 */
+	prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 0);
+	prettyprint_ich_descriptor_straps_56_pciecs(s->ibex.PCIEPCS1, 1);
+	msg_pdbg2("PCIe Lane Reversal 1: PCIe Lanes 0-3 are %sreserved.\n",
+		  s->ibex.PCIELR1 ? "" : "not ");
+	msg_pdbg2("PCIe Lane Reversal 2: PCIe Lanes 4-7 are %sreserved.\n",
+		  s->ibex.PCIELR2 ? "" : "not ");
+	msg_pdbg2("DMI Lane Reversal: DMI Lanes 0-3 are %sreserved.\n",
+		  s->ibex.DMILR ? "" : "not ");
+	msg_pdbg2("ME Debug status writes over SMBUS are %sabled.\n",
+		  s->cougar.MDSMBE_EN ? "en" : "dis");
+	msg_pdbg2("ME Debug SMBus Emergency Mode address: 0x%02x (raw)\n",
+		  s->cougar.MDSMBE_ADD);
+	msg_pdbg2("Default PHY PCIe Port is %d.\n", s->ibex.PHY_PCIEPORTSEL+1);
+	msg_pdbg2("Integrated MAC/PHY communication over PCIe is %sabled.\n",
+		  s->ibex.PHY_PCIE_EN ? "en" : "dis");
+	msg_pdbg2("PCIe ports Subtractive Decode Agent is %sabled.\n",
+		  s->cougar.SUB_DECODE_EN ? "en" : "dis");
+	msg_pdbg2("GPIO74 is used as %s.\n", s->cougar.PCHHOT_SML1ALERT_SEL ?
+		  "PCHHOT#" : "SML1ALERT#");
+
+	/* PCHSTRP10 */
+	msg_pdbg2("Management Engine will boot from %sflash.\n",
+		  s->ibex.ME_BOOT_FLASH ? "" : "ROM, then ");
+
+	msg_pdbg2("ME Debug SMBus Emergency Mode is %sabled.\n",
+		  s->cougar.MDSMBE_EN ? "en" : "dis");
+	msg_pdbg2("ME Debug SMBus Emergency Mode Address: 0x%02x\n",
+		  s->cougar.MDSMBE_ADD);
+
+	msg_pdbg2("Integrated Clocking Configuration used: %d\n",
+		  s->cougar.ICC_SEL);
+	msg_pdbg2("PCH Signal CL_RST1# does %sassert when Intel ME performs a "
+		  "reset.\n", s->ibex.MER_CL1 ? "" : "not ");
+	msg_pdbg2("ICC Profile is selected by %s.\n",
+		  s->cougar.ICC_PRO_SEL ? "Softstraps" : "BIOS");
+	msg_pdbg2("Deep SX is %ssupported on the platform.\n",
+		  s->cougar.Deep_SX_EN ? "not " : "");
+	msg_pdbg2("ME Debug LAN Emergency Mode is %sabled.\n",
+		  s->cougar.ME_DBG_LAN ? "en" : "dis");
+
+	prettyprint_ich_descriptor_pchstraps111213_56(s);
+
+	/* PCHSTRP14 */
+	/* PCHSTRP15 */
+	msg_pdbg2("Chipset configuration Softstrap 6: %d\n", s->cougar.cs_ss6);
+	msg_pdbg2("Integrated wired LAN is %sabled.\n",
+		  s->cougar.IWL_EN ? "en" : "dis");
+	msg_pdbg2("Chipset configuration Softstrap 5: %d\n", s->cougar.cs_ss5);
+	msg_pdbg2("SMLink1 provides temperature from %s.\n",
+		  s->cougar.SMLINK1_THERM_SEL ?
+					 "PCH only" : "the CPU, PCH and DIMMs");
+	msg_pdbg2("GPIO29 is used as %s.\n", s->cougar.SLP_LAN_GP29_SEL ?
+		  "general purpose output" : "SLP_LAN#");
+
+	/* PCHSTRP16 */
+	/* PCHSTRP17 */
+	msg_pdbg2("Integrated Clock: %s Clock Mode\n",
+		  s->cougar.ICML ? "Buffered Through" : "Full Integrated");
+	msg_pdbg2("\n");
+}
+
+void prettyprint_ich_descriptor_straps(enum ich_chipset cs, const struct ich_descriptors *desc)
+{
+	unsigned int i, max;
+	msg_pdbg2("=== Softstraps ===\n");
+
+	if (sizeof(desc->north.STRPs) / 4 + 1 < desc->content.MSL) {
+		max = sizeof(desc->north.STRPs) / 4 + 1;
+		msg_pdbg2("MSL (%u) is greater than the current maximum of %u "
+			  "entries.\n", desc->content.MSL, max + 1);
+		msg_pdbg2("Only the first %u entries will be printed.\n", max);
+	} else
+		max = desc->content.MSL;
+
+	msg_pdbg2("--- North/MCH/PROC (%d entries) ---\n", max);
+	for (i = 0; i < max; i++)
+		msg_pdbg2("STRP%-2d = 0x%08x\n", i, desc->north.STRPs[i]);
+	msg_pdbg2("\n");
+
+	if (sizeof(desc->south.STRPs) / 4 < desc->content.ISL) {
+		max = sizeof(desc->south.STRPs) / 4;
+		msg_pdbg2("ISL (%u) is greater than the current maximum of %u "
+			  "entries.\n", desc->content.ISL, max);
+		msg_pdbg2("Only the first %u entries will be printed.\n", max);
+	} else
+		max = desc->content.ISL;
+
+	msg_pdbg2("--- South/ICH/PCH (%d entries) ---\n", max);
+	for (i = 0; i < max; i++)
+		msg_pdbg2("STRP%-2d = 0x%08x\n", i, desc->south.STRPs[i]);
+	msg_pdbg2("\n");
+
+	switch (cs) {
+	case CHIPSET_ICH8:
+		if (sizeof(desc->north.ich8) / 4 != desc->content.MSL)
+			msg_pdbg2("Detailed North/MCH/PROC information is "
+				  "probably not reliable, printing anyway.\n");
+		if (sizeof(desc->south.ich8) / 4 != desc->content.ISL)
+			msg_pdbg2("Detailed South/ICH/PCH information is "
+				  "probably not reliable, printing anyway.\n");
+		prettyprint_ich_descriptor_straps_ich8(desc);
+		break;
+	case CHIPSET_5_SERIES_IBEX_PEAK:
+		/* PCH straps only. PROCSTRPs are unknown. */
+		if (sizeof(desc->south.ibex) / 4 != desc->content.ISL)
+			msg_pdbg2("Detailed South/ICH/PCH information is "
+				  "probably not reliable, printing anyway.\n");
+		prettyprint_ich_descriptor_straps_ibex(&desc->south);
+		break;
+	case CHIPSET_6_SERIES_COUGAR_POINT:
+		/* PCH straps only. PROCSTRP0 is "reserved". */
+		if (sizeof(desc->south.cougar) / 4 != desc->content.ISL)
+			msg_pdbg2("Detailed South/ICH/PCH information is "
+				  "probably not reliable, printing anyway.\n");
+		prettyprint_ich_descriptor_straps_cougar(&desc->south);
+		break;
+	case CHIPSET_ICH_UNKNOWN:
+		break;
+	default:
+		msg_pdbg2("The meaning of the descriptor straps are unknown "
+			  "yet.\n\n");
+		break;
+	}
+}
+
+void prettyprint_rdid(uint32_t reg_val)
+{
+	uint8_t mid = reg_val & 0xFF;
+	uint16_t did = ((reg_val >> 16) & 0xFF) | (reg_val & 0xFF00);
+	msg_pdbg2("Manufacturer ID 0x%02x, Device ID 0x%04x\n", mid, did);
+}
+
+void prettyprint_ich_descriptor_upper_map(const struct ich_desc_upper_map *umap)
+{
+	int i;
+	msg_pdbg2("=== Upper Map Section ===\n");
+	msg_pdbg2("FLUMAP1  0x%08x\n", umap->FLUMAP1);
+	msg_pdbg2("\n");
+
+	msg_pdbg2("--- Details ---\n");
+	msg_pdbg2("VTL (length in DWORDS) = %d\n", umap->VTL);
+	msg_pdbg2("VTBA (base address)    = 0x%6.6x\n", getVTBA(umap));
+	msg_pdbg2("\n");
+
+	msg_pdbg2("VSCC Table: %d entries\n", umap->VTL/2);
+	for (i = 0; i < umap->VTL/2; i++)
+	{
+		uint32_t jid = umap->vscc_table[i].JID;
+		uint32_t vscc = umap->vscc_table[i].VSCC;
+		msg_pdbg2("  JID%d  = 0x%08x\n", i, jid);
+		msg_pdbg2("  VSCC%d = 0x%08x\n", i, vscc);
+		msg_pdbg2("    "); /* indention */
+		prettyprint_rdid(jid);
+		msg_pdbg2("    "); /* indention */
+		prettyprint_ich_reg_vscc(vscc, 0);
+	}
+	msg_pdbg2("\n");
+}
+
+/* len is the length of dump in bytes */
+int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struct ich_descriptors *desc)
+{
+	unsigned int i, max;
+	uint8_t pch_bug_offset = 0;
+
+	if (dump == NULL || desc == NULL)
+		return ICH_RET_PARAM;
+
+	if (dump[0] != DESCRIPTOR_MODE_SIGNATURE) {
+		if (dump[4] == DESCRIPTOR_MODE_SIGNATURE)
+			pch_bug_offset = 4;
+		else
+			return ICH_RET_ERR;
+	}
+
+	/* map */
+	if (len < (4 + pch_bug_offset) * 4 - 1)
+		return ICH_RET_OOB;
+	desc->content.FLVALSIG	= dump[0 + pch_bug_offset];
+	desc->content.FLMAP0	= dump[1 + pch_bug_offset];
+	desc->content.FLMAP1	= dump[2 + pch_bug_offset];
+	desc->content.FLMAP2	= dump[3 + pch_bug_offset];
+
+	/* component */
+	if (len < (getFCBA(&desc->content) + 3 * 4 - 1))
+		return ICH_RET_OOB;
+	desc->component.FLCOMP	= dump[(getFCBA(&desc->content) >> 2) + 0];
+	desc->component.FLILL	= dump[(getFCBA(&desc->content) >> 2) + 1];
+	desc->component.FLPB	= dump[(getFCBA(&desc->content) >> 2) + 2];
+
+	/* region */
+	if (len < (getFRBA(&desc->content) + 5 * 4 - 1))
+		return ICH_RET_OOB;
+	desc->region.FLREGs[0] = dump[(getFRBA(&desc->content) >> 2) + 0];
+	desc->region.FLREGs[1] = dump[(getFRBA(&desc->content) >> 2) + 1];
+	desc->region.FLREGs[2] = dump[(getFRBA(&desc->content) >> 2) + 2];
+	desc->region.FLREGs[3] = dump[(getFRBA(&desc->content) >> 2) + 3];
+	desc->region.FLREGs[4] = dump[(getFRBA(&desc->content) >> 2) + 4];
+
+	/* master */
+	if (len < (getFMBA(&desc->content) + 3 * 4 - 1))
+		return ICH_RET_OOB;
+	desc->master.FLMSTR1 = dump[(getFMBA(&desc->content) >> 2) + 0];
+	desc->master.FLMSTR2 = dump[(getFMBA(&desc->content) >> 2) + 1];
+	desc->master.FLMSTR3 = dump[(getFMBA(&desc->content) >> 2) + 2];
+
+	/* upper map */
+	desc->upper.FLUMAP1 = dump[(UPPER_MAP_OFFSET >> 2) + 0];
+
+	/* VTL is 8 bits long. Quote from the Ibex Peak SPI programming guide:
+	 * "Identifies the 1s based number of DWORDS contained in the VSCC
+	 * Table. Each SPI component entry in the table is 2 DWORDS long." So
+	 * the maximum of 255 gives us 127.5 SPI components(!?) 8 bytes each. A
+	 * check ensures that the maximum offset actually accessed is available.
+	 */
+	if (len < (getVTBA(&desc->upper) + (desc->upper.VTL / 2 * 8) - 1))
+		return ICH_RET_OOB;
+
+	for (i = 0; i < desc->upper.VTL/2; i++) {
+		desc->upper.vscc_table[i].JID  =
+				 dump[(getVTBA(&desc->upper) >> 2) + i * 2 + 0];
+		desc->upper.vscc_table[i].VSCC =
+				 dump[(getVTBA(&desc->upper) >> 2) + i * 2 + 1];
+	}
+
+	/* MCH/PROC (aka. North) straps */
+	if (len < getFMSBA(&desc->content) + desc->content.MSL * 4)
+		return ICH_RET_OOB;
+
+	/* limit the range to be written */
+	max = min(sizeof(desc->north.STRPs) / 4, desc->content.MSL);
+	for (i = 0; i < max; i++)
+			desc->north.STRPs[i] =
+				      dump[(getFMSBA(&desc->content) >> 2) + i];
+
+	/* ICH/PCH (aka. South) straps */
+	if (len < getFISBA(&desc->content) + desc->content.ISL * 4)
+		return ICH_RET_OOB;
+
+	/* limit the range to be written */
+	max = min(sizeof(desc->south.STRPs) / 4, desc->content.ISL);
+	for (i = 0; i < max; i++)
+			desc->south.STRPs[i] =
+				      dump[(getFISBA(&desc->content) >> 2) + i];
+
+	return ICH_RET_OK;
+}
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
 /** Returns the integer representation of the component density with index
 idx in bytes or 0 if a correct size can not be determined. */
 int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx)
@@ -313,4 +839,5 @@
 	msg_pdbg2(" done.\n");
 	return ICH_RET_OK;
 }
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
 #endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/ich_descriptors.h b/ich_descriptors.h
index 714eda9..34bcc6a 100644
--- a/ich_descriptors.h
+++ b/ich_descriptors.h
@@ -227,22 +227,349 @@
 	};
 };
 
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+struct ich_desc_north_strap {
+	union {
+		uint32_t STRPs[1]; /* current maximum: ich8 */
+		struct { /* ich8 */
+			struct { /* STRP2 (in the datasheet) */
+				uint32_t MDB			:1,
+								:31;
+			};
+		} ich8;
+	};
+};
+
+struct ich_desc_south_strap {
+	union {
+		uint32_t STRPs[16]; /* current maximum: ibex peak */
+		struct { /* ich8 */
+			struct { /* STRP1 */
+				uint32_t ME_DISABLE		:1,
+								:6,
+					 TCOMODE		:1,
+					 ASD			:7,
+					 BMCMODE		:1,
+								:3,
+					 GLAN_PCIE_SEL		:1,
+					 GPIO12_SEL		:2,
+					 SPICS1_LANPHYPC_SEL	:1,
+					 MESM2SEL		:1,
+								:1,
+					 ASD2			:7;
+			};
+		} ich8;
+		struct { /* ibex peak */
+			struct { /* STRP0 */
+				uint32_t			:1,
+					 cs_ss2			:1,
+								:5,
+					 SMB_EN			:1,
+					 SML0_EN		:1,
+					 SML1_EN		:1,
+					 SML1FRQ		:2,
+					 SMB0FRQ		:2,
+					 SML0FRQ		:2,
+								:4,
+					 LANPHYPC_GP12_SEL	:1,
+					 cs_ss1			:1,
+								:2,
+					 DMI_REQID_DIS		:1,
+								:4,
+					 BBBS			:2,
+								:1;
+			};
+			struct { /* STRP1 */
+				uint32_t cs_ss3			:4,
+								:28;
+			};
+			struct { /* STRP2 */
+				uint32_t			:8,
+					 MESMASDEN		:1,
+					 MESMASDA		:7,
+								:8,
+					 MESMI2CEN		:1,
+					 MESMI2CA		:7;
+			};
+			struct { /* STRP3 */
+				uint32_t			:32;
+			};
+			struct { /* STRP4 */
+				uint32_t PHYCON			:2,
+								:6,
+					 GBEMAC_SMBUS_ADDR_EN	:1,
+					 GBEMAC_SMBUS_ADDR	:7,
+								:1,
+					 GBEPHY_SMBUS_ADDR	:7,
+								:8;
+			};
+			struct { /* STRP5 */
+				uint32_t			:32;
+			};
+			struct { /* STRP6 */
+				uint32_t			:32;
+			};
+			struct { /* STRP7 */
+				uint32_t MESMA2UDID_VENDOR	:16,
+					 MESMA2UDID_DEVICE	:16;
+			};
+			struct { /* STRP8 */
+				uint32_t			:32;
+			};
+			struct { /* STRP9 */
+				uint32_t PCIEPCS1		:2,
+					 PCIEPCS2		:2,
+					 PCIELR1		:1,
+					 PCIELR2		:1,
+					 DMILR			:1,
+								:1,
+					 PHY_PCIEPORTSEL	:3,
+					 PHY_PCIE_EN		:1,
+								:20;
+			};
+			struct { /* STRP10 */
+				uint32_t			:1,
+					 ME_BOOT_FLASH		:1,
+					 cs_ss5			:1,
+					 VE_EN			:1,
+								:4,
+					 MMDDE			:1,
+					 MMADDR			:7,
+					 cs_ss7			:1,
+								:1,
+					 ICC_SEL		:3,
+					 MER_CL1		:1,
+								:10;
+			};
+			struct { /* STRP11 */
+				uint32_t SML1GPAEN		:1,
+					 SML1GPA		:7,
+								:16,
+					 SML1I2CAEN		:1,
+					 SML1I2CA		:7;
+			};
+			struct { /* STRP12 */
+				uint32_t			:32;
+			};
+			struct { /* STRP13 */
+				uint32_t			:32;
+			};
+			struct { /* STRP14 */
+				uint32_t			:8,
+					 VE_EN2			:1,
+								:5,
+					 VE_BOOT_FLASH		:1,
+								:1,
+					 BW_SSD			:1,
+					 NVMHCI_EN		:1,
+								:14;
+			};
+			struct { /* STRP15 */
+				uint32_t			:3,
+					 cs_ss6			:2,
+								:1,
+					 IWL_EN			:1,
+								:1,
+					 t209min		:2,
+								:22;
+			};
+		} ibex;
+		struct { /* cougar point */
+			struct { /* STRP0 */
+				uint32_t			:1,
+					 cs_ss1			:1,
+								:5,
+					 SMB_EN			:1,
+					 SML0_EN		:1,
+					 SML1_EN		:1,
+					 SML1FRQ		:2,
+					 SMB0FRQ		:2,
+					 SML0FRQ		:2,
+								:4,
+					 LANPHYPC_GP12_SEL	:1,
+					 LINKSEC_DIS		:1,
+								:2,
+					 DMI_REQID_DIS		:1,
+								:4,
+					 BBBS			:2,
+								:1;
+			};
+			struct { /* STRP1 */
+				uint32_t cs_ss3			:4,
+								:4,
+					 cs_ss2			:1,
+								:28;
+			};
+			struct { /* STRP2 */
+				uint32_t			:8,
+					 MESMASDEN		:1,
+					 MESMASDA		:7,
+					 MESMMCTPAEN		:1,
+					 MESMMCTPA		:7,
+					 MESMI2CEN		:1,
+					 MESMI2CA		:7;
+			};
+			struct { /* STRP3 */
+				uint32_t			:32;
+			};
+			struct { /* STRP4 */
+				uint32_t PHYCON			:2,
+								:6,
+					 GBEMAC_SMBUS_ADDR_EN	:1,
+					 GBEMAC_SMBUS_ADDR	:7,
+								:1,
+					 GBEPHY_SMBUS_ADDR	:7,
+								:8;
+			};
+			struct { /* STRP5 */
+				uint32_t			:32;
+			};
+			struct { /* STRP6 */
+				uint32_t			:32;
+			};
+			struct { /* STRP7 */
+				uint32_t MESMA2UDID_VENDOR	:16,
+					 MESMA2UDID_DEVICE	:16;
+			};
+			struct { /* STRP8 */
+				uint32_t			:32;
+			};
+			struct { /* STRP9 */
+				uint32_t PCIEPCS1		:2,
+					 PCIEPCS2		:2,
+					 PCIELR1		:1,
+					 PCIELR2		:1,
+					 DMILR			:1,
+					 cs_ss4			:1,
+					 PHY_PCIEPORTSEL	:3,
+					 PHY_PCIE_EN		:1,
+								:2,
+					 SUB_DECODE_EN		:1,
+								:7,
+					 PCHHOT_SML1ALERT_SEL	:1,
+								:9;
+			};
+			struct { /* STRP10 */
+				uint32_t			:1,
+					 ME_BOOT_FLASH		:1,
+								:6,
+					 MDSMBE_EN		:1,
+					 MDSMBE_ADD		:7,
+								:2,
+					 ICC_SEL		:3,
+					 MER_CL1		:1,
+					 ICC_PRO_SEL		:1,
+					 Deep_SX_EN		:1,
+					 ME_DBG_LAN		:1,
+								:7;
+			};
+			struct { /* STRP11 */
+				uint32_t SML1GPAEN		:1,
+					 SML1GPA		:7,
+								:16,
+					 SML1I2CAEN		:1,
+					 SML1I2CA		:7;
+			};
+			struct { /* STRP12 */
+				uint32_t			:32;
+			};
+			struct { /* STRP13 */
+				uint32_t			:32;
+			};
+			struct { /* STRP14 */
+				uint32_t			:32;
+			};
+			struct { /* STRP15 */
+				uint32_t cs_ss6			:6,
+					 IWL_EN			:1,
+					 cs_ss5			:2,
+								:4,
+					 SMLINK1_THERM_SEL	:1,
+					 SLP_LAN_GP29_SEL	:1,
+								:16;
+			};
+			struct { /* STRP16 */
+				uint32_t			:32;
+			};
+			struct { /* STRP17 */
+				uint32_t ICML			:1,
+					 cs_ss7			:1,
+								:30;
+			};
+		} cougar;
+	};
+};
+
+struct ich_desc_upper_map {
+	union {
+		uint32_t FLUMAP1;		/* Flash Upper Map 1 */
+		struct {
+			uint32_t VTBA	:8,	/* ME VSCC Table Base Address */
+				 VTL	:8,	/* ME VSCC Table Length */
+					:16;
+		};
+	};
+	struct {
+		union {		/* JEDEC-ID Register */
+			uint32_t JID;
+			struct {
+				uint32_t vid	:8, /* Vendor ID */
+					 cid0	:8, /* Component ID 0 */
+					 cid1	:8, /* Component ID 1 */
+						:8;
+			};
+		};
+		union {		/* Vendor Specific Component Capabilities */
+			uint32_t VSCC;
+			struct {
+				uint32_t ubes	:2, /* Upper Block/Sector Erase Size */
+					 uwg	:1, /* Upper Write Granularity */
+					 uwsr	:1, /* Upper Write Status Required */
+					 uwews	:1, /* Upper Write Enable on Write Status */
+						:3,
+					 ueo	:8, /* Upper Erase Opcode */
+					 lbes	:2, /* Lower Block/Sector Erase Size */
+					 lwg	:1, /* Lower Write Granularity */
+					 lwsr	:1, /* Lower Write Status Required */
+					 lwews	:1, /* Lower Write Enable on Write Status */
+						:3,
+					 leo	:16; /* Lower Erase Opcode */
+			};
+		};
+	} vscc_table[128];
+};
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
+
 struct ich_descriptors {
 	struct ich_desc_content content;
 	struct ich_desc_component component;
 	struct ich_desc_region region;
 	struct ich_desc_master master;
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+	struct ich_desc_north_strap north;
+	struct ich_desc_south_strap south;
+	struct ich_desc_upper_map upper;
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
 };
 
-void prettyprint_ich_descriptors(enum ich_chipset, const struct ich_descriptors *desc);
+void prettyprint_ich_descriptors(enum ich_chipset cs, const struct ich_descriptors *desc);
 
 void prettyprint_ich_descriptor_content(const struct ich_desc_content *content);
 void prettyprint_ich_descriptor_component(const struct ich_descriptors *desc);
 void prettyprint_ich_descriptor_region(const struct ich_descriptors *desc);
 void prettyprint_ich_descriptor_master(const struct ich_desc_master *master);
 
+#ifdef ICH_DESCRIPTORS_FROM_DUMP
+
+void prettyprint_ich_descriptor_upper_map(const struct ich_desc_upper_map *umap);
+void prettyprint_ich_descriptor_straps(enum ich_chipset cs, const struct ich_descriptors *desc);
+int read_ich_descriptors_from_dump(const uint32_t *dump, unsigned int len, struct ich_descriptors *desc);
+
+#else /* ICH_DESCRIPTORS_FROM_DUMP */
+
 int read_ich_descriptors_via_fdo(void *spibar, struct ich_descriptors *desc);
 int getFCBA_component_density(const struct ich_descriptors *desc, uint8_t idx);
 
+#endif /* ICH_DESCRIPTORS_FROM_DUMP */
 #endif /* __ICH_DESCRIPTORS_H__ */
 #endif /* defined(__i386__) || defined(__x86_64__) */
diff --git a/programmer.h b/programmer.h
index a40d037..d6b9cd6 100644
--- a/programmer.h
+++ b/programmer.h
@@ -527,7 +527,6 @@
 int register_spi_programmer(const struct spi_programmer *programmer);
 
 /* ichspi.c */
-#if CONFIG_INTERNAL == 1
 enum ich_chipset {
 	CHIPSET_ICH_UNKNOWN,
 	CHIPSET_ICH7 = 7,
@@ -539,6 +538,7 @@
 	CHIPSET_7_SERIES_PANTHER_POINT
 };
 
+#if CONFIG_INTERNAL == 1
 extern uint32_t ichspi_bbar;
 int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 		 enum ich_chipset ich_generation);
diff --git a/util/ich_descriptors_tool/Makefile b/util/ich_descriptors_tool/Makefile
new file mode 100644
index 0000000..1af90ce
--- /dev/null
+++ b/util/ich_descriptors_tool/Makefile
@@ -0,0 +1,42 @@
+CC ?= gcc
+
+PROGRAM=ich_descriptors_tool
+EXTRAINCDIRS = ../../ .
+DEPPATH = .dep
+OBJATH = .obj
+SHAREDSRC = ich_descriptors.c
+SHAREDSRCDIR = ../..
+
+SRC = $(wildcard *.c)
+
+CFLAGS += -Wall
+CFLAGS += -MMD -MP -MF $(DEPPATH)/$(@F).d
+# enables functions that populate the descriptor structs from plain binary dumps
+CFLAGS += -D ICH_DESCRIPTORS_FROM_DUMP
+CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
+
+OBJ = $(OBJATH)/$(SRC:%.c=%.o)
+
+SHAREDOBJ = $(OBJATH)/$(notdir $(SHAREDSRC:%.c=%.o))
+
+all:$(PROGRAM)
+
+$(OBJ): $(OBJATH)/%.o : %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+# this enables us to share source files without simultaneously sharing .o files
+# with flashrom, which would lead to unexpected results (w/o running make clean)
+$(SHAREDOBJ): $(OBJATH)/%.o : $(SHAREDSRCDIR)/%.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+$(PROGRAM): $(OBJ) $(SHAREDOBJ)
+	$(CC) -o $(PROGRAM) $(OBJ) $(SHAREDOBJ)
+
+clean:
+	rm -f $(PROGRAM)
+	rm -rf $(DEPPATH) $(OBJATH)
+
+# Include the dependency files.
+-include $(shell mkdir -p $(DEPPATH) $(OBJATH) 2>/dev/null) $(wildcard $(DEPPATH)/*)
+
+.PHONY: all clean
diff --git a/util/ich_descriptors_tool/TODO b/util/ich_descriptors_tool/TODO
new file mode 100644
index 0000000..e1bb843
--- /dev/null
+++ b/util/ich_descriptors_tool/TODO
@@ -0,0 +1,10 @@
+- reverse the path: assemble a descriptormode image from various
+  blobs (BIOS, GbE, ME, OEM) and a description (xml? custom config?
+  sane defaults and cmd-line switches?)
+- dump 256 OEM bytes
+- deal with the various possible locations of mac address(es?)
+	/* mazzoo said: from what I've seen, the MAC address is the 1st or
+	 * 2nd 6 bytes in the GbE region. It seems the PXE-OpROM and/or the
+	 * intel EEUPDATE-tool copies the MAC address to the 2nd part.
+	 */
+- add descriptions for the missing chipsets
diff --git a/util/ich_descriptors_tool/ich_descriptors_tool.c b/util/ich_descriptors_tool/ich_descriptors_tool.c
new file mode 100644
index 0000000..a1bce1b
--- /dev/null
+++ b/util/ich_descriptors_tool/ich_descriptors_tool.c
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010  Matthias Wenzel <bios at mazzoo dot de>
+ * Copyright (C) 2011 Stefan Tauner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/*
+ * dump information and binaries from BIOS images that are in descriptor mode
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "ich_descriptors.h"
+
+static void dump_file(const char *basename, const uint32_t *dump, unsigned int len, struct ich_desc_region *reg, unsigned int i)
+{
+	int ret;
+	char *fn;
+	const char *reg_name;
+	uint32_t file_len;
+	const char *const region_names[5] = {
+		"Descriptor", "BIOS", "ME", "GbE", "Platform"
+	};
+	uint32_t base = ICH_FREG_BASE(reg->FLREGs[i]);
+	uint32_t limit = ICH_FREG_LIMIT(reg->FLREGs[i]);
+
+	reg_name = region_names[i];
+	if (base > limit) {
+		printf("The %s region is unused and thus not dumped.\n",
+		       reg_name);
+		return;
+	}
+
+	limit = limit | 0x0fff;
+	file_len = limit + 1 - base;
+	if (base + file_len > len) {
+		printf("The %s region is spanning 0x%08x-0x%08x, but it is "
+		       "not (fully) included in the image (0-0x%08x), thus not "
+		       "dumped.\n", reg_name, base, limit, len - 1);
+		return;
+	}
+
+	fn = malloc(strlen(basename) + strlen(reg_name) + strlen(".bin") + 2);
+	if (!fn) {
+		fprintf(stderr, "Out of memory!\n");
+		exit(1);
+	}
+	snprintf(fn, strlen(basename) + strlen(reg_name) + strlen(".bin") + 2,
+		 "%s.%s.bin", basename, reg_name);
+	printf("Dumping %u bytes of the %s region from 0x%08x-0x%08x to %s... ",
+	       file_len, region_names[i], base, limit, fn);
+	int fh = open(fn, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+	free(fn);
+	if (fh < 0) {
+		fprintf(stderr,
+			"ERROR: couldn't open(%s): %s\n", fn, strerror(errno));
+		exit(1);
+	}
+
+	ret = write(fh, &dump[base >> 2], file_len);
+	if (ret != file_len) {
+		fprintf(stderr, "FAILED.\n");
+		exit(1);
+	}
+
+	printf("done.\n");
+	close(fh);
+}
+
+void dump_files(const char *n, const uint32_t *buf, unsigned int len, struct ich_desc_region *reg)
+{
+	unsigned int i;
+	printf("=== Dumping region files ===\n");
+	for (i = 0; i < 5; i++)
+		dump_file(n, buf, len, reg, i);
+	printf("\n");
+}
+
+static void usage(char *argv[], char *error)
+{
+	if (error != NULL) {
+		fprintf(stderr, "%s\n", error);
+	}
+	printf("usage: '%s -f <image file name> [-c <chipset name>] [-d]'\n\n"
+"where <image file name> points to an image of the contents of the SPI flash.\n"
+"In case the image is really in descriptor mode %s\n"
+"will pretty print some of the contained information.\n"
+"To also print the data stored in the descriptor strap you have to indicate\n"
+"the chipset series with the '-c' parameter and one of the possible arguments:\n"
+"\t- \"ich8\",\n"
+"\t- \"ich9\",\n"
+"\t- \"ich10\",\n"
+"\t- \"5\" or \"ibex\" for Intel's 5 series chipsets,\n"
+"\t- \"6\" or \"cougar\" for Intel's 6 series chipsets,\n"
+"\t- \"7\" or \"panther\" for Intel's 7 series chipsets.\n"
+"If '-d' is specified some regions such as the BIOS image as seen by the CPU or\n"
+"the GbE blob that is required to initialize the GbE are also dumped to files.\n",
+	argv[0], argv[0]);
+	exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+	int fd;			/* file descriptor to flash file */
+	int len;		/* file/buffer size in bytes */
+	uint32_t *buf;		/* mmap'd file */
+	uint8_t *pMAC;
+	int opt, ret;
+
+	int dump = 0;
+	const char *fn = NULL;
+	const char *csn = NULL;
+	enum ich_chipset cs = CHIPSET_ICH_UNKNOWN;
+	struct ich_descriptors desc = {{ 0 }};
+
+	while ((opt = getopt(argc, argv, "df:c:")) != -1) {
+		switch (opt) {
+		case 'd':
+			dump = 1;
+			break;
+		case 'f':
+			fn = optarg;
+			break;
+		case 'c':
+			csn = optarg;
+			break;
+		default: /* '?' */
+			usage(argv, NULL);
+		}
+	}
+	if (fn == NULL)
+		usage(argv,
+		      "Need a file name of a descriptor image to read from.");
+
+	fd = open(fn, O_RDONLY);
+	if (fd < 0)
+		usage(argv, "No such file");
+	len = lseek(fd, 0, SEEK_END);
+	if (len < 0)
+		usage(argv, "Seeking to the end of the file failed");
+
+	buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (buf == (void *) -1) {
+		/* fallback for stupid OSes like cygwin */
+		int ret;
+		buf = malloc(len);
+		if (!buf)
+			usage(argv, "Could not allocate memory");
+		lseek(fd, 0, SEEK_SET);
+		ret = read(fd, buf, len);
+		if (ret != len)
+			usage(argv, "Seeking to the end of the file failed");
+	}
+	printf("The flash image has a size of %d [0x%x] bytes.\n", len, len);
+	close(fd);
+
+	if (csn != NULL) {
+		if (strcmp(csn, "ich8") == 0)
+			cs = CHIPSET_ICH8;
+		else if (strcmp(csn, "ich9") == 0)
+			cs = CHIPSET_ICH9;
+		else if (strcmp(csn, "ich10") == 0)
+			cs = CHIPSET_ICH10;
+		else if ((strcmp(csn, "5") == 0) ||
+			 (strcmp(csn, "ibex") == 0))
+			cs = CHIPSET_5_SERIES_IBEX_PEAK;
+		else if ((strcmp(csn, "6") == 0) ||
+			 (strcmp(csn, "cougar") == 0))
+			cs = CHIPSET_6_SERIES_COUGAR_POINT;
+		else if ((strcmp(csn, "7") == 0) ||
+			 (strcmp(csn, "panther") == 0))
+			cs = CHIPSET_7_SERIES_PANTHER_POINT;
+	}
+
+	ret = read_ich_descriptors_from_dump(buf, len, &desc);
+	switch (ret) {
+	case ICH_RET_OK:
+		break;
+	case ICH_RET_ERR:
+		printf("Image not in descriptor mode.\n");
+		exit(1);
+	case ICH_RET_OOB:
+		printf("Tried to access a location out of bounds of the image. "
+		       "- Corrupt image?\n");
+		exit(1);
+	default:
+		printf("Unhandled return value at %s:%u, please report this.\n",
+		       __FILE__, __LINE__);
+		exit(1);
+	}
+
+	prettyprint_ich_descriptors(cs, &desc);
+
+	pMAC = (uint8_t *) &buf[ICH_FREG_BASE(desc.region.reg3_base) >> 2];
+	if (len >= ICH_FREG_BASE(desc.region.reg3_base) + 5 && pMAC[0] != 0xff)
+		printf("The MAC address might be at offset 0x%x: "
+		       "%02x:%02x:%02x:%02x:%02x:%02x\n",
+		       ICH_FREG_BASE(desc.region.reg3_base),
+		       pMAC[0], pMAC[1], pMAC[2], pMAC[3], pMAC[4], pMAC[5]);
+
+	if (dump == 1)
+		dump_files(fn, buf, len, &desc.region);
+
+	return 0;
+}