summaryrefslogtreecommitdiffstats
path: root/vpnor/mboxd_flash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vpnor/mboxd_flash.cpp')
-rw-r--r--vpnor/mboxd_flash.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/vpnor/mboxd_flash.cpp b/vpnor/mboxd_flash.cpp
new file mode 100644
index 0000000..ffe75c6
--- /dev/null
+++ b/vpnor/mboxd_flash.cpp
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <algorithm>
+
+extern "C" {
+#include "common.h"
+}
+
+#include "config.h"
+#include "mboxd_flash.h"
+#include "mboxd_pnor_partition_table.h"
+#include "pnor_partition.hpp"
+#include "pnor_partition_table.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+
+#include <memory>
+#include <string>
+#include <exception>
+#include <stdexcept>
+
+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 init_flash_dev(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 free_flash_dev(struct mbox_context* context)
+{
+ // No-op
+}
+
+int set_flash_bytemap(struct mbox_context* context, uint32_t offset,
+ uint32_t count, uint8_t val)
+{
+ // No-op
+ return 0;
+}
+
+int erase_flash(struct mbox_context* context, uint32_t offset, uint32_t count)
+{
+ // No-op
+ return 0;
+}
+
+/*
+ * copy_flash() - 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. copy_flash will copy at most 'size' bytes, but it may
+ * copy less.
+ */
+int64_t copy_flash(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 -MBOX_R_SYSTEM_ERROR;
+ }
+
+ 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 write_flash() 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 = -MBOX_R_SYSTEM_ERROR;
+ }
+ return rc;
+}
+
+/*
+ * write_flash() - 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 write_flash(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 -MBOX_R_SYSTEM_ERROR;
+ }
+
+ vpnor::partition::Table* table = context->vpnor->table;
+
+ try
+ {
+ const struct pnor_partition& part = table->partition(offset);
+ if (part.data.user.data[1] & PARTITION_READONLY)
+ {
+ /* FIXME: This should be done on CREATE_WRITE_WINDOW, not here */
+ return -MBOX_R_WRITE_ERROR;
+ }
+
+ 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)
+ {
+ /* Paper over the fact that the write isn't persistent */
+ MSG_INFO("Dropping %d bytes host wrote to unmapped offset 0x%" PRIx32
+ "\n",
+ count, offset);
+ return 0;
+ }
+ catch (const vpnor::OutOfBoundsOffset& e)
+ {
+ MSG_ERR("%s\n", e.what());
+ return -MBOX_R_PARAM_ERROR;
+ }
+ catch (const std::exception& e)
+ {
+ MSG_ERR("%s\n", e.what());
+ phosphor::logging::commit<err::InternalFailure>();
+ return -MBOX_R_SYSTEM_ERROR;
+ }
+ return 0;
+}
OpenPOWER on IntegriCloud