summaryrefslogtreecommitdiffstats
path: root/src/nvme/mi.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvme/mi.h')
-rw-r--r--src/nvme/mi.h1111
1 files changed, 1111 insertions, 0 deletions
diff --git a/src/nvme/mi.h b/src/nvme/mi.h
new file mode 100644
index 0000000..81cb00f
--- /dev/null
+++ b/src/nvme/mi.h
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * This file is part of libnvme.
+ * Copyright (c) 2021 Code Construct Pty Ltd
+ *
+ * Authors: Jeremy Kerr <jk@codeconstruct.com.au>
+ */
+
+/**
+ * DOC: mi.h - NVMe Management Interface library (libnvme-mi) definitions.
+ *
+ * These provide an abstraction for the MI messaging between controllers
+ * and a host, typically over an MCTP-over-i2c link to a NVMe device, used
+ * as part of the out-of-band management of a system.
+ *
+ * We have a few data structures define here to reflect the topology
+ * of a MI connection with an NVMe subsystem:
+ *
+ * - &nvme_mi_ep_t: an MI endpoint - our mechanism of communication with a
+ * NVMe subsystem. For MCTP, an endpoint will be the component that
+ * holds the MCTP address (EID), and receives our request message.
+ *
+ * endpoints are defined in the NVMe-MI spec, and are specific to the MI
+ * interface.
+ *
+ * Each endpoint will provide access to one or more of:
+ *
+ * - &nvme_mi_ctrl_t: a NVMe controller, as defined by the NVMe base spec.
+ * The controllers are responsible for processing any NVMe standard
+ * commands (eg, the Admin command set). An endpoint (&nvme_mi_ep_t)
+ * may provide access to multiple controllers - so each of the controller-
+ * type commands will require a &nvme_mi_ctrl_t to be specified, rather than
+ * an endpoint
+ *
+ * A couple of conventions with the libnvme-mi API:
+ *
+ * - All types and functions have the nvme_mi prefix, to distinguish from
+ * the libnvme core.
+ *
+ * - We currently support either MI commands and Admin commands. The
+ * former adds a _mi prefix, the latter an _admin prefix. [This does
+ * result in the MI functions having a double _mi, like
+ * &nvme_mi_mi_subsystem_health_status_poll, which is apparently amusing
+ * for our German-speaking readers]
+ *
+ * For return values: unless specified in the per-function documentation,
+ * all functions:
+ *
+ * - return 0 on success
+ *
+ * - return -1, with errno set, for errors communicating with the MI device,
+ * either in request or response data
+ *
+ * - return >1 on MI status errors. This value is the 8-bit MI status
+ * value, represented by &enum nvme_mi_resp_status. Note that the
+ * status values may be vendor-defined above 0xe0.
+ *
+ * For the second case, we have a few conventions for errno values:
+ *
+ * - EPROTO: response data violated the MI protocol, and libnvme cannot
+ * validly interpret the response
+ *
+ * - EIO: Other I/O error communicating with device (eg., valid but
+ * unexpected response data)
+ *
+ * - EINVAL: invalid input arguments for a command
+ *
+ * In line with the core NVMe API, the Admin command functions take an
+ * `_args` structure to provide the command-specific parameters. However,
+ * for the MI interface, the fd and timeout members of these _args structs
+ * are ignored.
+ *
+ * References to the specifications here will either to be the NVM Express
+ * Management Interface ("NVMe-MI") or the NVM Express Base specification
+ * ("NVMe"). At the time of writing, the versions we're referencing here
+ * are:
+ * - NVMe-MI 1.2b
+ * - NVMe 2.0b
+ * with a couple of accommodations for older spec types, particularly NVMe-MI
+ * 1.1, where possible.
+ *
+ */
+
+#ifndef _LIBNVME_MI_MI_H
+#define _LIBNVME_MI_MI_H
+
+#include <endian.h>
+#include <stdint.h>
+
+#include "types.h"
+#include "tree.h"
+
+/**
+ * NVME_MI_MSGTYPE_NVME - MCTP message type for NVMe-MI messages.
+ *
+ * This is defined by MCTP, but is referenced as part of the NVMe-MI message
+ * spec. This is the MCTP NVMe message type (0x4), with the message-integrity
+ * bit (0x80) set.
+ */
+#define NVME_MI_MSGTYPE_NVME 0x84
+
+/* Basic MI message definitions */
+
+/**
+ * enum nvme_mi_message_type - NVMe-MI message type field.
+ * @NVME_MI_MT_CONTROL: NVME-MI Control Primitive
+ * @NVME_MI_MT_MI: NVMe-MI command
+ * @NVME_MI_MT_ADMIN: NVMe Admin command
+ * @NVME_MI_MT_PCIE: PCIe command
+ *
+ * Used as byte 1 of both request and response messages (NMIMT bits of NMP
+ * byte). Not to be confused with the MCTP message type in byte 0.
+ */
+enum nvme_mi_message_type {
+ NVME_MI_MT_CONTROL = 0,
+ NVME_MI_MT_MI = 1,
+ NVME_MI_MT_ADMIN = 2,
+ NVME_MI_MT_PCIE = 4,
+};
+
+/**
+ * enum nvme_mi_ror: Request or response field.
+ * @NVME_MI_ROR_REQ: request message
+ * @NVME_MI_ROR_RSP: response message
+ */
+enum nvme_mi_ror {
+ NVME_MI_ROR_REQ = 0,
+ NVME_MI_ROR_RSP = 1,
+};
+
+/**
+ * enum nvme_mi_resp_status - values for the response status field
+ * @NVME_MI_RESP_SUCCESS: success
+ * @NVME_MI_RESP_MPR: More Processing Required
+ * @NVME_MI_RESP_INTERNAL_ERR: Internal Error
+ * @NVME_MI_RESP_INVALID_OPCODE: Invalid command opcode
+ * @NVME_MI_RESP_INVALID_PARAM: Invalid command parameter
+ * @NVME_MI_RESP_INVALID_CMD_SIZE: Invalid command size
+ * @NVME_MI_RESP_INVALID_INPUT_SIZE: Invalid command input data size
+ * @NVME_MI_RESP_ACCESS_DENIED: Access Denied
+ * @NVME_MI_RESP_VPD_UPDATES_EXCEEDED: More VPD updates than allowed
+ * @NVME_MI_RESP_PCIE_INACCESSIBLE: PCIe functionality currently unavailable
+ * @NVME_MI_RESP_MEB_SANITIZED: MEB has been cleared due to sanitize
+ * @NVME_MI_RESP_ENC_SERV_FAILURE: Enclosure services process failed
+ * @NVME_MI_RESP_ENC_SERV_XFER_FAILURE: Transfer with enclosure services failed
+ * @NVME_MI_RESP_ENC_FAILURE: Unreoverable enclosure failure
+ * @NVME_MI_RESP_ENC_XFER_REFUSED: Enclosure services transfer refused
+ * @NVME_MI_RESP_ENC_FUNC_UNSUP: Unsupported enclosure services function
+ * @NVME_MI_RESP_ENC_SERV_UNAVAIL: Enclosure services unavailable
+ * @NVME_MI_RESP_ENC_DEGRADED: Noncritical failure detected by enc. services
+ * @NVME_MI_RESP_SANITIZE_IN_PROGRESS: Command prohibited during sanitize
+ */
+enum nvme_mi_resp_status {
+ NVME_MI_RESP_SUCCESS = 0x00,
+ NVME_MI_RESP_MPR = 0x01,
+ NVME_MI_RESP_INTERNAL_ERR = 0x02,
+ NVME_MI_RESP_INVALID_OPCODE = 0x03,
+ NVME_MI_RESP_INVALID_PARAM = 0x04,
+ NVME_MI_RESP_INVALID_CMD_SIZE = 0x05,
+ NVME_MI_RESP_INVALID_INPUT_SIZE = 0x06,
+ NVME_MI_RESP_ACCESS_DENIED = 0x07,
+ /* 0x08 - 0x1f: reserved */
+ NVME_MI_RESP_VPD_UPDATES_EXCEEDED = 0x20,
+ NVME_MI_RESP_PCIE_INACCESSIBLE = 0x21,
+ NVME_MI_RESP_MEB_SANITIZED = 0x22,
+ NVME_MI_RESP_ENC_SERV_FAILURE = 0x23,
+ NVME_MI_RESP_ENC_SERV_XFER_FAILURE = 0x24,
+ NVME_MI_RESP_ENC_FAILURE = 0x25,
+ NVME_MI_RESP_ENC_XFER_REFUSED = 0x26,
+ NVME_MI_RESP_ENC_FUNC_UNSUP = 0x27,
+ NVME_MI_RESP_ENC_SERV_UNAVAIL = 0x28,
+ NVME_MI_RESP_ENC_DEGRADED = 0x29,
+ NVME_MI_RESP_SANITIZE_IN_PROGRESS = 0x2a,
+ /* 0x2b - 0xdf: reserved */
+ /* 0xe0 - 0xff: vendor specific */
+};
+
+/**
+ * struct nvme_mi_msg_hdr - General MI message header.
+ * @type: MCTP message type, will always be NVME_MI_MSGTYPE_NVME
+ * @nmp: NVMe-MI message parameters (including MI message type)
+ * @meb: Management Endpoint Buffer flag; unused for libnvme-mi implementation
+ * @rsvd0: currently reserved
+ *
+ * Wire format shared by both request and response messages, per NVMe-MI
+ * section 3.1. This is used for all message types, MI and Admin.
+ */
+struct nvme_mi_msg_hdr {
+ __u8 type;
+ __u8 nmp;
+ __u8 meb;
+ __u8 rsvd0;
+} __attribute__((packed));
+
+/**
+ * struct nvme_mi_msg_resp - Generic response type.
+ * @hdr: the general request/response message header
+ * @status: response status value (see &enum nvme_mi_resp_status)
+ * @rsvd0: reserved data, may be defined by specific response
+ *
+ * Every response will start with one of these; command-specific responses
+ * will define parts of the reserved data, and may add further fields.
+ */
+struct nvme_mi_msg_resp {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+};
+
+/**
+ * enum nvme_mi_mi_opcode - Operation code for supported NVMe-MI commands.
+ * @nvme_mi_mi_opcode_mi_data_read: Read NVMe-MI Data Structure
+ * @nvme_mi_mi_opcode_subsys_health_status_poll: Subsystem Health Status Poll
+ * @nvme_mi_mi_opcode_configuration_set: MI Configuration Set
+ * @nvme_mi_mi_opcode_configuration_get: MI Configuration Get
+ */
+enum nvme_mi_mi_opcode {
+ nvme_mi_mi_opcode_mi_data_read = 0x00,
+ nvme_mi_mi_opcode_subsys_health_status_poll = 0x01,
+ nvme_mi_mi_opcode_configuration_set = 0x03,
+ nvme_mi_mi_opcode_configuration_get = 0x04,
+};
+
+/**
+ * struct nvme_mi_mi_req_hdr - MI request message header.
+ * @hdr: generic MI message header
+ * @opcode: opcode (OPC) for the specific MI command
+ * @rsvd0: reserved bytes
+ * @cdw0: Management Request Doubleword 0 - command specific usage
+ * @cdw1: Management Request Doubleword 1 - command specific usage
+ *
+ * Wire format for MI request message headers, defined in section 5 of NVMe-MI.
+ */
+struct nvme_mi_mi_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1;
+};
+
+/**
+ * struct nvme_mi_mi_resp_hdr - MI response message header.
+ * @hdr: generic MI message header
+ * @status: generic response status from command; non-zero on failure.
+ * @nmresp: NVMe Management Response: command-type-specific response data
+ *
+ * Wire format for MI response message header, defined in section 5 of NVMe-MI.
+ */
+struct nvme_mi_mi_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 nmresp[3];
+};
+
+/**
+ * enum nvme_mi_dtyp - Data Structure Type field.
+ * @nvme_mi_dtyp_subsys_info: NVM Subsystem Information
+ * @nvme_mi_dtyp_port_info: Port information
+ * @nvme_mi_dtyp_ctrl_list: Controller List
+ * @nvme_mi_dtyp_ctrl_info: Controller Information
+ * @nvme_mi_dtyp_opt_cmd_support: Optionally Supported Command List
+ * @nvme_mi_dtyp_meb_support: Management Endpoint Buffer Command Support List
+ *
+ * Data Structure Type field for Read NVMe-MI Data Structure command, used to
+ * indicate the particular structure to query from the endpoint.
+ */
+enum nvme_mi_dtyp {
+ nvme_mi_dtyp_subsys_info = 0x00,
+ nvme_mi_dtyp_port_info = 0x01,
+ nvme_mi_dtyp_ctrl_list = 0x02,
+ nvme_mi_dtyp_ctrl_info = 0x03,
+ nvme_mi_dtyp_opt_cmd_support = 0x04,
+ nvme_mi_dtyp_meb_support = 0x05,
+};
+
+/**
+ * enum nvme_mi_config_id - NVMe-MI Configuration identifier.
+ * @NVME_MI_CONFIG_SMBUS_FREQ: Current SMBus/I2C frequency
+ * @NVME_MI_CONFIG_HEALTH_STATUS_CHANGE: Health Status change - used to clear
+ * health status bits in CCS bits of
+ * status poll. Only for Set ops.
+ * @NVME_MI_CONFIG_MCTP_MTU: MCTP maximum transmission unit size of port
+ * specified in dw 0
+ *
+ * Configuration parameters for the MI Get/Set Configuration commands.
+ *
+ * See &nvme_mi_mi_config_get() and &nvme_mi_config_set().
+ */
+enum nvme_mi_config_id {
+ NVME_MI_CONFIG_SMBUS_FREQ = 0x1,
+ NVME_MI_CONFIG_HEALTH_STATUS_CHANGE = 0x2,
+ NVME_MI_CONFIG_MCTP_MTU = 0x3,
+};
+
+/**
+ * enum nvme_mi_config_smbus_freq - SMBus/I2C frequency values
+ * @NVME_MI_CONFIG_SMBUS_FREQ_100kHz: 100kHz
+ * @NVME_MI_CONFIG_SMBUS_FREQ_400kHz: 400kHz
+ * @NVME_MI_CONFIG_SMBUS_FREQ_1MHz: 1MHz
+ *
+ * Values used in the SMBus Frequency device configuration. See
+ * &nvme_mi_mi_config_get_smbus_freq() and &nvme_mi_mi_config_set_smbus_freq().
+ */
+enum nvme_mi_config_smbus_freq {
+ NVME_MI_CONFIG_SMBUS_FREQ_100kHz = 0x1,
+ NVME_MI_CONFIG_SMBUS_FREQ_400kHz = 0x2,
+ NVME_MI_CONFIG_SMBUS_FREQ_1MHz = 0x3,
+};
+
+/* Admin command definitions */
+
+/**
+ * struct nvme_mi_admin_req_hdr - Admin command request header.
+ * @hdr: Generic MI message header
+ * @opcode: Admin command opcode (using enum nvme_admin_opcode)
+ * @flags: Command Flags, indicating dlen and doff validity; Only defined in
+ * NVMe-MI version 1.1, no fields defined in 1.2 (where the dlen/doff
+ * are always considered valid).
+ * @ctrl_id: Controller ID target of command
+ * @cdw1: Submission Queue Entry doubleword 1
+ * @cdw2: Submission Queue Entry doubleword 2
+ * @cdw3: Submission Queue Entry doubleword 3
+ * @cdw4: Submission Queue Entry doubleword 4
+ * @cdw5: Submission Queue Entry doubleword 5
+ * @doff: Offset of data to return from command
+ * @dlen: Length of sent/returned data
+ * @rsvd0: Reserved
+ * @rsvd1: Reserved
+ * @cdw10: Submission Queue Entry doubleword 10
+ * @cdw11: Submission Queue Entry doubleword 11
+ * @cdw12: Submission Queue Entry doubleword 12
+ * @cdw13: Submission Queue Entry doubleword 13
+ * @cdw14: Submission Queue Entry doubleword 14
+ * @cdw15: Submission Queue Entry doubleword 15
+ *
+ * Wire format for Admin command message headers, defined in section 6 of
+ * NVMe-MI.
+ */
+struct nvme_mi_admin_req_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 opcode;
+ __u8 flags;
+ __le16 ctrl_id;
+ __le32 cdw1, cdw2, cdw3, cdw4, cdw5;
+ __le32 doff;
+ __le32 dlen;
+ __le32 rsvd0, rsvd1;
+ __le32 cdw10, cdw11, cdw12, cdw13, cdw14, cdw15;
+} __attribute((packed));
+
+/**
+ * struct nvme_mi_admin_resp_hdr - Admin command response header.
+ * @hdr: Generic MI message header
+ * @status: Generic response code, non-zero on failure
+ * @rsvd0: Reserved
+ * @cdw0: Completion Queue Entry doubleword 0
+ * @cdw1: Completion Queue Entry doubleword 1
+ * @cdw3: Completion Queue Entry doubleword 3
+ *
+ * This is the generic response format with the three doublewords of completion
+ * queue data, plus optional response data.
+ */
+struct nvme_mi_admin_resp_hdr {
+ struct nvme_mi_msg_hdr hdr;
+ __u8 status;
+ __u8 rsvd0[3];
+ __le32 cdw0, cdw1, cdw3;
+} __attribute__((packed));
+
+
+/**
+ * nvme_mi_create_root() - Create top-level MI (root) handle.
+ * @fp: File descriptor for logging messages
+ * @log_level: Logging level to use
+ *
+ * Create the top-level (library) handle for creating subsequent endpoint
+ * objects. Similar to nvme_create_root(), but we provide this to allow linking
+ * without the core libnvme.
+ *
+ * Return: new root object, or NULL on failure.
+ *
+ * See &nvme_create_root.
+ */
+nvme_root_t nvme_mi_create_root(FILE *fp, int log_level);
+
+/**
+ * nvme_mi_free_root() - Free root object.
+ * @root: root to free
+ */
+void nvme_mi_free_root(nvme_root_t root);
+
+/* Top level management object: NVMe-MI Management Endpoint */
+struct nvme_mi_ep;
+
+/**
+ * typedef nvme_mi_ep_t - MI Endpoint object.
+ *
+ * Represents our communication endpoint on the remote MI-capable device.
+ * To be used for direct MI commands for the endpoint (through the
+ * nvme_mi_mi_* functions(), or to communicate with individual controllers
+ * (see &nvme_mi_init_ctrl).
+ *
+ * Endpoints are created through a transport-specific constructor; currently
+ * only MCTP-connected endpoints are supported, through &nvme_mi_open_mctp.
+ * Subsequent operations on the endpoint (and related controllers) are
+ * transport-independent.
+ */
+typedef struct nvme_mi_ep * nvme_mi_ep_t;
+
+/**
+ * nvme_mi_first_endpoint - Start endpoint iterator
+ * @m: &nvme_root_t object
+ *
+ * Return: first MI endpoint object under this root, or NULL if no endpoints
+ * are present.
+ *
+ * See: &nvme_mi_next_endpoint, &nvme_mi_for_each_endpoint
+ */
+nvme_mi_ep_t nvme_mi_first_endpoint(nvme_root_t m);
+
+/**
+ * nvme_mi_next_endpoint - Continue endpoint iterator
+ * @m: &nvme_root_t object
+ * @e: &nvme_mi_ep_t current position of iterator
+ *
+ * Return: next endpoint MI endpoint object after @e under this root, or NULL
+ * if no further endpoints are present.
+ *
+ * See: &nvme_mi_first_endpoint, &nvme_mi_for_each_endpoint
+ */
+nvme_mi_ep_t nvme_mi_next_endpoint(nvme_root_t m, nvme_mi_ep_t e);
+
+/**
+ * nvme_mi_for_each_endpoint - Iterator for NVMe-MI endpoints.
+ * @m: &nvme_root_t containing endpoints
+ * @e: &nvme_mi_ep_t object, set on each iteration
+ */
+#define nvme_mi_for_each_endpoint(m, e) \
+ for (e = nvme_mi_first_endpoint(m); e != NULL; \
+ e = nvme_mi_next_endpoint(m, e))
+
+/**
+ * nvme_mi_for_each_endpoint_safe - Iterator for NVMe-MI endpoints, allowing
+ * deletion during traversal
+ * @m: &nvme_root_t containing endpoints
+ * @e: &nvme_mi_ep_t object, set on each iteration
+ * @_e: &nvme_mi_ep_t object used as temporary storage
+ */
+#define nvme_mi_for_each_endpoint_safe(m, e, _e) \
+ for (e = nvme_mi_first_endpoint(m), _e = nvme_mi_next_endpoint(m, e); \
+ e != NULL; \
+ e = _e, _e = nvme_mi_next_endpoint(m, e))
+
+struct nvme_mi_ctrl;
+
+/**
+ * typedef nvme_mi_ctrl_t - NVMe-MI Controller object.
+ *
+ * Provides NVMe command functionality, through the MI interface.
+ */
+typedef struct nvme_mi_ctrl * nvme_mi_ctrl_t;
+
+/**
+ * nvme_mi_first_ctrl - Start controller iterator
+ * @ep: &nvme_mi_ep_t object
+ *
+ * Return: first MI controller object under this root, or NULL if no controllers
+ * are present.
+ *
+ * See: &nvme_mi_next_ctrl, &nvme_mi_for_each_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_first_ctrl(nvme_mi_ep_t ep);
+
+/**
+ * nvme_mi_next_ctrl - Continue ctrl iterator
+ * @ep: &nvme_mi_ep_t object
+ * @c: &nvme_mi_ctrl_t current position of iterator
+ *
+ * Return: next MI controller object after @c under this endpoint, or NULL
+ * if no further controllers are present.
+ *
+ * See: &nvme_mi_first_ctrl, &nvme_mi_for_each_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_next_ctrl(nvme_mi_ep_t ep, nvme_mi_ctrl_t c);
+
+/**
+ * nvme_mi_for_each_ctrl - Iterator for NVMe-MI controllers.
+ * @ep: &nvme_mi_ep_t containing endpoints
+ * @c: &nvme_mi_ctrl_t object, set on each iteration
+ *
+ * Allows iteration of the list of controllers behind an endpoint. Unless the
+ * controllers have already been created explicitly, you'll probably want to
+ * call &nvme_mi_scan_ep() to scan for the controllers first.
+ *
+ * See: &nvme_mi_scan_ep()
+ */
+#define nvme_mi_for_each_ctrl(ep, c) \
+ for (c = nvme_mi_first_ctrl(ep); c != NULL; \
+ c = nvme_mi_next_ctrl(ep, c))
+
+/**
+ * nvme_mi_for_each_ctrl_safe - Iterator for NVMe-MI controllers, allowing
+ * deletion during traversal
+ * @ep: &nvme_mi_ep_t containing controllers
+ * @c: &nvme_mi_ctrl_t object, set on each iteration
+ * @_c: &nvme_mi_ctrl_t object used as temporary storage
+ *
+ * Allows iteration of the list of controllers behind an endpoint, safe against
+ * deletion during iteration. Unless the controllers have already been created
+ * explicitly (or you're just iterating to destroy controllers) you'll probably
+ * want to call &nvme_mi_scan_ep() to scan for the controllers first.
+ *
+ * See: &nvme_mi_scan_ep()
+ */
+#define nvme_mi_for_each_ctrl_safe(ep, c, _c) \
+ for (c = nvme_mi_first_ctrl(ep), _c = nvme_mi_next_ctrl(ep, c); \
+ c != NULL; \
+ c = _c, _c = nvme_mi_next_ctrl(ep, c))
+
+/**
+ * nvme_mi_open_mctp() - Create an endpoint using a MCTP connection.
+ * @root: root object to create under
+ * @netid: MCTP network ID on this system
+ * @eid: MCTP endpoint ID
+ *
+ * Transport-specific endpoint initialization for MI-connected endpoints. Once
+ * an endpoint is created, the rest of the API is transport-independent.
+ *
+ * Return: New endpoint object for @netid & @eid, or NULL on failure.
+ *
+ * See &nvme_mi_close
+ */
+nvme_mi_ep_t nvme_mi_open_mctp(nvme_root_t root, unsigned int netid, uint8_t eid);
+
+/**
+ * nvme_mi_close() - Close an endpoint connection and release resources,
+ * including controller objects.
+ *
+ * @ep: Endpoint object to close
+ */
+void nvme_mi_close(nvme_mi_ep_t ep);
+
+/**
+ * nvme_mi_scan_mctp - look for MCTP-connected NVMe-MI endpoints.
+ *
+ * Description: This function queries the system MCTP daemon ("mctpd") over
+ * D-Bus, to find MCTP endpoints that report support for NVMe-MI over MCTP.
+ *
+ * This requires libvnme-mi to be compiled with D-Bus support; if not, this
+ * will return NULL.
+ *
+ * Return: A @nvme_root_t populated with a set of MCTP-connected endpoints,
+ * or NULL on failure
+ */
+nvme_root_t nvme_mi_scan_mctp(void);
+
+/**
+ * nvme_mi_scan_ep - query an endpoint for its NVMe controllers.
+ * @ep: Endpoint to scan
+ * @force_rescan: close existing controllers and rescan
+ *
+ * This function queries an MI endpoint for the controllers available, by
+ * performing an MI Read MI Data Structure command (requesting the
+ * controller list). The controllers are stored in the endpoint's internal
+ * list, and can be iterated with nvme_mi_for_each_ctrl.
+ *
+ * This will only scan the endpoint once, unless @force_rescan is set. If
+ * so, all existing controller objects will be freed - the caller must not
+ * hold a reference to those across this call.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &nvme_mi_for_each_ctrl
+ */
+int nvme_mi_scan_ep(nvme_mi_ep_t ep, bool force_rescan);
+
+/**
+ * nvme_mi_init_ctrl() - initialise a NVMe controller.
+ * @ep: Endpoint to create under
+ * @ctrl_id: ID of controller to initialize.
+ *
+ * Create a connection to a controller behind the endpoint specified in @ep.
+ * Controller IDs may be queried from the endpoint through
+ * &nvme_mi_mi_read_mi_data_ctrl_list.
+ *
+ * Return: New controller object, or NULL on failure.
+ *
+ * See &nvme_mi_close_ctrl
+ */
+nvme_mi_ctrl_t nvme_mi_init_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id);
+
+/**
+ * nvme_mi_close_ctrl() - free a controller
+ * @ctrl: controller to free
+ */
+void nvme_mi_close_ctrl(nvme_mi_ctrl_t ctrl);
+
+/**
+ * nvme_mi_endpoint_desc - Get a string describing a MI endpoint.
+ * @ep: endpoint to describe
+ *
+ * Generates a human-readable string describing the endpoint, with possibly
+ * transport-specific data. The string is allocated during the call, and the
+ * caller is responsible for free()-ing the string.
+ *
+ * Return: a newly-allocated string containing the endpoint description, or
+ * NULL on failure.
+ */
+char *nvme_mi_endpoint_desc(nvme_mi_ep_t ep);
+
+/* MI Command API: nvme_mi_mi_ prefix */
+
+/**
+ * nvme_mi_mi_read_mi_data_subsys() - Perform a Read MI Data Structure command,
+ * retrieving subsystem data.
+ * @ep: endpoint for MI communication
+ * @s: subsystem information to populate
+ *
+ * Retrieves the Subsystem information - number of external ports and
+ * NVMe version information. See &struct nvme_mi_read_nvm_ss_info.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_read_mi_data_subsys(nvme_mi_ep_t ep,
+ struct nvme_mi_read_nvm_ss_info *s);
+
+/**
+ * nvme_mi_mi_read_mi_data_port() - Perform a Read MI Data Structure command,
+ * retrieving port data.
+ * @ep: endpoint for MI communication
+ * @portid: id of port data to retrieve
+ * @p: port information to populate
+ *
+ * Retrieves the Port information, for the specified port ID. The subsystem
+ * data (from &nvme_mi_mi_read_mi_data_subsys) nmp field contains the allowed
+ * range of port IDs.
+ *
+ * See &struct nvme_mi_read_port_info.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_read_mi_data_port(nvme_mi_ep_t ep, __u8 portid,
+ struct nvme_mi_read_port_info *p);
+
+/**
+ * nvme_mi_mi_read_mi_data_ctrl_list() - Perform a Read MI Data Structure
+ * command, retrieving the list of attached controllers.
+ * @ep: endpoint for MI communication
+ * @start_ctrlid: starting controller ID
+ * @list: controller list to populate
+ *
+ * Retrieves the list of attached controllers, with IDs greater than or
+ * equal to @start_ctrlid.
+ *
+ * See &struct nvme_ctrl_list.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_read_mi_data_ctrl_list(nvme_mi_ep_t ep, __u8 start_ctrlid,
+ struct nvme_ctrl_list *list);
+
+/**
+ * nvme_mi_mi_read_mi_data_ctrl() - Perform a Read MI Data Structure command,
+ * retrieving controller information
+ * @ep: endpoint for MI communication
+ * @ctrl_id: ID of controller to query
+ * @ctrl: controller data to populate
+ *
+ * Retrieves the Controller Information Data Structure for the attached
+ * controller with ID @ctrlid.
+ *
+ * See &struct nvme_mi_read_ctrl_info.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_read_mi_data_ctrl(nvme_mi_ep_t ep, __u16 ctrl_id,
+ struct nvme_mi_read_ctrl_info *ctrl);
+
+/**
+ * nvme_mi_mi_subsystem_health_status_poll() - Read the Subsystem Health
+ * Data Structure from the NVM subsystem
+ * @ep: endpoint for MI communication
+ * @clear: flag to clear the Composite Controller Status state
+ * @nshds: subsystem health status data to populate
+ *
+ * Retrieves the Subsystem Health Data Structure into @nshds. If @clear is
+ * set, requests that the Composite Controller Status bits are cleared after
+ * the read. See NVMe-MI section 5.6 for details on the CCS bits.
+ *
+ * See &struct nvme_mi_nvm_ss_health_status.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_subsystem_health_status_poll(nvme_mi_ep_t ep, bool clear,
+ struct nvme_mi_nvm_ss_health_status *nshds);
+
+/**
+ * nvme_mi_mi_config_get - query a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ * config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ * @nmresp: set to queried configuration data in NMRESP field of response.
+ *
+ * Performs a MI Configuration Get command, with the configuration identifier
+ * as the LSB of @dw0. Other @dw0 and @dw1 data is configuration-identifier
+ * specific.
+ *
+ * On a successful Configuration Get, the @nmresp pointer will be populated with
+ * the bytes from the 3-byte NMRESP field, converted to native endian.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_config_get(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1,
+ __u32 *nmresp);
+
+/**
+ * nvme_mi_mi_config_set - set a configuration parameter
+ * @ep: endpoint for MI communication
+ * @dw0: management doubleword 0, containing configuration identifier, plus
+ * config-specific fields
+ * @dw1: management doubleword 0, config-specific.
+ *
+ * Performs a MI Configuration Set command, with the command as the LSB of
+ * @dw0. Other @dw0 and @dw1 data is configuration-identifier specific.
+ *
+ * See &enum nvme_mi_config_id for identifiers.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_mi_config_set(nvme_mi_ep_t ep, __u32 dw0, __u32 dw1);
+
+/**
+ * nvme_mi_mi_config_get_smbus_freq - get configuration: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @freq: output value for current frequency configuration
+ *
+ * Performs a MI Configuration Get, to query the current SMBus frequency of
+ * the port specified in @port. On success, populates @freq with the port
+ * frequency
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_get_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+ enum nvme_mi_config_smbus_freq *freq)
+{
+ __u32 tmp, dw0;
+ int rc;
+
+ dw0 = port << 24 | NVME_MI_CONFIG_SMBUS_FREQ;
+
+ rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+ if (!rc)
+ *freq = tmp & 0x3;
+ return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_smbus_freq - set configuration: SMBus port frequency
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @freq: new frequency configuration
+ *
+ * Performs a MI Configuration Set, to update the current SMBus frequency of
+ * the port specified in @port.
+ *
+ * See &struct nvme_mi_read_port_info for the maximum supported SMBus frequency
+ * for the port.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_smbus_freq(nvme_mi_ep_t ep, __u8 port,
+ enum nvme_mi_config_smbus_freq freq)
+{
+ __u32 dw0 = port << 24 |
+ (freq & 0x3) << 8 |
+ NVME_MI_CONFIG_SMBUS_FREQ;
+
+ return nvme_mi_mi_config_set(ep, dw0, 0);
+}
+
+/**
+ * nvme_mi_mi_config_set_health_status_change - clear CCS bits in health status
+ * @ep: endpoint for MI communication
+ * @mask: bitmask to clear
+ *
+ * Performs a MI Configuration Set, to update the current health status poll
+ * values of the Composite Controller Status bits. Bits set in @mask will
+ * be cleared from future health status poll data, and may be re-triggered by
+ * a future health change event.
+ *
+ * See &nvme_mi_mi_subsystem_health_status_poll(), &enum nvme_mi_ccs for
+ * values in @mask.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_health_status_change(nvme_mi_ep_t ep,
+ __u32 mask)
+{
+ return nvme_mi_mi_config_set(ep, NVME_MI_CONFIG_HEALTH_STATUS_CHANGE,
+ mask);
+}
+
+/**
+ * nvme_mi_mi_config_get_mctp_mtu - get configuration: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to query
+ * @mtu: output value for current MCTP MTU configuration
+ *
+ * Performs a MI Configuration Get, to query the current MCTP Maximum
+ * Transmission Unit size (MTU) of the port specified in @port. On success,
+ * populates @mtu with the MTU.
+ *
+ * The default reset value is 64, corresponding to the MCTP baseline MTU.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_get_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+ __u16 *mtu)
+{
+ __u32 tmp, dw0;
+ int rc;
+
+ dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+ rc = nvme_mi_mi_config_get(ep, dw0, 0, &tmp);
+ if (!rc)
+ *mtu = tmp & 0xffff;
+ return rc;
+}
+
+/**
+ * nvme_mi_mi_config_set_mctp_mtu - set configuration: MCTP MTU
+ * @ep: endpoint for MI communication
+ * @port: port ID to set
+ * @mtu: new MTU configuration
+ *
+ * Performs a MI Configuration Set, to update the current MCTP MTU value for
+ * the port specified in @port.
+ *
+ * Some controllers may also use this as the maximum receive unit size, and
+ * may not accept MCTP messages larger than the configured MTU. When setting
+ * this value, you will likely need to change the MTU of the local MCTP
+ * interface(s) to match.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+static inline int nvme_mi_mi_config_set_mctp_mtu(nvme_mi_ep_t ep, __u8 port,
+ __u16 mtu)
+{
+ __u32 dw0 = port << 24 | NVME_MI_CONFIG_MCTP_MTU;
+
+ return nvme_mi_mi_config_set(ep, dw0, mtu);
+}
+
+/* Admin channel functions */
+
+/**
+ * nvme_mi_admin_xfer() - Raw admin transfer interface.
+ * @ctrl: controller to send the admin command to
+ * @admin_req: request data
+ * @req_data_size: size of request data payload
+ * @admin_resp: buffer for response data
+ * @resp_data_offset: offset into request data to retrieve from controller
+ * @resp_data_size: size of response data buffer, updated to received size
+ *
+ * Performs an arbitrary NVMe Admin command, using the provided request data,
+ * in @admin_req. The size of the request data *payload* is specified in
+ * @req_data_size - this does not include the standard header length (so a
+ * header-only request would have a size of 0).
+ *
+ * On success, response data is stored in @admin_resp, which has an optional
+ * appended payload buffer of @resp_data_size bytes. The actual payload
+ * transferred will be stored in @resp_data_size. These sizes do 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.
+ *
+ * See: &struct nvme_mi_admin_req_hdr and &struct nvme_mi_admin_resp_hdr.
+ *
+ * Return: 0 on success, non-zero on failure.
+ */
+int nvme_mi_admin_xfer(nvme_mi_ctrl_t ctrl,
+ struct nvme_mi_admin_req_hdr *admin_req,
+ size_t req_data_size,
+ struct nvme_mi_admin_resp_hdr *admin_resp,
+ off_t resp_data_offset,
+ size_t *resp_data_size);
+
+/**
+ * nvme_mi_admin_identify_partial() - Perform an Admin identify command,
+ * and retrieve partial response data.
+ * @ctrl: Controller to process identify command
+ * @args: Identify command arguments
+ * @offset: offset of identify data to retrieve from response
+ * @size: size of identify data to return
+ *
+ * Perform an Identify command, using the Identify command parameters in @args.
+ * The @offset and @size arguments allow the caller to retrieve part of
+ * the identify response. See NVMe-MI section 6.2 for the semantics (and some
+ * handy diagrams) of the offset & size parameters.
+ *
+ * Will return an error if the length of the response data (from the controller)
+ * did not match @size.
+ *
+ * Unless you're performing a vendor-unique identify command, You'll probably
+ * want to use one of the identify helpers (nvme_mi_admin_identify,
+ * nvme_mi_admin_identify_cns_nsid, or nvme_mi_admin_identify_<type>) instead
+ * of this. If the type of your identify command is standardized but not
+ * yet supported by libnvme-mi, please contact the maintainers.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_identify_args
+ */
+int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
+ struct nvme_identify_args *args,
+ off_t offset, size_t size);
+
+/**
+ * nvme_mi_admin_identify() - Perform an Admin identify command.
+ * @ctrl: Controller to process identify command
+ * @args: Identify command arguments
+ *
+ * Perform an Identify command, using the Identify command parameters in @args.
+ * Stores the identify data in ->data, and (if set) the result from cdw0
+ * into args->result.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_identify_args
+ */
+static inline int nvme_mi_admin_identify(nvme_mi_ctrl_t ctrl,
+ struct nvme_identify_args *args)
+{
+ return nvme_mi_admin_identify_partial(ctrl, args,
+ 0, NVME_IDENTIFY_DATA_SIZE);
+}
+
+/**
+ * nvme_mi_admin_identify_cns_nsid() - Perform an Admin identify command using
+ * specific CNS/NSID parameters.
+ * @ctrl: Controller to process identify command
+ * @cns: Controller or Namespace Structure, specifying identified object
+ * @nsid: namespace ID
+ * @data: buffer for identify data response
+ *
+ * Perform an Identify command, using the CNS specifier @cns, and the
+ * namespace ID @nsid if required by the CNS type.
+ *
+ * Stores the identify data in @data, which is expected to be a buffer of
+ * &NVME_IDENTIFY_DATA_SIZE bytes.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE.
+ *
+ * Return: 0 on success, non-zero on failure
+ */
+static inline int nvme_mi_admin_identify_cns_nsid(nvme_mi_ctrl_t ctrl,
+ enum nvme_identify_cns cns,
+ __u32 nsid, void *data)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = data,
+ .args_size = sizeof(args),
+ .cns = cns,
+ .csi = NVME_CSI_NVM,
+ .nsid = nsid,
+ .cntid = NVME_CNTLID_NONE,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_identify_ctrl() - Perform an Admin identify for a controller
+ * @ctrl: Controller to process identify command
+ * @id: Controller identify data to populate
+ *
+ * Perform an Identify command, for the controller specified by @ctrl,
+ * writing identify data to @id.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be
+ * fully populated on success.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_id_ctrl
+ */
+static inline int nvme_mi_admin_identify_ctrl(nvme_mi_ctrl_t ctrl,
+ struct nvme_id_ctrl *id)
+{
+ return nvme_mi_admin_identify_cns_nsid(ctrl, NVME_IDENTIFY_CNS_CTRL,
+ NVME_NSID_NONE, id);
+}
+
+/**
+ * nvme_mi_admin_identify_ctrl_list() - Perform an Admin identify for a
+ * controller list.
+ * @ctrl: Controller to process identify command
+ * @cntid: Controller ID to specify list start
+ * @list: List data to populate
+ *
+ * Perform an Identify command, for the controller list starting with
+ * IDs greater than or equal to @cntid.
+ *
+ * Will return an error if the length of the response data (from the
+ * controller) is not a full &NVME_IDENTIFY_DATA_SIZE, so @id will be
+ * fully populated on success.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_ctrl_list
+ */
+static inline int nvme_mi_admin_identify_ctrl_list(nvme_mi_ctrl_t ctrl,
+ __u16 cntid,
+ struct nvme_ctrl_list *list)
+{
+ struct nvme_identify_args args = {
+ .result = NULL,
+ .data = list,
+ .args_size = sizeof(args),
+ .cns = NVME_IDENTIFY_CNS_CTRL_LIST,
+ .csi = NVME_CSI_NVM,
+ .nsid = NVME_NSID_NONE,
+ .cntid = cntid,
+ .cns_specific_id = NVME_CNSSPECID_NONE,
+ .uuidx = NVME_UUID_NONE,
+ };
+
+ return nvme_mi_admin_identify(ctrl, &args);
+}
+
+/**
+ * nvme_mi_admin_get_log_page() - Retrieve log page data from controller
+ * @ctrl: Controller to query
+ * @args: Get Log Page command arguments
+ *
+ * Performs a Get Log Page Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * This request may be implemented as multiple log page commands, in order
+ * to fit within MI message-size limits.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_get_log_page(nvme_mi_ctrl_t ctrl,
+ struct nvme_get_log_args *args);
+
+/**
+ * nvme_mi_admin_security_send() - Perform a Security Send command on a
+ * controller.
+ * @ctrl: Controller to send command to
+ * @args: Security Send command arguments
+ *
+ * Performs a Security Send Admin command as specified by @args. Response data
+ * is stored in @args->data, which should be a buffer of @args->data_len bytes.
+ * Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * Security Send data length should not be greater than 4096 bytes to
+ * comply with specification limits.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_send_args *args);
+
+/**
+ * nvme_mi_admin_security_recv() - Perform a Security Receive command on a
+ * controller.
+ * @ctrl: Controller to send command to
+ * @args: Security Receive command arguments
+ *
+ * Performs a Security Receive Admin command as specified by @args. Response
+ * data is stored in @args->data, which should be a buffer of @args->data_len
+ * bytes. Resulting data length is stored in @args->data_len on successful
+ * command completion.
+ *
+ * Security Receive data length should not be greater than 4096 bytes to
+ * comply with specification limits.
+ *
+ * Return: 0 on success, non-zero on failure
+ *
+ * See: &struct nvme_get_log_args
+ */
+int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl,
+ struct nvme_security_receive_args *args);
+
+
+#endif /* _LIBNVME_MI_MI_H */