753 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Thunderbolt XDomain property support4 *5 * Copyright (C) 2017, Intel Corporation6 * 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 0x5558440137 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 block209 * @block: Property block to parse210 * @block_len: Number of dword elements in the property block211 *212 * This function parses the XDomain properties data block into format that213 * can be traversed using the helper functions provided by this module.214 * Upon success returns the parsed directory. In case of error returns215 * %NULL. The resulting &struct tb_property_dir needs to be released by216 * 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 directory237 * @uuid: UUID used to identify the particular directory238 *239 * Creates new, empty property directory. If @uuid is %NULL then the240 * 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 directory287 * @dir: Directory to release288 *289 * This will release all the memory the directory occupies including all290 * descendants. It is OK to pass %NULL @dir, then the function does291 * 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 else318 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. Leaf359 * data/text is included right after the directory and each360 * directory follows each other (even nested ones).361 *362 * +----------+ <-- start_offset363 * | header | <-- root directory header364 * +----------+ ---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. It399 * will increase as we add directories and each directory should be400 * 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 format479 * @dir: Directory to format480 * @block: Property block where the packed data is placed481 * @block_len: Length of the property block482 *483 * This function formats the directory to the packed format that can be484 * then send over the thunderbolt fabric to receiving host. Returns %0 in485 * case of success and negative errno on faulure. Passing %NULL in @block486 * 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 directory506 * @dir: Directory to copy507 *508 * This function takes a deep copy of @dir and returns back the copy. In509 * case of error returns %NULL. The resulting directory needs to be510 * 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 directory577 * @parent: Directory to add the property578 * @key: Key for the property579 * @value: Immediate value to store with the property580 */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 directory603 * @parent: Directory to add the property604 * @key: Key for the property605 * @buf: Data buffer to add606 * @buflen: Number of bytes in the data buffer607 *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 directory640 * @parent: Directory to add the property641 * @key: Key for the property642 * @text: String to add643 *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 directory676 * @parent: Directory to add the property677 * @key: Key for the property678 * @dir: Directory to add679 */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 directory701 * @property: Property to remove702 *703 * Note memory for @property is released as well so it is not allowed to704 * 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 directory715 * @dir: Directory where the property is searched716 * @key: Key to look for717 * @type: Type of the property718 *719 * Finds and returns property from the given directory. Does not recurse720 * 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 directory738 * @dir: Directory holding properties739 * @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);753