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 void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src) 555 { 556 u8 *mac_addr; 557 558 mac_addr = fbnic_tlv_attr_get_value_ptr(src); 559 memcpy(dest, mac_addr, ETH_ALEN); 560 } 561