xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_tlv.c (revision d522b1b004800728de5466451e4d7032a4f53de6)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3 
4 #include <linux/gfp.h>
5 #include <linux/mm.h>
6 #include <linux/once.h>
7 #include <linux/random.h>
8 #include <linux/string.h>
9 #include <uapi/linux/if_ether.h>
10 
11 #include "fbnic_tlv.h"
12 
13 /**
14  * fbnic_tlv_msg_alloc - Allocate page and initialize FW message header
15  * @msg_id: Identifier for new message we are starting
16  *
17  * Return: pointer to start of message, or NULL on failure.
18  *
19  * Allocates a page and initializes message header at start of page.
20  * Initial message size is 1 DWORD which is just the header.
21  **/
22 struct fbnic_tlv_msg *fbnic_tlv_msg_alloc(u16 msg_id)
23 {
24 	struct fbnic_tlv_hdr hdr = { 0 };
25 	struct fbnic_tlv_msg *msg;
26 
27 	msg = (struct fbnic_tlv_msg *)__get_free_page(GFP_KERNEL);
28 	if (!msg)
29 		return NULL;
30 
31 	/* Start with zero filled header and then back fill with data */
32 	hdr.type = msg_id;
33 	hdr.is_msg = 1;
34 	hdr.len = cpu_to_le16(1);
35 
36 	/* Copy header into start of message */
37 	msg->hdr = hdr;
38 
39 	return msg;
40 }
41 
42 /**
43  * fbnic_tlv_attr_put_flag - Add flag value to message
44  * @msg: Message header we are adding flag attribute to
45  * @attr_id: ID of flag attribute we are adding to message
46  *
47  * Return: -ENOSPC if there is no room for the attribute. Otherwise 0.
48  *
49  * Adds a 1 DWORD flag attribute to the message. The presence of this
50  * attribute can be used as a boolean value indicating true, otherwise the
51  * value is considered false.
52  **/
53 int fbnic_tlv_attr_put_flag(struct fbnic_tlv_msg *msg, const u16 attr_id)
54 {
55 	int attr_max_len = PAGE_SIZE - offset_in_page(msg) - sizeof(*msg);
56 	struct fbnic_tlv_hdr hdr = { 0 };
57 	struct fbnic_tlv_msg *attr;
58 
59 	attr_max_len -= le16_to_cpu(msg->hdr.len) * sizeof(u32);
60 	if (attr_max_len < sizeof(*attr))
61 		return -ENOSPC;
62 
63 	/* Get header pointer and bump attr to start of data */
64 	attr = &msg[le16_to_cpu(msg->hdr.len)];
65 
66 	/* Record attribute type and size */
67 	hdr.type = attr_id;
68 	hdr.len = cpu_to_le16(sizeof(hdr));
69 
70 	attr->hdr = hdr;
71 	le16_add_cpu(&msg->hdr.len,
72 		     FBNIC_TLV_MSG_SIZE(le16_to_cpu(hdr.len)));
73 
74 	return 0;
75 }
76 
77 /**
78  * fbnic_tlv_attr_put_value - Add data to message
79  * @msg: Message header we are adding flag attribute to
80  * @attr_id: ID of flag attribute we are adding to message
81  * @value: Pointer to data to be stored
82  * @len: Size of data to be stored.
83  *
84  * Return: -ENOSPC if there is no room for the attribute. Otherwise 0.
85  *
86  * Adds header and copies data pointed to by value into the message. The
87  * result is rounded up to the nearest DWORD for sizing so that the
88  * headers remain aligned.
89  *
90  * The assumption is that the value field is in a format where byte
91  * ordering can be guaranteed such as a byte array or a little endian
92  * format.
93  **/
94 int fbnic_tlv_attr_put_value(struct fbnic_tlv_msg *msg, const u16 attr_id,
95 			     const void *value, const int len)
96 {
97 	int attr_max_len = PAGE_SIZE - offset_in_page(msg) - sizeof(*msg);
98 	struct fbnic_tlv_hdr hdr = { 0 };
99 	struct fbnic_tlv_msg *attr;
100 
101 	attr_max_len -= le16_to_cpu(msg->hdr.len) * sizeof(u32);
102 	if (attr_max_len < sizeof(*attr) + len)
103 		return -ENOSPC;
104 
105 	/* Get header pointer and bump attr to start of data */
106 	attr = &msg[le16_to_cpu(msg->hdr.len)];
107 
108 	/* Record attribute type and size */
109 	hdr.type = attr_id;
110 	hdr.len = cpu_to_le16(sizeof(hdr) + len);
111 
112 	/* Zero pad end of region to be written if we aren't aligned */
113 	if (len % sizeof(hdr))
114 		attr->value[len / sizeof(hdr)] = 0;
115 
116 	/* Copy data over */
117 	memcpy(attr->value, value, len);
118 
119 	attr->hdr = hdr;
120 	le16_add_cpu(&msg->hdr.len,
121 		     FBNIC_TLV_MSG_SIZE(le16_to_cpu(hdr.len)));
122 
123 	return 0;
124 }
125 
126 /**
127  * __fbnic_tlv_attr_put_int - Add integer to message
128  * @msg: Message header we are adding flag attribute to
129  * @attr_id: ID of flag attribute we are adding to message
130  * @value: Data to be stored
131  * @len: Size of data to be stored, either 4 or 8 bytes.
132  *
133  * Return: -ENOSPC if there is no room for the attribute. Otherwise 0.
134  *
135  * Adds header and copies data pointed to by value into the message. Will
136  * format the data as little endian.
137  **/
138 int __fbnic_tlv_attr_put_int(struct fbnic_tlv_msg *msg, const u16 attr_id,
139 			     s64 value, const int len)
140 {
141 	__le64 le64_value = cpu_to_le64(value);
142 
143 	return fbnic_tlv_attr_put_value(msg, attr_id, &le64_value, len);
144 }
145 
146 /**
147  * fbnic_tlv_attr_put_mac_addr - Add mac_addr to message
148  * @msg: Message header we are adding flag attribute to
149  * @attr_id: ID of flag attribute we are adding to message
150  * @mac_addr: Byte pointer to MAC address to be stored
151  *
152  * Return: -ENOSPC if there is no room for the attribute. Otherwise 0.
153  *
154  * Adds header and copies data pointed to by mac_addr into the message. Will
155  * copy the address raw so it will be in big endian with start of MAC
156  * address at start of attribute.
157  **/
158 int fbnic_tlv_attr_put_mac_addr(struct fbnic_tlv_msg *msg, const u16 attr_id,
159 				const u8 *mac_addr)
160 {
161 	return fbnic_tlv_attr_put_value(msg, attr_id, mac_addr, ETH_ALEN);
162 }
163 
164 /**
165  * fbnic_tlv_attr_put_string - Add string to message
166  * @msg: Message header we are adding flag attribute to
167  * @attr_id: ID of flag attribute we are adding to message
168  * @string: Byte pointer to null terminated string to be stored
169  *
170  * Return: -ENOSPC if there is no room for the attribute. Otherwise 0.
171  *
172  * Adds header and copies data pointed to by string into the message. Will
173  * copy the address raw so it will be in byte order.
174  **/
175 int fbnic_tlv_attr_put_string(struct fbnic_tlv_msg *msg, u16 attr_id,
176 			      const char *string)
177 {
178 	int attr_max_len = PAGE_SIZE - sizeof(*msg);
179 	int str_len = 1;
180 
181 	/* The max length will be message minus existing message and new
182 	 * attribute header. Since the message is measured in DWORDs we have
183 	 * to multiply the size by 4.
184 	 *
185 	 * The string length doesn't include the \0 so we have to add one to
186 	 * the final value, so start with that as our initial value.
187 	 *
188 	 * We will verify if the string will fit in fbnic_tlv_attr_put_value()
189 	 */
190 	attr_max_len -= le16_to_cpu(msg->hdr.len) * sizeof(u32);
191 	str_len += strnlen(string, attr_max_len);
192 
193 	return fbnic_tlv_attr_put_value(msg, attr_id, string, str_len);
194 }
195 
196 /**
197  * fbnic_tlv_attr_get_unsigned - Retrieve unsigned value from result
198  * @attr: Attribute to retrieve data from
199  * @def: The default value if attr is NULL
200  *
201  * Return: unsigned 64b value containing integer value
202  **/
203 u64 fbnic_tlv_attr_get_unsigned(struct fbnic_tlv_msg *attr, u64 def)
204 {
205 	__le64 le64_value = 0;
206 
207 	if (!attr)
208 		return def;
209 
210 	memcpy(&le64_value, &attr->value[0],
211 	       le16_to_cpu(attr->hdr.len) - sizeof(*attr));
212 
213 	return le64_to_cpu(le64_value);
214 }
215 
216 /**
217  * fbnic_tlv_attr_get_signed - Retrieve signed value from result
218  * @attr: Attribute to retrieve data from
219  * @def: The default value if attr is NULL
220  *
221  * Return: signed 64b value containing integer value
222  **/
223 s64 fbnic_tlv_attr_get_signed(struct fbnic_tlv_msg *attr, s64 def)
224 {
225 	__le64 le64_value = 0;
226 	int shift;
227 	s64 value;
228 
229 	if (!attr)
230 		return def;
231 
232 	shift = (8 + sizeof(*attr) - le16_to_cpu(attr->hdr.len)) * 8;
233 
234 	/* Copy the value and adjust for byte ordering */
235 	memcpy(&le64_value, &attr->value[0],
236 	       le16_to_cpu(attr->hdr.len) - sizeof(*attr));
237 	value = le64_to_cpu(le64_value);
238 
239 	/* Sign extend the return value by using a pair of shifts */
240 	return (value << shift) >> shift;
241 }
242 
243 /**
244  * fbnic_tlv_attr_get_string - Retrieve string value from result
245  * @attr: Attribute to retrieve data from
246  * @dst: Pointer to an allocated string to store the data
247  * @dstsize: The maximum size which can be in dst
248  *
249  * Return: the size of the string read from firmware or negative error.
250  **/
251 ssize_t fbnic_tlv_attr_get_string(struct fbnic_tlv_msg *attr, char *dst,
252 				  size_t dstsize)
253 {
254 	size_t srclen, len;
255 	ssize_t ret;
256 
257 	if (!attr)
258 		return -EINVAL;
259 
260 	if (dstsize == 0)
261 		return -E2BIG;
262 
263 	srclen = le16_to_cpu(attr->hdr.len) - sizeof(*attr);
264 	if (srclen > 0 && ((char *)attr->value)[srclen - 1] == '\0')
265 		srclen--;
266 
267 	if (srclen >= dstsize) {
268 		len = dstsize - 1;
269 		ret = -E2BIG;
270 	} else {
271 		len = srclen;
272 		ret = len;
273 	}
274 
275 	memcpy(dst, &attr->value, len);
276 	/* Zero pad end of dst. */
277 	memset(dst + len, 0, dstsize - len);
278 
279 	return ret;
280 }
281 
282 /**
283  * fbnic_tlv_attr_nest_start - Add nested attribute header to message
284  * @msg: Message header we are adding flag attribute to
285  * @attr_id: ID of flag attribute we are adding to message
286  *
287  * Return: NULL if there is no room for the attribute. Otherwise a pointer
288  * to the new attribute header.
289  *
290  * New header length is stored initially in DWORDs.
291  **/
292 struct fbnic_tlv_msg *fbnic_tlv_attr_nest_start(struct fbnic_tlv_msg *msg,
293 						u16 attr_id)
294 {
295 	int attr_max_len = PAGE_SIZE - offset_in_page(msg) - sizeof(*msg);
296 	struct fbnic_tlv_msg *attr = &msg[le16_to_cpu(msg->hdr.len)];
297 	struct fbnic_tlv_hdr hdr = { 0 };
298 
299 	/* Make sure we have space for at least the nest header plus one more */
300 	attr_max_len -= le16_to_cpu(msg->hdr.len) * sizeof(u32);
301 	if (attr_max_len < sizeof(*attr) * 2)
302 		return NULL;
303 
304 	/* Record attribute type and size */
305 	hdr.type = attr_id;
306 
307 	/* Add current message length to account for consumption within the
308 	 * page and leave it as a multiple of DWORDs, we will shift to
309 	 * bytes when we close it out.
310 	 */
311 	hdr.len = cpu_to_le16(1);
312 
313 	attr->hdr = hdr;
314 
315 	return attr;
316 }
317 
318 /**
319  * fbnic_tlv_attr_nest_stop - Close out nested attribute and add it to message
320  * @msg: Message header we are adding flag attribute to
321  *
322  * Closes out nested attribute, adds length to message, and then bumps
323  * length from DWORDs to bytes to match other attributes.
324  **/
325 void fbnic_tlv_attr_nest_stop(struct fbnic_tlv_msg *msg)
326 {
327 	struct fbnic_tlv_msg *attr = &msg[le16_to_cpu(msg->hdr.len)];
328 	u16 len = le16_to_cpu(attr->hdr.len);
329 
330 	/* Add attribute to message if there is more than just a header */
331 	if (len <= 1)
332 		return;
333 
334 	le16_add_cpu(&msg->hdr.len, len);
335 
336 	/* Convert from DWORDs to bytes */
337 	attr->hdr.len = cpu_to_le16(len * sizeof(u32));
338 }
339 
340 static int
341 fbnic_tlv_attr_validate(struct fbnic_tlv_msg *attr,
342 			const struct fbnic_tlv_index *tlv_index)
343 {
344 	u16 len = le16_to_cpu(attr->hdr.len) - sizeof(*attr);
345 	u16 attr_id = attr->hdr.type;
346 	__le32 *value = &attr->value[0];
347 
348 	if (attr->hdr.is_msg)
349 		return -EINVAL;
350 
351 	if (attr_id >= FBNIC_TLV_RESULTS_MAX)
352 		return -EINVAL;
353 
354 	while (tlv_index->id != attr_id) {
355 		if  (tlv_index->id == FBNIC_TLV_ATTR_ID_UNKNOWN) {
356 			if (attr->hdr.cannot_ignore)
357 				return -ENOENT;
358 			return le16_to_cpu(attr->hdr.len);
359 		}
360 
361 		tlv_index++;
362 	}
363 
364 	if (offset_in_page(attr) + len > PAGE_SIZE - sizeof(*attr))
365 		return -E2BIG;
366 
367 	switch (tlv_index->type) {
368 	case FBNIC_TLV_STRING:
369 		if (!len || len > tlv_index->len)
370 			return -EINVAL;
371 		if (((char *)value)[len - 1])
372 			return -EINVAL;
373 		break;
374 	case FBNIC_TLV_FLAG:
375 		if (len)
376 			return -EINVAL;
377 		break;
378 	case FBNIC_TLV_UNSIGNED:
379 	case FBNIC_TLV_SIGNED:
380 		if (tlv_index->len > sizeof(__le64))
381 			return -EINVAL;
382 		fallthrough;
383 	case FBNIC_TLV_BINARY:
384 		if (!len || len > tlv_index->len)
385 			return -EINVAL;
386 		break;
387 	case FBNIC_TLV_NESTED:
388 	case FBNIC_TLV_ARRAY:
389 		if (len % 4)
390 			return -EINVAL;
391 		break;
392 	default:
393 		return -EINVAL;
394 	}
395 
396 	return 0;
397 }
398 
399 /**
400  * fbnic_tlv_attr_parse_array - Parse array of attributes into results array
401  * @attr: Start of attributes in the message
402  * @len: Length of attributes in the message
403  * @results: Array of pointers to store the results of parsing
404  * @tlv_index: List of TLV attributes to be parsed from message
405  * @tlv_attr_id: Specific ID that is repeated in array
406  * @array_len: Number of results to store in results array
407  *
408  * Return: zero on success, or negative value on error.
409  *
410  * Will take a list of attributes and a parser definition and will capture
411  * the results in the results array to have the data extracted later.
412  **/
413 int fbnic_tlv_attr_parse_array(struct fbnic_tlv_msg *attr, int len,
414 			       struct fbnic_tlv_msg **results,
415 			       const struct fbnic_tlv_index *tlv_index,
416 			       u16 tlv_attr_id, size_t array_len)
417 {
418 	int i = 0;
419 
420 	/* Initialize results table to NULL. */
421 	memset(results, 0, array_len * sizeof(results[0]));
422 
423 	/* Nothing to parse if header was only thing there */
424 	if (!len)
425 		return 0;
426 
427 	/* Work through list of attributes, parsing them as necessary */
428 	while (len > 0) {
429 		u16 attr_id = attr->hdr.type;
430 		u16 attr_len;
431 		int err;
432 
433 		if (tlv_attr_id != attr_id)
434 			return -EINVAL;
435 
436 		/* Stop parsing on full error */
437 		err = fbnic_tlv_attr_validate(attr, tlv_index);
438 		if (err < 0)
439 			return err;
440 
441 		if (i >= array_len)
442 			return -ENOSPC;
443 
444 		results[i++] = attr;
445 
446 		attr_len = FBNIC_TLV_MSG_SIZE(le16_to_cpu(attr->hdr.len));
447 		len -= attr_len;
448 		attr += attr_len;
449 	}
450 
451 	return len == 0 ? 0 : -EINVAL;
452 }
453 
454 /**
455  * fbnic_tlv_attr_parse - Parse attributes into a list of attribute results
456  * @attr: Start of attributes in the message
457  * @len: Length of attributes in the message
458  * @results: Array of pointers to store the results of parsing
459  * @tlv_index: List of TLV attributes to be parsed from message
460  *
461  * Return: zero on success, or negative value on error.
462  *
463  * Will take a list of attributes and a parser definition and will capture
464  * the results in the results array to have the data extracted later.
465  **/
466 int fbnic_tlv_attr_parse(struct fbnic_tlv_msg *attr, int len,
467 			 struct fbnic_tlv_msg **results,
468 			 const struct fbnic_tlv_index *tlv_index)
469 {
470 	/* Initialize results table to NULL. */
471 	memset(results, 0, sizeof(results[0]) * FBNIC_TLV_RESULTS_MAX);
472 
473 	/* Nothing to parse if header was only thing there */
474 	if (!len)
475 		return 0;
476 
477 	/* Work through list of attributes, parsing them as necessary */
478 	while (len > 0) {
479 		int err = fbnic_tlv_attr_validate(attr, tlv_index);
480 		u16 attr_id = attr->hdr.type;
481 		u16 attr_len;
482 
483 		/* Stop parsing on full error */
484 		if (err < 0)
485 			return err;
486 
487 		/* Ignore results for unsupported values */
488 		if (!err) {
489 			/* Do not overwrite existing entries */
490 			if (results[attr_id])
491 				return -EADDRINUSE;
492 
493 			results[attr_id] = attr;
494 		}
495 
496 		attr_len = FBNIC_TLV_MSG_SIZE(le16_to_cpu(attr->hdr.len));
497 		len -= attr_len;
498 		attr += attr_len;
499 	}
500 
501 	return len == 0 ? 0 : -EINVAL;
502 }
503 
504 /**
505  * fbnic_tlv_msg_parse - Parse message and process via predetermined functions
506  * @opaque: Value passed to parser function to enable driver access
507  * @msg: Message to be parsed.
508  * @parser: TLV message parser definition.
509  *
510  * Return: zero on success, or negative value on error.
511  *
512  * Will take a message a number of message types via the attribute parsing
513  * definitions and function provided for the parser array.
514  **/
515 int fbnic_tlv_msg_parse(void *opaque, struct fbnic_tlv_msg *msg,
516 			const struct fbnic_tlv_parser *parser)
517 {
518 	struct fbnic_tlv_msg *results[FBNIC_TLV_RESULTS_MAX];
519 	u16 msg_id = msg->hdr.type;
520 	int err;
521 
522 	if (!msg->hdr.is_msg)
523 		return -EINVAL;
524 
525 	if (le16_to_cpu(msg->hdr.len) > PAGE_SIZE / sizeof(u32))
526 		return -E2BIG;
527 
528 	while (parser->id != msg_id) {
529 		if (parser->id == FBNIC_TLV_MSG_ID_UNKNOWN)
530 			return -ENOENT;
531 		parser++;
532 	}
533 
534 	err = fbnic_tlv_attr_parse(&msg[1], le16_to_cpu(msg->hdr.len) - 1,
535 				   results, parser->attr);
536 	if (err)
537 		return err;
538 
539 	return parser->func(opaque, results);
540 }
541 
542 /**
543  * fbnic_tlv_parser_error - called if message doesn't match known type
544  * @opaque: (unused)
545  * @results: (unused)
546  *
547  * Return: -EBADMSG to indicate the message is an unsupported type
548  **/
549 int fbnic_tlv_parser_error(void *opaque, struct fbnic_tlv_msg **results)
550 {
551 	return -EBADMSG;
552 }
553 
554 #define FBNIC_TLV_TEST_STRING_LEN	32
555 
556 struct fbnic_tlv_test {
557 	u64	test_u64;
558 	s64	test_s64;
559 	u32	test_u32;
560 	s32	test_s32;
561 	u16	test_u16;
562 	s16	test_s16;
563 	u8	test_mac[ETH_ALEN];
564 	u8	test_mac_array[4][ETH_ALEN];
565 	u8	test_true;
566 	u8	test_false;
567 	char	test_string[FBNIC_TLV_TEST_STRING_LEN];
568 };
569 
570 static struct fbnic_tlv_test test_struct;
571 
572 const struct fbnic_tlv_index fbnic_tlv_test_index[] = {
573 	FBNIC_TLV_ATTR_U64(FBNIC_TLV_TEST_MSG_U64),
574 	FBNIC_TLV_ATTR_S64(FBNIC_TLV_TEST_MSG_S64),
575 	FBNIC_TLV_ATTR_U32(FBNIC_TLV_TEST_MSG_U32),
576 	FBNIC_TLV_ATTR_S32(FBNIC_TLV_TEST_MSG_S32),
577 	FBNIC_TLV_ATTR_U32(FBNIC_TLV_TEST_MSG_U16),
578 	FBNIC_TLV_ATTR_S32(FBNIC_TLV_TEST_MSG_S16),
579 	FBNIC_TLV_ATTR_MAC_ADDR(FBNIC_TLV_TEST_MSG_MAC_ADDR),
580 	FBNIC_TLV_ATTR_FLAG(FBNIC_TLV_TEST_MSG_FLAG_TRUE),
581 	FBNIC_TLV_ATTR_FLAG(FBNIC_TLV_TEST_MSG_FLAG_FALSE),
582 	FBNIC_TLV_ATTR_STRING(FBNIC_TLV_TEST_MSG_STRING,
583 			      FBNIC_TLV_TEST_STRING_LEN),
584 	FBNIC_TLV_ATTR_ARRAY(FBNIC_TLV_TEST_MSG_ARRAY),
585 	FBNIC_TLV_ATTR_NESTED(FBNIC_TLV_TEST_MSG_NESTED),
586 	FBNIC_TLV_ATTR_LAST
587 };
588 
589 static void fbnic_tlv_test_struct_init(void)
590 {
591 	int i = FBNIC_TLV_TEST_STRING_LEN - 1;
592 
593 	/* Populate the struct with random data */
594 	get_random_once(&test_struct,
595 			offsetof(struct fbnic_tlv_test, test_string) + i);
596 
597 	/* Force true/false to their expected values */
598 	test_struct.test_false = false;
599 	test_struct.test_true = true;
600 
601 	/* Convert test_string to a true ASCII string */
602 	test_struct.test_string[i] = '\0';
603 	while (i--) {
604 		/* Force characters into displayable range */
605 		if (test_struct.test_string[i] < 64 ||
606 		    test_struct.test_string[i] >= 96) {
607 			test_struct.test_string[i] %= 32;
608 			test_struct.test_string[i] += 64;
609 		}
610 	}
611 }
612 
613 static int fbnic_tlv_test_attr_data(struct fbnic_tlv_msg *msg)
614 {
615 	struct fbnic_tlv_msg *array;
616 	int err, i;
617 
618 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U64,
619 				     test_struct.test_u64);
620 	if (err)
621 		return err;
622 
623 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S64,
624 				     test_struct.test_s64);
625 	if (err)
626 		return err;
627 
628 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U32,
629 				     test_struct.test_u32);
630 	if (err)
631 		return err;
632 
633 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S32,
634 				     test_struct.test_s32);
635 	if (err)
636 		return err;
637 
638 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U16,
639 				     test_struct.test_u16);
640 	if (err)
641 		return err;
642 
643 	err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S16,
644 				     test_struct.test_s16);
645 	if (err)
646 		return err;
647 
648 	err = fbnic_tlv_attr_put_value(msg, FBNIC_TLV_TEST_MSG_MAC_ADDR,
649 				       test_struct.test_mac, ETH_ALEN);
650 	if (err)
651 		return err;
652 
653 	/* Start MAC address array */
654 	array = fbnic_tlv_attr_nest_start(msg, FBNIC_TLV_TEST_MSG_ARRAY);
655 	if (!array)
656 		return -ENOSPC;
657 
658 	for (i = 0; i < 4; i++) {
659 		err = fbnic_tlv_attr_put_value(array,
660 					       FBNIC_TLV_TEST_MSG_MAC_ADDR,
661 					       test_struct.test_mac_array[i],
662 					       ETH_ALEN);
663 		if (err)
664 			return err;
665 	}
666 
667 	/* Close array */
668 	fbnic_tlv_attr_nest_stop(msg);
669 
670 	err = fbnic_tlv_attr_put_flag(msg, FBNIC_TLV_TEST_MSG_FLAG_TRUE);
671 	if (err)
672 		return err;
673 
674 	return fbnic_tlv_attr_put_string(msg, FBNIC_TLV_TEST_MSG_STRING,
675 					 test_struct.test_string);
676 }
677 
678 /**
679  * fbnic_tlv_test_create - Allocate a test message and fill it w/ data
680  * @fbd: FBNIC device structure
681  *
682  * Return: NULL on failure to allocate or pointer to new TLV test message.
683  **/
684 struct fbnic_tlv_msg *fbnic_tlv_test_create(struct fbnic_dev *fbd)
685 {
686 	struct fbnic_tlv_msg *msg, *nest;
687 	int err;
688 
689 	msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_TEST);
690 	if (!msg)
691 		return NULL;
692 
693 	/* Randomize struct data */
694 	fbnic_tlv_test_struct_init();
695 
696 	/* Add first level of data to message */
697 	err = fbnic_tlv_test_attr_data(msg);
698 	if (err)
699 		goto free_message;
700 
701 	/* Start second level nested */
702 	nest = fbnic_tlv_attr_nest_start(msg, FBNIC_TLV_TEST_MSG_NESTED);
703 	if (!nest)
704 		goto free_message;
705 
706 	/* Add nested data */
707 	err = fbnic_tlv_test_attr_data(nest);
708 	if (err)
709 		goto free_message;
710 
711 	/* Close nest and report full message */
712 	fbnic_tlv_attr_nest_stop(msg);
713 
714 	return msg;
715 free_message:
716 	free_page((unsigned long)msg);
717 	return NULL;
718 }
719 
720 void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src)
721 {
722 	u8 *mac_addr;
723 
724 	mac_addr = fbnic_tlv_attr_get_value_ptr(src);
725 	memcpy(dest, mac_addr, ETH_ALEN);
726 }
727 
728 /**
729  * fbnic_tlv_parser_test_attr - Function loading test attributes into structure
730  * @str: Test structure to load
731  * @results: Pointer to results array
732  *
733  * Copies attributes into structure. Any attribute that doesn't exist in the
734  * results array is not populated.
735  **/
736 static void fbnic_tlv_parser_test_attr(struct fbnic_tlv_test *str,
737 				       struct fbnic_tlv_msg **results)
738 {
739 	struct fbnic_tlv_msg *array_results[4];
740 	struct fbnic_tlv_msg *attr;
741 	char *string = NULL;
742 	int i, err;
743 
744 	str->test_u64 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U64);
745 	str->test_u32 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U32);
746 	str->test_u16 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U16);
747 
748 	str->test_s64 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S64);
749 	str->test_s32 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S32);
750 	str->test_s16 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S16);
751 
752 	attr = results[FBNIC_TLV_TEST_MSG_MAC_ADDR];
753 	if (attr)
754 		fbnic_tlv_attr_addr_copy(str->test_mac, attr);
755 
756 	attr = results[FBNIC_TLV_TEST_MSG_ARRAY];
757 	if (attr) {
758 		int len = le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1;
759 
760 		err = fbnic_tlv_attr_parse_array(&attr[1], len,
761 						 array_results,
762 						 fbnic_tlv_test_index,
763 						 FBNIC_TLV_TEST_MSG_MAC_ADDR,
764 						 4);
765 		if (!err) {
766 			for (i = 0; i < 4 && array_results[i]; i++)
767 				fbnic_tlv_attr_addr_copy(str->test_mac_array[i],
768 							 array_results[i]);
769 		}
770 	}
771 
772 	str->test_true = !!results[FBNIC_TLV_TEST_MSG_FLAG_TRUE];
773 	str->test_false = !!results[FBNIC_TLV_TEST_MSG_FLAG_FALSE];
774 
775 	attr = results[FBNIC_TLV_TEST_MSG_STRING];
776 	if (attr) {
777 		string = fbnic_tlv_attr_get_value_ptr(attr);
778 		strscpy(str->test_string, string, FBNIC_TLV_TEST_STRING_LEN);
779 	}
780 }
781 
782 static void fbnic_tlv_test_dump(struct fbnic_tlv_test *value, char *prefix)
783 {
784 	print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 16, 1,
785 		       value, sizeof(*value), true);
786 }
787 
788 /**
789  * fbnic_tlv_parser_test - Function for parsing and testing test message
790  * @opaque: Unused value
791  * @results: Results of parser output
792  *
793  * Return: negative value on error, or 0 on success.
794  *
795  * Parses attributes to structures and compares the structure to the
796  * expected test value that should have been used to populate the message.
797  *
798  * Used to verify message generation and parser are working correctly.
799  **/
800 int fbnic_tlv_parser_test(void *opaque, struct fbnic_tlv_msg **results)
801 {
802 	struct fbnic_tlv_msg *nest_results[FBNIC_TLV_RESULTS_MAX] = { 0 };
803 	struct fbnic_tlv_test result_struct;
804 	struct fbnic_tlv_msg *attr;
805 	int err;
806 
807 	memset(&result_struct, 0, sizeof(result_struct));
808 	fbnic_tlv_parser_test_attr(&result_struct, results);
809 
810 	if (memcmp(&test_struct, &result_struct, sizeof(test_struct))) {
811 		fbnic_tlv_test_dump(&result_struct, "fbnic: found - ");
812 		fbnic_tlv_test_dump(&test_struct, "fbnic: expected - ");
813 		return -EINVAL;
814 	}
815 
816 	attr = results[FBNIC_TLV_TEST_MSG_NESTED];
817 	if (!attr)
818 		return -EINVAL;
819 
820 	err = fbnic_tlv_attr_parse(&attr[1],
821 				   le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1,
822 				   nest_results, fbnic_tlv_test_index);
823 	if (err)
824 		return err;
825 
826 	memset(&result_struct, 0, sizeof(result_struct));
827 	fbnic_tlv_parser_test_attr(&result_struct, nest_results);
828 
829 	if (memcmp(&test_struct, &result_struct, sizeof(test_struct))) {
830 		fbnic_tlv_test_dump(&result_struct, "fbnic: found - ");
831 		fbnic_tlv_test_dump(&test_struct, "fbnic: expected - ");
832 		return -EINVAL;
833 	}
834 
835 	return 0;
836 }
837