host: Added support to delete system dump

To delete host system dump using pldm oem command "FileAck" with
dump file type and dump id which is present as source dump id (which
is got from pldm after created the host system dump) in system dump entry
dbus object and also, to use pldm oem command added support to indicate
delete host system dump support is not available for other host transport
consumer and for other oem vendors who are using pldm as host transport
to get host dump.

Also, changed existing commited error i.e internal failure into not allowed
for host system dump offload to avoid bmc dump creation in dump path.

Signed-off-by: Ramesh Iyyar <rameshi1@in.ibm.com>
Change-Id: Ib6be5b521fb3c5aa2926497bae0b03c1d9737161
diff --git a/dump-extensions/openpower-dumps/system_dump_entry.cpp b/dump-extensions/openpower-dumps/system_dump_entry.cpp
index 83723b4..eed76bd 100644
--- a/dump-extensions/openpower-dumps/system_dump_entry.cpp
+++ b/dump-extensions/openpower-dumps/system_dump_entry.cpp
@@ -1,5 +1,6 @@
 #include "system_dump_entry.hpp"
 
+#include "dump_utils.hpp"
 #include "host_transport_exts.hpp"
 
 namespace phosphor
@@ -15,6 +16,23 @@
     phosphor::dump::host::requestOffload(sourceDumpId());
 }
 
+void Entry::delete_()
+{
+    auto srcDumpID = sourceDumpId();
+
+    // Remove Dump entry D-bus object
+    phosphor::dump::Entry::delete_();
+
+    // Remove host system dump when host is up by using source dump id
+    // which is present in system dump entry dbus object as a property.
+    BootProgress bootProgressStatus = phosphor::dump::getBootProgress();
+    if ((bootProgressStatus == BootProgress::SystemInitComplete) ||
+        (bootProgressStatus == BootProgress::OSStart) ||
+        (bootProgressStatus == BootProgress::OSRunning))
+    {
+        phosphor::dump::host::requestDelete(srcDumpID);
+    }
+}
 } // namespace system
 } // namespace dump
 } // namespace phosphor
diff --git a/dump-extensions/openpower-dumps/system_dump_entry.hpp b/dump-extensions/openpower-dumps/system_dump_entry.hpp
index 52d857a..96d5f9a 100644
--- a/dump-extensions/openpower-dumps/system_dump_entry.hpp
+++ b/dump-extensions/openpower-dumps/system_dump_entry.hpp
@@ -79,6 +79,11 @@
         status(OperationStatus::Completed);
         completedTime(timeStamp);
     }
+
+    /**
+     * @brief Delete host system dump and it entry dbus object
+     */
+    void delete_() override;
 };
 
 } // namespace system
diff --git a/dump_utils.cpp b/dump_utils.cpp
index b013706..30a8b47 100644
--- a/dump_utils.cpp
+++ b/dump_utils.cpp
@@ -7,12 +7,13 @@
 namespace dump
 {
 
+using namespace phosphor::logging;
+
 std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
                        const std::string& interface)
 {
     constexpr auto objectMapperName = "xyz.openbmc_project.ObjectMapper";
     constexpr auto objectMapperPath = "/xyz/openbmc_project/object_mapper";
-    using namespace phosphor::logging;
 
     auto method = bus.new_method_call(objectMapperName, objectMapperPath,
                                       objectMapperName, "GetObject");
@@ -45,5 +46,60 @@
     return response[0].first;
 }
 
+BootProgress getBootProgress()
+{
+    constexpr auto bootProgressInterface =
+        "xyz.openbmc_project.State.Boot.Progress";
+    // TODO Need to change host instance if multiple instead "0"
+    constexpr auto hostStateObjPath = "/xyz/openbmc_project/state/host0";
+
+    BootProgress bootProgessStage;
+
+    try
+    {
+        auto bus = sdbusplus::bus::new_default();
+        auto service = getService(bus, hostStateObjPath, bootProgressInterface);
+
+        auto method =
+            bus.new_method_call(service.c_str(), hostStateObjPath,
+                                "org.freedesktop.DBus.Properties", "Get");
+
+        method.append(bootProgressInterface, "BootProgress");
+
+        auto reply = bus.call(method);
+
+        using DBusValue_t =
+            std::variant<std::string, bool, std::vector<uint8_t>,
+                         std::vector<std::string>>;
+        DBusValue_t propertyVal;
+
+        reply.read(propertyVal);
+
+        // BootProgress property type is string
+        std::string bootPgs(std::get<std::string>(propertyVal));
+
+        bootProgessStage = sdbusplus::xyz::openbmc_project::State::Boot::
+            server::Progress::convertProgressStagesFromString(bootPgs);
+    }
+    catch (const sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("D-Bus call exception",
+                        entry("OBJPATH=%s", hostStateObjPath),
+                        entry("INTERFACE=%s", bootProgressInterface),
+                        entry("EXCEPTION=%s", e.what()));
+        throw std::runtime_error("Failed to get BootProgress stage");
+    }
+    catch (const std::bad_variant_access& e)
+    {
+        log<level::ERR>(
+            "Exception raised while read BootProgress property value",
+            entry("OBJPATH=%s", hostStateObjPath),
+            entry("INTERFACE=%s", bootProgressInterface),
+            entry("EXCEPTION=%s", e.what()));
+        throw std::runtime_error("Failed to get BootProgress stage");
+    }
+
+    return bootProgessStage;
+}
 } // namespace dump
 } // namespace phosphor
diff --git a/dump_utils.hpp b/dump_utils.hpp
index a2c2604..5121bab 100644
--- a/dump_utils.hpp
+++ b/dump_utils.hpp
@@ -5,12 +5,16 @@
 
 #include <memory>
 #include <sdbusplus/bus.hpp>
+#include <xyz/openbmc_project/State/Boot/Progress/server.hpp>
 
 namespace phosphor
 {
 namespace dump
 {
 
+using BootProgress = sdbusplus::xyz::openbmc_project::State::Boot::server::
+    Progress::ProgressStages;
+
 /* Need a custom deleter for freeing up sd_event */
 struct EventDeleter
 {
@@ -71,5 +75,14 @@
 std::string getService(sdbusplus::bus::bus& bus, const std::string& path,
                        const std::string& interface);
 
+/**
+ * @brief Get the host boot progress stage
+ *
+ * @return BootProgress on success
+ *         Throw exception on failure
+ *
+ */
+BootProgress getBootProgress();
+
 } // namespace dump
 } // namespace phosphor
diff --git a/host-transport-extensions/default/default.cpp b/host-transport-extensions/default/default.cpp
index 9eb2bfe..bc49afc 100644
--- a/host-transport-extensions/default/default.cpp
+++ b/host-transport-extensions/default/default.cpp
@@ -12,6 +12,10 @@
     throw std::runtime_error("Hostdump offload method not specified");
 }
 
+void requestDelete(uint32_t)
+{
+    throw std::runtime_error("Hostdump delete method not specified");
+}
 } // namespace host
 } // namespace dump
 } // namespace phosphor
diff --git a/host-transport-extensions/pldm/default/pldm_interface.cpp b/host-transport-extensions/pldm/default/pldm_interface.cpp
index 57658af..7d7139d 100644
--- a/host-transport-extensions/pldm/default/pldm_interface.cpp
+++ b/host-transport-extensions/pldm/default/pldm_interface.cpp
@@ -16,6 +16,11 @@
 {
     throw std::runtime_error("PLDM: Hostdump offload method not specified");
 }
+
+void requestDelete(uint32_t)
+{
+    throw std::runtime_error("PLDM: Hostdump delete method not specified");
+}
 } // namespace host
 } // namespace dump
 } // namespace phosphor
diff --git a/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.cpp b/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.cpp
index adc9c48..a26a5d3 100644
--- a/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.cpp
+++ b/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.cpp
@@ -20,6 +20,7 @@
 #include "xyz/openbmc_project/Common/error.hpp"
 
 #include <libpldm/base.h>
+#include <libpldm/file_io.h>
 #include <libpldm/platform.h>
 #include <unistd.h>
 
@@ -44,6 +45,11 @@
 {
     pldm::requestOffload(id);
 }
+
+void requestDelete(uint32_t id)
+{
+    pldm::requestDelete(id);
+}
 } // namespace host
 
 namespace pldm
@@ -54,8 +60,8 @@
 constexpr auto eidPath = "/usr/share/pldm/host_eid";
 constexpr mctp_eid_t defaultEIDValue = 9;
 
-using InternalFailure =
-    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
+using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
 
 mctp_eid_t readEID()
 {
@@ -65,7 +71,8 @@
     if (!eidFile.good())
     {
         log<level::ERR>("Could not open host EID file");
-        elog<InternalFailure>();
+        elog<NotAllowed>(Reason("Required host dump action via pldm is not "
+                                "allowed due to mctp end point read failed"));
     }
     else
     {
@@ -78,7 +85,9 @@
         else
         {
             log<level::ERR>("EID file was empty");
-            elog<InternalFailure>();
+            elog<NotAllowed>(
+                Reason("Required host dump action via pldm is not "
+                       "allowed due to mctp end point read failed"));
         }
     }
 
@@ -110,7 +119,8 @@
     if (rc != PLDM_SUCCESS)
     {
         log<level::ERR>("Message encode failure. ", entry("RC=%d", rc));
-        elog<InternalFailure>();
+        elog<NotAllowed>(Reason("Host system dump offload via pldm is not "
+                                "allowed due to encode failed"));
     }
 
     uint8_t* responseMsg = nullptr;
@@ -125,7 +135,8 @@
         auto e = errno;
         log<level::ERR>("pldm_send failed", entry("RC=%d", rc),
                         entry("ERRNO=%d", e));
-        elog<InternalFailure>();
+        elog<NotAllowed>(Reason("Host system dump offload via pldm is not "
+                                "allowed due to fileack send failed"));
     }
     pldm_msg* response = reinterpret_cast<pldm_msg*>(responseMsg);
     log<level::INFO>(
@@ -133,6 +144,80 @@
         entry("RC=%d", static_cast<uint16_t>(response->payload[0])));
 }
 
+/*
+ * Using FileAck pldm command with file type as PLDM_FILE_TYPE_DUMP
+ * to delete host system dump
+ */
+void requestDelete(uint32_t dumpId)
+{
+    const size_t pldmMsgHdrSize = sizeof(pldm_msg_hdr);
+    std::array<uint8_t, pldmMsgHdrSize + PLDM_FILE_ACK_REQ_BYTES> fileAckReqMsg;
+
+    mctp_eid_t mctpEndPointId = readEID();
+
+    auto pldmInstanceId = getPLDMInstanceID(mctpEndPointId);
+
+    // - PLDM_FILE_TYPE_DUMP - To indicate FileAck for Host system dump
+    // - PLDM_SUCCESS - To indicate dump was readed (offloaded) or user decided,
+    //   no longer host system dump is not required so, initiate deletion from
+    //   host memory
+    int retCode = encode_file_ack_req(
+        pldmInstanceId, PLDM_FILE_TYPE_DUMP, dumpId, PLDM_SUCCESS,
+        reinterpret_cast<pldm_msg*>(fileAckReqMsg.data()));
+
+    if (retCode != PLDM_SUCCESS)
+    {
+        log<level::ERR>(
+            "Failed to encode pldm FileAck to delete host system dump",
+            entry("SRC_DUMP_ID=%d", dumpId),
+            entry("PLDM_RETURN_CODE=%d", retCode));
+        elog<NotAllowed>(Reason("Host system dump deletion via pldm is not "
+                                "allowed due to encode fileack failed"));
+    }
+
+    uint8_t* pldmRespMsg = nullptr;
+    size_t pldmRespMsgSize;
+
+    CustomFd pldmFd(openPLDM());
+
+    retCode =
+        pldm_send_recv(mctpEndPointId, pldmFd(), fileAckReqMsg.data(),
+                       fileAckReqMsg.size(), &pldmRespMsg, &pldmRespMsgSize);
+
+    std::unique_ptr<uint8_t, decltype(std::free)*> pldmRespMsgPtr{pldmRespMsg,
+                                                                  std::free};
+    if (retCode != PLDM_REQUESTER_SUCCESS)
+    {
+        auto errorNumber = errno;
+        log<level::ERR>(
+            "Failed to send pldm FileAck to delete host system dump",
+            entry("SRC_DUMP_ID=%d", dumpId),
+            entry("PLDM_RETURN_CODE=%d", retCode),
+            entry("ERRNO=%d", errorNumber),
+            entry("ERRMSG=%s", strerror(errorNumber)));
+        elog<NotAllowed>(Reason("Host system dump deletion via pldm is not "
+                                "allowed due to fileack send failed"));
+    }
+
+    uint8_t completionCode;
+
+    retCode =
+        decode_file_ack_resp(reinterpret_cast<pldm_msg*>(pldmRespMsgPtr.get()),
+                             pldmRespMsgSize - pldmMsgHdrSize, &completionCode);
+
+    if (retCode || completionCode)
+    {
+        log<level::ERR>("Failed to delete host system dump",
+                        entry("SRC_DUMP_ID=%d", dumpId),
+                        entry("PLDM_RETURN_CODE=%d", retCode),
+                        entry("PLDM_COMPLETION_CODE=%d", completionCode));
+        elog<NotAllowed>(Reason("Host system dump deletion via pldm is "
+                                "failed"));
+    }
+
+    log<level::INFO>("Deleted host system dump",
+                     entry("SRC_DUMP_ID=%d", dumpId));
+}
 } // namespace pldm
 } // namespace dump
 } // namespace phosphor
diff --git a/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.hpp b/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.hpp
index 9b2fdb3..1ab099a 100644
--- a/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.hpp
+++ b/host-transport-extensions/pldm/oem/ibm/pldm_oem_cmds.hpp
@@ -15,6 +15,15 @@
  *
  */
 void requestOffload(uint32_t id);
+
+/**
+ * @brief Request to delete dump
+ *
+ * @param[in] id - The Dump Source ID.
+ * @return NULL
+ *
+ */
+void requestDelete(uint32_t id);
 } // namespace host
 
 namespace pldm
@@ -43,6 +52,14 @@
  */
 mctp_eid_t readEID();
 
+/**
+ * @brief Request to delete dump
+ *
+ * @param[in] id - The Dump Source ID.
+ * @return NULL
+ *
+ */
+void requestDelete(uint32_t id);
 } // namespace pldm
 } // namespace dump
 } // namespace phosphor
diff --git a/host_transport_exts.hpp b/host_transport_exts.hpp
index 3a1ab9c..b31585e 100644
--- a/host_transport_exts.hpp
+++ b/host_transport_exts.hpp
@@ -12,6 +12,16 @@
  *
  */
 void requestOffload(uint32_t id);
+
+/**
+ * @brief Request to delete dump
+ *
+ * @param[in] id - The Dump Source ID.
+ * @return NULL
+ *
+ */
+void requestDelete(uint32_t id);
+
 } // namespace host
 } // namespace dump
 } // namespace phosphor