Add overflow flag checking

This is to ensure that we check for and flag buffer overflow flag from
the BIOS.

The logic should be:

```
1. BIOS_switch ^ BMC_switch == 0 → No overflow incident
  a. CONTINUE
2. BIOS_switch ^ BMC_switch == 1 → an unlogged overflow incident has occurred
  b. Log the overflow incident
  c. Toggle the BMC overflow flag
```

Tested: Added unit test

Signed-off-by: Brandon Kim <brandonkim@google.com>
Change-Id: I25c50a8de93900480413389d7d2a89b9be4b5643
diff --git a/include/buffer.hpp b/include/buffer.hpp
index 06e282f..95154de 100644
--- a/include/buffer.hpp
+++ b/include/buffer.hpp
@@ -109,6 +109,11 @@
     virtual std::vector<uint8_t> readUeLogFromReservedRegion() = 0;
 
     /**
+     * Check for overflow and ackknolwedge if not acked yet
+     */
+    virtual bool checkForOverflowAndAcknowledge() = 0;
+
+    /**
      * Read the buffer header from shared buffer
      */
     virtual void readBufferHeader() = 0;
@@ -191,6 +196,7 @@
                     uint16_t ueRegionSize,
                     const std::array<uint32_t, 4>& magicNumber) override;
     std::vector<uint8_t> readUeLogFromReservedRegion() override;
+    bool checkForOverflowAndAcknowledge() override;
     void readBufferHeader() override;
     struct CircularBufferHeader getCachedBufferHeader() const override;
     void updateReadPtr(const uint32_t newReadPtr) override;
diff --git a/src/buffer.cpp b/src/buffer.cpp
index 0254cac..3af1125 100644
--- a/src/buffer.cpp
+++ b/src/buffer.cpp
@@ -269,6 +269,36 @@
                     currentUeRegionSize, ueLogData.size()));
 }
 
+bool BufferImpl::checkForOverflowAndAcknowledge()
+{
+    // Ensure cachedBufferHeader is up-to-date
+    readBufferHeader();
+
+    uint32_t biosSideFlags =
+        boost::endian::little_to_native(cachedBufferHeader.biosFlags);
+    uint32_t bmcSideFlags =
+        boost::endian::little_to_native(cachedBufferHeader.bmcFlags);
+
+    // Design: (BIOS_switch ^ BMC_switch) & BIT1 == BIT1 -> unlogged overflow
+    // This means if the overflow bit differs, there's an
+    // unacknowledged overflow.
+    if ((biosSideFlags ^ bmcSideFlags) &
+        static_cast<uint32_t>(BufferFlags::overflow))
+    {
+        // Overflow incident has occurred and BMC has not acknowledged it.
+        // Toggle BMC's view of the overflow flag to acknowledge.
+        uint32_t newBmcFlags =
+            bmcSideFlags ^ static_cast<uint32_t>(BufferFlags::overflow);
+        updateBmcFlags(newBmcFlags);
+
+        // Overflow was detected and acknowledged
+        return true;
+    }
+
+    // No new overflow incident or already acknowledged
+    return false;
+}
+
 EntryPair BufferImpl::readEntry()
 {
     struct QueueEntryHeader entryHeader = readEntryHeader();
diff --git a/src/main.cpp b/src/main.cpp
index 4465185..eb15677 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -79,6 +79,13 @@
             bufferInterface->updateBmcFlags(newBmcFlags);
         }
 
+        if (bufferInterface->checkForOverflowAndAcknowledge())
+        {
+            stdplus::print(
+                stdout,
+                "[WARN] Buffer overflow had occured and has been acked\n");
+        }
+
         std::vector<EntryPair> entryPairs = bufferInterface->readErrorLogs();
         for (const auto& [entryHeader, entry] : entryPairs)
         {
diff --git a/test/buffer_test.cpp b/test/buffer_test.cpp
index 675be79..d6f355a 100644
--- a/test/buffer_test.cpp
+++ b/test/buffer_test.cpp
@@ -420,6 +420,60 @@
         std::runtime_error);
 }
 
+TEST_F(BufferTest, CheckOverflow_NotPresentDueToFlags)
+{
+    struct CircularBufferHeader header = testInitializationHeader;
+    // Flags are the same, so no new overflow
+    header.biosFlags = boost::endian::native_to_little<uint32_t>(
+        static_cast<uint32_t>(BufferFlags::overflow));
+    header.bmcFlags = boost::endian::native_to_little<uint32_t>(
+        static_cast<uint32_t>(BufferFlags::overflow));
+
+    uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
+    std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
+    EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
+        .WillOnce(Return(headerBytes));
+
+    bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
+    ASSERT_FALSE(overflowDetected);
+}
+
+TEST_F(BufferTest, CheckOverflow_PresentAndAcknowledged)
+{
+    struct CircularBufferHeader header = testInitializationHeader;
+    header.biosFlags = boost::endian::native_to_little<uint32_t>(
+        static_cast<uint32_t>(BufferFlags::overflow));
+    header.bmcFlags =
+        boost::endian::native_to_little<uint32_t>(0); // BIOS set, BMC not yet
+
+    uint8_t* headerPtr = reinterpret_cast<uint8_t*>(&header);
+    std::vector<uint8_t> headerBytes(headerPtr, headerPtr + bufferHeaderSize);
+    EXPECT_CALL(*dataInterfaceMockPtr, read(0, bufferHeaderSize))
+        .WillOnce(Return(headerBytes));
+
+    uint32_t expectedNewBmcFlags =
+        static_cast<uint32_t>(BufferFlags::overflow); // BMC toggles its bit
+    little_uint32_t littleExpectedNewBmcFlags =
+        boost::endian::native_to_little(expectedNewBmcFlags);
+    uint8_t* flagPtr = reinterpret_cast<uint8_t*>(&littleExpectedNewBmcFlags);
+    std::vector<uint8_t> expectedFlagWrite(flagPtr,
+                                           flagPtr + sizeof(little_uint32_t));
+    constexpr uint8_t bmcFlagsOffset =
+        offsetof(struct CircularBufferHeader, bmcFlags);
+
+    EXPECT_CALL(*dataInterfaceMockPtr,
+                write(bmcFlagsOffset, ElementsAreArray(expectedFlagWrite)))
+        .WillOnce(Return(sizeof(little_uint32_t)));
+
+    bool overflowDetected = bufferImpl->checkForOverflowAndAcknowledge();
+    ASSERT_TRUE(overflowDetected);
+
+    struct CircularBufferHeader updatedCachedHeader =
+        bufferImpl->getCachedBufferHeader();
+    EXPECT_EQ(boost::endian::little_to_native(updatedCachedHeader.bmcFlags),
+              expectedNewBmcFlags);
+}
+
 class BufferWraparoundReadTest : public BufferTest
 {
   protected: