From 80ed950183376c9e6184d420ecfb11b92eba51c0 Mon Sep 17 00:00:00 2001 From: Dennis Maisenbacher Date: Tue, 1 Jul 2025 13:39:13 +0200 Subject: [PATCH] test: add tests for miscellaneous ioctl functions Use the mock ioctl infrastructure to test the functions in ioctl.h that issue miscellaneous ioctl commands. Signed-off-by: Dennis Maisenbacher --- test/ioctl/meson.build | 9 + test/ioctl/misc.c | 686 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 695 insertions(+) create mode 100644 test/ioctl/misc.c diff --git a/test/ioctl/meson.build b/test/ioctl/meson.build index 10fff28e7..f97925fe5 100644 --- a/test/ioctl/meson.build +++ b/test/ioctl/meson.build @@ -83,3 +83,12 @@ zns = executable( ) test('zns', zns, env: mock_ioctl_env) + +misc = executable( + 'test-misc', + 'misc.c', + dependencies: libnvme_dep, + link_with: mock_ioctl, +) + +test('misc', misc, env: mock_ioctl_env) diff --git a/test/ioctl/misc.c b/test/ioctl/misc.c new file mode 100644 index 000000000..2b4f3e768 --- /dev/null +++ b/test/ioctl/misc.c @@ -0,0 +1,686 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include + +#include "mock.h" +#include "util.h" +#include +#include +#include +#include +#include + +#define __cleanup__(fn) __attribute__((cleanup(fn))) + +static inline void freep(void *p) +{ + free(*(void **)p); +} +#define _cleanup_free_ __cleanup__(freep) + +#define TEST_FD 0xFD +#define TEST_NSID 0x12345678 +#define TEST_CSI NVME_CSI_KV + +static void test_format_nvm(void) +{ + __u32 result = 0; + struct nvme_format_nvm_args args = { + .result = &result, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .mset = NVME_FORMAT_MSET_EXTENDED, + .pi = NVME_FORMAT_PI_TYPE2, + .pil = NVME_FORMAT_PIL_FIRST, + .ses = NVME_FORMAT_SES_USER_DATA_ERASE, + .lbaf = 0xF, + .lbafu = 0x1, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_format_nvm, + .nsid = TEST_NSID, + .cdw10 = args.lbaf | (args.mset << 4) | (args.pi << 5) | + (args.pil << 8) | (args.ses << 9) | (args.lbafu << 12), + .result = 0, + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_format_nvm(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); +} + +static void test_ns_mgmt(void) +{ + struct nvme_ns_mgmt_host_sw_specified expected_data, data = {}; + __u32 result = 0; + struct nvme_ns_mgmt_args args = { + .result = &result, + .ns = NULL, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .sel = NVME_NS_MGMT_SEL_CREATE, + .csi = TEST_CSI, + .data = &data, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_mgmt, + .nsid = TEST_NSID, + .cdw10 = args.sel, + .cdw11 = args.csi << 24, + .result = 0, + .data_len = sizeof(expected_data), + .out_data = &expected_data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_mgmt(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_ns_mgmt_create(void) +{ + struct nvme_ns_mgmt_host_sw_specified expected_data, data = {}; + __u32 result = 0; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_mgmt, + .nsid = NVME_NSID_NONE, + .cdw10 = NVME_NS_MGMT_SEL_CREATE, + .cdw11 = NVME_CSI_ZNS << 24, + .result = TEST_NSID, + .data_len = sizeof(expected_data), + .out_data = &expected_data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_mgmt_create(TEST_FD, NULL, &result, 0, NVME_CSI_ZNS, + &data); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == TEST_NSID, "returned result %u", result); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_ns_mgmt_delete(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_mgmt, + .nsid = TEST_NSID, + .cdw10 = NVME_NS_MGMT_SEL_DELETE, + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_mgmt_delete(TEST_FD, TEST_NSID); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); +} + +static void test_get_property(void) +{ + __u64 expected_result, result; + struct nvme_get_property_args args = { + .value = &result, + .args_size = sizeof(args), + .fd = TEST_FD, + .offset = NVME_REG_ACQ, + }; + + arbitrary(&expected_result, sizeof(expected_result)); + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_get, + .cdw10 = !!true, + .cdw11 = NVME_REG_ACQ, + .result = expected_result, + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_property(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == expected_result, "returned wrong result"); +} + +static void test_set_property(void) +{ + __u64 value = 0xffffffff; + __u32 result; + struct nvme_set_property_args args = { + .value = value, + .result = &result, + .args_size = sizeof(args), + .fd = TEST_FD, + .offset = NVME_REG_BPMBL, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_fabrics, + .nsid = nvme_fabrics_type_property_set, + .cdw10 = !!true, + .cdw11 = NVME_REG_BPMBL, + .cdw12 = value & 0xffffffff, + .cdw13 = value >> 32, + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_set_property(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); +} + +static void test_ns_attach(void) +{ + __u32 result; + struct nvme_ctrl_list expected_ctrlist, ctrlist; + struct nvme_ns_attach_args args = { + .result = &result, + .ctrlist = &ctrlist, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .sel = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_attach, + .nsid = TEST_NSID, + .cdw10 = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + .data_len = sizeof(expected_ctrlist), + .out_data = &expected_ctrlist, + }; + + int err; + + arbitrary(&expected_ctrlist, sizeof(expected_ctrlist)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_attach(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); + cmp(&expected_ctrlist, &ctrlist, sizeof(expected_ctrlist), + "incorrect data"); +} + +static void test_ns_attach_ctrls(void) +{ + struct nvme_ctrl_list ctrlist; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_attach, + .nsid = TEST_NSID, + .cdw10 = NVME_NS_ATTACH_SEL_CTRL_ATTACH, + .data_len = sizeof(ctrlist), + .out_data = &ctrlist, + }; + + int err; + + arbitrary(&ctrlist, sizeof(ctrlist)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_attach_ctrls(TEST_FD, TEST_NSID, &ctrlist); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); +} + +static void test_ns_detach_ctrls(void) +{ + struct nvme_ctrl_list ctrlist; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_ns_attach, + .nsid = TEST_NSID, + .cdw10 = NVME_NS_ATTACH_SEL_CTRL_DEATTACH, + .data_len = sizeof(ctrlist), + .out_data = &ctrlist, + }; + + int err; + + arbitrary(&ctrlist, sizeof(ctrlist)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_ns_detach_ctrls(TEST_FD, TEST_NSID, &ctrlist); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); +} + +static void test_fw_download(void) +{ + __u32 result = 0; + __u8 expected_data[8], data[8]; + + struct nvme_fw_download_args args = { + .result = &result, + .data = &expected_data, + .args_size = sizeof(args), + .fd = TEST_FD, + .offset = 123, + .data_len = sizeof(expected_data), + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_fw_download, + .cdw10 = (args.data_len >> 2) - 1, + .cdw11 = args.offset >> 2, + .data_len = args.data_len, + .in_data = &data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + memcpy(&data, &expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_fw_download(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); +} + +static void test_fw_commit(void) +{ + __u32 result = 0; + + struct nvme_fw_commit_args args = { + .result = &result, + .args_size = sizeof(args), + .fd = TEST_FD, + .action = NVME_FW_COMMIT_CA_REPLACE_AND_ACTIVATE_IMMEDIATE, + .slot = 0xf, + .bpid = true, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_fw_commit, + .cdw10 = (!!args.bpid << 31) | (args.action << 3) | args.slot, + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_fw_commit(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); +} + +static void test_security_send(void) +{ + __u8 expected_data[8], data[8]; + __u32 result = 0; + + struct nvme_security_send_args args = { + .result = &result, + .data = &expected_data, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .tl = 0xffff, + .data_len = sizeof(expected_data), + .nssf = 0x1, + .spsp0 = 0x1, + .spsp1 = 0x1, + .secp = 0xE9, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_security_send, + .nsid = TEST_NSID, + .cdw10 = args.nssf | (args.spsp0 << 8) | (args.spsp1 << 16) | + (args.secp << 24), + .cdw11 = args.tl, + .data_len = args.data_len, + .in_data = &data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + memcpy(&data, &expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_security_send(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_security_receive(void) +{ + __u8 expected_data[8], data[8]; + __u32 result = 0; + + struct nvme_security_receive_args args = { + .result = &result, + .data = &data, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .al = 0xffff, + .data_len = sizeof(data), + .nssf = 0x1, + .spsp0 = 0x1, + .spsp1 = 0x1, + .secp = 0xE9, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_security_recv, + .nsid = TEST_NSID, + .cdw10 = args.nssf | (args.spsp0 << 8) | (args.spsp1 << 16) | + (args.secp << 24), + .cdw11 = args.al, + .data_len = args.data_len, + .out_data = &expected_data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_security_receive(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned result %u", result); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_get_lba_status(void) +{ + __u32 result = 0; + __u8 nlsd = 3; + int lba_status_size = sizeof(struct nvme_lba_status) + + nlsd * sizeof(struct nvme_lba_status_desc); + + _cleanup_free_ struct nvme_lba_status *lbas = NULL; + _cleanup_free_ struct nvme_lba_status *expected_lbas = NULL; + + lbas = malloc(lba_status_size); + check(lbas, "lbas: ENOMEM"); + expected_lbas = malloc(lba_status_size); + check(expected_lbas, "lbas: ENOMEM"); + + struct nvme_get_lba_status_args args = { + .slba = 0x123456789, + .result = &result, + .lbas = lbas, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .mndw = ((lba_status_size - 1) >> 2), + .atype = 0x11, + .rl = 0x42, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_get_lba_status, + .nsid = TEST_NSID, + .cdw10 = args.slba & 0xffffffff, + .cdw11 = args.slba >> 32, + .cdw12 = args.mndw, + .cdw13 = args.rl | (args.atype << 24), + .data_len = (args.mndw + 1) << 2, + .out_data = expected_lbas, + }; + + int err; + + arbitrary(expected_lbas, lba_status_size); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_get_lba_status(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned wrong result"); + cmp(lbas, expected_lbas, lba_status_size, "incorrect lbas"); +} + +static void test_directive_send(void) +{ + __u8 expected_data[8], data[8]; + __u32 result = 0; + + struct nvme_directive_send_args args = { + .result = &result, + .data = &expected_data, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .doper = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0xffff, + .data_len = sizeof(expected_data), + .dspec = 0x0, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_send, + .nsid = TEST_NSID, + .cdw10 = args.data_len ? (args.data_len >> 2) - 1 : 0, + .cdw11 = args.doper | (args.dtype << 8) | (args.dspec << 16), + .cdw12 = args.cdw12, + .data_len = args.data_len, + .in_data = &data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + memcpy(&data, &expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_send(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned wrong result"); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_directive_send_id_endir(void) +{ + struct nvme_id_directives expected_id, id; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_send, + .nsid = TEST_NSID, + .cdw10 = (sizeof(expected_id) >> 2) - 1, + .cdw11 = NVME_DIRECTIVE_SEND_IDENTIFY_DOPER_ENDIR | + (NVME_DIRECTIVE_DTYPE_IDENTIFY << 8), + .cdw12 = (!!true) | (NVME_DIRECTIVE_DTYPE_STREAMS << 1), + .data_len = sizeof(id), + .in_data = &id, + }; + + int err; + + arbitrary(&expected_id, sizeof(expected_id)); + memcpy(&id, &expected_id, sizeof(expected_id)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_send_id_endir(TEST_FD, TEST_NSID, true, + NVME_DIRECTIVE_DTYPE_STREAMS, &id); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + cmp(&id, &expected_id, sizeof(id), "incorrect id"); +} + +static void test_directive_send_stream_release_identifier(void) +{ + __u16 stream_id = 0x1234; + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_send, + .nsid = TEST_NSID, + .cdw11 = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_IDENTIFIER | + (NVME_DIRECTIVE_DTYPE_STREAMS << 8) | + (stream_id << 16), + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_send_stream_release_identifier(TEST_FD, TEST_NSID, + stream_id); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); +} + +static void test_directive_send_stream_release_resource(void) +{ + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_send, + .nsid = TEST_NSID, + .cdw11 = NVME_DIRECTIVE_SEND_STREAMS_DOPER_RELEASE_RESOURCE | + (NVME_DIRECTIVE_DTYPE_STREAMS << 8), + }; + + int err; + + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_send_stream_release_resource(TEST_FD, TEST_NSID); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); +} + +static void test_directive_recv(void) +{ + __u8 expected_data[8], data[8]; + __u32 result = 0; + + struct nvme_directive_recv_args args = { + .result = &result, + .data = &data, + .args_size = sizeof(args), + .fd = TEST_FD, + .nsid = TEST_NSID, + .doper = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM, + .dtype = NVME_DIRECTIVE_DTYPE_STREAMS, + .cdw12 = 0xffff, + .data_len = sizeof(data), + .dspec = 0x0, + }; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_recv, + .nsid = TEST_NSID, + .cdw10 = args.data_len ? (args.data_len >> 2) - 1 : 0, + .cdw11 = args.doper | (args.dtype << 8) | (args.dspec << 16), + .cdw12 = args.cdw12, + .data_len = sizeof(expected_data), + .out_data = &expected_data, + }; + + int err; + + arbitrary(&expected_data, sizeof(expected_data)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_recv(&args); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + check(result == 0, "returned wrong result"); + cmp(&data, &expected_data, sizeof(data), "incorrect data"); +} + +static void test_directive_recv_identify_parameters(void) +{ + struct nvme_id_directives expected_id, id; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_recv, + .nsid = TEST_NSID, + .cdw10 = (sizeof(expected_id) >> 2) - 1, + .cdw11 = NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM | + (NVME_DIRECTIVE_DTYPE_IDENTIFY << 8), + .data_len = sizeof(expected_id), + .out_data = &expected_id, + }; + + int err; + + arbitrary(&expected_id, sizeof(expected_id)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_recv_identify_parameters(TEST_FD, TEST_NSID, &id); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + cmp(&id, &expected_id, sizeof(id), "incorrect id"); +} + +static void test_directive_recv_stream_parameters(void) +{ + struct nvme_streams_directive_params expected_params, params; + + struct mock_cmd mock_admin_cmd = { + .opcode = nvme_admin_directive_recv, + .nsid = TEST_NSID, + .cdw10 = (sizeof(expected_params) >> 2) - 1, + .cdw11 = NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM | + (NVME_DIRECTIVE_DTYPE_STREAMS << 8), + .data_len = sizeof(expected_params), + .out_data = &expected_params, + }; + + int err; + + arbitrary(&expected_params, sizeof(expected_params)); + set_mock_admin_cmds(&mock_admin_cmd, 1); + err = nvme_directive_recv_stream_parameters(TEST_FD, TEST_NSID, + ¶ms); + end_mock_cmds(); + check(err == 0, "returned error %d, errno %m", err); + cmp(¶ms, &expected_params, sizeof(params), "incorrect params"); +} + +static void run_test(const char *test_name, void (*test_fn)(void)) +{ + printf("Running test %s...", test_name); + fflush(stdout); + test_fn(); + puts(" OK"); +} + +#define RUN_TEST(name) run_test(#name, test_##name) + +int main(void) +{ + set_mock_fd(TEST_FD); + RUN_TEST(format_nvm); + RUN_TEST(ns_mgmt); + RUN_TEST(ns_mgmt_create); + RUN_TEST(ns_mgmt_delete); + RUN_TEST(get_property); + RUN_TEST(set_property); + RUN_TEST(ns_attach); + RUN_TEST(ns_attach_ctrls); + RUN_TEST(ns_detach_ctrls); + RUN_TEST(fw_download); + RUN_TEST(fw_commit); + RUN_TEST(security_send); + RUN_TEST(security_receive); + RUN_TEST(get_lba_status); + RUN_TEST(directive_send); + RUN_TEST(directive_send_id_endir); + RUN_TEST(directive_send_stream_release_identifier); + RUN_TEST(directive_send_stream_release_resource); + RUN_TEST(directive_recv); + RUN_TEST(directive_recv_identify_parameters); + RUN_TEST(directive_recv_stream_parameters); +}