diff --git a/Common/DtaOptions.h b/Common/DtaOptions.h index c012af1d..007bb15f 100644 --- a/Common/DtaOptions.h +++ b/Common/DtaOptions.h @@ -21,6 +21,8 @@ along with sedutil. If not, see . #ifndef _DTAOPTIONS_H #define _DTAOPTIONS_H +#include + /** Output modes */ typedef enum _sedutiloutput { sedutilNormal, diff --git a/Makefile.am b/Makefile.am index 6656d593..fd0f4873 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 1ad8ba06..be52b958 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 00000000..4dc46d90 --- /dev/null +++ b/linux/DtaDevLinuxNvmeMi.cpp @@ -0,0 +1,202 @@ +/* C:B************************************************************************** +This software is Copyright 2014-2017 Bright Plaza Inc. + +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 . + + * C:E********************************************************************** */ +#include "config.h" +#include "os.h" +#include "DtaDevLinuxNvmeMi.h" + +#include + +constexpr int tcgDefaultTimeoutMS = 20*1000; + +static int parse_mi_dev(const char *dev, unsigned int *net, uint8_t *eid, + unsigned int *ctrl) +{ + int rc; + + /* ,: form */ + rc = sscanf(dev, "mctp:%u,%hhu:%u", net, eid, ctrl); + if (rc == 3) + return 0; + + /* , 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; + + unsigned int timeout = nvme_mi_ep_get_timeout(endpoint); + nvme_mi_ep_set_timeout(endpoint, tcgDefaultTimeoutMS); + rc = nvme_mi_admin_security_recv(controller, &args); + nvme_mi_ep_set_timeout(endpoint, timeout); + 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; + + unsigned int timeout = nvme_mi_ep_get_timeout(endpoint); + nvme_mi_ep_set_timeout(endpoint, tcgDefaultTimeoutMS); + rc = nvme_mi_admin_security_send(controller, &args); + nvme_mi_ep_set_timeout(endpoint, timeout); + 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 00000000..905e437f --- /dev/null +++ b/linux/DtaDevLinuxNvmeMi.h @@ -0,0 +1,64 @@ +/* C:B************************************************************************** +This software is Copyright 2014-2017 Bright Plaza Inc. + +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 . + + * C:E********************************************************************** */ +#pragma once +#include +#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 5261e736..f6163bcc 100644 --- a/linux/DtaDevOS.cpp +++ b/linux/DtaDevOS.cpp @@ -36,6 +36,7 @@ along with sedutil. If not, see . #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";