xref: /openbmc/libpldm/src/requester/pldm.c (revision 43a7985d)
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 = 1;                                              \
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 	PLDM_REQ_FN(eid, mctp_fd, pldm_transport_recv_msg, rc,
103 		    (void **)pldm_resp_msg, resp_msg_len);
104 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
105 	if (rc != PLDM_REQUESTER_SUCCESS) {
106 		return rc;
107 	}
108 	if (hdr && (hdr->request || hdr->datagram)) {
109 		free(*pldm_resp_msg);
110 		*pldm_resp_msg = NULL;
111 		return PLDM_REQUESTER_NOT_RESP_MSG;
112 	}
113 	uint8_t pldm_cc = 0;
114 	if (*resp_msg_len < (sizeof(struct pldm_msg_hdr) + sizeof(pldm_cc))) {
115 		free(*pldm_resp_msg);
116 		*pldm_resp_msg = NULL;
117 		return PLDM_REQUESTER_RESP_MSG_TOO_SMALL;
118 	}
119 	return rc;
120 }
121 
122 LIBPLDM_ABI_STABLE
123 pldm_requester_rc_t pldm_recv(mctp_eid_t eid, int mctp_fd,
124 			      __attribute__((unused)) uint8_t instance_id,
125 			      uint8_t **pldm_resp_msg, size_t *resp_msg_len)
126 {
127 	pldm_requester_rc_t rc =
128 		pldm_recv_any(eid, mctp_fd, pldm_resp_msg, resp_msg_len);
129 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
130 	if (rc == PLDM_REQUESTER_SUCCESS && hdr &&
131 	    hdr->instance_id != instance_id) {
132 		free(*pldm_resp_msg);
133 		*pldm_resp_msg = NULL;
134 		return PLDM_REQUESTER_INSTANCE_ID_MISMATCH;
135 	}
136 	return rc;
137 }
138 
139 LIBPLDM_ABI_STABLE
140 pldm_requester_rc_t pldm_send_recv(mctp_eid_t eid, int mctp_fd,
141 				   const uint8_t *pldm_req_msg,
142 				   size_t req_msg_len, uint8_t **pldm_resp_msg,
143 				   size_t *resp_msg_len)
144 {
145 	pldm_requester_rc_t rc = 0;
146 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)pldm_req_msg;
147 	if (hdr && !hdr->request) {
148 		return PLDM_REQUESTER_NOT_REQ_MSG;
149 	}
150 	PLDM_REQ_FN(eid, mctp_fd, pldm_transport_send_recv_msg, rc,
151 		    pldm_req_msg, req_msg_len, (void **)pldm_resp_msg,
152 		    resp_msg_len);
153 	if (rc != PLDM_REQUESTER_SUCCESS) {
154 		return rc;
155 	}
156 	hdr = (struct pldm_msg_hdr *)(*pldm_resp_msg);
157 	if (hdr && (hdr->request || hdr->datagram)) {
158 		free(*pldm_resp_msg);
159 		*pldm_resp_msg = NULL;
160 		return PLDM_REQUESTER_NOT_RESP_MSG;
161 	}
162 	return rc;
163 }
164 
165 LIBPLDM_ABI_STABLE
166 pldm_requester_rc_t pldm_send(mctp_eid_t eid, int mctp_fd,
167 			      const uint8_t *pldm_req_msg, size_t req_msg_len)
168 {
169 	pldm_requester_rc_t rc = 0;
170 	struct pldm_msg_hdr *hdr = (struct pldm_msg_hdr *)pldm_req_msg;
171 	if (!hdr->request) {
172 		return PLDM_REQUESTER_NOT_REQ_MSG;
173 	}
174 	PLDM_REQ_FN(eid, mctp_fd, pldm_transport_send_msg, rc,
175 		    (void *)pldm_req_msg, req_msg_len);
176 	return rc;
177 }
178 
179 /* Adding this here for completeness in the case we can't smoothly
180  * transition apps over to the new api */
181 LIBPLDM_ABI_TESTING
182 void pldm_close(void)
183 {
184 	if (open_transport) {
185 		pldm_transport_mctp_demux_destroy(open_transport);
186 	}
187 	open_transport = NULL;
188 }
189