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