vpnor: Consolidate backend and mboxd_pnor_partition_table sources

Change-Id: Ibf66c3a86c2a50e2304fb968f8c912ede84cf719
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/vpnor/backend.cpp b/vpnor/backend.cpp
index 06aed57..006104f 100644
--- a/vpnor/backend.cpp
+++ b/vpnor/backend.cpp
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: Apache-2.0
 // Copyright (C) 2018 IBM Corp.
 
+#include "config.h"
+
 #include <fcntl.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -12,19 +14,21 @@
 #include <algorithm>
 
 extern "C" {
+#include "backend.h"
 #include "common.h"
 #include "lpc.h"
 #include "mboxd.h"
 #include "protocol.h"
+#include "vpnor/backend.h"
 }
 
-#include "config.h"
-
 #include "pnor_partition.hpp"
 #include "pnor_partition_table.hpp"
 #include "xyz/openbmc_project/Common/error.hpp"
 
+#include <cassert>
 #include <exception>
+#include <experimental/filesystem>
 #include <memory>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
@@ -39,6 +43,143 @@
 
 static constexpr uint32_t VPNOR_ERASE_SIZE = 4 * 1024;
 
+void vpnor_default_paths(vpnor_partition_paths* paths)
+{
+    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';
+}
+
+/** @brief Create a virtual PNOR partition table.
+ *
+ *  @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.
+ *
+ *  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.
+ */
+static 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;
+
+    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
+        {
+            phosphor::logging::commit<err::InternalFailure>();
+        }
+        catch (const std::exception& e)
+        {
+            MSG_ERR("Failed to commit InternalFailure: %s\n", e.what());
+        }
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+/** @brief Copy bootloader partition (alongwith TOC) to LPC memory
+ *
+ *  @param[in] backend - The backend context pointer
+ *
+ *  @returns 0 on success, negative error code on failure
+ */
+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.
+    // It assumes the PNOR is 64M, the TOC size is 32K, the erase block is
+    // 4K, the page size is 4K.
+    // It also assumes the TOC is at the 'end of pnor - toc size - 1 page size'
+    // offset, and first looks for the TOC here, before proceeding to move up
+    // page by page looking for the TOC. So it is optimal to place the TOC at
+    // this offset.
+    constexpr size_t eraseSize = 0x1000;
+    constexpr size_t pageSize = 0x1000;
+    constexpr size_t pnorSize = 0x4000000;
+    constexpr size_t tocMaxSize = 0x8000;
+    constexpr size_t tocStart = pnorSize - tocMaxSize - pageSize;
+    constexpr auto blPartitionName = "HBB";
+
+    namespace err = sdbusplus::xyz::openbmc_project::Common::Error;
+    namespace fs = std::experimental::filesystem;
+    namespace vpnor = openpower::virtual_pnor;
+
+    try
+    {
+        vpnor_partition_table 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);
+
+        vtbl.table = &blTable;
+
+        size_t tocOffset = 0;
+
+        const pnor_partition& partition = blTable.partition(blPartitionName);
+        size_t hbbOffset = partition.data.base * eraseSize;
+        uint32_t hbbSize = partition.data.actual;
+
+        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*>(buf);
+        backend_copy(&local, tocOffset, buf8 + tocStart, blTable.capacity());
+        backend_copy(&local, hbbOffset, buf8 + hbbOffset, hbbSize);
+    }
+    catch (err::InternalFailure& e)
+    {
+        phosphor::logging::commit<err::InternalFailure>();
+        return -EIO;
+    }
+    catch (vpnor::ReasonedError& e)
+    {
+        MSG_ERR("%s\n", e.what());
+        phosphor::logging::commit<err::InternalFailure>();
+        return -EIO;
+    }
+
+    return 0;
+}
+
 int vpnor_dev_init(struct backend* backend, void* data)
 {
     vpnor_partition_paths* paths = (vpnor_partition_paths*)data;
@@ -111,7 +252,17 @@
 
 static void vpnor_free(struct backend* backend)
 {
-    vpnor_destroy(backend);
+    struct vpnor_data* priv = (struct vpnor_data*)backend->priv;
+
+    if (priv)
+    {
+        if (priv->vpnor)
+        {
+            delete priv->vpnor->table;
+        }
+        delete priv->vpnor;
+    }
+    delete priv;
 }
 
 /*
@@ -298,7 +449,7 @@
 
     vpnor_partition_paths paths = priv->paths;
 
-    vpnor_destroy(backend);
+    vpnor_free(backend);
 
     rc = vpnor_init(backend, &paths);
     if (rc < 0)