blob: 5434c72692b87f09d6b8a92ea3ffcd16938ce1e2 [file] [log] [blame]
#pragma once
#include "MctpEndpoint.hpp"
#include "NVMeError.hpp"
#include <libnvme-mi.h>
#include <cstdint>
#include <functional>
#include <memory>
#include <span>
#include <variant>
class NVMeBasicIntf;
class NVMeMiIntf;
struct NVMeNSIdentify;
/**
* @brief a container class to hold smart ptr to NVMe Basic or NVMe MI
* implementation instance
*/
class NVMeIntf
{
public:
enum class Protocol : uint8_t
{
NVMeBasic,
NVMeMI,
};
NVMeIntf() = default;
template <class IntfImpl, class... Args>
static NVMeIntf create(Args&&... args)
{
NVMeIntf nvmeIntf;
if constexpr (std::is_base_of_v<NVMeBasicIntf, IntfImpl>)
{
nvmeIntf.interface =
std::make_shared<IntfImpl>(std::forward<Args>(args)...);
return nvmeIntf;
}
if constexpr (std::is_base_of_v<NVMeMiIntf, IntfImpl>)
{
nvmeIntf.interface =
std::make_shared<IntfImpl>(std::forward<Args>(args)...);
return nvmeIntf;
}
throw std::runtime_error("Unsupported NVMe interface");
}
auto getInferface()
{
return interface;
}
Protocol getProtocol()
{
if (std::holds_alternative<std::shared_ptr<NVMeBasicIntf>>(interface))
{
return Protocol::NVMeBasic;
}
if (std::holds_alternative<std::shared_ptr<NVMeMiIntf>>(interface))
{
return Protocol::NVMeMI;
}
throw std::runtime_error("uninitiated NVMeIntf");
}
private:
std::variant<std::shared_ptr<NVMeBasicIntf>, std::shared_ptr<NVMeMiIntf>>
interface;
};
/**
* @brief Interface to get information via NVMe MI Basic CMD protocol.
*
* Can be used for implementation or mockup
*/
class NVMeBasicIntf
{
public:
struct DriveStatus
{
uint8_t Status;
uint8_t SmartWarnings;
uint8_t Temp;
uint8_t DriveLifeUsed;
uint8_t WarningTemp;
uint8_t PowerState;
};
enum StatusFlags : uint8_t
{
NVME_MI_BASIC_SFLGS_DRIVE_NOT_READY = 0x40,
NVME_MI_BASIC_SFLGS_DRIVE_FUNCTIONAL = 0x20,
};
NVMeBasicIntf() = default;
// The i2c bus number
virtual int getBus() const = 0;
// The i2c address for NVMe Basic
virtual int getAddress() const = 0;
// Get NVMe drive status, data address is from 00h~07h
virtual void getStatus(
std::function<void(const std::error_code&, DriveStatus*)>&& cb) = 0;
virtual ~NVMeBasicIntf() = default;
};
class NVMeMiIntf
{
public:
constexpr static std::string_view statusToString(nvme_mi_resp_status status)
{
switch (status)
{
case NVME_MI_RESP_SUCCESS:
return "success";
case NVME_MI_RESP_MPR:
return "More Processing Required";
case NVME_MI_RESP_INTERNAL_ERR:
return "Internal Error";
case NVME_MI_RESP_INVALID_OPCODE:
return "Invalid command opcode";
case NVME_MI_RESP_INVALID_PARAM:
return "Invalid command parameter";
case NVME_MI_RESP_INVALID_CMD_SIZE:
return "Invalid command size";
case NVME_MI_RESP_INVALID_INPUT_SIZE:
return "Invalid command input data size";
case NVME_MI_RESP_ACCESS_DENIED:
return "Access Denied";
case NVME_MI_RESP_VPD_UPDATES_EXCEEDED:
return "More VPD updates than allowed";
case NVME_MI_RESP_PCIE_INACCESSIBLE:
return "PCIe functionality currently unavailable";
case NVME_MI_RESP_MEB_SANITIZED:
return "MEB has been cleared due to sanitize";
case NVME_MI_RESP_ENC_SERV_FAILURE:
return "Enclosure services process failed";
case NVME_MI_RESP_ENC_SERV_XFER_FAILURE:
return "Transfer with enclosure services failed";
case NVME_MI_RESP_ENC_FAILURE:
return "Unreoverable enclosure failure";
case NVME_MI_RESP_ENC_XFER_REFUSED:
return "Enclosure services transfer refused";
case NVME_MI_RESP_ENC_FUNC_UNSUP:
return "Unsupported enclosure services function";
case NVME_MI_RESP_ENC_SERV_UNAVAIL:
return "Enclosure services unavailable";
case NVME_MI_RESP_ENC_DEGRADED:
return "Noncritical failure detected by enc. services";
case NVME_MI_RESP_SANITIZE_IN_PROGRESS:
return "Command prohibited during sanitize";
default:
return "";
}
return "";
}
virtual void miSubsystemHealthStatusPoll(
std::function<void(const std::error_code&,
nvme_mi_nvm_ss_health_status*)>&& cb) = 0;
virtual void
miScanCtrl(std::function<void(const std::error_code&,
const std::vector<nvme_mi_ctrl_t>&)>
cb) = 0;
virtual void start(const std::shared_ptr<MctpEndpoint>& ep) = 0;
virtual void stop() = 0;
virtual void recover() = 0;
virtual ~NVMeMiIntf() = default;
virtual bool flushOperations(std::function<void()>&& cb) = 0;
virtual void adminIdentify(
nvme_mi_ctrl_t ctrl, nvme_identify_cns cns, uint32_t nsid,
uint16_t cntid,
std::function<void(nvme_ex_ptr, std::span<uint8_t>)>&& cb) = 0;
virtual void adminGetLogPage(
nvme_mi_ctrl_t ctrl, nvme_cmd_get_log_lid lid, uint32_t nsid,
uint8_t lsp, uint16_t lsi,
std::function<void(const std::error_code&, std::span<uint8_t>)>&&
cb) = 0;
virtual void adminFwCommit(nvme_mi_ctrl_t ctrl, nvme_fw_commit_ca action,
uint8_t slot, bool bpid,
std::function<void(const std::error_code&,
nvme_status_field)>&& cb) = 0;
virtual void adminFwDownload(
nvme_mi_ctrl_t ctrl, std::string firmwarefile,
std::function<void(const std::error_code&, nvme_status_field)>&&
cb) = 0;
virtual void adminSecuritySend(
nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t protoSpecific,
std::span<uint8_t> data,
std::function<void(const std::error_code&, int nvmeStatus)>&& cb) = 0;
virtual void adminSecurityReceive(
nvme_mi_ctrl_t ctrl, uint8_t proto, uint16_t protoSpecific,
uint32_t transferLength,
std::function<void(const std::error_code&, int nvmeStatus,
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 nvmeStatus,
uint32_t comptionDw0)>&& cb) = 0;
virtual void createNamespace(
nvme_mi_ctrl_t ctrl, uint64_t size, size_t lbaFormat,
bool metadataAtEnd, std::function<void(nvme_ex_ptr ex)>&& submittedCb,
std::function<void(nvme_ex_ptr ex, NVMeNSIdentify newid)>&&
finishedCb) = 0;
virtual void adminDeleteNamespace(
nvme_mi_ctrl_t ctrl, uint32_t nsid,
std::function<void(const std::error_code&, int nvmeStatus)>&& cb) = 0;
/**
* listNamespaces() - return list of NSIDs
*
* @cb will be called on success or failure. On success @ns will
* contain the list of NSIDs in sequential order, including inactive
* namespaces.
*/
virtual void adminListNamespaces(
nvme_mi_ctrl_t ctrl,
std::function<void(nvme_ex_ptr ex, std::vector<uint32_t> ns)>&& cb) = 0;
virtual void adminAttachDetachNamespace(
nvme_mi_ctrl_t ctrl, uint16_t ctrlid, uint32_t nsid, bool attach,
std::function<void(const std::error_code&, int nvmeStatus)>&& cb) = 0;
virtual void adminSanitize(nvme_mi_ctrl_t ctrl,
enum nvme_sanitize_sanact sanact, uint8_t passes,
uint32_t pattern, bool invertPattern,
std::function<void(nvme_ex_ptr ex)>&& cb) = 0;
/**
* adminXfer() - Raw admin transfer interface.
* @ctrl: controller to send the admin command to
* @admin_req: request header
* @data: request data payload
* @timeout_ms: timeout in ms
* @resp_data_offset: offset into request data to retrieve from controller
* @cb: callback function after the response received.
* @ec: error code
* @admin_resp: response header
* @resp_data: response data payload
*
* Performs an arbitrary NVMe Admin command, using the provided request
* header, in @admin_req. The requested data is attached by @data, if any.
*
* On success, @cb will be called and response header and data are stored in
* @admin_resp and @resp_data, which has an optional appended payload
* buffer. The response data does not include the Admin request header, so 0
* represents no payload.
*
* As with all Admin commands, we can request partial data from the Admin
* Response payload, offset by @resp_data_offset. In case of resp_data
* contains only partial data of the caller's requirement, a follow-up call
* to adminXfer with offset is required.
*
* See: &struct nvme_mi_admin_req_hdr and &struct nvme_mi_admin_resp_hdr.
*
* @ec will be returned on failure.
*/
virtual void
adminXfer(nvme_mi_ctrl_t ctrl, const nvme_mi_admin_req_hdr& adminReq,
std::span<uint8_t> data, unsigned int timeoutMs,
std::function<void(const std::error_code& ec,
const nvme_mi_admin_resp_hdr& adminResp,
std::span<uint8_t> respData)>&& cb) = 0;
};
/* A subset of Namespace Identify details of interest */
struct NVMeNSIdentify
{
uint32_t namespaceId;
uint64_t size;
uint64_t capacity;
size_t blockSize;
size_t lbaFormat;
bool metadataAtEnd;
};