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