summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2022-11-05 18:17:21 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2022-11-05 18:17:32 +0000
commitb0dc2feab3271dbcb42df6e6d8a37138a90c44a1 (patch)
treeae02f159c125f183b2adae47fdf0e64357bf76a8 /test
parentReleasing debian version 1.1-2. (diff)
downloadlibnvme-b0dc2feab3271dbcb42df6e6d8a37138a90c44a1.tar.xz
libnvme-b0dc2feab3271dbcb42df6e6d8a37138a90c44a1.zip
Merging upstream version 1.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--test/meson.build11
-rw-r--r--test/mi-mctp.c38
-rw-r--r--test/mi.c1050
-rw-r--r--test/test.c7
-rw-r--r--test/uuid.c120
5 files changed, 1219 insertions, 7 deletions
diff --git a/test/meson.build b/test/meson.build
index 00c9ceb..d90f835 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -12,7 +12,7 @@
main = executable(
'main-test',
['test.c'],
- dependencies: [libnvme_dep, libuuid_dep],
+ dependencies: [libnvme_dep],
include_directories: [incdir, internal_incdir]
)
@@ -56,3 +56,12 @@ mi_mctp = executable(
)
test('mi-mctp', mi_mctp)
+
+uuid = executable(
+ 'test-uuid',
+ ['uuid.c'],
+ dependencies: libnvme_dep,
+ include_directories: [incdir, internal_incdir]
+)
+
+test('uuid', uuid)
diff --git a/test/mi-mctp.c b/test/mi-mctp.c
index 5b1a154..6d83d42 100644
--- a/test/mi-mctp.c
+++ b/test/mi-mctp.c
@@ -308,7 +308,8 @@ static void test_admin_resp_err(nvme_mi_ep_t ep, struct test_peer *peer)
peer->tx_buf_len = 8;
rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
- assert(rc == 0x2);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
+ assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
}
/* test: all 4-byte aligned response sizes - should be decoded into the
@@ -332,7 +333,8 @@ static void test_admin_resp_sizes(nvme_mi_ep_t ep, struct test_peer *peer)
for (i = 8; i <= 4096 + 8; i+=4) {
peer->tx_buf_len = i;
rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
- assert(rc == 2);
+ assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI);
+ assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR);
}
nvme_mi_close_ctrl(ctrl);
@@ -407,6 +409,7 @@ static void test_poll_timeout(nvme_mi_ep_t ep, struct test_peer *peer)
/* test: send a More Processing Required response, then the actual response */
struct mpr_tx_info {
int msg_no;
+ bool admin_quirk;
size_t final_len;
};
@@ -422,6 +425,9 @@ static int tx_mpr(struct test_peer *peer, void *buf, size_t len)
case 1:
peer->tx_buf[4] = NVME_MI_RESP_MPR;
peer->tx_buf_len = 8;
+ if (tx_info->admin_quirk) {
+ peer->tx_buf_len = 20;
+ }
break;
case 2:
peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
@@ -446,6 +452,7 @@ static void test_mpr_mi(nvme_mi_ep_t ep, struct test_peer *peer)
tx_info.msg_no = 1;
tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
+ tx_info.admin_quirk = false;
peer->tx_fn = tx_mpr;
peer->tx_data = &tx_info;
@@ -463,6 +470,32 @@ static void test_mpr_admin(nvme_mi_ep_t ep, struct test_peer *peer)
tx_info.msg_no = 1;
tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
+ tx_info.admin_quirk = false;
+
+ peer->tx_fn = tx_mpr;
+ peer->tx_data = &tx_info;
+
+ ctrl = nvme_mi_init_ctrl(ep, 1);
+
+ rc = nvme_mi_admin_identify_ctrl(ctrl, &id);
+ assert(rc == 0);
+
+ nvme_mi_close_ctrl(ctrl);
+}
+
+/* We have seen drives that send a MPR response as a full Admin message,
+ * rather than a MI message; these have a larger message body
+ */
+static void test_mpr_admin_quirked(nvme_mi_ep_t ep, struct test_peer *peer)
+{
+ struct mpr_tx_info tx_info;
+ struct nvme_id_ctrl id;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ tx_info.msg_no = 1;
+ tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
+ tx_info.admin_quirk = true;
peer->tx_fn = tx_mpr;
peer->tx_data = &tx_info;
@@ -638,6 +671,7 @@ struct test {
DEFINE_TEST(poll_timeout),
DEFINE_TEST(mpr_mi),
DEFINE_TEST(mpr_admin),
+ DEFINE_TEST(mpr_admin_quirked),
DEFINE_TEST(mpr_timeouts),
DEFINE_TEST(mpr_timeout_clamp),
DEFINE_TEST(mpr_mprt_zero),
diff --git a/test/mi.c b/test/mi.c
index d269060..c21f9db 100644
--- a/test/mi.c
+++ b/test/mi.c
@@ -11,6 +11,7 @@
#include <unistd.h>
#include <ccan/array_size/array_size.h>
+#include <ccan/endian/endian.h>
/* we define a custom transport, so need the internal headers */
#include "nvme/private.h"
@@ -747,6 +748,1037 @@ static void test_mi_config_set_freq_invalid(nvme_mi_ep_t ep)
assert(rc == 4);
}
+/* Get Features callback, implementing Arbitration (which doesn't return
+ * additional data) and Timestamp (which does).
+ */
+static int test_admin_get_features_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 sel, fid, ror, mt, *rq_hdr, *rs_hdr, *rs_data;
+ __u16 ctrl_id;
+ int i;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+
+ /* do we have enough for a mi header? */
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+
+ /* inspect response as raw bytes */
+ rq_hdr = (__u8 *)req->hdr;
+
+ /* opcode */
+ assert(rq_hdr[4] == nvme_admin_get_features);
+
+ /* controller */
+ ctrl_id = rq_hdr[7] << 8 | rq_hdr[6];
+ assert(ctrl_id == 0x5); /* controller id */
+
+ /* sel & fid from lower bytes of cdw10 */
+ fid = rq_hdr[44];
+ sel = rq_hdr[45] & 0x7;
+
+ /* reserved fields */
+ assert(!(rq_hdr[46] || rq_hdr[47] || rq_hdr[45] & 0xf8));
+
+ assert(sel == 0x00);
+
+ rs_hdr = (__u8 *)resp->hdr;
+ rs_hdr[4] = 0x00; /* status: success */
+ rs_data = resp->data;
+
+ /* feature-id specific checks, and response generation */
+ switch (fid) {
+ case NVME_FEAT_FID_ARBITRATION:
+ /* arbitrary (hah!) arbitration value in cdw0 of response */
+ rs_hdr[8] = 1;
+ rs_hdr[9] = 2;
+ rs_hdr[10] = 3;
+ rs_hdr[11] = 4;
+ resp->data_len = 0;
+ break;
+
+ case NVME_FEAT_FID_TIMESTAMP:
+ resp->data_len = 8;
+ for (i = 0; i < 6; i++)
+ rs_data[i] = i;
+ rs_data[6] = 1;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_get_features_nodata(nvme_mi_ep_t ep)
+{
+ struct nvme_get_features_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ uint32_t res;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_get_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_ARBITRATION;
+ args.sel = 0;
+ args.result = &res;
+
+ rc = nvme_mi_admin_get_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == 0);
+ assert(res == 0x04030201);
+}
+
+static void test_get_features_data(nvme_mi_ep_t ep)
+{
+ struct nvme_get_features_args args = { 0 };
+ struct nvme_timestamp tstamp;
+ nvme_mi_ctrl_t ctrl;
+ uint8_t exp[6];
+ uint32_t res;
+ int rc, i;
+
+ test_set_transport_callback(ep, test_admin_get_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_TIMESTAMP;
+ args.sel = 0;
+ args.result = &res;
+ args.data = &tstamp;
+ args.data_len = sizeof(tstamp);
+
+ /* expected timestamp value */
+ for (i = 0; i < sizeof(tstamp.timestamp); i++)
+ exp[i] = i;
+
+ rc = nvme_mi_admin_get_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == sizeof(tstamp));
+ assert(tstamp.attr == 1);
+ assert(!memcmp(tstamp.timestamp, exp, sizeof(tstamp.timestamp)));
+}
+
+/* Set Features callback for timestamp */
+static int test_admin_set_features_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 save, fid, ror, mt, *rq_hdr, *rq_data, *rs_hdr;
+ __u16 ctrl_id;
+ uint8_t ts[6];
+ int i;
+
+ assert(req->hdr->type == NVME_MI_MSGTYPE_NVME);
+
+ ror = req->hdr->nmp >> 7;
+ mt = req->hdr->nmp >> 3 & 0x7;
+ assert(ror == NVME_MI_ROR_REQ);
+ assert(mt == NVME_MI_MT_ADMIN);
+ assert(req->hdr_len == sizeof(struct nvme_mi_admin_req_hdr));
+ assert(req->data_len == 8);
+
+ rq_hdr = (__u8 *)req->hdr;
+ rq_data = req->data;
+
+ /* opcode */
+ assert(rq_hdr[4] == nvme_admin_set_features);
+
+ /* controller */
+ ctrl_id = rq_hdr[7] << 8 | rq_hdr[6];
+ assert(ctrl_id == 0x5); /* controller id */
+
+ /* fid from lower bytes of cdw10, save from top bit */
+ fid = rq_hdr[44];
+ save = rq_hdr[47] & 0x80;
+
+ /* reserved fields */
+ assert(!(rq_hdr[45] || rq_hdr[46]));
+
+ assert(fid == NVME_FEAT_FID_TIMESTAMP);
+ assert(save == 0x80);
+
+ for (i = 0; i < sizeof(ts); i++)
+ ts[i] = i;
+ assert(!memcmp(ts, rq_data, sizeof(ts)));
+
+ rs_hdr = (__u8 *)resp->hdr;
+ rs_hdr[4] = 0x00;
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_set_features(nvme_mi_ep_t ep)
+{
+ struct nvme_set_features_args args = { 0 };
+ struct nvme_timestamp tstamp;
+ nvme_mi_ctrl_t ctrl;
+ uint32_t res;
+ int rc, i;
+
+ test_set_transport_callback(ep, test_admin_set_features_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ for (i = 0; i < sizeof(tstamp.timestamp); i++)
+ tstamp.timestamp[i] = i;
+
+ args.args_size = sizeof(args);
+ args.fid = NVME_FEAT_FID_TIMESTAMP;
+ args.save = 1;
+ args.result = &res;
+ args.data = &tstamp;
+ args.data_len = sizeof(tstamp);
+
+ rc = nvme_mi_admin_set_features(ctrl, &args);
+ assert(rc == 0);
+ assert(args.data_len == 0);
+}
+
+enum ns_type {
+ NS_ACTIVE,
+ NS_ALLOC,
+};
+
+static int test_admin_id_ns_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_ns_list *list;
+ enum ns_type type;
+ int offset;
+ __u8 *hdr;
+ __u16 cns;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+
+ /* NSID */
+ assert(hdr[8] == 1 && !hdr[9] && !hdr[10] && !hdr[11]);
+
+ type = *(enum ns_type *)data;
+ resp->data_len = sizeof(*list);
+ list = resp->data;
+
+ switch (type) {
+ case NS_ALLOC:
+ assert(cns == NVME_IDENTIFY_CNS_ALLOCATED_NS_LIST);
+ offset = 2;
+ break;
+ case NS_ACTIVE:
+ assert(cns == NVME_IDENTIFY_CNS_NS_ACTIVE_LIST);
+ offset = 4;
+ break;
+ default:
+ assert(0);
+ }
+
+ list->ns[0] = cpu_to_le32(offset);
+ list->ns[1] = cpu_to_le32(offset + 1);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_alloc_ns_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ns_list list;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ALLOC;
+ test_set_transport_callback(ep, test_admin_id_ns_list_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_allocated_ns_list(ctrl, 1, &list);
+ assert(!rc);
+
+ assert(le32_to_cpu(list.ns[0]) == 2);
+ assert(le32_to_cpu(list.ns[1]) == 3);
+ assert(le32_to_cpu(list.ns[2]) == 0);
+}
+
+static void test_admin_id_active_ns_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ns_list list;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ACTIVE;
+ test_set_transport_callback(ep, test_admin_id_ns_list_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_active_ns_list(ctrl, 1, &list);
+ assert(!rc);
+
+ assert(le32_to_cpu(list.ns[0]) == 4);
+ assert(le32_to_cpu(list.ns[1]) == 5);
+ assert(le32_to_cpu(list.ns[2]) == 0);
+}
+
+static int test_admin_id_ns_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_id_ns *id;
+ enum ns_type type;
+ __u16 nsid, cns;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+
+ /* NSID */
+ nsid = hdr[8];
+ assert(!hdr[9] && !hdr[10] && !hdr[11]);
+
+ type = *(enum ns_type *)data;
+ resp->data_len = sizeof(*id);
+ id = resp->data;
+ id->nsze = cpu_to_le64(nsid);
+
+ switch (type) {
+ case NS_ALLOC:
+ assert(cns == NVME_IDENTIFY_CNS_ALLOCATED_NS);
+ break;
+ case NS_ACTIVE:
+ assert(cns == NVME_IDENTIFY_CNS_NS);
+ break;
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_alloc_ns(struct nvme_mi_ep *ep)
+{
+ struct nvme_id_ns id;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ALLOC;
+ test_set_transport_callback(ep, test_admin_id_ns_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_allocated_ns(ctrl, 1, &id);
+ assert(!rc);
+ assert(le64_to_cpu(id.nsze) == 1);
+}
+
+static void test_admin_id_active_ns(struct nvme_mi_ep *ep)
+{
+ struct nvme_id_ns id;
+ nvme_mi_ctrl_t ctrl;
+ enum ns_type type;
+ int rc;
+
+ type = NS_ACTIVE;
+ test_set_transport_callback(ep, test_admin_id_ns_cb, &type);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_ns(ctrl, 1, &id);
+ assert(!rc);
+ assert(le64_to_cpu(id.nsze) == 1);
+}
+
+static int test_admin_id_nsid_ctrl_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u16 cns, ctrlid;
+ __u32 nsid;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+ assert(cns == NVME_IDENTIFY_CNS_CTRL_LIST);
+
+ nsid = hdr[11] << 24 | hdr[10] << 16 | hdr[9] << 8 | hdr[8];
+ assert(nsid == 0x01020304);
+
+ ctrlid = hdr[47] << 8 | hdr[46];
+ assert(ctrlid == 5);
+
+ resp->data_len = sizeof(struct nvme_ctrl_list);
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_nsid_ctrl_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_id_nsid_ctrl_list_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_nsid_ctrl_list(ctrl, 0x01020304, 5, &list);
+ assert(!rc);
+}
+
+static int test_admin_id_secondary_ctrl_list_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u16 cns, ctrlid;
+ __u32 nsid;
+ __u8 *hdr;
+
+ hdr = (__u8 *)req->hdr;
+ assert(hdr[4] == nvme_admin_identify);
+
+ assert(req->data_len == 0);
+
+ cns = hdr[45] << 8 | hdr[44];
+ assert(cns == NVME_IDENTIFY_CNS_SECONDARY_CTRL_LIST);
+
+ nsid = hdr[11] << 24 | hdr[10] << 16 | hdr[9] << 8 | hdr[8];
+ assert(nsid == 0x01020304);
+
+ ctrlid = hdr[47] << 8 | hdr[46];
+ assert(ctrlid == 5);
+
+ resp->data_len = sizeof(struct nvme_secondary_ctrl_list);
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_id_secondary_ctrl_list(struct nvme_mi_ep *ep)
+{
+ struct nvme_secondary_ctrl_list list;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_id_secondary_ctrl_list_cb,
+ NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_identify_secondary_ctrl_list(ctrl, 0x01020304,
+ 5, &list);
+ assert(!rc);
+}
+
+static int test_admin_ns_mgmt_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ __u8 *rq_hdr, *rs_hdr, sel, csi;
+ struct nvme_id_ns *id;
+ __u32 nsid;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_ns_mgmt);
+
+ sel = rq_hdr[44];
+ csi = rq_hdr[45];
+ nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8];
+
+ rs_hdr = (__u8 *)resp->hdr;
+
+ switch (sel) {
+ case NVME_NS_MGMT_SEL_CREATE:
+ assert(req->data_len == sizeof(struct nvme_id_ns));
+ id = req->data;
+
+ /* No NSID on created namespaces */
+ assert(nsid == 0);
+ assert(csi == 0);
+
+ /* allow operations on nsze == 42, reject others */
+ if (le64_to_cpu(id->nsze) != 42) {
+ rs_hdr[4] = 0;
+ /* response cdw0 is created NSID */
+ rs_hdr[8] = 0x04;
+ rs_hdr[9] = 0x03;
+ rs_hdr[10] = 0x02;
+ rs_hdr[11] = 0x01;
+ } else {
+ rs_hdr[4] = NVME_MI_RESP_INVALID_PARAM;
+ }
+ break;
+
+ case NVME_NS_MGMT_SEL_DELETE:
+ assert(req->data_len == 0);
+ /* NSID required on delete */
+ assert(nsid == 0x05060708);
+ break;
+
+ default:
+ assert(0);
+ }
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_ns_mgmt_create(struct nvme_mi_ep *ep)
+{
+ struct nvme_id_ns nsid;
+ nvme_mi_ctrl_t ctrl;
+ __u32 ns;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_ns_mgmt_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_mgmt_create(ctrl, &nsid, 0, &ns);
+ assert(!rc);
+ assert(ns == 0x01020304);
+
+ nsid.nsze = 42;
+ rc = nvme_mi_admin_ns_mgmt_create(ctrl, &nsid, 0, &ns);
+ assert(rc);
+}
+
+static void test_admin_ns_mgmt_delete(struct nvme_mi_ep *ep)
+{
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ test_set_transport_callback(ep, test_admin_ns_mgmt_cb, NULL);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_mgmt_delete(ctrl, 0x05060708);
+ assert(!rc);
+}
+
+struct attach_op {
+ enum {
+ NS_ATTACH,
+ NS_DETACH,
+ } op;
+ struct nvme_ctrl_list *list;
+};
+
+static int test_admin_ns_attach_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct attach_op *op = data;
+ __u8 *rq_hdr, sel;
+ __u32 nsid;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_ns_attach);
+
+ sel = rq_hdr[44];
+ nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8];
+
+ assert(req->data_len == sizeof(*op->list));
+
+ assert(nsid == 0x02030405);
+ switch (op->op) {
+ case NS_ATTACH:
+ assert(sel == NVME_NS_ATTACH_SEL_CTRL_ATTACH);
+ break;
+ case NS_DETACH:
+ assert(sel == NVME_NS_ATTACH_SEL_CTRL_DEATTACH);
+ break;
+ default:
+ assert(0);
+ }
+
+ assert(!memcmp(req->data, op->list, sizeof(*op->list)));
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_ns_attach(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list = { 0 };
+ struct attach_op aop;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ list.num = cpu_to_le16(2);
+ list.identifier[0] = 4;
+ list.identifier[1] = 5;
+
+ aop.op = NS_ATTACH;
+ aop.list = &list;
+
+ test_set_transport_callback(ep, test_admin_ns_attach_cb, &aop);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_attach_ctrls(ctrl, 0x02030405, &list);
+ assert(!rc);
+}
+
+static void test_admin_ns_detach(struct nvme_mi_ep *ep)
+{
+ struct nvme_ctrl_list list = { 0 };
+ struct attach_op aop;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ list.num = cpu_to_le16(2);
+ list.identifier[0] = 6;
+ list.identifier[1] = 7;
+
+ aop.op = NS_DETACH;
+ aop.list = &list;
+
+ test_set_transport_callback(ep, test_admin_ns_attach_cb, &aop);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ rc = nvme_mi_admin_ns_detach_ctrls(ctrl, 0x02030405, &list);
+ assert(!rc);
+}
+
+struct fw_download_info {
+ uint32_t offset;
+ uint32_t len;
+ void *data;
+};
+
+static int test_admin_fw_download_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct fw_download_info *info = data;
+ __u32 len, off;
+ __u8 *rq_hdr;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_fw_download);
+
+ len = rq_hdr[47] << 24 | rq_hdr[46] << 16 | rq_hdr[45] << 8 | rq_hdr[44];
+ off = rq_hdr[51] << 24 | rq_hdr[50] << 16 | rq_hdr[49] << 8 | rq_hdr[48];
+
+ assert(off << 2 == info->offset);
+ assert(((len+1) << 2) == info->len);
+
+ /* ensure that the request len matches too */
+ assert(req->data_len == info->len);
+
+ assert(!memcmp(req->data, info->data, len));
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_fw_download(struct nvme_mi_ep *ep)
+{
+ struct nvme_fw_download_args args;
+ struct fw_download_info info;
+ unsigned char fw[4096];
+ nvme_mi_ctrl_t ctrl;
+ int rc, i;
+
+ for (i = 0; i < sizeof(fw); i++)
+ fw[i] = i % 0xff;
+
+ info.offset = 0;
+ info.len = 0;
+ info.data = fw;
+ args.data = fw;
+ args.args_size = sizeof(args);
+
+ test_set_transport_callback(ep, test_admin_fw_download_cb, &info);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ /* invalid (zero) len */
+ args.data_len = info.len = 1;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* invalid (unaligned) len */
+ args.data_len = info.len = 1;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* invalid offset */
+ args.data_len = info.len = 4;
+ args.offset = info.offset = 1;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(rc);
+
+ /* smallest len */
+ args.data_len = info.len = 4;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+
+ /* largest len */
+ args.data_len = info.len = 4096;
+ args.offset = info.offset = 0;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+
+ /* offset value */
+ args.data_len = info.len = 4096;
+ args.offset = info.offset = 4096;
+ rc = nvme_mi_admin_fw_download(ctrl, &args);
+ assert(!rc);
+}
+
+struct fw_commit_info {
+ __u8 bpid;
+ __u8 action;
+ __u8 slot;
+};
+
+static int test_admin_fw_commit_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct fw_commit_info *info = data;
+ __u8 bpid, action, slot;
+ __u8 *rq_hdr;
+
+ rq_hdr = (__u8 *)req->hdr;
+ assert(rq_hdr[4] == nvme_admin_fw_commit);
+
+ bpid = (rq_hdr[47] >> 7) & 0x1;
+ slot = rq_hdr[44] & 0x7;
+ action = (rq_hdr[44] >> 3) & 0x7;
+
+ assert(!!bpid == !!info->bpid);
+ assert(slot == info->slot);
+ assert(action == info->action);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_fw_commit(struct nvme_mi_ep *ep)
+{
+ struct nvme_fw_commit_args args;
+ struct fw_commit_info info;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ args.args_size = sizeof(args);
+ info.bpid = args.bpid = 0;
+
+ test_set_transport_callback(ep, test_admin_fw_commit_cb, &info);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ /* all zeros */
+ info.bpid = args.bpid = 0;
+ info.slot = args.slot = 0;
+ info.action = args.action = 0;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+
+ /* all ones */
+ info.bpid = args.bpid = 1;
+ info.slot = args.slot = 0x7;
+ info.action = args.action = 0x7;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+
+ /* correct fields */
+ info.bpid = args.bpid = 1;
+ info.slot = args.slot = 2;
+ info.action = args.action = 3;
+ rc = nvme_mi_admin_fw_commit(ctrl, &args);
+ assert(!rc);
+}
+
+struct format_data {
+ __u32 nsid;
+ __u8 lbafu;
+ __u8 ses;
+ __u8 pil;
+ __u8 pi;
+ __u8 mset;
+ __u8 lbafl;
+};
+
+static int test_admin_format_nvm_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_format_nvm_args *args = data;
+ __u8 *rq_hdr;
+ __u32 nsid;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_format_nvm);
+
+ nsid = rq_hdr[11] << 24 | rq_hdr[10] << 16 | rq_hdr[9] << 8 | rq_hdr[8];
+ assert(nsid == args->nsid);
+
+ assert(((rq_hdr[44] >> 0) & 0xf) == args->lbaf);
+ assert(((rq_hdr[44] >> 4) & 0x1) == args->mset);
+ assert(((rq_hdr[44] >> 5) & 0x7) == args->pi);
+
+ assert(((rq_hdr[45] >> 0) & 0x1) == args->pil);
+ assert(((rq_hdr[45] >> 1) & 0x7) == args->ses);
+ assert(((rq_hdr[45] >> 4) & 0x3) == args->lbafu);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_format_nvm(struct nvme_mi_ep *ep)
+{
+ struct nvme_format_nvm_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ test_set_transport_callback(ep, test_admin_format_nvm_cb, &args);
+
+ /* ensure we have the cdw0 bit field encoding correct, by testing twice
+ * with inverted bit values */
+ args.args_size = sizeof(args);
+ args.nsid = 0x04030201;
+ args.lbafu = 0x3;
+ args.ses = 0x0;
+ args.pil = 0x1;
+ args.pi = 0x0;
+ args.mset = 0x1;
+ args.lbaf = 0x0;
+
+ rc = nvme_mi_admin_format_nvm(ctrl, &args);
+ assert(!rc);
+
+ args.nsid = ~args.nsid;
+ args.lbafu = 0;
+ args.ses = 0x7;
+ args.pil = 0x0;
+ args.pi = 0x7;
+ args.mset = 0x0;
+ args.lbaf = 0xf;
+
+ rc = nvme_mi_admin_format_nvm(ctrl, &args);
+ assert(!rc);
+}
+
+static int test_admin_sanitize_nvm_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct nvme_sanitize_nvm_args *args = data;
+ __u8 *rq_hdr;
+ __u32 ovrpat;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_sanitize_nvm);
+
+ assert(((rq_hdr[44] >> 0) & 0x7) == args->sanact);
+ assert(((rq_hdr[44] >> 3) & 0x1) == args->ause);
+ assert(((rq_hdr[44] >> 4) & 0xf) == args->owpass);
+
+ assert(((rq_hdr[45] >> 0) & 0x1) == args->oipbp);
+ assert(((rq_hdr[45] >> 1) & 0x1) == args->nodas);
+
+ ovrpat = rq_hdr[51] << 24 | rq_hdr[50] << 16 |
+ rq_hdr[49] << 8 | rq_hdr[48];
+ assert(ovrpat == args->ovrpat);
+
+ test_transport_resp_calc_mic(resp);
+
+ return 0;
+}
+
+static void test_admin_sanitize_nvm(struct nvme_mi_ep *ep)
+{
+ struct nvme_sanitize_nvm_args args = { 0 };
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+ assert(ctrl);
+
+ test_set_transport_callback(ep, test_admin_sanitize_nvm_cb, &args);
+
+ args.args_size = sizeof(args);
+ args.sanact = 0x7;
+ args.ause = 0x0;
+ args.owpass = 0xf;
+ args.oipbp = 0x0;
+ args.nodas = 0x1;
+ args.ovrpat = ~0x04030201;
+
+ rc = nvme_mi_admin_sanitize_nvm(ctrl, &args);
+ assert(!rc);
+
+ args.sanact = 0x0;
+ args.ause = 0x1;
+ args.owpass = 0x0;
+ args.oipbp = 0x1;
+ args.nodas = 0x0;
+ args.ovrpat = 0x04030201;
+
+ rc = nvme_mi_admin_sanitize_nvm(ctrl, &args);
+ assert(!rc);
+}
+
+/* test that we set the correct offset and size on get_log() calls that
+ * are split into multiple requests */
+struct log_data {
+ int n;
+};
+
+static int test_admin_get_log_split_cb(struct nvme_mi_ep *ep,
+ struct nvme_mi_req *req,
+ struct nvme_mi_resp *resp,
+ void *data)
+{
+ struct log_data *ldata = data;
+ uint32_t len, off;
+ __u8 *rq_hdr;
+
+ assert(req->data_len == 0);
+
+ rq_hdr = (__u8 *)req->hdr;
+
+ assert(rq_hdr[4] == nvme_admin_get_log_page);
+
+ /* from the MI message's DOFST/DLEN fields */
+ off = rq_hdr[31] << 24 | rq_hdr[30] << 16 | rq_hdr[29] << 8 | rq_hdr[28];
+ len = rq_hdr[35] << 24 | rq_hdr[34] << 16 | rq_hdr[33] << 8 | rq_hdr[32];
+
+ /* we should have a full-sized start and middle, and a short end */
+ switch (ldata->n) {
+ case 0:
+ assert(len == 4096);
+ assert(off == 0);
+ break;
+ case 1:
+ assert(len == 4096);
+ assert(off == 4096);
+ break;
+ case 2:
+ assert(len == 4);
+ assert(off == 4096 * 2);
+ break;
+ default:
+ assert(0);
+ }
+
+ /* ensure we've sized the expected response correctly */
+ assert(resp->data_len == len);
+ memset(resp->data, ldata->n & 0xff, len);
+
+ test_transport_resp_calc_mic(resp);
+
+ ldata->n++;
+
+ return 0;
+}
+
+static void test_admin_get_log_split(struct nvme_mi_ep *ep)
+{
+ unsigned char buf[4096 * 2 + 4];
+ struct nvme_get_log_args args;
+ struct log_data ldata;
+ nvme_mi_ctrl_t ctrl;
+ int rc;
+
+ ldata.n = 0;
+ test_set_transport_callback(ep, test_admin_get_log_split_cb, &ldata);
+
+ ctrl = nvme_mi_init_ctrl(ep, 5);
+
+ args.args_size = sizeof(args);
+ args.lid = 1;
+ args.log = buf;
+ args.len = sizeof(buf);
+
+ rc = nvme_mi_admin_get_log(ctrl, &args);
+
+ assert(!rc);
+
+ /* we should have sent three commands */
+ assert(ldata.n == 3);
+}
+
#define DEFINE_TEST(name) { #name, test_ ## name }
struct test {
const char *name;
@@ -769,6 +1801,24 @@ struct test {
DEFINE_TEST(mi_config_get_mtu),
DEFINE_TEST(mi_config_set_freq),
DEFINE_TEST(mi_config_set_freq_invalid),
+ DEFINE_TEST(get_features_nodata),
+ DEFINE_TEST(get_features_data),
+ DEFINE_TEST(set_features),
+ DEFINE_TEST(admin_id_alloc_ns_list),
+ DEFINE_TEST(admin_id_active_ns_list),
+ DEFINE_TEST(admin_id_alloc_ns),
+ DEFINE_TEST(admin_id_active_ns),
+ DEFINE_TEST(admin_id_nsid_ctrl_list),
+ DEFINE_TEST(admin_id_secondary_ctrl_list),
+ DEFINE_TEST(admin_ns_mgmt_create),
+ DEFINE_TEST(admin_ns_mgmt_delete),
+ DEFINE_TEST(admin_ns_attach),
+ DEFINE_TEST(admin_ns_detach),
+ DEFINE_TEST(admin_fw_download),
+ DEFINE_TEST(admin_fw_commit),
+ DEFINE_TEST(admin_format_nvm),
+ DEFINE_TEST(admin_sanitize_nvm),
+ DEFINE_TEST(admin_get_log_split),
};
static void run_test(struct test *test, FILE *logfd, nvme_mi_ep_t ep)
diff --git a/test/test.c b/test/test.c
index bc5393b..2f24e1e 100644
--- a/test/test.c
+++ b/test/test.c
@@ -19,7 +19,6 @@
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
-#include <uuid.h>
#include <libnvme.h>
#include <ccan/endian/endian.h>
@@ -377,8 +376,8 @@ int main(int argc, char **argv)
nvme_ctrl_get_state(c));
nvme_ctrl_for_each_ns(c, n) {
- char uuid_str[40];
- uuid_t uuid;
+ char uuid_str[NVME_UUID_LEN_STRING];
+ unsigned char uuid[NVME_UUID_LEN];
printf(" `- %s lba size:%d lba max:%" PRIu64 "\n",
nvme_ns_get_name(n),
nvme_ns_get_lba_size(n),
@@ -388,7 +387,7 @@ int main(int argc, char **argv)
printf(" nguid:");
print_hex(nvme_ns_get_nguid(n), 16);
nvme_ns_get_uuid(n, uuid);
- uuid_unparse_lower(uuid, uuid_str);
+ nvme_uuid_to_string(uuid, uuid_str);
printf(" uuid:%s csi:%d\n", uuid_str,
nvme_ns_get_csi(n));
}
diff --git a/test/uuid.c b/test/uuid.c
new file mode 100644
index 0000000..9146453
--- /dev/null
+++ b/test/uuid.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/**
+ * This file is part of libnvme.
+ * Copyright (c) 2022 Daniel Wagner, SUSE Software Solutions
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <ccan/array_size/array_size.h>
+
+#include <libnvme.h>
+
+static int test_rc;
+
+struct test_data {
+ unsigned char uuid[NVME_UUID_LEN];
+ const char *str;
+};
+
+static struct test_data test_data[] = {
+ { { 0 }, "00000000-0000-0000-0000-000000000000" },
+ { { [0 ... 15] = 0xff }, "ffffffff-ffff-ffff-ffff-ffffffffffff" },
+ { { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0f, 0x10 },
+ "00010203-0405-0607-0809-0a0b0c0d0f10" },
+};
+
+static void check_str(const char *exp, const char *res)
+{
+ if (!strcmp(res, exp))
+ return;
+
+ printf("ERROR: got '%s', expected '%s'\n", res, exp);
+
+ test_rc = 1;
+}
+
+static void print_uuid_hex(const unsigned char uuid[NVME_UUID_LEN])
+{
+ for (int i = 0; i < NVME_UUID_LEN; i++)
+ printf("%02x", uuid[i]);
+}
+
+static void check_uuid(unsigned char exp[NVME_UUID_LEN],
+ unsigned char res[NVME_UUID_LEN])
+{
+ if (!memcmp(exp, res, NVME_UUID_LEN))
+ return;
+
+ printf("ERROR: got '");
+ print_uuid_hex(exp);
+ printf("', expected '");
+ print_uuid_hex(res);
+ printf("'\n");
+}
+
+static void tostr_test(struct test_data *test)
+{
+ char str[NVME_UUID_LEN_STRING];
+
+ if (nvme_uuid_to_string(test->uuid, str)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_to_string() failed\n");
+ return;
+ }
+ check_str(test->str, str);
+}
+
+static void fromstr_test(struct test_data *test)
+{
+
+ unsigned char uuid[NVME_UUID_LEN];
+
+ if (nvme_uuid_from_string(test->str, uuid)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_from_string() failed\n");
+ return;
+ }
+ check_uuid(test->uuid, uuid);
+}
+
+static void random_uuid_test(void)
+{
+ unsigned char uuid1[NVME_UUID_LEN], uuid2[NVME_UUID_LEN];
+ char str1[NVME_UUID_LEN_STRING], str2[NVME_UUID_LEN_STRING];
+
+ if (nvme_uuid_random(uuid1) || nvme_uuid_random(uuid2)) {
+ test_rc = 1;
+ printf("ERROR: nvme_uuid_random() failed\n");
+ return;
+ }
+
+ if (!memcmp(uuid1, uuid2, NVME_UUID_LEN)) {
+ test_rc = 1;
+ printf("ERROR: generated random numbers are equal\n");
+ return;
+ }
+
+ if (nvme_uuid_to_string(uuid1, str1) ||
+ nvme_uuid_to_string(uuid2, str2)) {
+ test_rc = 1;
+ printf("ERROR: could not stringify randomly generated UUID\n");
+ return;
+ }
+ printf("PASS: generated UUIDs %s %s\n", str1, str2);
+}
+
+int main(void)
+{
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++)
+ tostr_test(&test_data[i]);
+
+ for (int i = 0; i < ARRAY_SIZE(test_data); i++)
+ fromstr_test(&test_data[i]);
+
+ random_uuid_test();
+
+ return test_rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}