xref: /openbmc/libpldm/src/transport/transport.c (revision 860a43d996dacf98a94d62fdc7b8484ce04bde23)
1691668feSPatrick Williams /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2*860a43d9SAndrew Jeffery #include "compiler.h"
3c1b66f42SRashmica Gupta #include "transport.h"
4c1b66f42SRashmica Gupta 
5efb40069SAndrew Jeffery #include <libpldm/transport.h>
6efb40069SAndrew Jeffery #include <libpldm/base.h>
7efb40069SAndrew Jeffery #include <libpldm/pldm.h>
8efb40069SAndrew Jeffery 
9abaf61f4SDung Cao #include <errno.h>
10abaf61f4SDung Cao #include <limits.h>
11c1b66f42SRashmica Gupta #ifdef PLDM_HAS_POLL
12c1b66f42SRashmica Gupta #include <poll.h>
13c1b66f42SRashmica Gupta #endif
14abaf61f4SDung Cao #include <stdbool.h>
15c1b66f42SRashmica Gupta #include <stdlib.h>
16abaf61f4SDung Cao #include <sys/time.h>
17abaf61f4SDung Cao #include <time.h>
18c1b66f42SRashmica Gupta #include <unistd.h>
19c1b66f42SRashmica Gupta 
20c1b66f42SRashmica Gupta #ifndef PLDM_HAS_POLL
21c1b66f42SRashmica Gupta struct pollfd {
22c1b66f42SRashmica Gupta 	int fd;	       /* file descriptor */
23c1b66f42SRashmica Gupta 	short events;  /* requested events */
24c1b66f42SRashmica Gupta 	short revents; /* returned events */
25c1b66f42SRashmica Gupta };
26c1b66f42SRashmica Gupta 
poll(struct pollfd * fds LIBPLDM_CC_UNUSED,int nfds LIBPLDM_CC_UNUSED,int timeout LIBPLDM_CC_UNUSED)27*860a43d9SAndrew Jeffery static inline int poll(struct pollfd *fds LIBPLDM_CC_UNUSED,
28*860a43d9SAndrew Jeffery 		       int nfds LIBPLDM_CC_UNUSED,
29*860a43d9SAndrew Jeffery 		       int timeout LIBPLDM_CC_UNUSED)
30c1b66f42SRashmica Gupta {
31c1b66f42SRashmica Gupta 	return 0;
32c1b66f42SRashmica Gupta }
33c1b66f42SRashmica Gupta #endif
34c1b66f42SRashmica Gupta 
350a6d6821SAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_transport_poll(struct pldm_transport * transport,int timeout)363380a6c6SAndrew Jeffery int pldm_transport_poll(struct pldm_transport *transport, int timeout)
37c1b66f42SRashmica Gupta {
38c1b66f42SRashmica Gupta 	struct pollfd pollfd;
39c1b66f42SRashmica Gupta 	int rc = 0;
40c1b66f42SRashmica Gupta 	if (!transport) {
41c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_INVALID_SETUP;
42c1b66f42SRashmica Gupta 	}
433380a6c6SAndrew Jeffery 
443380a6c6SAndrew Jeffery 	/* If polling isn't supported then always indicate the transport is ready */
45c1b66f42SRashmica Gupta 	if (!transport->init_pollfd) {
463380a6c6SAndrew Jeffery 		return 1;
47c1b66f42SRashmica Gupta 	}
48c1b66f42SRashmica Gupta 
4998085fafSAndrew Jeffery 	rc = transport->init_pollfd(transport, &pollfd);
5098085fafSAndrew Jeffery 	if (rc < 0) {
5198085fafSAndrew Jeffery 		return PLDM_REQUESTER_POLL_FAIL;
5298085fafSAndrew Jeffery 	}
5398085fafSAndrew Jeffery 
54c1b66f42SRashmica Gupta 	rc = poll(&pollfd, 1, timeout);
55c1b66f42SRashmica Gupta 	if (rc < 0) {
56c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_POLL_FAIL;
57c1b66f42SRashmica Gupta 	}
58c1b66f42SRashmica Gupta 
593380a6c6SAndrew Jeffery 	/* rc is 0 if poll(2) times out, or 1 if pollfd becomes active. */
603380a6c6SAndrew Jeffery 	return rc;
61c1b66f42SRashmica Gupta }
62c1b66f42SRashmica Gupta 
630a6d6821SAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_transport_send_msg(struct pldm_transport * transport,pldm_tid_t tid,const void * pldm_msg,size_t msg_len)64c1b66f42SRashmica Gupta pldm_requester_rc_t pldm_transport_send_msg(struct pldm_transport *transport,
65c1b66f42SRashmica Gupta 					    pldm_tid_t tid,
66f1ebde49SRashmica Gupta 					    const void *pldm_msg,
67f1ebde49SRashmica Gupta 					    size_t msg_len)
68c1b66f42SRashmica Gupta {
69f1ebde49SRashmica Gupta 	if (!transport || !pldm_msg) {
70c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_INVALID_SETUP;
71c1b66f42SRashmica Gupta 	}
72c1b66f42SRashmica Gupta 
73f1ebde49SRashmica Gupta 	if (msg_len < sizeof(struct pldm_msg_hdr)) {
74c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_NOT_REQ_MSG;
75c1b66f42SRashmica Gupta 	}
76c1b66f42SRashmica Gupta 
77f1ebde49SRashmica Gupta 	return transport->send(transport, tid, pldm_msg, msg_len);
78c1b66f42SRashmica Gupta }
79c1b66f42SRashmica Gupta 
800a6d6821SAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_transport_recv_msg(struct pldm_transport * transport,pldm_tid_t * tid,void ** pldm_msg,size_t * msg_len)81c1b66f42SRashmica Gupta pldm_requester_rc_t pldm_transport_recv_msg(struct pldm_transport *transport,
8224576290SRashmica Gupta 					    pldm_tid_t *tid, void **pldm_msg,
83f1ebde49SRashmica Gupta 					    size_t *msg_len)
84c1b66f42SRashmica Gupta {
85f1ebde49SRashmica Gupta 	if (!transport || !msg_len) {
86c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_INVALID_SETUP;
87c1b66f42SRashmica Gupta 	}
88c1b66f42SRashmica Gupta 
89c1b66f42SRashmica Gupta 	pldm_requester_rc_t rc =
90f1ebde49SRashmica Gupta 		transport->recv(transport, tid, pldm_msg, msg_len);
91c1b66f42SRashmica Gupta 	if (rc != PLDM_REQUESTER_SUCCESS) {
92c1b66f42SRashmica Gupta 		return rc;
93c1b66f42SRashmica Gupta 	}
94c1b66f42SRashmica Gupta 
95f1ebde49SRashmica Gupta 	if (*msg_len < sizeof(struct pldm_msg_hdr)) {
96f1ebde49SRashmica Gupta 		free(*pldm_msg);
97f1ebde49SRashmica Gupta 		*pldm_msg = NULL;
980411b712SRashmica Gupta 		return PLDM_REQUESTER_INVALID_RECV_LEN;
99c1b66f42SRashmica Gupta 	}
100c1b66f42SRashmica Gupta 	return PLDM_REQUESTER_SUCCESS;
101c1b66f42SRashmica Gupta }
102c1b66f42SRashmica Gupta 
timespec_to_timeval(const struct timespec * ts,struct timeval * tv)103abaf61f4SDung Cao static void timespec_to_timeval(const struct timespec *ts, struct timeval *tv)
104abaf61f4SDung Cao {
105abaf61f4SDung Cao 	tv->tv_sec = ts->tv_sec;
106abaf61f4SDung Cao 	tv->tv_usec = ts->tv_nsec / 1000;
107abaf61f4SDung Cao }
108abaf61f4SDung Cao 
109abaf61f4SDung Cao /* Overflow safety must be upheld before call */
timeval_to_msec(const struct timeval * tv)110abaf61f4SDung Cao static long timeval_to_msec(const struct timeval *tv)
111abaf61f4SDung Cao {
112abaf61f4SDung Cao 	return tv->tv_sec * 1000 + tv->tv_usec / 1000;
113abaf61f4SDung Cao }
114abaf61f4SDung Cao 
115abaf61f4SDung Cao /* If calculations on `tv` don't overflow then operations on derived
116abaf61f4SDung Cao  * intervals can't either.
117abaf61f4SDung Cao  */
timeval_is_valid(const struct timeval * tv)118abaf61f4SDung Cao static bool timeval_is_valid(const struct timeval *tv)
119abaf61f4SDung Cao {
120abaf61f4SDung Cao 	if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
121abaf61f4SDung Cao 		return false;
122abaf61f4SDung Cao 	}
123abaf61f4SDung Cao 
124abaf61f4SDung Cao 	if (tv->tv_sec > (LONG_MAX - tv->tv_usec / 1000) / 1000) {
125abaf61f4SDung Cao 		return false;
126abaf61f4SDung Cao 	}
127abaf61f4SDung Cao 
128abaf61f4SDung Cao 	return true;
129abaf61f4SDung Cao }
130abaf61f4SDung Cao 
clock_gettimeval(clockid_t clockid,struct timeval * tv)131abaf61f4SDung Cao static int clock_gettimeval(clockid_t clockid, struct timeval *tv)
132abaf61f4SDung Cao {
133abaf61f4SDung Cao 	struct timespec now;
134abaf61f4SDung Cao 	int rc;
135abaf61f4SDung Cao 
136abaf61f4SDung Cao 	rc = clock_gettime(clockid, &now);
137abaf61f4SDung Cao 	if (rc < 0) {
138abaf61f4SDung Cao 		return rc;
139abaf61f4SDung Cao 	}
140abaf61f4SDung Cao 
141abaf61f4SDung Cao 	timespec_to_timeval(&now, tv);
142abaf61f4SDung Cao 
143abaf61f4SDung Cao 	return 0;
144abaf61f4SDung Cao }
145abaf61f4SDung Cao 
1460a6d6821SAndrew Jeffery LIBPLDM_ABI_STABLE
147c1b66f42SRashmica Gupta pldm_requester_rc_t
pldm_transport_send_recv_msg(struct pldm_transport * transport,pldm_tid_t tid,const void * pldm_req_msg,size_t req_msg_len,void ** pldm_resp_msg,size_t * resp_msg_len)148c1b66f42SRashmica Gupta pldm_transport_send_recv_msg(struct pldm_transport *transport, pldm_tid_t tid,
149c1b66f42SRashmica Gupta 			     const void *pldm_req_msg, size_t req_msg_len,
150c1b66f42SRashmica Gupta 			     void **pldm_resp_msg, size_t *resp_msg_len)
151c1b66f42SRashmica Gupta 
152c1b66f42SRashmica Gupta {
153abaf61f4SDung Cao 	/**
154abaf61f4SDung Cao 	 * Section "Requirements for requesters" in DSP0240, define the Time-out
155abaf61f4SDung Cao 	 * waiting for a response of the requester.
156abaf61f4SDung Cao 	 * PT2max = PT3min - 2*PT4max = 4800ms
157abaf61f4SDung Cao 	 */
158abaf61f4SDung Cao 	static const struct timeval max_response_interval = {
159abaf61f4SDung Cao 		.tv_sec = 4, .tv_usec = 800000
160abaf61f4SDung Cao 	};
161b01fb1ccSThu Nguyen 	const struct pldm_msg_hdr *req_hdr;
162abaf61f4SDung Cao 	struct timeval remaining;
163b01fb1ccSThu Nguyen 	pldm_requester_rc_t rc;
164abaf61f4SDung Cao 	struct timeval now;
165abaf61f4SDung Cao 	struct timeval end;
166abaf61f4SDung Cao 	int ret;
167f56e4dcdSThu Nguyen 	int cnt;
168abaf61f4SDung Cao 
169b01fb1ccSThu Nguyen 	if (req_msg_len < sizeof(*req_hdr) || !resp_msg_len) {
170c1b66f42SRashmica Gupta 		return PLDM_REQUESTER_INVALID_SETUP;
171c1b66f42SRashmica Gupta 	}
172c1b66f42SRashmica Gupta 
173b01fb1ccSThu Nguyen 	req_hdr = pldm_req_msg;
174b01fb1ccSThu Nguyen 
175486d85d2SAndrew Jeffery 	if (!req_hdr->request) {
176486d85d2SAndrew Jeffery 		return PLDM_REQUESTER_NOT_REQ_MSG;
177486d85d2SAndrew Jeffery 	}
178486d85d2SAndrew Jeffery 
179f56e4dcdSThu Nguyen 	for (cnt = 0; cnt <= (PLDM_INSTANCE_MAX + 1) * PLDM_MAX_TIDS &&
180f56e4dcdSThu Nguyen 		      pldm_transport_poll(transport, 0) == 1;
181f56e4dcdSThu Nguyen 	     cnt++) {
18224576290SRashmica Gupta 		pldm_tid_t l_tid;
18324576290SRashmica Gupta 		rc = pldm_transport_recv_msg(transport, &l_tid, pldm_resp_msg,
184f56e4dcdSThu Nguyen 					     resp_msg_len);
185f56e4dcdSThu Nguyen 		if (rc == PLDM_REQUESTER_SUCCESS) {
186f56e4dcdSThu Nguyen 			/* This isn't the message we wanted */
187f56e4dcdSThu Nguyen 			free(*pldm_resp_msg);
188f56e4dcdSThu Nguyen 		}
189f56e4dcdSThu Nguyen 	}
190f56e4dcdSThu Nguyen 	if (cnt == (PLDM_INSTANCE_MAX + 1) * PLDM_MAX_TIDS) {
191f56e4dcdSThu Nguyen 		return PLDM_REQUESTER_TRANSPORT_BUSY;
192f56e4dcdSThu Nguyen 	}
193f56e4dcdSThu Nguyen 
194abaf61f4SDung Cao 	rc = pldm_transport_send_msg(transport, tid, pldm_req_msg, req_msg_len);
195c1b66f42SRashmica Gupta 	if (rc != PLDM_REQUESTER_SUCCESS) {
196c1b66f42SRashmica Gupta 		return rc;
197c1b66f42SRashmica Gupta 	}
198c1b66f42SRashmica Gupta 
199abaf61f4SDung Cao 	ret = clock_gettimeval(CLOCK_MONOTONIC, &now);
200abaf61f4SDung Cao 	if (ret < 0) {
201abaf61f4SDung Cao 		return PLDM_REQUESTER_POLL_FAIL;
202c1b66f42SRashmica Gupta 	}
203abaf61f4SDung Cao 
204abaf61f4SDung Cao 	timeradd(&now, &max_response_interval, &end);
205abaf61f4SDung Cao 	if (!timeval_is_valid(&end)) {
206abaf61f4SDung Cao 		return PLDM_REQUESTER_POLL_FAIL;
207abaf61f4SDung Cao 	}
208abaf61f4SDung Cao 
2091d0e2d4bSAndrew Jeffery 	while (timercmp(&now, &end, <)) {
2101d0e2d4bSAndrew Jeffery 		pldm_tid_t src_tid;
2111d0e2d4bSAndrew Jeffery 
212abaf61f4SDung Cao 		timersub(&end, &now, &remaining);
2131d0e2d4bSAndrew Jeffery 
214abaf61f4SDung Cao 		/* 0 <= `timeval_to_msec()` <= 4800, and 4800 < INT_MAX */
2153380a6c6SAndrew Jeffery 		ret = pldm_transport_poll(transport,
216abaf61f4SDung Cao 					  (int)(timeval_to_msec(&remaining)));
2173380a6c6SAndrew Jeffery 		if (ret <= 0) {
2181184f09aSAndrew Jeffery 			return PLDM_REQUESTER_RECV_FAIL;
219abaf61f4SDung Cao 		}
220abaf61f4SDung Cao 
2215038268cSAndrew Jeffery 		ret = clock_gettimeval(CLOCK_MONOTONIC, &now);
2225038268cSAndrew Jeffery 		if (ret < 0) {
2235038268cSAndrew Jeffery 			return PLDM_REQUESTER_POLL_FAIL;
2245038268cSAndrew Jeffery 		}
2255038268cSAndrew Jeffery 
22624576290SRashmica Gupta 		rc = pldm_transport_recv_msg(transport, &src_tid, pldm_resp_msg,
227c1b66f42SRashmica Gupta 					     resp_msg_len);
2281d0e2d4bSAndrew Jeffery 		if (rc != PLDM_REQUESTER_SUCCESS) {
2291d0e2d4bSAndrew Jeffery 			continue;
230c1b66f42SRashmica Gupta 		}
231c1b66f42SRashmica Gupta 
2321d0e2d4bSAndrew Jeffery 		if (src_tid != tid || !pldm_msg_hdr_correlate_response(
2331d0e2d4bSAndrew Jeffery 					      pldm_req_msg, *pldm_resp_msg)) {
234b01fb1ccSThu Nguyen 			free(*pldm_resp_msg);
2351d0e2d4bSAndrew Jeffery 			continue;
236b01fb1ccSThu Nguyen 		}
2371d0e2d4bSAndrew Jeffery 
2381d0e2d4bSAndrew Jeffery 		return PLDM_REQUESTER_SUCCESS;
2391d0e2d4bSAndrew Jeffery 	}
240abaf61f4SDung Cao 
241abaf61f4SDung Cao 	return PLDM_REQUESTER_RECV_FAIL;
242c1b66f42SRashmica Gupta }
243