property.c (18697B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Thunderbolt XDomain property support 4 * 5 * Copyright (C) 2017, Intel Corporation 6 * Authors: Michael Jamet <michael.jamet@intel.com> 7 * Mika Westerberg <mika.westerberg@linux.intel.com> 8 */ 9 10#include <linux/err.h> 11#include <linux/slab.h> 12#include <linux/string.h> 13#include <linux/uuid.h> 14#include <linux/thunderbolt.h> 15 16struct tb_property_entry { 17 u32 key_hi; 18 u32 key_lo; 19 u16 length; 20 u8 reserved; 21 u8 type; 22 u32 value; 23}; 24 25struct tb_property_rootdir_entry { 26 u32 magic; 27 u32 length; 28 struct tb_property_entry entries[]; 29}; 30 31struct tb_property_dir_entry { 32 u32 uuid[4]; 33 struct tb_property_entry entries[]; 34}; 35 36#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 37 38static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, 39 size_t block_len, unsigned int dir_offset, size_t dir_len, 40 bool is_root); 41 42static inline void parse_dwdata(void *dst, const void *src, size_t dwords) 43{ 44 be32_to_cpu_array(dst, src, dwords); 45} 46 47static inline void format_dwdata(void *dst, const void *src, size_t dwords) 48{ 49 cpu_to_be32_array(dst, src, dwords); 50} 51 52static bool tb_property_entry_valid(const struct tb_property_entry *entry, 53 size_t block_len) 54{ 55 switch (entry->type) { 56 case TB_PROPERTY_TYPE_DIRECTORY: 57 case TB_PROPERTY_TYPE_DATA: 58 case TB_PROPERTY_TYPE_TEXT: 59 if (entry->length > block_len) 60 return false; 61 if (entry->value + entry->length > block_len) 62 return false; 63 break; 64 65 case TB_PROPERTY_TYPE_VALUE: 66 if (entry->length != 1) 67 return false; 68 break; 69 } 70 71 return true; 72} 73 74static bool tb_property_key_valid(const char *key) 75{ 76 return key && strlen(key) <= TB_PROPERTY_KEY_SIZE; 77} 78 79static struct tb_property * 80tb_property_alloc(const char *key, enum tb_property_type type) 81{ 82 struct tb_property *property; 83 84 property = kzalloc(sizeof(*property), GFP_KERNEL); 85 if (!property) 86 return NULL; 87 88 strcpy(property->key, key); 89 property->type = type; 90 INIT_LIST_HEAD(&property->list); 91 92 return property; 93} 94 95static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, 96 const struct tb_property_entry *entry) 97{ 98 char key[TB_PROPERTY_KEY_SIZE + 1]; 99 struct tb_property *property; 100 struct tb_property_dir *dir; 101 102 if (!tb_property_entry_valid(entry, block_len)) 103 return NULL; 104 105 parse_dwdata(key, entry, 2); 106 key[TB_PROPERTY_KEY_SIZE] = '\0'; 107 108 property = tb_property_alloc(key, entry->type); 109 if (!property) 110 return NULL; 111 112 property->length = entry->length; 113 114 switch (property->type) { 115 case TB_PROPERTY_TYPE_DIRECTORY: 116 dir = __tb_property_parse_dir(block, block_len, entry->value, 117 entry->length, false); 118 if (!dir) { 119 kfree(property); 120 return NULL; 121 } 122 property->value.dir = dir; 123 break; 124 125 case TB_PROPERTY_TYPE_DATA: 126 property->value.data = kcalloc(property->length, sizeof(u32), 127 GFP_KERNEL); 128 if (!property->value.data) { 129 kfree(property); 130 return NULL; 131 } 132 parse_dwdata(property->value.data, block + entry->value, 133 entry->length); 134 break; 135 136 case TB_PROPERTY_TYPE_TEXT: 137 property->value.text = kcalloc(property->length, sizeof(u32), 138 GFP_KERNEL); 139 if (!property->value.text) { 140 kfree(property); 141 return NULL; 142 } 143 parse_dwdata(property->value.text, block + entry->value, 144 entry->length); 145 /* Force null termination */ 146 property->value.text[property->length * 4 - 1] = '\0'; 147 break; 148 149 case TB_PROPERTY_TYPE_VALUE: 150 property->value.immediate = entry->value; 151 break; 152 153 default: 154 property->type = TB_PROPERTY_TYPE_UNKNOWN; 155 break; 156 } 157 158 return property; 159} 160 161static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, 162 size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) 163{ 164 const struct tb_property_entry *entries; 165 size_t i, content_len, nentries; 166 unsigned int content_offset; 167 struct tb_property_dir *dir; 168 169 dir = kzalloc(sizeof(*dir), GFP_KERNEL); 170 if (!dir) 171 return NULL; 172 173 if (is_root) { 174 content_offset = dir_offset + 2; 175 content_len = dir_len; 176 } else { 177 dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), 178 GFP_KERNEL); 179 if (!dir->uuid) { 180 tb_property_free_dir(dir); 181 return NULL; 182 } 183 content_offset = dir_offset + 4; 184 content_len = dir_len - 4; /* Length includes UUID */ 185 } 186 187 entries = (const struct tb_property_entry *)&block[content_offset]; 188 nentries = content_len / (sizeof(*entries) / 4); 189 190 INIT_LIST_HEAD(&dir->properties); 191 192 for (i = 0; i < nentries; i++) { 193 struct tb_property *property; 194 195 property = tb_property_parse(block, block_len, &entries[i]); 196 if (!property) { 197 tb_property_free_dir(dir); 198 return NULL; 199 } 200 201 list_add_tail(&property->list, &dir->properties); 202 } 203 204 return dir; 205} 206 207/** 208 * tb_property_parse_dir() - Parses properties from given property block 209 * @block: Property block to parse 210 * @block_len: Number of dword elements in the property block 211 * 212 * This function parses the XDomain properties data block into format that 213 * can be traversed using the helper functions provided by this module. 214 * Upon success returns the parsed directory. In case of error returns 215 * %NULL. The resulting &struct tb_property_dir needs to be released by 216 * calling tb_property_free_dir() when not needed anymore. 217 * 218 * The @block is expected to be root directory. 219 */ 220struct tb_property_dir *tb_property_parse_dir(const u32 *block, 221 size_t block_len) 222{ 223 const struct tb_property_rootdir_entry *rootdir = 224 (const struct tb_property_rootdir_entry *)block; 225 226 if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC) 227 return NULL; 228 if (rootdir->length > block_len) 229 return NULL; 230 231 return __tb_property_parse_dir(block, block_len, 0, rootdir->length, 232 true); 233} 234 235/** 236 * tb_property_create_dir() - Creates new property directory 237 * @uuid: UUID used to identify the particular directory 238 * 239 * Creates new, empty property directory. If @uuid is %NULL then the 240 * directory is assumed to be root directory. 241 */ 242struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid) 243{ 244 struct tb_property_dir *dir; 245 246 dir = kzalloc(sizeof(*dir), GFP_KERNEL); 247 if (!dir) 248 return NULL; 249 250 INIT_LIST_HEAD(&dir->properties); 251 if (uuid) { 252 dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL); 253 if (!dir->uuid) { 254 kfree(dir); 255 return NULL; 256 } 257 } 258 259 return dir; 260} 261EXPORT_SYMBOL_GPL(tb_property_create_dir); 262 263static void tb_property_free(struct tb_property *property) 264{ 265 switch (property->type) { 266 case TB_PROPERTY_TYPE_DIRECTORY: 267 tb_property_free_dir(property->value.dir); 268 break; 269 270 case TB_PROPERTY_TYPE_DATA: 271 kfree(property->value.data); 272 break; 273 274 case TB_PROPERTY_TYPE_TEXT: 275 kfree(property->value.text); 276 break; 277 278 default: 279 break; 280 } 281 282 kfree(property); 283} 284 285/** 286 * tb_property_free_dir() - Release memory allocated for property directory 287 * @dir: Directory to release 288 * 289 * This will release all the memory the directory occupies including all 290 * descendants. It is OK to pass %NULL @dir, then the function does 291 * nothing. 292 */ 293void tb_property_free_dir(struct tb_property_dir *dir) 294{ 295 struct tb_property *property, *tmp; 296 297 if (!dir) 298 return; 299 300 list_for_each_entry_safe(property, tmp, &dir->properties, list) { 301 list_del(&property->list); 302 tb_property_free(property); 303 } 304 kfree(dir->uuid); 305 kfree(dir); 306} 307EXPORT_SYMBOL_GPL(tb_property_free_dir); 308 309static size_t tb_property_dir_length(const struct tb_property_dir *dir, 310 bool recurse, size_t *data_len) 311{ 312 const struct tb_property *property; 313 size_t len = 0; 314 315 if (dir->uuid) 316 len += sizeof(*dir->uuid) / 4; 317 else 318 len += sizeof(struct tb_property_rootdir_entry) / 4; 319 320 list_for_each_entry(property, &dir->properties, list) { 321 len += sizeof(struct tb_property_entry) / 4; 322 323 switch (property->type) { 324 case TB_PROPERTY_TYPE_DIRECTORY: 325 if (recurse) { 326 len += tb_property_dir_length( 327 property->value.dir, recurse, data_len); 328 } 329 /* Reserve dword padding after each directory */ 330 if (data_len) 331 *data_len += 1; 332 break; 333 334 case TB_PROPERTY_TYPE_DATA: 335 case TB_PROPERTY_TYPE_TEXT: 336 if (data_len) 337 *data_len += property->length; 338 break; 339 340 default: 341 break; 342 } 343 } 344 345 return len; 346} 347 348static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir, 349 u32 *block, unsigned int start_offset, size_t block_len) 350{ 351 unsigned int data_offset, dir_end; 352 const struct tb_property *property; 353 struct tb_property_entry *entry; 354 size_t dir_len, data_len = 0; 355 int ret; 356 357 /* 358 * The structure of property block looks like following. Leaf 359 * data/text is included right after the directory and each 360 * directory follows each other (even nested ones). 361 * 362 * +----------+ <-- start_offset 363 * | header | <-- root directory header 364 * +----------+ --- 365 * | entry 0 | -^--------------------. 366 * +----------+ | | 367 * | entry 1 | -|--------------------|--. 368 * +----------+ | | | 369 * | entry 2 | -|-----------------. | | 370 * +----------+ | | | | 371 * : : | dir_len | | | 372 * . . | | | | 373 * : : | | | | 374 * +----------+ | | | | 375 * | entry n | v | | | 376 * +----------+ <-- data_offset | | | 377 * | data 0 | <------------------|--' | 378 * +----------+ | | 379 * | data 1 | <------------------|-----' 380 * +----------+ | 381 * | 00000000 | padding | 382 * +----------+ <-- dir_end <------' 383 * | UUID | <-- directory UUID (child directory) 384 * +----------+ 385 * | entry 0 | 386 * +----------+ 387 * | entry 1 | 388 * +----------+ 389 * : : 390 * . . 391 * : : 392 * +----------+ 393 * | entry n | 394 * +----------+ 395 * | data 0 | 396 * +----------+ 397 * 398 * We use dir_end to hold pointer to the end of the directory. It 399 * will increase as we add directories and each directory should be 400 * added starting from previous dir_end. 401 */ 402 dir_len = tb_property_dir_length(dir, false, &data_len); 403 data_offset = start_offset + dir_len; 404 dir_end = start_offset + data_len + dir_len; 405 406 if (data_offset > dir_end) 407 return -EINVAL; 408 if (dir_end > block_len) 409 return -EINVAL; 410 411 /* Write headers first */ 412 if (dir->uuid) { 413 struct tb_property_dir_entry *pe; 414 415 pe = (struct tb_property_dir_entry *)&block[start_offset]; 416 memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid)); 417 entry = pe->entries; 418 } else { 419 struct tb_property_rootdir_entry *re; 420 421 re = (struct tb_property_rootdir_entry *)&block[start_offset]; 422 re->magic = TB_PROPERTY_ROOTDIR_MAGIC; 423 re->length = dir_len - sizeof(*re) / 4; 424 entry = re->entries; 425 } 426 427 list_for_each_entry(property, &dir->properties, list) { 428 const struct tb_property_dir *child; 429 430 format_dwdata(entry, property->key, 2); 431 entry->type = property->type; 432 433 switch (property->type) { 434 case TB_PROPERTY_TYPE_DIRECTORY: 435 child = property->value.dir; 436 ret = __tb_property_format_dir(child, block, dir_end, 437 block_len); 438 if (ret < 0) 439 return ret; 440 entry->length = tb_property_dir_length(child, false, 441 NULL); 442 entry->value = dir_end; 443 dir_end = ret; 444 break; 445 446 case TB_PROPERTY_TYPE_DATA: 447 format_dwdata(&block[data_offset], property->value.data, 448 property->length); 449 entry->length = property->length; 450 entry->value = data_offset; 451 data_offset += entry->length; 452 break; 453 454 case TB_PROPERTY_TYPE_TEXT: 455 format_dwdata(&block[data_offset], property->value.text, 456 property->length); 457 entry->length = property->length; 458 entry->value = data_offset; 459 data_offset += entry->length; 460 break; 461 462 case TB_PROPERTY_TYPE_VALUE: 463 entry->length = property->length; 464 entry->value = property->value.immediate; 465 break; 466 467 default: 468 break; 469 } 470 471 entry++; 472 } 473 474 return dir_end; 475} 476 477/** 478 * tb_property_format_dir() - Formats directory to the packed XDomain format 479 * @dir: Directory to format 480 * @block: Property block where the packed data is placed 481 * @block_len: Length of the property block 482 * 483 * This function formats the directory to the packed format that can be 484 * then send over the thunderbolt fabric to receiving host. Returns %0 in 485 * case of success and negative errno on faulure. Passing %NULL in @block 486 * returns number of entries the block takes. 487 */ 488ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, 489 size_t block_len) 490{ 491 ssize_t ret; 492 493 if (!block) { 494 size_t dir_len, data_len = 0; 495 496 dir_len = tb_property_dir_length(dir, true, &data_len); 497 return dir_len + data_len; 498 } 499 500 ret = __tb_property_format_dir(dir, block, 0, block_len); 501 return ret < 0 ? ret : 0; 502} 503 504/** 505 * tb_property_copy_dir() - Take a deep copy of directory 506 * @dir: Directory to copy 507 * 508 * This function takes a deep copy of @dir and returns back the copy. In 509 * case of error returns %NULL. The resulting directory needs to be 510 * released by calling tb_property_free_dir(). 511 */ 512struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir) 513{ 514 struct tb_property *property, *p = NULL; 515 struct tb_property_dir *d; 516 517 if (!dir) 518 return NULL; 519 520 d = tb_property_create_dir(dir->uuid); 521 if (!d) 522 return NULL; 523 524 list_for_each_entry(property, &dir->properties, list) { 525 struct tb_property *p; 526 527 p = tb_property_alloc(property->key, property->type); 528 if (!p) 529 goto err_free; 530 531 p->length = property->length; 532 533 switch (property->type) { 534 case TB_PROPERTY_TYPE_DIRECTORY: 535 p->value.dir = tb_property_copy_dir(property->value.dir); 536 if (!p->value.dir) 537 goto err_free; 538 break; 539 540 case TB_PROPERTY_TYPE_DATA: 541 p->value.data = kmemdup(property->value.data, 542 property->length * 4, 543 GFP_KERNEL); 544 if (!p->value.data) 545 goto err_free; 546 break; 547 548 case TB_PROPERTY_TYPE_TEXT: 549 p->value.text = kzalloc(p->length * 4, GFP_KERNEL); 550 if (!p->value.text) 551 goto err_free; 552 strcpy(p->value.text, property->value.text); 553 break; 554 555 case TB_PROPERTY_TYPE_VALUE: 556 p->value.immediate = property->value.immediate; 557 break; 558 559 default: 560 break; 561 } 562 563 list_add_tail(&p->list, &d->properties); 564 } 565 566 return d; 567 568err_free: 569 kfree(p); 570 tb_property_free_dir(d); 571 572 return NULL; 573} 574 575/** 576 * tb_property_add_immediate() - Add immediate property to directory 577 * @parent: Directory to add the property 578 * @key: Key for the property 579 * @value: Immediate value to store with the property 580 */ 581int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, 582 u32 value) 583{ 584 struct tb_property *property; 585 586 if (!tb_property_key_valid(key)) 587 return -EINVAL; 588 589 property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE); 590 if (!property) 591 return -ENOMEM; 592 593 property->length = 1; 594 property->value.immediate = value; 595 596 list_add_tail(&property->list, &parent->properties); 597 return 0; 598} 599EXPORT_SYMBOL_GPL(tb_property_add_immediate); 600 601/** 602 * tb_property_add_data() - Adds arbitrary data property to directory 603 * @parent: Directory to add the property 604 * @key: Key for the property 605 * @buf: Data buffer to add 606 * @buflen: Number of bytes in the data buffer 607 * 608 * Function takes a copy of @buf and adds it to the directory. 609 */ 610int tb_property_add_data(struct tb_property_dir *parent, const char *key, 611 const void *buf, size_t buflen) 612{ 613 /* Need to pad to dword boundary */ 614 size_t size = round_up(buflen, 4); 615 struct tb_property *property; 616 617 if (!tb_property_key_valid(key)) 618 return -EINVAL; 619 620 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA); 621 if (!property) 622 return -ENOMEM; 623 624 property->length = size / 4; 625 property->value.data = kzalloc(size, GFP_KERNEL); 626 if (!property->value.data) { 627 kfree(property); 628 return -ENOMEM; 629 } 630 631 memcpy(property->value.data, buf, buflen); 632 633 list_add_tail(&property->list, &parent->properties); 634 return 0; 635} 636EXPORT_SYMBOL_GPL(tb_property_add_data); 637 638/** 639 * tb_property_add_text() - Adds string property to directory 640 * @parent: Directory to add the property 641 * @key: Key for the property 642 * @text: String to add 643 * 644 * Function takes a copy of @text and adds it to the directory. 645 */ 646int tb_property_add_text(struct tb_property_dir *parent, const char *key, 647 const char *text) 648{ 649 /* Need to pad to dword boundary */ 650 size_t size = round_up(strlen(text) + 1, 4); 651 struct tb_property *property; 652 653 if (!tb_property_key_valid(key)) 654 return -EINVAL; 655 656 property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT); 657 if (!property) 658 return -ENOMEM; 659 660 property->length = size / 4; 661 property->value.text = kzalloc(size, GFP_KERNEL); 662 if (!property->value.text) { 663 kfree(property); 664 return -ENOMEM; 665 } 666 667 strcpy(property->value.text, text); 668 669 list_add_tail(&property->list, &parent->properties); 670 return 0; 671} 672EXPORT_SYMBOL_GPL(tb_property_add_text); 673 674/** 675 * tb_property_add_dir() - Adds a directory to the parent directory 676 * @parent: Directory to add the property 677 * @key: Key for the property 678 * @dir: Directory to add 679 */ 680int tb_property_add_dir(struct tb_property_dir *parent, const char *key, 681 struct tb_property_dir *dir) 682{ 683 struct tb_property *property; 684 685 if (!tb_property_key_valid(key)) 686 return -EINVAL; 687 688 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY); 689 if (!property) 690 return -ENOMEM; 691 692 property->value.dir = dir; 693 694 list_add_tail(&property->list, &parent->properties); 695 return 0; 696} 697EXPORT_SYMBOL_GPL(tb_property_add_dir); 698 699/** 700 * tb_property_remove() - Removes property from a parent directory 701 * @property: Property to remove 702 * 703 * Note memory for @property is released as well so it is not allowed to 704 * touch the object after call to this function. 705 */ 706void tb_property_remove(struct tb_property *property) 707{ 708 list_del(&property->list); 709 kfree(property); 710} 711EXPORT_SYMBOL_GPL(tb_property_remove); 712 713/** 714 * tb_property_find() - Find a property from a directory 715 * @dir: Directory where the property is searched 716 * @key: Key to look for 717 * @type: Type of the property 718 * 719 * Finds and returns property from the given directory. Does not recurse 720 * into sub-directories. Returns %NULL if the property was not found. 721 */ 722struct tb_property *tb_property_find(struct tb_property_dir *dir, 723 const char *key, enum tb_property_type type) 724{ 725 struct tb_property *property; 726 727 list_for_each_entry(property, &dir->properties, list) { 728 if (property->type == type && !strcmp(property->key, key)) 729 return property; 730 } 731 732 return NULL; 733} 734EXPORT_SYMBOL_GPL(tb_property_find); 735 736/** 737 * tb_property_get_next() - Get next property from directory 738 * @dir: Directory holding properties 739 * @prev: Previous property in the directory (%NULL returns the first) 740 */ 741struct tb_property *tb_property_get_next(struct tb_property_dir *dir, 742 struct tb_property *prev) 743{ 744 if (prev) { 745 if (list_is_last(&prev->list, &dir->properties)) 746 return NULL; 747 return list_next_entry(prev, list); 748 } 749 return list_first_entry_or_null(&dir->properties, struct tb_property, 750 list); 751} 752EXPORT_SYMBOL_GPL(tb_property_get_next);