xref: /openbmc/libpldm/src/transport/transport.c (revision d10c6b0c)
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_TESTING
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_TESTING
61 pldm_requester_rc_t pldm_transport_send_msg(struct pldm_transport *transport,
62 					    pldm_tid_t tid,
63 					    const void *pldm_req_msg,
64 					    size_t req_msg_len)
65 {
66 	if (!transport || !pldm_req_msg) {
67 		return PLDM_REQUESTER_INVALID_SETUP;
68 	}
69 
70 	if (req_msg_len < sizeof(struct pldm_msg_hdr)) {
71 		return PLDM_REQUESTER_NOT_REQ_MSG;
72 	}
73 
74 	return transport->send(transport, tid, pldm_req_msg, req_msg_len);
75 }
76 
77 LIBPLDM_ABI_TESTING
78 pldm_requester_rc_t pldm_transport_recv_msg(struct pldm_transport *transport,
79 					    pldm_tid_t tid,
80 					    void **pldm_resp_msg,
81 					    size_t *resp_msg_len)
82 {
83 	if (!transport || !resp_msg_len) {
84 		return PLDM_REQUESTER_INVALID_SETUP;
85 	}
86 
87 	pldm_requester_rc_t rc =
88 		transport->recv(transport, tid, pldm_resp_msg, resp_msg_len);
89 	if (rc != PLDM_REQUESTER_SUCCESS) {
90 		return rc;
91 	}
92 
93 	if (*resp_msg_len < sizeof(struct pldm_msg_hdr)) {
94 		free(*pldm_resp_msg);
95 		*pldm_resp_msg = NULL;
96 		return PLDM_REQUESTER_INVALID_RECV_LEN;
97 	}
98 	return PLDM_REQUESTER_SUCCESS;
99 }
100 
101 static void timespec_to_timeval(const struct timespec *ts, struct timeval *tv)
102 {
103 	tv->tv_sec = ts->tv_sec;
104 	tv->tv_usec = ts->tv_nsec / 1000;
105 }
106 
107 /* Overflow safety must be upheld before call */
108 static long timeval_to_msec(const struct timeval *tv)
109 {
110 	return tv->tv_sec * 1000 + tv->tv_usec / 1000;
111 }
112 
113 /* If calculations on `tv` don't overflow then operations on derived
114  * intervals can't either.
115  */
116 static bool timeval_is_valid(const struct timeval *tv)
117 {
118 	if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
119 		return false;
120 	}
121 
122 	if (tv->tv_sec > (LONG_MAX - tv->tv_usec / 1000) / 1000) {
123 		return false;
124 	}
125 
126 	return true;
127 }
128 
129 static int clock_gettimeval(clockid_t clockid, struct timeval *tv)
130 {
131 	struct timespec now;
132 	int rc;
133 
134 	rc = clock_gettime(clockid, &now);
135 	if (rc < 0) {
136 		return rc;
137 	}
138 
139 	timespec_to_timeval(&now, tv);
140 
141 	return 0;
142 }
143 
144 LIBPLDM_ABI_TESTING
145 pldm_requester_rc_t
146 pldm_transport_send_recv_msg(struct pldm_transport *transport, pldm_tid_t tid,
147 			     const void *pldm_req_msg, size_t req_msg_len,
148 			     void **pldm_resp_msg, size_t *resp_msg_len)
149 
150 {
151 	/**
152 	 * Section "Requirements for requesters" in DSP0240, define the Time-out
153 	 * waiting for a response of the requester.
154 	 * PT2max = PT3min - 2*PT4max = 4800ms
155 	 */
156 	static const struct timeval max_response_interval = {
157 		.tv_sec = 4, .tv_usec = 800000
158 	};
159 	const struct pldm_msg_hdr *req_hdr;
160 	struct timeval remaining;
161 	pldm_requester_rc_t rc;
162 	struct timeval now;
163 	struct timeval end;
164 	int ret;
165 	int cnt;
166 
167 	if (req_msg_len < sizeof(*req_hdr) || !resp_msg_len) {
168 		return PLDM_REQUESTER_INVALID_SETUP;
169 	}
170 
171 	req_hdr = pldm_req_msg;
172 
173 	for (cnt = 0; cnt <= (PLDM_INSTANCE_MAX + 1) * PLDM_MAX_TIDS &&
174 		      pldm_transport_poll(transport, 0) == 1;
175 	     cnt++) {
176 		rc = pldm_transport_recv_msg(transport, 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 		rc = pldm_transport_recv_msg(transport, tid, pldm_resp_msg,
212 					     resp_msg_len);
213 		if (rc == PLDM_REQUESTER_SUCCESS) {
214 			const struct pldm_msg_hdr *resp_hdr = *pldm_resp_msg;
215 			if (req_hdr->instance_id == resp_hdr->instance_id) {
216 				return rc;
217 			}
218 
219 			/* This isn't the message we wanted */
220 			free(*pldm_resp_msg);
221 		}
222 
223 		ret = clock_gettimeval(CLOCK_MONOTONIC, &now);
224 		if (ret < 0) {
225 			return PLDM_REQUESTER_POLL_FAIL;
226 		}
227 	} while (timercmp(&now, &end, <));
228 
229 	return PLDM_REQUESTER_RECV_FAIL;
230 }
231