Implement set chassis capabilities IPMI commands

Create a new dbus interface for the chassis capabilities, and
store the chassis capabilities using phosphor settings manager

Tested By:
ipmitool -H $BMCIP  -U root -P 0penBmc -I lanplus raw 0 5 0 2 4 6 8 0xa
ipmitool -H $BMCIP  -U root -P 0penBmc -I lanplus raw 0 0

Change-Id: Id01555358adfec3814fd03b3e6d422916c861039
Signed-off-by: Yong Li <yong.b.li@linux.intel.com>
diff --git a/chassishandler.cpp b/chassishandler.cpp
index a58eee0..d4ce0e4 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -103,6 +103,16 @@
     "xyz.openbmc_project.State.PowerOnHours";
 static constexpr auto pOHCounterProperty = "POHCounter";
 static constexpr auto match = "chassis0";
+const static constexpr char chassisCapIntf[] =
+    "xyz.openbmc_project.Control.ChassisCapabilities";
+const static constexpr char chassisCapFlagsProp[] = "CapabilitiesFlags";
+const static constexpr char chassisFRUDevAddrProp[] = "FRUDeviceAddress";
+const static constexpr char chassisSDRDevAddrProp[] = "SDRDeviceAddress";
+const static constexpr char chassisSELDevAddrProp[] = "SELDeviceAddress";
+const static constexpr char chassisSMDevAddrProp[] = "SMDeviceAddress";
+const static constexpr char chassisBridgeDevAddrProp[] = "BridgeDeviceAddress";
+static constexpr uint8_t chassisCapFlagMask = 0x0f;
+static constexpr uint8_t chassisCapAddrMask = 0xfe;
 
 typedef struct
 {
@@ -666,41 +676,184 @@
 
     ipmi_chassis_cap_t chassis_cap{};
 
+    if (*data_len != 0)
+    {
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
     *data_len = sizeof(ipmi_chassis_cap_t);
 
-    // TODO: need future work. Get those flag from MRW.
+    try
+    {
+        sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
 
-    // capabilities flags
-    // [7..4] - reserved
-    // [3] – 1b = provides power interlock  (IPM 1.5)
-    // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
-    // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis has
-    // capabilities
-    //            to lock out external power control and reset button or front
-    //            panel interfaces and/or detect tampering with those
-    //            interfaces).
-    // [0] -1b = Chassis provides intrusion (physical security) sensor.
-    // set to default value 0x0.
-    chassis_cap.cap_flags = 0x0;
+        ipmi::DbusObjectInfo chassisCapObject =
+            ipmi::getDbusObject(bus, chassisCapIntf);
 
-    // Since we do not have a separate SDR Device/SEL Device/ FRU repository.
-    // The 20h was given as those 5 device addresses.
-    // Chassis FRU info Device Address
-    chassis_cap.fru_info_dev_addr = 0x20;
+        // capabilities flags
+        // [7..4] - reserved
+        // [3] – 1b = provides power interlock  (IPM 1.5)
+        // [2] – 1b = provides Diagnostic Interrupt (FP NMI)
+        // [1] – 1b = provides “Front Panel Lockout” (indicates that the chassis
+        // has capabilities
+        //            to lock out external power control and reset button or
+        //            front panel interfaces and/or detect tampering with those
+        //            interfaces).
+        // [0] -1b = Chassis provides intrusion (physical security) sensor.
+        // set to default value 0x0.
+        ipmi::Value variant = ipmi::getDbusProperty(
+            bus, chassisCapObject.second, chassisCapObject.first,
+            chassisCapIntf, chassisCapFlagsProp);
+        chassis_cap.cap_flags = variant_ns::get<uint8_t>(variant);
 
-    // Chassis SDR Device Address
-    chassis_cap.sdr_dev_addr = 0x20;
+        variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
+                                        chassisCapObject.first, chassisCapIntf,
+                                        chassisFRUDevAddrProp);
+        // Chassis FRU info Device Address.
+        chassis_cap.fru_info_dev_addr = variant_ns::get<uint8_t>(variant);
 
-    // Chassis SEL Device Address
-    chassis_cap.sel_dev_addr = 0x20;
+        variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
+                                        chassisCapObject.first, chassisCapIntf,
+                                        chassisSDRDevAddrProp);
+        // Chassis SDR Device Address.
+        chassis_cap.sdr_dev_addr = variant_ns::get<uint8_t>(variant);
 
-    // Chassis System Management Device Address
-    chassis_cap.system_management_dev_addr = 0x20;
+        variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
+                                        chassisCapObject.first, chassisCapIntf,
+                                        chassisSELDevAddrProp);
+        // Chassis SEL Device Address.
+        chassis_cap.sel_dev_addr = variant_ns::get<uint8_t>(variant);
 
-    // Chassis Bridge Device Address.
-    chassis_cap.bridge_dev_addr = 0x20;
+        variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
+                                        chassisCapObject.first, chassisCapIntf,
+                                        chassisSMDevAddrProp);
+        // Chassis System Management Device Address.
+        chassis_cap.system_management_dev_addr =
+            variant_ns::get<uint8_t>(variant);
 
-    std::memcpy(response, &chassis_cap, *data_len);
+        variant = ipmi::getDbusProperty(bus, chassisCapObject.second,
+                                        chassisCapObject.first, chassisCapIntf,
+                                        chassisBridgeDevAddrProp);
+        // Chassis Bridge Device Address.
+        chassis_cap.bridge_dev_addr = variant_ns::get<uint8_t>(variant);
+        uint8_t* respP = reinterpret_cast<uint8_t*>(response);
+        uint8_t* chassisP = reinterpret_cast<uint8_t*>(&chassis_cap);
+        std::copy(chassisP, chassisP + *data_len, respP);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+        *data_len = 0;
+        return rc;
+    }
+
+    return rc;
+}
+
+ipmi_ret_t ipmi_set_chassis_cap(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
+                                ipmi_request_t request,
+                                ipmi_response_t response,
+                                ipmi_data_len_t data_len,
+                                ipmi_context_t context)
+{
+    ipmi_ret_t rc = IPMI_CC_OK;
+
+    if (*data_len != sizeof(ipmi_chassis_cap_t))
+    {
+        log<level::ERR>("Unsupported request length",
+                        entry("LEN=0x%x", *data_len));
+        *data_len = 0;
+        return IPMI_CC_REQ_DATA_LEN_INVALID;
+    }
+
+    ipmi_chassis_cap_t* chassisCap = static_cast<ipmi_chassis_cap_t*>(request);
+
+    *data_len = 0;
+
+    // check input data
+    if (0 != (chassisCap->cap_flags & ~chassisCapFlagMask))
+    {
+        log<level::ERR>("Unsupported request parameter(CAP Flags)",
+                        entry("REQ=0x%x", chassisCap->cap_flags));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (0 != (chassisCap->fru_info_dev_addr & ~chassisCapAddrMask))
+    {
+        log<level::ERR>("Unsupported request parameter(FRU Addr)",
+                        entry("REQ=0x%x", chassisCap->fru_info_dev_addr));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (0 != (chassisCap->sdr_dev_addr & ~chassisCapAddrMask))
+    {
+        log<level::ERR>("Unsupported request parameter(SDR Addr)",
+                        entry("REQ=0x%x", chassisCap->sdr_dev_addr));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (0 != (chassisCap->sel_dev_addr & ~chassisCapAddrMask))
+    {
+        log<level::ERR>("Unsupported request parameter(SEL Addr)",
+                        entry("REQ=0x%x", chassisCap->sel_dev_addr));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (0 != (chassisCap->system_management_dev_addr & ~chassisCapAddrMask))
+    {
+        log<level::ERR>(
+            "Unsupported request parameter(SM Addr)",
+            entry("REQ=0x%x", chassisCap->system_management_dev_addr));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    if (0 != (chassisCap->bridge_dev_addr & ~chassisCapAddrMask))
+    {
+        log<level::ERR>("Unsupported request parameter(Bridge Addr)",
+                        entry("REQ=0x%x", chassisCap->bridge_dev_addr));
+        return IPMI_CC_INVALID_FIELD_REQUEST;
+    }
+
+    try
+    {
+        sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
+        ipmi::DbusObjectInfo chassisCapObject =
+            ipmi::getDbusObject(bus, chassisCapIntf);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisCapFlagsProp, chassisCap->cap_flags);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisFRUDevAddrProp,
+                              chassisCap->fru_info_dev_addr);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisSDRDevAddrProp, chassisCap->sdr_dev_addr);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisSELDevAddrProp, chassisCap->sel_dev_addr);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisSMDevAddrProp,
+                              chassisCap->system_management_dev_addr);
+
+        ipmi::setDbusProperty(bus, chassisCapObject.second,
+                              chassisCapObject.first, chassisCapIntf,
+                              chassisBridgeDevAddrProp,
+                              chassisCap->bridge_dev_addr);
+    }
+    catch (std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        rc = IPMI_CC_UNSPECIFIED_ERROR;
+        return rc;
+    }
 
     return rc;
 }
@@ -1714,6 +1867,10 @@
     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_CHASSIS_CAP, NULL,
                            ipmi_get_chassis_cap, PRIVILEGE_USER);
 
+    // Set Chassis Capabilities
+    ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_SET_CHASSIS_CAP, NULL,
+                           ipmi_set_chassis_cap, PRIVILEGE_USER);
+
     // <Get System Boot Options>
     ipmi_register_callback(NETFUN_CHASSIS, IPMI_CMD_GET_SYS_BOOT_OPTIONS, NULL,
                            ipmi_chassis_get_sys_boot_options,