1 /* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 2 #define _GNU_SOURCE 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 (void)tid; 110 111 if (test->cursor >= test->count) { 112 return PLDM_REQUESTER_RECV_FAIL; 113 } 114 115 desc = &test->seq[test->cursor]; 116 117 if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { 118 return PLDM_REQUESTER_RECV_FAIL; 119 } 120 121 msg = malloc(desc->recv_msg.len); 122 if (!msg) { 123 return PLDM_REQUESTER_RECV_FAIL; 124 } 125 126 memcpy(msg, desc->recv_msg.msg, desc->recv_msg.len); 127 *pldm_resp_msg = msg; 128 *resp_msg_len = desc->recv_msg.len; 129 130 test->cursor++; 131 132 return PLDM_REQUESTER_SUCCESS; 133 } 134 135 static pldm_requester_rc_t pldm_transport_test_send(struct pldm_transport *ctx, 136 pldm_tid_t tid, 137 const void *pldm_req_msg, 138 size_t req_msg_len) 139 { 140 struct pldm_transport_test *test = transport_to_test(ctx); 141 const struct pldm_transport_test_descriptor *desc; 142 143 if (test->cursor > test->count) { 144 return PLDM_REQUESTER_SEND_FAIL; 145 } 146 147 desc = &test->seq[test->cursor]; 148 149 if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_SEND) { 150 return PLDM_REQUESTER_SEND_FAIL; 151 } 152 153 if (desc->send_msg.dst != tid) { 154 return PLDM_REQUESTER_SEND_FAIL; 155 } 156 157 if (desc->send_msg.len != req_msg_len) { 158 return PLDM_REQUESTER_SEND_FAIL; 159 } 160 161 if (memcmp(desc->send_msg.msg, pldm_req_msg, req_msg_len) != 0) { 162 return PLDM_REQUESTER_SEND_FAIL; 163 } 164 165 test->cursor++; 166 167 return PLDM_REQUESTER_SUCCESS; 168 } 169 170 LIBPLDM_ABI_TESTING 171 int pldm_transport_test_init(struct pldm_transport_test **ctx, 172 const struct pldm_transport_test_descriptor *seq, 173 size_t count) 174 { 175 int rc; 176 177 if (!ctx || *ctx) { 178 return -EINVAL; 179 } 180 181 struct pldm_transport_test *test = malloc(sizeof(*test)); 182 if (!test) { 183 return -ENOMEM; 184 } 185 186 test->transport.name = "TEST"; 187 test->transport.version = 1; 188 test->transport.recv = pldm_transport_test_recv; 189 test->transport.send = pldm_transport_test_send; 190 test->transport.init_pollfd = pldm_transport_test_init_pollfd; 191 test->seq = seq; 192 test->count = count; 193 test->cursor = 0; 194 test->timerfd = timerfd_create(CLOCK_MONOTONIC, 0); 195 if (test->timerfd < 0) { 196 rc = -errno; 197 goto cleanup_test; 198 } 199 200 *ctx = test; 201 202 return 0; 203 204 cleanup_test: 205 free(test); 206 return rc; 207 } 208 209 LIBPLDM_ABI_TESTING 210 void pldm_transport_test_destroy(struct pldm_transport_test *ctx) 211 { 212 close(ctx->timerfd); 213 free(ctx); 214 } 215