Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support nvme-mi device #421

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Common/DtaOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ along with sedutil. If not, see <http://www.gnu.org/licenses/>.
#ifndef _DTAOPTIONS_H
#define _DTAOPTIONS_H

#include <cstdint>

/** Output modes */
typedef enum _sedutiloutput {
sedutilNormal,
Expand Down
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
5 changes: 5 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -23,6 +27,7 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT8_T


# Checks for library functions.
AC_CHECK_FUNCS([memset])

Expand Down
202 changes: 202 additions & 0 deletions linux/DtaDevLinuxNvmeMi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/* C:B**************************************************************************
This software is Copyright 2014-2017 Bright Plaza Inc. <[email protected]>

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>

constexpr int tcgDefaultTimeoutMS = 20*1000;

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;

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);
}
}
64 changes: 64 additions & 0 deletions linux/DtaDevLinuxNvmeMi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* C:B**************************************************************************
This software is Copyright 2014-2017 Bright Plaza Inc. <[email protected]>

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;
};
5 changes: 5 additions & 0 deletions linux/DtaDevOS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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";
Expand Down