Add CoreBiosDone signal support

Add support to monitor CoreBiosDone and filter IPMI commands accordingly.
All IPMI commands will be allowed over system interface channels
until CoreBiosDone or postCompleted signal is received.

Tested:
1. Verified no IPMI commands from system interface are getting filtered
   until CoreBiosDone or postCompleted signal is received.
2. Verified different channel restriction modes are working as
   expected after CoreBiosDone.
3. Verified the same by executing IPMI commands(having different channel
   privilages) over system interface under different restriction modes.

Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com>
Change-Id: I9449b1e8b475f13af7175fe86b43ae50976a3b6b
diff --git a/src/whitelist-filter.cpp b/src/whitelist-filter.cpp
index f8c349d..3f30a41 100644
--- a/src/whitelist-filter.cpp
+++ b/src/whitelist-filter.cpp
@@ -42,6 +42,8 @@
     void updatePostComplete(const std::string& value);
     void updateRestrictionMode(const std::string& value);
     ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
+    void handleCoreBiosDoneChange(sdbusplus::message::message& m);
+    void cacheCoreBiosDone();
 
     // the BMC KCS Policy Control Modes document uses different names
     // than the RestrictionModes D-Bus interface; use aliases
@@ -53,18 +55,23 @@
         RestrictionMode::Modes::ProvisionedHostDisabled;
 
     RestrictionMode::Modes restrictionMode = restrictionModeRestricted;
-    bool postCompleted = false;
+    bool postCompleted = true;
+    bool coreBIOSDone = true;
     int channelSMM = -1;
     std::shared_ptr<sdbusplus::asio::connection> bus;
     std::unique_ptr<sdbusplus::bus::match::match> modeChangeMatch;
     std::unique_ptr<sdbusplus::bus::match::match> modeIntfAddedMatch;
     std::unique_ptr<sdbusplus::bus::match::match> postCompleteMatch;
     std::unique_ptr<sdbusplus::bus::match::match> postCompleteIntfAddedMatch;
+    std::unique_ptr<sdbusplus::bus::match::match> platStateChangeMatch;
+    std::unique_ptr<sdbusplus::bus::match::match> platStateIntfAddedMatch;
 
     static constexpr const char restrictionModeIntf[] =
         "xyz.openbmc_project.Control.Security.RestrictionMode";
     static constexpr const char* systemOsStatusIntf =
         "xyz.openbmc_project.State.OperatingSystem.Status";
+    static constexpr const char* hostMiscIntf =
+        "xyz.openbmc_project.State.Host.Misc";
 };
 
 static inline uint8_t getSMMChannel()
@@ -272,6 +279,85 @@
         updatePostComplete(std::get<std::string>(itr->second));
     }
 }
+
+void WhitelistFilter::cacheCoreBiosDone()
+{
+    std::string coreBiosDonePath;
+    std::string coreBiosDoneService;
+    try
+    {
+        ipmi::DbusObjectInfo coreBiosDoneObj =
+            ipmi::getDbusObject(*bus, hostMiscIntf);
+
+        coreBiosDonePath = coreBiosDoneObj.first;
+        coreBiosDoneService = coreBiosDoneObj.second;
+    }
+    catch (const std::exception&)
+    {
+        log<level::ERR>("Could not initialize CoreBiosDone, "
+                        "coreBIOSDone asserted as default");
+        return;
+    }
+
+    bus->async_method_call(
+        [this](boost::system::error_code ec, const ipmi::Value& v) {
+            if (ec)
+            {
+                log<level::ERR>(
+                    "async call failed, coreBIOSDone asserted as default");
+                return;
+            }
+            coreBIOSDone = std::get<bool>(v);
+            log<level::INFO>("Read CoreBiosDone",
+                             entry("VALUE=%d", static_cast<int>(coreBIOSDone)));
+        },
+        coreBiosDoneService, coreBiosDonePath,
+        "org.freedesktop.DBus.Properties", "Get", hostMiscIntf, "CoreBiosDone");
+}
+
+void WhitelistFilter::handleCoreBiosDoneChange(sdbusplus::message::message& msg)
+{
+    std::string signal = msg.get_member();
+    if (signal == "PropertiesChanged")
+    {
+        std::string intf;
+        std::vector<std::pair<std::string, ipmi::Value>> propertyList;
+        msg.read(intf, propertyList);
+        auto it =
+            std::find_if(propertyList.begin(), propertyList.end(),
+                         [](const std::pair<std::string, ipmi::Value>& prop) {
+                             return prop.first == "CoreBiosDone";
+                         });
+
+        if (it != propertyList.end())
+        {
+            coreBIOSDone = std::get<bool>(it->second);
+            log<level::INFO>(coreBIOSDone ? "coreBIOSDone asserted"
+                                          : "coreBIOSDone not asserted");
+        }
+    }
+    else if (signal == "InterfacesAdded")
+    {
+        sdbusplus::message::object_path path;
+        DbusInterfaceMap eSpiresetObj;
+        msg.read(path, eSpiresetObj);
+        auto intfItr = eSpiresetObj.find(hostMiscIntf);
+        if (intfItr == eSpiresetObj.end())
+        {
+            return;
+        }
+        PropertyMap& propertyList = intfItr->second;
+        auto itr = propertyList.find("CoreBiosDone");
+        if (itr == propertyList.end())
+        {
+            return;
+        }
+        coreBIOSDone = std::get<bool>(itr->second);
+        log<level::INFO>(coreBIOSDone ? "coreBIOSDone asserted"
+                                      : "coreBIOSDone not asserted");
+    }
+}
+
 void WhitelistFilter::postInit()
 {
     // Wait for changes on Restricted mode
@@ -295,6 +381,15 @@
         rules::interfacesAdded() +
         rules::argNpath(0, "/xyz/openbmc_project/state/os");
 
+    const std::string filterStrPlatStateChange =
+        rules::type::signal() + rules::member("PropertiesChanged") +
+        rules::interface("org.freedesktop.DBus.Properties") +
+        rules::argN(0, hostMiscIntf);
+
+    const std::string filterStrPlatStateIntfAdd =
+        rules::interfacesAdded() +
+        rules::argNpath(0, "/xyz/openbmc_project/misc/platform_state");
+
     modeChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
         *bus, filterStrModeChange, [this](sdbusplus::message::message& m) {
             handleRestrictedModeChange(m);
@@ -314,8 +409,21 @@
             handlePostCompleteChange(m);
         });
 
+    platStateChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrPlatStateChange, [this](sdbusplus::message::message& m) {
+            handleCoreBiosDoneChange(m);
+        });
+
+    platStateIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrPlatStateIntfAdd,
+        [this](sdbusplus::message::message& m) {
+            handleCoreBiosDoneChange(m);
+        });
+
     // Initialize restricted mode
     cacheRestrictedAndPostCompleteMode();
+    // Initialize CoreBiosDone
+    cacheCoreBiosDone();
 }
 
 ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
@@ -353,9 +461,9 @@
     //                  ( whitelist ? ccSuccess : // ccCommandNotAvailable )
     // Deny All:   preboot ? ccSuccess : ccCommandNotAvailable
 
-    if (!postCompleted)
+    if (!(postCompleted || coreBIOSDone))
     {
-        // Allow all commands, till POST is not completed
+        // Allow all commands, till POST or CoreBiosDone is completed
         return ipmi::ccSuccess;
     }