diff --git a/flash.h b/flash.h
index bf381cf..be1134a 100644
--- a/flash.h
+++ b/flash.h
@@ -216,6 +216,7 @@ struct flashctx {
 	uintptr_t physical_registers;
 	chipaddr virtual_registers;
 	struct registered_master *mst;
+	const struct fl_layout *layout;
 };
 
 /* Timing used in probe routines. ZERO is -2 to differentiate between an unset
diff --git a/flashrom.c b/flashrom.c
index 25e53f2..a0948b9 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -5,6 +5,8 @@
  * Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
  * Copyright (C) 2005-2008 coresystems GmbH
  * Copyright (C) 2008,2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2016 secunet Security Networks AG
+ * (Written by Nico Huber <nico.huber@secunet.com> for secunet)
  *
  * 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
@@ -1640,6 +1642,330 @@ int erase_and_write_flash(struct flashctx *flash, uint8_t *oldcontents, uint8_t
 	return ret;
 }
 
+/** @private */
+static const struct fl_layout *get_layout(const struct flashctx *const flashctx,
+					  struct fl_layout_single *const fallback)
+{
+	if (flashctx->layout && flashctx->layout->num_entries)
+		return flashctx->layout;
+
+	fallback->base.entries		= &fallback->entry;
+	fallback->base.num_entries	= 1;
+	fallback->entry.start		= 0;
+	fallback->entry.end		= flashctx->chip->total_size * 1024 - 1;
+	fallback->entry.included	= true;
+	strcpy(fallback->entry.name, "complete flash");
+	return &fallback->base;
+}
+
+/**
+ * @brief Reads the included layout regions into a buffer.
+ *
+ * If there is no layout set in the given flash context, the whole chip will
+ * be read.
+ *
+ * @param flashctx Flash context to be used.
+ * @param buffer   Buffer of full chip size to read into.
+ * @return 0 on success,
+ * 	   1 if any read fails.
+ */
+static int read_by_layout(struct flashctx *const flashctx, void *const buffer)
+{
+	struct fl_layout_single fallback_layout;
+	const struct fl_layout *const layout = get_layout(flashctx, &fallback_layout);
+
+	size_t i;
+	for (i = 0; i < layout->num_entries; ++i) {
+		if (!layout->entries[i].included)
+			continue;
+
+		const chipoff_t region_start	= layout->entries[i].start;
+		const chipsize_t region_len	= layout->entries[i].end - layout->entries[i].start + 1;
+
+		if (flashctx->chip->read(flashctx, buffer + region_start, region_start, region_len))
+			return 1;
+	}
+	return 0;
+}
+
+typedef int (*erasefn_t)(struct flashctx *, unsigned int addr, unsigned int len);
+struct walk_info {
+	uint8_t *curcontents;
+	const uint8_t *newcontents;
+	unsigned int region_start;
+	unsigned int region_end;
+	unsigned int erase_start;
+	unsigned int erase_end;
+};
+typedef int (*per_blockfn_t)(struct flashctx *, const struct walk_info *, erasefn_t);
+
+/** @private */
+static int walk_eraseblocks(struct flashctx *const flashctx,
+			    struct walk_info *const info,
+			    const int erasefunction, per_blockfn_t per_blockfn)
+{
+	int ret;
+	int i, j;
+	bool first = true;
+	struct block_eraser *const eraser = &flashctx->chip->block_erasers[erasefunction];
+
+	info->erase_start = 0;
+	for (i = 0; i < NUM_ERASEREGIONS; i++) {
+		/* count==0 for all automatically initialized array
+		 * members so the loop below won't be executed for them.
+		 */
+		for (j = 0; j < eraser->eraseblocks[i].count; ++j, info->erase_start = info->erase_end + 1) {
+			info->erase_end = info->erase_start + eraser->eraseblocks[i].size - 1;
+
+			/* Skip any eraseblock that is completely outside the current region. */
+			if (info->erase_end < info->region_start || info->region_end < info->erase_start)
+				continue;
+
+			/* Print this for every block except the first one. */
+			if (first)
+				first = false;
+			else
+				msg_cdbg(", ");
+			msg_cdbg("0x%06x-0x%06x:", info->erase_start, info->erase_end);
+
+			ret = per_blockfn(flashctx, info, eraser->block_erase);
+			if (ret)
+				return ret;
+		}
+	}
+	msg_cdbg("\n");
+	return 0;
+}
+
+/** @private */
+static int walk_by_layout(struct flashctx *const flashctx, struct walk_info *const info,
+			  per_blockfn_t per_blockfn)
+{
+	struct fl_layout_single fallback_layout;
+	const struct fl_layout *const layout = get_layout(flashctx, &fallback_layout);
+
+	all_skipped = true;
+	msg_cinfo("Erasing and writing flash chip... ");
+
+	size_t i;
+	for (i = 0; i < layout->num_entries; ++i) {
+		if (!layout->entries[i].included)
+			continue;
+
+		info->region_start = layout->entries[i].start;
+		info->region_end   = layout->entries[i].end;
+
+		size_t j;
+		int ret = 1;
+		for (j = 0; j < NUM_ERASEFUNCTIONS; ++j) {
+			if (j != 0)
+				msg_cinfo("Looking for another erase function.\n");
+			msg_cdbg("Trying erase function %zi... ", j);
+			if (check_block_eraser(flashctx, j, 1))
+				continue;
+
+			ret = walk_eraseblocks(flashctx, info, j, per_blockfn);
+			if (ret != 1)
+				break;
+
+			if (info->curcontents) {
+				msg_cinfo("Reading current flash chip contents... ");
+				if (read_by_layout(flashctx, info->curcontents)) {
+					/* Now we are truly screwed. Read failed as well. */
+					msg_cerr("Can't read anymore! Aborting.\n");
+					/* We have no idea about the flash chip contents, so
+					 * retrying with another erase function is pointless.
+					 */
+					ret = 2;
+					break;
+				}
+				msg_cinfo("done. ");
+			}
+		}
+		if (ret == 1)
+			msg_cinfo("No usable erase functions left.\n");
+		if (ret) {
+			msg_cerr("FAILED!\n");
+			return 1;
+		}
+	}
+	if (all_skipped)
+		msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
+	msg_cinfo("Erase/write done.\n");
+	return 0;
+}
+
+/** @private */
+static int erase_block(struct flashctx *const flashctx, const struct walk_info *const info, erasefn_t erasefn)
+{
+	const unsigned int erase_len = info->erase_end + 1 - info->erase_start;
+
+	all_skipped = false;
+
+	msg_cdbg("E");
+	if (erasefn(flashctx, info->erase_start, erase_len))
+		return 1;
+	if (check_erased_range(flashctx, info->erase_start, erase_len)) {
+		msg_cerr("ERASE FAILED!\n");
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * @brief Erases the included layout regions.
+ *
+ * If there is no layout set in the given flash context, the whole chip will
+ * be erased.
+ *
+ * @param flashctx Flash context to be used.
+ * @param buffer   Buffer of full chip size to read into.
+ * @return 0 on success,
+ * 	   1 if all available erase functions failed.
+ */
+static int erase_by_layout(struct flashctx *const flashctx)
+{
+	struct walk_info info = { 0 };
+	return walk_by_layout(flashctx, &info, erase_block);
+}
+
+/** @private */
+static int read_erase_write_block(struct flashctx *const flashctx,
+				  const struct walk_info *const info, erasefn_t erasefn)
+{
+	const unsigned int erase_len = info->erase_end + 1 - info->erase_start;
+	const bool region_unaligned = info->region_start > info->erase_start ||
+				      info->erase_end > info->region_end;
+	const uint8_t *newcontents = NULL;
+	int ret = 2;
+	/*
+	 * Merge current flash contents into new buffer if the region is not erase-block
+	 * aligned.
+	 * Note: We can not use newcontents here as this erase block might overlap
+	 *       other regions as well. So we would invalidate data to be written there.
+	 */
+	if (region_unaligned) {
+		msg_cdbg("R");
+		uint8_t *const newc = malloc(erase_len);
+		if (!newc) {
+			msg_cerr("Out of memory!\n");
+			return 1;
+		}
+		memcpy(newc, info->newcontents + info->erase_start, erase_len);
+		if (info->region_start > info->erase_start &&
+		    flashctx->chip->read(flashctx, newc,
+					 info->erase_start, info->region_start - info->erase_start)) {
+			msg_cerr("Can't read! Aborting.\n");
+			goto _free_ret;
+		}
+		if (info->erase_end > info->region_end &&
+		    flashctx->chip->read(flashctx, newc - info->erase_start + info->region_end + 1,
+					 info->region_end + 1, info->erase_end - info->region_end)) {
+			msg_cerr("Can't read! Aborting.\n");
+			goto _free_ret;
+		}
+		newcontents = newc;
+	} else {
+		newcontents = info->newcontents + info->erase_start;
+	}
+
+	ret = 1;
+	bool skipped = true;
+	uint8_t *const curcontents = info->curcontents + info->erase_start;
+	if (need_erase(curcontents, newcontents, erase_len, flashctx->chip->gran)) {
+		if (erase_block(flashctx, info, erasefn))
+			goto _free_ret;
+		/* Erase was successful. Adjust curcontents. */
+		memset(curcontents, 0xff, erase_len);
+		skipped = false;
+	}
+
+	unsigned int starthere = 0, lenhere = 0, writecount = 0;
+	/* get_next_write() sets starthere to a new value after the call. */
+	while ((lenhere = get_next_write(curcontents + starthere, newcontents + starthere,
+					 erase_len - starthere, &starthere, flashctx->chip->gran))) {
+		if (!writecount++)
+			msg_cdbg("W");
+		/* Needs the partial write function signature. */
+		if (flashctx->chip->write(flashctx, newcontents + starthere,
+					  info->erase_start + starthere, lenhere))
+			goto _free_ret;
+		starthere += lenhere;
+		skipped = false;
+	}
+	if (skipped)
+		msg_cdbg("S");
+	else
+		all_skipped = false;
+
+	/* Update curcontents, other regions with overlapping erase blocks
+	   might rely on this. */
+	memcpy(curcontents, newcontents, erase_len);
+	ret = 0;
+
+_free_ret:
+	if (region_unaligned)
+		free((void *)newcontents);
+	return ret;
+}
+
+/**
+ * @brief Writes the included layout regions from a given image.
+ *
+ * If there is no layout set in the given flash context, the whole image
+ * will be written.
+ *
+ * @param flashctx    Flash context to be used.
+ * @param curcontents A buffer of full chip size with current chip contents of included regions.
+ * @param newcontents The new image to be written.
+ * @return 0 on success,
+ * 	   1 if anything has gone wrong.
+ */
+static int write_by_layout(struct flashctx *const flashctx,
+			   void *const curcontents, const void *const newcontents)
+{
+	struct walk_info info;
+	info.curcontents = curcontents;
+	info.newcontents = newcontents;
+	return walk_by_layout(flashctx, &info, read_erase_write_block);
+}
+
+/**
+ * @brief Compares the included layout regions with content from a buffer.
+ *
+ * If there is no layout set in the given flash context, the whole chip's
+ * contents will be compared.
+ *
+ * @param flashctx    Flash context to be used.
+ * @param curcontents A buffer of full chip size to read current chip contents into.
+ * @param newcontents The new image to compare to.
+ * @return 0 on success,
+ * 	   1 if reading failed,
+ * 	   3 if the contents don't match.
+ */
+static int verify_by_layout(struct flashctx *const flashctx,
+			    void *const curcontents, const void *const newcontents)
+{
+	struct fl_layout_single fallback_layout;
+	const struct fl_layout *const layout = get_layout(flashctx, &fallback_layout);
+
+	size_t i;
+	for (i = 0; i < layout->num_entries; ++i) {
+		if (!layout->entries[i].included)
+			continue;
+
+		const chipoff_t region_start	= layout->entries[i].start;
+		const chipsize_t region_len	= layout->entries[i].end - layout->entries[i].start + 1;
+
+		if (flashctx->chip->read(flashctx, curcontents + region_start, region_start, region_len))
+			return 1;
+		if (compare_range(newcontents + region_start, curcontents + region_start,
+				  region_start, region_len))
+			return 3;
+	}
+	return 0;
+}
+
 static void nonfatal_help_message(void)
 {
 	msg_gerr("Good, writing to the flash chip apparently didn't do anything.\n");
