tools/pci: refactor PCI bridge

Use polymorphism to handle the differences between Aspeed and Nuvoton
PCI devices.

Add unit tests (now at 100% line coverage for tools/pci.cpp).

Signed-off-by: Benjamin Fair <benjaminfair@google.com>
Change-Id: I43e63ec5eb9fce5fb0fc74e0e69667dd13b7433f
diff --git a/tools/pci.hpp b/tools/pci.hpp
index f421892..7f0cfb7 100644
--- a/tools/pci.hpp
+++ b/tools/pci.hpp
@@ -1,19 +1,30 @@
+/*
+ * Copyright 2020 Google Inc.
+ *
+ * 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.
+ */
+
 #pragma once
 
+#include "data.hpp"
 #include "internal/sys.hpp"
-
-extern "C"
-{
-#include <pciaccess.h>
-} // extern "C"
+#include "pciaccess.hpp"
 
 #include <linux/pci_regs.h>
 
-#include <cstdint>
-#include <memory>
-#include <optional>
-#include <vector>
+#include <stdplus/types.hpp>
 
+// Some versions of the linux/pci_regs.h header don't define this
 #ifndef PCI_STD_NUM_BARS
 #define PCI_STD_NUM_BARS 6
 #endif // !PCI_STD_NUM_BARS
@@ -21,76 +32,113 @@
 namespace host_tool
 {
 
-/* The ASPEED AST2400 & AST2500 have the same VIDDID. */
-
-/**
- * The PciDevice structure is a copy of the information to uniquely identify a
- * PCI device.
- */
-struct PciDevice
-{
-    std::uint16_t vid;
-    std::uint16_t did;
-    std::uint8_t bus;
-    std::uint8_t dev;
-    std::uint8_t func;
-    pciaddr_t bars[PCI_STD_NUM_BARS];
-};
-
-/**
- * The PciFilter structure is a simple mechanism for filtering devices by their
- * vendor and/or device ids.
- */
-struct PciFilter
-{
-    std::uint16_t vid;
-    std::uint16_t did;
-};
-
-class PciUtilInterface
+class PciBridgeIntf
 {
   public:
-    virtual ~PciUtilInterface() = default;
+    virtual ~PciBridgeIntf() = default;
 
-    /**
-     * Get a list of PCI devices from a system.
-     *
-     * @param[in] filter - optional filter for the list.
-     * @return the list of devices.
-     */
-    virtual std::vector<PciDevice>
-        getPciDevices(std::optional<PciFilter> filter = std::nullopt) = 0;
+    virtual void write(const stdplus::span<const std::uint8_t> data) = 0;
+    virtual void configure(const ipmi_flash::PciConfigResponse& config) = 0;
+
+    virtual std::size_t getDataLength() = 0;
 };
 
-class PciUtilImpl : public PciUtilInterface
+class PciAccessBridge : public PciBridgeIntf
 {
   public:
-    static PciUtilImpl& getInstance()
+    virtual ~PciAccessBridge();
+
+    virtual void write(const stdplus::span<const std::uint8_t> data) override;
+    virtual void
+        configure(const ipmi_flash::PciConfigResponse& configResp) override{};
+
+    std::size_t getDataLength() override
     {
-        static PciUtilImpl instance;
-        return instance;
+        return dataLength;
     }
 
-    std::vector<PciDevice>
-        getPciDevices(std::optional<PciFilter> filter = std::nullopt) override;
+  protected:
+    /**
+     * Finds the PCI device matching @a match and saves a reference to it in @a
+     * dev. Also maps the memory region described in BAR number @a bar to
+     * address @a addr,
+     */
+    PciAccessBridge(const struct pci_id_match* match, int bar,
+                    std::size_t dataOffset, std::size_t dataLength,
+                    const PciAccess* pci);
 
-    PciUtilImpl(const PciUtilImpl&) = delete;
-    PciUtilImpl& operator=(const PciUtilImpl&) = delete;
+    struct pci_device* dev = nullptr;
+    std::uint8_t* addr = nullptr;
+    std::size_t size = 0;
 
   private:
-    PciUtilImpl()
+    std::size_t dataOffset;
+    std::size_t dataLength;
+
+  protected:
+    const PciAccess* pci;
+};
+
+class NuvotonPciBridge : public PciAccessBridge
+{
+  public:
+    explicit NuvotonPciBridge(const PciAccess* pci) :
+        PciAccessBridge(&match, bar, dataOffset, dataLength, pci)
+    {}
+
+    ~NuvotonPciBridge()
+    {}
+
+  private:
+    static constexpr std::uint32_t vid = 0x1050;
+    static constexpr std::uint32_t did = 0x0750;
+    static constexpr int bar = 0;
+    static constexpr struct pci_id_match match
     {
-        int ret = pci_system_init();
-        if (ret)
-        {
-            throw internal::errnoException("pci_system_init");
-        }
+        vid, did, PCI_MATCH_ANY, PCI_MATCH_ANY
+    };
+
+    static constexpr pciaddr_t bridge = 0x04;
+    static constexpr std::uint8_t bridgeEnabled = 0x02;
+
+    static constexpr std::size_t dataOffset = 0x0;
+    static constexpr std::size_t dataLength = 0x4000;
+};
+
+class AspeedPciBridge : public PciAccessBridge
+{
+  public:
+    explicit AspeedPciBridge(const PciAccess* pci) :
+        PciAccessBridge(&match, bar, dataOffset, dataLength, pci)
+    {
+        enableBridge();
     }
 
-    ~PciUtilImpl()
+    ~AspeedPciBridge()
     {
-        pci_system_cleanup();
+        disableBridge();
     }
+
+    void configure(const ipmi_flash::PciConfigResponse& configResp) override;
+
+  private:
+    static constexpr std::uint32_t vid = 0x1a03;
+    static constexpr std::uint32_t did = 0x2000;
+    static constexpr int bar = 1;
+    static constexpr struct pci_id_match match
+    {
+        vid, did, PCI_MATCH_ANY, PCI_MATCH_ANY
+    };
+
+    static constexpr std::size_t config = 0x0f000;
+    static constexpr std::size_t bridge = 0x0f004;
+    static constexpr std::uint32_t bridgeEnabled = 0x1;
+
+    static constexpr std::size_t dataOffset = 0x10000;
+    static constexpr std::size_t dataLength = 0x10000;
+
+    void enableBridge();
+    void disableBridge();
 };
 
 } // namespace host_tool