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 #define _GNU_SOURCE 4 #include "array.h" 5 #include "container-of.h" 6 #include "transport.h" 7 #include "test.h" 8 9 #include <errno.h> 10 #include <poll.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 struct pldm_transport_test { 16 struct pldm_transport transport; 17 const struct pldm_transport_test_descriptor *seq; 18 size_t count; 19 size_t cursor; 20 int timerfd; 21 }; 22 23 #define transport_to_test(ptr) \ 24 container_of(ptr, struct pldm_transport_test, transport) 25 26 LIBPLDM_ABI_TESTING 27 struct pldm_transport *pldm_transport_test_core(struct pldm_transport_test *ctx) 28 { 29 return &ctx->transport; 30 } 31 32 #ifdef PLDM_HAS_POLL 33 #include <poll.h> 34 LIBPLDM_ABI_TESTING 35 int pldm_transport_test_init_pollfd(struct pldm_transport *ctx, 36 struct pollfd *pollfd) 37 { 38 static const struct itimerspec disable = { 39 .it_value = { 0, 0 }, 40 .it_interval = { 0, 0 }, 41 }; 42 struct pldm_transport_test *test = transport_to_test(ctx); 43 const struct pldm_transport_test_descriptor *desc; 44 int rc; 45 46 rc = timerfd_settime(test->timerfd, 0, &disable, NULL); 47 if (rc < 0) { 48 return PLDM_REQUESTER_POLL_FAIL; 49 } 50 51 if (test->cursor >= test->count) { 52 return PLDM_REQUESTER_POLL_FAIL; 53 } 54 55 desc = &test->seq[test->cursor]; 56 57 if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_LATENCY) { 58 rc = timerfd_settime(test->timerfd, 0, &desc->latency, NULL); 59 if (rc < 0) { 60 return PLDM_REQUESTER_POLL_FAIL; 61 } 62 63 /* This was an explicit latency element, so now move beyond it for recv */ 64 test->cursor++; 65 } else if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { 66 /* Expire the timer immediately so it appears ready */ 67 static const struct timespec ensure_ready = { 68 .tv_sec = 0, 69 .tv_nsec = 2, 70 }; 71 static const struct itimerspec ready = { 72 .it_value = { 0, 1 }, 73 .it_interval = { 0, 0 }, 74 }; 75 struct pollfd pfds[] = { 76 { .fd = test->timerfd, .events = POLLIN }, 77 }; 78 79 rc = timerfd_settime(test->timerfd, 0, &ready, NULL); 80 if (rc < 0) { 81 return PLDM_REQUESTER_POLL_FAIL; 82 } 83 84 rc = ppoll(pfds, ARRAY_SIZE(pfds), &ensure_ready, NULL); 85 if (rc < 1) { 86 return PLDM_REQUESTER_POLL_FAIL; 87 } 88 89 /* Don't increment test->cursor as recv needs to consume the current test element */ 90 } else { 91 return PLDM_REQUESTER_POLL_FAIL; 92 } 93 94 pollfd->fd = test->timerfd; 95 pollfd->events = POLLIN; 96 97 return 0; 98 } 99 #endif 100 101 static pldm_requester_rc_t pldm_transport_test_recv(struct pldm_transport *ctx, 102 pldm_tid_t *tid, 103 void **pldm_resp_msg, 104 size_t *resp_msg_len) 105 { 106 struct pldm_transport_test *test = transport_to_test(ctx); 107 const struct pldm_transport_test_descriptor *desc; 108 void *msg; 109 110 if (test->cursor >= test->count) { 111 return PLDM_REQUESTER_RECV_FAIL; 112 } 113 114 desc = &test->seq[test->cursor]; 115 116 if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) { 117 return PLDM_REQUESTER_RECV_FAIL; 118 } 119 120 msg = malloc(desc->recv_msg.len); 121 if (!msg) { 122 return PLDM_REQUESTER_RECV_FAIL; 123 } 124 125 memcpy(msg, desc->recv_msg.msg, desc->recv_msg.len); 126 *pldm_resp_msg = msg; 127 *resp_msg_len = desc->recv_msg.len; 128 *tid = desc->recv_msg.src; 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