pci: implement sending firmware image over p2a

Implement sending firmware image over p2a via the aspeed-p2a-ctrl
driver.

Test configuration:
# Image is static, uses the PCI bridge, and requires the ASPEED
# PCI-to-AHB hardware implementation.
EXTRA_OECONF_append_quanta-q71l = " --enable-static-layout"
EXTRA_OECONF_append_quanta-q71l = " --enable-pci-bridge"
EXTRA_OECONF_append_quanta-q71l = " --enable-aspeed-p2a"
EXTRA_OECONF_append_quanta-q71l = " MAPPED_ADDRESS=0x47FF0000"

Tested: Verified via md5sum the image-bmc file sent from the host via
this tool matches the hash of /run/initramfs/bmc-image.  This code can
be used to send any file down, but was only tested with a "static"
layout build.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: I430bc7444f06f4f93a63e3bf2646bd195eaa2e52
diff --git a/pci_handler.cpp b/pci_handler.cpp
index 51a2ca5..56016c7 100644
--- a/pci_handler.cpp
+++ b/pci_handler.cpp
@@ -98,8 +98,10 @@
 
 std::vector<std::uint8_t> PciDataHandler::copyFrom(std::uint32_t length)
 {
-    /* TODO: implement this. */
-    return {};
+    std::vector<std::uint8_t> results(length);
+    std::memcpy(results.data(), mapped, length);
+
+    return results;
 }
 
 bool PciDataHandler::writeMeta(const std::vector<std::uint8_t>& configuration)
diff --git a/tools/io.cpp b/tools/io.cpp
index cc9e7d1..420dd94 100644
--- a/tools/io.cpp
+++ b/tools/io.cpp
@@ -16,15 +16,10 @@
 bool DevMemDevice::read(const std::size_t offset, const std::size_t length,
                         void* const destination)
 {
-    if (!opened)
+    devMemFd = sys->open(devMemPath.c_str(), O_RDONLY);
+    if (devMemFd < 0)
     {
-        devMemFd = sys->open(devMemPath.c_str(), O_RDWR);
-        if (devMemFd < 0)
-        {
-            return false;
-        }
-
-        opened = true;
+        return false;
     }
 
     /* Map based on aligned addresses - behind the scenes. */
@@ -37,7 +32,10 @@
                              alignedOffset);
     if (devMemMapped == MAP_FAILED)
     {
-        return false; /* but leave the file open. */
+        std::fprintf(stderr, "Failed to mmap at offset: 0x%lx, length: %lu\n",
+                     offset, length);
+        sys->close(devMemFd);
+        return false;
     }
 
     void* alignedSource =
@@ -48,6 +46,7 @@
 
     /* Close the map between reads for now. */
     sys->munmap(devMemMapped, length);
+    sys->close(devMemFd);
 
     return true;
 }
@@ -55,15 +54,11 @@
 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)
     {
-        devMemFd = sys->open(devMemPath.c_str(), O_RDWR);
-        if (devMemFd < 0)
-        {
-            return false;
-        }
-
-        opened = true;
+        std::fprintf(stderr, "Failed to open /dev/mem for writing\n");
+        return false;
     }
 
     /* Map based on aligned addresses - behind the scenes. */
@@ -77,7 +72,10 @@
 
     if (devMemMapped == MAP_FAILED)
     {
-        return false; /* but leave the file open. */
+        std::fprintf(stderr, "Failed to mmap at offset: 0x%lx, length: %lu\n",
+                     offset, length);
+        sys->close(devMemFd);
+        return false;
     }
 
     void* alignedDestination =
@@ -88,6 +86,7 @@
 
     /* Close the map between writes for now. */
     sys->munmap(devMemMapped, length);
+    sys->close(devMemFd);
 
     return true;
 }
diff --git a/tools/io.hpp b/tools/io.hpp
index 0712694..44cd2cb 100644
--- a/tools/io.hpp
+++ b/tools/io.hpp
@@ -47,14 +47,7 @@
     {
     }
 
-    /* On destruction, close /dev/mem if it was open. */
-    ~DevMemDevice()
-    {
-        if (opened)
-        {
-            sys->close(devMemFd);
-        }
-    }
+    ~DevMemDevice() = default;
 
     /* Don't allow copying or assignment, only moving. */
     DevMemDevice(const DevMemDevice&) = delete;
@@ -70,7 +63,6 @@
 
   private:
     static const std::string devMemPath;
-    bool opened = false;
     int devMemFd = -1;
     void* devMemMapped = nullptr;
     const internal::Sys* sys;
diff --git a/tools/p2a.cpp b/tools/p2a.cpp
index 5dcf6c9..dd05ccd 100644
--- a/tools/p2a.cpp
+++ b/tools/p2a.cpp
@@ -16,10 +16,12 @@
 
 #include "p2a.hpp"
 
+#include "firmware_handler.hpp"
 #include "pci.hpp"
 #include "pci_handler.hpp"
 
 #include <cstring>
+#include <ipmiblob/blob_errors.hpp>
 
 namespace host_tool
 {
@@ -31,6 +33,7 @@
     PciUtilImpl pci;
     PciFilter filter;
     bool found = false;
+    pciaddr_t bar1;
 
     filter.vid = aspeedVendorId;
     filter.did = aspeedDeviceId;
@@ -42,13 +45,14 @@
         std::fprintf(stderr, "[0x%x 0x%x] ", d.vid, d.did);
 
         /* Verify it's a memory-based bar -- we want bar1. */
-        pciaddr_t bar1 = d.bars[1];
+        bar1 = d.bars[1];
         if ((bar1 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
         {
             /* We want it to not be IO-based access. */
             continue;
         }
 
+        /* For now capture the entire device even if we're only using BAR1 */
         result = d;
         found = true;
         break;
@@ -65,7 +69,7 @@
      * the bridge enabled.
      */
     std::uint32_t value;
-    if (!io->read(result.bars[1] | aspeedP2aConfig, sizeof(value), &value))
+    if (!io->read(bar1 | aspeedP2aConfig, sizeof(value), &value))
     {
         if (0 == (value & p2ABridgeEnabled))
         {
@@ -89,16 +93,73 @@
     std::memcpy(&pciResp, stat.metadata.data(), sizeof(pciResp));
     std::fprintf(stderr, "Received address: 0x%x\n", pciResp.address);
 
-#if 0
     /* Configure the mmio to point there. */
-    if (!io->IoWrite(bar | kAspeedP2aBridge, sizeof(phys), &phys)) {
+    if (!io->write(bar1 | aspeedP2aBridge, sizeof(pciResp.address),
+                   &pciResp.address))
+    {
         // Failed to set it up, so fall back.
         std::fprintf(stderr, "Failed to update the bridge address\n");
         return false;
     }
-#endif
 
-    return false;
+    /* For data blocks in 64kb, stage data, and send blob write command. */
+    int inputFd = sys->open(input.c_str(), 0);
+    if (inputFd < 0)
+    {
+        return false;
+    }
+
+    int bytesRead = 0;
+    std::uint32_t offset = 0;
+    const std::uint32_t p2aLength = aspeedP2aOffset;
+
+    auto readBuffer = std::make_unique<std::uint8_t[]>(p2aLength);
+    if (nullptr == readBuffer)
+    {
+        std::fprintf(stderr, "Unable to allocate memory for read buffer.\n");
+        return false;
+    }
+
+    try
+    {
+        do
+        {
+            bytesRead = sys->read(inputFd, readBuffer.get(), p2aLength);
+            if (bytesRead > 0)
+            {
+                /* TODO: Will likely need to store an rv somewhere to know when
+                 * we're exiting from failure.
+                 */
+                if (!io->write(bar1 | aspeedP2aOffset, bytesRead,
+                               readBuffer.get()))
+                {
+                    std::fprintf(stderr,
+                                 "Failed to write to region in memory!\n");
+                    break;
+                }
+
+                /* Ok, so the data is staged, now send the blob write with the
+                 * details.
+                 */
+                struct blobs::ExtChunkHdr chunk;
+                chunk.length = bytesRead;
+                std::vector<std::uint8_t> chunkBytes(sizeof(chunk));
+                std::memcpy(chunkBytes.data(), &chunk, sizeof(chunk));
+
+                /* This doesn't return anything on success. */
+                blob->writeBytes(session, offset, chunkBytes);
+                offset += bytesRead;
+            }
+        } while (bytesRead > 0);
+    }
+    catch (const ipmiblob::BlobException& b)
+    {
+        sys->close(inputFd);
+        return false;
+    }
+
+    sys->close(inputFd);
+    return true;
 }
 
 } // namespace host_tool
diff --git a/tools/p2a.hpp b/tools/p2a.hpp
index f229449..eda8f61 100644
--- a/tools/p2a.hpp
+++ b/tools/p2a.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "interface.hpp"
+#include "internal/sys.hpp"
 #include "io.hpp"
 #include "pci.hpp"
 
@@ -9,6 +10,7 @@
 
 constexpr std::uint16_t aspeedVendorId = 0x1a03;
 constexpr std::uint16_t aspeedDeviceId = 0x2000;
+constexpr std::size_t aspeedP2aOffset = 0x10000;
 constexpr std::size_t aspeedP2aConfig = 0x0f000;
 constexpr std::size_t aspeedP2aBridge = 0x0f004;
 constexpr std::uint32_t p2ABridgeEnabled = 0x1;
@@ -20,9 +22,10 @@
 {
   public:
     P2aDataHandler(ipmiblob::BlobInterface* blob, HostIoInterface* io,
-                   PciUtilInterface* pci) :
+                   PciUtilInterface* pci,
+                   const internal::Sys* sys = &internal::sys_impl) :
         blob(blob),
-        io(io), pci(pci)
+        io(io), pci(pci), sys(sys)
     {
     }
 
@@ -36,6 +39,7 @@
     ipmiblob::BlobInterface* blob;
     HostIoInterface* io;
     PciUtilInterface* pci;
+    const internal::Sys* sys;
 };
 
 } // namespace host_tool