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/vpnor/Makefile.am.include b/vpnor/Makefile.am.include
index 36e3c50..d69a94b 100644
--- a/vpnor/Makefile.am.include
+++ b/vpnor/Makefile.am.include
@@ -1,8 +1,7 @@
 mboxd_SOURCES += %reldir%/pnor_partition_table.cpp \
 	%reldir%/mboxd_pnor_partition_table.cpp \
-	%reldir%/flash.cpp \
-	%reldir%/pnor_partition.cpp \
-	%reldir%/lpc_reset.cpp
+	%reldir%/backend.cpp \
+	%reldir%/pnor_partition.cpp
 
 mboxd_LDFLAGS += -lstdc++fs \
 	$(SDBUSPLUS_LIBS) \
diff --git a/vpnor/backend.cpp b/vpnor/backend.cpp
new file mode 100644
index 0000000..ccda4ec
--- /dev/null
+++ b/vpnor/backend.cpp
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+extern "C" {
+#include "common.h"
+#include "lpc.h"
+#include "mboxd.h"
+#include "protocol.h"
+}
+
+#include "config.h"
+
+#include "pnor_partition.hpp"
+#include "pnor_partition_table.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <exception>
+#include <memory>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <stdexcept>
+#include <string>
+
+#include "mboxd_pnor_partition_table.h"
+
+namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
+namespace fs = std::experimental::filesystem;
+namespace vpnor = openpower::virtual_pnor;
+
+static constexpr uint32_t VPNOR_ERASE_SIZE = 4 * 1024;
+
+int vpnor_dev_init(struct backend* backend, void* data)
+{
+    vpnor_partition_paths* paths = (vpnor_partition_paths*)data;
+    struct mtd_info_user mtd_info;
+    const char* filename = NULL;
+    int fd;
+    int rc = 0;
+
+    if (!(fs::is_directory(fs::status(paths->ro_loc)) &&
+          fs::is_directory(fs::status(paths->rw_loc)) &&
+          fs::is_directory(fs::status(paths->prsv_loc))))
+    {
+        return -EINVAL;
+    }
+
+    if (backend->flash_size == 0)
+    {
+        filename = get_dev_mtd();
+
+        MSG_INFO("No flash size provided, using PNOR MTD size\n");
+
+        if (!filename)
+        {
+            MSG_ERR("Couldn't find the flash /dev/mtd partition\n");
+            return -errno;
+        }
+
+        MSG_DBG("Opening %s\n", filename);
+
+        fd = open(filename, O_RDWR);
+        if (fd < 0)
+        {
+            MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename,
+                    strerror(errno));
+            rc = -errno;
+            goto cleanup_filename;
+        }
+
+        // Read the Flash Info
+        if (ioctl(fd, MEMGETINFO, &mtd_info) == -1)
+        {
+            MSG_ERR("Couldn't get information about MTD: %s\n",
+                    strerror(errno));
+            rc = -errno;
+            goto cleanup_fd;
+        }
+
+        close(fd);
+        free((void*)filename);
+
+        // See comment in flash.c on why
+        // this is needed.
+        backend->flash_size = mtd_info.size;
+    }
+
+    // Hostboot requires a 4K block-size to be used in the FFS flash structure
+    backend->erase_size_shift = log_2(VPNOR_ERASE_SIZE);
+    backend->block_size_shift = backend->erase_size_shift;
+
+    return vpnor_init(backend, paths);
+
+cleanup_fd:
+    close(fd);
+
+cleanup_filename:
+    free((void*)filename);
+
+    return rc;
+}
+
+static void vpnor_free(struct backend* backend)
+{
+    vpnor_destroy(backend);
+}
+
+/*
+ * vpnor_copy() - Copy data from the virtual pnor into a provided buffer
+ * @context:    The backend context pointer
+ * @offset:     The pnor 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. vpnor_copy will copy at most 'size' bytes, but it may
+ *              copy less.
+ */
+static int64_t vpnor_copy(struct backend* backend, uint32_t offset, void* mem,
+                          uint32_t size)
+{
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+    vpnor::partition::Table* table;
+    int rc = size;
+
+    if (!(priv->vpnor && priv->vpnor->table))
+    {
+        MSG_ERR("Trying to copy data with uninitialised context!\n");
+        return -EINVAL;
+    }
+
+    table = priv->vpnor->table;
+
+    MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem,
+            size, offset);
+
+    /* The virtual PNOR partition table starts at offset 0 in the virtual
+     * pnor image. Check if host asked for an offset that lies within the
+     * partition table.
+     */
+    size_t sz = table->size();
+    if (offset < sz)
+    {
+        const pnor_partition_table& toc = table->getHostTable();
+        rc = std::min(sz - offset, static_cast<size_t>(size));
+        memcpy(mem, ((uint8_t*)&toc) + offset, rc);
+        return rc;
+    }
+
+    try
+    {
+        vpnor::Request req(backend, offset);
+        rc = req.read(mem, size);
+    }
+    catch (vpnor::UnmappedOffset& e)
+    {
+        /*
+         * Hooo boy. Pretend that this is valid flash so we don't have
+         * discontiguous regions presented to the host. Instead, fill a window
+         * with 0xff so the 'flash' looks erased. Writes to such regions are
+         * dropped on the floor, see the implementation of vpnor_write() below.
+         */
+        MSG_INFO("Host requested unmapped region of %" PRId32
+                 " bytes at offset 0x%" PRIx32 "\n",
+                 size, offset);
+        uint32_t span = e.next - e.base;
+        rc = std::min(size, span);
+        memset(mem, 0xff, rc);
+    }
+    catch (std::exception& e)
+    {
+        MSG_ERR("%s\n", e.what());
+        phosphor::logging::commit<err::InternalFailure>();
+        rc = -EIO;
+    }
+    return rc;
+}
+
+/*
+ * vpnor_write() - Write to the virtual pnor from a provided buffer
+ * @context: The backend 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 vpnor_write(struct backend* backend, uint32_t offset, void* buf,
+                       uint32_t count)
+{
+    assert(backend);
+
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+
+    if (!(priv && priv->vpnor && priv->vpnor->table))
+    {
+        MSG_ERR("Trying to write data with uninitialised context!\n");
+        return -EINVAL;
+    }
+
+    vpnor::partition::Table* table = priv->vpnor->table;
+
+    try
+    {
+        const struct pnor_partition& part = table->partition(offset);
+        if (part.data.user.data[1] & PARTITION_READONLY)
+        {
+            MSG_ERR("Unreachable: Host attempted to write to read-only "
+                    "partition %s\n",
+                    part.data.name);
+            return -EPERM;
+        }
+
+        MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count,
+                buf);
+        vpnor::Request req(backend, offset);
+        req.write(buf, count);
+    }
+    catch (vpnor::UnmappedOffset& e)
+    {
+        MSG_ERR("Unreachable: Host attempted to write %" PRIu32
+                " bytes to unmapped offset 0x%" PRIx32 "\n",
+                count, offset);
+        return -EACCES;
+    }
+    catch (const vpnor::OutOfBoundsOffset& e)
+    {
+        MSG_ERR("%s\n", e.what());
+        return -EINVAL;
+    }
+    catch (const std::exception& e)
+    {
+        MSG_ERR("%s\n", e.what());
+        phosphor::logging::commit<err::InternalFailure>();
+        return -EIO;
+    }
+    return 0;
+}
+
+static bool vpnor_partition_is_readonly(const pnor_partition& part)
+{
+    return part.data.user.data[1] & PARTITION_READONLY;
+}
+
+static int vpnor_validate(struct backend* backend, uint32_t offset,
+                          uint32_t size __attribute__((unused)), bool ro)
+{
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+
+    /* All reads are allowed */
+    if (ro)
+    {
+        return 0;
+    }
+
+    /* Only allow write windows on regions mapped by the ToC as writeable */
+    try
+    {
+        const pnor_partition& part = priv->vpnor->table->partition(offset);
+        if (vpnor_partition_is_readonly(part))
+        {
+            return -EPERM;
+        }
+    }
+    catch (const openpower::virtual_pnor::UnmappedOffset& e)
+    {
+        /*
+         * Writes to unmapped areas are not meaningful, so deny the request.
+         * This removes the ability for a compromised host to abuse unused
+         * space if any data was to be persisted (which it isn't).
+         */
+        return -EACCES;
+    }
+
+    // Allowed.
+    return 0;
+}
+
+/*
+ * vpnor_reset() - Reset the lpc bus mapping
+ * @context:     The mbox context pointer
+ *
+ * Return        0 on success otherwise negative error code
+ */
+static int vpnor_reset(struct backend* backend, void* buf, uint32_t count)
+{
+    const struct vpnor_data* priv = (const struct vpnor_data*)backend->priv;
+    int rc;
+
+    vpnor_partition_paths paths = priv->paths;
+
+    vpnor_destroy(backend);
+
+    rc = vpnor_init(backend, &paths);
+    if (rc < 0)
+        return rc;
+
+    rc = vpnor_copy_bootloader_partition(backend, buf, count);
+    if (rc < 0)
+        return rc;
+
+    return reset_lpc_memory;
+}
+
+static const struct backend_ops vpnor_ops = {
+    .init = vpnor_dev_init,
+    .free = vpnor_free,
+    .copy = vpnor_copy,
+    .set_bytemap = NULL,
+    .erase = NULL,
+    .write = vpnor_write,
+    .validate = vpnor_validate,
+    .reset = vpnor_reset,
+};
+
+struct backend backend_get_vpnor(void)
+{
+    struct backend be = {0};
+
+    be.ops = &vpnor_ops;
+
+    return be;
+}
+
+int backend_probe_vpnor(struct backend* master,
+                        const struct vpnor_partition_paths* paths)
+{
+    struct backend with;
+
+    assert(master);
+    with = backend_get_vpnor();
+
+    return backend_init(master, &with, (void*)paths);
+}
diff --git a/vpnor/flash.cpp b/vpnor/flash.cpp
deleted file mode 100644
index 847e6a5..0000000
--- a/vpnor/flash.cpp
+++ /dev/null
@@ -1,275 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-// Copyright (C) 2018 IBM Corp.
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include <algorithm>
-
-extern "C" {
-#include "common.h"
-#include "flash.h"
-}
-
-#include "config.h"
-
-#include "pnor_partition.hpp"
-#include "pnor_partition_table.hpp"
-#include "xyz/openbmc_project/Common/error.hpp"
-
-#include <exception>
-#include <memory>
-#include <phosphor-logging/elog-errors.hpp>
-#include <phosphor-logging/log.hpp>
-#include <stdexcept>
-#include <string>
-
-#include "mboxd_pnor_partition_table.h"
-
-namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
-namespace fs = std::experimental::filesystem;
-namespace vpnor = openpower::virtual_pnor;
-
-/** @brief unique_ptr functor to release a char* reference. */
-struct StringDeleter
-{
-    void operator()(char* ptr) const
-    {
-        free(ptr);
-    }
-};
-using StringPtr = std::unique_ptr<char, StringDeleter>;
-
-int flash_dev_init(struct mbox_context* context)
-{
-    StringPtr filename(get_dev_mtd());
-    int fd = 0;
-    int rc = 0;
-
-    if (!filename)
-    {
-        MSG_ERR("Couldn't find the flash /dev/mtd partition\n");
-        return -1;
-    }
-
-    MSG_DBG("Opening %s\n", filename.get());
-
-    fd = open(filename.get(), O_RDWR);
-    if (fd < 0)
-    {
-        MSG_ERR("Couldn't open %s with flags O_RDWR: %s\n", filename.get(),
-                strerror(errno));
-        return -errno;
-    }
-
-    // Read the Flash Info
-    if (ioctl(fd, MEMGETINFO, &context->mtd_info) == -1)
-    {
-        MSG_ERR("Couldn't get information about MTD: %s\n", strerror(errno));
-        close(fd);
-        return -errno;
-    }
-
-    if (context->flash_size == 0)
-    {
-        // See comment in mboxd_flash_physical.c on why
-        // this is needed.
-        context->flash_size = context->mtd_info.size;
-    }
-
-    // Hostboot requires a 4K block-size to be used in the FFS flash structure
-    context->mtd_info.erasesize = 4096;
-    context->erase_size_shift = log_2(context->mtd_info.erasesize);
-    context->flash_bmap = NULL;
-    context->fds[MTD_FD].fd = -1;
-
-    close(fd);
-    return rc;
-}
-
-void flash_dev_free(struct mbox_context* context)
-{
-    // No-op
-}
-
-int flash_set_bytemap(struct mbox_context* context, uint32_t offset,
-                      uint32_t count, uint8_t val)
-{
-    // No-op
-    return 0;
-}
-
-int flash_erase(struct mbox_context* context, uint32_t offset, uint32_t count)
-{
-    // No-op
-    return 0;
-}
-
-/*
- * flash_copy() - Copy data from the virtual pnor into a provided buffer
- * @context:    The mbox context pointer
- * @offset:     The pnor 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. flash_copy will copy at most 'size' bytes, but it may
- *              copy less.
- */
-int64_t flash_copy(struct mbox_context* context, uint32_t offset, void* mem,
-                   uint32_t size)
-{
-    vpnor::partition::Table* table;
-    int rc = size;
-
-    if (!(context && context->vpnor && context->vpnor->table))
-    {
-        MSG_ERR("Trying to copy data with uninitialised context!\n");
-        return -EINVAL;
-    }
-
-    table = context->vpnor->table;
-
-    MSG_DBG("Copy virtual pnor to %p for size 0x%.8x from offset 0x%.8x\n", mem,
-            size, offset);
-
-    /* The virtual PNOR partition table starts at offset 0 in the virtual
-     * pnor image. Check if host asked for an offset that lies within the
-     * partition table.
-     */
-    size_t sz = table->size();
-    if (offset < sz)
-    {
-        const pnor_partition_table& toc = table->getHostTable();
-        rc = std::min(sz - offset, static_cast<size_t>(size));
-        memcpy(mem, ((uint8_t*)&toc) + offset, rc);
-        return rc;
-    }
-
-    try
-    {
-        vpnor::Request req(context, offset);
-        rc = req.read(mem, size);
-    }
-    catch (vpnor::UnmappedOffset& e)
-    {
-        /*
-         * Hooo boy. Pretend that this is valid flash so we don't have
-         * discontiguous regions presented to the host. Instead, fill a window
-         * with 0xff so the 'flash' looks erased. Writes to such regions are
-         * dropped on the floor, see the implementation of flash_write() below.
-         */
-        MSG_INFO("Host requested unmapped region of %" PRId32
-                 " bytes at offset 0x%" PRIx32 "\n",
-                 size, offset);
-        uint32_t span = e.next - e.base;
-        rc = std::min(size, span);
-        memset(mem, 0xff, rc);
-    }
-    catch (std::exception& e)
-    {
-        MSG_ERR("%s\n", e.what());
-        phosphor::logging::commit<err::InternalFailure>();
-        rc = -EIO;
-    }
-    return rc;
-}
-
-/*
- * flash_write() - Write to the virtual pnor 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
- */
-
-int flash_write(struct mbox_context* context, uint32_t offset, void* buf,
-                uint32_t count)
-{
-
-    if (!(context && context->vpnor && context->vpnor->table))
-    {
-        MSG_ERR("Trying to write data with uninitialised context!\n");
-        return -EINVAL;
-    }
-
-    vpnor::partition::Table* table = context->vpnor->table;
-
-    try
-    {
-        const struct pnor_partition& part = table->partition(offset);
-        if (part.data.user.data[1] & PARTITION_READONLY)
-        {
-            MSG_ERR("Unreachable: Host attempted to write to read-only "
-                    "partition %s\n",
-                    part.data.name);
-            return -EPERM;
-        }
-
-        MSG_DBG("Write flash @ 0x%.8x for 0x%.8x from %p\n", offset, count,
-                buf);
-        vpnor::Request req(context, offset);
-        req.write(buf, count);
-    }
-    catch (vpnor::UnmappedOffset& e)
-    {
-        MSG_ERR("Unreachable: Host attempted to write %" PRIu32
-                " bytes to unmapped offset 0x%" PRIx32 "\n",
-                count, offset);
-        return -EACCES;
-    }
-    catch (const vpnor::OutOfBoundsOffset& e)
-    {
-        MSG_ERR("%s\n", e.what());
-        return -EINVAL;
-    }
-    catch (const std::exception& e)
-    {
-        MSG_ERR("%s\n", e.what());
-        phosphor::logging::commit<err::InternalFailure>();
-        return -EIO;
-    }
-    return 0;
-}
-
-static bool vpnor_partition_is_readonly(const pnor_partition& part)
-{
-    return part.data.user.data[1] & PARTITION_READONLY;
-}
-
-int flash_validate(struct mbox_context* context, uint32_t offset,
-                   uint32_t size __attribute__((unused)), bool ro)
-{
-    /* All reads are allowed */
-    if (ro)
-    {
-        return 0;
-    }
-
-    /* Only allow write windows on regions mapped by the ToC as writeable */
-    try
-    {
-        const pnor_partition& part = context->vpnor->table->partition(offset);
-        if (vpnor_partition_is_readonly(part))
-        {
-            return -EPERM;
-        }
-    }
-    catch (const openpower::virtual_pnor::UnmappedOffset& e)
-    {
-        /*
-         * Writes to unmapped areas are not meaningful, so deny the request.
-         * This removes the ability for a compromised host to abuse unused
-         * space if any data was to be persisted (which it isn't).
-         */
-        return -EACCES;
-    }
-
-    return 0;
-}
diff --git a/vpnor/lpc_reset.cpp b/vpnor/lpc_reset.cpp
deleted file mode 100644
index 80ec6ac..0000000
--- a/vpnor/lpc_reset.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Mailbox Daemon LPC Helpers
- *
- * Copyright 2017 IBM
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-extern "C" {
-#include "lpc.h"
-#include "mboxd.h"
-}
-
-#include "mboxd_pnor_partition_table.h"
-
-/*
- * lpc_reset() - Reset the lpc bus mapping
- * @context:     The mbox context pointer
- *
- * Return        0 on success otherwise negative error code
- */
-int lpc_reset(struct mbox_context* context)
-{
-    int rc;
-
-    destroy_vpnor(context);
-
-    rc = init_vpnor(context);
-    if (rc < 0)
-        return rc;
-
-    rc = vpnor_copy_bootloader_partition(context);
-    if (rc < 0)
-        return rc;
-
-    return lpc_map_memory(context);
-}
diff --git a/vpnor/mboxd_pnor_partition_table.cpp b/vpnor/mboxd_pnor_partition_table.cpp
index a6ebd4e..394cc18 100644
--- a/vpnor/mboxd_pnor_partition_table.cpp
+++ b/vpnor/mboxd_pnor_partition_table.cpp
@@ -1,11 +1,13 @@
 // SPDX-License-Identifier: Apache-2.0
 // Copyright (C) 2018 IBM Corp.
-extern "C" {
-#include "flash.h"
-}
-
 #include "config.h"
 
+#include <assert.h>
+
+extern "C" {
+#include "backend.h"
+}
+
 #include "pnor_partition_table.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
@@ -16,57 +18,58 @@
 #include "mboxd.h"
 #include "mboxd_pnor_partition_table.h"
 
-int init_vpnor(struct mbox_context* context)
+void vpnor_default_paths(vpnor_partition_paths* paths)
 {
-    if (context && !context->vpnor)
-    {
-        int rc;
-
-        strncpy(context->paths.ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX);
-        context->paths.ro_loc[PATH_MAX - 1] = '\0';
-        strncpy(context->paths.rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX);
-        context->paths.rw_loc[PATH_MAX - 1] = '\0';
-        strncpy(context->paths.prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX);
-        context->paths.prsv_loc[PATH_MAX - 1] = '\0';
-        strncpy(context->paths.patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX);
-        context->paths.prsv_loc[PATH_MAX - 1] = '\0';
-
-        rc = init_vpnor_from_paths(context);
-        if (rc < 0)
-        {
-            return rc;
-        }
-    }
-
-    return 0;
+    strncpy(paths->ro_loc, PARTITION_FILES_RO_LOC, PATH_MAX);
+    paths->ro_loc[PATH_MAX - 1] = '\0';
+    strncpy(paths->rw_loc, PARTITION_FILES_RW_LOC, PATH_MAX);
+    paths->rw_loc[PATH_MAX - 1] = '\0';
+    strncpy(paths->prsv_loc, PARTITION_FILES_PRSV_LOC, PATH_MAX);
+    paths->prsv_loc[PATH_MAX - 1] = '\0';
+    strncpy(paths->patch_loc, PARTITION_FILES_PATCH_LOC, PATH_MAX);
+    paths->prsv_loc[PATH_MAX - 1] = '\0';
 }
 
-int init_vpnor_from_paths(struct mbox_context* context)
+int vpnor_init(struct backend* backend, const vpnor_partition_paths* paths)
 {
     namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
     namespace fs = std::experimental::filesystem;
     namespace vpnor = openpower::virtual_pnor;
 
-    if (context && !context->vpnor)
+    if (!(backend && paths))
+        return -EINVAL;
+
+    vpnor_data* priv = new vpnor_data;
+    assert(priv);
+
+    priv->paths = *paths;
+    backend->priv = priv;
+
+    try
     {
+        priv->vpnor = new vpnor_partition_table;
+        priv->vpnor->table =
+            new openpower::virtual_pnor::partition::Table(backend);
+    }
+    catch (vpnor::TocEntryError& e)
+    {
+        MSG_ERR("%s\n", e.what());
         try
         {
-            context->vpnor = new vpnor_partition_table;
-            context->vpnor->table =
-                new openpower::virtual_pnor::partition::Table(context);
-        }
-        catch (vpnor::TocEntryError& e)
-        {
-            MSG_ERR("%s\n", e.what());
             phosphor::logging::commit<err::InternalFailure>();
-            return -EINVAL;
         }
+        catch (const std::exception& e)
+        {
+            MSG_ERR("Failed to commit InternalFailure: %s\n", e.what());
+        }
+        return -EINVAL;
     }
 
     return 0;
 }
 
-int vpnor_copy_bootloader_partition(const struct mbox_context* context)
+int vpnor_copy_bootloader_partition(const struct backend* backend, void* buf,
+                                    uint32_t count)
 {
     // The hostboot bootloader has certain size/offset assumptions, so
     // we need a special partition table here.
@@ -90,8 +93,12 @@
     try
     {
         vpnor_partition_table vtbl{};
-        struct mbox_context local = *context;
-        local.vpnor = &vtbl;
+        struct vpnor_data priv;
+        struct backend local = *backend;
+
+        priv.vpnor = &vtbl;
+        priv.paths = ((struct vpnor_data*)backend->priv)->paths;
+        local.priv = &priv;
         local.block_size_shift = log_2(eraseSize);
 
         openpower::virtual_pnor::partition::Table blTable(&local);
@@ -104,16 +111,16 @@
         size_t hbbOffset = partition.data.base * eraseSize;
         uint32_t hbbSize = partition.data.actual;
 
-        if (context->mem_size < tocStart + blTable.capacity() ||
-            context->mem_size < hbbOffset + hbbSize)
+        if (count < tocStart + blTable.capacity() ||
+            count < hbbOffset + hbbSize)
         {
             MSG_ERR("Reserved memory too small for dumb bootstrap\n");
             return -EINVAL;
         }
 
-        uint8_t* buf8 = static_cast<uint8_t*>(context->mem);
-        flash_copy(&local, tocOffset, buf8 + tocStart, blTable.capacity());
-        flash_copy(&local, hbbOffset, buf8 + hbbOffset, hbbSize);
+        uint8_t* buf8 = static_cast<uint8_t*>(buf);
+        backend_copy(&local, tocOffset, buf8 + tocStart, blTable.capacity());
+        backend_copy(&local, hbbOffset, buf8 + hbbOffset, hbbSize);
     }
     catch (err::InternalFailure& e)
     {
@@ -130,12 +137,17 @@
     return 0;
 }
 
-void destroy_vpnor(struct mbox_context* context)
+void vpnor_destroy(struct backend* backend)
 {
-    if (context && context->vpnor)
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+
+    if (priv)
     {
-        delete context->vpnor->table;
-        delete context->vpnor;
-        context->vpnor = nullptr;
+        if (priv->vpnor)
+        {
+            delete priv->vpnor->table;
+        }
+        delete priv->vpnor;
     }
+    delete priv;
 }
diff --git a/vpnor/mboxd_pnor_partition_table.h b/vpnor/mboxd_pnor_partition_table.h
index d13a2d2..e325775 100644
--- a/vpnor/mboxd_pnor_partition_table.h
+++ b/vpnor/mboxd_pnor_partition_table.h
@@ -6,6 +6,7 @@
 
 #include <limits.h>
 #include "pnor_partition_defs.h"
+#include "backend.h"
 
 struct mbox_context;
 struct vpnor_partition_table;
@@ -18,47 +19,55 @@
     char patch_loc[PATH_MAX];
 };
 
+struct vpnor_data {
+	struct vpnor_partition_table *vpnor;
+	struct vpnor_partition_paths paths;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/** @brief Populate the path object with the default partition paths
+ *
+ *  @param[in/out] paths - A paths object in which to store the defaults
+ *
+ *  Returns 0 if the call succeeds, else a negative error code.
+ */
+void vpnor_default_paths(struct vpnor_partition_paths *paths);
+
 /** @brief Create a virtual PNOR partition table.
  *
- *  @param[in] context - mbox context pointer
+ *  @param[in] backend - The backend context pointer
+ *  @param[in] paths - A paths object pointer to initialise vpnor
  *
  *  This API should be called before calling any other APIs below. If a table
  *  already exists, this function will not do anything further. This function
  *  will not do anything if the context is NULL.
  *
- *  Returns 0 if the call succeeds, else a negative error code.
- */
-int init_vpnor(struct mbox_context *context);
-
-/** @brief Create a virtual PNOR partition table.
- *
- *  @param[in] context - mbox context pointer
- *
- *  This API is same as above one but requires context->path is initialised
- *  with all the necessary paths.
+ *  The content of the paths object is copied out, ownership is retained by the
+ *  caller.
  *
  *  Returns 0 if the call succeeds, else a negative error code.
  */
 
-int init_vpnor_from_paths(struct mbox_context *context);
+int vpnor_init(struct backend *backend,
+	       const struct vpnor_partition_paths *paths);
 
 /** @brief Copy bootloader partition (alongwith TOC) to LPC memory
  *
- *  @param[in] context - mbox context pointer
+ *  @param[in] backend - The backend context pointer
  *
  *  @returns 0 on success, negative error code on failure
  */
-int vpnor_copy_bootloader_partition(const struct mbox_context *context);
+int vpnor_copy_bootloader_partition(const struct backend *backend, void *buf,
+				    uint32_t count);
 
 /** @brief Destroy partition table, if it exists.
  *
- *  @param[in] context - mbox context pointer
+ *  @param[in] backend - The backend context pointer
  */
-void destroy_vpnor(struct mbox_context *context);
+void vpnor_destroy(struct backend *backend);
 
 #ifdef __cplusplus
 }
diff --git a/vpnor/pnor_partition.cpp b/vpnor/pnor_partition.cpp
index 613ee26..9854159 100644
--- a/vpnor/pnor_partition.cpp
+++ b/vpnor/pnor_partition.cpp
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // Copyright (C) 2018 IBM Corp.
 extern "C" {
-#include "flash.h"
+#include "mboxd.h"
 }
 
 #include "config.h"
@@ -39,8 +39,10 @@
 
 fs::path Request::getPartitionFilePath(int flags)
 {
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+
     // Check if partition exists in patch location
-    auto dst = fs::path(ctx->paths.patch_loc) / partition.data.name;
+    auto dst = fs::path(priv->paths.patch_loc) / partition.data.name;
     if (fs::is_regular_file(dst))
     {
         return dst;
@@ -50,15 +52,15 @@
             (PARTITION_PRESERVED | PARTITION_READONLY))
     {
         case PARTITION_PRESERVED:
-            dst = ctx->paths.prsv_loc;
+            dst = priv->paths.prsv_loc;
             break;
 
         case PARTITION_READONLY:
-            dst = ctx->paths.ro_loc;
+            dst = priv->paths.ro_loc;
             break;
 
         default:
-            dst = ctx->paths.rw_loc;
+            dst = priv->paths.rw_loc;
     }
     dst /= partition.data.name;
 
@@ -69,22 +71,22 @@
 
     if (flags == O_RDONLY)
     {
-        dst = fs::path(ctx->paths.ro_loc) / partition.data.name;
+        dst = fs::path(priv->paths.ro_loc) / partition.data.name;
         assert(fs::exists(dst));
         return dst;
     }
 
     assert(flags == O_RDWR);
-    auto src = fs::path(ctx->paths.ro_loc) / partition.data.name;
+    auto src = fs::path(priv->paths.ro_loc) / partition.data.name;
     assert(fs::exists(src));
 
     MSG_DBG("RWRequest: Didn't find '%s' under '%s', copying from '%s'\n",
             partition.data.name, dst.c_str(), src.c_str());
 
-    dst = ctx->paths.rw_loc;
+    dst = priv->paths.rw_loc;
     if (partition.data.user.data[1] & PARTITION_PRESERVED)
     {
-        dst = ctx->paths.prsv_loc;
+        dst = priv->paths.prsv_loc;
     }
 
     dst /= partition.data.name;
@@ -96,7 +98,7 @@
 size_t Request::clamp(size_t len)
 {
     size_t maxAccess = offset + len;
-    size_t partSize = partition.data.size << ctx->block_size_shift;
+    size_t partSize = partition.data.size << backend->block_size_shift;
     return std::min(maxAccess, partSize) - offset;
 }
 
@@ -168,7 +170,7 @@
     else
     {
         memcpy((char*)map + offset, buf, len);
-        flash_set_bytemap(ctx, base + offset, len, FLASH_DIRTY);
+        backend_set_bytemap(backend, base + offset, len, FLASH_DIRTY);
     }
     munmap(map, fileSize);
     close(fd);
diff --git a/vpnor/pnor_partition.hpp b/vpnor/pnor_partition.hpp
index 95d4309..c91f22e 100644
--- a/vpnor/pnor_partition.hpp
+++ b/vpnor/pnor_partition.hpp
@@ -3,7 +3,8 @@
 #pragma once
 
 extern "C" {
-#include "mboxd.h"
+#include "backend.h"
+#include "mboxd_pnor_partition_table.h"
 };
 
 #include "pnor_partition_table.hpp"
@@ -14,8 +15,6 @@
 #include <experimental/filesystem>
 #include <string>
 
-#include "mboxd_pnor_partition_table.h"
-
 namespace openpower
 {
 namespace virtual_pnor
@@ -28,7 +27,7 @@
   public:
     /** @brief Construct a flash access request
      *
-     *  @param[in] ctx - The mbox context used to process the request
+     *  @param[in] backend - The backend context used to process the request
      *  @param[in] offset - The absolute offset into the flash device as
      *                      provided by the mbox message associated with the
      *                      request
@@ -37,9 +36,10 @@
      *  the ctx pointer must strictly exceed the lifetime of the class
      *  instance.
      */
-    Request(struct mbox_context* ctx, size_t offset) :
-        ctx(ctx), partition(ctx->vpnor->table->partition(offset)),
-        base(partition.data.base << ctx->block_size_shift),
+    Request(struct backend* backend, size_t offset) :
+        backend(backend), partition(((struct vpnor_data*)backend->priv)
+                                        ->vpnor->table->partition(offset)),
+        base(partition.data.base << backend->block_size_shift),
         offset(offset - base)
     {
     }
@@ -64,7 +64,8 @@
             std::stringstream err;
             err << "Request size 0x" << std::hex << len << " from offset 0x"
                 << std::hex << offset << " exceeds the partition size 0x"
-                << std::hex << (partition.data.size << ctx->block_size_shift);
+                << std::hex
+                << (partition.data.size << backend->block_size_shift);
             throw OutOfBoundsOffset(err.str());
         }
         constexpr auto flags = O_RDWR;
@@ -132,7 +133,7 @@
     size_t fulfil(const std::experimental::filesystem::path& path, int flags,
                   void* dst, size_t len);
 
-    struct mbox_context* ctx;
+    struct backend* backend;
     const pnor_partition& partition;
     size_t base;
     size_t offset;
diff --git a/vpnor/pnor_partition_table.cpp b/vpnor/pnor_partition_table.cpp
index 7e99ae2..2a9442b 100644
--- a/vpnor/pnor_partition_table.cpp
+++ b/vpnor/pnor_partition_table.cpp
@@ -14,8 +14,11 @@
 #include <phosphor-logging/elog-errors.hpp>
 #include <regex>
 
+extern "C" {
+#include "backend.h"
 #include "common.h"
 #include "mboxd.h"
+}
 
 namespace openpower
 {
@@ -28,11 +31,11 @@
 namespace partition
 {
 
-Table::Table(const struct mbox_context* ctx) :
+Table::Table(const struct backend* be) :
     szBytes(sizeof(pnor_partition_table)), numParts(0),
-    blockSize(1 << ctx->erase_size_shift), pnorSize(ctx->flash_size)
+    blockSize(1 << be->erase_size_shift), pnorSize(be->flash_size)
 {
-    preparePartitions(ctx);
+    preparePartitions((const struct vpnor_data*)be->priv);
     prepareHeader();
     hostTbl = endianFixup(tbl);
 }
@@ -74,10 +77,10 @@
     tbl.resize(capacity());
 }
 
-void Table::preparePartitions(const struct mbox_context* ctx)
+void Table::preparePartitions(const struct vpnor_data* priv)
 {
-    const fs::path roDir = ctx->paths.ro_loc;
-    const fs::path patchDir = ctx->paths.patch_loc;
+    const fs::path roDir(priv->paths.ro_loc);
+    const fs::path patchDir(priv->paths.patch_loc);
     fs::path tocFile = roDir / PARTITION_TOC_FILE;
     allocateMemory(tocFile);
 
diff --git a/vpnor/pnor_partition_table.hpp b/vpnor/pnor_partition_table.hpp
index 8ba432a..90f014c 100644
--- a/vpnor/pnor_partition_table.hpp
+++ b/vpnor/pnor_partition_table.hpp
@@ -7,8 +7,12 @@
 #include <numeric>
 #include <vector>
 
+extern "C" {
+#include "backend.h"
 #include "common.h"
+#include "mboxd_pnor_partition_table.h"
 #include "pnor_partition_defs.h"
+}
 
 struct mbox_context;
 
@@ -97,7 +101,7 @@
      *
      * Throws MalformedTocEntry, InvalidTocEntry
      */
-    Table(const struct mbox_context* ctx);
+    Table(const struct backend* be);
 
     Table(const Table&) = delete;
     Table& operator=(const Table&) = delete;
@@ -186,7 +190,7 @@
      *
      * Throws: MalformedTocEntry, InvalidTocEntry
      */
-    void preparePartitions(const struct mbox_context* ctx);
+    void preparePartitions(const struct vpnor_data* ctx);
 
     /** @brief Prepares the PNOR header.
      */
diff --git a/vpnor/protocol.h b/vpnor/protocol.h
deleted file mode 100644
index 8cebda6..0000000
--- a/vpnor/protocol.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: Apache-2.0 */
-/* Copyright (C) 2018 IBM Corp. */
-#ifndef VPNOR_PROTOCOL_H
-#define VPNOR_PROTOCOL_H
-
-#include "protocol.h"
-
-/* Protocol v1 */
-int protocol_v1_vpnor_create_window(struct mbox_context *context,
-                                    struct protocol_create_window *io);
-
-/* Protocol v2 */
-int protocol_v2_vpnor_create_window(struct mbox_context *context,
-			            struct protocol_create_window *io);
-
-#endif /* VPNOR_PROTOCOL_H */
diff --git a/vpnor/test/Makefile.am.include b/vpnor/test/Makefile.am.include
index 5ff4dcc..0600969 100644
--- a/vpnor/test/Makefile.am.include
+++ b/vpnor/test/Makefile.am.include
@@ -1,7 +1,4 @@
-TEST_MBOX_VPNOR_SRCS = \
-	common.c \
-	vpnor/pnor_partition_table.cpp \
-	%reldir%/tmpd.cpp
+TEST_MOCK_VPNOR_SRCS = $(TEST_MOCK_CORE)
 
 TEST_MBOX_VPNOR_INTEG_SRCS = \
 	common.c \
@@ -9,9 +6,8 @@
 	transport_mbox.c \
 	windows.c \
 	lpc.c \
-	vpnor/lpc_reset.cpp \
+	vpnor/backend.cpp \
 	vpnor/mboxd_pnor_partition_table.cpp \
-	vpnor/flash.cpp \
 	vpnor/pnor_partition.cpp \
 	vpnor/pnor_partition_table.cpp \
 	%reldir%/tmpd.cpp
@@ -22,14 +18,14 @@
 	$(PHOSPHOR_DBUS_INTERFACES_LIBS)
 
 vpnor_test_create_pnor_partition_table_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_pnor_partition_table.cpp
 vpnor_test_create_pnor_partition_table_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_pnor_partition_table_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_partition_exists_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_partition_exists.cpp
 vpnor_test_create_read_window_partition_exists_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
@@ -106,63 +102,63 @@
 vpnor_test_toc_flags_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_toc_overlap_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/toc_overlap.cpp
 vpnor_test_toc_overlap_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_toc_overlap_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_toc_lookup_found_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/toc_lookup_found.cpp
 vpnor_test_toc_lookup_found_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_toc_lookup_found_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_toc_lookup_failed_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/toc_lookup_failed.cpp
 vpnor_test_toc_lookup_failed_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_toc_lookup_failed_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_toc_missing_file_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/toc_missing_file.cpp
 vpnor_test_toc_missing_file_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_toc_missing_file_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_oob_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_oob.cpp
 vpnor_test_create_read_window_oob_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_oob_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_toc_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_toc.cpp
 vpnor_test_create_read_window_toc_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_toc_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_straddle_partitions_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_straddle_partitions.cpp
 vpnor_test_create_read_window_straddle_partitions_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_straddle_partitions_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_partition_invalid_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_partition_invalid.cpp
 vpnor_test_create_read_window_partition_invalid_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_partition_invalid_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_read_patch_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/read_patch.cpp
 vpnor_test_read_patch_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
@@ -176,56 +172,56 @@
 vpnor_test_write_patch_resize_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_dump_flash_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/dump_flash.cpp
 vpnor_test_dump_flash_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_dump_flash_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_size_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_size.cpp
 vpnor_test_create_read_window_size_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_size_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_read_window_remap_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_read_window_remap.cpp
 vpnor_test_create_read_window_remap_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_read_window_remap_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_write_window_ro_partition_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_write_window_ro_partition.cpp
 vpnor_test_create_write_window_ro_partition_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_write_window_ro_partition_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_write_window_rw_partition_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_write_window_rw_partition.cpp
 vpnor_test_create_write_window_rw_partition_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_write_window_rw_partition_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_create_write_window_unmapped_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/create_write_window_unmapped.cpp
 vpnor_test_create_write_window_unmapped_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_create_write_window_unmapped_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_write_toc_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/write_toc.cpp
 vpnor_test_write_toc_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
 vpnor_test_write_toc_LDADD = $(VPNOR_LDADD)
 
 vpnor_test_force_readonly_toc_SOURCES = \
-	$(TEST_MOCK_SRCS) \
+	$(TEST_MOCK_VPNOR_SRCS) \
 	$(TEST_MBOX_VPNOR_INTEG_SRCS) \
 	%reldir%/force_readonly_toc.cpp
 vpnor_test_force_readonly_toc_LDFLAGS = $(OESDK_TESTCASE_FLAGS)
diff --git a/vpnor/test/create_pnor_partition_table.cpp b/vpnor/test/create_pnor_partition_table.cpp
index cdbe8b6..f198f29 100644
--- a/vpnor/test/create_pnor_partition_table.cpp
+++ b/vpnor/test/create_pnor_partition_table.cpp
@@ -34,9 +34,9 @@
 
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    const openpower::virtual_pnor::partition::Table table(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    const openpower::virtual_pnor::partition::Table table(&ctx->backend);
 
     pnor_partition_table expectedTable{};
     expectedTable.data.magic = PARTITION_HEADER_MAGIC;
diff --git a/vpnor/test/create_read_window_oob.cpp b/vpnor/test/create_read_window_oob.cpp
index 240c10e..d1e7e64 100644
--- a/vpnor/test/create_read_window_oob.cpp
+++ b/vpnor/test/create_read_window_oob.cpp
@@ -48,9 +48,8 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_read_window_partition_exists.cpp b/vpnor/test/create_read_window_partition_exists.cpp
index d3532ec..7ff3018 100644
--- a/vpnor/test/create_read_window_partition_exists.cpp
+++ b/vpnor/test/create_read_window_partition_exists.cpp
@@ -53,13 +53,11 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
     root.write("HBB", data, sizeof(data));
 
-    init_vpnor_from_paths(ctx);
-
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
 
diff --git a/vpnor/test/create_read_window_partition_invalid.cpp b/vpnor/test/create_read_window_partition_invalid.cpp
index 36431d3..4d28b2c 100644
--- a/vpnor/test/create_read_window_partition_invalid.cpp
+++ b/vpnor/test/create_read_window_partition_invalid.cpp
@@ -44,14 +44,14 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
 
     rc = mbox_command_dispatch(ctx, create_read_window,
                                sizeof(create_read_window));
+
     return !(rc == 1);
 }
diff --git a/vpnor/test/create_read_window_remap.cpp b/vpnor/test/create_read_window_remap.cpp
index 7a11053..f81d1bc 100644
--- a/vpnor/test/create_read_window_remap.cpp
+++ b/vpnor/test/create_read_window_remap.cpp
@@ -47,9 +47,8 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_read_window_size.cpp b/vpnor/test/create_read_window_size.cpp
index 6ea11f1..4e8cb5d 100644
--- a/vpnor/test/create_read_window_size.cpp
+++ b/vpnor/test/create_read_window_size.cpp
@@ -56,9 +56,8 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_read_window_straddle_partitions.cpp b/vpnor/test/create_read_window_straddle_partitions.cpp
index bc963dd..bdc3fa5 100644
--- a/vpnor/test/create_read_window_straddle_partitions.cpp
+++ b/vpnor/test/create_read_window_straddle_partitions.cpp
@@ -52,9 +52,8 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_read_window_toc.cpp b/vpnor/test/create_read_window_toc.cpp
index fee61ca..7571f37 100644
--- a/vpnor/test/create_read_window_toc.cpp
+++ b/vpnor/test/create_read_window_toc.cpp
@@ -55,15 +55,13 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    vpnor::partition::Table table(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    vpnor::partition::Table table(&ctx->backend);
 
     /* Make sure the ToC exactly fits in the space allocated for it */
     assert(table.capacity() == TOC_PART_SIZE);
 
-    init_vpnor_from_paths(ctx);
-
     rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
 
diff --git a/vpnor/test/create_write_window_ro_partition.cpp b/vpnor/test/create_write_window_ro_partition.cpp
index 5cc19ca..59d4a0d 100644
--- a/vpnor/test/create_write_window_ro_partition.cpp
+++ b/vpnor/test/create_write_window_ro_partition.cpp
@@ -45,11 +45,9 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-
-    init_vpnor_from_paths(ctx);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_write_window_rw_partition.cpp b/vpnor/test/create_write_window_rw_partition.cpp
index 879fd85..ed6f3dd 100644
--- a/vpnor/test/create_write_window_rw_partition.cpp
+++ b/vpnor/test/create_write_window_rw_partition.cpp
@@ -45,11 +45,9 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-
-    init_vpnor_from_paths(ctx);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/create_write_window_unmapped.cpp b/vpnor/test/create_write_window_unmapped.cpp
index 76694fc..d7f76fd 100644
--- a/vpnor/test/create_write_window_unmapped.cpp
+++ b/vpnor/test/create_write_window_unmapped.cpp
@@ -46,11 +46,9 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-
-    init_vpnor_from_paths(ctx);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == MBOX_R_SUCCESS);
diff --git a/vpnor/test/dump_flash.cpp b/vpnor/test/dump_flash.cpp
index 5179b7c..f285a61 100644
--- a/vpnor/test/dump_flash.cpp
+++ b/vpnor/test/dump_flash.cpp
@@ -68,9 +68,8 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    tctx->ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(tctx->ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(tctx->ctx);
+    tctx->ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&tctx->ctx->backend, toc, BLOCK_SIZE);
 
     rc = mbox_command_dispatch(tctx->ctx, get_info, sizeof(get_info));
     assert(rc == 1);
diff --git a/vpnor/test/force_readonly_toc.cpp b/vpnor/test/force_readonly_toc.cpp
index 8d3bec1..465adad 100644
--- a/vpnor/test/force_readonly_toc.cpp
+++ b/vpnor/test/force_readonly_toc.cpp
@@ -52,14 +52,12 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    vpnor::partition::Table table(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    vpnor::partition::Table table(&ctx->backend);
 
     assert(table.capacity() == TOC_PART_SIZE);
 
-    init_vpnor_from_paths(ctx);
-
     rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == MBOX_R_SUCCESS);
 
diff --git a/vpnor/test/read_patch.cpp b/vpnor/test/read_patch.cpp
index 631a23c..4d541f6 100644
--- a/vpnor/test/read_patch.cpp
+++ b/vpnor/test/read_patch.cpp
@@ -47,8 +47,8 @@
 
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     // PATCH_SIZE is smaller than the size of the partition we defined. This
     // test ensures that mboxd will behave correctly when we request an offset
@@ -57,8 +57,6 @@
     std::vector<uint8_t> patch(PATCH_SIZE, 0xff);
     root.patch("ONE", patch.data(), patch.size());
 
-    init_vpnor_from_paths(ctx);
-
     int rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == 1);
 
diff --git a/vpnor/test/tmpd.hpp b/vpnor/test/tmpd.hpp
index 7386f2f..f6ea7c9 100644
--- a/vpnor/test/tmpd.hpp
+++ b/vpnor/test/tmpd.hpp
@@ -4,6 +4,7 @@
 #include "config.h"
 
 extern "C" {
+#include "backend.h"
 #include "mboxd.h"
 }
 
@@ -28,8 +29,9 @@
 {
   public:
     template <std::size_t N>
-    VpnorRoot(struct mbox_context* ctx, const std::string (&toc)[N],
-              size_t blockSize)
+    VpnorRoot(struct backend* backend, const std::string (&toc)[N],
+              size_t blockSize) :
+        backend(backend)
     {
         char tmplt[] = "/tmp/vpnor_root.XXXXXX";
         char* tmpdir = mkdtemp(tmplt);
@@ -58,14 +60,21 @@
             std::ofstream(tocFilePath, std::ofstream::app) << line << "\n";
         }
 
-        strncpy(ctx->paths.ro_loc, ro().c_str(), PATH_MAX - 1);
-        ctx->paths.ro_loc[PATH_MAX - 1] = '\0';
-        strncpy(ctx->paths.rw_loc, rw().c_str(), PATH_MAX - 1);
-        ctx->paths.rw_loc[PATH_MAX - 1] = '\0';
-        strncpy(ctx->paths.prsv_loc, prsv().c_str(), PATH_MAX - 1);
-        ctx->paths.prsv_loc[PATH_MAX - 1] = '\0';
-        strncpy(ctx->paths.patch_loc, patch().c_str(), PATH_MAX - 1);
-        ctx->paths.patch_loc[PATH_MAX - 1] = '\0';
+        vpnor_partition_paths paths{};
+
+        snprintf(paths.ro_loc, PATH_MAX - 1, "%s/ro", root.c_str());
+        paths.ro_loc[PATH_MAX - 1] = '\0';
+        snprintf(paths.rw_loc, PATH_MAX - 1, "%s/rw", root.c_str());
+        paths.rw_loc[PATH_MAX - 1] = '\0';
+        snprintf(paths.prsv_loc, PATH_MAX - 1, "%s/prsv", root.c_str());
+        paths.prsv_loc[PATH_MAX - 1] = '\0';
+        snprintf(paths.patch_loc, PATH_MAX - 1, "%s/patch", root.c_str());
+        paths.patch_loc[PATH_MAX - 1] = '\0';
+
+        if (backend_probe_vpnor(backend, &paths))
+        {
+            throw std::system_error(errno, std::system_category());
+        }
     }
 
     VpnorRoot(const VpnorRoot&) = delete;
@@ -75,6 +84,7 @@
 
     ~VpnorRoot()
     {
+        backend_free(backend);
         fs::remove_all(root);
     }
     fs::path ro()
@@ -97,6 +107,7 @@
     size_t patch(const std::string& name, const void* data, size_t len);
 
   private:
+    struct backend* backend;
     fs::path root;
     const std::string attributes[4] = {"ro", "rw", "prsv", "patch"};
 };
diff --git a/vpnor/test/toc_lookup_failed.cpp b/vpnor/test/toc_lookup_failed.cpp
index bb07725..cb56e51 100644
--- a/vpnor/test/toc_lookup_failed.cpp
+++ b/vpnor/test/toc_lookup_failed.cpp
@@ -37,10 +37,10 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    vpnor::partition::Table table(ctx);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    vpnor::partition::Table table(&ctx->backend);
 
     try
     {
diff --git a/vpnor/test/toc_lookup_found.cpp b/vpnor/test/toc_lookup_found.cpp
index fccee0c..b8c879c 100644
--- a/vpnor/test/toc_lookup_found.cpp
+++ b/vpnor/test/toc_lookup_found.cpp
@@ -37,10 +37,10 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    vpnor::partition::Table table(ctx);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    vpnor::partition::Table table(&ctx->backend);
 
     const struct pnor_partition& part = table.partition("TWO");
     assert(part.data.id == 2);
diff --git a/vpnor/test/toc_missing_file.cpp b/vpnor/test/toc_missing_file.cpp
index 6b7e068..95147c0 100644
--- a/vpnor/test/toc_missing_file.cpp
+++ b/vpnor/test/toc_missing_file.cpp
@@ -37,15 +37,15 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     fs::remove(root.ro() / "TWO");
 
     try
     {
-        vpnor::partition::Table table(ctx);
+        vpnor::partition::Table table(&ctx->backend);
     }
     catch (vpnor::InvalidTocEntry& e)
     {
diff --git a/vpnor/test/toc_overlap.cpp b/vpnor/test/toc_overlap.cpp
index a7ac20d..18b7151 100644
--- a/vpnor/test/toc_overlap.cpp
+++ b/vpnor/test/toc_overlap.cpp
@@ -36,15 +36,13 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(MEM_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
 
     try
     {
-        vpnor::partition::Table table(ctx);
+        test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
     }
-    catch (vpnor::InvalidTocEntry& e)
+    catch (std::system_error& e)
     {
         return 0;
     }
diff --git a/vpnor/test/write_patch.cpp b/vpnor/test/write_patch.cpp
index 1bf6ba9..4f59db5 100644
--- a/vpnor/test/write_patch.cpp
+++ b/vpnor/test/write_patch.cpp
@@ -4,8 +4,8 @@
 #include "config.h"
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
-#include "flash.h"
 #include "mboxd.h"
 }
 
@@ -47,15 +47,14 @@
     mbox_vlog = &mbox_log_console;
     verbosity = (verbose)2;
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    ctx->backend.flash_size = 0x2000;
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
     root.write("TEST1", data, sizeof(data));
     /* flash_write doesn't copy the file for us */
     assert(fs::copy_file(root.ro() / "TEST1", root.rw() / "TEST1"));
     fs::path patch = root.patch() / "TEST1";
     assert(fs::copy_file(root.ro() / "TEST1", patch));
 
-    init_vpnor_from_paths(ctx);
-
     /* Test */
     memset(src, 0x33, sizeof(src));
     rc = flash_write(ctx, 0x1000, src, sizeof(src));
@@ -79,8 +78,5 @@
     munmap(map, sizeof(src));
     close(fd);
 
-    destroy_vpnor(ctx);
-    free(ctx->flash_bmap);
-
     return rc;
 }
diff --git a/vpnor/test/write_patch_resize.cpp b/vpnor/test/write_patch_resize.cpp
index 9a6979f..ce128c8 100644
--- a/vpnor/test/write_patch_resize.cpp
+++ b/vpnor/test/write_patch_resize.cpp
@@ -4,8 +4,8 @@
 #include "config.h"
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
-#include "flash.h"
 #include "mboxd.h"
 }
 
@@ -46,15 +46,14 @@
     mbox_vlog = &mbox_log_console;
     verbosity = (verbose)2;
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    ctx->backend.flash_size = 0x2000;
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
     std::vector<uint8_t> roContent(PART_SIZE, 0xff);
     root.write("TEST1", roContent.data(), roContent.size());
     /* flash_write doesn't copy the file for us */
     std::vector<uint8_t> patchContent(PATCH_SIZE, 0xaa);
     root.patch("TEST1", patchContent.data(), patchContent.size());
 
-    init_vpnor_from_paths(ctx);
-
     /* Test */
     std::vector<uint8_t> update(UPDATE_SIZE, 0x55);
     rc = flash_write(ctx, 0x1000, update.data(), update.size());
@@ -71,8 +70,5 @@
     munmap(map, update.size());
     close(fd);
 
-    destroy_vpnor(ctx);
-    free(ctx->flash_bmap);
-
     return rc;
 }
diff --git a/vpnor/test/write_prsv.cpp b/vpnor/test/write_prsv.cpp
index 53f3cfe..9ee1fb3 100644
--- a/vpnor/test/write_prsv.cpp
+++ b/vpnor/test/write_prsv.cpp
@@ -2,8 +2,8 @@
 // Copyright (C) 2018 IBM Corp.
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
-#include "flash.h"
 #include "mboxd.h"
 }
 
@@ -41,8 +41,8 @@
     mbox_vlog = &mbox_log_console;
     verbosity = (verbose)2;
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx->backend.flash_size = 0x2000;
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     /* Test */
     memset(src, 0xaa, sizeof(src));
@@ -60,8 +60,5 @@
     munmap(map, sizeof(src));
     close(fd);
 
-    /* Cleanup */
-    destroy_vpnor(ctx);
-
     return 0;
 }
diff --git a/vpnor/test/write_ro.cpp b/vpnor/test/write_ro.cpp
index e5671b1..e6bedd3 100644
--- a/vpnor/test/write_ro.cpp
+++ b/vpnor/test/write_ro.cpp
@@ -3,8 +3,8 @@
 #include "config.h"
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
-#include "flash.h"
 #include "mboxd.h"
 }
 
@@ -39,8 +39,8 @@
     mbox_vlog = &mbox_log_console;
     verbosity = (verbose)2;
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    init_vpnor_from_paths(ctx);
+    ctx->backend.flash_size = 0x2000;
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
 
     /* Test */
     rc = flash_write(ctx, 0x1000, src, sizeof(src));
@@ -48,7 +48,5 @@
     /* Verify we can't write to RO partitions */
     assert(rc != 0);
 
-    destroy_vpnor(ctx);
-
     return 0;
 }
diff --git a/vpnor/test/write_rw.cpp b/vpnor/test/write_rw.cpp
index e64f01e..3b742a1 100644
--- a/vpnor/test/write_rw.cpp
+++ b/vpnor/test/write_rw.cpp
@@ -3,8 +3,8 @@
 #include "config.h"
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
-#include "flash.h"
 #include "mboxd.h"
 }
 
@@ -41,10 +41,10 @@
     mbox_vlog = &mbox_log_console;
     verbosity = (verbose)2;
 
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
+    ctx->backend.flash_size = 0x2000;
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
     /* flash_write() doesn't copy the file for us */
     assert(fs::copy_file(root.ro() / "TEST1", root.rw() / "TEST1"));
-    init_vpnor_from_paths(ctx);
 
     /* Test */
     memset(src, 0xbb, sizeof(src));
@@ -95,7 +95,5 @@
     munmap(map, sizeof(src));
     close(fd);
 
-    destroy_vpnor(ctx);
-
     return 0;
 }
diff --git a/vpnor/test/write_toc.cpp b/vpnor/test/write_toc.cpp
index 6f6b101..8b690a1 100644
--- a/vpnor/test/write_toc.cpp
+++ b/vpnor/test/write_toc.cpp
@@ -67,14 +67,12 @@
     system_set_reserved_size(MEM_SIZE);
     system_set_mtd_sizes(PNOR_SIZE, ERASE_SIZE);
 
-    ctx = mbox_create_test_context(N_WINDOWS, WINDOW_SIZE);
-    test::VpnorRoot root(ctx, toc, BLOCK_SIZE);
-    vpnor::partition::Table table(ctx);
+    ctx = mbox_create_frontend_context(N_WINDOWS, WINDOW_SIZE);
+    test::VpnorRoot root(&ctx->backend, toc, BLOCK_SIZE);
+    vpnor::partition::Table table(&ctx->backend);
 
     assert(table.capacity() == TOC_PART_SIZE);
 
-    init_vpnor_from_paths(ctx);
-
     rc = mbox_command_dispatch(ctx, get_info, sizeof(get_info));
     assert(rc == MBOX_R_SUCCESS);