oem-ampere: eventManager: Handle `BootProgress` sensor event

Add Ampere OEM code to handle the `sensorEvent` for PLDM `BootProgress`
sensor. In Ampere system, the SOC termini will have the TID 1 or 2. The
Ampere OEM EventManager will check the terminus TID to confirm about the
terminus type. Base on the value of `BootProgress` sensor, the OEM code
will add the Redfish Log to report the boot progress of Ampere SoC.

Tested:
1. Power on the host.
2. Check the Redfish SEL log.

Signed-off-by: Chau Ly <chaul@amperecomputing.com>
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Change-Id: Icc51537ef17ee8eb4b5b571eafeea7b5d7763cbe
diff --git a/libpldmresponder/platform.hpp b/libpldmresponder/platform.hpp
index 6c30c52..9c45ef8 100644
--- a/libpldmresponder/platform.hpp
+++ b/libpldmresponder/platform.hpp
@@ -219,6 +219,22 @@
         oemPlatformHandler = handler;
     }
 
+    /* @brief Method to register event handlers
+     *
+     * @param[in] handler - oem event handlers
+     */
+    inline void registerEventHandlers(EventType eventId, EventHandlers handlers)
+    {
+        // Try to emplace the eventId with an empty vector if it doesn't exist
+        auto [iter,
+              inserted] = eventHandlers.try_emplace(eventId, EventHandlers{});
+
+        for (const auto& handler : handlers)
+        {
+            iter->second.emplace_back(handler);
+        }
+    }
+
     /** @brief Handler for GetPDR
      *
      *  @param[in] request - Request message payload
diff --git a/meson.build b/meson.build
index 32684b3..681dd7a 100644
--- a/meson.build
+++ b/meson.build
@@ -138,6 +138,12 @@
 )
 conf_data.set('SENSOR_POLLING_TIME', get_option('sensor-polling-time'))
 
+oem_files = []
+if get_option('oem-ampere').enabled()
+    add_project_arguments('-DOEM_AMPERE', language : 'cpp')
+    subdir('oem/ampere')
+endif
+
 configure_file(output: 'config.h', configuration: conf_data)
 
 add_project_arguments(
@@ -247,6 +253,7 @@
     'platform-mc/sensor_manager.cpp',
     'platform-mc/numeric_sensor.cpp',
     'platform-mc/event_manager.cpp',
+    oem_files,
     'requester/mctp_endpoint_discovery.cpp',
     implicit_include_directories: false,
     dependencies: deps,
diff --git a/meson.options b/meson.options
index 8a7138a..93ac021 100644
--- a/meson.options
+++ b/meson.options
@@ -175,6 +175,15 @@
     description: 'OEM-IBM: max DMA size'
 )
 
+
+## OEM AMPERE Options
+option(
+    'oem-ampere',
+    type: 'feature',
+    description: 'Enable AMPERE OEM PLDM',
+    value: 'disabled',
+)
+
 ## Default Sensor Update Interval Options
 option(
     'default-sensor-update-interval',
diff --git a/oem/ampere/event/meson.build b/oem/ampere/event/meson.build
new file mode 100644
index 0000000..6dc6a9e
--- /dev/null
+++ b/oem/ampere/event/meson.build
@@ -0,0 +1 @@
+oem_files += files('oem_event_manager.cpp',)
\ No newline at end of file
diff --git a/oem/ampere/event/oem_event_manager.cpp b/oem/ampere/event/oem_event_manager.cpp
new file mode 100644
index 0000000..e99288d
--- /dev/null
+++ b/oem/ampere/event/oem_event_manager.cpp
@@ -0,0 +1,427 @@
+#include "oem_event_manager.hpp"
+
+#include "requester/handler.hpp"
+#include "requester/request.hpp"
+
+#include <config.h>
+#include <libpldm/pldm.h>
+#include <libpldm/utils.h>
+#include <systemd/sd-journal.h>
+
+#include <phosphor-logging/lg2.hpp>
+#include <xyz/openbmc_project/Logging/Entry/server.hpp>
+
+#include <algorithm>
+#include <map>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+namespace pldm
+{
+namespace oem_ampere
+{
+namespace boot_stage = boot::stage;
+
+constexpr const char* BIOSFWPanicRegistry =
+    "OpenBMC.0.1.BIOSFirmwarePanicReason.Warning";
+constexpr auto maxDIMMIdxBitNum = 24;
+
+/*
+    An array of possible boot status of a boot stage.
+    The index maps with byte 0 of boot code.
+*/
+std::array<std::string, 3> bootStatMsg = {" booting", " completed", " failed"};
+
+/*
+    An array of possible boot status of DDR training stage.
+    The index maps with byte 0 of boot code.
+*/
+std::array<std::string, 3> ddrTrainingMsg = {
+    " progress started", " in-progress", " progress completed"};
+
+/*
+    In Ampere systems, BMC only directly communicates with MCTP/PLDM SoC
+    EPs through SMBus and PCIe. When host boots up, SMBUS interface
+    comes up first. In this interface, BMC is bus owner.
+
+    mctpd will set the EID 0x14 for S0 and 0x16 for S1 (if available).
+    pldmd will always use TID 1 for S0 and TID 2 for S1 (if available).
+*/
+EventToMsgMap_t tidToSocketNameMap = {{1, "SOCKET 0"}, {2, "SOCKET 1"}};
+
+/*
+    A map between sensor IDs and their names in string.
+    Using pldm::oem::sensor_ids
+*/
+EventToMsgMap_t sensorIdToStrMap = {{BOOT_OVERALL, "BOOT_OVERALL"}};
+
+/*
+    A map between the boot stages and logging strings.
+    Using pldm::oem::boot::stage::boot_stage
+*/
+EventToMsgMap_t bootStageToMsgMap = {
+    {boot_stage::SECPRO, "SECpro"},
+    {boot_stage::MPRO, "Mpro"},
+    {boot_stage::ATF_BL1, "ATF BL1"},
+    {boot_stage::ATF_BL2, "ATF BL2"},
+    {boot_stage::DDR_INITIALIZATION, "DDR initialization"},
+    {boot_stage::DDR_TRAINING, "DDR training"},
+    {boot_stage::S0_DDR_TRAINING_FAILURE, "DDR training failure"},
+    {boot_stage::ATF_BL31, "ATF BL31"},
+    {boot_stage::ATF_BL32, "ATF BL32"},
+    {boot_stage::S1_DDR_TRAINING_FAILURE, "DDR training failure"},
+    {boot_stage::UEFI_STATUS_CLASS_CODE_MIN,
+     "ATF BL33 (UEFI) booting status = "}};
+
+/*
+    A map between log level and the registry used for Redfish SEL log
+    Using pldm::oem::log_level
+*/
+std::unordered_map<log_level, std::string> logLevelToRedfishMsgIdMap = {
+    {log_level::BIOSFWPANIC, BIOSFWPanicRegistry}};
+
+std::string
+    OemEventManager::prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId)
+{
+    std::string description;
+    if (!tidToSocketNameMap.contains(tid))
+    {
+        description += "TID " + std::to_string(tid) + ": ";
+    }
+    else
+    {
+        description += tidToSocketNameMap[tid] + ": ";
+    }
+
+    if (!sensorIdToStrMap.contains(sensorId))
+    {
+        description += "Sensor ID " + std::to_string(sensorId) + ": ";
+    }
+    else
+    {
+        description += sensorIdToStrMap[sensorId] + ": ";
+    }
+
+    return description;
+}
+
+void OemEventManager::sendJournalRedfish(const std::string& description,
+                                         log_level& logLevel)
+{
+    if (description.empty())
+    {
+        return;
+    }
+
+    if (!logLevelToRedfishMsgIdMap.contains(logLevel))
+    {
+        lg2::error("Invalid {LEVEL} Description {DES}", "LEVEL", logLevel,
+                   "DES", description);
+        return;
+    }
+    auto redfishMsgId = logLevelToRedfishMsgIdMap[logLevel];
+    lg2::info("MESSAGE={DES}", "DES", description, "REDFISH_MESSAGE_ID",
+              redfishMsgId, "REDFISH_MESSAGE_ARGS", description);
+}
+
+std::string OemEventManager::dimmIdxsToString(uint32_t dimmIdxs)
+{
+    std::string description;
+    for (const auto bitIdx : std::views::iota(0, maxDIMMIdxBitNum))
+    {
+        if (dimmIdxs & (static_cast<uint32_t>(1) << bitIdx))
+        {
+            description += " #" + std::to_string(bitIdx);
+        }
+    }
+    return description;
+}
+
+void OemEventManager::handleBootOverallEvent(
+    pldm_tid_t /*tid*/, uint16_t /*sensorId*/, uint32_t presentReading)
+{
+    log_level logLevel{log_level::OK};
+    std::string description;
+    std::stringstream strStream;
+
+    uint8_t byte0 = (presentReading & 0x000000ff);
+    uint8_t byte1 = (presentReading & 0x0000ff00) >> 8;
+    uint8_t byte2 = (presentReading & 0x00ff0000) >> 16;
+    uint8_t byte3 = (presentReading & 0xff000000) >> 24;
+    /*
+     * Handle SECpro, Mpro, ATF BL1, ATF BL2, ATF BL31,
+     * ATF BL32 and DDR initialization
+     */
+    if (bootStageToMsgMap.contains(byte3))
+    {
+        // Boot stage adding
+        description += bootStageToMsgMap[byte3];
+
+        switch (byte3)
+        {
+            case boot_stage::DDR_TRAINING:
+                if (byte0 >= ddrTrainingMsg.size())
+                {
+                    logLevel = log_level::BIOSFWPANIC;
+                    description += " unknown status";
+                }
+                else
+                {
+                    description += ddrTrainingMsg[byte0];
+                }
+                if (0x01 == byte0)
+                {
+                    // Add complete percentage
+                    description += " at " + std::to_string(byte1) + "%";
+                }
+                break;
+            case boot_stage::S0_DDR_TRAINING_FAILURE:
+            case boot_stage::S1_DDR_TRAINING_FAILURE:
+                // ddr_training_status_msg()
+                logLevel = log_level::BIOSFWPANIC;
+                description += " at DIMMs:";
+                // dimmIdxs = presentReading & 0x00ffffff;
+                description += dimmIdxsToString(presentReading & 0x00ffffff);
+                description += " of socket ";
+                description +=
+                    (boot_stage::S0_DDR_TRAINING_FAILURE == byte3) ? "0" : "1";
+                break;
+            default:
+                if (byte0 >= bootStatMsg.size())
+                {
+                    logLevel = log_level::BIOSFWPANIC;
+                    description += " unknown status";
+                }
+                else
+                {
+                    description += bootStatMsg[byte0];
+                }
+                break;
+        }
+
+        // Sensor report action is fail
+        if (boot::status::BOOT_STATUS_FAILURE == byte2)
+        {
+            logLevel = log_level::BIOSFWPANIC;
+        }
+    }
+    else
+    {
+        if (byte3 <= boot_stage::UEFI_STATUS_CLASS_CODE_MAX)
+        {
+            description +=
+                bootStageToMsgMap[boot_stage::UEFI_STATUS_CLASS_CODE_MIN];
+
+            strStream
+                << "Segment (0x" << std::setfill('0') << std::hex
+                << std::setw(8) << static_cast<uint32_t>(presentReading)
+                << "), Status Class (0x" << std::setw(2)
+                << static_cast<uint32_t>(byte3) << "), Status SubClass (0x"
+                << std::setw(2) << static_cast<uint32_t>(byte2)
+                << "), Operation Code (0x" << std::setw(4)
+                << static_cast<uint32_t>((presentReading & 0xffff0000) >> 16)
+                << ")" << std::dec;
+
+            description += strStream.str();
+        }
+    }
+
+    // Log to Redfish event
+    sendJournalRedfish(description, logLevel);
+}
+
+int OemEventManager::processNumericSensorEvent(
+    pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
+    size_t sensorDataLength)
+{
+    uint8_t eventState = 0;
+    uint8_t previousEventState = 0;
+    uint8_t sensorDataSize = 0;
+    uint32_t presentReading;
+    auto rc = decode_numeric_sensor_data(
+        sensorData, sensorDataLength, &eventState, &previousEventState,
+        &sensorDataSize, &presentReading);
+    if (rc)
+    {
+        lg2::error(
+            "Failed to decode numericSensorState event for terminus ID {TID}, error {RC} ",
+            "TID", tid, "RC", rc);
+        return rc;
+    }
+
+    switch (sensorId)
+    {
+        case BOOT_OVERALL:
+            handleBootOverallEvent(tid, sensorId, presentReading);
+            break;
+        default:
+            std::string description;
+            std::stringstream strStream;
+            log_level logLevel = log_level::OK;
+
+            description += "SENSOR_EVENT : NUMERIC_SENSOR_STATE: ";
+            description += prefixMsgStrCreation(tid, sensorId);
+            strStream << std::setfill('0') << std::hex << "eventState 0x"
+                      << std::setw(2) << static_cast<uint32_t>(eventState)
+                      << " previousEventState 0x" << std::setw(2)
+                      << static_cast<uint32_t>(previousEventState)
+                      << " sensorDataSize 0x" << std::setw(2)
+                      << static_cast<uint32_t>(sensorDataSize)
+                      << " presentReading 0x" << std::setw(8)
+                      << static_cast<uint32_t>(presentReading) << std::dec;
+            description += strStream.str();
+
+            sendJournalRedfish(description, logLevel);
+            break;
+    }
+    return PLDM_SUCCESS;
+}
+
+int OemEventManager::processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId,
+                                             const uint8_t* sensorData,
+                                             size_t sensorDataLength)
+{
+    uint8_t sensorOffset = 0;
+    uint8_t eventState = 0;
+    uint8_t previousEventState = 0;
+
+    auto rc =
+        decode_state_sensor_data(sensorData, sensorDataLength, &sensorOffset,
+                                 &eventState, &previousEventState);
+    if (rc)
+    {
+        lg2::error(
+            "Failed to decode stateSensorState event for terminus ID {TID}, error {RC}",
+            "TID", tid, "RC", rc);
+        return rc;
+    }
+
+    std::string description;
+    std::stringstream strStream;
+    log_level logLevel = log_level::OK;
+
+    description += "SENSOR_EVENT : STATE_SENSOR_STATE: ";
+    description += prefixMsgStrCreation(tid, sensorId);
+    strStream << std::setfill('0') << std::hex << "sensorOffset 0x"
+              << std::setw(2) << static_cast<uint32_t>(sensorOffset)
+              << "eventState 0x" << std::setw(2)
+              << static_cast<uint32_t>(eventState) << " previousEventState 0x"
+              << std::setw(2) << static_cast<uint32_t>(previousEventState)
+              << std::dec;
+    description += strStream.str();
+
+    sendJournalRedfish(description, logLevel);
+
+    return PLDM_SUCCESS;
+}
+
+int OemEventManager::processSensorOpStateEvent(
+    pldm_tid_t tid, uint16_t sensorId, const uint8_t* sensorData,
+    size_t sensorDataLength)
+{
+    uint8_t present_op_state = 0;
+    uint8_t previous_op_state = 0;
+
+    auto rc = decode_sensor_op_data(sensorData, sensorDataLength,
+                                    &present_op_state, &previous_op_state);
+    if (rc)
+    {
+        lg2::error(
+            "Failed to decode sensorOpState event for terminus ID {TID}, error {RC}",
+            "TID", tid, "RC", rc);
+        return rc;
+    }
+
+    std::string description;
+    std::stringstream strStream;
+    log_level logLevel = log_level::OK;
+
+    description += "SENSOR_EVENT : SENSOR_OP_STATE: ";
+    description += prefixMsgStrCreation(tid, sensorId);
+    strStream << std::setfill('0') << std::hex << "present_op_state 0x"
+              << std::setw(2) << static_cast<uint32_t>(present_op_state)
+              << "previous_op_state 0x" << std::setw(2)
+              << static_cast<uint32_t>(previous_op_state) << std::dec;
+    description += strStream.str();
+
+    sendJournalRedfish(description, logLevel);
+
+    return PLDM_SUCCESS;
+}
+
+int OemEventManager::handleSensorEvent(
+    const pldm_msg* request, size_t payloadLength, uint8_t /* formatVersion */,
+    pldm_tid_t tid, size_t eventDataOffset)
+{
+    /* This OEM event handler is only used for SoC terminus*/
+    if (!tidToSocketNameMap.contains(tid))
+    {
+        return PLDM_SUCCESS;
+    }
+    auto eventData =
+        reinterpret_cast<const uint8_t*>(request->payload) + eventDataOffset;
+    auto eventDataSize = payloadLength - eventDataOffset;
+
+    uint16_t sensorId = 0;
+    uint8_t sensorEventClassType = 0;
+    size_t eventClassDataOffset = 0;
+    auto rc =
+        decode_sensor_event_data(eventData, eventDataSize, &sensorId,
+                                 &sensorEventClassType, &eventClassDataOffset);
+    if (rc)
+    {
+        lg2::error("Failed to decode sensor event data return code {RC}.", "RC",
+                   rc);
+        return rc;
+    }
+    const uint8_t* sensorData = eventData + eventClassDataOffset;
+    size_t sensorDataLength = eventDataSize - eventClassDataOffset;
+
+    switch (sensorEventClassType)
+    {
+        case PLDM_NUMERIC_SENSOR_STATE:
+        {
+            return processNumericSensorEvent(tid, sensorId, sensorData,
+                                             sensorDataLength);
+        }
+        case PLDM_STATE_SENSOR_STATE:
+        {
+            return processStateSensorEvent(tid, sensorId, sensorData,
+                                           sensorDataLength);
+        }
+        case PLDM_SENSOR_OP_STATE:
+        {
+            return processSensorOpStateEvent(tid, sensorId, sensorData,
+                                             sensorDataLength);
+        }
+        default:
+            std::string description;
+            std::stringstream strStream;
+            log_level logLevel = log_level::OK;
+
+            description += "SENSOR_EVENT : Unsupported Sensor Class " +
+                           std::to_string(sensorEventClassType) + ": ";
+            description += prefixMsgStrCreation(tid, sensorId);
+            strStream << std::setfill('0') << std::hex
+                      << std::setw(sizeof(sensorData) * 2) << "Sensor data: ";
+
+            auto dataPtr = sensorData;
+            for ([[maybe_unused]] const auto& i :
+                 std::views::iota(0, (int)sensorDataLength))
+            {
+                strStream << "0x" << static_cast<uint32_t>(*dataPtr);
+                dataPtr += sizeof(sensorData);
+            }
+
+            description += strStream.str();
+
+            sendJournalRedfish(description, logLevel);
+    }
+    lg2::info("Unsupported class type {CLASSTYPE}", "CLASSTYPE",
+              sensorEventClassType);
+    return PLDM_ERROR;
+}
+
+} // namespace oem_ampere
+} // namespace pldm
diff --git a/oem/ampere/event/oem_event_manager.hpp b/oem/ampere/event/oem_event_manager.hpp
new file mode 100644
index 0000000..15b615f
--- /dev/null
+++ b/oem/ampere/event/oem_event_manager.hpp
@@ -0,0 +1,176 @@
+#pragma once
+
+#include "libpldm/pldm.h"
+
+#include "common/instance_id.hpp"
+#include "common/types.hpp"
+#include "oem_event_manager.hpp"
+#include "platform-mc/manager.hpp"
+#include "requester/handler.hpp"
+#include "requester/request.hpp"
+
+namespace pldm
+{
+namespace oem_ampere
+{
+using namespace pldm::pdr;
+
+using EventToMsgMap_t = std::unordered_map<uint8_t, std::string>;
+
+enum sensor_ids
+{
+    BOOT_OVERALL = 175,
+};
+
+namespace boot
+{
+namespace status
+{
+enum boot_status
+{
+    BOOT_STATUS_SUCCESS = 0x80,
+    BOOT_STATUS_FAILURE = 0x81,
+};
+} // namespace status
+namespace stage
+{
+enum boot_stage
+{
+    UEFI_STATUS_CLASS_CODE_MIN = 0x00,
+    UEFI_STATUS_CLASS_CODE_MAX = 0x7f,
+    SECPRO = 0x90,
+    MPRO = 0x91,
+    ATF_BL1 = 0x92,
+    ATF_BL2 = 0x93,
+    DDR_INITIALIZATION = 0x94,
+    DDR_TRAINING = 0x95,
+    S0_DDR_TRAINING_FAILURE = 0x96,
+    ATF_BL31 = 0x97,
+    ATF_BL32 = 0x98,
+    S1_DDR_TRAINING_FAILURE = 0x99,
+};
+} // namespace stage
+} // namespace boot
+
+enum class log_level : int
+{
+    OK,
+    BIOSFWPANIC,
+};
+
+/**
+ * @brief OemEventManager
+ *
+ *
+ */
+class OemEventManager
+{
+  public:
+    OemEventManager() = delete;
+    OemEventManager(const OemEventManager&) = delete;
+    OemEventManager(OemEventManager&&) = delete;
+    OemEventManager& operator=(const OemEventManager&) = delete;
+    OemEventManager& operator=(OemEventManager&&) = delete;
+    virtual ~OemEventManager() = default;
+
+    explicit OemEventManager(
+        sdeventplus::Event& event,
+        requester::Handler<requester::Request>* /* handler */,
+        pldm::InstanceIdDb& /* instanceIdDb */) : event(event) {};
+
+    /** @brief Decode sensor event messages and handle correspondingly.
+     *
+     *  @param[in] request - the request message of sensor event
+     *  @param[in] payloadLength - the payload length of sensor event
+     *  @param[in] formatVersion - the format version of sensor event
+     *  @param[in] tid - TID
+     *  @param[in] eventDataOffset - the event data offset of sensor event
+     *
+     *  @return int - returned error code
+     */
+    int handleSensorEvent(const pldm_msg* request, size_t payloadLength,
+                          uint8_t /* formatVersion */, pldm_tid_t tid,
+                          size_t eventDataOffset);
+
+  protected:
+    /** @brief Create prefix string for logging message.
+     *
+     *  @param[in] tid - TID
+     *  @param[in] sensorId - Sensor ID
+     *
+     *  @return std::string - the prefeix string
+     */
+    std::string prefixMsgStrCreation(pldm_tid_t tid, uint16_t sensorId);
+
+    /** @brief Log the message into Redfish SEL.
+     *
+     *  @param[in] description - the logging message
+     *  @param[in] logLevel - the logging level
+     */
+    void sendJournalRedfish(const std::string& description,
+                            log_level& logLevel);
+
+    /** @brief Convert the one-hot DIMM index byte into a string of DIMM
+     * indexes.
+     *
+     *  @param[in] dimmIdxs - the one-hot DIMM index byte
+     *
+     *  @return std::string - the string of DIMM indexes
+     */
+    std::string dimmIdxsToString(uint32_t dimmIdxs);
+
+    /** @brief Handle numeric sensor event message from boot overall sensor.
+     *
+     *  @param[in] tid - TID
+     *  @param[in] sensorId - Sensor ID
+     *  @param[in] presentReading - the present reading of the sensor
+     */
+    void handleBootOverallEvent(pldm_tid_t /*tid*/, uint16_t /*sensorId*/,
+                                uint32_t presentReading);
+
+    /** @brief Handle numeric sensor event messages.
+     *
+     *  @param[in] tid - TID
+     *  @param[in] sensorId - Sensor ID
+     *  @param[in] sensorData - the sensor data
+     *  @param[in] sensorDataLength - the length of sensor data
+     *
+     *  @return int - returned error code
+     */
+    int processNumericSensorEvent(pldm_tid_t tid, uint16_t sensorId,
+                                  const uint8_t* sensorData,
+                                  size_t sensorDataLength);
+
+    /** @brief Handle state sensor event messages.
+     *
+     *  @param[in] tid - TID
+     *  @param[in] sensorId - Sensor ID
+     *  @param[in] sensorData - the sensor data
+     *  @param[in] sensorDataLength - the length of sensor data
+     *
+     *  @return int - returned error code
+     */
+    int processStateSensorEvent(pldm_tid_t tid, uint16_t sensorId,
+                                const uint8_t* sensorData,
+                                size_t sensorDataLength);
+
+    /** @brief Handle op state sensor event messages.
+     *
+     *  @param[in] tid - TID
+     *  @param[in] sensorId - Sensor ID
+     *  @param[in] sensorData - the sensor data
+     *  @param[in] sensorDataLength - the length of sensor data
+     *
+     *  @return int - returned error code
+     */
+    int processSensorOpStateEvent(pldm_tid_t tid, uint16_t sensorId,
+                                  const uint8_t* sensorData,
+                                  size_t sensorDataLength);
+
+    /** @brief reference of main event loop of pldmd, primarily used to schedule
+     *  work
+     */
+    sdeventplus::Event& event;
+};
+} // namespace oem_ampere
+} // namespace pldm
diff --git a/oem/ampere/meson.build b/oem/ampere/meson.build
new file mode 100644
index 0000000..8c37ab1
--- /dev/null
+++ b/oem/ampere/meson.build
@@ -0,0 +1 @@
+subdir('event')
\ No newline at end of file
diff --git a/oem/ampere/oem_ampere.hpp b/oem/ampere/oem_ampere.hpp
new file mode 100644
index 0000000..00d8b90
--- /dev/null
+++ b/oem/ampere/oem_ampere.hpp
@@ -0,0 +1,109 @@
+#pragma once
+#include "../../common/utils.hpp"
+#include "../../libpldmresponder/base.hpp"
+#include "../../libpldmresponder/bios.hpp"
+#include "../../libpldmresponder/fru.hpp"
+#include "../../libpldmresponder/platform.hpp"
+#include "../../oem/ampere/event/oem_event_manager.hpp"
+#include "../../platform-mc/manager.hpp"
+#include "../../pldmd/invoker.hpp"
+#include "../../requester/request.hpp"
+
+namespace pldm
+{
+namespace oem_ampere
+{
+
+/**
+ * @class OemAMPERE
+ *
+ * @brief class for creating all the OEM AMPERE handlers
+ *
+ *  Only in case of OEM_AMPERE this class object will be instantiated
+ */
+class OemAMPERE
+{
+  public:
+    OemAMPERE() = delete;
+    OemAMPERE& operator=(const OemAMPERE&) = delete;
+    OemAMPERE(OemAMPERE&&) = delete;
+    OemAMPERE& operator=(OemAMPERE&&) = delete;
+
+  public:
+    /** Constructs OemAMPERE object
+     *
+     * @param[in] dBusIntf - D-Bus handler
+     * @param[in] mctp_fd - fd of MCTP communications socket
+     * @param[in] mctp_eid - MCTP EID of remote host firmware
+     * @param[in] repo - pointer to BMC's primary PDR repo
+     * @param[in] instanceIdDb - pointer to an InstanceIdDb object
+     * @param[in] event - sd_event handler
+     * @param[in] invoker - invoker handler
+     * @param[in] hostPDRHandler - hostPDRHandler handler
+     * @param[in] platformHandler - platformHandler handler
+     * @param[in] fruHandler - fruHandler handler
+     * @param[in] baseHandler - baseHandler handler
+     * @param[in] biosHandler - biosHandler handler
+     * @param[in] reqHandler - reqHandler handler
+     */
+    explicit OemAMPERE(
+        const pldm::utils::DBusHandler* /* dBusIntf */, int /* mctp_fd */,
+        pldm_pdr* /* repo */, pldm::InstanceIdDb& instanceIdDb,
+        sdeventplus::Event& event, responder::Invoker& /* invoker */,
+        HostPDRHandler* /* hostPDRHandler */,
+        responder::platform::Handler* platformHandler,
+        responder::fru::Handler* /* fruHandler */,
+        responder::base::Handler* /* baseHandler */,
+        responder::bios::Handler* /* biosHandler */,
+        platform_mc::Manager* /* platformManager */,
+        pldm::requester::Handler<pldm::requester::Request>* reqHandler) :
+        instanceIdDb(instanceIdDb), event(event),
+        platformHandler(platformHandler), reqHandler(reqHandler)
+    {
+        oemEventManager = std::make_shared<oem_ampere::OemEventManager>(
+            this->event, this->reqHandler, this->instanceIdDb);
+        createOemEventHandler(oemEventManager);
+    }
+
+  private:
+    /** @brief Method for creating OemEventManager
+     *
+     *  This method also assigns the OemEventManager to the below
+     *  different handlers.
+     */
+    void createOemEventHandler(
+        std::shared_ptr<oem_ampere::OemEventManager> oemEventManager)
+    {
+        platformHandler->registerEventHandlers(
+            PLDM_SENSOR_EVENT,
+            {[&oemEventManager](const pldm_msg* request, size_t payloadLength,
+                                uint8_t formatVersion, uint8_t tid,
+                                size_t eventDataOffset) {
+                return oemEventManager->handleSensorEvent(
+                    request, payloadLength, formatVersion, tid,
+                    eventDataOffset);
+            }});
+    }
+
+  private:
+    /** @brief reference to an Instance ID database object, used to obtain PLDM
+     * instance IDs
+     */
+    pldm::InstanceIdDb& instanceIdDb;
+
+    /** @brief reference of main event loop of pldmd, primarily used to schedule
+     *  work
+     */
+    sdeventplus::Event& event;
+
+    /** @brief Platform handler*/
+    responder::platform::Handler* platformHandler = nullptr;
+
+    /** @brief pointer to the requester class*/
+    requester::Handler<requester::Request>* reqHandler = nullptr;
+
+    std::shared_ptr<oem_ampere::OemEventManager> oemEventManager{};
+};
+
+} // namespace oem_ampere
+} // namespace pldm
diff --git a/pldmd/pldmd.cpp b/pldmd/pldmd.cpp
index e9ff90e..2352b60 100644
--- a/pldmd/pldmd.cpp
+++ b/pldmd/pldmd.cpp
@@ -64,6 +64,10 @@
 #include "oem_ibm.hpp"
 #endif
 
+#ifdef OEM_AMPERE
+#include "oem/ampere/oem_ampere.hpp"
+#endif
+
 constexpr const char* PLDMService = "xyz.openbmc_project.PLDM";
 
 using namespace pldm;
@@ -309,6 +313,14 @@
 
     auto baseHandler = std::make_unique<base::Handler>(event);
 
+#ifdef OEM_AMPERE
+    pldm::oem_ampere::OemAMPERE oemAMPERE(
+        &dbusHandler, pldmTransport.getEventSource(), pdrRepo.get(),
+        instanceIdDb, event, invoker, hostPDRHandler.get(),
+        platformHandler.get(), fruHandler.get(), baseHandler.get(),
+        biosHandler.get(), platformManager.get(), &reqHandler);
+#endif
+
 #ifdef OEM_IBM
     pldm::oem_ibm::OemIBM oemIBM(
         &dbusHandler, pldmTransport.getEventSource(), hostEID, pdrRepo.get(),