Bug Summary

File:.build-ci/../libnvme/test/mi-mctp.c
Warning:line 1190, column 3
Address of stack memory associated with local variable 'fn_data' is still referred to by the caller variable 'peer' upon returning to the caller. This will be a dangling reference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mi-mctp.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/test/test-mi-mctp.p -I libnvme/test -I ../libnvme/test -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I shared -I ../shared -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-08-08-045408-589-1 -x c ../libnvme/test/mi-mctp.c
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/**
3 * This file is part of libnvme.
4 * Copyright (c) 2022 Code Construct
5 */
6
7#undef NDEBUG
8#include <shr-assert.h>
9#include <errno(*__errno_location ()).h>
10#include <fcntl.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16#include <sys/ioctl.h>
17#include <sys/socket.h>
18
19#if NVME_HAVE_LINUX_MCTP_H1
20#include <linux1/mctp.h>
21#else
22#include "nvme/mi-mctp-compat.h"
23#endif
24
25#include <ccan/array_size/array_size.h>
26#include <ccan/endian/endian.h>
27
28#include <libnvme.h>
29#include <libnvme-mi.h>
30
31#include "nvme/private.h"
32#include "nvme/private-mi.h"
33
34#include "utils.h"
35
36/* 4096 byte max MCTP message, plus space for header data */
37#define MAX_BUFSIZ8192 8192
38
39struct test_peer;
40
41typedef int (*rx_test_fn)(struct test_peer *peer, void *buf, size_t len, int sd);
42typedef int (*poll_test_fn)(struct test_peer *peer,
43 struct pollfd *fds, nfds_t nfds, int timeout);
44
45#define TEST_PEER_SD_COMMANDS_IDX(0) (0)
46#define TEST_PEER_SD_AEMS_IDX(1) (1)
47
48/* Our fake MCTP "peer".
49 *
50 * The terms TX (transmit) and RX (receive) are from the perspective of
51 * the NVMe device. TX is device-to-libnvme, RX is libnvme-to-device.
52 *
53 * The RX and TX buffers are linear versions of the data sent and received by
54 * libnvme-mi, and *include* the MCTP message type byte (even though it's
55 * omitted in the sendmsg/recvmsg interface), so that the buffer inspection
56 * in the tests can exactly match the NVMe-MI spec packet diagrams.
57 */
58static struct test_peer {
59 /* rx (sendmsg) data sent from libnvme, and return value */
60 unsigned char rx_buf[MAX_BUFSIZ8192];
61 size_t rx_buf_len;
62 ssize_t rx_rc; /* if zero, return the sendmsg len */
63 int rx_errno;
64
65 /* tx (recvmsg) data to be received by libnvme and return value */
66 unsigned char tx_buf[MAX_BUFSIZ8192];
67 size_t tx_buf_len;
68 ssize_t tx_rc; /* if zero, return the recvmsg len */
69 int tx_errno;
70
71 /* Optional, called before TX, may set tx_buf according to request.
72 * Return value stored in tx_res, may be used by test */
73 rx_test_fn tx_fn;
74 void *tx_data;
75 int tx_fn_res;
76
77 poll_test_fn poll_fn;
78 void *poll_data;
79
80 /* store sd from socket() setup */
81 int sd[2];
82} test_peer;
83
84/* ensure tests start from a standard state */
85void reset_test_peer(void)
86{
87 int temp_sd[2] = {test_peer.sd[TEST_PEER_SD_COMMANDS_IDX(0)],
88 test_peer.sd[TEST_PEER_SD_AEMS_IDX(1)]};
89
90 memset(&test_peer, 0, sizeof(test_peer));
91 test_peer.tx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
92 test_peer.rx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
93 memcpy(test_peer.sd, temp_sd, 2*sizeof(*temp_sd));
94}
95
96/* calculate MIC of peer-to-libnvme data, expand buf by 4 bytes and insert
97 * the new MIC */
98static void test_set_tx_mic(struct test_peer *peer)
99{
100 extern __u32 libnvme_mi_crc32_update(__u32 crc, void *data, size_t len);
101 __u32 crc = 0xffffffff;
102 __le32 crc_le;
103
104 shr_assert(peer->tx_buf_len + sizeof(crc_le) <= MAX_BUFSIZ)((void)((peer->tx_buf_len + sizeof(crc_le) <= 8192) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 104, "peer->tx_buf_len + sizeof(crc_le) <= MAX_BUFSIZ"
), 0)))
;
105
106 crc = libnvme_mi_crc32_update(crc, peer->tx_buf, peer->tx_buf_len);
107 crc_le = cpu_to_le32(~crc);
108 memcpy(peer->tx_buf + peer->tx_buf_len, &crc_le, sizeof(crc_le));
109 peer->tx_buf_len += sizeof(crc_le);
110}
111
112int __wrap_msg_socket(void)
113{
114 /* we do an open here to give the mi-mctp code something to close() */
115 test_peer.sd[TEST_PEER_SD_COMMANDS_IDX(0)] = open("/dev/null", 0);
116 return test_peer.sd[TEST_PEER_SD_COMMANDS_IDX(0)];
117}
118
119int __wrap_aem_socket(__u8 eid, unsigned int network)
120{
121 /* we do an open here to give the mi-mctp code something to close() */
122 test_peer.sd[TEST_PEER_SD_AEMS_IDX(1)] = open("/dev/null", 0);
123 return test_peer.sd[TEST_PEER_SD_AEMS_IDX(1)];
124}
125
126ssize_t __wrap_sendmsg(int sd, const struct msghdr *hdr, int flags)
127{
128 size_t i, pos;
129
130 shr_assert(sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == test_peer.sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 130, "sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
131
132 test_peer.rx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
133
134 /* gather iovec into buf */
135 for (i = 0, pos = 1; i < hdr->msg_iovlen; i++) {
136 struct iovec *iov = &hdr->msg_iov[i];
137
138 shr_assert(pos + iov->iov_len < MAX_BUFSIZ - 1)((void)((pos + iov->iov_len < 8192 - 1) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 138, "pos + iov->iov_len < MAX_BUFSIZ - 1"
), 0)))
;
139 memcpy(test_peer.rx_buf + pos, iov->iov_base, iov->iov_len);
140 pos += iov->iov_len;
141 }
142
143 test_peer.rx_buf_len = pos;
144
145 errno(*__errno_location ()) = test_peer.rx_errno;
146
147 return test_peer.rx_rc ?: (pos - 1);
148}
149
150ssize_t __wrap_recvmsg(int sd, struct msghdr *hdr, int flags)
151{
152 size_t i, pos, len;
153
154 shr_assert(sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX] ||((void)((sd == test_peer.sd[(0)] || sd == test_peer.sd[(1)]) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 155, "sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX] || sd == test_peer.sd[TEST_PEER_SD_AEMS_IDX]"
), 0)))
155 sd == test_peer.sd[TEST_PEER_SD_AEMS_IDX])((void)((sd == test_peer.sd[(0)] || sd == test_peer.sd[(1)]) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 155, "sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX] || sd == test_peer.sd[TEST_PEER_SD_AEMS_IDX]"
), 0)))
;
156
157 //Check for purge case
158 if (flags & MSG_TRUNCMSG_TRUNC)
159 return 0;
160
161 if (test_peer.tx_fn) {
162 test_peer.tx_fn_res = test_peer.tx_fn(&test_peer,
163 test_peer.rx_buf,
164 test_peer.rx_buf_len,
165 sd);
166 } else {
167 if (sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX(0)] && test_peer.tx_buf_len == 0) {
168 errno(*__errno_location ()) = EAGAIN11;
169 return -1;
170 }
171 /* set up a few default response fields; caller may have
172 * initialised the rest of the response */
173 test_peer.tx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
174 test_peer.tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
175 test_set_tx_mic(&test_peer);
176 }
177
178 /* scatter buf into iovec */
179 for (i = 0, pos = 1; i < hdr->msg_iovlen && pos < test_peer.tx_buf_len;
180 i++) {
181 struct iovec *iov = &hdr->msg_iov[i];
182
183 len = iov->iov_len;
184 if (len > test_peer.tx_buf_len - pos)
185 len = test_peer.tx_buf_len - pos;
186
187 memcpy(iov->iov_base, test_peer.tx_buf + pos, len);
188 pos += len;
189 }
190
191 errno(*__errno_location ()) = test_peer.tx_errno;
192
193 test_peer.tx_buf_len = 0; //Clear since this is sent
194 return test_peer.tx_rc ?: (pos - 1);
195}
196
197int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
198{
199 if (!test_peer.poll_fn)
200 return 1;
201
202 return test_peer.poll_fn(&test_peer, fds, nfds, timeout);
203}
204
205struct mctp_ioc_tag_ctl;
206
207#ifdef SIOCMCTPALLOCTAG(0x89E0 + 0)
208int test_ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl)
209{
210 shr_assert(sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == test_peer.sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 210, "sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
211
212 switch (req) {
213 case SIOCMCTPALLOCTAG(0x89E0 + 0):
214 ctl->tag = 1 | MCTP_TAG_PREALLOC0x10 | MCTP_TAG_OWNER0x08;
215 break;
216 case SIOCMCTPDROPTAG(0x89E0 + 1):
217 shr_assert(ctl->tag == (1 | MCTP_TAG_PREALLOC | MCTP_TAG_OWNER))((void)((ctl->tag == (1 | 0x10 | 0x08)) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 217, "ctl->tag == (1 | MCTP_TAG_PREALLOC | MCTP_TAG_OWNER)"
), 0)))
;
218 break;
219 };
220
221 return 0;
222}
223#else
224int test_ioctl_tag(int sd, unsigned long req, struct mctp_ioc_tag_ctl *ctl)
225{
226 shr_assert(sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == test_peer.sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 226, "sd == test_peer.sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
227 return 0;
228}
229#endif
230
231static struct __mi_mctp_socket_ops ops = {
232 __wrap_msg_socket,
233 __wrap_aem_socket,
234 __wrap_sendmsg,
235 __wrap_recvmsg,
236 __wrap_poll,
237 test_ioctl_tag,
238};
239
240/* tests */
241static void test_rx_err(libnvme_mi_ep_t ep, struct test_peer *peer)
242{
243 struct nvme_mi_read_nvm_ss_info ss_info;
244 int rc;
245
246 peer->rx_rc = -1;
247
248 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
249 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 249, "rc != 0"), 0)))
;
250}
251
252static int tx_none(struct test_peer *peer, void *buf, size_t len, int sd)
253{
254 return 0;
255}
256
257static void test_tx_none(libnvme_mi_ep_t ep, struct test_peer *peer)
258{
259 struct nvme_mi_read_nvm_ss_info ss_info;
260 int rc;
261
262 peer->tx_buf_len = 0;
263 peer->tx_fn = tx_none;
264
265 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
266 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 266, "rc != 0"), 0)))
;
267}
268
269static void test_tx_err(libnvme_mi_ep_t ep, struct test_peer *peer)
270{
271 struct nvme_mi_read_nvm_ss_info ss_info;
272 int rc;
273
274 peer->tx_rc = -1;
275
276 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
277 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 277, "rc != 0"), 0)))
;
278}
279
280static void test_tx_short(libnvme_mi_ep_t ep, struct test_peer *peer)
281{
282 struct nvme_mi_read_nvm_ss_info ss_info;
283 int rc;
284
285 peer->tx_buf_len = 11;
286
287 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
288 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 288, "rc != 0"), 0)))
;
289}
290
291static int poll_fn_err(struct test_peer *peer, struct pollfd *fds,
292 nfds_t nfds, int timeout)
293{
294 return -1;
295}
296
297static void test_poll_err(libnvme_mi_ep_t ep, struct test_peer *peer)
298{
299 struct nvme_mi_read_nvm_ss_info ss_info;
300 int rc;
301
302 peer->poll_fn = poll_fn_err;
303
304 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
305 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 305, "rc != 0"), 0)))
;
306}
307
308static void test_read_mi_data(libnvme_mi_ep_t ep, struct test_peer *peer)
309{
310 struct nvme_mi_read_nvm_ss_info ss_info;
311 int rc;
312
313 /* empty response data */
314 peer->tx_buf_len = 8 + 32;
315
316 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
317 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 317, "rc == 0"), 0)))
;
318}
319
320static void test_mi_resp_err(libnvme_mi_ep_t ep, struct test_peer *peer)
321{
322 struct nvme_mi_read_nvm_ss_info ss_info;
323 int rc;
324
325 /* simple error response */
326 peer->tx_buf[4] = 0x02; /* internal error */
327 peer->tx_buf_len = 8;
328
329 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
330 shr_assert(rc == 0x2)((void)((rc == 0x2) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 330, "rc == 0x2"), 0)))
;
331}
332
333static void setup_unaligned_ctrl_list_resp(struct test_peer *peer)
334{
335 /* even number of controllers */
336 peer->tx_buf[8] = 0x02;
337 peer->tx_buf[9] = 0x00;
338
339 /* controller ID 1 */
340 peer->tx_buf[10] = 0x01;
341 peer->tx_buf[11] = 0x00;
342
343 /* controller ID 2 */
344 peer->tx_buf[12] = 0x02;
345 peer->tx_buf[13] = 0x00;
346
347 peer->tx_buf_len = 14;
348}
349
350/* Will call through the xfer/submit API expecting a full-sized list (so
351 * resp->data_len is set to sizeof(list)), but the endpoint will return an
352 * unaligned short list.
353 */
354static void test_mi_resp_unaligned(libnvme_mi_ep_t ep, struct test_peer *peer)
355{
356 struct nvme_ctrl_list list;
357 int rc;
358
359 setup_unaligned_ctrl_list_resp(peer);
360
361 memset(&list, 0, sizeof(list));
362
363 rc = libnvme_mi_mi_read_mi_data_ctrl_list(ep, 0, &list);
364 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 364, "rc == 0"), 0)))
;
365
366 shr_assert(le16_to_cpu(list.num) == 2)((void)((le16_to_cpu(list.num) == 2) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 366, "le16_to_cpu(list.num) == 2"), 0)))
;
367 shr_assert(le16_to_cpu(list.identifier[0]) == 1)((void)((le16_to_cpu(list.identifier[0]) == 1) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 367, "le16_to_cpu(list.identifier[0]) == 1"
), 0)))
;
368 shr_assert(le16_to_cpu(list.identifier[1]) == 2)((void)((le16_to_cpu(list.identifier[1]) == 2) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 368, "le16_to_cpu(list.identifier[1]) == 2"
), 0)))
;
369}
370
371/* Will call through the xfer/submit API expecting an unaligned list,
372 * and get a response of exactly that size.
373 */
374static void test_mi_resp_unaligned_expected(libnvme_mi_ep_t ep,
375 struct test_peer *peer)
376{
377 /* direct access to the raw submit() API */
378 extern int libnvme_mi_submit(libnvme_mi_ep_t ep, struct libnvme_mi_req *req,
379 struct libnvme_mi_resp *resp);
380 struct nvme_mi_mi_resp_hdr resp_hdr;
381 struct nvme_mi_mi_req_hdr req_hdr;
382 struct nvme_ctrl_list list;
383 struct libnvme_mi_resp resp;
384 struct libnvme_mi_req req;
385 int rc;
386
387 setup_unaligned_ctrl_list_resp(peer);
388
389 memset(&list, 0, sizeof(list));
390
391 memset(&req_hdr, 0, sizeof(req_hdr));
392 req_hdr.hdr.type = NVME_MI_MSGTYPE_NVME0x84;
393 req_hdr.hdr.nmp = (NVME_MI_ROR_REQ << 7) | (NVME_MI_MT_MI << 3);
394 req_hdr.opcode = nvme_mi_mi_opcode_mi_data_read;
395 req_hdr.cdw0 = cpu_to_le32(nvme_mi_dtyp_ctrl_list << 24);
396
397 memset(&req, 0, sizeof(req));
398 req.hdr = &req_hdr.hdr;
399 req.hdr_len = sizeof(req_hdr);
400
401 memset(&resp, 0, sizeof(resp));
402 resp.hdr = &resp_hdr.hdr;
403 resp.hdr_len = sizeof(resp_hdr);
404 resp.data = &list;
405 resp.data_len = peer->tx_buf_len;
406
407 rc = libnvme_mi_submit(ep, &req, &resp);
408 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 408, "rc == 0"), 0)))
;
409 shr_assert(resp.data_len == 6)((void)((resp.data_len == 6) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 409, "resp.data_len == 6"), 0)))
; /* 2-byte length, 2*2 byte controller IDs */
410
411 shr_assert(le16_to_cpu(list.num) == 2)((void)((le16_to_cpu(list.num) == 2) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 411, "le16_to_cpu(list.num) == 2"), 0)))
;
412 shr_assert(le16_to_cpu(list.identifier[0]) == 1)((void)((le16_to_cpu(list.identifier[0]) == 1) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 412, "le16_to_cpu(list.identifier[0]) == 1"
), 0)))
;
413 shr_assert(le16_to_cpu(list.identifier[1]) == 2)((void)((le16_to_cpu(list.identifier[1]) == 2) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 413, "le16_to_cpu(list.identifier[1]) == 2"
), 0)))
;
414}
415
416static void test_admin_resp_err(libnvme_mi_ep_t ep, struct test_peer *peer)
417{
418 struct libnvme_transport_handle *hdl;
419 struct libnvme_passthru_cmd cmd;
420 struct nvme_id_ctrl id;
421 int rc;
422
423 hdl = libnvme_mi_init_transport_handle(ep, 1);
424 shr_assert(hdl)((void)((hdl) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 424, "hdl"), 0)))
;
425
426 /* Simple error response, will be shorter than the expected Admin
427 * command response header. */
428 peer->tx_buf[4] = 0x02; /* internal error */
429 peer->tx_buf_len = 8;
430
431 nvme_init_identify_ctrl(&cmd, &id);
432 rc = libnvme_exec_admin_passthru(hdl, &cmd);
433 shr_assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI)((void)((nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 433, "nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI"
), 0)))
;
434 shr_assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR)((void)((nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 434, "nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR"
), 0)))
;
435}
436
437/* test: all 4-byte aligned response sizes - should be decoded into the
438 * response status value. We use an admin command here as the header size will
439 * be larger than the minimum header size (it contains the completion
440 * doublewords), and we need to ensure that an error response is correctly
441 * interpreted, including having the MIC extracted from the message.
442 */
443static void test_admin_resp_sizes(libnvme_mi_ep_t ep, struct test_peer *peer)
444{
445 struct libnvme_transport_handle *hdl;
446 struct libnvme_passthru_cmd cmd;
447 struct nvme_id_ctrl id;
448 unsigned int i;
449 int rc;
450
451 hdl = libnvme_mi_init_transport_handle(ep, 1);
452 shr_assert(hdl)((void)((hdl) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 452, "hdl"), 0)))
;
453
454 peer->tx_buf[4] = 0x02; /* internal error */
455
456 for (i = 8; i <= 4096 + 8; i+=4) {
457 peer->tx_buf_len = i;
458 nvme_init_identify_ctrl(&cmd, &id);
459 rc = libnvme_exec_admin_passthru(hdl, &cmd);
460 shr_assert(nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI)((void)((nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 460, "nvme_status_get_type(rc) == NVME_STATUS_TYPE_MI"
), 0)))
;
461 shr_assert(nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR)((void)((nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 461, "nvme_status_get_value(rc) == NVME_MI_RESP_INTERNAL_ERR"
), 0)))
;
462 }
463
464 libnvme_close(hdl);
465}
466
467/* test: timeout value passed to poll */
468static int poll_fn_timeout_value(struct test_peer *peer, struct pollfd *fds,
469 nfds_t nfds, int timeout)
470{
471 shr_assert(timeout == 3141)((void)((timeout == 3141) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 471, "timeout == 3141"), 0)))
;
472 return 1;
473}
474
475static void test_poll_timeout_value(libnvme_mi_ep_t ep, struct test_peer *peer)
476{
477 struct nvme_mi_read_nvm_ss_info ss_info;
478 int rc;
479
480 /* empty response data */
481 peer->tx_buf_len = 8 + 32;
482
483 peer->poll_fn = poll_fn_timeout_value;
484 libnvme_mi_ep_set_timeout(ep, 3141);
485
486 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
487 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 487, "rc == 0"), 0)))
;
488}
489
490/* test: poll timeout expiry */
491static int poll_fn_timeout(struct test_peer *peer, struct pollfd *fds,
492 nfds_t nfds, int timeout)
493{
494 return 0;
495}
496
497static void test_poll_timeout(libnvme_mi_ep_t ep, struct test_peer *peer)
498{
499 struct nvme_mi_read_nvm_ss_info ss_info;
500 int rc;
501
502 peer->poll_fn = poll_fn_timeout;
503
504 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
505 shr_assert(rc != 0)((void)((rc != 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 505, "rc != 0"), 0)))
;
506 shr_assert(errno == ETIMEDOUT)((void)(((*__errno_location ()) == 110) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 506, "errno == ETIMEDOUT"), 0)))
;
507}
508
509/* test: send a More Processing Required response, then the actual response */
510struct mpr_tx_info {
511 int msg_no;
512 bool_Bool admin_quirk;
513 size_t final_len;
514};
515
516static int tx_mpr(struct test_peer *peer, void *buf, size_t len, int sd)
517{
518 struct mpr_tx_info *tx_info = peer->tx_data;
519
520 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 520, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
521
522 memset(peer->tx_buf, 0, sizeof(peer->tx_buf));
523 peer->tx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
524 peer->tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
525
526 switch (tx_info->msg_no) {
527 case 1:
528 peer->tx_buf[4] = NVME_MI_RESP_MPR;
529 peer->tx_buf_len = 8;
530 if (tx_info->admin_quirk) {
531 peer->tx_buf_len = 20;
532 }
533 break;
534 case 2:
535 peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
536 peer->tx_buf_len = tx_info->final_len;
537 break;
538 default:
539 shr_assert(0)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 539
, "0"), 0)))
;
540 }
541
542 test_set_tx_mic(peer);
543
544 tx_info->msg_no++;
545
546 return 0;
547}
548
549static void test_mpr_mi(libnvme_mi_ep_t ep, struct test_peer *peer)
550{
551 struct nvme_mi_read_nvm_ss_info ss_info;
552 struct mpr_tx_info tx_info;
553 int rc;
554
555 tx_info.msg_no = 1;
556 tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
557 tx_info.admin_quirk = false0;
558
559 peer->tx_fn = tx_mpr;
560 peer->tx_data = &tx_info;
561
562 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
563 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 563, "rc == 0"), 0)))
;
564}
565
566static void test_mpr_admin(libnvme_mi_ep_t ep, struct test_peer *peer)
567{
568 struct libnvme_transport_handle *hdl;
569 struct libnvme_passthru_cmd cmd;
570 struct mpr_tx_info tx_info;
571 struct nvme_id_ctrl id;
572 int rc;
573
574 tx_info.msg_no = 1;
575 tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
576 tx_info.admin_quirk = false0;
577
578 peer->tx_fn = tx_mpr;
579 peer->tx_data = &tx_info;
580
581 hdl = libnvme_mi_init_transport_handle(ep, 1);
582
583 nvme_init_identify_ctrl(&cmd, &id);
584 rc = libnvme_exec_admin_passthru(hdl, &cmd);
585 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 585, "rc == 0"), 0)))
;
586
587 libnvme_close(hdl);
588}
589
590/* We have seen drives that send a MPR response as a full Admin message,
591 * rather than a MI message; these have a larger message body
592 */
593static void test_mpr_admin_quirked(libnvme_mi_ep_t ep, struct test_peer *peer)
594{
595 struct libnvme_transport_handle *hdl;
596 struct libnvme_passthru_cmd cmd;
597 struct mpr_tx_info tx_info;
598 struct nvme_id_ctrl id;
599 int rc;
600
601 tx_info.msg_no = 1;
602 tx_info.final_len = sizeof(struct nvme_mi_admin_resp_hdr) + sizeof(id);
603 tx_info.admin_quirk = true1;
604
605 peer->tx_fn = tx_mpr;
606 peer->tx_data = &tx_info;
607
608 hdl = libnvme_mi_init_transport_handle(ep, 1);
609
610 nvme_init_identify_ctrl(&cmd, &id);
611 rc = libnvme_exec_admin_passthru(hdl, &cmd);
612 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 612, "rc == 0"), 0)))
;
613
614 libnvme_close(hdl);
615}
616
617/* helpers for the MPR + poll tests */
618struct mpr_poll_info {
619 int poll_no;
620 uint16_t mprt;
621 unsigned int timeouts[2];
622};
623
624static int poll_fn_mpr_poll(struct test_peer *peer, struct pollfd *fds,
625 nfds_t nfds, int timeout)
626{
627 struct mpr_poll_info *info = peer->poll_data;
628
629 switch (info->poll_no) {
630 case 1:
631 case 2:
632 shr_assert(timeout == info->timeouts[info->poll_no - 1])((void)((timeout == info->timeouts[info->poll_no - 1]) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 632, "timeout == info->timeouts[info->poll_no - 1]"
), 0)))
;
633 break;
634 default:
635 shr_assert(0)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 635
, "0"), 0)))
;
636 }
637
638 info->poll_no++;
639 return 1;
640}
641
642static int tx_fn_mpr_poll(struct test_peer *peer, void *buf, size_t len, int sd)
643{
644 struct mpr_tx_info *tx_info = peer->tx_data;
645 struct mpr_poll_info *poll_info = peer->poll_data;
646 unsigned int mprt;
647
648 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 648, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
649
650 memset(peer->tx_buf, 0, sizeof(peer->tx_buf));
651 peer->tx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
652 peer->tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
653
654 switch (tx_info->msg_no) {
655 case 1:
656 peer->tx_buf[4] = NVME_MI_RESP_MPR;
657 peer->tx_buf_len = 8;
658 mprt = poll_info->mprt;
659 peer->tx_buf[7] = mprt >> 8;
660 peer->tx_buf[6] = mprt & 0xff;
661 break;
662 case 2:
663 peer->tx_buf[4] = NVME_MI_RESP_SUCCESS;
664 peer->tx_buf_len = tx_info->final_len;
665 break;
666 default:
667 shr_assert(0)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 667
, "0"), 0)))
;
668 }
669
670 test_set_tx_mic(peer);
671
672 tx_info->msg_no++;
673
674 return 0;
675}
676
677/* test: correct timeout value used from MPR response */
678static void test_mpr_timeouts(libnvme_mi_ep_t ep, struct test_peer *peer)
679{
680 struct nvme_mi_read_nvm_ss_info ss_info;
681 struct mpr_poll_info poll_info;
682 struct mpr_tx_info tx_info;
683 int rc;
684
685 libnvme_mi_ep_set_timeout(ep, 3141);
686
687 tx_info.msg_no = 1;
688 tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
689
690 poll_info.poll_no = 1;
691 poll_info.mprt = 1234;
692 poll_info.timeouts[0] = 3141;
693 poll_info.timeouts[1] = 1234 * 100;
694
695 peer->tx_fn = tx_fn_mpr_poll;
696 peer->tx_data = &tx_info;
697
698 peer->poll_fn = poll_fn_mpr_poll;
699 peer->poll_data = &poll_info;
700
701 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
702 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 702, "rc == 0"), 0)))
;
703}
704
705/* test: MPR value is limited to the max mpr */
706static void test_mpr_timeout_clamp(libnvme_mi_ep_t ep, struct test_peer *peer)
707{
708 struct nvme_mi_read_nvm_ss_info ss_info;
709 struct mpr_poll_info poll_info;
710 struct mpr_tx_info tx_info;
711 int rc;
712
713 libnvme_mi_ep_set_timeout(ep, 3141);
714 libnvme_mi_ep_set_mprt_max(ep, 123400);
715
716 tx_info.msg_no = 1;
717 tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
718
719 poll_info.poll_no = 1;
720 poll_info.mprt = 1235;
721 poll_info.timeouts[0] = 3141;
722 poll_info.timeouts[1] = 1234 * 100;
723
724 peer->tx_fn = tx_fn_mpr_poll;
725 peer->tx_data = &tx_info;
726
727 peer->poll_fn = poll_fn_mpr_poll;
728 peer->poll_data = &poll_info;
729
730 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
731 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 731, "rc == 0"), 0)))
;
732}
733
734/* test: MPR value of zero doesn't result in poll with zero timeout */
735static void test_mpr_mprt_zero(libnvme_mi_ep_t ep, struct test_peer *peer)
736{
737 struct nvme_mi_read_nvm_ss_info ss_info;
738 struct mpr_poll_info poll_info;
739 struct mpr_tx_info tx_info;
740 int rc;
741
742 libnvme_mi_ep_set_timeout(ep, 3141);
743 libnvme_mi_ep_set_mprt_max(ep, 123400);
744
745 tx_info.msg_no = 1;
746 tx_info.final_len = sizeof(struct nvme_mi_mi_resp_hdr) + sizeof(ss_info);
747
748 poll_info.poll_no = 1;
749 poll_info.mprt = 0;
750 poll_info.timeouts[0] = 3141;
751 poll_info.timeouts[1] = 3141;
752
753 peer->tx_fn = tx_fn_mpr_poll;
754 peer->tx_data = &tx_info;
755
756 peer->poll_fn = poll_fn_mpr_poll;
757 peer->poll_data = &poll_info;
758
759 rc = libnvme_mi_mi_read_mi_data_subsys(ep, &ss_info);
760 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 760, "rc == 0"), 0)))
;
761}
762
763enum aem_enable_state {
764 AEM_ES_GET_ENABLED,
765 AEM_ES_SET_TO_DISABLED,
766 AEM_ES_ENABLE_SET_ENABLED,
767 AEM_ES_PROCESS,
768 AEM_ES_ACK_RESPONSE,
769 AEM_ES_ACK_RECEIVED
770};
771
772enum aem_failure_condition {
773 AEM_FC_NONE,
774 AEM_FC_BAD_GET_CONFIG_HEADER_LEN,
775 AEM_FC_BAD_GET_CONFIG_TOTAL_LEN,
776 AEM_FC_BAD_GET_CONFIG_BUFFER_LEN,
777 AEM_FC_BAD_OCC_RSP_HDR_LEN_SYNC,
778 AEM_FC_BAD_OCC_RSP_TOTAL_LEN_SYNC,
779 AEM_FC_BAD_OCC_RSP_BUFFER_LEN_SYNC,
780 AEM_FC_BAD_OCC_RSP_HDR_LEN_AEM,
781 AEM_FC_BAD_OCC_RSP_TOTAL_LEN_AEM,
782 AEM_FC_BAD_OCC_RSP_BUFFER_LEN_AEM,
783};
784
785struct aem_rcv_enable_fn_data {
786 enum aem_enable_state state;
787 enum aem_failure_condition fc;
788 struct libnvme_mi_aem_enabled_map ep_enabled_map;
789 struct libnvme_mi_aem_enabled_map host_enabled_map;
790 struct libnvme_mi_aem_enabled_map aem_during_process_map;
791 struct libnvme_mi_aem_enabled_map ack_events_map;
792 struct libnvme_mi_event *events[256];
793 int callback_count;
794};
795
796static void populate_tx_occ_list(bool_Bool aem_not_ack,
797 struct aem_rcv_enable_fn_data *fn_data, struct libnvme_mi_aem_enabled_map *to_send)
798{
799 struct nvme_mi_mi_resp_hdr *resp_hdr =
800 (struct nvme_mi_mi_resp_hdr *)test_peer.tx_buf;
801
802 struct nvme_mi_msg_hdr *mi_msg_hdr =
803 (struct nvme_mi_msg_hdr *)test_peer.tx_buf;
804
805 size_t hdr_len = sizeof(*resp_hdr);
806
807 struct nvme_mi_aem_occ_list_hdr *list_hdr =
808 (struct nvme_mi_aem_occ_list_hdr *)(resp_hdr+1);
809
810 //For AEM, the data is actually in request format
811 //since it originates from the endpoint
812 if (aem_not_ack) {
813 list_hdr = (struct nvme_mi_aem_occ_list_hdr *)(mi_msg_hdr+1);
814 hdr_len = sizeof(*mi_msg_hdr);
815 mi_msg_hdr->nmp = (NVME_MI_MT_AE << 3);
816 } else {
817 resp_hdr->status = 0;
818 }
819
820 list_hdr->aelver = 0;
821 list_hdr->aeolhl = sizeof(*list_hdr);
822 list_hdr->numaeo = 0;
823 __u32 aeoltl = list_hdr->aeolhl;
824
825 struct nvme_mi_aem_occ_data *data =
826 (struct nvme_mi_aem_occ_data *)(list_hdr+1);
827
828 for (int i = 0; i < 255; i++) {
829 if (fn_data->events[i] && to_send->enabled[i]) {
830 struct libnvme_mi_event *event = fn_data->events[i];
831
832 list_hdr->numaeo++;
833 aeoltl += sizeof(struct nvme_mi_aem_occ_data);
834 aeoltl += event->spec_info_len +
835 event->vend_spec_info_len;
836
837 data->aelhlen = sizeof(*data);
838
839 if ((fn_data->fc == AEM_FC_BAD_OCC_RSP_HDR_LEN_SYNC && !aem_not_ack) ||
840 (fn_data->fc == AEM_FC_BAD_OCC_RSP_HDR_LEN_AEM && aem_not_ack))
841 data->aelhlen--;
842
843 data->aeoui.aeocidi = event->aeocidi;
844 data->aeoui.aeoi = event->aeoi;
845 data->aeoui.aessi = event->aessi;
846 data->aeosil = event->spec_info_len;
847 data->aeovsil = event->vend_spec_info_len;
848
849 if ((fn_data->fc == AEM_FC_BAD_OCC_RSP_TOTAL_LEN_SYNC &&
850 !aem_not_ack) ||
851 (fn_data->fc == AEM_FC_BAD_OCC_RSP_TOTAL_LEN_AEM &&
852 aem_not_ack))
853 aeoltl -= 1;
854
855 //Now the data
856 uint8_t *spec = (uint8_t *)(data+1);
857
858 if (data->aeosil) {
859 memcpy(spec, event->spec_info, event->spec_info_len);
860 spec += event->spec_info_len;
861 }
862
863 if (data->aeovsil) {
864 memcpy(spec, event->vend_spec_info, event->vend_spec_info_len);
865 spec += event->vend_spec_info_len;
866 }
867
868 data = (struct nvme_mi_aem_occ_data *)(spec);
869 }
870 }
871
872 nvme_mi_aem_aeolli_set_aeoltl(list_hdr, aeoltl);
873 test_peer.tx_buf_len = hdr_len + aeoltl;
874
875 if ((fn_data->fc == AEM_FC_BAD_OCC_RSP_BUFFER_LEN_SYNC && !aem_not_ack) ||
876 (fn_data->fc == AEM_FC_BAD_OCC_RSP_BUFFER_LEN_AEM && aem_not_ack))
877 test_peer.tx_buf_len--;
878
879 test_set_tx_mic(&test_peer);
880}
881
882static void check_aem_sync_message(struct libnvme_mi_aem_enabled_map *expected_mask,
883 struct libnvme_mi_aem_enabled_map *expected_state,
884 struct aem_rcv_enable_fn_data *fn_data)
885{
886 //Check the RX buffer for the endpoint. We should be getting a CONFIG SET AEM
887 //with all enabled items disabled
888 struct nvme_mi_mi_req_hdr *req =
889 (struct nvme_mi_mi_req_hdr *)test_peer.rx_buf;
890
891 struct nvme_mi_aem_supported_list *list =
892 (struct nvme_mi_aem_supported_list *)(req+1);
893
894 shr_assert(req->opcode == nvme_mi_mi_opcode_configuration_set)((void)((req->opcode == nvme_mi_mi_opcode_configuration_set
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 894, "req->opcode == nvme_mi_mi_opcode_configuration_set"
), 0)))
;
895 shr_assert((le32_to_cpu(req->cdw0) & 0xFF) == NVME_MI_CONFIG_AE)((void)(((le32_to_cpu(req->cdw0) & 0xFF) == NVME_MI_CONFIG_AE
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 895, "(le32_to_cpu(req->cdw0) & 0xFF) == NVME_MI_CONFIG_AE"
), 0)))
;
896 shr_assert(list->hdr.aeslver == 0)((void)((list->hdr.aeslver == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 896, "list->hdr.aeslver == 0"), 0)))
;
897
898 int count = 0;
899 //Count how many events we want to act are in the expected state
900 for (int i = 0; i < 256; i++) {
901 if (expected_mask->enabled[i])
902 count++;
903 }
904
905 shr_assert(list->hdr.numaes == count)((void)((list->hdr.numaes == count) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 905, "list->hdr.numaes == count"), 0)))
;
906 shr_assert(list->hdr.aeslhl == sizeof(struct nvme_mi_aem_supported_list))((void)((list->hdr.aeslhl == sizeof(struct nvme_mi_aem_supported_list
)) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 906, "list->hdr.aeslhl == sizeof(struct nvme_mi_aem_supported_list)"
), 0)))
;
907 shr_assert(list->hdr.aest == list->hdr.aeslhl +((void)((list->hdr.aest == list->hdr.aeslhl + count * sizeof
(struct nvme_mi_aem_supported_item)) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 908, "list->hdr.aest == list->hdr.aeslhl + count * sizeof(struct nvme_mi_aem_supported_item)"
), 0)))
908 count * sizeof(struct nvme_mi_aem_supported_item))((void)((list->hdr.aest == list->hdr.aeslhl + count * sizeof
(struct nvme_mi_aem_supported_item)) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 908, "list->hdr.aest == list->hdr.aeslhl + count * sizeof(struct nvme_mi_aem_supported_item)"
), 0)))
;
909
910 struct nvme_mi_aem_supported_item *item =
911 (struct nvme_mi_aem_supported_item *)(list+1);
912
913 //Check the items
914 for (int i = 0; i < 256; i++) {
915 if (expected_mask->enabled[i]) {
916 bool_Bool found = false0;
917
918 for (int j = 0; j < count; j++) {
919 if (nvme_mi_aem_aesi_get_aesid(item[j].aesi) == i &&
920 nvme_mi_aem_aesi_get_aese(item[j].aesi) ==
921 expected_state->enabled[i]) {
922 found = true1;
923 break;
924 }
925 }
926 shr_assert(found)((void)((found) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 926, "found"), 0)))
;
927 }
928 }
929}
930
931static int aem_rcv_enable_fn(struct test_peer *peer, void *buf, size_t len, int sd)
932{
933 struct aem_rcv_enable_fn_data *fn_data = peer->tx_data;
934 struct nvme_mi_mi_resp_hdr *tx_hdr = (struct nvme_mi_mi_resp_hdr *)peer->tx_buf;
935
936 /* set up a few default response fields; caller may have
937 * initialised the rest of the response
938 */
939 test_peer.tx_buf[0] = NVME_MI_MSGTYPE_NVME0x84;
940 test_peer.tx_buf[1] = test_peer.rx_buf[1] | (NVME_MI_ROR_RSP << 7);
941 tx_hdr->status = 0;
942
943 switch (fn_data->state) {
944 case AEM_ES_GET_ENABLED:
945 {
946 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 946, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
947
948 //First, we want to return some data about what is already enabled
949 struct nvme_mi_aem_supported_list_header *list_hdr =
950 (struct nvme_mi_aem_supported_list_header *)(tx_hdr+1);
951
952 if (fn_data->fc == AEM_FC_BAD_GET_CONFIG_HEADER_LEN)
953 list_hdr->aeslhl =
954 sizeof(struct nvme_mi_aem_supported_list_header) - 1;
955 else
956 list_hdr->aeslhl =
957 sizeof(struct nvme_mi_aem_supported_list_header);
958
959 list_hdr->aeslver = 0;
960 struct nvme_mi_aem_supported_item *item =
961 (struct nvme_mi_aem_supported_item *)(list_hdr+1);
962 int item_count = 0;
963
964 list_hdr->numaes = 0;
965 //Count how many events we want to act are enabled
966 for (int i = 0; i < 256; i++) {
967 if (fn_data->ep_enabled_map.enabled[i]) {
968 list_hdr->numaes++;
969 nvme_mi_aem_aesi_set_aesid(&item[item_count], i);
970 nvme_mi_aem_aesi_set_aee(&item[item_count], 1);
971 item[item_count].aesl =
972 sizeof(struct nvme_mi_aem_supported_item);
973 item_count++;
974 }
975 }
976
977 list_hdr->aest = list_hdr->aeslhl +
978 list_hdr->numaes * sizeof(struct nvme_mi_aem_supported_item);
979 if (fn_data->fc == AEM_FC_BAD_GET_CONFIG_TOTAL_LEN)
980 list_hdr->aest--;//Shrink
981
982 test_peer.tx_buf_len =
983 sizeof(struct nvme_mi_mi_resp_hdr) + list_hdr->aest;
984 if (fn_data->fc == AEM_FC_BAD_GET_CONFIG_BUFFER_LEN)
985 test_peer.tx_buf_len--;
986
987 test_set_tx_mic(&test_peer);
988
989 fn_data->state = AEM_ES_SET_TO_DISABLED;
990 break;
991 }
992 case AEM_ES_SET_TO_DISABLED:
993 {
994 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 994, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
995
996 struct libnvme_mi_aem_enabled_map expected = {false0};
997 //The items in the ep_enabled_map should get disabled
998 check_aem_sync_message(&fn_data->ep_enabled_map, &expected, fn_data);
999
1000 //Need to queue a reasonable response with no OCC
1001 struct nvme_mi_mi_resp_hdr *tx_hdr =
1002 (struct nvme_mi_mi_resp_hdr *)test_peer.tx_buf;
1003 struct nvme_mi_aem_occ_list_hdr *list_hdr =
1004 (struct nvme_mi_aem_occ_list_hdr *)(tx_hdr+1);
1005
1006 list_hdr->aelver = 0;
1007 list_hdr->aeolhl = sizeof(*list_hdr);
1008 list_hdr->numaeo = 0;
1009 nvme_mi_aem_aeolli_set_aeoltl(list_hdr, list_hdr->aeolhl);
1010
1011 test_peer.tx_buf_len = sizeof(struct nvme_mi_mi_resp_hdr) +
1012 nvme_mi_aem_aeolli_get_aeoltl(list_hdr->aeolli);
1013
1014 test_set_tx_mic(&test_peer);
1015
1016 fn_data->state = AEM_ES_ENABLE_SET_ENABLED;
1017 break;
1018 }
1019 case AEM_ES_ENABLE_SET_ENABLED:
1020 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1020, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
1021
1022 //We should verify the right things are enabled
1023 //The items in the host enable map should get enabled
1024 check_aem_sync_message(&fn_data->host_enabled_map,
1025 &fn_data->host_enabled_map, fn_data);
1026
1027 //Prepare an OCC list response
1028 populate_tx_occ_list(false0, fn_data, &fn_data->host_enabled_map);
1029
1030 fn_data->state = AEM_ES_PROCESS;
1031 break;
1032 case AEM_ES_PROCESS:
1033 //This case is actually a TX without any request from the host
1034 shr_assert(sd == peer->sd[TEST_PEER_SD_AEMS_IDX])((void)((sd == peer->sd[(1)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1034, "sd == peer->sd[TEST_PEER_SD_AEMS_IDX]"), 0)))
;
1035
1036 //Prepare an OCC list response
1037 populate_tx_occ_list(true1, fn_data, &fn_data->aem_during_process_map);
1038
1039 fn_data->state = AEM_ES_ACK_RESPONSE;
1040 break;
1041 case AEM_ES_ACK_RESPONSE:
1042 shr_assert(sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX])((void)((sd == peer->sd[(0)]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1042, "sd == peer->sd[TEST_PEER_SD_COMMANDS_IDX]"), 0)))
;
1043
1044 //Prepare an OCC list response
1045 populate_tx_occ_list(false0, fn_data, &fn_data->ack_events_map);
1046
1047 fn_data->state = AEM_ES_ACK_RECEIVED;
1048 break;
1049 default:
1050 shr_assert(false)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1050
, "false"), 0)))
;//Not expected
1051 }
1052
1053 return 0;
1054}
1055
1056enum libnvme_mi_aem_handler_next_action aem_handler(libnvme_mi_ep_t ep, size_t num_events, void *userdata)
1057{
1058 struct aem_rcv_enable_fn_data *fn_data = userdata;
1059
1060 fn_data->callback_count++;
1061
1062 switch (fn_data->state) {
1063 case AEM_ES_PROCESS:
1064 case AEM_ES_ACK_RESPONSE:
1065 case AEM_ES_ACK_RECEIVED:
1066 {
1067 //This means we just sent out first OCC data
1068 int item_count = 0;
1069 struct libnvme_mi_aem_enabled_map *map;
1070
1071 //Count how many events we want to act are enabled
1072 switch (fn_data->state) {
1073 case AEM_ES_PROCESS:
1074 map = &fn_data->host_enabled_map;
1075 break;
1076 case AEM_ES_ACK_RESPONSE:
1077 map = &fn_data->aem_during_process_map;
1078 break;
1079 case AEM_ES_ACK_RECEIVED:
1080 map = &fn_data->ack_events_map;
1081 break;
1082 default:
1083 shr_assert(false)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1083
, "false"), 0)))
;
1084 }
1085
1086 for (int i = 0; i < 256; i++)
1087 if (map->enabled[i])
1088 item_count++;
1089
1090 shr_assert(num_events == item_count)((void)((num_events == item_count) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1090, "num_events == item_count"), 0)))
;
1091
1092 for (int i = 0; i < num_events; i++) {
1093 struct libnvme_mi_event *e = libnvme_mi_aem_get_next_event(ep);
1094 uint8_t idx = e->aeoi;
1095
1096 shr_assert(fn_data->events[idx])((void)((fn_data->events[idx]) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1096, "fn_data->events[idx]"), 0)))
;
1097 shr_assert(fn_data->host_enabled_map.enabled[idx])((void)((fn_data->host_enabled_map.enabled[idx]) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1097, "fn_data->host_enabled_map.enabled[idx]"
), 0)))
;
1098 shr_assert(fn_data->events[idx]->aeocidi == e->aeocidi)((void)((fn_data->events[idx]->aeocidi == e->aeocidi
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1098, "fn_data->events[idx]->aeocidi == e->aeocidi"
), 0)))
;
1099 shr_assert(fn_data->events[idx]->aessi == e->aessi)((void)((fn_data->events[idx]->aessi == e->aessi) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 1099, "fn_data->events[idx]->aessi == e->aessi"
), 0)))
;
1100 shr_assert(fn_data->events[idx]->spec_info_len ==((void)((fn_data->events[idx]->spec_info_len == e->spec_info_len
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1101, "fn_data->events[idx]->spec_info_len == e->spec_info_len"
), 0)))
1101 e->spec_info_len)((void)((fn_data->events[idx]->spec_info_len == e->spec_info_len
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1101, "fn_data->events[idx]->spec_info_len == e->spec_info_len"
), 0)))
;
1102 shr_assert(memcmp(fn_data->events[idx]->spec_info,((void)((memcmp(fn_data->events[idx]->spec_info, e->
spec_info, e->spec_info_len) == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1103, "memcmp(fn_data->events[idx]->spec_info, e->spec_info, e->spec_info_len) == 0"
), 0)))
1103 e->spec_info, e->spec_info_len) == 0)((void)((memcmp(fn_data->events[idx]->spec_info, e->
spec_info, e->spec_info_len) == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1103, "memcmp(fn_data->events[idx]->spec_info, e->spec_info, e->spec_info_len) == 0"
), 0)))
;
1104 shr_assert(fn_data->events[idx]->vend_spec_info_len ==((void)((fn_data->events[idx]->vend_spec_info_len == e->
vend_spec_info_len) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1105, "fn_data->events[idx]->vend_spec_info_len == e->vend_spec_info_len"
), 0)))
1105 e->vend_spec_info_len)((void)((fn_data->events[idx]->vend_spec_info_len == e->
vend_spec_info_len) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1105, "fn_data->events[idx]->vend_spec_info_len == e->vend_spec_info_len"
), 0)))
;
1106 shr_assert(memcmp(fn_data->events[idx]->vend_spec_info,((void)((memcmp(fn_data->events[idx]->vend_spec_info, e
->vend_spec_info, e->vend_spec_info_len) == 0) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1107, "memcmp(fn_data->events[idx]->vend_spec_info, e->vend_spec_info, e->vend_spec_info_len) == 0"
), 0)))
1107 e->vend_spec_info, e->vend_spec_info_len) == 0)((void)((memcmp(fn_data->events[idx]->vend_spec_info, e
->vend_spec_info, e->vend_spec_info_len) == 0) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1107, "memcmp(fn_data->events[idx]->vend_spec_info, e->vend_spec_info, e->vend_spec_info_len) == 0"
), 0)))
;
1108 }
1109
1110 shr_assert(libnvme_mi_aem_get_next_event(ep) == NULL)((void)((libnvme_mi_aem_get_next_event(ep) == ((void*)0)) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 1110, "libnvme_mi_aem_get_next_event(ep) == NULL"
), 0)))
;
1111 break;
1112 }
1113 default:
1114 shr_assert(false)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1114
, "false"), 0)))
;
1115 }
1116
1117 return NVME_MI_AEM_HNA_ACK;
1118}
1119
1120static void aem_test_aem_api_helper(libnvme_mi_ep_t ep,
1121 struct libnvme_mi_aem_config *config, int expected_event_count)
1122{
1123 struct aem_rcv_enable_fn_data *fn_data =
1124 (struct aem_rcv_enable_fn_data *)test_peer.tx_data;
1125 int rc = 0;
1126
1127 test_peer.tx_fn = aem_rcv_enable_fn;
1128
1129 //This should not work outside the handler
1130 shr_assert(libnvme_mi_aem_get_next_event(ep) == NULL)((void)((libnvme_mi_aem_get_next_event(ep) == ((void*)0)) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 1130, "libnvme_mi_aem_get_next_event(ep) == NULL"
), 0)))
;
1131
1132 rc = libnvme_mi_aem_enable(ep, config, test_peer.tx_data);
1133 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1133, "rc == 0"), 0)))
;
1134
1135 //This should not work outside the handler
1136 shr_assert(libnvme_mi_aem_get_next_event(ep) == NULL)((void)((libnvme_mi_aem_get_next_event(ep) == ((void*)0)) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 1136, "libnvme_mi_aem_get_next_event(ep) == NULL"
), 0)))
;
1137
1138 rc = libnvme_mi_aem_process(ep, test_peer.tx_data);
1139 shr_assert(rc == 0)((void)((rc == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1139, "rc == 0"), 0)))
;
1140
1141 //One for initial enable, one for AEM. No ACK events
1142 shr_assert(fn_data->callback_count == expected_event_count)((void)((fn_data->callback_count == expected_event_count) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 1142, "fn_data->callback_count == expected_event_count"
), 0)))
;
1143
1144 //This should not work outside the handler
1145 shr_assert(libnvme_mi_aem_get_next_event(ep) == NULL)((void)((libnvme_mi_aem_get_next_event(ep) == ((void*)0)) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 1145, "libnvme_mi_aem_get_next_event(ep) == NULL"
), 0)))
;
1146}
1147
1148static void aem_test_aem_disable_helper(libnvme_mi_ep_t ep,
1149 struct aem_rcv_enable_fn_data *fn_data)
1150{
1151 memcpy(&fn_data->ep_enabled_map, &fn_data->host_enabled_map,
1152 sizeof(fn_data->host_enabled_map));
1153
1154 fn_data->state = AEM_ES_GET_ENABLED;//This is the flow for disabling
1155 shr_assert(libnvme_mi_aem_disable(ep) == 0)((void)((libnvme_mi_aem_disable(ep) == 0) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1155, "libnvme_mi_aem_disable(ep) == 0"
), 0)))
;
1156}
1157
1158static void test_mi_aem_ep_based_failure_helper(libnvme_mi_ep_t ep,
1159 enum aem_failure_condition fc, struct test_peer *peer)
1160{
1161 struct aem_rcv_enable_fn_data fn_data = {0};
1162 struct libnvme_mi_aem_config config = {0};
1163
1164 config.aemd = 1;
1165 config.aerd = 2;
1166 config.enabled_map.enabled[3] = true1;
1167 fn_data.aem_during_process_map.enabled[3] = true1;
1168 struct libnvme_mi_event e = {0};
1169
1170 e.aeoi = 3;
1171 e.spec_info_len = 0;
1172 fn_data.events[3] = &e;
1173
1174 memcpy(&fn_data.host_enabled_map, &config.enabled_map, sizeof(config.enabled_map));
1175
1176 config.aem_handler = aem_handler;
1177 peer->tx_data = (void *) &fn_data;
1178 peer->tx_fn = aem_rcv_enable_fn;
1179
1180 fn_data.fc = fc;
1181 switch (fc) {
2
Control jumps to 'case AEM_FC_BAD_GET_CONFIG_HEADER_LEN:' at line 1182
1182 case AEM_FC_BAD_GET_CONFIG_HEADER_LEN:
1183 case AEM_FC_BAD_GET_CONFIG_TOTAL_LEN:
1184 case AEM_FC_BAD_GET_CONFIG_BUFFER_LEN:
1185 case AEM_FC_BAD_OCC_RSP_HDR_LEN_SYNC:
1186 case AEM_FC_BAD_OCC_RSP_TOTAL_LEN_SYNC:
1187 case AEM_FC_BAD_OCC_RSP_BUFFER_LEN_SYNC:
1188 //These all should fail before processing
1189 shr_assert(libnvme_mi_aem_enable(ep, &config, &fn_data) == -EPROTO)((void)((libnvme_mi_aem_enable(ep, &config, &fn_data)
== -71) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1189
, "libnvme_mi_aem_enable(ep, &config, &fn_data) == -EPROTO"
), 0)))
;
3
Assuming the condition is true
1190 break;
4
Execution jumps to the end of the function
5
Address of stack memory associated with local variable 'fn_data' is still referred to by the caller variable 'peer' upon returning to the caller. This will be a dangling reference
1191 case AEM_FC_BAD_OCC_RSP_HDR_LEN_AEM:
1192 case AEM_FC_BAD_OCC_RSP_TOTAL_LEN_AEM:
1193 case AEM_FC_BAD_OCC_RSP_BUFFER_LEN_AEM:
1194 //These should fail on the processing
1195 shr_assert(libnvme_mi_aem_enable(ep, &config, &fn_data) == 0)((void)((libnvme_mi_aem_enable(ep, &config, &fn_data)
== 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1195,
"libnvme_mi_aem_enable(ep, &config, &fn_data) == 0")
, 0)))
;
1196 shr_assert(libnvme_mi_aem_process(ep, &fn_data) == -EPROTO)((void)((libnvme_mi_aem_process(ep, &fn_data) == -71) || (
shr_assert_fail("../libnvme/test/mi-mctp.c", 1196, "libnvme_mi_aem_process(ep, &fn_data) == -EPROTO"
), 0)))
;
1197 break;
1198 default:
1199 shr_assert(false)((void)((0) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1199
, "false"), 0)))
;//Unexpected
1200 }
1201}
1202
1203/* test: Check validation of endpoint messages in various stages of aem handling */
1204static void test_mi_aem_ep_based_failure_conditions(libnvme_mi_ep_t ep, struct test_peer *peer)
1205{
1206 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_GET_CONFIG_HEADER_LEN, peer);
1
Calling 'test_mi_aem_ep_based_failure_helper'
1207 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_GET_CONFIG_TOTAL_LEN, peer);
1208 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_GET_CONFIG_BUFFER_LEN, peer);
1209 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_HDR_LEN_SYNC, peer);
1210 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_HDR_LEN_AEM, peer);
1211 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_TOTAL_LEN_SYNC, peer);
1212 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_TOTAL_LEN_AEM, peer);
1213 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_BUFFER_LEN_SYNC, peer);
1214 test_mi_aem_ep_based_failure_helper(ep, AEM_FC_BAD_OCC_RSP_BUFFER_LEN_AEM, peer);
1215}
1216
1217/* test: Check aem process logic when API used improperly */
1218static void test_mi_aem_enable_invalid_usage(libnvme_mi_ep_t ep, struct test_peer *peer)
1219{
1220 struct libnvme_mi_aem_config config = {0};
1221
1222 config.aem_handler = aem_handler;
1223 config.enabled_map.enabled[0] = false0;
1224 config.aemd = 1;
1225 config.aerd = 2;
1226
1227 //Call with invalid config due to nothing enabled
1228 shr_assert(libnvme_mi_aem_enable(ep, &config, NULL) == -1)((void)((libnvme_mi_aem_enable(ep, &config, ((void*)0)) ==
-1) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1228, "libnvme_mi_aem_enable(ep, &config, NULL) == -1"
), 0)))
;
1229
1230 config.aem_handler = NULL((void*)0);
1231 config.enabled_map.enabled[0] = true1;
1232
1233 //Call with invalid config due to no callback
1234 shr_assert(libnvme_mi_aem_enable(ep, &config, NULL) == -1)((void)((libnvme_mi_aem_enable(ep, &config, ((void*)0)) ==
-1) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1234, "libnvme_mi_aem_enable(ep, &config, NULL) == -1"
), 0)))
;
1235
1236 //Call with invalid config due to being NULL
1237 shr_assert(libnvme_mi_aem_enable(ep, NULL, NULL) == -1)((void)((libnvme_mi_aem_enable(ep, ((void*)0), ((void*)0)) ==
-1) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1237, "libnvme_mi_aem_enable(ep, NULL, NULL) == -1"
), 0)))
;
1238
1239 config.aem_handler = aem_handler;
1240 config.enabled_map.enabled[0] = true1;
1241
1242 //Call with invalid endpoint
1243 shr_assert(libnvme_mi_aem_enable(NULL, &config, NULL) == -1)((void)((libnvme_mi_aem_enable(((void*)0), &config, ((void
*)0)) == -1) || (shr_assert_fail("../libnvme/test/mi-mctp.c",
1243, "libnvme_mi_aem_enable(NULL, &config, NULL) == -1"
), 0)))
;
1244}
1245
1246/* test: Check aem process logic when API used improperly */
1247static void test_mi_aem_process_invalid_usage(libnvme_mi_ep_t ep, struct test_peer *peer)
1248{
1249 //Without calling enable first
1250 shr_assert(libnvme_mi_aem_process(ep, NULL) == -1)((void)((libnvme_mi_aem_process(ep, ((void*)0)) == -1) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1250, "libnvme_mi_aem_process(ep, NULL) == -1"
), 0)))
;
1251
1252 //Call with invalid ep
1253 shr_assert(libnvme_mi_aem_process(NULL, NULL) == -1)((void)((libnvme_mi_aem_process(((void*)0), ((void*)0)) == -1
) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1253, "libnvme_mi_aem_process(NULL, NULL) == -1"
), 0)))
;
1254}
1255
1256/* test: Check aem disable logic when API used improperly */
1257static void test_mi_aem_disable_invalid_usage(libnvme_mi_ep_t ep, struct test_peer *peer)
1258{
1259 shr_assert(libnvme_mi_aem_disable(NULL) == -1)((void)((libnvme_mi_aem_disable(((void*)0)) == -1) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1259, "libnvme_mi_aem_disable(NULL) == -1"
), 0)))
;
1260}
1261
1262static void test_mi_aem_get_enabled_invalid_usage(libnvme_mi_ep_t ep, struct test_peer *peer)
1263{
1264 struct libnvme_mi_aem_enabled_map map;
1265
1266 shr_assert(libnvme_mi_aem_get_enabled(ep, NULL) == -1)((void)((libnvme_mi_aem_get_enabled(ep, ((void*)0)) == -1) ||
(shr_assert_fail("../libnvme/test/mi-mctp.c", 1266, "libnvme_mi_aem_get_enabled(ep, NULL) == -1"
), 0)))
;
1267 shr_assert(libnvme_mi_aem_get_enabled(NULL, &map) == -1)((void)((libnvme_mi_aem_get_enabled(((void*)0), &map) == -
1) || (shr_assert_fail("../libnvme/test/mi-mctp.c", 1267, "libnvme_mi_aem_get_enabled(NULL, &map) == -1"
), 0)))
;
1268}
1269
1270/* test: Check aem get enabled logic*/
1271static void test_mi_aem_get_enabled(libnvme_mi_ep_t ep, struct test_peer *peer)
1272{
1273 //When no events enabled on Endpoint
1274 struct aem_rcv_enable_fn_data fn_data = {0};
1275 struct libnvme_mi_aem_enabled_map map;
1276
1277 test_peer.tx_fn = aem_rcv_enable_fn;
1278 peer->tx_data = (void *) &fn_data;
1279 fn_data.ep_enabled_map.enabled[8] = true1;
1280 fn_data.ep_enabled_map.enabled[20] = true1;
1281 fn_data.ep_enabled_map.enabled[51] = true1;
1282 fn_data.ep_enabled_map.enabled[255] = true1;
1283
1284 shr_assert(libnvme_mi_aem_get_enabled(ep, &map) == 0)((void)((libnvme_mi_aem_get_enabled(ep, &map) == 0) || (shr_assert_fail
("../libnvme/test/mi-mctp.c", 1284, "libnvme_mi_aem_get_enabled(ep, &map) == 0"
), 0)))
;
1285 shr_assert(memcmp(&fn_data.ep_enabled_map, &map, sizeof(map)) == 0)((void)((memcmp(&fn_data.ep_enabled_map, &map, sizeof
(map)) == 0) || (shr_assert_fail("../libnvme/test/mi-mctp.c",
1285, "memcmp(&fn_data.ep_enabled_map, &map, sizeof(map)) == 0"
), 0)))
;
1286}
1287
1288
1289/* test: Check aem disable logic when called without an enable */
1290static void test_mi_aem_disable_no_enable(libnvme_mi_ep_t ep, struct test_peer *peer)
1291{
1292 //When no events enabled on Endpoint
1293 struct aem_rcv_enable_fn_data fn_data = {0};
1294
1295 test_peer.tx_fn = aem_rcv_enable_fn;
1296 peer->tx_data = (void *) &fn_data;
1297
1298 aem_test_aem_disable_helper(ep, &fn_data);
1299
1300 //When some events enabled on Endpoint
1301 fn_data.ep_enabled_map.enabled[45] = true1;
1302
1303 aem_test_aem_disable_helper(ep, &fn_data);
1304}
1305
1306/* test: Check aem enable logic with ack carrying events */
1307static void test_mi_aem_api_w_ack_events(libnvme_mi_ep_t ep, struct test_peer *peer)
1308{
1309 struct aem_rcv_enable_fn_data fn_data = {0};
1310 struct libnvme_mi_aem_config config = {0};
1311
1312 config.aemd = 1;
1313 config.aerd = 2;
1314 peer->tx_data = (void *) &fn_data;
1315 config.aem_handler = aem_handler;
1316
1317 config.enabled_map.enabled[5] = true1;
1318 config.enabled_map.enabled[15] = true1;
1319
1320 fn_data.aem_during_process_map.enabled[5] = true1;
1321
1322 //No ack_events_map will be enabled in this test
1323 fn_data.ack_events_map.enabled[15] = true1;
1324
1325 //Will have EP have nothing enabled at start (ep_enabled_map)
1326
1327 struct libnvme_mi_event ev5 = {0};
1328
1329 ev5.aeoi = 5;
1330 ev5.aeocidi = 2;
1331 ev5.aessi = 3;
1332
1333 struct libnvme_mi_event ev15 = {0};
1334 uint8_t ev15_spec[] = { 45, 15};
1335
1336 ev15.aeoi = 15;
1337 ev15.aeocidi = 60213;
1338 ev15.aessi = 200;
1339 ev15.spec_info = ev15_spec;
1340 ev15.spec_info_len = sizeof(ev15_spec);
1341
1342 fn_data.events[5] = &ev5;
1343 fn_data.events[15] = &ev15;
1344
1345 memcpy(&fn_data.host_enabled_map, &config.enabled_map, sizeof(config.enabled_map));
1346
1347 aem_test_aem_api_helper(ep, &config, 3);
1348
1349 aem_test_aem_disable_helper(ep, &fn_data);
1350}
1351
1352/* test: Check aem enable logic */
1353static void test_mi_aem_api_simple(libnvme_mi_ep_t ep, struct test_peer *peer)
1354{
1355 struct aem_rcv_enable_fn_data fn_data = {0};
1356 struct libnvme_mi_aem_config config = {0};
1357
1358 config.aemd = 1;
1359 config.aerd = 2;
1360 peer->tx_data = (void *) &fn_data;
1361 config.aem_handler = aem_handler;
1362
1363 config.enabled_map.enabled[1] = true1;
1364 config.enabled_map.enabled[3] = true1;
1365 config.enabled_map.enabled[16] = true1;
1366
1367 fn_data.aem_during_process_map.enabled[3] = true1;
1368
1369 //No ack_events_map will be enabled in this test
1370
1371 fn_data.ep_enabled_map.enabled[3] = true1;
1372 fn_data.ep_enabled_map.enabled[20] = true1;
1373 fn_data.ep_enabled_map.enabled[200] = true1;
1374
1375 struct libnvme_mi_event ev1 = {0};
1376 uint8_t ev1_spec[] = { 98, 56, 32, 12};
1377
1378 ev1.aeoi = 1;
1379 ev1.aeocidi = 2;
1380 ev1.aessi = 3;
1381 ev1.spec_info = ev1_spec;
1382 ev1.spec_info_len = sizeof(ev1_spec);
1383
1384 struct libnvme_mi_event ev3 = {0};
1385 uint8_t ev3_spec[] = { 45, 15};
1386
1387 ev3.aeoi = 3;
1388 ev3.aeocidi = 4;
1389 ev3.aessi = 5;
1390 ev3.spec_info = ev3_spec;
1391 ev3.spec_info_len = sizeof(ev3_spec);
1392
1393 struct libnvme_mi_event ev16 = {0};
1394
1395 ev16.aeoi = 16;
1396 ev16.aeocidi = 6;
1397 ev16.aessi = 7;
1398
1399 fn_data.events[1] = &ev1;
1400 fn_data.events[3] = &ev3;
1401 fn_data.events[16] = &ev16;
1402
1403 memcpy(&fn_data.host_enabled_map, &config.enabled_map, sizeof(config.enabled_map));
1404
1405 aem_test_aem_api_helper(ep, &config, 2);
1406
1407 aem_test_aem_disable_helper(ep, &fn_data);
1408}
1409
1410#define DEFINE_TEST(name){ "name", test_name } { #name, test_ ## name }
1411struct test {
1412 const char *name;
1413 void (*fn)(libnvme_mi_ep_t, struct test_peer *);
1414} tests[] = {
1415 DEFINE_TEST(rx_err){ "rx_err", test_rx_err },
1416 DEFINE_TEST(tx_none){ "tx_none", test_tx_none },
1417 DEFINE_TEST(tx_err){ "tx_err", test_tx_err },
1418 DEFINE_TEST(tx_short){ "tx_short", test_tx_short },
1419 DEFINE_TEST(read_mi_data){ "read_mi_data", test_read_mi_data },
1420 DEFINE_TEST(poll_err){ "poll_err", test_poll_err },
1421 DEFINE_TEST(mi_resp_err){ "mi_resp_err", test_mi_resp_err },
1422 DEFINE_TEST(mi_resp_unaligned){ "mi_resp_unaligned", test_mi_resp_unaligned },
1423 DEFINE_TEST(mi_resp_unaligned_expected){ "mi_resp_unaligned_expected", test_mi_resp_unaligned_expected
}
,
1424 DEFINE_TEST(admin_resp_err){ "admin_resp_err", test_admin_resp_err },
1425 DEFINE_TEST(admin_resp_sizes){ "admin_resp_sizes", test_admin_resp_sizes },
1426 DEFINE_TEST(poll_timeout_value){ "poll_timeout_value", test_poll_timeout_value },
1427 DEFINE_TEST(poll_timeout){ "poll_timeout", test_poll_timeout },
1428 DEFINE_TEST(mpr_mi){ "mpr_mi", test_mpr_mi },
1429 DEFINE_TEST(mpr_admin){ "mpr_admin", test_mpr_admin },
1430 DEFINE_TEST(mpr_admin_quirked){ "mpr_admin_quirked", test_mpr_admin_quirked },
1431 DEFINE_TEST(mpr_timeouts){ "mpr_timeouts", test_mpr_timeouts },
1432 DEFINE_TEST(mpr_timeout_clamp){ "mpr_timeout_clamp", test_mpr_timeout_clamp },
1433 DEFINE_TEST(mpr_mprt_zero){ "mpr_mprt_zero", test_mpr_mprt_zero },
1434 DEFINE_TEST(mi_aem_api_simple){ "mi_aem_api_simple", test_mi_aem_api_simple },
1435 DEFINE_TEST(mi_aem_api_w_ack_events){ "mi_aem_api_w_ack_events", test_mi_aem_api_w_ack_events },
1436 DEFINE_TEST(mi_aem_disable_no_enable){ "mi_aem_disable_no_enable", test_mi_aem_disable_no_enable },
1437 DEFINE_TEST(mi_aem_process_invalid_usage){ "mi_aem_process_invalid_usage", test_mi_aem_process_invalid_usage
}
,
1438 DEFINE_TEST(mi_aem_enable_invalid_usage){ "mi_aem_enable_invalid_usage", test_mi_aem_enable_invalid_usage
}
,
1439 DEFINE_TEST(mi_aem_disable_invalid_usage){ "mi_aem_disable_invalid_usage", test_mi_aem_disable_invalid_usage
}
,
1440 DEFINE_TEST(mi_aem_get_enabled){ "mi_aem_get_enabled", test_mi_aem_get_enabled },
1441 DEFINE_TEST(mi_aem_get_enabled_invalid_usage){ "mi_aem_get_enabled_invalid_usage", test_mi_aem_get_enabled_invalid_usage
}
,
1442 DEFINE_TEST(mi_aem_ep_based_failure_conditions){ "mi_aem_ep_based_failure_conditions", test_mi_aem_ep_based_failure_conditions
}
,
1443};
1444
1445static void run_test(struct test *test, FILE *logfd, libnvme_mi_ep_t ep,
1446 struct test_peer *peer)
1447{
1448 printf("Running test %s...", test->name);
1449 fflush(stdoutstdout);
1450 test->fn(ep, peer);
1451 printf(" OK\n");
1452 test_print_log_buf(logfd);
1453}
1454
1455int main(void)
1456{
1457 struct libnvme_global_ctx *ctx;
1458 libnvme_mi_ep_t ep;
1459 unsigned int i;
1460 FILE *fd;
1461
1462 fd = test_setup_log();
1463
1464 __libnvme_mi_mctp_set_ops(&ops);
1465
1466 ctx = libnvme_create_global_ctx();
1467 shr_assert(ctx)((void)((ctx) || (shr_assert_fail("../libnvme/test/mi-mctp.c"
, 1467, "ctx"), 0)))
;
1468 libnvme_set_logging_file(ctx, fd);
1469
1470 ep = libnvme_mi_open_mctp(ctx, 0, 0);
1471 shr_assert(ep)((void)((ep) || (shr_assert_fail("../libnvme/test/mi-mctp.c",
1471, "ep"), 0)))
;
1472
1473 for (i = 0; i < ARRAY_SIZE(tests)(sizeof(tests) / sizeof((tests)[0]) + 0); i++) {
1474 reset_test_peer();
1475 run_test(&tests[i], fd, ep, &test_peer);
1476 }
1477
1478 libnvme_mi_close(ep);
1479 libnvme_free_global_ctx(ctx);
1480
1481 test_close_log(fd);
1482
1483 return EXIT_SUCCESS0;
1484}