xref: /openbmc/libpldm/src/dsp/fru.c (revision 82c34815)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include <libpldm/base.h>
3 #include <libpldm/fru.h>
4 #include <libpldm/utils.h>
5 
6 #include <assert.h>
7 #include <endian.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <string.h>
11 
12 LIBPLDM_ABI_STABLE
13 int encode_get_fru_record_table_metadata_req(uint8_t instance_id,
14 					     struct pldm_msg *msg,
15 					     size_t payload_length)
16 {
17 	if (msg == NULL) {
18 		return PLDM_ERROR_INVALID_DATA;
19 	}
20 
21 	if (payload_length != PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES) {
22 		return PLDM_ERROR_INVALID_LENGTH;
23 	}
24 
25 	struct pldm_header_info header = { 0 };
26 	header.instance = instance_id;
27 	header.msg_type = PLDM_REQUEST;
28 	header.pldm_type = PLDM_FRU;
29 	header.command = PLDM_GET_FRU_RECORD_TABLE_METADATA;
30 
31 	return pack_pldm_header(&header, &(msg->hdr));
32 }
33 
34 LIBPLDM_ABI_STABLE
35 int decode_get_fru_record_table_metadata_resp(
36 	const struct pldm_msg *msg, size_t payload_length,
37 	uint8_t *completion_code, uint8_t *fru_data_major_version,
38 	uint8_t *fru_data_minor_version, uint32_t *fru_table_maximum_size,
39 	uint32_t *fru_table_length, uint16_t *total_record_set_identifiers,
40 	uint16_t *total_table_records, uint32_t *checksum)
41 {
42 	if (msg == NULL || completion_code == NULL ||
43 	    fru_data_major_version == NULL || fru_data_minor_version == NULL ||
44 	    fru_table_maximum_size == NULL || fru_table_length == NULL ||
45 	    total_record_set_identifiers == NULL ||
46 	    total_table_records == NULL || checksum == NULL) {
47 		return PLDM_ERROR_INVALID_DATA;
48 	}
49 
50 	*completion_code = msg->payload[0];
51 	if (PLDM_SUCCESS != *completion_code) {
52 		return PLDM_SUCCESS;
53 	}
54 
55 	if (payload_length != PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES) {
56 		return PLDM_ERROR_INVALID_LENGTH;
57 	}
58 
59 	struct pldm_get_fru_record_table_metadata_resp *response =
60 		(struct pldm_get_fru_record_table_metadata_resp *)msg->payload;
61 
62 	*fru_data_major_version = response->fru_data_major_version;
63 	*fru_data_minor_version = response->fru_data_minor_version;
64 	*fru_table_maximum_size = le32toh(response->fru_table_maximum_size);
65 	*fru_table_length = le32toh(response->fru_table_length);
66 	*total_record_set_identifiers =
67 		le16toh(response->total_record_set_identifiers);
68 	*total_table_records = le16toh(response->total_table_records);
69 	*checksum = le32toh(response->checksum);
70 
71 	return PLDM_SUCCESS;
72 }
73 
74 LIBPLDM_ABI_STABLE
75 int encode_get_fru_record_table_metadata_resp(
76 	uint8_t instance_id, uint8_t completion_code,
77 	uint8_t fru_data_major_version, uint8_t fru_data_minor_version,
78 	uint32_t fru_table_maximum_size, uint32_t fru_table_length,
79 	uint16_t total_record_set_identifiers, uint16_t total_table_records,
80 	uint32_t checksum, struct pldm_msg *msg)
81 {
82 	if (msg == NULL) {
83 		return PLDM_ERROR_INVALID_DATA;
84 	}
85 
86 	struct pldm_header_info header = { 0 };
87 	header.msg_type = PLDM_RESPONSE;
88 	header.instance = instance_id;
89 	header.pldm_type = PLDM_FRU;
90 	header.command = PLDM_GET_FRU_RECORD_TABLE_METADATA;
91 
92 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
93 	if (PLDM_SUCCESS != rc) {
94 		return rc;
95 	}
96 
97 	struct pldm_get_fru_record_table_metadata_resp *response =
98 		(struct pldm_get_fru_record_table_metadata_resp *)msg->payload;
99 	response->completion_code = completion_code;
100 	if (response->completion_code == PLDM_SUCCESS) {
101 		response->fru_data_major_version = fru_data_major_version;
102 		response->fru_data_minor_version = fru_data_minor_version;
103 		response->fru_table_maximum_size =
104 			htole32(fru_table_maximum_size);
105 		response->fru_table_length = htole32(fru_table_length);
106 		response->total_record_set_identifiers =
107 			htole16(total_record_set_identifiers);
108 		response->total_table_records = htole16(total_table_records);
109 		response->checksum = htole32(checksum);
110 	}
111 
112 	return PLDM_SUCCESS;
113 }
114 
115 LIBPLDM_ABI_STABLE
116 int decode_get_fru_record_table_req(const struct pldm_msg *msg,
117 				    size_t payload_length,
118 				    uint32_t *data_transfer_handle,
119 				    uint8_t *transfer_operation_flag)
120 {
121 	if (msg == NULL || data_transfer_handle == NULL ||
122 	    transfer_operation_flag == NULL) {
123 		return PLDM_ERROR_INVALID_DATA;
124 	}
125 
126 	if (payload_length != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES) {
127 		return PLDM_ERROR_INVALID_LENGTH;
128 	}
129 
130 	struct pldm_get_fru_record_table_req *req =
131 		(struct pldm_get_fru_record_table_req *)msg->payload;
132 
133 	*data_transfer_handle = le32toh(req->data_transfer_handle);
134 	*transfer_operation_flag = req->transfer_operation_flag;
135 
136 	return PLDM_SUCCESS;
137 }
138 
139 LIBPLDM_ABI_STABLE
140 int encode_get_fru_record_table_resp(uint8_t instance_id,
141 				     uint8_t completion_code,
142 				     uint32_t next_data_transfer_handle,
143 				     uint8_t transfer_flag,
144 				     struct pldm_msg *msg)
145 {
146 	if (msg == NULL) {
147 		return PLDM_ERROR_INVALID_DATA;
148 	}
149 
150 	struct pldm_header_info header = { 0 };
151 	header.msg_type = PLDM_RESPONSE;
152 	header.instance = instance_id;
153 	header.pldm_type = PLDM_FRU;
154 	header.command = PLDM_GET_FRU_RECORD_TABLE;
155 
156 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
157 	if (rc > PLDM_SUCCESS) {
158 		return rc;
159 	}
160 
161 	struct pldm_get_fru_record_table_resp *resp =
162 		(struct pldm_get_fru_record_table_resp *)msg->payload;
163 
164 	resp->completion_code = completion_code;
165 
166 	if (resp->completion_code == PLDM_SUCCESS) {
167 		resp->next_data_transfer_handle =
168 			htole32(next_data_transfer_handle);
169 		resp->transfer_flag = transfer_flag;
170 	}
171 
172 	return PLDM_SUCCESS;
173 }
174 
175 LIBPLDM_ABI_STABLE
176 int encode_fru_record(uint8_t *fru_table, size_t total_size, size_t *curr_size,
177 		      uint16_t record_set_id, uint8_t record_type,
178 		      uint8_t num_frus, uint8_t encoding, uint8_t *tlvs,
179 		      size_t tlvs_size)
180 {
181 	size_t record_hdr_size = sizeof(struct pldm_fru_record_data_format) -
182 				 sizeof(struct pldm_fru_record_tlv);
183 
184 	if (fru_table == NULL || curr_size == NULL || !tlvs_size) {
185 		return PLDM_ERROR_INVALID_DATA;
186 	}
187 	if ((*curr_size + record_hdr_size + tlvs_size) != total_size) {
188 		return PLDM_ERROR_INVALID_LENGTH;
189 	}
190 
191 	struct pldm_fru_record_data_format *record =
192 		(struct pldm_fru_record_data_format *)(fru_table + *curr_size);
193 	record->record_set_id = htole16(record_set_id);
194 	record->record_type = record_type;
195 	record->num_fru_fields = num_frus;
196 	record->encoding_type = encoding;
197 	*curr_size += record_hdr_size;
198 
199 	if (tlvs) {
200 		memcpy(fru_table + *curr_size, tlvs, tlvs_size);
201 		*curr_size += tlvs_size;
202 	}
203 
204 	return PLDM_SUCCESS;
205 }
206 
207 static bool is_table_end(const struct pldm_fru_record_data_format *p,
208 			 const void *table, size_t table_size)
209 {
210 	return p >=
211 	       (const struct pldm_fru_record_data_format *)((uint8_t *)table +
212 							    table_size);
213 }
214 
215 LIBPLDM_ABI_STABLE
216 int get_fru_record_by_option(const uint8_t *table, size_t table_size,
217 			     uint8_t *record_table, size_t *record_size,
218 			     uint16_t rsi, uint8_t rt, uint8_t ft)
219 {
220 	const struct pldm_fru_record_data_format *record_data_src =
221 		(const struct pldm_fru_record_data_format *)table;
222 	struct pldm_fru_record_data_format *record_data_dest;
223 	int count = 0;
224 
225 	const struct pldm_fru_record_tlv *tlv;
226 	size_t len;
227 	uint8_t *pos = record_table;
228 
229 	while (!is_table_end(record_data_src, table, table_size)) {
230 		if ((record_data_src->record_set_id != htole16(rsi) &&
231 		     rsi != 0) ||
232 		    (record_data_src->record_type != rt && rt != 0)) {
233 			tlv = record_data_src->tlvs;
234 			for (int i = 0; i < record_data_src->num_fru_fields;
235 			     i++) {
236 				len = sizeof(*tlv) - 1 + tlv->length;
237 				tlv = (const struct pldm_fru_record_tlv
238 					       *)((char *)tlv + len);
239 			}
240 			record_data_src =
241 				(const struct pldm_fru_record_data_format
242 					 *)(tlv);
243 			continue;
244 		}
245 
246 		len = sizeof(struct pldm_fru_record_data_format) -
247 		      sizeof(struct pldm_fru_record_tlv);
248 
249 		if (pos - record_table + len >= *record_size) {
250 			return PLDM_ERROR_INVALID_LENGTH;
251 		}
252 		memcpy(pos, record_data_src, len);
253 
254 		record_data_dest = (struct pldm_fru_record_data_format *)pos;
255 		pos += len;
256 
257 		tlv = record_data_src->tlvs;
258 		count = 0;
259 		for (int i = 0; i < record_data_src->num_fru_fields; i++) {
260 			len = sizeof(*tlv) - 1 + tlv->length;
261 			if (tlv->type == ft || ft == 0) {
262 				if (pos - record_table + len >= *record_size) {
263 					return PLDM_ERROR_INVALID_LENGTH;
264 				}
265 				memcpy(pos, tlv, len);
266 				pos += len;
267 				count++;
268 			}
269 			tlv = (const struct pldm_fru_record_tlv *)((char *)tlv +
270 								   len);
271 		}
272 		record_data_dest->num_fru_fields = count;
273 		record_data_src =
274 			(const struct pldm_fru_record_data_format *)(tlv);
275 	}
276 
277 	*record_size = pos - record_table;
278 
279 	return PLDM_SUCCESS;
280 }
281 
282 LIBPLDM_ABI_STABLE
283 int encode_get_fru_record_by_option_req(
284 	uint8_t instance_id, uint32_t data_transfer_handle,
285 	uint16_t fru_table_handle, uint16_t record_set_identifier,
286 	uint8_t record_type, uint8_t field_type, uint8_t transfer_op_flag,
287 	struct pldm_msg *msg, size_t payload_length)
288 {
289 	if (msg == NULL) {
290 		return PLDM_ERROR_INVALID_DATA;
291 	}
292 
293 	if (payload_length !=
294 	    sizeof(struct pldm_get_fru_record_by_option_req)) {
295 		return PLDM_ERROR_INVALID_LENGTH;
296 	}
297 
298 	struct pldm_header_info header = { 0 };
299 	header.instance = instance_id;
300 	header.msg_type = PLDM_REQUEST;
301 	header.pldm_type = PLDM_FRU;
302 	header.command = PLDM_GET_FRU_RECORD_BY_OPTION;
303 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
304 	if (rc != PLDM_SUCCESS) {
305 		return rc;
306 	}
307 
308 	struct pldm_get_fru_record_by_option_req *req =
309 		(struct pldm_get_fru_record_by_option_req *)msg->payload;
310 
311 	req->data_transfer_handle = htole32(data_transfer_handle);
312 	req->fru_table_handle = htole16(fru_table_handle);
313 	req->record_set_identifier = htole16(record_set_identifier);
314 	req->record_type = record_type;
315 	req->field_type = field_type;
316 	req->transfer_op_flag = transfer_op_flag;
317 
318 	return PLDM_SUCCESS;
319 }
320 
321 LIBPLDM_ABI_STABLE
322 int decode_get_fru_record_by_option_req(
323 	const struct pldm_msg *msg, size_t payload_length,
324 	uint32_t *data_transfer_handle, uint16_t *fru_table_handle,
325 	uint16_t *record_set_identifier, uint8_t *record_type,
326 	uint8_t *field_type, uint8_t *transfer_op_flag)
327 {
328 	if (msg == NULL || data_transfer_handle == NULL ||
329 	    fru_table_handle == NULL || record_set_identifier == NULL ||
330 	    record_type == NULL || field_type == NULL ||
331 	    transfer_op_flag == NULL) {
332 		return PLDM_ERROR_INVALID_DATA;
333 	}
334 
335 	if (payload_length !=
336 	    sizeof(struct pldm_get_fru_record_by_option_req)) {
337 		return PLDM_ERROR_INVALID_LENGTH;
338 	}
339 
340 	struct pldm_get_fru_record_by_option_req *req =
341 		(struct pldm_get_fru_record_by_option_req *)msg->payload;
342 
343 	*data_transfer_handle = le32toh(req->data_transfer_handle);
344 	*fru_table_handle = le16toh(req->fru_table_handle);
345 	*record_set_identifier = le16toh(req->record_set_identifier);
346 	*record_type = req->record_type;
347 	*field_type = req->field_type;
348 	*transfer_op_flag = req->transfer_op_flag;
349 	return PLDM_SUCCESS;
350 }
351 
352 LIBPLDM_ABI_STABLE
353 int encode_get_fru_record_by_option_resp(uint8_t instance_id,
354 					 uint8_t completion_code,
355 					 uint32_t next_data_transfer_handle,
356 					 uint8_t transfer_flag,
357 					 const void *fru_structure_data,
358 					 size_t data_size, struct pldm_msg *msg,
359 					 size_t payload_length)
360 {
361 	if (msg == NULL || fru_structure_data == NULL) {
362 		return PLDM_ERROR_INVALID_DATA;
363 	}
364 
365 	if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) {
366 		return PLDM_ERROR_INVALID_LENGTH;
367 	}
368 
369 	if (payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES <
370 	    data_size) {
371 		return PLDM_ERROR_INVALID_LENGTH;
372 	}
373 
374 	struct pldm_header_info header = { 0 };
375 	header.instance = instance_id;
376 	header.msg_type = PLDM_RESPONSE;
377 	header.pldm_type = PLDM_FRU;
378 	header.command = PLDM_GET_FRU_RECORD_BY_OPTION;
379 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
380 	if (rc != PLDM_SUCCESS) {
381 		return rc;
382 	}
383 
384 	struct pldm_get_fru_record_by_option_resp *resp =
385 		(struct pldm_get_fru_record_by_option_resp *)msg->payload;
386 
387 	resp->completion_code = completion_code;
388 	resp->next_data_transfer_handle = htole32(next_data_transfer_handle);
389 	resp->transfer_flag = transfer_flag;
390 
391 	if (completion_code == PLDM_SUCCESS) {
392 		memcpy(resp->fru_structure_data, fru_structure_data, data_size);
393 	}
394 
395 	return PLDM_SUCCESS;
396 }
397 
398 LIBPLDM_ABI_STABLE
399 int decode_get_fru_record_by_option_resp(
400 	const struct pldm_msg *msg, size_t payload_length,
401 	uint8_t *completion_code, uint32_t *next_transfer_handle,
402 	uint8_t *transfer_flag, struct variable_field *fru_structure_data)
403 {
404 	if (msg == NULL || completion_code == NULL ||
405 	    next_transfer_handle == NULL || transfer_flag == NULL ||
406 	    fru_structure_data == NULL) {
407 		return PLDM_ERROR_INVALID_DATA;
408 	}
409 
410 	*completion_code = msg->payload[0];
411 	if (PLDM_SUCCESS != *completion_code) {
412 		return PLDM_SUCCESS;
413 	}
414 
415 	if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) {
416 		return PLDM_ERROR_INVALID_LENGTH;
417 	}
418 
419 	struct pldm_get_fru_record_by_option_resp *resp =
420 		(struct pldm_get_fru_record_by_option_resp *)msg->payload;
421 
422 	*next_transfer_handle = le32toh(resp->next_data_transfer_handle);
423 	*transfer_flag = resp->transfer_flag;
424 	fru_structure_data->ptr = resp->fru_structure_data;
425 	fru_structure_data->length =
426 		payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES;
427 
428 	return PLDM_SUCCESS;
429 }
430 
431 LIBPLDM_ABI_STABLE
432 int encode_get_fru_record_table_req(uint8_t instance_id,
433 				    uint32_t data_transfer_handle,
434 				    uint8_t transfer_operation_flag,
435 				    struct pldm_msg *msg, size_t payload_length)
436 
437 {
438 	if (msg == NULL) {
439 		return PLDM_ERROR_INVALID_DATA;
440 	}
441 	if (payload_length != sizeof(struct pldm_get_fru_record_table_req)) {
442 		return PLDM_ERROR_INVALID_LENGTH;
443 	}
444 
445 	struct pldm_header_info header = { 0 };
446 	header.msg_type = PLDM_REQUEST;
447 	header.instance = instance_id;
448 	header.pldm_type = PLDM_FRU;
449 	header.command = PLDM_GET_FRU_RECORD_TABLE;
450 
451 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
452 	if (rc != PLDM_SUCCESS) {
453 		return rc;
454 	}
455 
456 	struct pldm_get_fru_record_table_req *req =
457 		(struct pldm_get_fru_record_table_req *)msg->payload;
458 	req->data_transfer_handle = htole32(data_transfer_handle);
459 	req->transfer_operation_flag = transfer_operation_flag;
460 
461 	return PLDM_SUCCESS;
462 }
463 
464 LIBPLDM_ABI_STABLE
465 int decode_get_fru_record_table_resp_safe(
466 	const struct pldm_msg *msg, size_t payload_length,
467 	uint8_t *completion_code, uint32_t *next_data_transfer_handle,
468 	uint8_t *transfer_flag, uint8_t *fru_record_table_data,
469 	size_t *fru_record_table_length, size_t max_fru_record_table_length)
470 {
471 	if (msg == NULL || completion_code == NULL ||
472 	    next_data_transfer_handle == NULL || transfer_flag == NULL ||
473 	    fru_record_table_data == NULL || fru_record_table_length == NULL) {
474 		return PLDM_ERROR_INVALID_DATA;
475 	}
476 
477 	*completion_code = msg->payload[0];
478 	if (PLDM_SUCCESS != *completion_code) {
479 		return PLDM_SUCCESS;
480 	}
481 	if (payload_length <= PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES) {
482 		return PLDM_ERROR_INVALID_LENGTH;
483 	}
484 
485 	struct pldm_get_fru_record_table_resp *resp =
486 		(struct pldm_get_fru_record_table_resp *)msg->payload;
487 
488 	*next_data_transfer_handle = le32toh(resp->next_data_transfer_handle);
489 	*transfer_flag = resp->transfer_flag;
490 
491 	*fru_record_table_length =
492 		payload_length - PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES;
493 
494 	if (*fru_record_table_length > max_fru_record_table_length) {
495 		return PLDM_ERROR_INVALID_LENGTH;
496 	}
497 
498 	memcpy(fru_record_table_data, resp->fru_record_table_data,
499 	       *fru_record_table_length);
500 
501 	return PLDM_SUCCESS;
502 }
503 
504 LIBPLDM_ABI_STABLE
505 int decode_get_fru_record_table_resp(const struct pldm_msg *msg,
506 				     size_t payload_length,
507 				     uint8_t *completion_code,
508 				     uint32_t *next_data_transfer_handle,
509 				     uint8_t *transfer_flag,
510 				     uint8_t *fru_record_table_data,
511 				     size_t *fru_record_table_length)
512 {
513 	return decode_get_fru_record_table_resp_safe(
514 		msg, payload_length, completion_code, next_data_transfer_handle,
515 		transfer_flag, fru_record_table_data, fru_record_table_length,
516 		(size_t)-1);
517 }
518 
519 LIBPLDM_ABI_STABLE
520 int decode_set_fru_record_table_req(const struct pldm_msg *msg,
521 				    size_t payload_length,
522 				    uint32_t *data_transfer_handle,
523 				    uint8_t *transfer_flag,
524 				    struct variable_field *fru_table_data)
525 
526 {
527 	if (msg == NULL || data_transfer_handle == NULL ||
528 	    transfer_flag == NULL || fru_table_data == NULL) {
529 		return PLDM_ERROR_INVALID_DATA;
530 	}
531 
532 	if (payload_length <= PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES) {
533 		return PLDM_ERROR_INVALID_LENGTH;
534 	}
535 
536 	struct pldm_set_fru_record_table_req *req =
537 		(struct pldm_set_fru_record_table_req *)msg->payload;
538 
539 	*data_transfer_handle = le32toh(req->data_transfer_handle);
540 	*transfer_flag = req->transfer_flag;
541 	fru_table_data->length =
542 		payload_length - PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES;
543 	fru_table_data->ptr = req->fru_record_table_data;
544 
545 	return PLDM_SUCCESS;
546 }
547 
548 LIBPLDM_ABI_STABLE
549 int encode_set_fru_record_table_resp(uint8_t instance_id,
550 				     uint8_t completion_code,
551 				     uint32_t next_data_transfer_handle,
552 				     size_t payload_length,
553 				     struct pldm_msg *msg)
554 {
555 	if (msg == NULL) {
556 		return PLDM_ERROR_INVALID_DATA;
557 	}
558 	if (payload_length != PLDM_SET_FRU_RECORD_TABLE_RESP_BYTES) {
559 		return PLDM_ERROR_INVALID_LENGTH;
560 	}
561 
562 	struct pldm_header_info header = { 0 };
563 	header.instance = instance_id;
564 	header.msg_type = PLDM_RESPONSE;
565 	header.pldm_type = PLDM_FRU;
566 	header.command = PLDM_SET_FRU_RECORD_TABLE;
567 
568 	uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
569 	if (PLDM_SUCCESS != rc) {
570 		return rc;
571 	}
572 
573 	struct pldm_set_fru_record_table_resp *response =
574 		(struct pldm_set_fru_record_table_resp *)msg->payload;
575 	response->completion_code = completion_code;
576 	response->next_data_transfer_handle =
577 		htole32(next_data_transfer_handle);
578 
579 	return PLDM_SUCCESS;
580 }
581