1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 #include "compiler.h" 3 #include "transport.h" 4 5 #include <libpldm/transport.h> 6 #include <libpldm/base.h> 7 #include <libpldm/pldm.h> 8 9 #include <errno.h> 10 #include <limits.h> 11 #ifdef PLDM_HAS_POLL 12 #include <poll.h> 13 #endif 14 #include <stdbool.h> 15 #include <stdlib.h> 16 #include <sys/time.h> 17 #include <time.h> 18 #include <unistd.h> 19 20 #ifndef PLDM_HAS_POLL 21 struct pollfd { 22 int fd; /* file descriptor */ 23 short events; /* requested events */ 24 short revents; /* returned events */ 25 }; 26 27 static inline int poll(struct pollfd *fds LIBPLDM_CC_UNUSED, 28 int nfds LIBPLDM_CC_UNUSED, 29 int timeout LIBPLDM_CC_UNUSED) 30 { 31 return 0; 32 } 33 #endif 34 35 LIBPLDM_ABI_STABLE 36 int pldm_transport_poll(struct pldm_transport *transport, int timeout) 37 { 38 struct pollfd pollfd; 39 int rc = 0; 40 if (!transport) { 41 return PLDM_REQUESTER_INVALID_SETUP; 42 } 43 44 /* If polling isn't supported then always indicate the transport is ready */ 45 if (!transport->init_pollfd) { 46 return 1; 47 } 48 49 rc = transport->init_pollfd(transport, &pollfd); 50 if (rc < 0) { 51 return PLDM_REQUESTER_POLL_FAIL; 52 } 53 54 rc = poll(&pollfd, 1, timeout); 55 if (rc < 0) { 56 return PLDM_REQUESTER_POLL_FAIL; 57 } 58 59 /* rc is 0 if poll(2) times out, or 1 if pollfd becomes active. */ 60 return rc; 61 } 62 63 LIBPLDM_ABI_STABLE 64 pldm_requester_rc_t pldm_transport_send_msg(struct pldm_transport *transport, 65 pldm_tid_t tid, 66 const void *pldm_msg, 67 size_t msg_len) 68 { 69 if (!transport || !pldm_msg) { 70 return PLDM_REQUESTER_INVALID_SETUP; 71 } 72 73 if (msg_len < sizeof(struct pldm_msg_hdr)) { 74 return PLDM_REQUESTER_NOT_REQ_MSG; 75 } 76 77 return transport->send(transport, tid, pldm_msg, msg_len); 78 } 79 80 LIBPLDM_ABI_STABLE 81 pldm_requester_rc_t pldm_transport_recv_msg(struct pldm_transport *transport, 82 pldm_tid_t *tid, void **pldm_msg, 83 size_t *msg_len) 84 { 85 if (!transport || !msg_len) { 86 return PLDM_REQUESTER_INVALID_SETUP; 87 } 88 89 pldm_requester_rc_t rc = 90 transport->recv(transport, tid, pldm_msg, msg_len); 91 if (rc != PLDM_REQUESTER_SUCCESS) { 92 return rc; 93 } 94 95 if (*msg_len < sizeof(struct pldm_msg_hdr)) { 96 free(*pldm_msg); 97 *pldm_msg = NULL; 98 return PLDM_REQUESTER_INVALID_RECV_LEN; 99 } 100 return PLDM_REQUESTER_SUCCESS; 101 } 102 103 static void timespec_to_timeval(const struct timespec *ts, struct timeval *tv) 104 { 105 tv->tv_sec = ts->tv_sec; 106 tv->tv_usec = ts->tv_nsec / 1000; 107 } 108 109 /* Overflow safety must be upheld before call */ 110 static long timeval_to_msec(const struct timeval *tv) 111 { 112 return tv->tv_sec * 1000 + tv->tv_usec / 1000; 113 } 114 115 /* If calculations on `tv` don't overflow then operations on derived 116 * intervals can't either. 117 */ 118 static bool timeval_is_valid(const struct timeval *tv) 119 { 120 if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) { 121 return false; 122 } 123 124 if (tv->tv_sec > (LONG_MAX - tv->tv_usec / 1000) / 1000) { 125 return false; 126 } 127 128 return true; 129 } 130 131 static int clock_gettimeval(clockid_t clockid, struct timeval *tv) 132 { 133 struct timespec now; 134 int rc; 135 136 rc = clock_gettime(clockid, &now); 137 if (rc < 0) { 138 return rc; 139 } 140 141 timespec_to_timeval(&now, tv); 142 143 return 0; 144 } 145 146 LIBPLDM_ABI_STABLE 147 pldm_requester_rc_t 148 pldm_transport_send_recv_msg(struct pldm_transport *transport, pldm_tid_t tid, 149 const void *pldm_req_msg, size_t req_msg_len, 150 void **pldm_resp_msg, size_t *resp_msg_len) 151 152 { 153 /** 154 * Section "Requirements for requesters" in DSP0240, define the Time-out 155 * waiting for a response of the requester. 156 * PT2max = PT3min - 2*PT4max = 4800ms 157 */ 158 static const struct timeval max_response_interval = { 159 .tv_sec = 4, .tv_usec = 800000 160 }; 161 const struct pldm_msg_hdr *req_hdr; 162 struct timeval remaining; 163 pldm_requester_rc_t rc; 164 struct timeval now; 165 struct timeval end; 166 int ret; 167 int cnt; 168 169 if (req_msg_len < sizeof(*req_hdr) || !resp_msg_len) { 170 return PLDM_REQUESTER_INVALID_SETUP; 171 } 172 173 req_hdr = pldm_req_msg; 174 175 if (!req_hdr->request) { 176 return PLDM_REQUESTER_NOT_REQ_MSG; 177 } 178 179 for (cnt = 0; cnt <= (PLDM_INSTANCE_MAX + 1) * PLDM_MAX_TIDS && 180 pldm_transport_poll(transport, 0) == 1; 181 cnt++) { 182 pldm_tid_t l_tid; 183 rc = pldm_transport_recv_msg(transport, &l_tid, pldm_resp_msg, 184 resp_msg_len); 185 if (rc == PLDM_REQUESTER_SUCCESS) { 186 /* This isn't the message we wanted */ 187 free(*pldm_resp_msg); 188 } 189 } 190 if (cnt == (PLDM_INSTANCE_MAX + 1) * PLDM_MAX_TIDS) { 191 return PLDM_REQUESTER_TRANSPORT_BUSY; 192 } 193 194 rc = pldm_transport_send_msg(transport, tid, pldm_req_msg, req_msg_len); 195 if (rc != PLDM_REQUESTER_SUCCESS) { 196 return rc; 197 } 198 199 ret = clock_gettimeval(CLOCK_MONOTONIC, &now); 200 if (ret < 0) { 201 return PLDM_REQUESTER_POLL_FAIL; 202 } 203 204 timeradd(&now, &max_response_interval, &end); 205 if (!timeval_is_valid(&end)) { 206 return PLDM_REQUESTER_POLL_FAIL; 207 } 208 209 while (timercmp(&now, &end, <)) { 210 pldm_tid_t src_tid; 211 212 timersub(&end, &now, &remaining); 213 214 /* 0 <= `timeval_to_msec()` <= 4800, and 4800 < INT_MAX */ 215 ret = pldm_transport_poll(transport, 216 (int)(timeval_to_msec(&remaining))); 217 if (ret <= 0) { 218 return PLDM_REQUESTER_RECV_FAIL; 219 } 220 221 ret = clock_gettimeval(CLOCK_MONOTONIC, &now); 222 if (ret < 0) { 223 return PLDM_REQUESTER_POLL_FAIL; 224 } 225 226 rc = pldm_transport_recv_msg(transport, &src_tid, pldm_resp_msg, 227 resp_msg_len); 228 if (rc != PLDM_REQUESTER_SUCCESS) { 229 continue; 230 } 231 232 if (src_tid != tid || !pldm_msg_hdr_correlate_response( 233 pldm_req_msg, *pldm_resp_msg)) { 234 free(*pldm_resp_msg); 235 continue; 236 } 237 238 return PLDM_REQUESTER_SUCCESS; 239 } 240 241 return PLDM_REQUESTER_RECV_FAIL; 242 } 243