Platform event command

This command is used for logging SEL.

Tested:
1. test with netipmid and ipmid.
2. test pass with good parameter
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x80 0xc0 0x10 0xFF
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x00 0x30 0x10 0xab
3. test pass with bad parameter (expect invalid data length error)
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x00 0x30
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x00 0x30 0x11
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x00 0xc0
ipmitool raw 0x4 0x2 0x20 0x11 0x04 0x11 0x00

Change-Id: I7d51aac8fee2edb1faeb91f4c96a033736068779
Signed-off-by: Jia, Chunhui <chunhui.jia@linux.intel.com>
diff --git a/sensorhandler.cpp b/sensorhandler.cpp
index 3edf198..8a0d23c 100644
--- a/sensorhandler.cpp
+++ b/sensorhandler.cpp
@@ -869,12 +869,105 @@
     return ret;
 }
 
+static bool isFromSystemChannel()
+{
+    // TODO we could not figure out where the request is from based on IPMI
+    // command handler parameters. because of it, we can not differentiate
+    // request from SMS/SMM or IPMB channel
+    return true;
+}
+
+ipmi_ret_t ipmicmdPlatformEvent(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                ipmi_request_t request,
+                                ipmi_response_t response,
+                                ipmi_data_len_t dataLen, ipmi_context_t context)
+{
+    uint16_t generatorID;
+    size_t count;
+    bool assert = true;
+    std::string sensorPath;
+    size_t paraLen = *dataLen;
+    PlatformEventRequest* req;
+    *dataLen = 0;
+
+    if ((paraLen < selSystemEventSizeWith1Bytes) ||
+        (paraLen > selSystemEventSizeWith3Bytes))
+    {
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    if (isFromSystemChannel())
+    { // first byte for SYSTEM Interface is Generator ID
+        // +1 to get common struct
+        req = reinterpret_cast<PlatformEventRequest*>((uint8_t*)request + 1);
+        // Capture the generator ID
+        generatorID = *reinterpret_cast<uint8_t*>(request);
+        // Platform Event usually comes from other firmware, like BIOS.
+        // Unlike BMC sensor, it does not have BMC DBUS sensor path.
+        sensorPath = "System";
+    }
+    else
+    {
+        req = reinterpret_cast<PlatformEventRequest*>(request);
+        // TODO GenratorID for IPMB is combination of RqSA and RqLUN
+        generatorID = 0xff;
+        sensorPath = "IPMB";
+    }
+    // Content of event data field depends on sensor class.
+    // When data0 bit[5:4] is non-zero, valid data counts is 3.
+    // When data0 bit[7:6] is non-zero, valid data counts is 2.
+    if (((req->data[0] & byte3EnableMask) != 0 &&
+         paraLen < selSystemEventSizeWith3Bytes) ||
+        ((req->data[0] & byte2EnableMask) != 0 &&
+         paraLen < selSystemEventSizeWith2Bytes))
+    {
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    // Count bytes of Event Data
+    if ((req->data[0] & byte3EnableMask) != 0)
+    {
+        count = 3;
+    }
+    else if ((req->data[0] & byte2EnableMask) != 0)
+    {
+        count = 2;
+    }
+    else
+    {
+        count = 1;
+    }
+    assert = req->eventDirectionType & directionMask ? false : true;
+    std::vector<uint8_t> eventData(req->data, req->data + count);
+
+    sdbusplus::bus::bus dbus(bus);
+    std::string service =
+        ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath);
+    sdbusplus::message::message writeSEL = dbus.new_method_call(
+        service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd");
+    writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert,
+                    generatorID);
+    try
+    {
+        dbus.call(writeSEL);
+    }
+    catch (sdbusplus::exception_t& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    return IPMI_CC_OK;
+}
+
 void register_netfn_sen_functions()
 {
     // <Wildcard Command>
     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
                            ipmi_sen_wildcard, PRIVILEGE_USER);
 
+    // <Platform Event Message>
+    ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_PLATFORM_EVENT, nullptr,
+                           ipmicmdPlatformEvent, PRIVILEGE_OPERATOR);
     // <Get Sensor Type>
     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE, nullptr,
                            ipmi_sen_get_sensor_type, PRIVILEGE_USER);