mboxd: Add a backend abstraction layer to mboxd.

Introduce a backend abstraction, enabling multiple implementations to be
compiled in at once. This change formally abstracts the two existing
backends, mtd and vpnor.

With the backend abstraction in place, subsequent backends are easier to
implement.

This change is based of Evan's work and he retains authorship credit. I
(AJ) have reworked the patch to pass the vpnor tests, refactored some
parts to enable broader use of const structures and others to clarify
the initialisation sequences.

Due to the existing lack of abstraction the patch has unfortunately
wide-ranging impacts. I've whittled it down as much as I consider
reasonable.

Change-Id: I29984a36dae4ea86ec00b853d2a756f0b9afb3ec
Signed-off-by: Evan Lojewski <github@meklort.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/mtd/Makefile.am.include b/mtd/Makefile.am.include
new file mode 100644
index 0000000..155db50
--- /dev/null
+++ b/mtd/Makefile.am.include
@@ -0,0 +1 @@
+mboxd_SOURCES += %reldir%/backend.c
diff --git a/mtd/backend.c b/mtd/backend.c
new file mode 100644
index 0000000..be8f2b2
--- /dev/null
+++ b/mtd/backend.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <mtd/mtd-abi.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "backend.h"
+#include "lpc.h"
+#include "mboxd.h"
+#include "mtd/backend.h"
+
+static int mtd_dev_init(struct backend *backend, void *data)
+{
+	const char *path = data;
+	struct mtd_data *priv;
+	int rc = 0;
+
+	if (!path) {
+		MSG_INFO("Discovering PNOR MTD\n");
+		path = get_dev_mtd();
+	}
+
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		rc = -errno;
+		goto out;
+	}
+
+	MSG_DBG("Opening %s\n", path);
+
+	priv->fd = open(path, O_RDWR);
+	if (priv->fd < 0) {
+		MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", path,
+			strerror(errno));
+		rc = -errno;
+		goto cleanup_priv;
+	}
+
+	/* If the file does not support MEMGETINFO it's not an mtd device */
+	if (ioctl(priv->fd, MEMGETINFO, &priv->mtd_info) == -1) {
+		rc = -errno;
+		close(priv->fd);
+		goto cleanup_priv;
+	}
+
+	if (backend->flash_size == 0) {
+		/*
+		 * PNOR images for current OpenPOWER systems are at most 64MB
+		 * despite the PNOR itself sometimes being as big as 128MB. To
+		 * ensure the image read from the PNOR is exposed in the LPC
+		 * address space at the location expected by the host firmware,
+		 * it is required that the image size be used for
+		 * context->flash_size, and not the size of the flash device.
+		 *
+		 * However, the test cases specify the flash size via special
+		 * test APIs (controlling flash behaviour) which don't have
+		 * access to the mbox context. Rather than requiring
+		 * error-prone assignments in every test case, we instead rely
+		 * on context->flash_size being set to the size reported by the
+		 * MEMINFO ioctl().
+		 *
+		 * As this case should never be hit in production (i.e. outside
+		 * the test environment), log an error. As a consequence, this
+		 * error is expected in the test case output.
+		 */
+		MSG_ERR(
+		    "Flash size MUST be supplied on the commandline. However, "
+		    "continuing by assuming flash is %u bytes\n",
+		    priv->mtd_info.size);
+		backend->flash_size = priv->mtd_info.size;
+	}
+
+	/* We know the erase size so we can allocate the flash_erased bytemap */
+	backend->erase_size_shift = log_2(priv->mtd_info.erasesize);
+	backend->block_size_shift = backend->erase_size_shift;
+	priv->flash_bmap = calloc(backend->flash_size
+			>> backend->erase_size_shift,
+		   sizeof(*priv->flash_bmap));
+	MSG_DBG("Flash erase size: 0x%.8x\n", priv->mtd_info.erasesize);
+
+	backend->priv = priv;
+
+out:
+	return rc;
+
+cleanup_priv:
+	free(priv);
+	return rc;
+}
+
+static void mtd_dev_free(struct backend *backend)
+{
+	struct mtd_data *priv = backend->priv;
+
+	free(priv->flash_bmap);
+	close(priv->fd);
+	free(priv);
+}
+
+/* Flash Functions */
+
+int flash_validate(struct mbox_context *context, uint32_t offset,
+		   uint32_t size, bool ro)
+{
+	/* Default behaviour is all accesses are valid */
+	return 0;
+}
+
+/*
+ * mtd_is_erased() - Check if an offset into flash is erased
+ * @context:	The mbox context pointer
+ * @offset:	The flash offset to check (bytes)
+ *
+ * Return:	true if erased otherwise false
+ */
+static inline bool mtd_is_erased(struct backend *backend, uint32_t offset)
+{
+	const off_t index = offset >> backend->erase_size_shift;
+	struct mtd_data *priv = backend->priv;
+
+	return priv->flash_bmap[index] == FLASH_ERASED;
+}
+
+/*
+ * mtd_set_bytemap() - Set the flash erased bytemap
+ * @context:	The backend context pointer
+ * @offset:	The flash offset to set (bytes)
+ * @count:	Number of bytes to set
+ * @val:	Value to set the bytemap to
+ *
+ * The flash bytemap only tracks the erased status at the erase block level so
+ * this will update the erased state for an (or many) erase blocks
+ *
+ * Return:	0 if success otherwise negative error code
+ */
+static int mtd_set_bytemap(struct backend *backend, uint32_t offset,
+			   uint32_t count, uint8_t val)
+{
+	struct mtd_data *priv = backend->priv;
+
+	if ((offset + count) > backend->flash_size) {
+		return -EINVAL;
+	}
+
+	MSG_DBG("Set flash bytemap @ 0x%.8x for 0x%.8x to %s\n", offset, count,
+		val ? "ERASED" : "DIRTY");
+	memset(priv->flash_bmap + (offset >> backend->erase_size_shift),
+	       val,
+	       align_up(count, 1 << backend->erase_size_shift) >>
+		   backend->erase_size_shift);
+
+	return 0;
+}
+
+/*
+ * mtd_erase() - Erase the flash
+ * @context:	The mbox context pointer
+ * @offset:	The flash offset to erase (bytes)
+ * @size:	The number of bytes to erase
+ *
+ * Return:	0 on success otherwise negative error code
+ */
+static int mtd_erase(struct backend *backend, uint32_t offset, uint32_t count)
+{
+	const uint32_t erase_size = 1 << backend->erase_size_shift;
+	struct mtd_data *priv = backend->priv;
+	struct erase_info_user erase_info = {0};
+	int rc;
+
+	MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", offset, count);
+
+	/*
+	 * We have an erased_bytemap for the flash so we want to avoid erasing
+	 * blocks which we already know to be erased. Look for runs of blocks
+	 * which aren't erased and erase the entire run at once to avoid how
+	 * often we have to call the erase ioctl. If the block is already
+	 * erased then there's nothing we need to do.
+	 */
+	while (count) {
+		if (!mtd_is_erased(backend, offset)) { /* Need to erase */
+			if (!erase_info.length) { /* Start of not-erased run */
+				erase_info.start = offset;
+			}
+			erase_info.length += erase_size;
+		} else if (erase_info.length) { /* Already erased|end of run? */
+			/* Erase the previous run which just ended */
+			MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n",
+				erase_info.start, erase_info.length);
+			rc = ioctl(priv->fd, MEMERASE, &erase_info);
+			if (rc < 0) {
+				MSG_ERR("Couldn't erase flash at 0x%.8x\n",
+					erase_info.start);
+				return -errno;
+			}
+			/* Mark ERASED where we just erased */
+			mtd_set_bytemap(backend, erase_info.start,
+					erase_info.length, FLASH_ERASED);
+			erase_info.start = 0;
+			erase_info.length = 0;
+		}
+
+		offset += erase_size;
+		count -= erase_size;
+	}
+
+	if (erase_info.length) {
+		MSG_DBG("Erase flash @ 0x%.8x for 0x%.8x\n", erase_info.start,
+			erase_info.length);
+		rc = ioctl(priv->fd, MEMERASE, &erase_info);
+		if (rc < 0) {
+			MSG_ERR("Couldn't erase flash at 0x%.8x\n",
+				erase_info.start);
+			return -errno;
+		}
+		/* Mark ERASED where we just erased */
+		mtd_set_bytemap(backend, erase_info.start, erase_info.length,
+				FLASH_ERASED);
+	}
+
+	return 0;
+}
+
+#define CHUNKSIZE (64 * 1024)
+
+/*
+ * mtd_copy() - Copy data from the flash device into a provided buffer
+ * @context:	The backend context pointer
+ * @offset:	The flash offset to copy from (bytes)
+ * @mem:	The buffer to copy into (must be of atleast 'size' bytes)
+ * @size:	The number of bytes to copy
+ * Return:	Number of bytes copied on success, otherwise negative error
+ *		code. mtd_copy will copy at most 'size' bytes, but it may
+ *		copy less.
+ */
+static int64_t mtd_copy(struct backend *backend, uint32_t offset,
+			  void *mem, uint32_t size)
+{
+	struct mtd_data *priv = backend->priv;
+	int32_t size_read;
+	void *start = mem;
+
+	MSG_DBG("Copy flash to %p for size 0x%.8x from offset 0x%.8x\n", mem,
+		size, offset);
+	if (lseek(priv->fd, offset, SEEK_SET) != offset) {
+		MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
+			strerror(errno));
+		return -errno;
+	}
+
+	do {
+		size_read = read(priv->fd, mem,
+				 min_u32(CHUNKSIZE, size));
+		if (size_read < 0) {
+			MSG_ERR("Couldn't copy mtd into ram: %s\n",
+				strerror(errno));
+			return -errno;
+		}
+
+		size -= size_read;
+		mem += size_read;
+	} while (size && size_read);
+
+	return size_read ? mem - start : -EIO;
+}
+
+/*
+ * mtd_write() - Write the flash from a provided buffer
+ * @context:	The mbox context pointer
+ * @offset:	The flash offset to write to (bytes)
+ * @buf:	The buffer to write from (must be of atleast size)
+ * @size:	The number of bytes to write
+ *
+ * Return:	0 on success otherwise negative error code
+ */
+static int mtd_write(struct backend *backend, uint32_t offset, void *buf,
+		       uint32_t count)
+{
+	struct mtd_data *priv = backend->priv;
+	uint32_t buf_offset = 0;
+	int rc;
+
+	MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count,
+		buf);
+
+	if (lseek(priv->fd, offset, SEEK_SET) != offset) {
+		MSG_ERR("Couldn't seek flash at pos: %u %s\n", offset,
+			strerror(errno));
+		return -errno;
+	}
+
+	while (count) {
+		rc = write(priv->fd, buf + buf_offset, count);
+		if (rc < 0) {
+			MSG_ERR("Couldn't write to flash, write lost: %s\n",
+				strerror(errno));
+			return -errno;
+		}
+		/* Mark *NOT* erased where we just wrote */
+		mtd_set_bytemap(backend, offset + buf_offset, rc, FLASH_DIRTY);
+		count -= rc;
+		buf_offset += rc;
+	}
+
+	return 0;
+}
+
+/*
+ * mtd_reset() - Reset the lpc bus mapping
+ * @context:    The mbox context pointer
+ *
+ * Return:      A value from enum backend_reset_mode, otherwise a negative
+ *		error code
+ */
+static int mtd_reset(struct backend *backend,
+		     void *buf __attribute__((unused)),
+		     uint32_t count __attribute__((unused)))
+{
+	return reset_lpc_flash;
+}
+
+static const struct backend_ops mtd_ops = {
+	.init = mtd_dev_init,
+	.free = mtd_dev_free,
+	.copy = mtd_copy,
+	.set_bytemap = mtd_set_bytemap,
+	.erase = mtd_erase,
+	.write = mtd_write,
+	.validate = NULL,
+	.reset = mtd_reset,
+};
+
+struct backend backend_get_mtd(void)
+{
+	struct backend be = {0};
+
+	be.ops = &mtd_ops;
+
+	return be;
+}
+
+int backend_probe_mtd(struct backend *master, const char *path)
+{
+	struct backend with;
+
+	assert(master);
+	with = backend_get_mtd();
+
+	return backend_init(master, &with, (void *)path);
+}
diff --git a/mtd/backend.h b/mtd/backend.h
new file mode 100644
index 0000000..0f64215
--- /dev/null
+++ b/mtd/backend.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+/* Copyright (C) 2019 IBM Corp. */
+
+#ifndef MTD_BACKEND_H
+#define MTD_BACKEND_H
+
+struct mtd_data {
+	int fd;
+	uint8_t *flash_bmap;
+	struct mtd_info_user mtd_info;
+};
+
+#endif