xref: /openbmc/libpldm/src/control.c (revision bde874e9c057b537da85f5fe96a2c436639fc4a2)
1 #include <stdint.h>
2 #include <stdbool.h>
3 #include <string.h>
4 
5 #include <libpldm/pldm.h>
6 #include <libpldm/utils.h>
7 #include <libpldm/platform.h>
8 #include <libpldm/control.h>
9 #include <compiler.h>
10 #include <msgbuf.h>
11 
12 #include "control-internal.h"
13 
14 #define PLDM_BASE_VERSIONS_COUNT 2
15 static const uint32_t PLDM_BASE_VERSIONS[PLDM_BASE_VERSIONS_COUNT] = {
16 	/* PLDM 1.1.0 is current implemented. */
17 	0xf1f1f000,
18 	/* CRC. Calculated with python:
19 	hex(crccheck.crc.Crc32.calc(struct.pack('<I', 0xf1f1f000)))
20 	*/
21 	0x539dbeba,
22 };
23 const bitfield8_t PLDM_CONTROL_COMMANDS[32] = {
24 	// 0x00..0x07
25 	{ .byte = (1 << PLDM_GET_TID | 1 << PLDM_GET_PLDM_VERSION |
26 		   1 << PLDM_GET_PLDM_TYPES | 1 << PLDM_GET_PLDM_COMMANDS) }
27 };
28 
pldm_control_reply_error(uint8_t ccode,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)29 static int pldm_control_reply_error(uint8_t ccode,
30 				    const struct pldm_header_info *req_hdr,
31 				    struct pldm_msg *resp,
32 				    size_t *resp_payload_len)
33 {
34 	int rc;
35 
36 	/* 1 byte completion code */
37 	if (*resp_payload_len < 1) {
38 		return -EOVERFLOW;
39 	}
40 	*resp_payload_len = 1;
41 
42 	rc = encode_cc_only_resp(req_hdr->instance, PLDM_FWUP, req_hdr->command,
43 				 ccode, resp);
44 	if (rc != PLDM_SUCCESS) {
45 		return -EINVAL;
46 	}
47 	return 0;
48 }
49 
pldm_control_get_tid(const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)50 static int pldm_control_get_tid(const struct pldm_header_info *hdr,
51 				const struct pldm_msg *req LIBPLDM_CC_UNUSED,
52 				size_t req_payload_len, struct pldm_msg *resp,
53 				size_t *resp_payload_len)
54 {
55 	if (req_payload_len != PLDM_GET_TID_REQ_BYTES) {
56 		return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr,
57 						resp, resp_payload_len);
58 	}
59 
60 	if (*resp_payload_len <= PLDM_GET_TID_RESP_BYTES) {
61 		return -EOVERFLOW;
62 	}
63 	*resp_payload_len = PLDM_GET_TID_RESP_BYTES;
64 
65 	uint8_t cc = encode_get_tid_resp(hdr->instance, PLDM_SUCCESS,
66 					 PLDM_TID_UNASSIGNED, resp);
67 	if (cc) {
68 		return pldm_control_reply_error(cc, hdr, resp,
69 						resp_payload_len);
70 	}
71 	return 0;
72 }
73 
pldm_control_get_version(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)74 static int pldm_control_get_version(struct pldm_control *control,
75 				    const struct pldm_header_info *hdr,
76 				    const struct pldm_msg *req,
77 				    size_t req_payload_len,
78 				    struct pldm_msg *resp,
79 				    size_t *resp_payload_len)
80 {
81 	uint8_t cc;
82 
83 	uint32_t handle;
84 	uint8_t opflag;
85 	uint8_t type;
86 	cc = decode_get_version_req(req, req_payload_len, &handle, &opflag,
87 				    &type);
88 	if (cc) {
89 		return pldm_control_reply_error(cc, hdr, resp,
90 						resp_payload_len);
91 	}
92 
93 	/* Response is always sent as a single transfer */
94 	if (opflag != PLDM_GET_FIRSTPART) {
95 		return pldm_control_reply_error(
96 			PLDM_CONTROL_INVALID_TRANSFER_OPERATION_FLAG, hdr, resp,
97 			resp_payload_len);
98 	}
99 
100 	const struct pldm_type_versions *v = NULL;
101 	for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
102 		if (control->types[i].pldm_type == type &&
103 		    control->types[i].versions) {
104 			v = &control->types[i];
105 			break;
106 		}
107 	}
108 
109 	if (!v) {
110 		return pldm_control_reply_error(
111 			PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr,
112 			resp, resp_payload_len);
113 	}
114 
115 	/* encode_get_version_resp doesn't have length checking */
116 	uint32_t required_resp_payload =
117 		1 + 4 + 1 + v->versions_count * sizeof(ver32_t);
118 	if (*resp_payload_len < required_resp_payload) {
119 		return -EOVERFLOW;
120 	}
121 	*resp_payload_len = required_resp_payload;
122 
123 	/* crc32 is included in the versions buffer */
124 	cc = encode_get_version_resp(hdr->instance, PLDM_SUCCESS, 0,
125 				     PLDM_START_AND_END, v->versions,
126 				     v->versions_count * sizeof(ver32_t), resp);
127 	if (cc) {
128 		return pldm_control_reply_error(cc, hdr, resp,
129 						resp_payload_len);
130 	}
131 	return 0;
132 }
133 
pldm_control_get_types(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)134 static int pldm_control_get_types(struct pldm_control *control,
135 				  const struct pldm_header_info *hdr,
136 				  const struct pldm_msg *req LIBPLDM_CC_UNUSED,
137 				  size_t req_payload_len, struct pldm_msg *resp,
138 				  size_t *resp_payload_len)
139 {
140 	uint8_t cc;
141 
142 	if (req_payload_len != PLDM_GET_TYPES_REQ_BYTES) {
143 		return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr,
144 						resp, resp_payload_len);
145 	}
146 
147 	bitfield8_t types[8];
148 	memset(types, 0, sizeof(types));
149 	for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
150 		uint8_t ty = control->types[i].pldm_type;
151 		if (ty < 64 && control->types[i].versions) {
152 			uint8_t bit = 1 << (ty % 8);
153 			types[ty / 8].byte |= bit;
154 		}
155 	}
156 
157 	/* encode_get_types_resp doesn't have length checking */
158 	uint32_t required_resp_payload = 1 + 8;
159 	if (*resp_payload_len < required_resp_payload) {
160 		return -EOVERFLOW;
161 	}
162 	*resp_payload_len = required_resp_payload;
163 
164 	cc = encode_get_types_resp(hdr->instance, PLDM_SUCCESS, types, resp);
165 	if (cc) {
166 		return pldm_control_reply_error(cc, hdr, resp,
167 						resp_payload_len);
168 	}
169 	return 0;
170 }
171 
pldm_control_get_commands(struct pldm_control * control,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)172 static int pldm_control_get_commands(struct pldm_control *control,
173 				     const struct pldm_header_info *hdr,
174 				     const struct pldm_msg *req,
175 				     size_t req_payload_len,
176 				     struct pldm_msg *resp,
177 				     size_t *resp_payload_len)
178 {
179 	uint8_t cc;
180 
181 	uint8_t ty;
182 	// version in request is ignored, since SelectPLDMVersion isn't supported currently
183 	ver32_t version;
184 	cc = decode_get_commands_req(req, req_payload_len, &ty, &version);
185 	if (cc) {
186 		return pldm_control_reply_error(cc, hdr, resp,
187 						resp_payload_len);
188 	}
189 
190 	const struct pldm_type_versions *v = NULL;
191 	for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
192 		if (control->types[i].pldm_type == ty &&
193 		    control->types[i].versions && control->types[i].commands) {
194 			v = &control->types[i];
195 			break;
196 		}
197 	}
198 
199 	if (!v) {
200 		return pldm_control_reply_error(
201 			PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr,
202 			resp, resp_payload_len);
203 	}
204 
205 	/* encode_get_commands_resp doesn't have length checking */
206 	uint32_t required_resp_payload = 1 + 32;
207 	if (*resp_payload_len < required_resp_payload) {
208 		return -EOVERFLOW;
209 	}
210 	*resp_payload_len = required_resp_payload;
211 
212 	cc = encode_get_commands_resp(hdr->instance, PLDM_SUCCESS, v->commands,
213 				      resp);
214 	if (cc) {
215 		return pldm_control_reply_error(cc, hdr, resp,
216 						resp_payload_len);
217 	}
218 	return 0;
219 }
220 
221 /* A response should only be used when this returns 0, and *resp_len > 0 */
222 LIBPLDM_ABI_TESTING
pldm_control_handle_msg(struct pldm_control * control,const void * req_msg,size_t req_len,void * resp_msg,size_t * resp_len)223 int pldm_control_handle_msg(struct pldm_control *control, const void *req_msg,
224 			    size_t req_len, void *resp_msg, size_t *resp_len)
225 {
226 	int rc;
227 
228 	/* Space for header plus completion code */
229 	if (*resp_len < sizeof(struct pldm_msg_hdr) + 1) {
230 		return -EOVERFLOW;
231 	}
232 	size_t resp_payload_len = *resp_len - sizeof(struct pldm_msg_hdr);
233 	struct pldm_msg *resp = resp_msg;
234 
235 	if (req_len < sizeof(struct pldm_msg_hdr)) {
236 		return -EOVERFLOW;
237 	}
238 	size_t req_payload_len = req_len - sizeof(struct pldm_msg_hdr);
239 	const struct pldm_msg *req = req_msg;
240 
241 	struct pldm_header_info hdr;
242 	rc = unpack_pldm_header(&req->hdr, &hdr);
243 	if (rc != PLDM_SUCCESS) {
244 		return -EINVAL;
245 	}
246 
247 	if (hdr.pldm_type != PLDM_BASE) {
248 		/* Caller should not have passed non-control */
249 		return -ENOMSG;
250 	}
251 
252 	if (hdr.msg_type != PLDM_REQUEST) {
253 		return -EINVAL;
254 	}
255 
256 	/* Dispatch command */
257 	switch (hdr.command) {
258 	case PLDM_GET_TID:
259 		rc = pldm_control_get_tid(&hdr, req, req_payload_len, resp,
260 					  &resp_payload_len);
261 		break;
262 	case PLDM_GET_PLDM_VERSION:
263 		rc = pldm_control_get_version(control, &hdr, req,
264 					      req_payload_len, resp,
265 					      &resp_payload_len);
266 		break;
267 	case PLDM_GET_PLDM_TYPES:
268 		rc = pldm_control_get_types(control, &hdr, req, req_payload_len,
269 					    resp, &resp_payload_len);
270 		break;
271 	case PLDM_GET_PLDM_COMMANDS:
272 		rc = pldm_control_get_commands(control, &hdr, req,
273 					       req_payload_len, resp,
274 					       &resp_payload_len);
275 		break;
276 	default:
277 		rc = pldm_control_reply_error(PLDM_ERROR_UNSUPPORTED_PLDM_CMD,
278 					      &hdr, resp, &resp_payload_len);
279 	}
280 
281 	if (rc == 0) {
282 		*resp_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
283 	}
284 
285 	return rc;
286 }
287 
288 LIBPLDM_ABI_TESTING
pldm_control_setup(struct pldm_control * control,size_t pldm_control_size)289 int pldm_control_setup(struct pldm_control *control, size_t pldm_control_size)
290 {
291 	int rc;
292 
293 	if (pldm_control_size < sizeof(struct pldm_control)) {
294 		return -EINVAL;
295 	}
296 
297 	memset(control, 0, sizeof(struct pldm_control));
298 
299 	rc = pldm_control_add_type(control, PLDM_BASE, &PLDM_BASE_VERSIONS,
300 				   PLDM_BASE_VERSIONS_COUNT,
301 				   PLDM_CONTROL_COMMANDS);
302 	if (rc) {
303 		return rc;
304 	}
305 
306 	return 0;
307 }
308 
309 LIBPLDM_ABI_TESTING
pldm_control_add_type(struct pldm_control * control,uint8_t pldm_type,const void * versions,size_t versions_count,const bitfield8_t * commands)310 int pldm_control_add_type(struct pldm_control *control, uint8_t pldm_type,
311 			  const void *versions, size_t versions_count,
312 			  const bitfield8_t *commands)
313 {
314 	if (versions_count < 2) {
315 		/* At least one version must be provided, along with a CRC32 */
316 		return -EINVAL;
317 	}
318 
319 	struct pldm_type_versions *v = NULL;
320 	for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) {
321 		if (control->types[i].versions == NULL ||
322 		    (control->types[i].versions != NULL &&
323 		     control->types[i].pldm_type == pldm_type)) {
324 			v = &control->types[i];
325 			break;
326 		}
327 	}
328 
329 	if (!v) {
330 		return -ENOMEM;
331 		// No spare slots
332 	}
333 
334 	v->pldm_type = pldm_type;
335 	v->versions = versions;
336 	v->versions_count = versions_count;
337 	v->commands = commands;
338 
339 	return 0;
340 }
341