xref: /openbmc/libpldm/src/transport/transport.c (revision 2643f83c)
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