tools: add io interface for mmio, etc

Add an IO interface for host-side memory access.  This is done as an
interface to allow platform specific implementations.

Change-Id: Id8e35d5beb21f7db544e27d6592bbeac58748dcb
Signed-off-by: Patrick Venture <venture@google.com>
diff --git a/test/io_mock.hpp b/test/io_mock.hpp
new file mode 100644
index 0000000..2cff9a8
--- /dev/null
+++ b/test/io_mock.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "io.hpp"
+
+#include <gmock/gmock.h>
+
+namespace host_tool
+{
+
+class HostIoInterfaceMock : public HostIoInterface
+{
+  public:
+    ~HostIoInterfaceMock() = default;
+
+    MOCK_METHOD3(write,
+                 bool(const std::size_t, const std::size_t, const void* const));
+};
+
+} // namespace host_tool
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 61d2907..05daf75 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -14,4 +14,5 @@
 	blob_handler.cpp \
 	ipmi_handler.cpp \
 	crc.cpp \
+	io.cpp \
 	../internal/sys.cpp
diff --git a/tools/io.cpp b/tools/io.cpp
new file mode 100644
index 0000000..e25216e
--- /dev/null
+++ b/tools/io.cpp
@@ -0,0 +1,47 @@
+#include "io.hpp"
+
+#include "internal/sys.hpp"
+
+#include <fcntl.h>
+
+#include <cstdint>
+#include <cstring>
+#include <string>
+
+namespace host_tool
+{
+
+const std::string DevMemDevice::devMemPath = "/dev/mem";
+
+bool DevMemDevice::write(const std::size_t offset, const std::size_t length,
+                         const void* const source)
+{
+    if (!opened)
+    {
+        devMemFd = sys->open(devMemPath.c_str(), O_RDWR);
+        if (devMemFd < 0)
+        {
+            return false;
+        }
+
+        opened = true;
+    }
+
+    // addr, length, prot, flags, fd, offset
+    devMemMapped =
+        sys->mmap(0, length, PROT_WRITE, MAP_SHARED, devMemFd, offset);
+    if (devMemMapped == MAP_FAILED)
+    {
+        return false; /* but leave the file open. */
+    }
+
+    /* Copy the bytes. */
+    std::memcpy(devMemMapped, source, length);
+
+    /* Close the map between writes for now. */
+    sys->munmap(devMemMapped, length);
+
+    return true;
+}
+
+} // namespace host_tool
diff --git a/tools/io.hpp b/tools/io.hpp
new file mode 100644
index 0000000..b8da0ef
--- /dev/null
+++ b/tools/io.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include "internal/sys.hpp"
+
+#include <cstdint>
+#include <string>
+
+namespace host_tool
+{
+
+class HostIoInterface
+{
+  public:
+    virtual ~HostIoInterface() = default;
+
+    /**
+     * Attempt to write bytes from source to offset into the host memory device.
+     *
+     * @param[in] offset - offset into the host memory device.
+     * @param[in] length - the number of bytes to copy from source.
+     * @param[in] source - the souce of the bytes to copy to the memory device.
+     * @return true on success, false on failure (such as unable to initialize
+     * device).
+     */
+    virtual bool write(const std::size_t offset, const std::size_t length,
+                       const void* const source) = 0;
+};
+
+class DevMemDevice : public HostIoInterface
+{
+  public:
+    explicit DevMemDevice(const internal::Sys* sys = &internal::sys_impl) :
+        sys(sys)
+    {
+    }
+
+    /* On destruction, close /dev/mem if it was open. */
+    ~DevMemDevice()
+    {
+        if (opened)
+        {
+            sys->close(devMemFd);
+        }
+    }
+
+    /* Don't allow copying or assignment, only moving. */
+    DevMemDevice(const DevMemDevice&) = delete;
+    DevMemDevice& operator=(const DevMemDevice&) = delete;
+    DevMemDevice(DevMemDevice&&) = default;
+    DevMemDevice& operator=(DevMemDevice&&) = default;
+
+    bool write(const std::size_t offset, const std::size_t length,
+               const void* const source) override;
+
+  private:
+    static const std::string devMemPath;
+    bool opened = false;
+    int devMemFd = -1;
+    void* devMemMapped = nullptr;
+    const internal::Sys* sys;
+};
+
+} // namespace host_tool