sedutil: patch to support nvme-mi interface

We need sedutil to test and verify TCG feature of NVMe SSD in BMC.
In the meanwhile, we are tracking the patch on:
https://github.com/jinliangw/sedutil/tree/nvme-mi

Tested:
sedutil-cli --query mctp:1,16:0

mctp:1,16:0 NVMe MZWLR3T8HBLS-00AGG                       MPK9GP5Q S6HRNA0T517671
TPer function (0x0001)
    ACKNAK = N, ASYNC = N. BufferManagement = N, comIDManagement  = Y, Streaming = Y, SYNC = Y
Locking function (0x0002)
    Locked = N, LockingEnabled = N, LockingSupported = Y, MBRDone = N, MBREnabled = N, MediaEncrypt = Y
Geometry function (0x0003)
    Align = Y, Alignment Granularity = 9223372036854775807 (18446744073709551104), Logical Block size = 512, Lowest Aligned LBA = 0
SingleUser function (0x0201)
    ALL = N, ANY = N, Policy = Y, Locking Objects = 33
DataStore function (0x0202)
    Max Tables = 8, Max Size Tables = 10485760, Table size alignment = 1
OPAL 2.0 function (0x0203)
    Base comID = 0x1000, Initial PIN = 0x00, Reverted PIN = 0x00, comIDs = 2
    Locking Admins = 4, Locking Users = 33, Range Crossing = Y

TPer Properties:
  AckNak = 0  Asynchronous = 0
  ContinuedTokens = 0  SequenceNumbers = 0  DefSessionTimeout = 120000
  MaxAuthentications = 4  MaxComIDTime = 60000  MaxComPacketSize = 16384
  MaxIndTokenSize = 16328  MaxMethods = 1  MaxPackets = 1
  MaxPacketSize = 16364  MaxReadSessions = 8  MaxResponseComPacketSize = 16384
  MaxSessions = 9  MaxSessionTimeout = 0  MaxSubpackets = 1
  MaxTransactionLimit = 1  MinSessionTimeout = 60000
Host Properties:

  MaxComPacketSize = 2048  MaxIndTokenSize = 1992  MaxMethods = 1
  MaxPackets = 1  MaxPacketSize = 2028  MaxSubpackets = 1

Google-Bug-Id: 274977344
Signed-off-by: Jinliang Wang <jinliangw@google.com>
Change-Id: Ia85646beb85c41a8193d9c0712fc976feb78cc61
diff --git a/recipes-extended/sedutil/sedutil/0001-Support-nvme-mi-device.patch b/recipes-extended/sedutil/sedutil/0001-Support-nvme-mi-device.patch
new file mode 100644
index 0000000..51bd2e0
--- /dev/null
+++ b/recipes-extended/sedutil/sedutil/0001-Support-nvme-mi-device.patch
@@ -0,0 +1,357 @@
+From a6b0a1544d10c67c942025555d6a4a5d3f787292 Mon Sep 17 00:00:00 2001
+From: Jinliang Wang <jinliangw@google.com>
+Date: Mon, 19 Dec 2022 15:20:55 -0800
+Subject: [PATCH] Support nvme-mi device
+
+Support NVMe-MI device based on libnvme-mi library.
+
+Tested:
+sedutil-cli --query mctp:1,17:0
+
+Patch Tracking Bug: b/274977344
+Upstream info / review: https://github.com/Drive-Trust-Alliance/sedutil/pull/421
+Upstream-Status: Submitted
+Justification: Upstreaming to Drive-Trust-Alliance
+
+Signed-off-by: Jinliang Wang <jinliangw@google.com>
+---
+ Makefile.am                 |   1 +
+ configure.ac                |   5 +
+ linux/DtaDevLinuxNvmeMi.cpp | 194 ++++++++++++++++++++++++++++++++++++
+ linux/DtaDevLinuxNvmeMi.h   |  64 ++++++++++++
+ linux/DtaDevOS.cpp          |   5 +
+ 5 files changed, 269 insertions(+)
+ create mode 100644 linux/DtaDevLinuxNvmeMi.cpp
+ create mode 100644 linux/DtaDevLinuxNvmeMi.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 6656d59..fd0f487 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -28,6 +28,7 @@ SEDUTIL_LINUX_CODE = \
+ 	linux/Version.h linux/os.h linux/DtaDevLinuxDrive.h \
+ 	linux/DtaDevLinuxNvme.cpp linux/DtaDevLinuxSata.cpp \
+ 	linux/DtaDevLinuxNvme.h linux/DtaDevLinuxSata.h \
++	linux/DtaDevLinuxNvmeMi.h linux/DtaDevLinuxNvmeMi.cpp \
+ 	linux/DtaDevOS.cpp linux/DtaDevOS.h 
+ sbin_PROGRAMS = sedutil-cli linuxpba
+ sedutil_cli_SOURCES = Common/sedutil.cpp Common/DtaOptions.cpp \
+diff --git a/configure.ac b/configure.ac
+index 1ad8ba0..be52b95 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -11,6 +11,10 @@ AC_PROG_CXX
+ AC_PROG_CC
+ 
+ # Checks for libraries.
++AC_CHECK_LIB(nvme-mi, nvme_mi_create_root)
++
++# libnvme-mi uses meson build tool which enables D_FILE_OFFSET_BITS=64 by default
++AC_SYS_LARGEFILE
+ 
+ # Checks for header files.
+ AC_CHECK_HEADERS([arpa/inet.h fcntl.h malloc.h stdint.h stdlib.h string.h sys/ioctl.h unistd.h])
+@@ -23,6 +27,7 @@ AC_TYPE_UINT16_T
+ AC_TYPE_UINT32_T
+ AC_TYPE_UINT8_T
+ 
++
+ # Checks for library functions.
+ AC_CHECK_FUNCS([memset])
+ 
+diff --git a/linux/DtaDevLinuxNvmeMi.cpp b/linux/DtaDevLinuxNvmeMi.cpp
+new file mode 100644
+index 0000000..d81b2f7
+--- /dev/null
++++ b/linux/DtaDevLinuxNvmeMi.cpp
+@@ -0,0 +1,194 @@
++/* C:B**************************************************************************
++This software is Copyright 2014-2017 Bright Plaza Inc. <drivetrust@drivetrust.com>
++
++This file is part of sedutil.
++
++sedutil is free software: you can redistribute it and/or modify
++it under the terms of the GNU General Public License as published by
++the Free Software Foundation, either version 3 of the License, or
++(at your option) any later version.
++
++sedutil is distributed in the hope that it will be useful,
++but WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++GNU General Public License for more details.
++
++You should have received a copy of the GNU General Public License
++along with sedutil.  If not, see <http://www.gnu.org/licenses/>.
++
++ * C:E********************************************************************** */
++#include "config.h"
++#include "os.h"
++#include "DtaDevLinuxNvmeMi.h"
++
++#include <cstring>
++
++static int parse_mi_dev(const char *dev, unsigned int *net, uint8_t *eid,
++                        unsigned int *ctrl)
++{
++    int rc;
++
++    /* <net>,<eid>:<ctrl-id> form */
++    rc = sscanf(dev, "mctp:%u,%hhu:%u", net, eid, ctrl);
++    if (rc == 3)
++        return 0;
++
++    /* <net>,<eid> form, implicit ctrl-id = 0 */
++    *ctrl = 0;
++    rc = sscanf(dev, "mctp:%u,%hhu", net, eid);
++    if (rc == 2)
++        return 0;
++
++    return -1;
++}
++
++
++/** The Device class represents a single disk device.
++ *  Linux specific implementation using the NVMe interface
++ */
++DtaDevLinuxNvmeMi::DtaDevLinuxNvmeMi() {
++    net = 0;
++    eid = 0;
++    ctrl_id = 0;
++
++    root = NULL;
++    endpoint = NULL;
++    controller = NULL;
++}
++
++bool DtaDevLinuxNvmeMi::init(const char * devref)
++{
++    LOG(D1) << "Creating DtaDevLinuxNvmeMi::init " << devref;
++
++    int rc =  parse_mi_dev(devref, &net, &eid, &ctrl_id);
++    if (rc)
++    {
++        LOG(E) << "invalid nvme-mi device specifier: " << devref;
++        goto error_free;
++    }
++
++    root = nvme_mi_create_root(stderr, LOG_WARNING);
++    if (!root)
++    {
++        LOG(E) << "can't create NVMe root";
++        goto error_free;
++    }
++
++    endpoint = nvme_mi_open_mctp(root, net, eid);
++    if (!endpoint)
++    {
++        LOG(E) << "can't open MCTP endpoint " << devref;
++        goto error_free;
++    }
++
++    controller = nvme_mi_init_ctrl(endpoint, ctrl_id);
++    if (!controller)
++    {
++        LOG(E) << "can't open MCTP controller " << devref;
++        goto error_free;
++    }
++
++    return TRUE; // isOpen = true
++
++error_free:
++    if (controller) {
++        nvme_mi_close_ctrl(controller);
++        controller = NULL;
++    }
++
++    if (endpoint) {
++        nvme_mi_close(endpoint);
++        endpoint = NULL;
++    }
++
++    if (root) {
++        nvme_mi_free_root(root);
++        root = NULL;
++    }
++    return FALSE; // isOpen = false
++}
++
++/** Send an ioctl to the device using nvme admin commands. */
++uint8_t DtaDevLinuxNvmeMi::sendCmd(ATACOMMAND cmd, uint8_t protocol,
++        uint16_t comID, void * buffer, uint32_t bufferlen)
++{
++    int rc;
++    LOG(D1) << "Entering DtaDevLinuxNvmeMi::sendCmd()";
++    if (IF_RECV == cmd) {
++        LOG(D3) << "Security Receive Command";
++        struct nvme_security_receive_args args = { 0 };
++        args.args_size = sizeof(args);
++        args.secp = protocol;
++        args.spsp0 = comID & 0xFF;
++        args.spsp1 = (comID >> 8);
++        args.al = bufferlen;
++        args.data_len = bufferlen;
++        args.data = buffer;
++
++        rc = nvme_mi_admin_security_recv(controller, &args);
++        if (rc < 0)
++        {
++            // transport layer error
++            LOG(E) << "security-receive failed: " << std::strerror(errno);
++            return rc;
++        }
++    }
++    else {
++        LOG(D3) << "Security Send Command";
++        nvme_security_send_args args= { 0 };
++        args.args_size = sizeof(args);
++        args.secp = protocol;
++        args.spsp0 = comID & 0xFF;
++        args.spsp1 = (comID >> 8);
++        args.tl = bufferlen;
++        args.data_len = bufferlen;
++        args.data = buffer;
++
++        rc = nvme_mi_admin_security_send(controller, &args);
++        if (rc < 0) {
++            // transport layer error
++            LOG(E) << "security-send failed: " << std::strerror(errno);
++            return rc;
++        }
++    }
++
++    if (rc != 0) {
++        // Status Field in Complection Queue Entry is not zero
++        LOG(E) << "NVME Security Command Error:" <<  std::hex << rc << std::dec;
++    }
++    return rc;
++}
++
++void DtaDevLinuxNvmeMi::identify(OPAL_DiskInfo& disk_info)
++{
++    LOG(D4) << "Entering DtaDevLinuxNvmeMi::identify()";
++    int rc = nvme_mi_admin_identify_ctrl(controller, &id_ctrl);
++    if (rc) {
++        LOG(E) << "Identify Controller failed: " << std::strerror(errno);
++        disk_info.devType = DEVICE_TYPE_OTHER;
++        return;
++    }
++
++    disk_info.devType = DEVICE_TYPE_NVME;
++    memcpy(disk_info.serialNum, id_ctrl.sn, sizeof(disk_info.serialNum));
++    memcpy(disk_info.modelNum, id_ctrl.mn, sizeof(disk_info.modelNum));
++    memcpy(disk_info.firmwareRev, id_ctrl.fr, sizeof(disk_info.firmwareRev));
++    return;
++}
++
++/** Close the device reference so this object can be delete. */
++DtaDevLinuxNvmeMi::~DtaDevLinuxNvmeMi()
++{
++    LOG(D1) << "Destroying DtaDevLinuxNvmeMi";
++    if (controller) {
++        nvme_mi_close_ctrl(controller);
++    }
++
++    if (endpoint) {
++        nvme_mi_close(endpoint);
++    }
++
++    if (root) {
++        nvme_mi_free_root(root);
++    }
++}
+diff --git a/linux/DtaDevLinuxNvmeMi.h b/linux/DtaDevLinuxNvmeMi.h
+new file mode 100644
+index 0000000..905e437
+--- /dev/null
++++ b/linux/DtaDevLinuxNvmeMi.h
+@@ -0,0 +1,64 @@
++/* C:B**************************************************************************
++This software is Copyright 2014-2017 Bright Plaza Inc. <drivetrust@drivetrust.com>
++
++This file is part of sedutil.
++
++sedutil is free software: you can redistribute it and/or modify
++it under the terms of the GNU General Public License as published by
++the Free Software Foundation, either version 3 of the License, or
++(at your option) any later version.
++
++sedutil is distributed in the hope that it will be useful,
++but WITHOUT ANY WARRANTY; without even the implied warranty of
++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++GNU General Public License for more details.
++
++You should have received a copy of the GNU General Public License
++along with sedutil.  If not, see <http://www.gnu.org/licenses/>.
++
++ * C:E********************************************************************** */
++#pragma once
++#include <libnvme-mi.h>
++#include "DtaStructures.h"
++#include "DtaDevLinuxDrive.h"
++
++/** Linux specific implementation of DtaDevOS.
++ * Uses the NVMe to send commands to the 
++ * device 
++ */
++class DtaDevLinuxNvmeMi: public DtaDevLinuxDrive{
++public:
++    /** Default constructor */
++    DtaDevLinuxNvmeMi();
++    /** Destructor */
++    ~DtaDevLinuxNvmeMi();
++    /** NVMe specific initialization.
++     * This function should perform the necessary authority and environment checking
++     * to allow proper functioning of the program, open the device, perform an ATA
++     * identify, add the fields from the identify response to the disk info structure
++     * and if the device is an ATA device perform a call to Discovery0() to complete
++     * the disk_info structure
++     * @param devref character representation of the device is standard OS lexicon
++     */
++    bool init(const char * devref);
++    /** NVMe specific method to send a command to the device
++     * @param cmd command to be sent to the device
++     * @param protocol security protocol to be used in the command
++     * @param comID communications ID to be used
++     * @param buffer input/output buffer
++     * @param bufferlen length of the input/output buffer
++     */
++    uint8_t sendCmd(ATACOMMAND cmd, uint8_t protocol, uint16_t comID,
++            void * buffer, uint32_t bufferlen);
++    /** NVMe specific routine to send an identify to the device */
++    void identify(OPAL_DiskInfo& disk_info);
++
++private:
++    unsigned int net;
++    unsigned char eid;
++    unsigned int ctrl_id;
++    nvme_root_t root;
++    nvme_mi_ep_t endpoint;
++    nvme_mi_ctrl_t controller;
++    struct nvme_id_ctrl id_ctrl;
++};
+diff --git a/linux/DtaDevOS.cpp b/linux/DtaDevOS.cpp
+index 5261e73..f6163bc 100644
+--- a/linux/DtaDevOS.cpp
++++ b/linux/DtaDevOS.cpp
+@@ -36,6 +36,7 @@ along with sedutil.  If not, see <http://www.gnu.org/licenses/>.
+ #include "DtaDevOS.h"
+ #include "DtaHexDump.h"
+ #include "DtaDevLinuxSata.h"
++#include "DtaDevLinuxNvmeMi.h"
+ #include "DtaDevLinuxNvme.h"
+ #include "DtaDevGeneric.h"
+ 
+@@ -70,6 +71,10 @@ void DtaDevOS::init(const char * devref)
+ //		DtaDevLinuxSata *SataDrive = new DtaDevLinuxSata();
+ 		drive = new DtaDevLinuxSata();
+ 	}
++    else if (!strncmp(devref, "mctp:", 5))
++	{
++		drive = new DtaDevLinuxNvmeMi();
++	}
+ 	else 
+         {
+ 		LOG(E) << "DtaDevOS::init ERROR - unknown drive type";
+-- 
+2.39.0.314.g84b9a713c41-goog
+
diff --git a/recipes-extended/sedutil/sedutil_%.bbappend b/recipes-extended/sedutil/sedutil_%.bbappend
new file mode 100644
index 0000000..663b221
--- /dev/null
+++ b/recipes-extended/sedutil/sedutil_%.bbappend
@@ -0,0 +1,12 @@
+FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
+
+DEPENDS += " libnvme"
+
+SRC_URI += " \
+  file://0001-Support-nvme-mi-device.patch \
+  "
+
+do_install() {
+    install -d ${D}${bindir}
+    install -m 0755 ${B}/sedutil-cli ${D}${bindir}/
+}