Add support in the isolator to perform write operations on a signature

Signed-off-by: Caleb Palmer <cnpalmer@us.ibm.com>
Change-Id: Ia1f189a8ce7d1d71efc0ae81942151c31d63a4f5
diff --git a/src/isolator/hei_isolation_node.cpp b/src/isolator/hei_isolation_node.cpp
index d156015..099a955 100644
--- a/src/isolator/hei_isolation_node.cpp
+++ b/src/isolator/hei_isolation_node.cpp
@@ -156,6 +156,21 @@
 
 //------------------------------------------------------------------------------
 
+std::pair<OpRuleType_t, RegisterId_t>
+    IsolationNode::getOpRule(OpRuleName_t i_name) const
+{
+    return iv_op_rules.at(i_name);
+}
+
+//------------------------------------------------------------------------------
+
+bool IsolationNode::doesOpExist(OpRuleName_t i_name) const
+{
+    return (0 != iv_op_rules.count(i_name));
+}
+
+//------------------------------------------------------------------------------
+
 std::vector<const IsolationNode*> IsolationNode::cv_isolationStack{};
 
 //------------------------------------------------------------------------------
diff --git a/src/isolator/hei_isolation_node.hpp b/src/isolator/hei_isolation_node.hpp
index 9043f1d..5c5f8ed 100644
--- a/src/isolator/hei_isolation_node.hpp
+++ b/src/isolator/hei_isolation_node.hpp
@@ -187,6 +187,23 @@
     void addOpRule(OpRuleName_t i_opName, OpRuleType_t i_opType,
                    RegisterId_t i_regId);
 
+    /**
+     * @brief Returns a write operation for the isolation node based on the
+     *        input operation name.
+     *
+     * @param i_name The name of the operation.
+     * @return The operation type and reg ID of the operation rule in a pair.
+     */
+    std::pair<OpRuleType_t, RegisterId_t> getOpRule(OpRuleName_t i_name) const;
+
+    /**
+     * @brief Returns whether the write operation rule exists for the node
+     *
+     * @param i_name The name of the operation.
+     * @return True if the operation exists, else false.
+     */
+    bool doesOpExist(OpRuleName_t i_name) const;
+
     /** @return The node ID. */
     NodeId_t getId() const
     {
diff --git a/src/isolator/hei_isolator.cpp b/src/isolator/hei_isolator.cpp
index f431cb3..85995a6 100644
--- a/src/isolator/hei_isolator.cpp
+++ b/src/isolator/hei_isolator.cpp
@@ -73,4 +73,148 @@
     }
 }
 
+//------------------------------------------------------------------------------
+
+#ifdef __HEI_ENABLE_HW_WRITE
+
+bool __atomicOr(Signature i_sig, HardwareRegister::ConstPtr i_hwReg)
+{
+    // Input register is for an atomic OR register used for setting a bit. Get
+    // the bit in the input signature and write that bit to the register.
+    i_hwReg->clearAllBits(i_sig.getChip());
+    i_hwReg->setBit(i_sig.getChip(), i_sig.getBit());
+
+    return i_hwReg->write(i_sig.getChip());
+}
+
+bool __atomicAnd(Signature i_sig, HardwareRegister::ConstPtr i_hwReg)
+{
+    // Input register is for an atomic AND register used for clearing a bit. Get
+    // the bit in the input signature and write that bit to the register.
+    i_hwReg->setAllBits(i_sig.getChip());
+    i_hwReg->clearBit(i_sig.getChip(), i_sig.getBit());
+
+    return i_hwReg->write(i_sig.getChip());
+}
+
+bool __readSetWrite(Signature i_sig, HardwareRegister::ConstPtr i_hwReg)
+{
+    // Perform a read, modify, write to set a bit. Get the bit from the input
+    // signature.
+    if (i_hwReg->read(i_sig.getChip(), true))
+    {
+        HEI_ERR("Failed to read reg ID 0x%04x", i_hwReg->getId());
+        return true;
+    }
+    i_hwReg->setBit(i_sig.getChip(), i_sig.getBit());
+
+    return i_hwReg->write(i_sig.getChip());
+}
+
+bool __readClearWrite(Signature i_sig, HardwareRegister::ConstPtr i_hwReg)
+{
+    // Perform a read, modify, write to clear a bit. Get the bit from the input
+    // signature.
+    if (i_hwReg->read(i_sig.getChip(), true))
+    {
+        HEI_ERR("Failed to read reg ID 0x%04x", i_hwReg->getId());
+        return true;
+    }
+    i_hwReg->clearBit(i_sig.getChip(), i_sig.getBit());
+
+    return i_hwReg->write(i_sig.getChip());
+}
+
+bool Isolator::performWriteOp(OpRuleName_t i_op, Signature i_sig)
+{
+    // Return true on a failure.
+    bool writefail = false;
+
+    // Use the signature to determine the relevant isolation node.
+    IsolationNode::Key nodeKey = {i_sig.getId(), i_sig.getInstance()};
+    IsolationChip::ConstPtr isoChip = iv_isoChips.at(i_sig.getChip().getType());
+    IsolationNode::ConstPtr node = isoChip->getIsolationNode(nodeKey);
+
+    // If the operation name does not exist for the node, print an error
+    // message and return.
+    if (!node->doesOpExist(i_op))
+    {
+        HEI_ERR("Operation rule %d does not exist for node 0x%04x", i_op,
+                node->getId());
+        writeFail = true;
+        return writeFail;
+    }
+
+    // Get the write operation defined in the node.
+    std::pair<OpRuleType_t, RegisterId_t> op = node->getOpRule(i_op);
+
+    // Get the relevant hardware register from the isolation chip. The instance
+    // of the register should match the instance of the signature.
+    HardwareRegister::Key regKey = {op.second, i_sig.getInstance()};
+    HardwareRegister::ConstPtr hwReg = isoChip->getHardwareRegister(regKey);
+
+    // Perform write operation dependent on the operation type.
+    switch (op.first)
+    {
+        case ATOMIC_OR:
+        {
+            if (__atomicOr(i_sig, hwReg))
+            {
+                HEI_ERR("Failed performing ATOMIC_OR write operation %d on "
+                        "node 0x%04x",
+                        i_op, node->getId());
+                writeFail = true;
+            }
+            break;
+        }
+        case ATOMIC_AND:
+        {
+            if (__atomicAnd(i_sig, hwReg))
+            {
+                HEI_ERR("Failed performing ATOMIC_AND write operation %d on "
+                        "node 0x%04x",
+                        i_op, node->getId());
+                writeFail = true;
+            }
+            break;
+        }
+        case READ_SET_WRITE:
+        {
+            if (__readSetWrite(i_sig, hwReg))
+            {
+                HEI_ERR("Failed performing READ_SET_WRITE write operation %d "
+                        "on node 0x%04x",
+                        i_op, node->getId());
+                writeFail = true;
+            }
+            break;
+        }
+        case READ_CLEAR_WRITE:
+        {
+            if (__readClearWrite(i_sig, hwReg))
+            {
+                HEI_ERR("Failed performing READ_CLEAR_WRITE write operation %d "
+                        "on node 0x%04x",
+                        i_op, node->getId());
+                writeFail = true;
+            }
+            break;
+        }
+        default:
+        {
+            HEI_ERR("Invalid operation type %d for op %d on node 0x%04x",
+                    op.first, i_op, node->getId());
+            writeFail = true;
+        }
+    }
+
+    // Flush the affected register from the cache so it is re-read from hardware
+    // next time it is read.
+    hwReg->flush(i_sig.getChip());
+
+    return writeFail;
+}
+
+#endif
+
 } // end namespace libhei
diff --git a/src/isolator/hei_isolator.hpp b/src/isolator/hei_isolator.hpp
index 79a7993..4e9419b 100644
--- a/src/isolator/hei_isolator.hpp
+++ b/src/isolator/hei_isolator.hpp
@@ -76,6 +76,19 @@
     void isolate(const std::vector<Chip>& i_chipList,
                  IsolationData& o_isoData) const;
 
+#ifdef __HEI_ENABLE_HW_WRITE
+    /**
+     * @brief  Performs the given write operation on the input signature. The
+     *         node and bit in the input signature will determine which register
+     *         and bit are to be written by the operation.
+     * @param  i_op  The given write operation rule. See the OpRuleName_t enum
+     *               for supported values.
+     * @param  i_sig The signature to determine what's to be written.
+     * @return True if write operation failed, false if successful.
+     */
+    bool performWriteOp(OpRuleName_t i_op, Signature i_sig);
+#endif
+
 }; // end class Isolator
 
 } // end namespace libhei
diff --git a/src/register/hei_hardware_register.cpp b/src/register/hei_hardware_register.cpp
index 0aa2342..b990fc9 100644
--- a/src/register/hei_hardware_register.cpp
+++ b/src/register/hei_hardware_register.cpp
@@ -101,6 +101,42 @@
 
 //------------------------------------------------------------------------------
 
+void HardwareRegister::setBit(const Chip& i_chip, uint64_t i_pos) const
+{
+    // Get the buffer from the register cache.
+    BitString& bs = accessCache(i_chip);
+    bs.setBit(i_pos);
+}
+
+//------------------------------------------------------------------------------
+
+void HardwareRegister::setAllBits(const Chip& i_chip) const
+{
+    // Get the buffer from the register cache.
+    BitString& bs = accessCache(i_chip);
+    bs.setAll();
+}
+
+//------------------------------------------------------------------------------
+
+void HardwareRegister::clearBit(const Chip& i_chip, uint64_t i_pos) const
+{
+    // Get the buffer from the register cache.
+    BitString& bs = accessCache(i_chip);
+    bs.clearBit(i_pos);
+}
+
+//------------------------------------------------------------------------------
+
+void HardwareRegister::clearAllBits(const Chip& i_chip) const
+{
+    // Get the buffer from the register cache.
+    BitString& bs = accessCache(i_chip);
+    bs.clearAll();
+}
+
+//------------------------------------------------------------------------------
+
 HardwareRegister::Cache HardwareRegister::cv_cache{};
 
 //------------------------------------------------------------------------------
diff --git a/src/register/hei_hardware_register.hpp b/src/register/hei_hardware_register.hpp
index 85d87ff..d75960c 100644
--- a/src/register/hei_hardware_register.hpp
+++ b/src/register/hei_hardware_register.hpp
@@ -188,6 +188,32 @@
 
 #endif // __HEI_ENABLE_HW_WRITE
 
+    /**
+     * @brief Sets the target position to 1 in the cached bit string.
+     * @param i_chip The target chip in which this register belongs.
+     * @param i_pos  The target position.
+     */
+    void setBit(const Chip& i_chip, uint64_t i_pos) const;
+
+    /**
+     * @brief Sets the entire cached bit string to 1s.
+     * @param i_chip The target chip in which this register belongs.
+     */
+    void setAllBits(const Chip& i_chip) const;
+
+    /**
+     * @brief Sets the target position to 0 in the cached bit string.
+     * @param i_chip The target chip in which this register belongs.
+     * @param i_pos  The target position.
+     */
+    void clearBit(const Chip& i_chip, uint64_t i_pos) const;
+
+    /**
+     * @brief Sets the entire cached bit string to 0s.
+     * @param i_chip The target chip in which this register belongs.
+     */
+    void clearAllBits(const Chip& i_chip) const;
+
   protected:
     /**
      * @brief  Provides access to this register's BitString.