diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/meson.build | 11 | ||||
-rw-r--r-- | test/mi-mctp.c | 38 | ||||
-rw-r--r-- | test/mi.c | 1050 | ||||
-rw-r--r-- | test/test.c | 7 | ||||
-rw-r--r-- | test/uuid.c | 120 |
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), @@ -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; +} |