nvmesensor: add Admin Non Data Command passthru DBus interface

We need to add a general non data Admin command DBus interface to
support all kinds of Non Data Admin commands including Vendor Uniqure
commands.

Tested:
https://paste.googleplex.com/5595446155149312

Google-Bug-Id: 296467492
Change-Id: I6f0d60c2fce0200b3545e77a6b2db0e7b0313c20
Signed-off-by: Jinliang Wang <jinliangw@google.com>
diff --git a/recipes-phosphor/sensors/dbus-sensors/0052-nvmesensor-add-Admin-non-data-command-passthru-DBus-.patch b/recipes-phosphor/sensors/dbus-sensors/0052-nvmesensor-add-Admin-non-data-command-passthru-DBus-.patch
new file mode 100644
index 0000000..e071095
--- /dev/null
+++ b/recipes-phosphor/sensors/dbus-sensors/0052-nvmesensor-add-Admin-non-data-command-passthru-DBus-.patch
@@ -0,0 +1,229 @@
+From 95a3e7de6ad14af09e87f8b0cbbd68b45d926fbf Mon Sep 17 00:00:00 2001
+From: Jinliang Wang <jinliangw@google.com>
+Date: Wed, 30 Aug 2023 15:39:30 -0700
+Subject: [PATCH] nvmesensor: add Admin non data command passthru DBus
+ interface
+
+We need to add a general non data Admin command DBus interface to
+support all kinds of Non Data Admin commands including Vendor Uniqure
+commands.
+
+Interface: xyz.openbmc_project.NVMe.Passthru
+Method: AdminNondataCmd
+    Method need 10 parameters (yuuuuuuuuu): uint8_t opcode, uint32_t cdw1, uint32_t cdw2, uint32_t cdw3,
+                              uint32_t cdw10, uint32_t cdw11, uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15
+    Return (uuu) mi_status, nvme_status, and completion_dw0
+
+Tested:
+busctl introspect xyz.openbmc_project.NVMe /xyz/openbmc_project/inventory/system/board/IronFist_1/IronFist_1_NVMe_1/controllers/0 xyz.openbmc_project.NVMe.Passthru
+NAME                              TYPE      SIGNATURE  RESULT/VALUE FLAGS
+.AdminNondataCmd                  method    yuuuuuuuuu (uuu)        -
+
+GetFeature Number of Queues  (opcode = 0x0A, cdw10 = 0x7)
+root@iacwn11-nfd01:~# busctl call xyz.openbmc_project.NVMe\
+>   /xyz/openbmc_project/inventory/system/board/IronFist_1/IronFist_1_NVMe_1/controllers/0 \
+>   xyz.openbmc_project.NVMe.Passthru AdminNondataCmd \
+>   yuuuuuuuuu 10 0 0 0 7 0 0 0 0 0
+(uuu) 0 0 917518
+
+Patch Tracking Bug: b/298484993
+Upstream info / review: N/A
+Upstream-Status: Pending
+Justification: enable feature
+
+Change-Id: Ibb25ffa0c4bb3c4f675dc9a027b5f86b8022fc11
+Signed-off-by: Jinliang Wang <jinliangw@google.com>
+---
+ src/NVMeController.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++
+ src/NVMeController.hpp |  6 ++++
+ src/NVMeIntf.hpp       |  8 ++++-
+ src/NVMeMi.cpp         | 30 +++++++++++++++++++
+ src/NVMeMi.hpp         |  7 +++++
+ 5 files changed, 118 insertions(+), 1 deletion(-)
+
+diff --git a/src/NVMeController.cpp b/src/NVMeController.cpp
+index 215b2e4..6d96417 100644
+--- a/src/NVMeController.cpp
++++ b/src/NVMeController.cpp
+@@ -81,6 +81,26 @@ void NVMeControllerEnabled::init()
+     assocIntf->register_property("Associations", associations);
+     assocIntf->initialize();
+ 
++    passthruInterface =
++        objServer.add_interface(path, "xyz.openbmc_project.NVMe.Passthru");
++
++    passthruInterface->register_method(
++        "AdminNonDataCmd",
++        [selfWeak{weak_from_this()}](
++            boost::asio::yield_context yield, uint8_t opcode, uint32_t cdw1,
++            uint32_t cdw2, uint32_t cdw3, uint32_t cdw10, uint32_t cdw11,
++            uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15) {
++        if (selfWeak.expired())
++        {
++            checkLibNVMeError(std::make_error_code(std::errc::no_such_device),
++                              -1, "AdminNonDataCmd");
++            return std::tuple<uint32_t, uint32_t, uint32_t>{0, 0, 0};
++        }
++        return selfWeak.lock()->adminNonDataCmdMethod(yield, opcode, cdw1, cdw2,
++                                                      cdw3, cdw10, cdw11, cdw12,
++                                                      cdw13, cdw14, cdw15);
++    });
++    passthruInterface->initialize();
+ 
+     securityInterface = objServer.add_interface(
+         path, "xyz.openbmc_project.Inventory.Item.StorageControllerSecurity");
+@@ -459,6 +479,54 @@ std::vector<uint8_t> NVMeControllerEnabled::securityReceiveMethod(
+     return data;
+ }
+ 
++std::tuple<uint32_t, uint32_t, uint32_t>
++    NVMeControllerEnabled::adminNonDataCmdMethod(
++        boost::asio::yield_context yield, uint8_t opcode, uint32_t cdw1,
++        uint32_t cdw2, uint32_t cdw3, uint32_t cdw10, uint32_t cdw11,
++        uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15)
++{
++    using callback_t = void(std::tuple<std::error_code, int, uint32_t>);
++    auto [err, nvme_status, completion_dw0] =
++        boost::asio::async_initiate<boost::asio::yield_context, callback_t>(
++            [intf{nvmeIntf}, ctrl{nvmeCtrl}, opcode, cdw1, cdw2, cdw3, cdw10,
++             cdw11, cdw12, cdw13, cdw14, cdw15](auto&& handler) {
++        auto h = asio_helper::CopyableCallback(std::move(handler));
++
++        intf->adminNonDataCmd(ctrl, opcode, cdw1, cdw2, cdw3, cdw10, cdw11,
++                              cdw12, cdw13, cdw14, cdw15,
++                              [h](const std::error_code& err, int nvme_status,
++                                  uint32_t completion_dw0) mutable {
++            h(std::make_tuple(err, nvme_status, completion_dw0));
++        });
++    },
++            yield);
++
++    std::cerr << "nvme_status:" << nvme_status << ", dw0:" << completion_dw0
++              << std::endl;
++    if (nvme_status < 0)
++    {
++        throw sdbusplus::exception::SdBusError(err.value(),
++                                               "adminNonDataCmdMethod");
++    }
++
++    // Parse MI status Or MI status from nvme_status
++    uint32_t mi_status = 0;
++    uint32_t admin_status = 0;
++    if (nvme_status_get_type(nvme_status) == NVME_STATUS_TYPE_MI)
++    {
++        // there is no Admin status and dw0 if MI layer failed.
++        mi_status = nvme_status_get_value(nvme_status);
++        admin_status = 0;
++        completion_dw0 = 0;
++    }
++    else
++    {
++        mi_status = 0;
++        admin_status = nvme_status_get_value(nvme_status);
++    }
++    return {mi_status, admin_status, completion_dw0};
++}
++
+ class NVMeSdBusPlusError : public sdbusplus::exception::exception
+ {
+ 
+diff --git a/src/NVMeController.hpp b/src/NVMeController.hpp
+index fc798dc..a3522b2 100644
+--- a/src/NVMeController.hpp
++++ b/src/NVMeController.hpp
+@@ -75,6 +75,7 @@ class NVMeController
+     std::string path;
+ 
+     std::shared_ptr<sdbusplus::asio::dbus_interface> securityInterface;
++    std::shared_ptr<sdbusplus::asio::dbus_interface> passthruInterface;
+ 
+     std::shared_ptr<NVMeMiIntf> nvmeIntf;
+     nvme_mi_ctrl_t nvmeCtrl;
+@@ -200,4 +201,9 @@ class NVMeControllerEnabled :
+                                                uint16_t proto_specific,
+                                                uint32_t transfer_length);
+ 
++    std::tuple<uint32_t, uint32_t, uint32_t>
++        adminNonDataCmdMethod(boost::asio::yield_context yield, uint8_t opcode,
++                              uint32_t cdw1, uint32_t cdw2, uint32_t cdw3,
++                              uint32_t cdw10, uint32_t cdw11, uint32_t cdw12,
++                              uint32_t cdw13, uint32_t cdw14, uint32_t cdw15);
+ };
+diff --git a/src/NVMeIntf.hpp b/src/NVMeIntf.hpp
+index 7917748..20ffda7 100644
+--- a/src/NVMeIntf.hpp
++++ b/src/NVMeIntf.hpp
+@@ -198,7 +198,13 @@ class NVMeMiIntf
+         uint32_t transfer_length,
+         std::function<void(const std::error_code&, int nvme_status,
+                            const std::span<uint8_t> data)>&& cb) = 0;
+-
++    
++    virtual void adminNonDataCmd(
++        nvme_mi_ctrl_t ctrl, uint8_t opcode, uint32_t cdw1, uint32_t cdw2,
++        uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, uint32_t cdw12,
++        uint32_t cdw13, uint32_t cdw14, uint32_t cdw15,
++        std::function<void(const std::error_code&, int nvme_status,
++                           uint32_t comption_dw0)>&& cb) = 0;
+     /**
+      * adminXfer() -  Raw admin transfer interface.
+      * @ctrl: controller to send the admin command to
+diff --git a/src/NVMeMi.cpp b/src/NVMeMi.cpp
+index 74d6e27..a8b56ed 100644
+--- a/src/NVMeMi.cpp
++++ b/src/NVMeMi.cpp
+@@ -1255,3 +1255,33 @@ void NVMeMi::adminSecurityReceive(
+         io.post([cb{std::move(cb)}, post_err]() { cb(post_err, -1, {}); });
+     }
+ }
++
++void NVMeMi::adminNonDataCmd(
++    nvme_mi_ctrl_t ctrl, uint8_t opcode, uint32_t cdw1, uint32_t cdw2,
++    uint32_t cdw3, uint32_t cdw10, uint32_t cdw11,
++    uint32_t cdw12, uint32_t cdw13, uint32_t cdw14, uint32_t cdw15,
++    std::function<void(const std::error_code&, int nvme_status,
++                       uint32_t comption_dw0)>&& cb)
++{
++    std::error_code post_err =
++        try_post([self{shared_from_this()}, ctrl, opcode, cdw1, cdw2, cdw3,
++             cdw10, cdw11, cdw12, cdw13, cdw14, cdw15, cb{std::move(cb)}]() {
++        uint32_t comption_dw0 = 0;
++        int nvme_status = nvme_mi_admin_admin_passthru(
++            ctrl, opcode, 0, 0, cdw1, cdw2, cdw3, cdw10, cdw11, cdw12, cdw13,
++            cdw14, cdw15, 0, nullptr, 0, nullptr, 10*1000, &comption_dw0);
++        self->io.post(
++            [cb{std::move(cb)}, nvme_errno{errno}, nvme_status, comption_dw0]() mutable {
++            auto err = std::make_error_code(static_cast<std::errc>(nvme_errno));
++            cb(err, nvme_status, comption_dw0);
++        });
++    });
++    if (post_err)
++    {
++        std::cerr << "[bus: " << bus << ", addr: " << addr
++                  << ", eid: " << static_cast<int>(eid) << "]"
++                  << "adminNonDataCmd post failed: " << post_err
++                  << std::endl;
++        io.post([cb{std::move(cb)}, post_err]() { cb(post_err, -1, 0); });
++    }
++}
+diff --git a/src/NVMeMi.hpp b/src/NVMeMi.hpp
+index 054382f..59b48d4 100644
+--- a/src/NVMeMi.hpp
++++ b/src/NVMeMi.hpp
+@@ -63,6 +63,13 @@ class NVMeMi : public NVMeMiIntf, public std::enable_shared_from_this<NVMeMi>
+         std::function<void(const std::error_code&, int nvme_status,
+                            std::span<uint8_t> data)>&& cb) override;
+ 
++    void adminNonDataCmd(
++        nvme_mi_ctrl_t ctrl, uint8_t opcode, uint32_t cdw1, uint32_t cdw2,
++        uint32_t cdw3, uint32_t cdw10, uint32_t cdw11, uint32_t cdw12,
++        uint32_t cdw13, uint32_t cdw14, uint32_t cdw15,
++        std::function<void(const std::error_code&, int nvme_status,
++                           uint32_t comption_dw0)>&& cb);
++
+   private:
+     // the transfer size for nvme mi messages.
+     // define in github.com/linux-nvme/libnvme/blob/master/src/nvme/mi.c
+-- 
+2.42.0.283.g2d96d420d3-goog
+
diff --git a/recipes-phosphor/sensors/dbus-sensors_%.bbappend b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
index 451bc10..b58e0a4 100644
--- a/recipes-phosphor/sensors/dbus-sensors_%.bbappend
+++ b/recipes-phosphor/sensors/dbus-sensors_%.bbappend
@@ -68,6 +68,7 @@
   file://0049-nvmesensor-Limit-logPage-07-to-Area-1-and-2.patch \
   file://0050-nvmesensor-Add-FirmwareDownload-for-NVMe-devices.patch \
   file://0051-nvmesensor-remove-duplicate-chassis-prefix-in-sensor.patch \
+  file://0052-nvmesensor-add-Admin-non-data-command-passthru-DBus-.patch \
 "
 PACKAGECONFIG[nvmesensor] = "-Dnvme=enabled, -Dnvme=disabled, libnvme"
 SYSTEMD_SERVICE:${PN} += "${@bb.utils.contains('PACKAGECONFIG', 'nvmesensor', \