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