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 16 struct 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 25 struct tb_property_rootdir_entry { 26 u32 magic; 27 u32 length; 28 struct tb_property_entry entries[]; 29 }; 30 31 struct tb_property_dir_entry { 32 u32 uuid[4]; 33 struct tb_property_entry entries[]; 34 }; 35 36 #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 37 38 static 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 42 static inline void parse_dwdata(void *dst, const void *src, size_t dwords) 43 { 44 be32_to_cpu_array(dst, src, dwords); 45 } 46 47 static inline void format_dwdata(void *dst, const void *src, size_t dwords) 48 { 49 cpu_to_be32_array(dst, src, dwords); 50 } 51 52 static 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 74 static bool tb_property_key_valid(const char *key) 75 { 76 return key && strlen(key) <= TB_PROPERTY_KEY_SIZE; 77 } 78 79 static struct tb_property * 80 tb_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 95 static 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 161 static 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 */ 220 struct 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 */ 242 struct 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 } 261 EXPORT_SYMBOL_GPL(tb_property_create_dir); 262 263 static 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 */ 293 void 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 } 307 EXPORT_SYMBOL_GPL(tb_property_free_dir); 308 309 static 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 348 static 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 */ 488 ssize_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_add_immediate() - Add immediate property to directory 506 * @parent: Directory to add the property 507 * @key: Key for the property 508 * @value: Immediate value to store with the property 509 */ 510 int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, 511 u32 value) 512 { 513 struct tb_property *property; 514 515 if (!tb_property_key_valid(key)) 516 return -EINVAL; 517 518 property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE); 519 if (!property) 520 return -ENOMEM; 521 522 property->length = 1; 523 property->value.immediate = value; 524 525 list_add_tail(&property->list, &parent->properties); 526 return 0; 527 } 528 EXPORT_SYMBOL_GPL(tb_property_add_immediate); 529 530 /** 531 * tb_property_add_data() - Adds arbitrary data property to directory 532 * @parent: Directory to add the property 533 * @key: Key for the property 534 * @buf: Data buffer to add 535 * @buflen: Number of bytes in the data buffer 536 * 537 * Function takes a copy of @buf and adds it to the directory. 538 */ 539 int tb_property_add_data(struct tb_property_dir *parent, const char *key, 540 const void *buf, size_t buflen) 541 { 542 /* Need to pad to dword boundary */ 543 size_t size = round_up(buflen, 4); 544 struct tb_property *property; 545 546 if (!tb_property_key_valid(key)) 547 return -EINVAL; 548 549 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA); 550 if (!property) 551 return -ENOMEM; 552 553 property->length = size / 4; 554 property->value.data = kzalloc(size, GFP_KERNEL); 555 if (!property->value.data) { 556 kfree(property); 557 return -ENOMEM; 558 } 559 560 memcpy(property->value.data, buf, buflen); 561 562 list_add_tail(&property->list, &parent->properties); 563 return 0; 564 } 565 EXPORT_SYMBOL_GPL(tb_property_add_data); 566 567 /** 568 * tb_property_add_text() - Adds string property to directory 569 * @parent: Directory to add the property 570 * @key: Key for the property 571 * @text: String to add 572 * 573 * Function takes a copy of @text and adds it to the directory. 574 */ 575 int tb_property_add_text(struct tb_property_dir *parent, const char *key, 576 const char *text) 577 { 578 /* Need to pad to dword boundary */ 579 size_t size = round_up(strlen(text) + 1, 4); 580 struct tb_property *property; 581 582 if (!tb_property_key_valid(key)) 583 return -EINVAL; 584 585 property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT); 586 if (!property) 587 return -ENOMEM; 588 589 property->length = size / 4; 590 property->value.text = kzalloc(size, GFP_KERNEL); 591 if (!property->value.text) { 592 kfree(property); 593 return -ENOMEM; 594 } 595 596 strcpy(property->value.text, text); 597 598 list_add_tail(&property->list, &parent->properties); 599 return 0; 600 } 601 EXPORT_SYMBOL_GPL(tb_property_add_text); 602 603 /** 604 * tb_property_add_dir() - Adds a directory to the parent directory 605 * @parent: Directory to add the property 606 * @key: Key for the property 607 * @dir: Directory to add 608 */ 609 int tb_property_add_dir(struct tb_property_dir *parent, const char *key, 610 struct tb_property_dir *dir) 611 { 612 struct tb_property *property; 613 614 if (!tb_property_key_valid(key)) 615 return -EINVAL; 616 617 property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY); 618 if (!property) 619 return -ENOMEM; 620 621 property->value.dir = dir; 622 623 list_add_tail(&property->list, &parent->properties); 624 return 0; 625 } 626 EXPORT_SYMBOL_GPL(tb_property_add_dir); 627 628 /** 629 * tb_property_remove() - Removes property from a parent directory 630 * @property: Property to remove 631 * 632 * Note memory for @property is released as well so it is not allowed to 633 * touch the object after call to this function. 634 */ 635 void tb_property_remove(struct tb_property *property) 636 { 637 list_del(&property->list); 638 kfree(property); 639 } 640 EXPORT_SYMBOL_GPL(tb_property_remove); 641 642 /** 643 * tb_property_find() - Find a property from a directory 644 * @dir: Directory where the property is searched 645 * @key: Key to look for 646 * @type: Type of the property 647 * 648 * Finds and returns property from the given directory. Does not recurse 649 * into sub-directories. Returns %NULL if the property was not found. 650 */ 651 struct tb_property *tb_property_find(struct tb_property_dir *dir, 652 const char *key, enum tb_property_type type) 653 { 654 struct tb_property *property; 655 656 list_for_each_entry(property, &dir->properties, list) { 657 if (property->type == type && !strcmp(property->key, key)) 658 return property; 659 } 660 661 return NULL; 662 } 663 EXPORT_SYMBOL_GPL(tb_property_find); 664 665 /** 666 * tb_property_get_next() - Get next property from directory 667 * @dir: Directory holding properties 668 * @prev: Previous property in the directory (%NULL returns the first) 669 */ 670 struct tb_property *tb_property_get_next(struct tb_property_dir *dir, 671 struct tb_property *prev) 672 { 673 if (prev) { 674 if (list_is_last(&prev->list, &dir->properties)) 675 return NULL; 676 return list_next_entry(prev, list); 677 } 678 return list_first_entry_or_null(&dir->properties, struct tb_property, 679 list); 680 } 681 EXPORT_SYMBOL_GPL(tb_property_get_next); 682