xref: /openbmc/libpldm/src/transport/transport.c (revision f89befe3)
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 pldm_requester_rc_t pldm_transport_poll(struct pldm_transport *transport,
34 					int timeout)
35 {
36 	struct pollfd pollfd;
37 	int rc = 0;
38 	if (!transport) {
39 		return PLDM_REQUESTER_INVALID_SETUP;
40 	}
41 	if (!transport->init_pollfd) {
42 		return PLDM_REQUESTER_SUCCESS;
43 	}
44 
45 	transport->init_pollfd(transport, &pollfd);
46 	rc = poll(&pollfd, 1, timeout);
47 	if (rc < 0) {
48 		return PLDM_REQUESTER_POLL_FAIL;
49 	}
50 
51 	return PLDM_REQUESTER_SUCCESS;
52 }
53 
54 LIBPLDM_ABI_TESTING
55 pldm_requester_rc_t pldm_transport_send_msg(struct pldm_transport *transport,
56 					    pldm_tid_t tid,
57 					    const void *pldm_req_msg,
58 					    size_t req_msg_len)
59 {
60 	if (!transport || !pldm_req_msg) {
61 		return PLDM_REQUESTER_INVALID_SETUP;
62 	}
63 
64 	if (req_msg_len < sizeof(struct pldm_msg_hdr)) {
65 		return PLDM_REQUESTER_NOT_REQ_MSG;
66 	}
67 
68 	return transport->send(transport, tid, pldm_req_msg, req_msg_len);
69 }
70 
71 LIBPLDM_ABI_TESTING
72 pldm_requester_rc_t pldm_transport_recv_msg(struct pldm_transport *transport,
73 					    pldm_tid_t tid,
74 					    void **pldm_resp_msg,
75 					    size_t *resp_msg_len)
76 {
77 	if (!transport || !resp_msg_len) {
78 		return PLDM_REQUESTER_INVALID_SETUP;
79 	}
80 
81 	pldm_requester_rc_t rc =
82 		transport->recv(transport, tid, pldm_resp_msg, resp_msg_len);
83 	if (rc != PLDM_REQUESTER_SUCCESS) {
84 		return rc;
85 	}
86 
87 	if (*resp_msg_len < sizeof(struct pldm_msg_hdr)) {
88 		free(*pldm_resp_msg);
89 		*pldm_resp_msg = NULL;
90 		return PLDM_REQUESTER_INVALID_RECV_LEN;
91 	}
92 	return PLDM_REQUESTER_SUCCESS;
93 }
94 
95 static void timespec_to_timeval(const struct timespec *ts, struct timeval *tv)
96 {
97 	tv->tv_sec = ts->tv_sec;
98 	tv->tv_usec = ts->tv_nsec / 1000;
99 }
100 
101 /* Overflow safety must be upheld before call */
102 static long timeval_to_msec(const struct timeval *tv)
103 {
104 	return tv->tv_sec * 1000 + tv->tv_usec / 1000;
105 }
106 
107 /* If calculations on `tv` don't overflow then operations on derived
108  * intervals can't either.
109  */
110 static bool timeval_is_valid(const struct timeval *tv)
111 {
112 	if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
113 		return false;
114 	}
115 
116 	if (tv->tv_sec > (LONG_MAX - tv->tv_usec / 1000) / 1000) {
117 		return false;
118 	}
119 
120 	return true;
121 }
122 
123 static int clock_gettimeval(clockid_t clockid, struct timeval *tv)
124 {
125 	struct timespec now;
126 	int rc;
127 
128 	rc = clock_gettime(clockid, &now);
129 	if (rc < 0) {
130 		return rc;
131 	}
132 
133 	timespec_to_timeval(&now, tv);
134 
135 	return 0;
136 }
137 
138 LIBPLDM_ABI_TESTING
139 pldm_requester_rc_t
140 pldm_transport_send_recv_msg(struct pldm_transport *transport, pldm_tid_t tid,
141 			     const void *pldm_req_msg, size_t req_msg_len,
142 			     void **pldm_resp_msg, size_t *resp_msg_len)
143 
144 {
145 	/**
146 	 * Section "Requirements for requesters" in DSP0240, define the Time-out
147 	 * waiting for a response of the requester.
148 	 * PT2max = PT3min - 2*PT4max = 4800ms
149 	 */
150 	static const struct timeval max_response_interval = {
151 		.tv_sec = 4, .tv_usec = 800000
152 	};
153 	const struct pldm_msg_hdr *req_hdr;
154 	struct timeval remaining;
155 	pldm_requester_rc_t rc;
156 	struct timeval now;
157 	struct timeval end;
158 	int ret;
159 
160 	if (req_msg_len < sizeof(*req_hdr) || !resp_msg_len) {
161 		return PLDM_REQUESTER_INVALID_SETUP;
162 	}
163 
164 	req_hdr = pldm_req_msg;
165 
166 	rc = pldm_transport_send_msg(transport, tid, pldm_req_msg, req_msg_len);
167 	if (rc != PLDM_REQUESTER_SUCCESS) {
168 		return rc;
169 	}
170 
171 	ret = clock_gettimeval(CLOCK_MONOTONIC, &now);
172 	if (ret < 0) {
173 		return PLDM_REQUESTER_POLL_FAIL;
174 	}
175 
176 	timeradd(&now, &max_response_interval, &end);
177 	if (!timeval_is_valid(&end)) {
178 		return PLDM_REQUESTER_POLL_FAIL;
179 	}
180 
181 	do {
182 		timersub(&end, &now, &remaining);
183 		/* 0 <= `timeval_to_msec()` <= 4800, and 4800 < INT_MAX */
184 		rc = pldm_transport_poll(transport,
185 					 (int)(timeval_to_msec(&remaining)));
186 		if (rc != PLDM_REQUESTER_SUCCESS) {
187 			return rc;
188 		}
189 
190 		rc = pldm_transport_recv_msg(transport, tid, pldm_resp_msg,
191 					     resp_msg_len);
192 		if (rc == PLDM_REQUESTER_SUCCESS) {
193 			const struct pldm_msg_hdr *resp_hdr = *pldm_resp_msg;
194 			if (req_hdr->instance_id == resp_hdr->instance_id) {
195 				return rc;
196 			}
197 
198 			/* This isn't the message we wanted */
199 			free(*pldm_resp_msg);
200 		}
201 
202 		ret = clock_gettimeval(CLOCK_MONOTONIC, &now);
203 		if (ret < 0) {
204 			return PLDM_REQUESTER_POLL_FAIL;
205 		}
206 	} while (!timercmp(&now, &end, <));
207 
208 	return PLDM_REQUESTER_RECV_FAIL;
209 }
210