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