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