psutil: Add PSU Event Log Reporting Methods

This commit introduces new methods in the Updater class to log errors
and create Platform Event Logs for PSU failures and I2C-related
issues. The following methods are added:

- createServiceableEventLog:
  Creates a serviceable Platform Event Log using
  xyz.openbmc_project.Logging.Create. Takes an error name, severity, and
  additional data as parameters. Retrieves the logging service and calls
  the D-Bus method to create the log.

- getI2CAdditionalData:
  Retrieves I2C-related callout data, including I2C bus ID, address, and
  error number. Formats the ID and address as hexadecimal strings and
  returns them as a map.

- callOutI2CEventLog:
  Reports a Event Log for I2C failures. Collects PSU inventory path,
  priority, and I2C-specific callout data. Merges any additional
  provided data and creates a Event Log.

- callOutPsuEventLog:
  Reports a Event Log for general PSU failures. Includes PSU inventory
  callout information and priority.

- callOutSWEventLog:
  Reports Event Log for software-related PSU file issues.Logs errors
  using predefined PSU firmware file issue messages. These changes
  improve fault logging and troubleshooting capabilities in PSU
  management by ensuring proper logging and event recording.

- callOutGoodEventLog:
  Reports a successful PSU firmware update Event Log along with the
  firmware level.

- Added accessor functions to provide control over Event logging,
  allowing:
  - Enabling/disabling Event logging at runtime.
  - Tracking if Event Log  has been logged in the current session.

  Accessor functions:
    - enableEventLogging()
    - disableEventLogging()
    - isEventLogEnabled()
    - enableEventLoggedThisSession()
    - isEventLoggedThisSession()

Test:
 Verified each function correctly generates Event Log  in openBmc. The
 test was conducted by writing a standalone test program that:
 1 - Calls createServiceableEventLog() with various error names,
 severity levels, and additionalData then check the generated Event Log.
 2 - Calls getI2CAdditionalData() and verifies the returned data
 contains the correct I2C bus ID, Address and error number.
 3 - Calls callOutI2CEventLog() with simulated I2C error and checks the
 generated Event Log.
 4 - Calls callOutPsuEventLog() and callOutSWEventLog() with different
 input data and verifies the correct error messages are logged.

Change-Id: Id52b3e1c0b2a3b09ae3ac5d93bc53e02d335d6c7
Signed-off-by: Faisal Awada <faisal@us.ibm.com>
diff --git a/tools/power-utils/aei_updater.cpp b/tools/power-utils/aei_updater.cpp
index 27da440..a98a1d8 100644
--- a/tools/power-utils/aei_updater.cpp
+++ b/tools/power-utils/aei_updater.cpp
@@ -22,10 +22,12 @@
 #include "types.hpp"
 #include "updater.hpp"
 #include "utility.hpp"
+#include "utils.hpp"
 
 #include <phosphor-logging/lg2.hpp>
 
 #include <fstream>
+#include <system_error>
 
 namespace aeiUpdater
 {
@@ -72,8 +74,15 @@
 int AeiUpdater::doUpdate()
 {
     i2cInterface = Updater::getI2C();
+    enableEventLogging();
     if (i2cInterface == nullptr)
     {
+        // Report serviceable error
+        std::map<std::string, std::string> additionalData = {
+            {"I2C_INTERFACE", "I2C interface is null pointer."}};
+        // Callout PSU & I2C
+        callOutI2CEventLog(additionalData);
+
         throw std::runtime_error("I2C interface error");
     }
     if (!getFirmwarePath() || !isFirmwareFileValid())
@@ -83,24 +92,22 @@
     bool downloadFwFailed = false; // Download Firmware status
     int retryProcessTwo(0);
     int retryProcessOne(0);
-    int serviceableEvent(0);
+    disableEventLogging();
     while ((retryProcessTwo < MAX_RETRIES) && (retryProcessOne < MAX_RETRIES))
     {
-        retryProcessTwo++;
         // Write AEI PSU ISP key
         if (!writeIspKey())
         {
-            serviceableEvent++;
-            if (serviceableEvent == MAX_RETRIES)
-            {
-                createServiceableError(
-                    "createServiceableError: ISP key failed");
-            }
             lg2::error("Failed to set ISP Key");
             downloadFwFailed = true; // Download Firmware status
-            continue;
+            break;
         }
 
+        if (retryProcessTwo == (MAX_RETRIES - 1))
+        {
+            enableEventLogging();
+        }
+        retryProcessTwo++;
         while (retryProcessOne < MAX_RETRIES)
         {
             downloadFwFailed = false; // Download Firmware status
@@ -117,7 +124,7 @@
             // Reset ISP status
             if (writeIspStatusReset())
             {
-                // Start PSU frimware download.
+                // Start PSU firmware download.
                 if (downloadPsuFirmware())
                 {
                     if (!verifyDownloadFWStatus())
@@ -128,11 +135,16 @@
                 }
                 else
                 {
+                    // One of the block write commands failed, retry download
+                    // procedure one time starting with re-writing initial ISP
+                    // mode. If it fails again, log serviceable error.
                     if (retryProcessOne == MAX_RETRIES)
                     {
-                        // no more retries report serviceable error
-                        createServiceableError(
-                            "serviceableError: Download firmware failed");
+                        // Callout PSU failed to update FW
+                        std::map<std::string, std::string> additionalData = {
+                            {"UPDATE_FAILED", "Download firmware failed"}};
+
+                        callOutPsuEventLog(additionalData);
                         ispReboot(); // Try to set PSU to normal mode
                     }
                     downloadFwFailed = true;
@@ -148,6 +160,7 @@
             }
 
             ispReboot();
+
             if (ispReadRebootStatus() && !downloadFwFailed)
             {
                 // Download completed successful
@@ -156,6 +169,8 @@
             }
             else
             {
+                // Retry the whole download process starting with the key and
+                // if fails again then report event log
                 if ((retryProcessOne < (MAX_RETRIES - 1)) &&
                     (retryProcessTwo < (MAX_RETRIES - 1)))
                 {
@@ -169,6 +184,10 @@
     {
         return 1;
     }
+    enableEventLogging();
+    bindUnbind(true);
+    updater::internal::delay(100);
+    callOutGoodEventLog();
     return 0; // Update successful
 }
 
@@ -177,24 +196,46 @@
     // ISP Key to unlock programming mode ( ASCII for "artY").
     constexpr std::array<uint8_t, 4> unlockData = {0x61, 0x72, 0x74,
                                                    0x59}; // ISP Key "artY"
-    try
+    for (int retry = 0; retry < MAX_RETRIES; ++retry)
     {
-        // Send ISP Key to unlock device for firmware update
-        i2cInterface->write(KEY_REGISTER, unlockData.size(), unlockData.data());
-        return true;
+        try
+        {
+            // Send ISP Key to unlock device for firmware update
+            i2cInterface->write(KEY_REGISTER, unlockData.size(),
+                                unlockData.data());
+            disableEventLogging();
+            return true;
+        }
+        catch (const i2c::I2CException& e)
+        {
+            // Log failure if I2C write fails.
+            lg2::error("I2C write failed: {ERROR}", "ERROR", e);
+            std::map<std::string, std::string> additionalData = {
+                {"I2C_ISP_KEY", "ISP key failed due to I2C exception"}};
+            callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+            enableEventLogging(); // enable event logging if fail again call out
+                                  // PSU & I2C
+        }
+
+        catch (const std::exception& e)
+        {
+            lg2::error("Exception write failed: {ERROR}", "ERROR", e);
+            std::map<std::string, std::string> additionalData = {
+                {"ISP_KEY", "ISP key failed due to exception"},
+                {"EXCEPTION", e.what()}};
+            callOutPsuEventLog(additionalData);
+            enableEventLogging(); // enable Event Logging if fail again call out
+                                  // PSU
+        }
     }
-    catch (const std::exception& e)
-    {
-        // Log failure if I2C write fails.
-        lg2::error("I2C write failed: {ERROR}", "ERROR", e);
-        return false;
-    }
+    return false;
 }
 
 bool AeiUpdater::writeIspMode()
 {
     // Attempt to set device in ISP mode with retries.
     uint8_t ispStatus = 0x0;
+    uint8_t exceptionCount = 0;
     for (int retry = 0; retry < MAX_RETRIES; ++retry)
     {
         try
@@ -209,17 +250,57 @@
             if (ispStatus & B_ISP_MODE)
             {
                 lg2::info("Set ISP Mode");
+                disableEventLogging();
                 return true;
             }
+            enableEventLogging();
+        }
+        catch (const i2c::I2CException& e)
+        {
+            exceptionCount++;
+            // Log I2C error with each retry attempt.
+            lg2::error("I2C exception during ISP mode write/read: {ERROR}",
+                       "ERROR", e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"I2C_FIRMWARE_STATUS",
+                     "Download firmware failed during writeIspMode due to I2C exception"}};
+                // Callout PSU & I2C
+                callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+                return false; // Failed to set ISP Mode
+            }
         }
         catch (const std::exception& e)
         {
-            // Log I2C error with each retry attempt.
-            lg2::error("I2C error during ISP mode write/read: {ERROR}", "ERROR",
+            exceptionCount++;
+            // Log error with each retry attempt.
+            lg2::error("Exception during ISP mode write/read: {ERROR}", "ERROR",
                        e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"FIRMWARE_STATUS",
+                     "Download firmware failed during writeIspMode due to exception"},
+                    {"EXCEPTION", e.what()}};
+                // Callout PSU
+                callOutPsuEventLog(additionalData);
+                return false; // Failed to set ISP Mode
+            }
         }
     }
-    createServiceableError("createServiceableError: ISP Status failed");
+
+    if (exceptionCount != MAX_RETRIES)
+    {
+        // Callout PSU
+        std::map<std::string, std::string> additionalData = {
+            {"FIRMWARE_STATUS",
+             "Download firmware failed during writeIspMode"}};
+        callOutPsuEventLog(additionalData);
+    }
+
     lg2::error("Failed to set ISP Mode");
     return false; // Failed to set ISP Mode after retries
 }
@@ -228,6 +309,7 @@
 {
     // Reset ISP status register before firmware download.
     uint8_t ispStatus = 0;
+    uint8_t exceptionCount = 0;
     for (int retry = 0; retry < MAX_RETRIES; retry++)
     {
         try
@@ -236,13 +318,42 @@
                                 CMD_RESET_SEQ); // Start reset sequence.
             retry = MAX_RETRIES;
         }
-        catch (const std::exception& e)
+        catch (const i2c::I2CException& e)
         {
+            exceptionCount++;
             // Log any errors encountered during reset sequence.
             lg2::error("I2C Write ISP reset failed: {ERROR}", "ERROR", e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"I2C_ISP_RESET", "I2C exception during ISP status reset"}};
+                // Callout PSU & I2C
+                callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+                ispReboot();
+                return false;
+            }
+        }
+        catch (const std::exception& e)
+        {
+            exceptionCount++;
+            // Log any errors encountered during reset sequence.
+            lg2::error("Write ISP reset failed: {ERROR}", "ERROR", e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"ISP_RESET", "Exception during ISP status reset"},
+                    {"EXCEPTION", e.what()}};
+                // Callout PSU
+                callOutPsuEventLog(additionalData);
+                ispReboot();
+                return false;
+            }
         }
     }
 
+    exceptionCount = 0;
     for (int retry = 0; retry < MAX_RETRIES; ++retry)
     {
         try
@@ -250,22 +361,57 @@
             i2cInterface->read(STATUS_REGISTER, ispStatus);
             if (ispStatus == B_ISP_MODE)
             {
-                lg2::info("Read/Write ISP reset");
+                lg2::info("write/read ISP reset");
+                disableEventLogging();
                 return true; // ISP status reset successfully.
             }
             i2cInterface->write(STATUS_REGISTER,
                                 CMD_CLEAR_STATUS); // Clear status if
                                                    // not reset.
-            lg2::error("Read/Write ISP reset failed");
+            lg2::error("Write ISP reset failed");
+            enableEventLogging();
+        }
+        catch (const i2c::I2CException& e)
+        {
+            exceptionCount++;
+            // Log any errors encountered during reset sequence.
+            lg2::error(
+                "I2C Write/Read or Write error during ISP reset: {ERROR}",
+                "ERROR", e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"I2C_ISP_READ_STATUS",
+                     "I2C exception during read ISP status"}};
+                // Callout PSU & I2C
+                callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+            }
         }
         catch (const std::exception& e)
         {
+            exceptionCount++;
             // Log any errors encountered during reset sequence.
-            lg2::error("I2C Read/Write error during ISP reset: {ERROR}",
+            lg2::error("Write/Read or Write error during ISP reset: {ERROR}",
                        "ERROR", e);
+            if (exceptionCount == MAX_RETRIES)
+            {
+                enableEventLogging();
+                std::map<std::string, std::string> additionalData = {
+                    {"ISP_READ_STATUS", "Exception during read ISP status"},
+                    {"EXCEPTION", e.what()}};
+                // Callout PSU
+                callOutPsuEventLog(additionalData);
+            }
         }
     }
-    createServiceableError("createServiceableError: Failed to reset ISP");
+    if (exceptionCount != MAX_RETRIES)
+    {
+        std::map<std::string, std::string> additionalData = {
+            {"ISP_REST_FAILED", "Failed to read ISP expected status"}};
+        // Callout PSU
+        callOutPsuEventLog(additionalData);
+    }
     lg2::error("Failed to reset ISP Status");
     ispReboot();
     return false;
@@ -276,6 +422,10 @@
     fspath = updater::internal::getFWFilenamePath(getImageDir());
     if (fspath.empty())
     {
+        std::map<std::string, std::string> additionalData = {
+            {"FILE_PATH", "Firmware file path is null"}};
+        // Callout BMC0001 procedure
+        callOutSWEventLog(additionalData);
         lg2::error("Firmware file path not found");
         return false;
     }
@@ -286,6 +436,11 @@
 {
     if (!updater::internal::validateFWFile(fspath))
     {
+        std::map<std::string, std::string> additionalData = {
+            {"FIRMWARE_VALID",
+             "Firmware validation failed, FW file path = " + fspath}};
+        // Callout BMC0001 procedure
+        callOutSWEventLog(additionalData);
         lg2::error("Firmware validation failed, fspath={PATH}", "PATH", fspath);
         return false;
     }
@@ -297,6 +452,11 @@
     auto inputFile = updater::internal::openFirmwareFile(fspath);
     if (!inputFile)
     {
+        std::map<std::string, std::string> additionalData = {
+            {"FIRMWARE_OPEN",
+             "Firmware file failed to open, FW file path = " + fspath}};
+        // Callout BMC0001 procedure
+        callOutSWEventLog(additionalData);
         lg2::error("Failed to open firmware file");
     }
     return inputFile;
@@ -336,6 +496,16 @@
     auto inputFile = openFirmwareFile();
     if (!inputFile)
     {
+        if (isEventLogEnabled())
+        {
+            // Callout BMC0001 procedure
+            std::map<std::string, std::string> additionalData = {
+                {"FW_FAILED_TO_OPEN", "Firmware file failed to open"},
+                {"FW_FILE_PATH", fspath}};
+
+            callOutSWEventLog(additionalData);
+            ispReboot(); // Try to set PSU to normal mode
+        }
         lg2::error("Unable to open firmware file {FILE}", "FILE", fspath);
         return false;
     }
@@ -377,7 +547,6 @@
             "BYTESREAD", bytesRead);
         return false; // Failed
     }
-
     return true;
 }
 
@@ -385,6 +554,8 @@
     uint8_t regAddr, const uint8_t expectedReadSize, uint8_t* readData,
     const int retries, const int delayTime)
 {
+    uint8_t exceptionCount = 0;
+    uint32_t bigEndianValue = 0;
     for (int i = 0; i < retries; ++i)
     {
         uint8_t readReplySize = 0;
@@ -406,22 +577,46 @@
             }
             else
             {
-                uint32_t littleEndianValue =
-                    *reinterpret_cast<uint32_t*>(readData);
-                uint32_t bigEndianValue =
-                    ((littleEndianValue & 0x000000FF) << 24) |
-                    ((littleEndianValue & 0x0000FF00) << 8) |
-                    ((littleEndianValue & 0x00FF0000) >> 8) |
-                    ((littleEndianValue & 0xFF000000) >> 24);
+                bigEndianValue = (readData[0] << 24) | (readData[1] << 16) |
+                                 (readData[2] << 8) | (readData[3]);
                 lg2::error("Write/read block {NUM} failed", "NUM",
                            bigEndianValue);
             }
         }
+        catch (const i2c::I2CException& e)
+        {
+            exceptionCount++;
+            if (exceptionCount == MAX_RETRIES)
+            {
+                std::map<std::string, std::string> additionalData = {
+                    {"I2C_WRITE_READ",
+                     "I2C exception while flashing the firmware."}};
+                // Callout PSU & I2C
+                callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+            }
+            lg2::error("I2C exception write/read block failed: {ERROR}",
+                       "ERROR", e.what());
+        }
         catch (const std::exception& e)
         {
-            lg2::error("I2C write/read block failed: {ERROR}", "ERROR", e);
+            exceptionCount++;
+            if (exceptionCount == MAX_RETRIES)
+            {
+                std::map<std::string, std::string> additionalData = {
+                    {"WRITE_READ", "Exception while flashing the firmware."},
+                    {"EXCEPTION", e.what()}};
+                // Callout PSU
+                callOutPsuEventLog(additionalData);
+            }
+            lg2::error("Exception write/read block failed: {ERROR}", "ERROR",
+                       e.what());
         }
     }
+    std::map<std::string, std::string> additionalData = {
+        {"WRITE_READ",
+         "Download firmware failed block: " + std::to_string(bigEndianValue)}};
+    // Callout PSU
+    callOutPsuEventLog(additionalData);
     return false;
 }
 
@@ -481,27 +676,56 @@
 
 bool AeiUpdater::ispReadRebootStatus()
 {
-    try
+    for (int retry = 0; retry < MAX_RETRIES; ++retry)
     {
-        // Read from the status register to verify reboot
-        uint8_t data = 1; // Initialize data to a non-zero value
-        i2cInterface->read(STATUS_REGISTER, data);
-
-        // If the reboot was successful, the read data should be 0
-        if (data == SUCCESSFUL_ISP_REBOOT_STATUS)
+        try
         {
-            lg2::info("ISP Status Reboot successful.");
-            return true;
+            // Read from the status register to verify reboot
+            uint8_t data = 1; // Initialize data to a non-zero value
+            i2cInterface->read(STATUS_REGISTER, data);
+
+            // If the reboot was successful, the read data should be 0
+            if (data == SUCCESSFUL_ISP_REBOOT_STATUS)
+            {
+                lg2::info("ISP Status Reboot successful.");
+                return true;
+            }
         }
-    }
-    catch (const std::exception& e)
-    {
-        lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR", e);
+        catch (const i2c::I2CException& e)
+        {
+            if (isEventLogEnabled())
+            {
+                std::map<std::string, std::string> additionalData = {
+                    {"I2C_READ_REBOOT",
+                     "I2C exception while reading ISP reboot status"}};
+
+                // Callout PSU & I2C
+                callOutI2CEventLog(additionalData, e.what(), e.errorCode);
+            }
+            lg2::error("I2C read error during reboot attempt: {ERROR}", "ERROR",
+                       e);
+        }
+        catch (const std::exception& e)
+        {
+            if (isEventLogEnabled())
+            {
+                std::map<std::string, std::string> additionalData = {
+                    {"READ_REBOOT",
+                     "Exception while reading ISP reboot status"},
+                    {"EXCEPTION", e.what()}};
+
+                // Callout PSU
+                callOutPsuEventLog(additionalData);
+            }
+            lg2::error("Read exception during reboot attempt: {ERROR}", "ERROR",
+                       e);
+        }
+        // Reboot the PSU
+        ispReboot(); // Try to set PSU to normal mode
     }
 
     // If we reach here, all retries have failed
     lg2::error("Failed to reboot ISP status after max retries.");
     return false;
 }
-
 } // namespace aeiUpdater
diff --git a/tools/power-utils/aei_updater.hpp b/tools/power-utils/aei_updater.hpp
index 6766077..c9d43aa 100644
--- a/tools/power-utils/aei_updater.hpp
+++ b/tools/power-utils/aei_updater.hpp
@@ -182,16 +182,6 @@
     bool ispReadRebootStatus();
 
     /**
-     * @brief Create serviceable error
-     *
-     * Place holder for future enhancement
-     */
-    void createServiceableError(const std::string str)
-    {
-        std::cout << "createServiceableError: " << str << "\n";
-    }
-
-    /**
      * @brief Pointer to the I2C interface for communication
      *
      * This pointer is not owned by the class. The caller is responsible for
diff --git a/tools/power-utils/updater.cpp b/tools/power-utils/updater.cpp
index 7da839f..16e225f 100644
--- a/tools/power-utils/updater.cpp
+++ b/tools/power-utils/updater.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "updater.hpp"
 
 #include "aei_updater.hpp"
@@ -20,13 +21,16 @@
 #include "types.hpp"
 #include "utility.hpp"
 #include "utils.hpp"
+#include "version.hpp"
 
 #include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Logging/Create/client.hpp>
 
 #include <chrono>
 #include <fstream>
-#include <thread>
-#include <vector>
+#include <iostream>
+#include <map>
+#include <string>
 
 using namespace phosphor::logging;
 namespace util = phosphor::power::util;
@@ -169,7 +173,7 @@
     }
     catch (const std::ios_base::failure& e)
     {
-        lg2::error("Error reading firmware: {ERROR}", "ERROR", e.what());
+        lg2::error("Error reading firmware: {ERROR}", "ERROR", e);
         readDataBytes.clear();
     }
     return readDataBytes;
@@ -220,7 +224,7 @@
     catch (const fs::filesystem_error& e)
     {
         lg2::error("Failed to get canonical path DEVPATH= {PATH}, ERROR= {ERR}",
-                   "PATH", devPath, "ERR", e.what());
+                   "PATH", devPath, "ERR", e);
     }
 }
 
@@ -380,4 +384,105 @@
     auto [id, addr] = utils::parseDeviceName(devName);
     i2c = i2c::create(id, addr);
 }
+
+void Updater::createServiceableEventLog(
+    const std::string& errorName, const std::string& severity,
+    std::map<std::string, std::string>& additionalData)
+{
+    if (!isEventLogEnabled() || isEventLoggedThisSession())
+    {
+        return;
+    }
+
+    using namespace sdbusplus::xyz::openbmc_project;
+    using LoggingCreate =
+        sdbusplus::client::xyz::openbmc_project::logging::Create<>;
+    enableEventLoggedThisSession();
+    try
+    {
+        additionalData["_PID"] = std::to_string(getpid());
+        auto method = bus.new_method_call(LoggingCreate::default_service,
+                                          LoggingCreate::instance_path,
+                                          LoggingCreate::interface, "Create");
+        method.append(errorName, severity, additionalData);
+
+        bus.call(method);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        lg2::error(
+            "Failed creating event log for fault {ERROR_NAME}, error {ERR}",
+            "ERROR_NAME", errorName, "ERR", e);
+    }
+    disableEventLogging();
+}
+
+std::map<std::string, std::string> Updater::getI2CAdditionalData()
+{
+    std::map<std::string, std::string> additionalData;
+    auto [id, addr] = utils::parseDeviceName(getDevName());
+    std::string hexIdString = std::format("0x{:x}", id);
+    std::string hexAddrString = std::format("0x{:x}", addr);
+
+    additionalData["CALLOUT_IIC_BUS"] = hexIdString;
+    additionalData["CALLOUT_IIC_ADDR"] = hexAddrString;
+    return additionalData;
+}
+
+/*
+ * callOutI2CEventLog calls out FRUs in the following order:
+ * 1 - PSU  high priority
+ * 2 - CALLOUT_IIC_BUS
+ */
+void Updater::callOutI2CEventLog(
+    std::map<std::string, std::string> extraAdditionalData,
+    const std::string& exceptionString, const int errorCode)
+{
+    std::map<std::string, std::string> additionalData = {
+        {"CALLOUT_INVENTORY_PATH", getPsuInventoryPath()}};
+    additionalData.merge(extraAdditionalData);
+    additionalData.merge(getI2CAdditionalData());
+    additionalData["CALLOUT_ERRNO"] = std::to_string(errorCode);
+    if (!exceptionString.empty())
+    {
+        additionalData["I2C_EXCEPTION"] = exceptionString;
+    }
+    createServiceableEventLog(FW_UPDATE_FAILED_MSG, ERROR_SEVERITY,
+                              additionalData);
+}
+
+/*
+ * callOutPsuEventLog calls out PSU and system planar
+ */
+void Updater::callOutPsuEventLog(
+    std::map<std::string, std::string> extraAdditionalData)
+{
+    std::map<std::string, std::string> additionalData = {
+        {"CALLOUT_INVENTORY_PATH", getPsuInventoryPath()}};
+    additionalData.merge(extraAdditionalData);
+    createServiceableEventLog(updater::FW_UPDATE_FAILED_MSG,
+                              updater::ERROR_SEVERITY, additionalData);
+}
+
+/*
+ * callOutSWEventLog calls out the BMC0001 procedure.
+ */
+void Updater::callOutSWEventLog(
+    std::map<std::string, std::string> additionalData)
+{
+    createServiceableEventLog(updater::PSU_FW_FILE_ISSUE_MSG,
+                              updater::ERROR_SEVERITY, additionalData);
+}
+
+/*
+ * callOutGoodEventLog calls out a successful firmware update.
+ */
+void Updater::callOutGoodEventLog()
+{
+    std::map<std::string, std::string> additionalData = {
+        {"SUCCESSFUL_PSU_UPDATE", getPsuInventoryPath()},
+        {"FIRMWARE_VERSION", version::getVersion(bus, getPsuInventoryPath())}};
+    createServiceableEventLog(updater::FW_UPDATE_SUCCESS_MSG,
+                              updater::INFORMATIONAL_SEVERITY, additionalData);
+}
 } // namespace updater
diff --git a/tools/power-utils/updater.hpp b/tools/power-utils/updater.hpp
index c02541b..21bc4cd 100644
--- a/tools/power-utils/updater.hpp
+++ b/tools/power-utils/updater.hpp
@@ -30,6 +30,17 @@
 
 namespace fs = std::filesystem;
 
+constexpr auto FW_UPDATE_FAILED_MSG =
+    "xyz.openbmc_project.Power.PowerSupply.Error.FirmwareUpdateFailed";
+constexpr auto PSU_FW_FILE_ISSUE_MSG =
+    "xyz.openbmc_project.Power.PowerSupply.Error.FirmwareIssue";
+constexpr auto FW_UPDATE_SUCCESS_MSG =
+    "xyz.openbmc_project.Power.PowerSupply.Info.FirmwareUpdateSuccessful";
+
+constexpr auto ERROR_SEVERITY = "xyz.openbmc_project.Logging.Entry.Level.Error";
+constexpr auto INFORMATIONAL_SEVERITY =
+    "xyz.openbmc_project.Logging.Entry.Level.Informational";
+
 /**
  * Update PSU firmware
  *
@@ -127,6 +138,128 @@
         return i2c.get();
     }
 
+    /**
+     * @brief Creates a serviceable Predictive Event Log,
+     *
+     * This method generates an event log with the given error name, severity,
+     * and additional data. It interacts with the OpenBMC logging service to
+     * record faults.
+     *
+     * @param[in] errorName The name of the error to log.
+     * @param[in] severity The severity level of the error.
+     * @param[in] additionalData Additional key-value pairs containing details
+     *                           about the error.
+     */
+    void createServiceableEventLog(
+        const std::string& errorName, const std::string& severity,
+        std::map<std::string, std::string>& additionalData);
+
+    /**
+     * @brief Retrieves additional data related to I2C communication.
+     *
+     * This method collects and returns I2C bus information, including the
+     * bus ID, address, and error number, which are used for reporting
+     * Predictive Error Log.
+     *
+     * @return A map containing I2C-related key-value pairs.
+     */
+    std::map<std::string, std::string> getI2CAdditionalData();
+
+    /**
+     * @brief Call out an I2C-related Predictive Error Log.
+     *
+     * This method creates a serviceable event log related to I2C failures.
+     * It collects additional data about the I2C communication and logs the
+     * failure with appropriate severity.
+     *
+     * @param[in] extraAdditionalData Additional key-value pairs specific to
+     *                                the error context.
+     * @param[in] exceptionString A string describing the exception that
+     *                            triggered the error.
+     * @param[in] errorCode Exception error code.
+     */
+    void callOutI2CEventLog(
+        std::map<std::string, std::string> extraAdditionalData,
+        const std::string& exceptionString = "", const int errorCode = 0);
+
+    /**
+     * @brief Call out a PSU-related Predictive Error Log.
+     *
+     * This method logs a failure related to PSU firmware updates and additional
+     * diagnostics data to the event log.
+     *
+     * @param[in] extraAdditionalData Additional key-value pairs specific to
+     *                                the PSU-related error.
+     */
+    void callOutPsuEventLog(
+        std::map<std::string, std::string> extraAdditionalData);
+
+    /**
+     * @brief Call out a software-related Predictive Error Log.
+     *
+     * This method logs a failure related to PSU firmware file issues or other
+     * software-related errors. It merges any additional error-specific data
+     * before logging the event.
+     *
+     * @param[in] extraAdditionalData Additional key-value pairs specific to
+     *                                the software-related error.
+     */
+    void callOutSWEventLog(
+        std::map<std::string, std::string> extraAdditionalData);
+
+    /**
+     * @brief Accessor to set logEventLog to true
+     *
+     */
+    void enableEventLogging()
+    {
+        eventLogState = true;
+    }
+
+    /**
+     * @brief Accessor to set eventLogState to false
+     *
+     */
+    void disableEventLogging()
+    {
+        eventLogState = false;
+    }
+
+    /**
+     * @brief Accessor eventLogState status (enable true, disable false)
+     *
+     * @return true or false
+     */
+    bool isEventLogEnabled()
+    {
+        return eventLogState;
+    }
+
+    /**
+     * @brief Accessor to disable eventLoggedThisSession
+     *
+     */
+    void enableEventLoggedThisSession()
+    {
+        eventLoggedThisSession = true;
+    }
+
+    /**
+     * @brief Accessor to retieve eventLoggedThisSession status
+     *
+     * @return true or false
+     */
+    bool isEventLoggedThisSession()
+    {
+        return eventLoggedThisSession;
+    }
+
+    /**
+     * @brief Call out successful PSU firmware update.
+     *
+     */
+    void callOutGoodEventLog();
+
   private:
     /** @brief The sdbusplus DBus bus connection */
     sdbusplus::bus_t bus;
@@ -160,6 +293,14 @@
 
     /** @brief The i2c device interface */
     std::unique_ptr<i2c::I2CInterface> i2c;
+
+    /** @brief Event Log flag */
+    bool eventLogState = false;
+
+    /** @brief Event logged this session flag, this is to make sure no other
+     * event log can be logged
+     */
+    bool eventLoggedThisSession = false;
 };
 
 namespace internal