xref: /openbmc/libpldm/src/firmware_device/fd.c (revision 2628990be4f3355ff564dfe9ad0b2d57c9038013)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include <stdint.h>
3 #include <stdbool.h>
4 #include <string.h>
5 #include <stdlib.h>
6 
7 #include <libpldm/pldm.h>
8 #include <libpldm/firmware_update.h>
9 #include <libpldm/firmware_fd.h>
10 #include <libpldm/utils.h>
11 #include <compiler.h>
12 #include <msgbuf.h>
13 
14 #include "fd-internal.h"
15 
16 /* FD_T1 Update mode idle timeout, 120 seconds (range [60s, 120s])*/
17 static const pldm_fd_time_t DEFAULT_FD_T1_TIMEOUT = 120000;
18 
19 /* FD_T2 "Retry request for firmware data", 1 second (range [1s, 5s]) */
20 static const pldm_fd_time_t DEFAULT_FD_T2_RETRY_TIME = 1000;
21 
22 static const uint8_t INSTANCE_ID_COUNT = 32;
23 static const uint8_t PROGRESS_PERCENT_NOT_SUPPORTED = 101;
24 
25 #define PLDM_FD_VERSIONS_COUNT 2
26 static const uint32_t PLDM_FD_VERSIONS[PLDM_FD_VERSIONS_COUNT] = {
27 	/* Only PLDM Firmware 1.1.0 is current implemented. */
28 	0xf1f1f000,
29 	/* CRC. Calculated with python:
30 	hex(crccheck.crc.Crc32.calc(struct.pack('<I', 0xf1f1f000)))
31 	*/
32 	0x539dbeba,
33 };
34 const bitfield8_t PLDM_FD_COMMANDS[32] = {
35 	// 0x00..0x07
36 	{ .byte = (1 << PLDM_QUERY_DEVICE_IDENTIFIERS |
37 		   1 << PLDM_GET_FIRMWARE_PARAMETERS) },
38 	{ 0 },
39 	// 0x10..0x17
40 	{ .byte = (1u << PLDM_REQUEST_UPDATE | 1u << PLDM_PASS_COMPONENT_TABLE |
41 		   1u << PLDM_UPDATE_COMPONENT) >>
42 		  0x10 },
43 	// 0x18..0x1f
44 	{
45 		.byte = (1u << PLDM_ACTIVATE_FIRMWARE | 1u << PLDM_GET_STATUS |
46 			 1u << PLDM_CANCEL_UPDATE_COMPONENT |
47 			 1u << PLDM_CANCEL_UPDATE) >>
48 			0x18,
49 	},
50 };
51 
52 /* Ensure that public definition is kept updated */
53 static_assert(alignof(struct pldm_fd) == PLDM_ALIGNOF_PLDM_FD,
54 	      "PLDM_ALIGNOF_PLDM_FD wrong");
55 
56 /* Maybe called with success or failure completion codes, though
57  * success only makes sense for responses without a body.
58  * Returns 0 or negative errno. */
59 LIBPLDM_CC_NONNULL
pldm_fd_reply_cc(uint8_t ccode,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)60 static int pldm_fd_reply_cc(uint8_t ccode,
61 			    const struct pldm_header_info *req_hdr,
62 			    struct pldm_msg *resp, size_t *resp_payload_len)
63 {
64 	int status;
65 
66 	/* 1 byte completion code */
67 	if (*resp_payload_len < 1) {
68 		return -EOVERFLOW;
69 	}
70 	*resp_payload_len = 1;
71 
72 	status = encode_cc_only_resp(req_hdr->instance, PLDM_FWUP,
73 				     req_hdr->command, ccode, resp);
74 	if (status != PLDM_SUCCESS) {
75 		return -EINVAL;
76 	}
77 	return 0;
78 }
79 
80 /* Must be called with a negative errno.
81  * Returns 0 or negative errno. */
82 LIBPLDM_CC_NONNULL
pldm_fd_reply_errno(int err,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)83 static int pldm_fd_reply_errno(int err, const struct pldm_header_info *req_hdr,
84 			       struct pldm_msg *resp, size_t *resp_payload_len)
85 {
86 	uint8_t ccode = PLDM_ERROR;
87 
88 	assert(err < 0);
89 	switch (err) {
90 	case -EINVAL:
91 		// internal error, shouldn't occur.
92 		ccode = PLDM_ERROR;
93 		break;
94 	case -EPROTO:
95 		// Bad data from peer
96 		ccode = PLDM_ERROR_INVALID_DATA;
97 		break;
98 	case -EOVERFLOW:
99 	case -EBADMSG:
100 		// Bad data from peer
101 		ccode = PLDM_ERROR_INVALID_LENGTH;
102 		break;
103 	default:
104 		// general error
105 		ccode = PLDM_ERROR;
106 	}
107 
108 	return pldm_fd_reply_cc(ccode, req_hdr, resp, resp_payload_len);
109 }
110 
111 LIBPLDM_CC_NONNULL
pldm_fd_set_state(struct pldm_fd * fd,enum pldm_firmware_device_states state)112 static void pldm_fd_set_state(struct pldm_fd *fd,
113 			      enum pldm_firmware_device_states state)
114 {
115 	/* pldm_fd_set_idle should be used instead */
116 	assert(state != PLDM_FD_STATE_IDLE);
117 
118 	if (fd->state == state) {
119 		return;
120 	}
121 
122 	fd->prev_state = fd->state;
123 	fd->state = state;
124 }
125 
126 LIBPLDM_CC_NONNULL
pldm_fd_set_idle(struct pldm_fd * fd,enum pldm_get_status_reason_code_values reason)127 static void pldm_fd_set_idle(struct pldm_fd *fd,
128 			     enum pldm_get_status_reason_code_values reason)
129 {
130 	fd->prev_state = fd->state;
131 	fd->state = PLDM_FD_STATE_IDLE;
132 	fd->reason = reason;
133 	fd->ua_address_set = false;
134 }
135 
136 LIBPLDM_CC_NONNULL
pldm_fd_idle_timeout(struct pldm_fd * fd)137 static void pldm_fd_idle_timeout(struct pldm_fd *fd)
138 {
139 	enum pldm_get_status_reason_code_values reason = PLDM_FD_INITIALIZATION;
140 
141 	switch (fd->state) {
142 	case PLDM_FD_STATE_IDLE:
143 		return;
144 	case PLDM_FD_STATE_LEARN_COMPONENTS:
145 		reason = PLDM_FD_TIMEOUT_LEARN_COMPONENT;
146 		break;
147 	case PLDM_FD_STATE_READY_XFER:
148 		reason = PLDM_FD_TIMEOUT_READY_XFER;
149 		break;
150 	case PLDM_FD_STATE_DOWNLOAD:
151 		reason = PLDM_FD_TIMEOUT_DOWNLOAD;
152 		break;
153 	case PLDM_FD_STATE_VERIFY:
154 		reason = PLDM_FD_TIMEOUT_VERIFY;
155 		break;
156 	case PLDM_FD_STATE_APPLY:
157 		reason = PLDM_FD_TIMEOUT_APPLY;
158 		break;
159 	case PLDM_FD_STATE_ACTIVATE:
160 		/* Not a timeout */
161 		reason = PLDM_FD_ACTIVATE_FW;
162 		break;
163 	}
164 
165 	pldm_fd_set_idle(fd, reason);
166 }
167 
168 LIBPLDM_CC_NONNULL
pldm_fd_get_aux_state(const struct pldm_fd * fd,uint8_t * aux_state,uint8_t * aux_state_status)169 static void pldm_fd_get_aux_state(const struct pldm_fd *fd, uint8_t *aux_state,
170 				  uint8_t *aux_state_status)
171 {
172 	*aux_state_status = 0;
173 
174 	switch (fd->req.state) {
175 	case PLDM_FD_REQ_UNUSED:
176 		*aux_state = PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER;
177 		break;
178 	case PLDM_FD_REQ_SENT:
179 		*aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
180 		break;
181 	case PLDM_FD_REQ_READY:
182 		if (fd->req.complete) {
183 			*aux_state = PLDM_FD_OPERATION_SUCCESSFUL;
184 		} else {
185 			*aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
186 		}
187 		break;
188 	case PLDM_FD_REQ_FAILED:
189 		*aux_state = PLDM_FD_OPERATION_FAILED;
190 		*aux_state_status = fd->req.result;
191 		break;
192 	}
193 }
194 
195 LIBPLDM_CC_NONNULL
pldm_fd_now(struct pldm_fd * fd)196 static uint64_t pldm_fd_now(struct pldm_fd *fd)
197 {
198 	return fd->ops->now(fd->ops_ctx);
199 }
200 
201 LIBPLDM_CC_NONNULL
pldm_fd_req_should_send(struct pldm_fd * fd)202 static bool pldm_fd_req_should_send(struct pldm_fd *fd)
203 {
204 	pldm_fd_time_t now = pldm_fd_now(fd);
205 
206 	switch (fd->req.state) {
207 	case PLDM_FD_REQ_UNUSED:
208 		assert(false);
209 		return false;
210 	case PLDM_FD_REQ_READY:
211 		return true;
212 	case PLDM_FD_REQ_FAILED:
213 		return false;
214 	case PLDM_FD_REQ_SENT:
215 		if (now < fd->req.sent_time) {
216 			/* Time went backwards */
217 			return false;
218 		}
219 
220 		/* Send if retry time has elapsed */
221 		return (now - fd->req.sent_time) >= fd->fd_t2_retry_time;
222 	}
223 	return false;
224 }
225 
226 /* Allocate the next instance ID. Only one request is outstanding so cycling
227  * through the range is OK */
228 LIBPLDM_CC_NONNULL
pldm_fd_req_next_instance(struct pldm_fd_req * req)229 static uint8_t pldm_fd_req_next_instance(struct pldm_fd_req *req)
230 {
231 	req->instance_id = (req->instance_id + 1) % INSTANCE_ID_COUNT;
232 	return req->instance_id;
233 }
234 
235 LIBPLDM_CC_NONNULL
pldm_fd_qdi(struct pldm_fd * fd,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)236 static int pldm_fd_qdi(struct pldm_fd *fd, const struct pldm_header_info *hdr,
237 		       const struct pldm_msg *req LIBPLDM_CC_UNUSED,
238 		       size_t req_payload_len, struct pldm_msg *resp,
239 		       size_t *resp_payload_len)
240 {
241 	uint8_t descriptor_count;
242 	const struct pldm_descriptor *descriptors;
243 	int rc;
244 
245 	/* QDI has no request data */
246 	if (req_payload_len != PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES) {
247 		return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
248 					resp_payload_len);
249 	}
250 
251 	/* Retrieve platform-specific data */
252 	rc = fd->ops->device_identifiers(fd->ops_ctx, &descriptor_count,
253 					 &descriptors);
254 	if (rc) {
255 		return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
256 					resp_payload_len);
257 	}
258 
259 	rc = encode_query_device_identifiers_resp(hdr->instance,
260 						  descriptor_count, descriptors,
261 						  resp, resp_payload_len);
262 
263 	if (rc) {
264 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
265 	}
266 
267 	return 0;
268 }
269 
270 LIBPLDM_CC_NONNULL
pldm_fd_fw_param(struct pldm_fd * fd,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)271 static int pldm_fd_fw_param(struct pldm_fd *fd,
272 			    const struct pldm_header_info *hdr,
273 			    const struct pldm_msg *req LIBPLDM_CC_UNUSED,
274 			    size_t req_payload_len, struct pldm_msg *resp,
275 			    size_t *resp_payload_len)
276 {
277 	uint16_t entry_count;
278 	const struct pldm_firmware_component_standalone **entries;
279 	struct pldm_msgbuf _buf;
280 	struct pldm_msgbuf *buf = &_buf;
281 	int rc;
282 
283 	/* No request data */
284 	if (req_payload_len != PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES) {
285 		return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
286 					resp_payload_len);
287 	}
288 
289 	/* Retrieve platform-specific data */
290 	rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
291 	if (rc) {
292 		return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
293 					resp_payload_len);
294 	}
295 
296 	rc = pldm_msgbuf_init_errno(buf, 0, resp->payload, *resp_payload_len);
297 	if (rc) {
298 		return rc;
299 	}
300 
301 	/* Add the fixed parameters */
302 	{
303 		struct pldm_get_firmware_parameters_resp_full fwp = {
304 			.completion_code = PLDM_SUCCESS,
305 			// TODO defaulted to 0, could have a callback.
306 			.capabilities_during_update = { 0 },
307 			.comp_count = entry_count,
308 		};
309 		/* fill active and pending strings */
310 		rc = fd->ops->imageset_versions(
311 			fd->ops_ctx, &fwp.active_comp_image_set_ver_str,
312 			&fwp.pending_comp_image_set_ver_str);
313 		if (rc) {
314 			return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
315 						resp_payload_len);
316 		}
317 
318 		size_t len = buf->remaining;
319 		rc = encode_get_firmware_parameters_resp(hdr->instance, &fwp,
320 							 resp, &len);
321 		if (rc) {
322 			return pldm_fd_reply_errno(rc, hdr, resp,
323 						   resp_payload_len);
324 		}
325 		rc = pldm_msgbuf_skip(buf, len);
326 		if (rc) {
327 			return rc;
328 		}
329 	}
330 
331 	/* Add the component table entries */
332 	for (uint16_t i = 0; i < entry_count; i++) {
333 		const struct pldm_firmware_component_standalone *e = entries[i];
334 		void *out = NULL;
335 		size_t len;
336 
337 		struct pldm_component_parameter_entry_full comp = {
338 			.comp_classification = e->comp_classification,
339 			.comp_identifier = e->comp_identifier,
340 			.comp_classification_index =
341 				e->comp_classification_index,
342 
343 			.active_ver = e->active_ver,
344 			.pending_ver = e->pending_ver,
345 
346 			.comp_activation_methods = e->comp_activation_methods,
347 			.capabilities_during_update =
348 				e->capabilities_during_update,
349 		};
350 
351 		if (pldm_msgbuf_peek_remaining(buf, &out, &len)) {
352 			return rc;
353 		}
354 		rc = encode_get_firmware_parameters_resp_comp_entry(&comp, out,
355 								    &len);
356 		if (rc) {
357 			return pldm_fd_reply_errno(rc, hdr, resp,
358 						   resp_payload_len);
359 		}
360 		rc = pldm_msgbuf_skip(buf, len);
361 		if (rc) {
362 			return rc;
363 		}
364 	}
365 
366 	return pldm_msgbuf_destroy_used(buf, *resp_payload_len,
367 					resp_payload_len);
368 }
369 
370 LIBPLDM_CC_NONNULL
pldm_fd_request_update(struct pldm_fd * fd,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,uint8_t address)371 static int pldm_fd_request_update(struct pldm_fd *fd,
372 				  const struct pldm_header_info *hdr,
373 				  const struct pldm_msg *req,
374 				  size_t req_payload_len, struct pldm_msg *resp,
375 				  size_t *resp_payload_len, uint8_t address)
376 {
377 	struct pldm_request_update_req_full upd;
378 	const struct pldm_request_update_resp resp_data = {
379 		.fd_meta_data_len = 0,
380 		.fd_will_send_pkg_data = 0,
381 	};
382 	int rc;
383 
384 	if (fd->state != PLDM_FD_STATE_IDLE) {
385 		return pldm_fd_reply_cc(PLDM_FWUP_ALREADY_IN_UPDATE_MODE, hdr,
386 					resp, resp_payload_len);
387 	}
388 
389 	rc = decode_request_update_req(req, req_payload_len, &upd);
390 	if (rc) {
391 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
392 	}
393 
394 	/* No metadata nor pkg data */
395 	rc = encode_request_update_resp(hdr->instance, &resp_data, resp,
396 					resp_payload_len);
397 	if (rc) {
398 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
399 	}
400 
401 	// TODO pass num_of_comp and image_set_ver to application?
402 
403 	fd->max_transfer =
404 		fd->ops->transfer_size(fd->ops_ctx, upd.max_transfer_size);
405 	if (fd->max_transfer > upd.max_transfer_size) {
406 		/* Limit to UA's size */
407 		fd->max_transfer = upd.max_transfer_size;
408 	}
409 	if (fd->max_transfer < PLDM_FWUP_BASELINE_TRANSFER_SIZE) {
410 		/* Don't let it be zero, that will loop forever  */
411 		fd->max_transfer = PLDM_FWUP_BASELINE_TRANSFER_SIZE;
412 	}
413 	fd->ua_address = address;
414 	fd->ua_address_set = true;
415 
416 	pldm_fd_set_state(fd, PLDM_FD_STATE_LEARN_COMPONENTS);
417 
418 	return 0;
419 }
420 
421 /* wrapper around ops->cancel, will only run ops->cancel when a component update is
422  * active */
423 LIBPLDM_CC_NONNULL
pldm_fd_maybe_cancel_component(struct pldm_fd * fd)424 static void pldm_fd_maybe_cancel_component(struct pldm_fd *fd)
425 {
426 	bool cancel = false;
427 
428 	switch (fd->state) {
429 	case PLDM_FD_STATE_DOWNLOAD:
430 	case PLDM_FD_STATE_VERIFY:
431 		cancel = true;
432 		break;
433 	case PLDM_FD_STATE_APPLY:
434 		/* In apply state, once the application ops->apply() has completed
435 		 * successfully the component is no longer in update state.
436 		 * In that case the cancel should not be forwarded to the application.
437 		 * This can occur if a cancel is received while waiting for the
438 		 * response to a success ApplyComplete. */
439 		cancel = !(fd->req.complete &&
440 			   fd->req.result == PLDM_FWUP_APPLY_SUCCESS);
441 		break;
442 	default:
443 		break;
444 	}
445 
446 	if (cancel) {
447 		/* Call the platform handler for the current component in progress */
448 		fd->ops->cancel_update_component(fd->ops_ctx, &fd->update_comp);
449 	}
450 }
451 
452 /* Wrapper around ops->update_component() that first checks that the component
453  * is in the list returned from ops->components() */
454 LIBPLDM_CC_NONNULL
pldm_fd_check_update_component(struct pldm_fd * fd,bool update,const struct pldm_firmware_update_component * comp)455 static enum pldm_component_response_codes pldm_fd_check_update_component(
456 	struct pldm_fd *fd, bool update,
457 	const struct pldm_firmware_update_component *comp)
458 {
459 	bool found;
460 	uint16_t entry_count;
461 	int rc;
462 
463 	const struct pldm_firmware_component_standalone **entries;
464 	rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
465 	if (rc) {
466 		return PLDM_CRC_COMP_NOT_SUPPORTED;
467 	}
468 
469 	found = false;
470 	for (uint16_t i = 0; i < entry_count; i++) {
471 		if (entries[i]->comp_classification ==
472 			    comp->comp_classification &&
473 		    entries[i]->comp_identifier == comp->comp_identifier &&
474 		    entries[i]->comp_classification_index ==
475 			    comp->comp_classification_index) {
476 			found = true;
477 			break;
478 		}
479 	}
480 	if (found) {
481 		return fd->ops->update_component(fd->ops_ctx, update, comp);
482 	}
483 	return PLDM_CRC_COMP_NOT_SUPPORTED;
484 }
485 
486 LIBPLDM_CC_NONNULL
pldm_fd_pass_comp(struct pldm_fd * fd,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)487 static int pldm_fd_pass_comp(struct pldm_fd *fd,
488 			     const struct pldm_header_info *hdr,
489 			     const struct pldm_msg *req, size_t req_payload_len,
490 			     struct pldm_msg *resp, size_t *resp_payload_len)
491 {
492 	struct pldm_pass_component_table_req_full pcomp;
493 	uint8_t comp_response_code;
494 	int rc;
495 
496 	if (fd->state != PLDM_FD_STATE_LEARN_COMPONENTS) {
497 		return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
498 					hdr, resp, resp_payload_len);
499 	}
500 
501 	/* fd->update_comp is used as temporary storage during PassComponent validation */
502 	/* Some portions are unused for PassComponentTable */
503 	fd->update_comp.comp_image_size = 0;
504 	fd->update_comp.update_option_flags.value = 0;
505 
506 	rc = decode_pass_component_table_req(req, req_payload_len, &pcomp);
507 	if (rc) {
508 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
509 	}
510 
511 	fd->update_comp.comp_classification = pcomp.comp_classification;
512 	fd->update_comp.comp_identifier = pcomp.comp_identifier;
513 	fd->update_comp.comp_classification_index =
514 		pcomp.comp_classification_index;
515 	fd->update_comp.comp_comparison_stamp = pcomp.comp_comparison_stamp;
516 	memcpy(&fd->update_comp.version, &pcomp.version, sizeof(pcomp.version));
517 
518 	comp_response_code =
519 		pldm_fd_check_update_component(fd, false, &fd->update_comp);
520 
521 	const struct pldm_pass_component_table_resp resp_data = {
522 		/* Component Response Code is 0 for ComponentResponse, 1 otherwise */
523 		.comp_resp = (comp_response_code != 0),
524 		.comp_resp_code = comp_response_code,
525 	};
526 
527 	rc = encode_pass_component_table_resp(hdr->instance, &resp_data, resp,
528 					      resp_payload_len);
529 	if (rc) {
530 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
531 	}
532 
533 	if (pcomp.transfer_flag & PLDM_END) {
534 		pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
535 	}
536 
537 	return 0;
538 }
539 
540 LIBPLDM_CC_NONNULL
pldm_fd_update_comp(struct pldm_fd * fd,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)541 static int pldm_fd_update_comp(struct pldm_fd *fd,
542 			       const struct pldm_header_info *hdr,
543 			       const struct pldm_msg *req,
544 			       size_t req_payload_len, struct pldm_msg *resp,
545 			       size_t *resp_payload_len)
546 {
547 	struct pldm_update_component_req_full up;
548 	uint8_t comp_response_code;
549 	int rc;
550 
551 	if (fd->state != PLDM_FD_STATE_READY_XFER) {
552 		return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
553 					hdr, resp, resp_payload_len);
554 	}
555 
556 	rc = decode_update_component_req(req, req_payload_len, &up);
557 	if (rc) {
558 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
559 	}
560 
561 	/* Store update_comp to pass to further callbacks. This persists
562 	 * until the component update completes or is cancelled */
563 	fd->update_comp.comp_classification = up.comp_classification;
564 	fd->update_comp.comp_identifier = up.comp_identifier;
565 	fd->update_comp.comp_classification_index =
566 		up.comp_classification_index;
567 	fd->update_comp.comp_comparison_stamp = up.comp_comparison_stamp;
568 	fd->update_comp.comp_image_size = up.comp_image_size;
569 	fd->update_comp.update_option_flags = up.update_option_flags;
570 	memcpy(&fd->update_comp.version, &up.version, sizeof(up.version));
571 
572 	comp_response_code =
573 		pldm_fd_check_update_component(fd, true, &fd->update_comp);
574 
575 	// Mask to only the "Force Update" flag, others are not handled.
576 	bitfield32_t update_flags = {
577 		.bits.bit0 = fd->update_comp.update_option_flags.bits.bit0
578 	};
579 
580 	const struct pldm_update_component_resp resp_data = {
581 		/* Component Response Code is 0 for ComponentResponse, 1 otherwise */
582 		.comp_compatibility_resp = (comp_response_code != 0),
583 		.comp_compatibility_resp_code = comp_response_code,
584 		.update_option_flags_enabled = update_flags,
585 		.time_before_req_fw_data = 0,
586 	};
587 
588 	rc = encode_update_component_resp(hdr->instance, &resp_data, resp,
589 					  resp_payload_len);
590 	if (rc) {
591 		/* Encoding response failed */
592 		if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
593 			/* Inform the application of cancellation. Call it directly
594 			 * rather than going through pldm_fd_maybe_cancel_component() */
595 			fd->ops->cancel_update_component(fd->ops_ctx,
596 							 &fd->update_comp);
597 		}
598 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
599 	}
600 
601 	/* Set up download state */
602 	if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
603 		memset(&fd->specific, 0x0, sizeof(fd->specific));
604 		fd->update_flags = update_flags;
605 		fd->req.state = PLDM_FD_REQ_READY;
606 		fd->req.complete = false;
607 		pldm_fd_set_state(fd, PLDM_FD_STATE_DOWNLOAD);
608 	}
609 
610 	return 0;
611 }
612 
613 LIBPLDM_CC_NONNULL
pldm_fd_get_status(struct pldm_fd * fd,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)614 static int pldm_fd_get_status(struct pldm_fd *fd,
615 			      const struct pldm_header_info *hdr,
616 			      const struct pldm_msg *req LIBPLDM_CC_UNUSED,
617 			      size_t req_payload_len, struct pldm_msg *resp,
618 			      size_t *resp_payload_len)
619 {
620 	int rc;
621 
622 	/* No request data */
623 	if (req_payload_len != PLDM_GET_STATUS_REQ_BYTES) {
624 		return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
625 					resp_payload_len);
626 	}
627 
628 	/* Defaults */
629 	struct pldm_get_status_resp st = {
630 		.current_state = fd->state,
631 		.previous_state = fd->prev_state,
632 		.progress_percent = PROGRESS_PERCENT_NOT_SUPPORTED,
633 	};
634 
635 	pldm_fd_get_aux_state(fd, &st.aux_state, &st.aux_state_status);
636 
637 	switch (fd->state) {
638 	case PLDM_FD_STATE_IDLE:
639 		st.reason_code = fd->reason;
640 		break;
641 	case PLDM_FD_STATE_DOWNLOAD:
642 		if (fd->update_comp.comp_image_size > 0) {
643 			uint32_t one_percent =
644 				fd->update_comp.comp_image_size / 100;
645 			if (fd->update_comp.comp_image_size % 100 != 0) {
646 				one_percent += 1;
647 			}
648 			st.progress_percent =
649 				(fd->specific.download.offset / one_percent);
650 		}
651 		st.update_option_flags_enabled = fd->update_flags;
652 		break;
653 	case PLDM_FD_STATE_VERIFY:
654 		st.update_option_flags_enabled = fd->update_flags;
655 		st.progress_percent = fd->specific.verify.progress_percent;
656 		break;
657 	case PLDM_FD_STATE_APPLY:
658 		st.update_option_flags_enabled = fd->update_flags;
659 		st.progress_percent = fd->specific.apply.progress_percent;
660 		break;
661 	default:
662 		break;
663 	}
664 
665 	rc = encode_get_status_resp(hdr->instance, &st, resp, resp_payload_len);
666 	if (rc) {
667 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
668 	}
669 
670 	return 0;
671 }
672 
673 LIBPLDM_CC_NONNULL
pldm_fd_cancel_update_comp(struct pldm_fd * fd,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)674 static int pldm_fd_cancel_update_comp(
675 	struct pldm_fd *fd, const struct pldm_header_info *hdr,
676 	const struct pldm_msg *req LIBPLDM_CC_UNUSED, size_t req_payload_len,
677 	struct pldm_msg *resp, size_t *resp_payload_len)
678 {
679 	int rc;
680 
681 	/* No request data */
682 	if (req_payload_len != PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES) {
683 		return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
684 					resp_payload_len);
685 	}
686 
687 	switch (fd->state) {
688 	case PLDM_FD_STATE_DOWNLOAD:
689 	case PLDM_FD_STATE_VERIFY:
690 	case PLDM_FD_STATE_APPLY:
691 		break;
692 	default:
693 		return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
694 					resp_payload_len);
695 	}
696 
697 	/* No response payload */
698 	rc = pldm_fd_reply_cc(PLDM_SUCCESS, hdr, resp, resp_payload_len);
699 	if (rc) {
700 		return rc;
701 	}
702 
703 	pldm_fd_maybe_cancel_component(fd);
704 	pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
705 
706 	return 0;
707 }
708 
709 LIBPLDM_CC_NONNULL
pldm_fd_cancel_update(struct pldm_fd * fd,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)710 static int pldm_fd_cancel_update(struct pldm_fd *fd,
711 				 const struct pldm_header_info *hdr,
712 				 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
713 				 size_t req_payload_len, struct pldm_msg *resp,
714 				 size_t *resp_payload_len)
715 {
716 	int rc;
717 
718 	/* No request data */
719 	if (req_payload_len != PLDM_CANCEL_UPDATE_REQ_BYTES) {
720 		return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
721 					resp_payload_len);
722 	}
723 
724 	if (fd->state == PLDM_FD_STATE_IDLE) {
725 		return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
726 					resp_payload_len);
727 	}
728 
729 	/* Assume non_functioning_component_indication = False, in future
730 	 * could add a platform callback */
731 	const struct pldm_cancel_update_resp resp_data = {
732 		.non_functioning_component_indication = 0,
733 		.non_functioning_component_bitmap = 0,
734 	};
735 	rc = encode_cancel_update_resp(hdr->instance, &resp_data, resp,
736 				       resp_payload_len);
737 	if (rc) {
738 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
739 	}
740 
741 	pldm_fd_maybe_cancel_component(fd);
742 	pldm_fd_set_idle(fd, PLDM_FD_CANCEL_UPDATE);
743 
744 	return 0;
745 }
746 
747 LIBPLDM_CC_NONNULL
pldm_fd_activate_firmware(struct pldm_fd * fd,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)748 static int pldm_fd_activate_firmware(struct pldm_fd *fd,
749 				     const struct pldm_header_info *hdr,
750 				     const struct pldm_msg *req,
751 				     size_t req_payload_len,
752 				     struct pldm_msg *resp,
753 				     size_t *resp_payload_len)
754 {
755 	uint16_t estimated_time;
756 	uint8_t ccode;
757 	int rc;
758 	bool self_contained;
759 
760 	rc = decode_activate_firmware_req(req, req_payload_len,
761 					  &self_contained);
762 	if (rc) {
763 		return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
764 	}
765 
766 	if (fd->state != PLDM_FD_STATE_READY_XFER) {
767 		return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
768 					hdr, resp, resp_payload_len);
769 	}
770 
771 	estimated_time = 0;
772 	ccode = fd->ops->activate(fd->ops_ctx, self_contained, &estimated_time);
773 
774 	if (ccode == PLDM_SUCCESS ||
775 	    ccode == PLDM_FWUP_ACTIVATION_NOT_REQUIRED) {
776 		/* Transition through states so that the prev_state is correct */
777 		pldm_fd_set_state(fd, PLDM_FD_STATE_ACTIVATE);
778 		pldm_fd_set_idle(fd, PLDM_FD_ACTIVATE_FW);
779 	}
780 
781 	if (ccode == PLDM_SUCCESS) {
782 		const struct pldm_activate_firmware_resp resp_data = {
783 			.estimated_time_activation = estimated_time,
784 		};
785 		rc = encode_activate_firmware_resp(hdr->instance, &resp_data,
786 						   resp, resp_payload_len);
787 		if (rc) {
788 			return pldm_fd_reply_errno(rc, hdr, resp,
789 						   resp_payload_len);
790 		}
791 	} else {
792 		return pldm_fd_reply_cc(ccode, hdr, resp, resp_payload_len);
793 	}
794 
795 	return 0;
796 }
797 
798 LIBPLDM_CC_NONNULL
pldm_fd_fwdata_size(struct pldm_fd * fd)799 static uint32_t pldm_fd_fwdata_size(struct pldm_fd *fd)
800 {
801 	uint32_t size;
802 
803 	if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
804 		assert(false);
805 		return 0;
806 	}
807 
808 	if (fd->specific.download.offset > fd->update_comp.comp_image_size) {
809 		assert(false);
810 		return 0;
811 	}
812 	size = fd->update_comp.comp_image_size - fd->specific.download.offset;
813 
814 	if (size > fd->max_transfer) {
815 		size = fd->max_transfer;
816 	}
817 	return size;
818 }
819 
820 LIBPLDM_CC_NONNULL
pldm_fd_handle_fwdata_resp(struct pldm_fd * fd,const struct pldm_msg * resp,size_t resp_payload_len)821 static int pldm_fd_handle_fwdata_resp(struct pldm_fd *fd,
822 				      const struct pldm_msg *resp,
823 				      size_t resp_payload_len)
824 {
825 	struct pldm_fd_download *dl;
826 	uint32_t fwdata_size;
827 	uint8_t res;
828 
829 	if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
830 		return -EPROTO;
831 	}
832 
833 	if (fd->req.state != PLDM_FD_REQ_SENT) {
834 		/* Not waiting for a response, ignore it */
835 		return -EPROTO;
836 	}
837 
838 	dl = &fd->specific.download;
839 	if (fd->req.complete) {
840 		/* Received data after completion */
841 		return -EPROTO;
842 	}
843 
844 	switch (resp->payload[0]) {
845 	case PLDM_SUCCESS:
846 		break;
847 	case PLDM_FWUP_RETRY_REQUEST_FW_DATA:
848 		/* Just return, let the retry timer send another request later */
849 		return 0;
850 	default:
851 		/* Send a TransferComplete failure */
852 		fd->req.state = PLDM_FD_REQ_READY;
853 		fd->req.complete = true;
854 		fd->req.result = PLDM_FWUP_FD_ABORTED_TRANSFER;
855 		return 0;
856 	}
857 
858 	/* Handle the received data */
859 
860 	fwdata_size = pldm_fd_fwdata_size(fd);
861 	if (resp_payload_len != fwdata_size + 1) {
862 		/* Data is incorrect size. Could indicate MCTP corruption, drop it
863 		 * and let retry timer handle it */
864 		return -EOVERFLOW;
865 	}
866 
867 	/* Check pldm_fd_fwdata_size calculation, should not fail */
868 	if (dl->offset + fwdata_size < dl->offset ||
869 	    dl->offset + fwdata_size > fd->update_comp.comp_image_size) {
870 		assert(false);
871 		return -EINVAL;
872 	}
873 
874 	/* Provide the data chunk to the device */
875 	res = fd->ops->firmware_data(fd->ops_ctx, dl->offset, &resp->payload[1],
876 				     fwdata_size, &fd->update_comp);
877 
878 	fd->req.state = PLDM_FD_REQ_READY;
879 	if (res == PLDM_FWUP_TRANSFER_SUCCESS) {
880 		/* Move to next offset */
881 		dl->offset += fwdata_size;
882 		if (dl->offset == fd->update_comp.comp_image_size) {
883 			/* Mark as complete, next progress() call will send the TransferComplete request */
884 			fd->req.complete = true;
885 			fd->req.result = PLDM_FWUP_TRANSFER_SUCCESS;
886 		}
887 	} else {
888 		/* Pass the callback error as the TransferResult */
889 		fd->req.complete = true;
890 		fd->req.result = res;
891 	}
892 
893 	return 0;
894 }
895 
896 LIBPLDM_CC_NONNULL
pldm_fd_handle_transfer_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)897 static int pldm_fd_handle_transfer_complete_resp(
898 	struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
899 	size_t resp_payload_len LIBPLDM_CC_UNUSED)
900 {
901 	if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
902 		return -EPROTO;
903 	}
904 
905 	if (fd->req.state != PLDM_FD_REQ_SENT) {
906 		/* Not waiting for a response, ignore it */
907 		return -EPROTO;
908 	}
909 
910 	if (!fd->req.complete) {
911 		/* Were waiting for RequestFirmwareData instead, ignore it */
912 		return -EPROTO;
913 	}
914 
915 	/* Disregard the response completion code */
916 
917 	/* Next state depends whether the transfer succeeded */
918 	if (fd->req.result == PLDM_FWUP_TRANSFER_SUCCESS) {
919 		/* Switch to Verify */
920 		memset(&fd->specific, 0x0, sizeof(fd->specific));
921 		fd->specific.verify.progress_percent =
922 			PROGRESS_PERCENT_NOT_SUPPORTED;
923 		fd->req.state = PLDM_FD_REQ_READY;
924 		fd->req.complete = false;
925 		pldm_fd_set_state(fd, PLDM_FD_STATE_VERIFY);
926 	} else {
927 		/* Wait for UA to cancel */
928 		fd->req.state = PLDM_FD_REQ_FAILED;
929 	}
930 	return 0;
931 }
932 
933 LIBPLDM_CC_NONNULL
pldm_fd_handle_verify_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)934 static int pldm_fd_handle_verify_complete_resp(
935 	struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
936 	size_t resp_payload_len LIBPLDM_CC_UNUSED)
937 {
938 	if (fd->state != PLDM_FD_STATE_VERIFY) {
939 		return -EPROTO;
940 	}
941 
942 	if (fd->req.state != PLDM_FD_REQ_SENT) {
943 		/* Not waiting for a response, ignore it */
944 		return -EPROTO;
945 	}
946 
947 	assert(fd->req.complete);
948 
949 	/* Disregard the response completion code */
950 
951 	/* Next state depends whether the verify succeeded */
952 	if (fd->req.result == PLDM_FWUP_VERIFY_SUCCESS) {
953 		/* Switch to Apply */
954 		memset(&fd->specific, 0x0, sizeof(fd->specific));
955 		fd->specific.apply.progress_percent =
956 			PROGRESS_PERCENT_NOT_SUPPORTED;
957 		fd->req.state = PLDM_FD_REQ_READY;
958 		fd->req.complete = false;
959 		pldm_fd_set_state(fd, PLDM_FD_STATE_APPLY);
960 	} else {
961 		/* Wait for UA to cancel */
962 		fd->req.state = PLDM_FD_REQ_FAILED;
963 	}
964 	return 0;
965 }
966 
967 LIBPLDM_CC_NONNULL
pldm_fd_handle_apply_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)968 static int pldm_fd_handle_apply_complete_resp(
969 	struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
970 	size_t resp_payload_len LIBPLDM_CC_UNUSED)
971 {
972 	if (fd->state != PLDM_FD_STATE_APPLY) {
973 		return -EPROTO;
974 	}
975 
976 	if (fd->req.state != PLDM_FD_REQ_SENT) {
977 		/* Not waiting for a response, ignore it */
978 		return -EPROTO;
979 	}
980 
981 	assert(fd->req.complete);
982 
983 	/* Disregard the response completion code */
984 
985 	/* Next state depends whether the apply succeeded */
986 	if (fd->req.result == PLDM_FWUP_APPLY_SUCCESS) {
987 		/* Switch to ReadyXfer */
988 		fd->req.state = PLDM_FD_REQ_UNUSED;
989 		pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
990 	} else {
991 		/* Wait for UA to cancel */
992 		fd->req.state = PLDM_FD_REQ_FAILED;
993 	}
994 	return 0;
995 }
996 
997 LIBPLDM_CC_NONNULL
pldm_fd_handle_resp(struct pldm_fd * fd,pldm_tid_t address,const void * resp_msg,size_t resp_len)998 static int pldm_fd_handle_resp(struct pldm_fd *fd, pldm_tid_t address,
999 			       const void *resp_msg, size_t resp_len)
1000 {
1001 	size_t resp_payload_len;
1002 	const struct pldm_msg *resp = resp_msg;
1003 
1004 	if (!(fd->ua_address_set && fd->ua_address == address)) {
1005 		// Either an early response, or a response from a wrong TID */
1006 		return -EBUSY;
1007 	}
1008 
1009 	/* Must have a ccode */
1010 	if (resp_len < sizeof(struct pldm_msg_hdr) + 1) {
1011 		return -EINVAL;
1012 	}
1013 	resp_payload_len = resp_len - sizeof(struct pldm_msg_hdr);
1014 
1015 	if (fd->req.state != PLDM_FD_REQ_SENT) {
1016 		// No response was expected
1017 		return -EPROTO;
1018 	}
1019 
1020 	if (fd->req.instance_id != resp->hdr.instance_id) {
1021 		// Response wasn't for the expected request
1022 		return -EPROTO;
1023 	}
1024 	if (fd->req.command != resp->hdr.command) {
1025 		// Response wasn't for the expected request
1026 		return -EPROTO;
1027 	}
1028 
1029 	fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1030 
1031 	switch (resp->hdr.command) {
1032 	case PLDM_REQUEST_FIRMWARE_DATA:
1033 		return pldm_fd_handle_fwdata_resp(fd, resp, resp_payload_len);
1034 		break;
1035 	case PLDM_TRANSFER_COMPLETE:
1036 		return pldm_fd_handle_transfer_complete_resp(fd, resp,
1037 							     resp_payload_len);
1038 		break;
1039 	case PLDM_VERIFY_COMPLETE:
1040 		return pldm_fd_handle_verify_complete_resp(fd, resp,
1041 							   resp_payload_len);
1042 		break;
1043 	case PLDM_APPLY_COMPLETE:
1044 		return pldm_fd_handle_apply_complete_resp(fd, resp,
1045 							  resp_payload_len);
1046 		break;
1047 	default:
1048 		/* Unsolicited response. Already compared to command above */
1049 		assert(false);
1050 		return -EINVAL;
1051 	}
1052 }
1053 
1054 LIBPLDM_CC_NONNULL
pldm_fd_progress_download(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1055 static int pldm_fd_progress_download(struct pldm_fd *fd, struct pldm_msg *req,
1056 				     size_t *req_payload_len)
1057 {
1058 	uint8_t instance_id;
1059 	struct pldm_fd_download *dl;
1060 	int rc;
1061 
1062 	if (!pldm_fd_req_should_send(fd)) {
1063 		/* Nothing to do */
1064 		*req_payload_len = 0;
1065 		return 0;
1066 	}
1067 
1068 	instance_id = pldm_fd_req_next_instance(&fd->req);
1069 	dl = &fd->specific.download;
1070 	if (fd->req.complete) {
1071 		/* Send TransferComplete */
1072 		rc = encode_transfer_complete_req(instance_id, fd->req.result,
1073 						  req, req_payload_len);
1074 	} else {
1075 		/* Send a new RequestFirmwareData */
1076 		const struct pldm_request_firmware_data_req req_params = {
1077 			.offset = dl->offset,
1078 			.length = pldm_fd_fwdata_size(fd),
1079 		};
1080 
1081 		rc = encode_request_firmware_data_req(instance_id, &req_params,
1082 						      req, req_payload_len);
1083 	}
1084 
1085 	if (rc) {
1086 		return rc;
1087 	}
1088 
1089 	/* Wait for response */
1090 	fd->req.state = PLDM_FD_REQ_SENT;
1091 	fd->req.instance_id = req->hdr.instance_id;
1092 	fd->req.command = req->hdr.command;
1093 	fd->req.sent_time = pldm_fd_now(fd);
1094 
1095 	return 0;
1096 }
1097 
1098 LIBPLDM_CC_NONNULL
pldm_fd_progress_verify(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1099 static int pldm_fd_progress_verify(struct pldm_fd *fd, struct pldm_msg *req,
1100 				   size_t *req_payload_len)
1101 {
1102 	uint8_t instance_id;
1103 	int rc;
1104 
1105 	if (!pldm_fd_req_should_send(fd)) {
1106 		/* Nothing to do */
1107 		*req_payload_len = 0;
1108 		return 0;
1109 	}
1110 
1111 	if (!fd->req.complete) {
1112 		bool pending = false;
1113 		uint8_t res;
1114 		res = fd->ops->verify(fd->ops_ctx, &fd->update_comp, &pending,
1115 				      &fd->specific.verify.progress_percent);
1116 		if (pending) {
1117 			if (res == PLDM_FWUP_VERIFY_SUCCESS) {
1118 				/* Return without a VerifyComplete request.
1119 				* Will call verify() again on next call */
1120 				*req_payload_len = 0;
1121 				return 0;
1122 			}
1123 			/* This is an API infraction by the implementer, return a distinctive failure */
1124 			res = PLDM_FWUP_VENDOR_VERIFY_RESULT_RANGE_MAX;
1125 		}
1126 		fd->req.result = res;
1127 		fd->req.complete = true;
1128 	}
1129 
1130 	instance_id = pldm_fd_req_next_instance(&fd->req);
1131 	rc = encode_verify_complete_req(instance_id, fd->req.result, req,
1132 					req_payload_len);
1133 	if (rc) {
1134 		return rc;
1135 	}
1136 
1137 	/* Wait for response */
1138 	fd->req.state = PLDM_FD_REQ_SENT;
1139 	fd->req.instance_id = req->hdr.instance_id;
1140 	fd->req.command = req->hdr.command;
1141 	fd->req.sent_time = pldm_fd_now(fd);
1142 
1143 	return 0;
1144 }
1145 
1146 LIBPLDM_CC_NONNULL
pldm_fd_progress_apply(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1147 static int pldm_fd_progress_apply(struct pldm_fd *fd, struct pldm_msg *req,
1148 				  size_t *req_payload_len)
1149 {
1150 	uint8_t instance_id;
1151 	int rc;
1152 
1153 	if (!pldm_fd_req_should_send(fd)) {
1154 		/* Nothing to do */
1155 		*req_payload_len = 0;
1156 		return 0;
1157 	}
1158 
1159 	if (!fd->req.complete) {
1160 		bool pending = false;
1161 		uint8_t res;
1162 		res = fd->ops->apply(fd->ops_ctx, &fd->update_comp, &pending,
1163 				     &fd->specific.apply.progress_percent);
1164 		if (pending) {
1165 			if (res == PLDM_FWUP_APPLY_SUCCESS) {
1166 				/* Return without a ApplyComplete request.
1167 				* Will call apply() again on next call */
1168 				*req_payload_len = 0;
1169 				return 0;
1170 			}
1171 			/* This is an API infraction by the implementer, return a distinctive failure */
1172 			res = PLDM_FWUP_VENDOR_APPLY_RESULT_RANGE_MAX;
1173 		}
1174 		fd->req.result = res;
1175 		fd->req.complete = true;
1176 		if (fd->req.result ==
1177 		    PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD) {
1178 			/* modified activation method isn't currently handled */
1179 			fd->req.result = PLDM_FWUP_APPLY_SUCCESS;
1180 		}
1181 	}
1182 
1183 	instance_id = pldm_fd_req_next_instance(&fd->req);
1184 	const struct pldm_apply_complete_req req_data = {
1185 		.apply_result = fd->req.result,
1186 		.comp_activation_methods_modification = { 0 },
1187 	};
1188 	rc = encode_apply_complete_req(instance_id, &req_data, req,
1189 				       req_payload_len);
1190 	if (rc) {
1191 		return rc;
1192 	}
1193 
1194 	/* Wait for response */
1195 	fd->req.state = PLDM_FD_REQ_SENT;
1196 	fd->req.instance_id = req->hdr.instance_id;
1197 	fd->req.command = req->hdr.command;
1198 	fd->req.sent_time = pldm_fd_now(fd);
1199 
1200 	return 0;
1201 }
1202 
1203 LIBPLDM_ABI_TESTING
pldm_fd_new(const struct pldm_fd_ops * ops,void * ops_ctx,struct pldm_control * control)1204 struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
1205 			    struct pldm_control *control)
1206 {
1207 	struct pldm_fd *fd = malloc(sizeof(*fd));
1208 	if (fd) {
1209 		if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx, control) ==
1210 		    0) {
1211 			return fd;
1212 		}
1213 		free(fd);
1214 		fd = NULL;
1215 	}
1216 	return fd;
1217 }
1218 
1219 LIBPLDM_ABI_TESTING
pldm_fd_setup(struct pldm_fd * fd,size_t pldm_fd_size,const struct pldm_fd_ops * ops,void * ops_ctx,struct pldm_control * control)1220 int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
1221 		  const struct pldm_fd_ops *ops, void *ops_ctx,
1222 		  struct pldm_control *control)
1223 {
1224 	int rc;
1225 
1226 	if (fd == NULL || ops == NULL) {
1227 		return -EINVAL;
1228 	}
1229 
1230 	if (ops->device_identifiers == NULL || ops->components == NULL ||
1231 	    ops->imageset_versions == NULL || ops->update_component == NULL ||
1232 	    ops->transfer_size == NULL || ops->firmware_data == NULL ||
1233 	    ops->verify == NULL || ops->activate == NULL ||
1234 	    ops->cancel_update_component == NULL || ops->now == NULL) {
1235 		return -EINVAL;
1236 	}
1237 
1238 	if (pldm_fd_size < sizeof(struct pldm_fd)) {
1239 		/* Safety check that sufficient storage was provided for *fd,
1240 		 * in case PLDM_SIZEOF_PLDM_FD is incorrect */
1241 		return -EINVAL;
1242 	}
1243 	memset(fd, 0x0, sizeof(*fd));
1244 	fd->ops = ops;
1245 	fd->ops_ctx = ops_ctx;
1246 	fd->fd_t1_timeout = DEFAULT_FD_T1_TIMEOUT;
1247 	fd->fd_t2_retry_time = DEFAULT_FD_T2_RETRY_TIME;
1248 
1249 	if (control) {
1250 		rc = pldm_control_add_type(control, PLDM_FWUP,
1251 					   &PLDM_FD_VERSIONS,
1252 					   PLDM_FD_VERSIONS_COUNT,
1253 					   PLDM_FD_COMMANDS);
1254 		if (rc) {
1255 			return rc;
1256 		}
1257 	}
1258 
1259 	return 0;
1260 }
1261 
1262 LIBPLDM_ABI_TESTING
pldm_fd_handle_msg(struct pldm_fd * fd,pldm_tid_t remote_address,const void * in_msg,size_t in_len,void * out_msg,size_t * out_len)1263 int pldm_fd_handle_msg(struct pldm_fd *fd, pldm_tid_t remote_address,
1264 		       const void *in_msg, size_t in_len, void *out_msg,
1265 		       size_t *out_len)
1266 {
1267 	size_t req_payload_len;
1268 	size_t resp_payload_len;
1269 	struct pldm_header_info hdr;
1270 	const struct pldm_msg *req = in_msg;
1271 	struct pldm_msg *resp = out_msg;
1272 	uint8_t rc;
1273 
1274 	if (fd == NULL || in_msg == NULL || out_msg == NULL ||
1275 	    out_len == NULL) {
1276 		return -EINVAL;
1277 	}
1278 
1279 	if (in_len < sizeof(struct pldm_msg_hdr)) {
1280 		return -EOVERFLOW;
1281 	}
1282 	req_payload_len = in_len - sizeof(struct pldm_msg_hdr);
1283 
1284 	rc = unpack_pldm_header(&req->hdr, &hdr);
1285 	if (rc != PLDM_SUCCESS) {
1286 		return -EINVAL;
1287 	}
1288 
1289 	if (hdr.pldm_type != PLDM_FWUP) {
1290 		/* Caller should not have passed non-pldmfw */
1291 		return -ENOMSG;
1292 	}
1293 
1294 	if (hdr.msg_type == PLDM_RESPONSE) {
1295 		*out_len = 0;
1296 		return pldm_fd_handle_resp(fd, remote_address, in_msg, in_len);
1297 	}
1298 
1299 	if (hdr.msg_type != PLDM_REQUEST) {
1300 		return -EPROTO;
1301 	}
1302 
1303 	/* Space for header plus completion code */
1304 	if (*out_len < sizeof(struct pldm_msg_hdr) + 1) {
1305 		return -EOVERFLOW;
1306 	}
1307 	resp_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1308 
1309 	/* Check address */
1310 	switch (hdr.command) {
1311 	/* Information or cancel commands are always allowed */
1312 	case PLDM_QUERY_DEVICE_IDENTIFIERS:
1313 	case PLDM_GET_FIRMWARE_PARAMETERS:
1314 	case PLDM_GET_STATUS:
1315 	case PLDM_CANCEL_UPDATE:
1316 	case PLDM_QUERY_DOWNSTREAM_DEVICES:
1317 	case PLDM_QUERY_DOWNSTREAM_IDENTIFIERS:
1318 	case PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS:
1319 	/* Request Update handler will set address */
1320 	case PLDM_REQUEST_UPDATE:
1321 		break;
1322 	default:
1323 		/* Requests must come from the same address that requested the update */
1324 		if (!fd->ua_address_set || remote_address != fd->ua_address) {
1325 			return pldm_fd_reply_cc(PLDM_ERROR_NOT_READY, &hdr,
1326 						resp, &resp_payload_len);
1327 		}
1328 	}
1329 
1330 	/* Update timeout */
1331 	switch (hdr.command) {
1332 	case PLDM_REQUEST_UPDATE:
1333 	case PLDM_PASS_COMPONENT_TABLE:
1334 	case PLDM_UPDATE_COMPONENT:
1335 	case PLDM_CANCEL_UPDATE:
1336 		fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1337 		break;
1338 	default:
1339 		break;
1340 	}
1341 
1342 	/* Dispatch command.
1343 	 Update PLDM_FD_COMMANDS if adding new handlers */
1344 	switch (hdr.command) {
1345 	case PLDM_QUERY_DEVICE_IDENTIFIERS:
1346 		rc = pldm_fd_qdi(fd, &hdr, req, req_payload_len, resp,
1347 				 &resp_payload_len);
1348 		break;
1349 	case PLDM_GET_FIRMWARE_PARAMETERS:
1350 		rc = pldm_fd_fw_param(fd, &hdr, req, req_payload_len, resp,
1351 				      &resp_payload_len);
1352 		break;
1353 	case PLDM_REQUEST_UPDATE:
1354 		rc = pldm_fd_request_update(fd, &hdr, req, req_payload_len,
1355 					    resp, &resp_payload_len,
1356 					    remote_address);
1357 		break;
1358 	case PLDM_PASS_COMPONENT_TABLE:
1359 		rc = pldm_fd_pass_comp(fd, &hdr, req, req_payload_len, resp,
1360 				       &resp_payload_len);
1361 		break;
1362 	case PLDM_UPDATE_COMPONENT:
1363 		rc = pldm_fd_update_comp(fd, &hdr, req, req_payload_len, resp,
1364 					 &resp_payload_len);
1365 		break;
1366 	case PLDM_GET_STATUS:
1367 		rc = pldm_fd_get_status(fd, &hdr, req, req_payload_len, resp,
1368 					&resp_payload_len);
1369 		break;
1370 	case PLDM_CANCEL_UPDATE_COMPONENT:
1371 		rc = pldm_fd_cancel_update_comp(fd, &hdr, req, req_payload_len,
1372 						resp, &resp_payload_len);
1373 		break;
1374 	case PLDM_CANCEL_UPDATE:
1375 		rc = pldm_fd_cancel_update(fd, &hdr, req, req_payload_len, resp,
1376 					   &resp_payload_len);
1377 		break;
1378 	case PLDM_ACTIVATE_FIRMWARE:
1379 		rc = pldm_fd_activate_firmware(fd, &hdr, req, req_payload_len,
1380 					       resp, &resp_payload_len);
1381 		break;
1382 	default:
1383 		rc = pldm_fd_reply_cc(PLDM_ERROR_UNSUPPORTED_PLDM_CMD, &hdr,
1384 				      resp, &resp_payload_len);
1385 	}
1386 
1387 	if (rc == 0) {
1388 		*out_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
1389 	}
1390 
1391 	return rc;
1392 }
1393 
1394 LIBPLDM_ABI_TESTING
pldm_fd_progress(struct pldm_fd * fd,void * out_msg,size_t * out_len,pldm_tid_t * address)1395 int pldm_fd_progress(struct pldm_fd *fd, void *out_msg, size_t *out_len,
1396 		     pldm_tid_t *address)
1397 {
1398 	size_t req_payload_len;
1399 	struct pldm_msg *req = out_msg;
1400 	int rc = -EINVAL;
1401 	bool ua_timeout_check = false;
1402 
1403 	if (fd == NULL || out_msg == NULL || out_len == NULL) {
1404 		return -EINVAL;
1405 	}
1406 
1407 	/* Space for header */
1408 	if (*out_len < sizeof(struct pldm_msg_hdr)) {
1409 		return -EOVERFLOW;
1410 	}
1411 	req_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1412 	*out_len = 0;
1413 
1414 	// Handle FD-driven states
1415 	switch (fd->state) {
1416 	case PLDM_FD_STATE_DOWNLOAD:
1417 		rc = pldm_fd_progress_download(fd, req, &req_payload_len);
1418 		break;
1419 	case PLDM_FD_STATE_VERIFY:
1420 		rc = pldm_fd_progress_verify(fd, req, &req_payload_len);
1421 		break;
1422 	case PLDM_FD_STATE_APPLY:
1423 		rc = pldm_fd_progress_apply(fd, req, &req_payload_len);
1424 		break;
1425 	default:
1426 		req_payload_len = 0;
1427 		break;
1428 	}
1429 
1430 	// Cancel update if expected UA message isn't received within timeout
1431 	switch (fd->state) {
1432 	case PLDM_FD_STATE_DOWNLOAD:
1433 	case PLDM_FD_STATE_VERIFY:
1434 	case PLDM_FD_STATE_APPLY:
1435 		// FD-driven states will time out if a response isn't received
1436 		ua_timeout_check = (fd->req.state == PLDM_FD_REQ_SENT);
1437 		break;
1438 	case PLDM_FD_STATE_IDLE:
1439 		ua_timeout_check = false;
1440 		break;
1441 	default:
1442 		// Other Update Mode states have a timeout for the UA to
1443 		// send a request
1444 		ua_timeout_check = true;
1445 	}
1446 
1447 	if (ua_timeout_check) {
1448 		if ((pldm_fd_now(fd) - fd->update_timestamp_fd_t1) >
1449 		    fd->fd_t1_timeout) {
1450 			pldm_fd_maybe_cancel_component(fd);
1451 			pldm_fd_idle_timeout(fd);
1452 			req_payload_len = 0;
1453 		}
1454 	}
1455 
1456 	if (rc == 0 && fd->ua_address_set && req_payload_len > 0) {
1457 		*out_len = req_payload_len + sizeof(struct pldm_msg_hdr);
1458 		*address = fd->ua_address;
1459 	}
1460 
1461 	return rc;
1462 }
1463 
1464 LIBPLDM_ABI_TESTING
pldm_fd_set_update_idle_timeout(struct pldm_fd * fd,uint32_t time)1465 int pldm_fd_set_update_idle_timeout(struct pldm_fd *fd, uint32_t time)
1466 {
1467 	if (fd == NULL) {
1468 		return -EINVAL;
1469 	}
1470 
1471 	fd->fd_t1_timeout = time;
1472 	return 0;
1473 }
1474 
1475 LIBPLDM_ABI_TESTING
pldm_fd_set_request_retry_time(struct pldm_fd * fd,uint32_t time)1476 int pldm_fd_set_request_retry_time(struct pldm_fd *fd, uint32_t time)
1477 {
1478 	if (fd == NULL) {
1479 		return -EINVAL;
1480 	}
1481 
1482 	fd->fd_t2_retry_time = time;
1483 	return 0;
1484 }
1485