1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 /* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 3 #include "array.h" 4 #include "container-of.h" 5 #include "transport.h" 6 #include "test.h" 7 8 #include <errno.h> 9 #include <poll.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 14 struct pldm_transport_test { 15 struct pldm_transport transport; 16 const struct pldm_transport_test_descriptor *seq; 17 size_t count; 18 size_t cursor; 19 int timerfd; 20 }; 21 22 #define transport_to_test(ptr) \ 23 container_of(ptr, struct pldm_transport_test, transport) 24 25 LIBPLDM_ABI_TESTING 26 struct pldm_transport *pldm_transport_test_core(struct pldm_transport_test *ctx) 27 { 28 return &ctx->transport; 29 } 30 31 #ifdef PLDM_HAS_POLL 32 #include <poll.h> 33 LIBPLDM_ABI_TESTING 34 int pldm_transport_test_init_pollfd(struct pldm_transport *ctx, 35 struct pollfd *pollfd) 36 { 37 static const struct itimerspec disable = { 38 .it_value = { 0, 0 }, 39 .it_interval = { 0, 0 }, 40 }; 41 struct pldm_transport_test *test = transport_to_test(ctx); 42 const struct pldm_transport_test_descriptor *desc; 43 int rc; 44 45 rc = timerfd_settime(test->timerfd, 0, &disable, NULL); 46 if (rc < 0) { 47 return PLDM_REQUESTER_POLL_FAIL; 48 } 49 50 if (test->cursor >= test->count) { 51 return PLDM_REQUESTER_POLL_FAIL; 52 } 53 54 desc = &test->seq[test->cursor]; 55 56 if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_LATENCY) { 57 rc = timerfd_settime(test->timerfd, 0, &desc->latency, NULL); 58 if (rc < 0) { 59 return PLDM_REQUESTER_POLL_FAIL; 60 } 61 62 /* This was an explicit latency element, so now move beyond it for recv */ 63 test->cursor++; 64 } else if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { 65 /* Expire the timer immediately so it appears ready */ 66 static const struct timespec ensure_ready = { 67 .tv_sec = 0, 68 .tv_nsec = 2, 69 }; 70 static const struct itimerspec ready = { 71 .it_value = { 0, 1 }, 72 .it_interval = { 0, 0 }, 73 }; 74 struct pollfd pfds[] = { 75 { .fd = test->timerfd, .events = POLLIN }, 76 }; 77 78 rc = timerfd_settime(test->timerfd, 0, &ready, NULL); 79 if (rc < 0) { 80 return PLDM_REQUESTER_POLL_FAIL; 81 } 82 83 rc = ppoll(pfds, ARRAY_SIZE(pfds), &ensure_ready, NULL); 84 if (rc < 1) { 85 return PLDM_REQUESTER_POLL_FAIL; 86 } 87 88 /* Don't increment test->cursor as recv needs to consume the current test element */ 89 } else { 90 return PLDM_REQUESTER_POLL_FAIL; 91 } 92 93 pollfd->fd = test->timerfd; 94 pollfd->events = POLLIN; 95 96 return 0; 97 } 98 #endif 99 100 static pldm_requester_rc_t pldm_transport_test_recv(struct pldm_transport *ctx, 101 pldm_tid_t *tid, 102 void **pldm_resp_msg, 103 size_t *resp_msg_len) 104 { 105 struct pldm_transport_test *test = transport_to_test(ctx); 106 const struct pldm_transport_test_descriptor *desc; 107 void *msg; 108 109 if (test->cursor >= test->count) { 110 return PLDM_REQUESTER_RECV_FAIL; 111 } 112 113 desc = &test->seq[test->cursor]; 114 115 if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { 116 return PLDM_REQUESTER_RECV_FAIL; 117 } 118 119 msg = malloc(desc->recv_msg.len); 120 if (!msg) { 121 return PLDM_REQUESTER_RECV_FAIL; 122 } 123 124 memcpy(msg, desc->recv_msg.msg, desc->recv_msg.len); 125 *pldm_resp_msg = msg; 126 *resp_msg_len = desc->recv_msg.len; 127 *tid = desc->recv_msg.src; 128 129 test->cursor++; 130 131 return PLDM_REQUESTER_SUCCESS; 132 } 133 134 static pldm_requester_rc_t pldm_transport_test_send(struct pldm_transport *ctx, 135 pldm_tid_t tid, 136 const void *pldm_req_msg, 137 size_t req_msg_len) 138 { 139 struct pldm_transport_test *test = transport_to_test(ctx); 140 const struct pldm_transport_test_descriptor *desc; 141 142 if (test->cursor > test->count) { 143 return PLDM_REQUESTER_SEND_FAIL; 144 } 145 146 desc = &test->seq[test->cursor]; 147 148 if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_SEND) { 149 return PLDM_REQUESTER_SEND_FAIL; 150 } 151 152 if (desc->send_msg.dst != tid) { 153 return PLDM_REQUESTER_SEND_FAIL; 154 } 155 156 if (desc->send_msg.len != req_msg_len) { 157 return PLDM_REQUESTER_SEND_FAIL; 158 } 159 160 if (memcmp(desc->send_msg.msg, pldm_req_msg, req_msg_len) != 0) { 161 return PLDM_REQUESTER_SEND_FAIL; 162 } 163 164 test->cursor++; 165 166 return PLDM_REQUESTER_SUCCESS; 167 } 168 169 LIBPLDM_ABI_TESTING 170 int pldm_transport_test_init(struct pldm_transport_test **ctx, 171 const struct pldm_transport_test_descriptor *seq, 172 size_t count) 173 { 174 int rc; 175 176 if (!ctx || *ctx) { 177 return -EINVAL; 178 } 179 180 struct pldm_transport_test *test = malloc(sizeof(*test)); 181 if (!test) { 182 return -ENOMEM; 183 } 184 185 test->transport.name = "TEST"; 186 test->transport.version = 1; 187 test->transport.recv = pldm_transport_test_recv; 188 test->transport.send = pldm_transport_test_send; 189 test->transport.init_pollfd = pldm_transport_test_init_pollfd; 190 test->seq = seq; 191 test->count = count; 192 test->cursor = 0; 193 test->timerfd = timerfd_create(CLOCK_MONOTONIC, 0); 194 if (test->timerfd < 0) { 195 rc = -errno; 196 goto cleanup_test; 197 } 198 199 *ctx = test; 200 201 return 0; 202 203 cleanup_test: 204 free(test); 205 return rc; 206 } 207 208 LIBPLDM_ABI_TESTING 209 void pldm_transport_test_destroy(struct pldm_transport_test *ctx) 210 { 211 close(ctx->timerfd); 212 free(ctx); 213 } 214