Signed-off-by: Mohit Kapoor --- diff --git a/Makefile b/Makefile index 86eb7c6..3ea82dd 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,13 @@ PLUGIN_OBJS := \ plugins/wdc/wdc-utils.o \ plugins/huawei/huawei-nvme.o \ plugins/netapp/netapp-nvme.o \ + plugins/mi/util/hal/mi-nvme-hal-main.o \ + plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.o \ + plugins/mi/util/mi-nvme-util-base.o \ + plugins/mi/util/mi-nvme-util-crc.o \ + plugins/mi/util/mi-nvme-util-tool.o \ + plugins/mi/mi-nvme.o \ + plugins/mi/mi-nvme-cmd.o \ plugins/toshiba/toshiba-nvme.o \ plugins/micron/micron-nvme.o \ plugins/seagate/seagate-nvme.o \ diff --git a/plugins/mi/mi-nvme-cmd.c b/plugins/mi/mi-nvme-cmd.c new file mode 100644 index 0000000..3a71170 --- /dev/null +++ b/plugins/mi/mi-nvme-cmd.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-cmd.c - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-cmd.h" + +uint32_t gettransmissionunitsize() +{ + return MCTP_TRANS_UNIT_SIZE_VAL_DEF; +} + +int executecommand(__u8 *cmdobj) +{ + struct nvme_mi_mctp_message_t message; + memset(&message, 0, sizeof(struct nvme_mi_mctp_message_t)); + nvme_mi_admin_cmd_mctp_message adminmessage; + memset(&adminmessage, 0, sizeof(nvme_mi_admin_cmd_mctp_message)); + bool ret = false; + uint32_t RequestDataSize = 0; + struct gencmd_nvmemi *micmd; + struct gencmd_nvmemi_admin *miadmincmd; + uint32_t uiMCTP_TUS = gettransmissionunitsize(); + + ret = initialize(uiMCTP_TUS); + if (ret == false) { + return -1; + } + + streply.length = 0; + streply.errorcode = 0; + memset(streply.replybuf, 0, sizeof(streply.replybuf)); + + /*Skipping first element in the structure.... Size of buffer is 4 bytes each(uint32_t) in the NVMe_MI_Header structure*/ + memcpy(&message.msgPld, cmdobj, sizeof(struct nvme_mi_mctp_message_t) - 8); + + /*Check if the incoming command is an MI/MI-Admin Command*/ + if (message.msgPld.nvme_mi_message_header & 0x00001000) { + miadmincmd = (struct gencmd_nvmemi_admin *)cmdobj; + /*Skipping first element in the structure....Size of buffer is 4 bytes each(uint32_t) in the NVMe_MI_Header structure*/ + memcpy(&adminmessage.msgPld, cmdobj, sizeof(nvme_mi_admin_cmd_mctp_message) - 8); + if (miadmincmd->buf != NULL) { + adminmessage.msgPld.buffer = miadmincmd->buf; + } + ret = execute_nvme_mi_admin_command(adminmessage, &(streply), REPLY_BUFFER_SIZE, miadmincmd->dlen); + } else if (message.msgPld.nvme_mi_message_header & 0x00000800) { + micmd = (struct gencmd_nvmemi *)cmdobj; + if (micmd->buf != NULL) { + message.msgPld.buffer = micmd->buf; + } + ret = execute_nvme_mi_command(message, &(streply), REPLY_BUFFER_SIZE, RequestDataSize); + } + + if (!ret) { + return -1; + } else { + return 0; + } +} + +int getresponsedata(uint8_t *buf, uint32_t size, bool ismicmd) +{ + uint32_t offset = 0; + uint32_t length = gettransmissionunitsize() - NVME_MI_HEADER_SIZE; + uint32_t bytesread = 0; + + if (buf == NULL) { + printf("Error : getresponsedata input buf is NULL\n"); + return -1; + } + + if (ismicmd == true) { + offset = MCTP_PKT_HEADER_SIZE + NVME_MI_STATUS_SIZE + NVME_MI_HEADER_SIZE; + length = gettransmissionunitsize() - NVME_MI_HEADER_SIZE - NVME_MI_STATUS_SIZE; + streply.length = streply.length - NVME_MI_HEADER_SIZE - NVME_MI_STATUS_SIZE - CRC_SIZE; + } else { + offset = MCTP_PKT_HEADER_SIZE + NVME_MI_STATUS_SIZE + NVME_MI_HEADER_SIZE + CQENTRY_SIZE; + length = gettransmissionunitsize() - NVME_MI_HEADER_SIZE - NVME_MI_STATUS_SIZE - CQENTRY_SIZE; + streply.length = streply.length - NVME_MI_HEADER_SIZE - NVME_MI_STATUS_SIZE - CRC_SIZE - CQENTRY_SIZE; + } + + if (length > size) { + length = size; + } + + while (bytesread < streply.length) { + memcpy(buf + bytesread, streply.replybuf + offset, length); + offset += length + MCTP_PKT_HEADER_SIZE; + bytesread += length; + + if (bytesread + gettransmissionunitsize() > streply.length) { + length = streply.length - bytesread; + } else { + length = gettransmissionunitsize(); + } + } + return 0; +} + +void getresponsemessage(uint8_t *buf, uint32_t size) +{ + memcpy(buf, streply.replybuf + MCTP_PKT_HEADER_SIZE, size); +} diff --git a/plugins/mi/mi-nvme-cmd.h b/plugins/mi/mi-nvme-cmd.h new file mode 100644 index 0000000..9a15ada --- /dev/null +++ b/plugins/mi/mi-nvme-cmd.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-cmd.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#ifndef _MI_NVME__CMD_H_ +#define _MI_NVME_CMD_H_ + +#include "mi-nvme-struct.h" +#include "util/mi-nvme-util-base.h" +#include "util/mi-nvme-util-tool.h" + +mctp_reply_buffer_struct streply; + +uint32_t gettransmissionunitsize(); +int executecommand(__u8 *cmdobj); +int getresponsedata(uint8_t *buf, uint32_t size, bool flagmi); +void getresponsemessage(uint8_t *buf, uint32_t size); + +#endif diff --git a/plugins/mi/mi-nvme-struct.h b/plugins/mi/mi-nvme-struct.h new file mode 100644 index 0000000..4eed296 --- /dev/null +++ b/plugins/mi/mi-nvme-struct.h @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-struct.h - Implementation of NVMe Management Interface commands in NVMe. + * This file contains required header and response structures for MI commands. + * + * Developer : Mohit Kapoor + */ + +#ifndef _MI_NVME__HEADER_H_ +#define _MI_NVME_HEADER_H_ + +#include +#include +#include +#include +#include +#include + +#define MCTP_PKT_HEADER_SIZE 8 +#define NVME_MI_HEADER_SIZE 4 +#define NVME_MI_STATUS_SIZE 4 +#define CQENTRY_SIZE 12 +#define MCTP_TRANS_UNIT_SIZE_VAL_DEF 64 + +enum nvmemi_opcode { + nvmemi_cmd_readnvmemids = 0x00, + nvmemi_cmd_subsyshealthstpoll = 0x01, + nvmemi_cmd_chsp = 0x02, + nvmemi_cmd_configurationset = 0x03, + nvmemi_cmd_configurationget = 0x04, + nvmemi_cmd_vpdread = 0x05, + nvmemi_cmd_vpdwrite = 0x06 +}; + +typedef enum _MI_COMMAND_TYPE { + COMMAND_TYPE_MI, + COMMAND_TYPE_MI_ADMIN +}MI_COMMAND_TYPE; + +enum CONFIGURATION_IDENTIFIER { + RESERVED, + SMBUS_I2C_FREQ, + HEALTH_STATUS_CHG, + MCTP_TRANS_UNIT_SIZE, +}; + +struct nvmemi_message_header_struct { + uint32_t message_type:7; + uint32_t ic:1; + uint32_t csi:1; + uint32_t reserved:2; + uint32_t nmimt:4; + uint32_t ror:1; + uint32_t reserved1:16; +}; + +typedef struct _mctp_message_header { + uint8_t messsage_type:7; + uint8_t ic:1; + uint8_t instance_id:5; + uint8_t rsvd:1; + uint8_t D:1; + uint8_t RQ:1; + uint8_t Command_Code; +}mctp_message_header; + +/*Generic command Structure for NVMe MI Commands*/ +struct gencmd_nvmemi { + struct nvmemi_message_header_struct msg_header; + uint8_t opcode; + uint8_t reserved0; + uint8_t reserved1; + uint8_t reserved2; + uint32_t cdw0; + uint32_t cdw1; + uint8_t *buf; + uint32_t mic; +}; + +/*Generic command Structure for NVMe MI Admin Commands*/ +struct gencmd_nvmemi_admin { + struct nvmemi_message_header_struct msg_header; + uint8_t opcode; + uint8_t cflgs; + uint16_t ctlid; + uint32_t nsid; + uint32_t cdw2; + uint32_t cdw3; + uint32_t cdw4; + uint32_t cdw5; + uint32_t dofst; + uint32_t dlen; + uint32_t cdw8; + uint32_t cdw9; + uint32_t cdw10; + uint32_t cdw11; + uint32_t cdw12; + uint32_t cdw13; + uint32_t cdw14; + uint32_t cdw15; + uint8_t *buf; + uint32_t mic; +}; + +struct admin_cmd_resp_dw3 { + uint16_t cid; + uint16_t p:1; + uint16_t sc:8; + uint16_t sct:3; + uint16_t crd:2; + uint16_t m:1; + uint16_t NVME_DIR_SND_ID_OP_ENABLE:1; +}; + +struct nvme_admin_cmd_resp_status { + uint8_t msg_type:7; + uint8_t ic:1; + + uint8_t csi:1; + uint8_t reserved :2; + uint8_t nvme_mi_msg_type:4; + uint8_t ror:1; + + uint16_t reserved1; + + uint32_t status:8; + uint32_t nvme_mgmt_response:24; + + uint32_t cqdw0; + uint32_t cqdw1; + struct admin_cmd_resp_dw3 cqdw3; +}; + +struct nvme_mi_cmd_resp { + uint8_t msg_type:7; + uint8_t ic:1; + + uint8_t csi:1; + uint8_t reserved:2; + uint8_t nvme_mi_msg_type:4; + uint8_t ror:1; + + uint16_t reserved1; + + uint32_t status:8; + uint32_t nvme_mgmt_response:24; +}; + +struct read_nvme_mi_data_struct_resp { + uint16_t resp_data_len; + uint8_t reserved; +}; + +struct smbus_i2c_freq_cfgget_resp { + uint8_t smbus_i2c_freq:4; + uint8_t reserved1:4; + uint16_t reserved2; +}; + +struct mctp_tus_cfgget_resp { + uint16_t mctp_trans_unit_size; + uint8_t reserved; +}; + +struct nvme_subsys_info_data { + uint8_t nump; + uint8_t mjr; + uint8_t mnr; + uint8_t reserved[29]; +}; + +struct option_sup_cmd_struct { + uint8_t cmdtype; + uint8_t opc; +}; + +struct option_sup_cmd_list_struct { + uint16_t numcmd; + struct option_sup_cmd_struct cmdstruct[2047]; +}; + +struct nss_status_struct { + uint8_t RESERVED :2; + uint8_t PORT1_PLA :1; + uint8_t PORT2_PLA :1; + uint8_t RESET_NOT_REQ :1; + uint8_t DRIVE_FUNC :1; + uint8_t RESERVED1 :2; +}; + +struct comp_ctrl_status_struct { + uint16_t READY :1; + uint16_t CFS :1; + uint16_t SHN_STS :2; + uint16_t NSSR_OCCURRED :1; + uint16_t CECO :1; + uint16_t NSAC :1; + uint16_t FWACT :1; + uint16_t CS_CH :1; + uint16_t CTC :1; + uint16_t PERCENTAGE_USED :1; + uint16_t AVAILABLE_SPARE :1; + uint16_t CRITICAL_WARNING :1; + uint16_t RESERVED :3; +}; + +struct nvm_subsys_health_struct { + struct nss_status_struct nss_status; + uint8_t smart_warnings; + uint8_t composite_temp; + uint8_t per_drv_life_used; + struct comp_ctrl_status_struct comp_ctrl_status; + uint16_t reserved; +}; + +struct ctrl_health_status_poll_resp { + __u16 reserved; + __u8 rent; +}; + +struct cwarn_struct { + __u8 spare_thresh:1; + __u8 temp_above_or_under_thresh:1; + __u8 rel_degraded:1; + __u8 read_only:1; + __u8 vol_mem_bup_fail:1; + __u8 reserved:3; +}; + +struct csts_struct { + __u16 rdy :1; + __u16 cfs :1; + __u16 shst :2; + __u16 nssro :1; + __u16 en :1; + __u16 nssac :1; + __u16 fwact :1; + __u16 reserved :8; +}; + +struct ctrl_health_data { + __u16 ctlid; + struct csts_struct csts; + __u16 ctemp; + __u8 pdlu; + __u8 spare; + struct cwarn_struct cwarn; + __u8 reserved[7]; +}; + +struct log_page_error_info +{ + uint64_t errcnt; + uint16_t subqid; + uint16_t cid; + uint16_t statusfield; + uint8_t perr_loc_byte; + uint8_t perr_loc_bit:3; + uint8_t perr_loc_res:5; + uint64_t lba; + uint32_t ns; + uint8_t vsinfoavl; + uint8_t reserved[35]; +}; + +struct getf_temp_thres +{ + uint16_t tmpth; + uint16_t tmpsel:4; + uint16_t thsel:2; + uint16_t reserved:10; +}; + +struct getf_no_queues +{ + uint16_t nsqa; + uint16_t ncqa; +}; + +#endif diff --git a/plugins/mi/mi-nvme.c b/plugins/mi/mi-nvme.c new file mode 100644 index 0000000..91ed5c7 --- /dev/null +++ b/plugins/mi/mi-nvme.c @@ -0,0 +1,1213 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme.c - Implementation of NVMe Management Interface commands in NVMe. + * This file contains the MI command implementation for the plugin + * + * Developer : Mohit Kapoor + */ + +#include +#include +#include +#include +#include +#include + +#include "linux/nvme_ioctl.h" + +#include "common.h" +#include "nvme-print.h" +#include "nvme-ioctl.h" +#include "json.h" +#include "plugin.h" + +#include "argconfig.h" +#include "suffix.h" + +#define CREATE_CMD +#include +#include "mi-nvme.h" +#include "linux/nvme.h" +#include "mi-nvme-cmd.h" +#include "util/hal/mi-nvme-hal-main.h" + +void cleanup_nvmemi() +{ + bool ret = close_device(); + if (ret == false) { + printf("Error : Close device Failed!\n"); + } +} + +int parse_and_open_nvmemi(int argc, char **argv, const char *desc, + const struct argconfig_commandline_options *opts) +{ + int ret = 0; + ret = argconfig_parse(argc, argv, desc, opts); + if (ret < 0) { + return ret; + } + + printf("Setting Sideband Interface to QEMU MI\n"); + SetSidebandInterface(qemu_nvme_mi); + + return ret; +} + +void msg_header_nvmemi(struct nvmemi_message_header_struct *str, MI_COMMAND_TYPE cmdtype) +{ + if (cmdtype == COMMAND_TYPE_MI) { + str->message_type = 4; + str->ic = 1; + str->csi = 0; + str->reserved = 0; + str->nmimt = 1; + str->ror = 0; + str->reserved1 = 0; + } else if (cmdtype == COMMAND_TYPE_MI_ADMIN) { + str->message_type = 4; + str->ic = 1; + str->csi = 0; + str->reserved = 0; + str->nmimt = 2; + str->ror = 0; + str->reserved1 = 0; + } +} + +static int nvmemi_cmd_response(struct nvme_mi_cmd_resp *resp, bool ismgmtresp) +{ + if (ismgmtresp) { + printf("Get the response message for the executed command\n"); + getresponsemessage((__u8 *)resp, sizeof(struct nvme_mi_cmd_resp)); + } + + return 0; +} + +static int nvmemi_readnvmemids(__u16 ctrlid, __u8 portid, __u8 dtyp) +{ + int retval = 0; + __u32 cdw0 = ctrlid | portid << 16 | dtyp << 24; + __u32 cdw1 = 0; + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_readnvmemids, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct read_nvme_mi_data_struct_resp readNVMeDS; + struct nvme_mi_cmd_resp resp; + memset(&readNVMeDS, 0, sizeof(struct read_nvme_mi_data_struct_resp)); + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, true); + if (retval == -1) { + return retval; + } + + /*Copy the Management Response*/ + uint64_t address = (uint64_t)&resp; + memcpy(&readNVMeDS, (void*)(address + 5), sizeof(struct read_nvme_mi_data_struct_resp)); + + if (readNVMeDS.resp_data_len == 0) { + printf("Response data length is 0 in NVMe Management Response!\n"); + return -1; + } + + __u8 *Respbuffer = (__u8 *)malloc(readNVMeDS.resp_data_len); + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + + retval = getresponsedata(Respbuffer, readNVMeDS.resp_data_len, true); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + if (dtyp == 0x0) { + struct nvme_subsys_info_data NVMeSubsysInfoDS; + memset(&NVMeSubsysInfoDS, 0, sizeof(struct nvme_subsys_info_data)); + memcpy(&NVMeSubsysInfoDS, Respbuffer, readNVMeDS.resp_data_len); + + printf("NVMe-MI Major Version Number = %u\n", NVMeSubsysInfoDS.mjr); + printf("NVMe-MI Minor Version Number = %u\n", NVMeSubsysInfoDS.mnr); + printf("Number of Ports = %u\n", NVMeSubsysInfoDS.nump); + } else if (dtyp == 0x4) { + struct option_sup_cmd_list_struct opCommandList; + memset(&opCommandList, 0 , sizeof(struct option_sup_cmd_list_struct)); + memcpy(&opCommandList, Respbuffer, readNVMeDS.resp_data_len); + + printf("Number of commands = %u\n", opCommandList.numcmd); + } + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int readnvmemids(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Read NVMe MI Data Structure"; + const char *ctrlid = "Controller Identifier"; + const char *portid = "Port Identifier"; + const char *dtyp = "Data Structure Type"; + + int retval; + int err = -1; + + struct config { + __u16 ctrlid; + __u8 portid; + __u8 dtyp; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_SHRT("ctrlid", 'c', &cfg.ctrlid, ctrlid), + OPT_BYTE("portid", 'p', &cfg.portid, portid), + OPT_BYTE("dtyp", 'd', &cfg.dtyp, dtyp), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing readnvmemids Command, ctrlid:%"PRIx16" portid:%d dtyp:%d\n", + (uint16_t)cfg.ctrlid, cfg.portid, cfg.dtyp); + err = nvmemi_readnvmemids(cfg.ctrlid, cfg.portid, cfg.dtyp); + if (!err) { + printf("readnvmemids: Success\n"); + } else if (err > 0) { + printf("readnvmemids: Fail, ctrlid:%"PRIx16" portid:%d dtyp:%d\n", + (uint16_t)cfg.ctrlid, cfg.portid, cfg.dtyp); + } + + cleanup_nvmemi(); + return err; +} + +static int nvmemi_shspoll(__u8 cs) +{ + int retval = 0; + __u32 Reserved = 0; + __u32 cdw0 = Reserved | cs << 31; + + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_subsyshealthstpoll, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = 0, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct nvm_subsys_health_struct subsysStruct; + memset(&subsysStruct, 0, sizeof(struct nvm_subsys_health_struct)); + uint16_t sizetocopy = sizeof(struct nvm_subsys_health_struct); + + __u8 *Respbuffer = (__u8 *)malloc(sizetocopy); + + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + + retval = getresponsedata(Respbuffer, sizetocopy, true); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + memcpy(&subsysStruct, Respbuffer, sizetocopy); + + printf("**********COMMAND RESPONSE START**********\n"); + printf("SMART Warnings = %u\n", subsysStruct.smart_warnings); + printf("Composite Temprature = %u\n", subsysStruct.composite_temp); + printf("Percentage Drive Life Used = %u\n", subsysStruct.per_drv_life_used); + printf("**********COMMAND RESPONSE END**********\n"); + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int shspoll(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "NVM Subsystem Health Status Poll"; + const char *cs = "Clear Status"; + + int retval; + int err = -1; + + struct config { + __u8 cs; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_SHRT("cs", 'c', &cfg.cs, cs), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing Subsystem Health Status Poll Command, cs:%"PRIx8"\n", + (uint8_t)cfg.cs); + err = nvmemi_shspoll(cfg.cs); + if (!err) { + printf("NVM Subsystem Health Status Poll: Success\n"); + } else if (err > 0) { + printf("NVM Subsystem Health Status Poll Fail, ctrlid:%"PRIx8"\n", + (uint8_t)cfg.cs); + } + + cleanup_nvmemi(); + return err; +} + +static int nvmemi_chspoll(__u32 cdw0, __u32 cdw1) +{ + int retval = 0; + struct nvmemi_message_header_struct str; + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_chsp, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct ctrl_health_status_poll_resp mgmtresp; + struct nvme_mi_cmd_resp resp; + memset(&mgmtresp, 0, sizeof(struct ctrl_health_status_poll_resp)); + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, true); + if (retval == -1) { + return retval; + } + + /*Copy the Management Response*/ + uint64_t address = (uint64_t)&resp; + memcpy(&mgmtresp, (void*)(address + 5), sizeof(struct ctrl_health_status_poll_resp)); + //memcpy((void *)&mgmtresp, (void *)&resp.nvme_mgmt_response , sizeof(struct ctrl_health_status_poll_resp)); + + if (mgmtresp.rent == 0) { + printf("The number of Response Entries is zero!\n"); + return -1; + } + + __u8 *Respbuffer = (__u8 *)malloc(mgmtresp.rent * sizeof(struct ctrl_health_data)); + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + + retval = getresponsedata(Respbuffer, mgmtresp.rent * sizeof(struct ctrl_health_data), true); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + struct ctrl_health_data chds; + memset(&chds, 0, sizeof(struct ctrl_health_data)); + memcpy(&chds, Respbuffer, sizeof(struct ctrl_health_data)); + + printf("Controller Identifier = %u\n", chds.ctlid); + printf("Composite Temprature = %u\n", chds.ctemp); + printf("Percentage Used = %u\n", chds.pdlu); + printf("Available Space = %u\n", chds.spare); + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int chspoll(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "NVM Controller Health Status Poll"; + const char *cdw0 = "Command DWORD0 Value"; + const char *cdw1 = "Command DWORD1 Value"; + + int retval; + int err = -1; + + struct config { + __u32 cdw0; + __u32 cdw1; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_SHRT("cdw0", 'c', &cfg.cdw0, cdw0), + OPT_SHRT("cdw1", 'd', &cfg.cdw1, cdw1), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing Controller Health Status Poll Command, cdw0 : \ + %"PRIx32" cdw1 : %"PRIx32"\n", cfg.cdw0, cfg.cdw1); + + err = nvmemi_chspoll(cfg.cdw0, cfg.cdw1); + if (!err) { + printf("Controller Health Status Poll: Success\n"); + } else if (err > 0) { + printf("Controller Health Status Poll Failed!\n"); + } + + cleanup_nvmemi(); + return err; +} + +static int nvmemi_configget(__u8 configid, __u8 portid) +{ + int retval = 0; + __u32 cdw0 = configid | portid << 24; + __u32 cdw1 = 0; + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_configurationget, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct nvme_mi_cmd_resp resp; + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, true); + if (retval == -1) { + return retval; + } + + + uint64_t address = (uint64_t)&resp; + + if (configid == SMBUS_I2C_FREQ) { + struct smbus_i2c_freq_cfgget_resp mgmt_resp; + memset(&mgmt_resp, 0, sizeof(struct smbus_i2c_freq_cfgget_resp)); + memcpy(&mgmt_resp, (void *)(address + 5), sizeof(struct smbus_i2c_freq_cfgget_resp)); + printf("SMBus frequency:%d\n", mgmt_resp.smbus_i2c_freq); + } else if (configid == MCTP_TRANS_UNIT_SIZE) { + struct mctp_tus_cfgget_resp mgmt_resp; + memset(&mgmt_resp, 0, sizeof(struct mctp_tus_cfgget_resp)); + memcpy(&mgmt_resp, (void *)(address + 5), sizeof(struct mctp_tus_cfgget_resp)); + printf("MCTP Transmission unit size:%d\n" , mgmt_resp.mctp_trans_unit_size); + } + + cleanup_nvmemi(); + return retval; +} + +static int configget(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Configuration Get"; + const char *configid = "Configuration Identifier"; + const char *portid = "Port Identifier"; + + int retval; + int err = -1; + + struct config { + __u8 configid; + __u8 portid; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_BYTE("configid", 'c', &cfg.configid, configid), + OPT_BYTE("portid", 'p', &cfg.portid, portid), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing Configuration Get Command, configid:%d\t portid:%d\n", + cfg.configid, cfg.portid); + err = nvmemi_configget(cfg.configid, cfg.portid); + return err; +} + +static int nvmemi_configset(__u8 configid, __u8 portid, __u8 smbusfreq, __u16 mctpunitsz) +{ + int retval = 0; + __u32 cdw0 = 0; + __u32 cdw1 = 0; + + if (configid == SMBUS_I2C_FREQ) { + cdw0 = configid | smbusfreq << 8 | portid << 24; + cdw1 = 0; + } else if (configid == MCTP_TRANS_UNIT_SIZE) { + cdw0 = configid | portid << 24; + cdw1 = mctpunitsz; + } + + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_configurationset, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct nvme_mi_cmd_resp resp; + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, false); + if (retval == -1) { + return retval; + } + + cleanup_nvmemi(); + return retval; +} + +static int configset(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Configuration Set"; + const char *configid = "Configuration Identifier"; + const char *portid = "Port Identifier"; + const char *smbusfreq = "SMBus I2C frequency"; + const char *mctpunitsz = "MCTP Transmission Unit Size"; + + int retval; + int err = -1; + + struct config { + __u8 configid; + __u8 portid; + __u8 smbusfreq; + __u16 mctpunitsz; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_BYTE("configid", 'c', &cfg.configid, configid), + OPT_BYTE("portid", 'p', &cfg.portid, portid), + OPT_BYTE("smbusfreq", 'f', &cfg.smbusfreq, smbusfreq), + OPT_BYTE("mctpunitsz", 's', &cfg.mctpunitsz, mctpunitsz), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing Configuration Set Command, configid:%d portid:%d smbusfreq:%d mctpunitsz:%d\n", + cfg.configid, cfg.portid, cfg.smbusfreq, cfg.mctpunitsz); + err = nvmemi_configset(cfg.configid, cfg.portid, cfg.smbusfreq, cfg.mctpunitsz); + return err; +} + +static int nvmemi_vpdread(__u16 dofst, __u16 dlen, char *file) +{ + int retval = 0; + __u32 cdw0 = dofst; + __u32 cdw1 = dlen; + + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_vpdread, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct nvme_mi_cmd_resp resp; + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, false); + if (retval == -1) { + return retval; + } + + int dfd = -1; + int opcode = 2; + int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT; + int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + uint64_t address = (uint64_t)&resp; + + if (strlen(file)) { + dfd = open(file, flags, mode); + if (dfd < 0) { + printf("Failed to open the file\n"); + } else { + printf("Open successful\n"); + } + } + + int sz = write(dfd, (void *)(address + 8), dlen); + if (sz < 0) { + printf("Failed to write\n"); + } + + cleanup_nvmemi(); + return retval; +} + +static int vpdread(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "VPD Read"; + const char *dofst = "Data Offset"; + const char *dlen = "Data Length"; + const char *data = "Response Data"; + + int retval; + int err = -1; + + struct config { + __u16 dofst; + __u16 dlen; + char *data; + }; + + struct config cfg; + + OPT_ARGS(opts) = { + OPT_SHRT("dofst", 'o', &cfg.dofst, dofst), + OPT_SHRT("dlen", 'l', &cfg.dlen, dlen), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing VPD Read Command, dofst:%d\t dlen:%d\n", + cfg.dofst, cfg.dlen); + err = nvmemi_vpdread(cfg.dofst, cfg.dlen, cfg.data); + return err; +} + +static int nvmemi_vpdwrite(__u16 dofst, __u16 dlen, char *req_data) +{ + int retval = 0; + __u32 cdw0 = dofst; + __u32 cdw1 = dlen; + + struct nvmemi_message_header_struct str; + + msg_header_nvmemi(&str, COMMAND_TYPE_MI); + + struct gencmd_nvmemi cmd = { + .msg_header = str, + .opcode = nvmemi_cmd_vpdread, + .reserved0 = 0, + .reserved1 = 0, + .reserved2 = 0, + .cdw0 = cdw0, + .cdw1 = cdw1, + .buf = (uint8_t *)req_data, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + struct nvme_mi_cmd_resp resp; + memset(&resp, 0, sizeof(struct nvme_mi_cmd_resp)); + + retval = nvmemi_cmd_response(&resp, false); + if (retval == -1) { + return retval; + } + + cleanup_nvmemi(); + return retval; +} + +static int vpdwrite(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "VPD Write"; + const char *dofst = "Data Offset"; + const char *dlen = "Data Length"; + const char *data = "Request Data"; + + int retval; + int err = -1; + int dfd = -1; + int opcode = 1; + int flags = opcode & 1 ? O_RDONLY : O_WRONLY | O_CREAT; + int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + + struct config { + __u16 dofst; + __u16 dlen; + char *data; + }; + + struct config cfg = { + .data = "", + }; + + OPT_ARGS(opts) = { + OPT_SHRT("dofst", 'o', &cfg.dofst, dofst), + OPT_SHRT("dlen", 'l', &cfg.dlen, dlen), + OPT_FILE("data", 'd', &cfg.data, data), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + if (strlen(cfg.data)) { + dfd = open(cfg.data, flags, mode); + if (dfd < 0) { + printf("Failed to open the file\n"); + } else { + printf("Open successful\n"); + } + } + char req_data[cfg.dlen]; + int sz = read(dfd, req_data, cfg.dlen); + if (sz < 0) { + printf("Failed to read\n"); + } + printf("Issuing VPD Write Command, dofst:%d\t dlen:%d\n", cfg.dofst, cfg.dlen); + err = nvmemi_vpdwrite(cfg.dofst, cfg.dlen, req_data); + return err; +} + +static int nvmemi_id(__u8 cns, __u16 cntid) +{ + __u32 data_len = 0; + int retval = 0; + __u32 cdw10 = 0; + cdw10 = cns | cntid << 16; + + struct nvmemi_message_header_struct str; + msg_header_nvmemi(&str, COMMAND_TYPE_MI_ADMIN); + + if (cns == NVME_ID_CNS_NS) { + data_len = sizeof(struct nvme_id_ns); + } else if (cns == NVME_ID_CNS_CTRL) { + data_len = sizeof(struct nvme_id_ctrl); + } + + struct gencmd_nvmemi_admin cmd = { + .msg_header = str, + .opcode = nvme_admin_identify, + .cflgs = 0x3, + .ctlid = 0, + .nsid = 0, + .cdw2 = 0, + .cdw3 = 0, + .cdw4 = 0, + .cdw5 = 0, + .dofst = 0, + .dlen = data_len, + .cdw8 = 0, + .cdw9 = 0, + .cdw10 = cdw10, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + retval = nvmemi_cmd_response(NULL, false); + if (retval == -1) { + return retval; + } + + __u8 *Respbuffer = (__u8 *)malloc(data_len); + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + memset(Respbuffer, 0, data_len); + + retval = getresponsedata(Respbuffer, data_len, false); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + if (cns == NVME_ID_CNS_NS) { + struct nvme_id_ns idns; + memset(&idns, 0, sizeof(struct nvme_id_ns)); + memcpy(&idns, Respbuffer, sizeof(struct nvme_id_ns)); + nvme_show_id_ns(&idns, 0, 0); + } else if (cns == NVME_ID_CNS_CTRL) { + struct nvme_id_ctrl idctrl; + memset(&idctrl, 0, sizeof(struct nvme_id_ctrl)); + memcpy(&idctrl, Respbuffer, sizeof(struct nvme_id_ctrl)); + nvme_show_id_ctrl(&idctrl, 0); + } + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int identify(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Identify Command"; + char *cns = "Controller or Namespace Structure"; + const char *cntid = "Controller Identifier"; + + struct config { + __u8 cns; + __u16 cntid; + }; + struct config cfg; + + OPT_ARGS(opts) = { + OPT_SHRT("cns", 'c', &cfg.cns, cns), + OPT_BYTE("cntid", 'C', &cfg.cntid, cntid), + OPT_END() + }; + + int retval = -1; + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + printf("Issuing Identify Command, \ + cns:%"PRIx8" cntid:%"PRIx16" \n", cfg.cns, cfg.cntid); + + retval = nvmemi_id(cfg.cns, cfg.cntid); + if (!retval) { + printf("identify: Success\n"); + } else if (retval > 0) { + printf("identify: Failed\n"); + } + + cleanup_nvmemi(); + return retval; +} + +static int nvmemi_getlog(__u8 log_id, __u8 lsp, __u64 lpo, + __u16 lsi, bool rae, __u8 uuid_ix, __u32 data_len) +{ + __u32 numd = (data_len >> 2) - 1; + __u16 numdu = numd >> 16, numdl = numd & 0xffff; + __u32 cdw10 = log_id | (numdl << 16) | (rae ? 1 << 15 : 0) | lsp << 8; + int retval = 0; + + struct nvmemi_message_header_struct str; + msg_header_nvmemi(&str, COMMAND_TYPE_MI_ADMIN); + + struct gencmd_nvmemi_admin cmd = { + .msg_header = str, + .opcode = nvme_admin_get_log_page, + .cflgs = 0x3, + .ctlid = 0, + .nsid = 0, + .cdw2 = 0, + .cdw3 = 0, + .cdw4 = 0, + .cdw5 = 0, + .dofst = 0, + .dlen = data_len, + .cdw8 = 0, + .cdw9 = 0, + .cdw10 = cdw10, + .cdw11 = numdu | (lsi << 16), + .cdw12 = lpo & 0xffffffff, + .cdw13 = lpo >> 32, + .cdw14 = uuid_ix, + .cdw15 = 0, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + retval = nvmemi_cmd_response(NULL, false); + if (retval == -1) { + return retval; + } + + __u8 *Respbuffer = (__u8 *)malloc(data_len); + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + + retval = getresponsedata(Respbuffer, data_len, false); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + printf("sizeof logpage error info : 0x%lx\n", sizeof(struct log_page_error_info)); + if (log_id == 0x1) { + struct log_page_error_info resp; + memset(&resp, 0, sizeof(struct log_page_error_info)); + memcpy(&resp, Respbuffer, sizeof(struct log_page_error_info)); + printf("Error Count = %"PRIx64"\n", resp.errcnt); + printf("Submission Queue ID = %"PRIx16"\n", resp.subqid); + printf("Command ID = %"PRIx16"\n", resp.cid); + printf("Status Field = %"PRIx16"\n", resp.cid); + printf("LBA = %"PRIx64"\n", resp.lba); + printf("Namespace = %"PRIx32"\n", resp.ns); + printf("Vendor Specific Information Available = %"PRIx8"\n", resp.ns); + } + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int getlog(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "NVMe Get Log Page command via Sideband"; + const char *log_id = "identifier of log to retrieve"; + const char *log_len = "how many bytes to retrieve"; + const char *lsp = "log specific field"; + const char *lpo = "log page offset specifies the location within a log page from where to start returning data"; + const char *rae = "retain an asynchronous event"; + const char *uuid_index = "UUID index"; + int retval = 0; + + struct config { + __u8 log_id; + __u32 log_len; + __u64 lpo; + __u8 lsp; + __u8 uuid_index; + int rae; + }; + + struct config cfg = { + .log_id = 0xff, + .log_len = 0, + .lpo = NVME_NO_LOG_LPO, + .lsp = NVME_NO_LOG_LSP, + .rae = 0, + .uuid_index = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("log-id", 'i', &cfg.log_id, log_id), + OPT_UINT("log-len", 'l', &cfg.log_len, log_len), + OPT_LONG("lpo", 'o', &cfg.lpo, lpo), + OPT_BYTE("lsp", 's', &cfg.lsp, lsp), + OPT_FLAG("rae", 'r', &cfg.rae, rae), + OPT_BYTE("uuid-index", 'U', &cfg.uuid_index, uuid_index), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + retval = nvmemi_getlog(cfg.log_id, + cfg.lsp, cfg.lpo, 0, cfg.rae, + cfg.uuid_index, cfg.log_len); + if (!retval) { + printf("Get Log Page: Success\n"); + } else if (retval > 0) { + printf("Get Log Page: Failed\n"); + } + + cleanup_nvmemi(); + return retval; +} + +static int nvmemi_getfeature(__u8 fid, __u8 sel, __u32 cdw11, + __u32 data_len) +{ + __u32 cdw10 = fid | sel << 8; + int retval = 0; + + struct nvmemi_message_header_struct str; + msg_header_nvmemi(&str, COMMAND_TYPE_MI_ADMIN); + + struct gencmd_nvmemi_admin cmd = { + .msg_header = str, + .opcode = nvme_admin_get_features, + .cflgs = 0x3, + .ctlid = 0, + .nsid = 0, + .cdw2 = 0, + .cdw3 = 0, + .cdw4 = 0, + .cdw5 = 0, + .dofst = 0, + .dlen = data_len, + .cdw8 = 0, + .cdw9 = 0, + .cdw10 = cdw10, + .cdw11 = cdw11, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .buf = NULL, + .mic = 0 + }; + + /*Sending Command*/ + retval = executecommand((__u8 *)&cmd); + if (retval == -1) { + return retval; + } + + /*Checking Response*/ + retval = nvmemi_cmd_response(NULL, false); + if (retval == -1) { + return retval; + } + + __u8 *Respbuffer = (__u8 *)malloc(data_len); + if (Respbuffer == NULL) { + printf("Memory allocation error.\n"); + return -1; + } + + retval = getresponsedata(Respbuffer, data_len, false); + if (retval == -1) { + printf("Error : Failed to get response data for the command!\n"); + return retval; + } + + if (fid == NVME_FEAT_TEMP_THRESH) { + struct getf_temp_thres resp; + memset(&resp, 0, sizeof(struct getf_temp_thres)); + memcpy(&resp, Respbuffer, sizeof(struct getf_temp_thres)); + printf("Temprature Threshold = %"PRIx16"\n", resp.tmpth); + printf("Threshold Temprature Select = %"PRIx16"\n", resp.tmpsel); + printf("Threshold Type Select = %"PRIx16"\n", resp.thsel); + } else if (fid == NVME_FEAT_NUM_QUEUES) { + struct getf_no_queues resp; + memset(&resp, 0, sizeof(struct getf_no_queues)); + memcpy(&resp, Respbuffer, sizeof(struct getf_no_queues)); + printf("Number of I/O Submission Queues Requested = \ + %"PRIx16"\n", resp.nsqa); + printf("Number of I/O Completion Queues Requested = \ + %"PRIx16"\n", resp.ncqa); + } + + if (Respbuffer != NULL) { + free(Respbuffer); + Respbuffer = NULL; + } + + return retval; +} + +static int getfeature(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "NVMe Get Features Command via Sideband Interface."; + const char *namespace_id = "identifier of desired namespace"; + const char *feature_id = "feature identifier"; + const char *sel = "[0-3]: current/default/saved/supported"; + const char *data_len = "buffer len if data is returned through host memory buffer"; + const char *cdw11 = "dword 11 for interrupt vector config"; + + int retval = 0; + + struct config { + __u32 namespace_id; + __u8 feature_id; + __u8 sel; + __u32 cdw11; + __u32 data_len; + }; + + struct config cfg = { + .namespace_id = 0, + .feature_id = 0, + .sel = 0, + .cdw11 = 0, + .data_len = 0, + }; + + OPT_ARGS(opts) = { + OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id), + OPT_UINT("feature-id", 'f', &cfg.feature_id, feature_id), + OPT_BYTE("sel", 's', &cfg.sel, sel), + OPT_UINT("data-len", 'l', &cfg.data_len, data_len), + OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11), + OPT_END() + }; + + retval = parse_and_open_nvmemi(argc, argv, desc, opts); + if (retval < 0) { + printf("parse_and_open_nvmemi failed!\n"); + return errno; + } + + retval = nvmemi_getfeature(cfg.feature_id, + cfg.sel, cfg.cdw11, cfg.data_len); + if (!retval) { + printf("Get Log Page: Success\n"); + } else if (retval > 0) { + printf("Get Log Page: Failed\n"); + } + + cleanup_nvmemi(); + return retval; +} \ No newline at end of file diff --git a/plugins/mi/mi-nvme.h b/plugins/mi/mi-nvme.h new file mode 100644 index 0000000..3fa9285 --- /dev/null +++ b/plugins/mi/mi-nvme.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#undef CMD_INC_FILE +#define CMD_INC_FILE plugins/mi/mi-nvme + +#if !defined(MI) || defined(CMD_HEADER_MULTI_READ) +#define MI + +#include "cmd.h" + +PLUGIN(NAME("mi", "NVMe Management Interface Specific Extention"), + COMMAND_LIST( + ENTRY("readnvmemids", "nvme-mi : Read NVMe-MI Data Structure", readnvmemids) + ENTRY("shspoll", "nvme-mi : NVM Subsystem Health Status Poll", shspoll) + ENTRY("chspoll", "nvme-mi : Controller Health Status Poll", chspoll) + ENTRY("configget", "nvme-mi : Configuration Get", configget) + ENTRY("configset", "nvme-mi : Configuration Set", configset) + ENTRY("vpdread", "nvme-mi : VPD Read", vpdread) + ENTRY("vpdwrite", "nvme-mi : VPD Write", vpdwrite) + ENTRY("identify", "nvme-mi-admin : Identify", identify) + ENTRY("getlogpage", "nvme-mi-admin : Get Log Page", getlog) + ENTRY("getfeatures", "nvme-mi-admin : Get Features", getfeature) + ) +); + +#endif + +#include "define_cmd.h" diff --git a/plugins/mi/util/hal/mi-nvme-hal-main.c b/plugins/mi/util/hal/mi-nvme-hal-main.c new file mode 100644 index 0000000..cdabe36 --- /dev/null +++ b/plugins/mi/util/hal/mi-nvme-hal-main.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-hal-main.c - Implementation of HAL Layer for supporting Multiple Hardwares + * for NVMe Management Interface command support + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-hal-main.h" + +void SetSidebandInterface(SidebandInterface interface) +{ + sbInterface = interface; +} + +SidebandInterface GetSidebandInterface() +{ + return sbInterface; +} + +int hal_init() +{ + int retval = -1; + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + retval = qemu_mi_init(); + break; + default: + break; + } + return retval; +} + +int hal_open() +{ + int retval = -1; + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + retval = qemu_mi_open(); + break; + default: + break; + } + return retval; +} + +int hal_close() +{ + int retval = -1; + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + retval = qemu_mi_close(); + break; + default: + break; + } + return retval; +} + +int hal_i2c_write(uint8_t *data_out, uint16_t num_bytes) +{ + int retval = -1; + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + retval = qemu_mi_write(data_out, num_bytes); + break; + default: + break; + } + return retval; +} + +int hal_i2c_read(uint8_t *data_in, uint16_t num_bytes) +{ + uint32_t retval = -1; + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + retval = qemu_mi_read(data_in, num_bytes); + break; + default: + break; + } + return retval; +} diff --git a/plugins/mi/util/hal/mi-nvme-hal-main.h b/plugins/mi/util/hal/mi-nvme-hal-main.h new file mode 100644 index 0000000..ad9bc58 --- /dev/null +++ b/plugins/mi/util/hal/mi-nvme-hal-main.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-hal-main.h - Implementation of HAL Layer for supporting Multiple Hardwares + * for NVMe Management Interface command support + * + * Developer : Mohit Kapoor + */ + +#include +#include "mi-nvme-qemu/mi-nvme-qemu.h" + +typedef enum _SidebandInterface { + qemu_nvme_mi +} SidebandInterface; + +SidebandInterface sbInterface; + +int hal_init(); +int hal_open(); +int hal_close(); +int hal_i2c_write(uint8_t *data_out, uint16_t num_bytes); +int hal_i2c_read(uint8_t *data_in, uint16_t num_bytes); +void SetSidebandInterface(SidebandInterface interface); +SidebandInterface GetSidebandInterface(); diff --git a/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.c b/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.c new file mode 100644 index 0000000..7a0e42f --- /dev/null +++ b/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-qemu.c - Implementation of QEMU HAL Layer for + * for NVMe Management Interface command support via QEMU + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-qemu.h" +static int connectfd = 0; + +int qemu_mi_init() +{ + connectfd = -1; + struct sockaddr_vm sa = { + .svm_family = AF_VSOCK, + .svm_cid = HOST_CID, + .svm_port = 1342, + }; + connectfd = socket(AF_VSOCK, SOCK_STREAM, 0); + if (connectfd == -1) { + printf("Socket Error : Could not create socket!\n"); + } + if (connect(connectfd, (struct sockaddr *)&sa , sizeof(sa)) < 0) { + printf("Socket Error : Could not connect to QEMU Socket!\n"); + return -1; + } + printf("Connection established with QEMU Socket. Socket Handle is 0x%x\n", connectfd); + return connectfd; +} + +int qemu_mi_read(uint8_t *data_in, uint16_t num_bytes) +{ + printf("qemu_mi_read : QEMU Socket handle : 0x%x num bytes to receive : %d\n", connectfd, num_bytes); + int nrecv = 0; + if (connectfd != -1) { + nrecv = 0; + nrecv = recv(connectfd, data_in , num_bytes, 0); + if (nrecv < 0) { + printf("Error while Revieving packet from QEMU!\n"); + return -1; + } + printf("Message successfully Received. Bytes Received : %d\n", nrecv); + } + return nrecv; +} + +int qemu_mi_write(uint8_t *data_out, uint16_t num_bytes) +{ + printf("qemu_mi_write : QEMU Socket handle : 0x%x\n", connectfd); + int nsend = 0; + if (connectfd != -1) { + nsend = write(connectfd, data_out, num_bytes); + if (nsend < 0) { + printf("Error while sending packet to QEMU!\n"); + return -1; + } + printf("Message successfully sent. Bytes sent : %d\n", nsend); + } + return nsend; +} + +int qemu_mi_open() +{ + if (connectfd == -1) { + int handle = -1; + handle = qemu_mi_init(); + if (handle != -1) { + return handle; + } else { + return -1; + } + } else { + printf("qemu_mi_open : QEMU Socket handle : 0x%x\n", connectfd); + return connectfd; + } +} + +int qemu_mi_close() +{ + if (connectfd != -1) { + printf("qemu_mi_close : QEMU Socket handle : 0x%x\n", connectfd); + close(connectfd); + connectfd = -1; + } + return connectfd; +} \ No newline at end of file diff --git a/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.h b/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.h new file mode 100644 index 0000000..4bd084a --- /dev/null +++ b/plugins/mi/util/hal/mi-nvme-qemu/mi-nvme-qemu.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-qemu.h - Implementation of QEMU HAL Layer for + * for NVMe Management Interface command support via QEMU + * + * Developer : Mohit Kapoor + */ + +#ifndef __MI_NVME_QEMU_H__ +#define __MI_NVME_QEMU_H__ + +#include +#include +#include +#include +#include +#include +#define HOST_CID 2 + +char server_reply[2000]; +int n; + +int qemu_mi_open(); +int qemu_mi_close(); +int qemu_mi_init(); +int qemu_mi_read(uint8_t *data_in, uint16_t num_bytes); +int qemu_mi_write(uint8_t *data_out, uint16_t num_bytes); + +#endif diff --git a/plugins/mi/util/mi-nvme-util-base.c b/plugins/mi/util/mi-nvme-util-base.c new file mode 100644 index 0000000..0d71f72 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-base.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-base.c - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-util-master.h" + +extern __u32 TotalByteCount; + +void print_mctp_packet (mctp_i2c_header *m) +{ + printf("\t\t\t Destination Addr : 0x%02X (7-bit: 0x%02x)\n", m->destAddr, m->destAddr >> 1); + printf("\t\t\t Source Addr : 0x%02X (7-bit: 0x%02x)\n", m->srcAddr & 0xFE, m->srcAddr >> 1); + printf("\t\t\t Byte Count : 0x%02X (%d dec)\n", m->byteCnt, m->byteCnt); + printf("\t\t\t MCTP Header Version : %d\n", m->headerVer & 0xF); + printf("\t\t\t Destination Endpoint: 0x%02X\n", m->destEID); + printf("\t\t\t Source Endpoint: 0x%02X\n", m->srcEID); + printf("\t\t\t Message Tag : 0x%X\n", m->pktCtrl & MCTP_CTRL_PKT_MSGTAG_MASK); + printf("\t\t\t MCTP Pkt Seq Num : %d\n", (m->pktCtrl & MCTP_CTRL_PKT_PKTSEQ_MASK) >> MCTP_CTRL_PKT_PKTSEQ_SHIFT); + printf("\t\t\t Packet Control bits : SOM(%d), EOM(%d), TO(%d)\n", + (m->pktCtrl & MCTP_CTRL_PKT_SOM) >> MCTP_CTRL_PKT_SOM_SHIFT, + (m->pktCtrl & MCTP_CTRL_PKT_EOM) >> MCTP_CTRL_PKT_EOM_SHIFT, + (m->pktCtrl & MCTP_CTRL_PKT_TO) >> MCTP_CTRL_PKT_TO_SHIFT); +} + +void format_base_pkt (mctp_message_t *m) +{ + /* Prepare the I2C header */ + m->i2cHdr.cmdCode = MCTP_CMD_CODE; + m->i2cHdr.headerVer = MCTP_HEADER_VER; + m->i2cHdr.destEID = MCTP_EID_DESTINATION; + m->i2cHdr.srcEID = MCTP_EID_SOURCE; + /* Base on Table 11 of the MCTP Base Specification about the Response message setting */ + m->i2cHdr.pktCtrl = MCTP_CTRL_MSGTAG(3) | MCTP_CTRL_PKT_SOM | MCTP_CTRL_PKT_EOM | MCTP_CTRL_PKT_TO | 1; +} + +int rcv_pkt (void *inp_parm) +{ + unsigned int status = 0; + uint8_t eom = 0; + mctp_i2c_header mctp_hdr; + rcv_parm_t *rcv_parm = (rcv_parm_t *)inp_parm; + + printf("Getting Data from the device.\n"); + switch (GetSidebandInterface()) + { + case qemu_nvme_mi: + rcv_parm->buf_size = 0; + memset(&mctp_hdr, 0, sizeof(mctp_i2c_header)); + uint32_t bytesread = 0; + while (!eom) { + uint8_t buf[8]; + /*Reading first 8 bytes of header info*/ + int ret = hal_i2c_read(buf, 8); + if (ret == -1) { + printf("Unable to receive MI response header from device.\n"); + status = -1; + break; + } + mctp_hdr.byteCnt = buf[2]; + eom = (buf[7] & 0x40) >> 6; + printf("Header info received from device:\n"); + print_mctp_packet((mctp_i2c_header *)buf); + + /*copy header info to response buffer*/ + memcpy(rcv_parm->buffer + bytesread, buf, 8); + bytesread += 8; + + /*Reading the data sent in next transaction*/ + ret = hal_i2c_read(rcv_parm->buffer + bytesread, mctp_hdr.byteCnt - 5); + if (ret == -1) { + printf("Unable to receive data from the device.\n"); + status = -1; + break; + } + printf("Data Received from Device:\n"); + PrintBuffer(rcv_parm->buffer + bytesread, mctp_hdr.byteCnt - 5); + rcv_parm->buf_size += mctp_hdr.byteCnt - 5; + bytesread += mctp_hdr.byteCnt - 5; + } + *(rcv_parm->ret_bytes) = rcv_parm->buf_size; + break; + default: + break; + } + return status; +} + +int xmit_pkt (__u8 *buffer) +{ + mctp_message_t *message = NULL; + __u8 *p = NULL; + __u32 mtu = mctp_tus; + __u32 bytesleft = 0; + int ret = -1; + + message = (mctp_message_t *)buffer; + bytesleft = TotalByteCount - 5; + /* Append PEC byte to the end of the packet */ + p = buffer + TotalByteCount + 3; + message->i2cHdr.byteCnt = TotalByteCount; + *p = Calc_Crc8((__u8 *)buffer, TotalByteCount + 3); + + if ((sys_cfg.sys_flags | SYS_FLAG_PEC_OVERRIDE) && + (sys_cfg.peccode == 1)) + { + *p ^= *buffer; /* Screw up the PEC */ + } + + usleep(10); + + print_mctp_packet(&message->i2cHdr); + + if (bytesleft) { + if ((sys_cfg.sys_flags & SYS_FLAG_PEC_OVERRIDE) && + (sys_cfg.peccode == 0)) { + ret = send_data(TotalByteCount+2, (__u8 *)&message->i2cHdr.cmdCode); + } else { + ret = send_data(TotalByteCount+3, (__u8 *)&message->i2cHdr.cmdCode); + } + } else { + bool isStart = true; + __u32 counter = 0; + do + { + mctp_message_t msg_in_chunks; + __u8 *buffer_in_chunks; + + buffer_in_chunks = (__u8*)&msg_in_chunks; + + memcpy(&msg_in_chunks, message, MCTP_HEADER_SIZE); + msg_in_chunks.i2cHdr.byteCnt = mtu + 5; + + msg_in_chunks.i2cHdr.pktCtrl &= 0x0F; + msg_in_chunks.i2cHdr.pktCtrl |= ((counter % 4)<<4); + + if (isStart == true) { + msg_in_chunks.i2cHdr.pktCtrl |= 0x80; /*Start of message*/ + memcpy(&msg_in_chunks.msgHdr, buffer + MCTP_HEADER_SIZE + (mtu*counter), mtu); + isStart = false; + } else if (bytesleft <= mtu) { + msg_in_chunks.i2cHdr.pktCtrl |= 0x40; /*End of message*/ + msg_in_chunks.i2cHdr.byteCnt = bytesleft + 5; + memcpy(&msg_in_chunks.msgHdr, buffer + MCTP_HEADER_SIZE + (mtu*counter), bytesleft); + } else { + msg_in_chunks.i2cHdr.byteCnt = BYTE_COUNT_WHEN_DATA_EXCEEDS_MTU; + memcpy(&msg_in_chunks.msgHdr, buffer + MCTP_HEADER_SIZE + (mtu*counter), mtu); + } + + p = buffer_in_chunks + msg_in_chunks.i2cHdr.byteCnt + 3; + *p = Calc_Crc8((__u8 *)buffer_in_chunks, msg_in_chunks.i2cHdr.byteCnt + 3); + + if ((sys_cfg.sys_flags | SYS_FLAG_PEC_OVERRIDE) && (sys_cfg.peccode == 1)){ + *p ^= *buffer_in_chunks; /* Screw up the PEC */ + } + + if ((sys_cfg.sys_flags & SYS_FLAG_PEC_OVERRIDE) && + (sys_cfg.peccode == 0)) { + ret = send_data(msg_in_chunks.i2cHdr.byteCnt + 2, (__u8 *)&msg_in_chunks.i2cHdr.cmdCode); + if (ret == -1) { + break; + } + } else { + ret = send_data(msg_in_chunks.i2cHdr.byteCnt + 3, (__u8 *)&msg_in_chunks.i2cHdr.cmdCode); + if (ret == -1) { + break; + } + } + + bytesleft -= mtu; + counter++; + } while (bytesleft > 0); + } + return ret; +} + +bool mi_pkt_transaction (__u8 *TxBuf, __u8 *RxBuf, __u16 Rxbuf_size) +{ + mctp_reply_buffer_struct *stReplyStruct; + stReplyStruct = (mctp_reply_buffer_struct*)RxBuf; + + rcv_parm_t rcv_parm; + rcv_parm.buf_size = Rxbuf_size; + rcv_parm.buffer = stReplyStruct->replybuf; + rcv_parm.ret_bytes = &stReplyStruct->length; + rcv_parm.errcode = &stReplyStruct->errorcode; + + int ret = xmit_pkt(TxBuf); + if (ret == -1) { + printf("Unable to send command to device.\n"); + return false; + } + sleep(1); + ret = rcv_pkt (&rcv_parm); + if (ret == -1) { + printf("Unable to receive receive response from device.\n"); + return false; + } + return true; +} + +bool execute_nvme_mi_command(struct nvme_mi_mctp_message_t message, mctp_reply_buffer_struct *stReply, int replysize, int RequestDataSize) +{ + uint8_t *buffer = NULL; + uint32_t size_of_message = 0; + uint32_t crc = 0; + bool ret = false; + + format_base_pkt((mctp_message_t*)&message); + + sys_cfg.sys_flags |= SYS_FLAG_NVME_MI; + + if (message.msgPld.buffer == NULL) { + size_of_message = sizeof(struct nvme_mi_mctp_message_t) - SIZE_OF_BUFFER_ADDRESS; + buffer = (uint8_t*)malloc(size_of_message + 1); /*Adding one for the PEC Byte*/ + TotalByteCount = size_of_message - 3; + + /*Copy the contents of message apart from buffer, as it is NULL*/ + if (buffer !=NULL) { + memcpy(buffer, &message, OFST_TILL_BUFFER_NVME_MI_CMD); + } + } else if (message.msgPld.opcode == 06) { /*This check is for VPD Write*/ + size_of_message = sizeof(struct nvme_mi_mctp_message_t) - SIZE_OF_BUFFER_ADDRESS; + int buffer_len = message.msgPld.dword1 & 0xFFFF; + + size_of_message += buffer_len; + TotalByteCount = size_of_message - 3; + + buffer = (uint8_t*)malloc(size_of_message + 1); /*Adding one for PEC byte*/ + if (buffer !=NULL) { + memcpy(buffer, &message, OFST_TILL_BUFFER_NVME_MI_CMD); + memcpy(buffer + OFST_TILL_BUFFER_NVME_MI_CMD, message.msgPld.buffer, buffer_len); + } + + } else { + size_of_message = sizeof(struct nvme_mi_mctp_message_t) - SIZE_OF_BUFFER_ADDRESS + RequestDataSize; + buffer = (uint8_t*)malloc(size_of_message + 1); /*Add one for the PEC byte*/ + + TotalByteCount = size_of_message - 3; + if (buffer != NULL) { + memcpy(buffer, &message, OFST_TILL_BUFFER_NVME_MI_CMD); + memcpy(buffer+OFST_TILL_BUFFER_NVME_MI_CMD, message.msgPld.buffer, RequestDataSize); + } + } + + if (buffer != NULL) { + crc = GenerateCRC(buffer + MCTP_HEADER_SIZE, size_of_message - MCTP_HEADER_SIZE - CRC_SIZE ); + memcpy(buffer + size_of_message - CRC_SIZE, &crc ,CRC_SIZE); + ret = mi_pkt_transaction(buffer, (__u8 *)stReply, replysize); + if (buffer !=NULL) { + free(buffer); + buffer = NULL; + } + } + + return ret; +} + +bool execute_nvme_mi_admin_command(nvme_mi_admin_cmd_mctp_message message, mctp_reply_buffer_struct *stReply, int replysize, int RequestDataSize) +{ + uint8_t *buffer = NULL; + uint32_t size_of_message = 0; + uint32_t crc = 0; + bool ret = false; + + format_base_pkt((mctp_message_t*)&message); + + if (message.msgPld.buffer == NULL) { + size_of_message = sizeof(nvme_mi_admin_cmd_mctp_message) - SIZE_OF_BUFFER_ADDRESS; + buffer = (uint8_t*)malloc(size_of_message + 1); // Adding one for the PEC byte + + TotalByteCount = size_of_message - 3; + + /*Copy the contents of message apart from buffer, as it is NULL*/ + if (buffer != NULL) { + memcpy(buffer, &message, OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD); + memcpy(buffer + OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD, (char*)&message + + OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD + SIZE_OF_BUFFER_ADDRESS,SIZE_OF_MIC); + } + } else { + size_of_message = sizeof(nvme_mi_admin_cmd_mctp_message) - SIZE_OF_BUFFER_ADDRESS + RequestDataSize; + buffer = (uint8_t*)malloc(size_of_message + 1); /*Adding one for the PEC Byte*/ + + TotalByteCount = size_of_message - 3; + if (buffer !=NULL) { + memcpy(buffer, &message, OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD); + memcpy(buffer + OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD, message.msgPld.buffer, RequestDataSize); + } + } + + if (buffer !=NULL) { + /*CRC has to be calculated only for the message body*/ + crc = GenerateCRC(buffer + MCTP_HEADER_SIZE, size_of_message - MCTP_HEADER_SIZE - CRC_SIZE); + memcpy(buffer + size_of_message - CRC_SIZE, &crc, CRC_SIZE); + ret = mi_pkt_transaction(buffer, (__u8 *)stReply, replysize); + } + + if (buffer !=NULL) { + free(buffer); + buffer = NULL; + } + + return ret; +} diff --git a/plugins/mi/util/mi-nvme-util-base.h b/plugins/mi/util/mi-nvme-util-base.h new file mode 100644 index 0000000..f9e41fc --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-base.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-base.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#ifndef __MI_NVME_UTIL_BASE_H__ +#define __MI_NVME_UTIL_BASE_H__ + +#include +#include + +#define MCTP_CMD_CODE 0x0F +#define MCTP_HEADER_VER 0x1 + +#define MCTP_EID_DESTINATION 0x00 +#define MCTP_EID_SOURCE 0x00 + +/* Byte 7 Packet Control Bits */ +#define MCTP_CTRL_PKT_MSGTAG_MASK 0x07 +#define MCTP_CTRL_PKT_TO 0x08 +#define MCTP_CTRL_PKT_TO_SHIFT 3 +#define MCTP_CTRL_PKT_PKTSEQ_MASK 0x30 +#define MCTP_CTRL_PKT_PKTSEQ_SHIFT 4 +#define MCTP_CTRL_PKT_EOM 0x40 +#define MCTP_CTRL_PKT_EOM_SHIFT 6 +#define MCTP_CTRL_PKT_SOM 0x80 +#define MCTP_CTRL_PKT_SOM_SHIFT 7 +#define MCTP_CTRL_MSGTAG(x) (x & MCTP_CTRL_PKT_MSGTAG_MASK) +#define MCTP_CTRL_PKTSEQ(x) ((x << MCTP_CTRL_PKT_PKTSEQ_SHIFT) & MCTP_CTRL_PKT_PKTSEQ_MASK) + +/* Byte 8 Packet Message Type Bits */ +#define MCTP_CTRL_MSG_IC 0x80 +#define MCTP_CTRL_MSG_IC_SHIFT 7 +#define VDM_ARG_COUNT 20 + +#define SIZE_OF_BUFFER_ADDRESS 8 +#define SIZE_OF_32_BUFFER_ADDRESS 4 +#define OFST_TILL_BUFFER_NVME_MI_ADMIN_CMD 76 +#define OFST_TILL_BUFFER_NVME_MI_CMD 24 +#define SIZE_OF_MIC 4 +#define MCTP_HEADER_SIZE 8 +#define CRC_SIZE 4 +#define BYTE_COUNT_WHEN_DATA_EXCEEDS_MTU 69 +#define REPLY_BUFFER_SIZE 5120 +#define REPLAY_BUFFER_SIZE 5120 +#define REPLAY_RESPONSE_MESSAGE_SIZE 20 + +#define PACKED +#pragma pack(push,1) + +typedef struct _mctp_i2c_header { + __u8 destAddr; + __u8 cmdCode; + __u8 byteCnt; + __u8 srcAddr; + __u8 headerVer; + __u8 destEID; + __u8 srcEID; + /* Byte 3 for msgTag(2:0), TO(3), pktSeq(5:4), EOM(6), SOM(7) */ + __u8 pktCtrl; +} mctp_i2c_header; + +typedef struct mctp_msg_header_ { + __u8 msgTpe; + union + { + struct + { + /* Byte for InstID(4:0), D(6), Rq(7) fields */ + __u8 InstCde; + __u8 cmdCode; + __u8 OpCpl; + } ctrMsg; + } msgReqRsp; +} mctp_msg_header_t; + +typedef struct mctp_msg_payload_ { + union + { + struct + { + __u8 EID_status; + __u8 EP_Type; + __u8 Misc; + __u8 byte[((VDM_ARG_COUNT*4)+13)]; + } baseCtrl; + } dataPld; +} mctp_msg_payload_t; + +typedef struct mctp_message_ { + mctp_i2c_header i2cHdr; + mctp_msg_header_t msgHdr; + mctp_msg_payload_t msgPld; + __u32 pad[1]; +} mctp_message_t; + +typedef struct _mctp_message_header_t +{ + __u8 messsage_type : 7; + __u8 ic : 1; + __u8 instance_id : 5; + __u8 rsvd : 1; + __u8 D : 1; + __u8 RQ : 1; + __u8 Command_Code; +}mctp_message_header_t; + +typedef struct _nvme_mi_mctp_cmd_pld +{ + mctp_message_header_t nvme_mi_message_header; + __u8* buffer; +}nvme_mi_mctp_cmd_pld; + +typedef struct _mctp_command_packet +{ + mctp_i2c_header i2cHdr; + nvme_mi_mctp_cmd_pld msgPld; +}mctp_command_packet; + +typedef struct _mctp_command_reply_packet +{ + mctp_i2c_header i2cHdr; + nvme_mi_mctp_cmd_pld msgPld; +}mctp_command_reply_packet; + +struct nvme_mi_mctp_message_pld +{ + __u32 nvme_mi_message_header; + __u8 opcode; + __u8 reserved0; + __u8 reserved1; + __u8 reserved2; + __u32 dword0; + __u32 dword1; + __u8 *buffer; + __u32 mic; +}__attribute__((packed)); + +typedef struct _nvme_mi_Admin_cmd_mctp_message_pld +{ + __u32 nvme_mi_message_header; + __u8 opcode; + __u8 cflgs; + __u16 ctlid; + __u32 cdw1; + __u32 cdw2; + __u32 cdw3; + __u32 cdw4; + __u32 cdw5; + __u32 dofst; + __u32 dlen; + __u32 cdw8; + __u32 cdw9; + __u32 cdw10; + __u32 cdw11; + __u32 cdw12; + __u32 cdw13; + __u32 cdw14; + __u32 cdw15; + __u8* buffer; + __u32 mic; +}nvme_mi_Admin_cmd_mctp_message_pld; + +struct nvme_mi_mctp_message_t { + mctp_i2c_header i2cHdr; + struct nvme_mi_mctp_message_pld msgPld; +}__attribute__((packed)); + +typedef struct _nvme_mi_admin_cmd_mctp_message_ { + mctp_i2c_header i2cHdr; + nvme_mi_Admin_cmd_mctp_message_pld msgPld; +}nvme_mi_admin_cmd_mctp_message; + +typedef struct rcv_parm_ +{ + __u16 buf_size; + __u8 *buffer; + __u32 *ret_bytes; + __u32 *errcode; +} rcv_parm_t; + +typedef struct _mctp_reply_buffer_struct +{ + __u8 replybuf[REPLY_BUFFER_SIZE]; + __u32 length; + __u32 errorcode; +}mctp_reply_buffer_struct; + +#pragma pack(pop) +#undef PACKED + +void format_base_pkt(mctp_message_t *m); +int rcv_pkt(void *inp_parm); +int xmit_pkt(__u8 *buffer); +bool mi_pkt_transaction(__u8 *TxBuf, __u8 *RxBuf, __u16 Rxbuf_size); +bool execute_nvme_mi_command(struct nvme_mi_mctp_message_t message, mctp_reply_buffer_struct *stReply, int replysize, int RequestDataSize); +bool execute_nvme_mi_admin_command(nvme_mi_admin_cmd_mctp_message message, mctp_reply_buffer_struct *stReply, int replysize, int RequestDataSize); + +#endif diff --git a/plugins/mi/util/mi-nvme-util-crc.c b/plugins/mi/util/mi-nvme-util-crc.c new file mode 100644 index 0000000..76e1171 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-crc.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-crc.c - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-util-master.h" + +static __u8 crc8(__u8 crc, __u8 crc_data) +{ + __u8 i = 0, data = 0; + data = crc ^ crc_data; + + for ( i = 0; i < 8; i++ ) { + if (( data & 0x80 ) != 0 ) { + data <<= 1; + data ^= 0x07; + } else { + data <<= 1; + } + } + return data; +} + +__u8 Calc_Crc8(__u8 *Buffer, __u8 byte_cnt) +{ + __u8 crc = 0, *p; + int i; + p = Buffer; + + for (i = 0; i < byte_cnt; i++) { + crc = crc8(crc, *p++); + } + return (crc); +} + +uint32_t GenerateCRC(uint8_t *message, uint32_t length) +{ + if (message != NULL) { + uint32_t crc = Calc_Crc32(0x1EDC6F41, -1, message, length); + printf("Generated CRC32 : %"PRIx32" \n", crc); + return crc; + } + return 0; +} + +void gen_crc_table(uint32_t poly) +{ + register uint16_t i = 0, j = 0; + register uint32_t crc_accum = 0; + + for (i = 0; i < 256; i++) { + crc_accum = ( (uint32_t) i << 24 ); + for (j = 0; j < 8; j++ ) { + if (crc_accum & 0x80000000L) { + crc_accum = (crc_accum << 1) ^ poly; + } else { + crc_accum = (crc_accum << 1); + } + } + crc_table[i] = crc_accum; + } +} + +uint32_t Calc_Crc32(uint32_t poly, uint32_t crc_accum, uint8_t *data_blk_ptr, uint32_t data_blk_size) +{ + register uint32_t i = 0, j = 0; + gen_crc_table(poly); + + for (j = 0; j < data_blk_size; j++) { + i = ((int) (crc_accum >> 24) ^ *data_blk_ptr++) & 0xFF; + crc_accum = (crc_accum << 8) ^ crc_table[i]; + } + crc_accum = ~crc_accum; + return crc_accum; +} diff --git a/plugins/mi/util/mi-nvme-util-crc.h b/plugins/mi/util/mi-nvme-util-crc.h new file mode 100644 index 0000000..a9f6f54 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-crc.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-crc.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#ifndef __MI_NVME_UTIL_CRC_H__ +#define __MI_NVME_UTIL_CRC_H__ + +#include +#include +#include + +uint32_t crc_table[256]; + +uint32_t GenerateCRC(uint8_t *message, uint32_t length); +void gen_crc_table(); +uint32_t Calc_Crc32(uint32_t poly, uint32_t crc_accum, uint8_t *message, uint32_t size); +__u8 Calc_Crc8(__u8 *Buffer, __u8 byte_cnt); + +#endif diff --git a/plugins/mi/util/mi-nvme-util-master.h b/plugins/mi/util/mi-nvme-util-master.h new file mode 100644 index 0000000..ae2a322 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-master.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-master.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#ifndef __MI_NVME_UTIL_MASTER_H__ +#define __MI_NVME_UTIL_MASTER_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include "hal/mi-nvme-hal-main.h" +#include "mi-nvme-util-tool.h" +#include "mi-nvme-util-base.h" +#include "mi-nvme-util-crc.h" + +#endif diff --git a/plugins/mi/util/mi-nvme-util-tool.c b/plugins/mi/util/mi-nvme-util-tool.c new file mode 100644 index 0000000..8d7f533 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-tool.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-tool.c - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-util-master.h" + +static bool state_dev = false; +static bool hardware_init = false; + +system_cfg_t sys_cfg; +__u32 mctp_tus; +__u32 TotalByteCount; + +bool initialize(__u32 uiMCTP_TUS) +{ + bool retval = false; + memset(&sys_cfg, 0, sizeof(system_cfg_t)); + + switch (GetSidebandInterface()) { + case qemu_nvme_mi: + mctp_tus = uiMCTP_TUS; + if (!hardware_init) { + int ret = hal_init(); + if (ret == -1) { + printf("Initialiation Failed.\n"); + return false; + } + printf("QEMU Socket initialized : %d\n", ret); + hardware_init = true; + } + + retval = open_device(); + if (retval == false) { + printf("open device Failed!\n"); + return false; + } + break; + default: + break; + } + + return retval; +} + +int send_data(__u16 num_write, __u8 *data_out) +{ + int count = 0; + + printf("\nData being written to the device, byte count = 0x%X:\n", num_write); + __u8 *buffertemp = (__u8*)malloc(num_write + 1); + if (buffertemp != NULL) { + buffertemp[0] = sys_cfg.taddr << 1; + memcpy(&buffertemp[1], data_out, num_write); + PrintBuffer(buffertemp, num_write + 1); + + free(buffertemp); + buffertemp = NULL; + } + + count = hal_i2c_write(data_out, num_write); + usleep(10); + + printf("num_write=0x%02x(0x%02x)\n", num_write, count); + + if (count < 0) { + printf("Error in sending data\n"); + return -1; + } + printf("Number of bytes written to the device 0x%02x(%d)\n", count, count); + + return count; +} + +bool open_device() +{ + int status = -1, i = 0; + + if (!state_dev) { + for (i = 0; i < MAX_OPEN_RETRY; i++) { + status = hal_open(); + if (status < 0) { + printf("Unable to open device\n"); + } else { + break; + } + } + + if (status <= 0) { + printf("Unable to open device on port %d\n", sys_cfg.aport); + return false; + } + + state_dev = true; + } + return true; +} + +bool close_device() +{ + int status = -1, i = 0; + + if (state_dev) { + for (i = 0; i < MAX_CLOSE_RETRY; i++) { + status = hal_close(); + if (status == -1) { + break; + } + } + + if (status != -1) { + printf("Device handle close unsuccessful!\n"); + return false; + } + state_dev = false; + } + return true; +} + +void PrintBuffer( __u8* buffer,__u32 length) +{ + __u32 i = 0; + for (i = 0; i < length; i += 0x8) { + if (i+1 > length -1) { + printf("%06x %02x\n",i,buffer[i]); + } else if (i+2 > length -1) { + printf("%06x %02x %02x \n",i, buffer[i], \ + buffer[i + 1]); + } else if (i+3 > length -1) { + printf("%06x %02x %02x %02x\n", i, buffer[i], \ + buffer[i + 1], buffer[i + 2]); + } else if (i+4 > length -1) { + printf("%06x %02x %02x %02x %02x\n", i, buffer[i], \ + buffer[i + 1], buffer[i + 2], buffer[i + 3]); + } else if (i+5 > length -1) { + printf("%06x %02x %02x %02x %02x %02x\n", i, buffer[i], \ + buffer[i + 1], buffer[i + 2], buffer[i + 3], buffer[i + 4]); + } else if (i+6 > length -1) { + printf("%06x %02x %02x %02x %02x %02x %02x\n", i, \ + buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], \ + buffer[i + 4], buffer[i + 5]); + } else if (i+7 > length -1) { + printf("%06x %02x %02x %02x %02x %02x %02x %02x\n", i, \ + buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], \ + buffer[i + 4], buffer[i + 5], buffer[i + 6]); + } else { + printf("%06x %02x %02x %02x %02x %02x %02x %02x %02x\n",i , \ + buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], \ + buffer[i + 4], buffer[i + 5], buffer[i + 6], buffer[i + 7]); + } + } +} diff --git a/plugins/mi/util/mi-nvme-util-tool.h b/plugins/mi/util/mi-nvme-util-tool.h new file mode 100644 index 0000000..1b7b103 --- /dev/null +++ b/plugins/mi/util/mi-nvme-util-tool.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 Samsung Electronics Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * mi-nvme-util-tool.h - Implementation of NVMe Management Interface commands in Nvme + * + * Developer : Mohit Kapoor + */ + +#include "mi-nvme-util-base.h" + +#ifndef __MI_NVME_UTIL_TOOL_H__ +#define __MI_NVME_UTIL_TOOL_H__ + +#define SYS_FLAG_PEC_OVERRIDE 0x00000400 +#define SYS_FLAG_NVME_MI 0x00010000 +#define MAX_OPEN_RETRY 10 +#define MAX_CLOSE_RETRY 5 + +typedef struct _system_cfg_t { + __u32 sys_flags; + __u32 verbose_level; + __u32 op_state; + + int phandle; + int dhandle; + __u16 iport; + __u16 aport; + __u16 dport; + + __u8 taddr; + __u8 saddr; + + __u8 peccode; +} system_cfg_t; + +__u32 mctp_tus; +extern system_cfg_t sys_cfg; + +bool initialize(__u32 uiMCTP_TUS); +int send_data(__u16 num_write, __u8 *data_out); +bool open_device(); +bool close_device(); +void PrintBuffer(__u8* buffer,__u32 length); + +#endif