xref: /openbmc/libpldm/src/requester/pldm.c (revision efb40069)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include <libpldm/base.h>
3 #include <libpldm/pldm.h>
4 #include <libpldm/transport.h>
5 
6 #include <bits/types/struct_iovec.h>
7 #include <fcntl.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <unistd.h>
14 
15 /* Temporary for old api */
16 #include <libpldm/transport/mctp-demux.h>
17 extern int
18 pldm_transport_mctp_demux_get_socket_fd(struct pldm_transport_mctp_demux *ctx);
19 extern struct pldm_transport_mctp_demux *
20 pldm_transport_mctp_demux_init_with_fd(int mctp_fd);
21 
22 /* ---  old APIS written in terms of the new API -- */
23 /*
24  * pldm_open returns the file descriptor to the MCTP socket, which needs to
25  * persist over api calls (so a consumer can poll it for incoming messages).
26  * So we need a global variable to store the transport struct
27  */
28 static struct pldm_transport_mctp_demux *open_transport;
29 
30 LIBPLDM_ABI_DEPRECATED
pldm_open(void)31 pldm_requester_rc_t pldm_open(void)
32 {
33 	int fd = PLDM_REQUESTER_OPEN_FAIL;
34 
35 	if (open_transport) {
36 		fd = pldm_transport_mctp_demux_get_socket_fd(open_transport);
37 
38 		/* If someone has externally issued close() on fd then we need to start again. Use
39 		 * `fcntl(..., F_GETFD)` to test whether fd is valid. */
40 		if (fd < 0 || fcntl(fd, F_GETFD) < 0) {
41 			pldm_close();
42 		}
43 	}
44 
45 	/* We retest open_transport as it may have been set to NULL by pldm_close() above. */
46 	if (!open_transport) {
47 		struct pldm_transport_mctp_demux *demux = NULL;
48 
49 		if (pldm_transport_mctp_demux_init(&demux) < 0) {
50 			return PLDM_REQUESTER_OPEN_FAIL;
51 		}
52 
53 		open_transport = demux;
54 
55 		fd = pldm_transport_mctp_demux_get_socket_fd(open_transport);
56 	}
57 
58 	return fd;
59 }
60 
61 /* This macro does the setup and teardown required for the old API to use the
62  * new API. Since the setup/teardown logic is the same for all four send/recv
63  * functions, it makes sense to only define it once. */
64 #define PLDM_REQ_FN(eid, fd, fn, rc, ...)                                        \
65 	do {                                                                     \
66 		struct pldm_transport_mctp_demux *demux;                         \
67 		bool using_open_transport = false;                               \
68 		pldm_tid_t tid = eid;                                            \
69 		struct pldm_transport *ctx;                                      \
70 		/* The fd can be for a socket we opened or one the consumer    \
71 		 * opened. */ \
72 		if (open_transport &&                                            \
73 		    mctp_fd == pldm_transport_mctp_demux_get_socket_fd(          \
74 				       open_transport)) {                        \
75 			using_open_transport = true;                             \
76 			demux = open_transport;                                  \
77 		} else {                                                         \
78 			demux = pldm_transport_mctp_demux_init_with_fd(fd);      \
79 			if (!demux) {                                            \
80 				rc = PLDM_REQUESTER_OPEN_FAIL;                   \
81 				goto transport_out;                              \
82 			}                                                        \
83 		}                                                                \
84 		ctx = pldm_transport_mctp_demux_core(demux);                     \
85 		rc = pldm_transport_mctp_demux_map_tid(demux, tid, eid);         \
86 		if (rc) {                                                        \
87 			rc = PLDM_REQUESTER_OPEN_FAIL;                           \
88 			goto transport_out;                                      \
89 		}                                                                \
90 		rc = fn(ctx, tid, __VA_ARGS__);                                  \
91 	transport_out:                                                           \
92 		if (!using_open_transport) {                                     \
93 			pldm_transport_mctp_demux_destroy(demux);                \
94 		}                                                                \
95 		break;                                                           \
96 	} while (0)
97 
98 LIBPLDM_ABI_DEPRECATED
pldm_recv_any(mctp_eid_t eid,int mctp_fd,uint8_t ** pldm_resp_msg,size_t * resp_msg_len)99 pldm_requester_rc_t pldm_recv_any(mctp_eid_t eid, int mctp_fd,
100 				  uint8_t **pldm_resp_msg, size_t *resp_msg_len)
101 {
102 	pldm_requester_rc_t rc = 0;
103 
104 	struct pldm_transport_mctp_demux *demux;
105 	bool using_open_transport = false;
106 	pldm_tid_t tid = eid;
107 	struct pldm_transport *ctx;
108 	/* The fd can be for a socket we opened or one the consumer
109 	 * opened. */
110 	if (open_transport &&
111 	    mctp_fd ==
112 		    pldm_transport_mctp_demux_get_socket_fd(open_transport)) {
113 		using_open_transport = true;
114 		demux = open_transport;
115 	} else {
116 		demux = pldm_transport_mctp_demux_init_with_fd(mctp_fd);
117 		if (!demux) {
118 			rc = PLDM_REQUESTER_OPEN_FAIL;
119 			goto transport_out;
120 		}
121 	}
122 	ctx = pldm_transport_mctp_demux_core(demux);
123 	rc = pldm_transport_mctp_demux_map_tid(demux, tid, eid);
124 	if (rc) {
125 		rc = PLDM_REQUESTER_OPEN_FAIL;
126 		goto transport_out;
127 	}
128 	/* TODO this is the only change, can we work this into the macro? */
129 	rc = pldm_transport_recv_msg(ctx, &tid, (void **)pldm_resp_msg,
130 				     resp_msg_len);
131 
132 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
133 	if (rc != PLDM_REQUESTER_SUCCESS) {
134 		return rc;
135 	}
136 	if (hdr && (hdr->request || hdr->datagram)) {
137 		free(*pldm_resp_msg);
138 		*pldm_resp_msg = NULL;
139 		return PLDM_REQUESTER_NOT_RESP_MSG;
140 	}
141 	uint8_t pldm_cc = 0;
142 	if (*resp_msg_len < (sizeof(struct pldm_msg_hdr) + sizeof(pldm_cc))) {
143 		free(*pldm_resp_msg);
144 		*pldm_resp_msg = NULL;
145 		return PLDM_REQUESTER_RESP_MSG_TOO_SMALL;
146 	}
147 
148 transport_out:
149 	if (!using_open_transport) {
150 		pldm_transport_mctp_demux_destroy(demux);
151 	}
152 
153 	return rc;
154 }
155 
156 LIBPLDM_ABI_DEPRECATED
pldm_recv(mctp_eid_t eid,int mctp_fd,uint8_t instance_id,uint8_t ** pldm_resp_msg,size_t * resp_msg_len)157 pldm_requester_rc_t pldm_recv(mctp_eid_t eid, int mctp_fd,
158 			      __attribute__((unused)) uint8_t instance_id,
159 			      uint8_t **pldm_resp_msg, size_t *resp_msg_len)
160 {
161 	pldm_requester_rc_t rc =
162 		pldm_recv_any(eid, mctp_fd, pldm_resp_msg, resp_msg_len);
163 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
164 	if (rc == PLDM_REQUESTER_SUCCESS && hdr &&
165 	    hdr->instance_id != instance_id) {
166 		free(*pldm_resp_msg);
167 		*pldm_resp_msg = NULL;
168 		return PLDM_REQUESTER_INSTANCE_ID_MISMATCH;
169 	}
170 	return rc;
171 }
172 
173 LIBPLDM_ABI_DEPRECATED
pldm_send_recv(mctp_eid_t eid,int mctp_fd,const uint8_t * pldm_req_msg,size_t req_msg_len,uint8_t ** pldm_resp_msg,size_t * resp_msg_len)174 pldm_requester_rc_t pldm_send_recv(mctp_eid_t eid, int mctp_fd,
175 				   const uint8_t *pldm_req_msg,
176 				   size_t req_msg_len, uint8_t **pldm_resp_msg,
177 				   size_t *resp_msg_len)
178 {
179 	pldm_requester_rc_t rc = 0;
180 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)pldm_req_msg;
181 	if (hdr && !hdr->request) {
182 		return PLDM_REQUESTER_NOT_REQ_MSG;
183 	}
184 	PLDM_REQ_FN(eid, mctp_fd, pldm_transport_send_recv_msg, rc,
185 		    pldm_req_msg, req_msg_len, (void **)pldm_resp_msg,
186 		    resp_msg_len);
187 	if (rc != PLDM_REQUESTER_SUCCESS) {
188 		return rc;
189 	}
190 	hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
191 	if (hdr && (hdr->request || hdr->datagram)) {
192 		free(*pldm_resp_msg);
193 		*pldm_resp_msg = NULL;
194 		return PLDM_REQUESTER_NOT_RESP_MSG;
195 	}
196 	return rc;
197 }
198 
199 LIBPLDM_ABI_DEPRECATED
pldm_send(mctp_eid_t eid,int mctp_fd,const uint8_t * pldm_req_msg,size_t req_msg_len)200 pldm_requester_rc_t pldm_send(mctp_eid_t eid, int mctp_fd,
201 			      const uint8_t *pldm_req_msg, size_t req_msg_len)
202 {
203 	pldm_requester_rc_t rc = 0;
204 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)pldm_req_msg;
205 	if (!hdr->request) {
206 		return PLDM_REQUESTER_NOT_REQ_MSG;
207 	}
208 	PLDM_REQ_FN(eid, mctp_fd, pldm_transport_send_msg, rc,
209 		    (void *)pldm_req_msg, req_msg_len);
210 	return rc;
211 }
212 
213 /* Adding this here for completeness in the case we can't smoothly
214  * transition apps over to the new api */
215 LIBPLDM_ABI_DEPRECATED
pldm_close(void)216 void pldm_close(void)
217 {
218 	if (open_transport) {
219 		pldm_transport_mctp_demux_destroy(open_transport);
220 	}
221 	open_transport = NULL;
222 }
223