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