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