Add option to read ROM layout from IFD

Add an option --ifd to read the ROM layout from an Intel Firmware
Descriptor (IFD). Works the same as the -l option, if given, -i
specifies the images to update.

v2: o Rebased on libflashrom, use libflashrom interface.
    o Use functions from ich_descriptors.c.

v3: o Move ich_descriptors.o to LIB_OBJS, thus build it independent
      of arch and programmers.
    o Bail out if we aren't compiled for little endian.
    o Update flashrom.8.tmpl.

v4: o Incorporated David's comments.
    o Removed single-character `-d` option.

v5: Changed region names to match the output of `ifdtool --layout ...`

Change-Id: Ifafff2bf6d5c5e62283416b3269723f81fdc0fa3
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/17953
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/libflashrom.c b/libflashrom.c
index 5447bae..1176e02 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -31,6 +31,8 @@
 #include "flash.h"
 #include "programmer.h"
 #include "layout.h"
+#include "hwaccess.h"
+#include "ich_descriptors.h"
 #include "libflashrom.h"
 
 /**
@@ -305,6 +307,86 @@
 }
 
 /**
+ * @brief Read a layout from the Intel ICH descriptor in the flash.
+ *
+ * Optionally verify that the layout matches the one in the given
+ * descriptor dump.
+ *
+ * @param[out] layout Points to a struct flashrom_layout pointer that
+ *                    gets set if the descriptor is read and parsed
+ *                    successfully.
+ * @param[in] flashctx Flash context to read the descriptor from flash.
+ * @param[in] dump     The descriptor dump to compare to or NULL.
+ * @param[in] len      The length of the descriptor dump.
+ *
+ * @return 0 on success,
+ *         6 if descriptor parsing isn't implemented for the host,
+ *         5 if the descriptors don't match,
+ *         4 if the descriptor dump couldn't be parsed,
+ *         3 if the descriptor on flash couldn't be parsed,
+ *         2 if the descriptor on flash couldn't be read,
+ *         1 on any other error.
+ */
+int flashrom_layout_read_from_ifd(struct flashrom_layout **const layout, struct flashctx *const flashctx,
+				  const void *const dump, const size_t len)
+{
+#ifndef __FLASHROM_LITTLE_ENDIAN__
+	return 6;
+#else
+	struct ich_layout dump_layout;
+	int ret = 1;
+
+	void *const desc = malloc(0x1000);
+	struct ich_layout *const chip_layout = malloc(sizeof(*chip_layout));
+	if (!desc || !chip_layout) {
+		msg_gerr("Out of memory!\n");
+		goto _free_ret;
+	}
+
+	if (prepare_flash_access(flashctx, true, false, false, false))
+		goto _free_ret;
+
+	msg_cinfo("Reading ich descriptor... ");
+	if (flashctx->chip->read(flashctx, desc, 0, 0x1000)) {
+		msg_cerr("Read operation failed!\n");
+		msg_cinfo("FAILED.\n");
+		ret = 2;
+		goto _finalize_ret;
+	}
+	msg_cinfo("done.\n");
+
+	if (layout_from_ich_descriptors(chip_layout, desc, 0x1000)) {
+		ret = 3;
+		goto _finalize_ret;
+	}
+
+	if (dump) {
+		if (layout_from_ich_descriptors(&dump_layout, dump, len)) {
+			ret = 4;
+			goto _finalize_ret;
+		}
+
+		if (chip_layout->base.num_entries != dump_layout.base.num_entries ||
+		    memcmp(chip_layout->entries, dump_layout.entries, sizeof(dump_layout.entries))) {
+			ret = 5;
+			goto _finalize_ret;
+		}
+	}
+
+	*layout = (struct flashrom_layout *)chip_layout;
+	ret = 0;
+
+_finalize_ret:
+	finalize_flash_access(flashctx);
+_free_ret:
+	if (ret)
+		free(chip_layout);
+	free(desc);
+	return ret;
+#endif
+}
+
+/**
  * @brief Free a layout.
  *
  * @param layout Layout to free.