xref: /openbmc/libpldm/src/transport/transport.c (revision 860a43d9)
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 
poll(struct pollfd * fds LIBPLDM_CC_UNUSED,int nfds LIBPLDM_CC_UNUSED,int timeout LIBPLDM_CC_UNUSED)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
pldm_transport_poll(struct pldm_transport * transport,int timeout)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
pldm_transport_send_msg(struct pldm_transport * transport,pldm_tid_t tid,const void * pldm_msg,size_t msg_len)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
pldm_transport_recv_msg(struct pldm_transport * transport,pldm_tid_t * tid,void ** pldm_msg,size_t * msg_len)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 
timespec_to_timeval(const struct timespec * ts,struct timeval * tv)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 */
timeval_to_msec(const struct timeval * tv)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  */
timeval_is_valid(const struct timeval * tv)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 
clock_gettimeval(clockid_t clockid,struct timeval * tv)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
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)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