Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Thunderbolt XDomain property support
0004  *
0005  * Copyright (C) 2017, Intel Corporation
0006  * Authors: Michael Jamet <michael.jamet@intel.com>
0007  *          Mika Westerberg <mika.westerberg@linux.intel.com>
0008  */
0009 
0010 #include <linux/err.h>
0011 #include <linux/slab.h>
0012 #include <linux/string.h>
0013 #include <linux/uuid.h>
0014 #include <linux/thunderbolt.h>
0015 
0016 struct tb_property_entry {
0017     u32 key_hi;
0018     u32 key_lo;
0019     u16 length;
0020     u8 reserved;
0021     u8 type;
0022     u32 value;
0023 };
0024 
0025 struct tb_property_rootdir_entry {
0026     u32 magic;
0027     u32 length;
0028     struct tb_property_entry entries[];
0029 };
0030 
0031 struct tb_property_dir_entry {
0032     u32 uuid[4];
0033     struct tb_property_entry entries[];
0034 };
0035 
0036 #define TB_PROPERTY_ROOTDIR_MAGIC   0x55584401
0037 
0038 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
0039     size_t block_len, unsigned int dir_offset, size_t dir_len,
0040     bool is_root);
0041 
0042 static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
0043 {
0044     be32_to_cpu_array(dst, src, dwords);
0045 }
0046 
0047 static inline void format_dwdata(void *dst, const void *src, size_t dwords)
0048 {
0049     cpu_to_be32_array(dst, src, dwords);
0050 }
0051 
0052 static bool tb_property_entry_valid(const struct tb_property_entry *entry,
0053                   size_t block_len)
0054 {
0055     switch (entry->type) {
0056     case TB_PROPERTY_TYPE_DIRECTORY:
0057     case TB_PROPERTY_TYPE_DATA:
0058     case TB_PROPERTY_TYPE_TEXT:
0059         if (entry->length > block_len)
0060             return false;
0061         if (entry->value + entry->length > block_len)
0062             return false;
0063         break;
0064 
0065     case TB_PROPERTY_TYPE_VALUE:
0066         if (entry->length != 1)
0067             return false;
0068         break;
0069     }
0070 
0071     return true;
0072 }
0073 
0074 static bool tb_property_key_valid(const char *key)
0075 {
0076     return key && strlen(key) <= TB_PROPERTY_KEY_SIZE;
0077 }
0078 
0079 static struct tb_property *
0080 tb_property_alloc(const char *key, enum tb_property_type type)
0081 {
0082     struct tb_property *property;
0083 
0084     property = kzalloc(sizeof(*property), GFP_KERNEL);
0085     if (!property)
0086         return NULL;
0087 
0088     strcpy(property->key, key);
0089     property->type = type;
0090     INIT_LIST_HEAD(&property->list);
0091 
0092     return property;
0093 }
0094 
0095 static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
0096                     const struct tb_property_entry *entry)
0097 {
0098     char key[TB_PROPERTY_KEY_SIZE + 1];
0099     struct tb_property *property;
0100     struct tb_property_dir *dir;
0101 
0102     if (!tb_property_entry_valid(entry, block_len))
0103         return NULL;
0104 
0105     parse_dwdata(key, entry, 2);
0106     key[TB_PROPERTY_KEY_SIZE] = '\0';
0107 
0108     property = tb_property_alloc(key, entry->type);
0109     if (!property)
0110         return NULL;
0111 
0112     property->length = entry->length;
0113 
0114     switch (property->type) {
0115     case TB_PROPERTY_TYPE_DIRECTORY:
0116         dir = __tb_property_parse_dir(block, block_len, entry->value,
0117                           entry->length, false);
0118         if (!dir) {
0119             kfree(property);
0120             return NULL;
0121         }
0122         property->value.dir = dir;
0123         break;
0124 
0125     case TB_PROPERTY_TYPE_DATA:
0126         property->value.data = kcalloc(property->length, sizeof(u32),
0127                            GFP_KERNEL);
0128         if (!property->value.data) {
0129             kfree(property);
0130             return NULL;
0131         }
0132         parse_dwdata(property->value.data, block + entry->value,
0133                  entry->length);
0134         break;
0135 
0136     case TB_PROPERTY_TYPE_TEXT:
0137         property->value.text = kcalloc(property->length, sizeof(u32),
0138                            GFP_KERNEL);
0139         if (!property->value.text) {
0140             kfree(property);
0141             return NULL;
0142         }
0143         parse_dwdata(property->value.text, block + entry->value,
0144                  entry->length);
0145         /* Force null termination */
0146         property->value.text[property->length * 4 - 1] = '\0';
0147         break;
0148 
0149     case TB_PROPERTY_TYPE_VALUE:
0150         property->value.immediate = entry->value;
0151         break;
0152 
0153     default:
0154         property->type = TB_PROPERTY_TYPE_UNKNOWN;
0155         break;
0156     }
0157 
0158     return property;
0159 }
0160 
0161 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
0162     size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
0163 {
0164     const struct tb_property_entry *entries;
0165     size_t i, content_len, nentries;
0166     unsigned int content_offset;
0167     struct tb_property_dir *dir;
0168 
0169     dir = kzalloc(sizeof(*dir), GFP_KERNEL);
0170     if (!dir)
0171         return NULL;
0172 
0173     if (is_root) {
0174         content_offset = dir_offset + 2;
0175         content_len = dir_len;
0176     } else {
0177         dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
0178                     GFP_KERNEL);
0179         if (!dir->uuid) {
0180             tb_property_free_dir(dir);
0181             return NULL;
0182         }
0183         content_offset = dir_offset + 4;
0184         content_len = dir_len - 4; /* Length includes UUID */
0185     }
0186 
0187     entries = (const struct tb_property_entry *)&block[content_offset];
0188     nentries = content_len / (sizeof(*entries) / 4);
0189 
0190     INIT_LIST_HEAD(&dir->properties);
0191 
0192     for (i = 0; i < nentries; i++) {
0193         struct tb_property *property;
0194 
0195         property = tb_property_parse(block, block_len, &entries[i]);
0196         if (!property) {
0197             tb_property_free_dir(dir);
0198             return NULL;
0199         }
0200 
0201         list_add_tail(&property->list, &dir->properties);
0202     }
0203 
0204     return dir;
0205 }
0206 
0207 /**
0208  * tb_property_parse_dir() - Parses properties from given property block
0209  * @block: Property block to parse
0210  * @block_len: Number of dword elements in the property block
0211  *
0212  * This function parses the XDomain properties data block into format that
0213  * can be traversed using the helper functions provided by this module.
0214  * Upon success returns the parsed directory. In case of error returns
0215  * %NULL. The resulting &struct tb_property_dir needs to be released by
0216  * calling tb_property_free_dir() when not needed anymore.
0217  *
0218  * The @block is expected to be root directory.
0219  */
0220 struct tb_property_dir *tb_property_parse_dir(const u32 *block,
0221                           size_t block_len)
0222 {
0223     const struct tb_property_rootdir_entry *rootdir =
0224         (const struct tb_property_rootdir_entry *)block;
0225 
0226     if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC)
0227         return NULL;
0228     if (rootdir->length > block_len)
0229         return NULL;
0230 
0231     return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
0232                        true);
0233 }
0234 
0235 /**
0236  * tb_property_create_dir() - Creates new property directory
0237  * @uuid: UUID used to identify the particular directory
0238  *
0239  * Creates new, empty property directory. If @uuid is %NULL then the
0240  * directory is assumed to be root directory.
0241  */
0242 struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid)
0243 {
0244     struct tb_property_dir *dir;
0245 
0246     dir = kzalloc(sizeof(*dir), GFP_KERNEL);
0247     if (!dir)
0248         return NULL;
0249 
0250     INIT_LIST_HEAD(&dir->properties);
0251     if (uuid) {
0252         dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL);
0253         if (!dir->uuid) {
0254             kfree(dir);
0255             return NULL;
0256         }
0257     }
0258 
0259     return dir;
0260 }
0261 EXPORT_SYMBOL_GPL(tb_property_create_dir);
0262 
0263 static void tb_property_free(struct tb_property *property)
0264 {
0265     switch (property->type) {
0266     case TB_PROPERTY_TYPE_DIRECTORY:
0267         tb_property_free_dir(property->value.dir);
0268         break;
0269 
0270     case TB_PROPERTY_TYPE_DATA:
0271         kfree(property->value.data);
0272         break;
0273 
0274     case TB_PROPERTY_TYPE_TEXT:
0275         kfree(property->value.text);
0276         break;
0277 
0278     default:
0279         break;
0280     }
0281 
0282     kfree(property);
0283 }
0284 
0285 /**
0286  * tb_property_free_dir() - Release memory allocated for property directory
0287  * @dir: Directory to release
0288  *
0289  * This will release all the memory the directory occupies including all
0290  * descendants. It is OK to pass %NULL @dir, then the function does
0291  * nothing.
0292  */
0293 void tb_property_free_dir(struct tb_property_dir *dir)
0294 {
0295     struct tb_property *property, *tmp;
0296 
0297     if (!dir)
0298         return;
0299 
0300     list_for_each_entry_safe(property, tmp, &dir->properties, list) {
0301         list_del(&property->list);
0302         tb_property_free(property);
0303     }
0304     kfree(dir->uuid);
0305     kfree(dir);
0306 }
0307 EXPORT_SYMBOL_GPL(tb_property_free_dir);
0308 
0309 static size_t tb_property_dir_length(const struct tb_property_dir *dir,
0310                      bool recurse, size_t *data_len)
0311 {
0312     const struct tb_property *property;
0313     size_t len = 0;
0314 
0315     if (dir->uuid)
0316         len += sizeof(*dir->uuid) / 4;
0317     else
0318         len += sizeof(struct tb_property_rootdir_entry) / 4;
0319 
0320     list_for_each_entry(property, &dir->properties, list) {
0321         len += sizeof(struct tb_property_entry) / 4;
0322 
0323         switch (property->type) {
0324         case TB_PROPERTY_TYPE_DIRECTORY:
0325             if (recurse) {
0326                 len += tb_property_dir_length(
0327                     property->value.dir, recurse, data_len);
0328             }
0329             /* Reserve dword padding after each directory */
0330             if (data_len)
0331                 *data_len += 1;
0332             break;
0333 
0334         case TB_PROPERTY_TYPE_DATA:
0335         case TB_PROPERTY_TYPE_TEXT:
0336             if (data_len)
0337                 *data_len += property->length;
0338             break;
0339 
0340         default:
0341             break;
0342         }
0343     }
0344 
0345     return len;
0346 }
0347 
0348 static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir,
0349     u32 *block, unsigned int start_offset, size_t block_len)
0350 {
0351     unsigned int data_offset, dir_end;
0352     const struct tb_property *property;
0353     struct tb_property_entry *entry;
0354     size_t dir_len, data_len = 0;
0355     int ret;
0356 
0357     /*
0358      * The structure of property block looks like following. Leaf
0359      * data/text is included right after the directory and each
0360      * directory follows each other (even nested ones).
0361      *
0362      * +----------+ <-- start_offset
0363      * |  header  | <-- root directory header
0364      * +----------+ ---
0365      * |  entry 0 | -^--------------------.
0366      * +----------+  |                    |
0367      * |  entry 1 | -|--------------------|--.
0368      * +----------+  |                    |  |
0369      * |  entry 2 | -|-----------------.  |  |
0370      * +----------+  |                 |  |  |
0371      * :          :  |  dir_len        |  |  |
0372      * .          .  |                 |  |  |
0373      * :          :  |                 |  |  |
0374      * +----------+  |                 |  |  |
0375      * |  entry n |  v                 |  |  |
0376      * +----------+ <-- data_offset    |  |  |
0377      * |  data 0  | <------------------|--'  |
0378      * +----------+                    |     |
0379      * |  data 1  | <------------------|-----'
0380      * +----------+                    |
0381      * | 00000000 | padding            |
0382      * +----------+ <-- dir_end <------'
0383      * |   UUID   | <-- directory UUID (child directory)
0384      * +----------+
0385      * |  entry 0 |
0386      * +----------+
0387      * |  entry 1 |
0388      * +----------+
0389      * :          :
0390      * .          .
0391      * :          :
0392      * +----------+
0393      * |  entry n |
0394      * +----------+
0395      * |  data 0  |
0396      * +----------+
0397      *
0398      * We use dir_end to hold pointer to the end of the directory. It
0399      * will increase as we add directories and each directory should be
0400      * added starting from previous dir_end.
0401      */
0402     dir_len = tb_property_dir_length(dir, false, &data_len);
0403     data_offset = start_offset + dir_len;
0404     dir_end = start_offset + data_len + dir_len;
0405 
0406     if (data_offset > dir_end)
0407         return -EINVAL;
0408     if (dir_end > block_len)
0409         return -EINVAL;
0410 
0411     /* Write headers first */
0412     if (dir->uuid) {
0413         struct tb_property_dir_entry *pe;
0414 
0415         pe = (struct tb_property_dir_entry *)&block[start_offset];
0416         memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid));
0417         entry = pe->entries;
0418     } else {
0419         struct tb_property_rootdir_entry *re;
0420 
0421         re = (struct tb_property_rootdir_entry *)&block[start_offset];
0422         re->magic = TB_PROPERTY_ROOTDIR_MAGIC;
0423         re->length = dir_len - sizeof(*re) / 4;
0424         entry = re->entries;
0425     }
0426 
0427     list_for_each_entry(property, &dir->properties, list) {
0428         const struct tb_property_dir *child;
0429 
0430         format_dwdata(entry, property->key, 2);
0431         entry->type = property->type;
0432 
0433         switch (property->type) {
0434         case TB_PROPERTY_TYPE_DIRECTORY:
0435             child = property->value.dir;
0436             ret = __tb_property_format_dir(child, block, dir_end,
0437                                block_len);
0438             if (ret < 0)
0439                 return ret;
0440             entry->length = tb_property_dir_length(child, false,
0441                                    NULL);
0442             entry->value = dir_end;
0443             dir_end = ret;
0444             break;
0445 
0446         case TB_PROPERTY_TYPE_DATA:
0447             format_dwdata(&block[data_offset], property->value.data,
0448                       property->length);
0449             entry->length = property->length;
0450             entry->value = data_offset;
0451             data_offset += entry->length;
0452             break;
0453 
0454         case TB_PROPERTY_TYPE_TEXT:
0455             format_dwdata(&block[data_offset], property->value.text,
0456                       property->length);
0457             entry->length = property->length;
0458             entry->value = data_offset;
0459             data_offset += entry->length;
0460             break;
0461 
0462         case TB_PROPERTY_TYPE_VALUE:
0463             entry->length = property->length;
0464             entry->value = property->value.immediate;
0465             break;
0466 
0467         default:
0468             break;
0469         }
0470 
0471         entry++;
0472     }
0473 
0474     return dir_end;
0475 }
0476 
0477 /**
0478  * tb_property_format_dir() - Formats directory to the packed XDomain format
0479  * @dir: Directory to format
0480  * @block: Property block where the packed data is placed
0481  * @block_len: Length of the property block
0482  *
0483  * This function formats the directory to the packed format that can be
0484  * then send over the thunderbolt fabric to receiving host. Returns %0 in
0485  * case of success and negative errno on faulure. Passing %NULL in @block
0486  * returns number of entries the block takes.
0487  */
0488 ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block,
0489                    size_t block_len)
0490 {
0491     ssize_t ret;
0492 
0493     if (!block) {
0494         size_t dir_len, data_len = 0;
0495 
0496         dir_len = tb_property_dir_length(dir, true, &data_len);
0497         return dir_len + data_len;
0498     }
0499 
0500     ret = __tb_property_format_dir(dir, block, 0, block_len);
0501     return ret < 0 ? ret : 0;
0502 }
0503 
0504 /**
0505  * tb_property_copy_dir() - Take a deep copy of directory
0506  * @dir: Directory to copy
0507  *
0508  * This function takes a deep copy of @dir and returns back the copy. In
0509  * case of error returns %NULL. The resulting directory needs to be
0510  * released by calling tb_property_free_dir().
0511  */
0512 struct tb_property_dir *tb_property_copy_dir(const struct tb_property_dir *dir)
0513 {
0514     struct tb_property *property, *p = NULL;
0515     struct tb_property_dir *d;
0516 
0517     if (!dir)
0518         return NULL;
0519 
0520     d = tb_property_create_dir(dir->uuid);
0521     if (!d)
0522         return NULL;
0523 
0524     list_for_each_entry(property, &dir->properties, list) {
0525         struct tb_property *p;
0526 
0527         p = tb_property_alloc(property->key, property->type);
0528         if (!p)
0529             goto err_free;
0530 
0531         p->length = property->length;
0532 
0533         switch (property->type) {
0534         case TB_PROPERTY_TYPE_DIRECTORY:
0535             p->value.dir = tb_property_copy_dir(property->value.dir);
0536             if (!p->value.dir)
0537                 goto err_free;
0538             break;
0539 
0540         case TB_PROPERTY_TYPE_DATA:
0541             p->value.data = kmemdup(property->value.data,
0542                         property->length * 4,
0543                         GFP_KERNEL);
0544             if (!p->value.data)
0545                 goto err_free;
0546             break;
0547 
0548         case TB_PROPERTY_TYPE_TEXT:
0549             p->value.text = kzalloc(p->length * 4, GFP_KERNEL);
0550             if (!p->value.text)
0551                 goto err_free;
0552             strcpy(p->value.text, property->value.text);
0553             break;
0554 
0555         case TB_PROPERTY_TYPE_VALUE:
0556             p->value.immediate = property->value.immediate;
0557             break;
0558 
0559         default:
0560             break;
0561         }
0562 
0563         list_add_tail(&p->list, &d->properties);
0564     }
0565 
0566     return d;
0567 
0568 err_free:
0569     kfree(p);
0570     tb_property_free_dir(d);
0571 
0572     return NULL;
0573 }
0574 
0575 /**
0576  * tb_property_add_immediate() - Add immediate property to directory
0577  * @parent: Directory to add the property
0578  * @key: Key for the property
0579  * @value: Immediate value to store with the property
0580  */
0581 int tb_property_add_immediate(struct tb_property_dir *parent, const char *key,
0582                   u32 value)
0583 {
0584     struct tb_property *property;
0585 
0586     if (!tb_property_key_valid(key))
0587         return -EINVAL;
0588 
0589     property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE);
0590     if (!property)
0591         return -ENOMEM;
0592 
0593     property->length = 1;
0594     property->value.immediate = value;
0595 
0596     list_add_tail(&property->list, &parent->properties);
0597     return 0;
0598 }
0599 EXPORT_SYMBOL_GPL(tb_property_add_immediate);
0600 
0601 /**
0602  * tb_property_add_data() - Adds arbitrary data property to directory
0603  * @parent: Directory to add the property
0604  * @key: Key for the property
0605  * @buf: Data buffer to add
0606  * @buflen: Number of bytes in the data buffer
0607  *
0608  * Function takes a copy of @buf and adds it to the directory.
0609  */
0610 int tb_property_add_data(struct tb_property_dir *parent, const char *key,
0611              const void *buf, size_t buflen)
0612 {
0613     /* Need to pad to dword boundary */
0614     size_t size = round_up(buflen, 4);
0615     struct tb_property *property;
0616 
0617     if (!tb_property_key_valid(key))
0618         return -EINVAL;
0619 
0620     property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA);
0621     if (!property)
0622         return -ENOMEM;
0623 
0624     property->length = size / 4;
0625     property->value.data = kzalloc(size, GFP_KERNEL);
0626     if (!property->value.data) {
0627         kfree(property);
0628         return -ENOMEM;
0629     }
0630 
0631     memcpy(property->value.data, buf, buflen);
0632 
0633     list_add_tail(&property->list, &parent->properties);
0634     return 0;
0635 }
0636 EXPORT_SYMBOL_GPL(tb_property_add_data);
0637 
0638 /**
0639  * tb_property_add_text() - Adds string property to directory
0640  * @parent: Directory to add the property
0641  * @key: Key for the property
0642  * @text: String to add
0643  *
0644  * Function takes a copy of @text and adds it to the directory.
0645  */
0646 int tb_property_add_text(struct tb_property_dir *parent, const char *key,
0647              const char *text)
0648 {
0649     /* Need to pad to dword boundary */
0650     size_t size = round_up(strlen(text) + 1, 4);
0651     struct tb_property *property;
0652 
0653     if (!tb_property_key_valid(key))
0654         return -EINVAL;
0655 
0656     property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT);
0657     if (!property)
0658         return -ENOMEM;
0659 
0660     property->length = size / 4;
0661     property->value.text = kzalloc(size, GFP_KERNEL);
0662     if (!property->value.text) {
0663         kfree(property);
0664         return -ENOMEM;
0665     }
0666 
0667     strcpy(property->value.text, text);
0668 
0669     list_add_tail(&property->list, &parent->properties);
0670     return 0;
0671 }
0672 EXPORT_SYMBOL_GPL(tb_property_add_text);
0673 
0674 /**
0675  * tb_property_add_dir() - Adds a directory to the parent directory
0676  * @parent: Directory to add the property
0677  * @key: Key for the property
0678  * @dir: Directory to add
0679  */
0680 int tb_property_add_dir(struct tb_property_dir *parent, const char *key,
0681             struct tb_property_dir *dir)
0682 {
0683     struct tb_property *property;
0684 
0685     if (!tb_property_key_valid(key))
0686         return -EINVAL;
0687 
0688     property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY);
0689     if (!property)
0690         return -ENOMEM;
0691 
0692     property->value.dir = dir;
0693 
0694     list_add_tail(&property->list, &parent->properties);
0695     return 0;
0696 }
0697 EXPORT_SYMBOL_GPL(tb_property_add_dir);
0698 
0699 /**
0700  * tb_property_remove() - Removes property from a parent directory
0701  * @property: Property to remove
0702  *
0703  * Note memory for @property is released as well so it is not allowed to
0704  * touch the object after call to this function.
0705  */
0706 void tb_property_remove(struct tb_property *property)
0707 {
0708     list_del(&property->list);
0709     kfree(property);
0710 }
0711 EXPORT_SYMBOL_GPL(tb_property_remove);
0712 
0713 /**
0714  * tb_property_find() - Find a property from a directory
0715  * @dir: Directory where the property is searched
0716  * @key: Key to look for
0717  * @type: Type of the property
0718  *
0719  * Finds and returns property from the given directory. Does not recurse
0720  * into sub-directories. Returns %NULL if the property was not found.
0721  */
0722 struct tb_property *tb_property_find(struct tb_property_dir *dir,
0723     const char *key, enum tb_property_type type)
0724 {
0725     struct tb_property *property;
0726 
0727     list_for_each_entry(property, &dir->properties, list) {
0728         if (property->type == type && !strcmp(property->key, key))
0729             return property;
0730     }
0731 
0732     return NULL;
0733 }
0734 EXPORT_SYMBOL_GPL(tb_property_find);
0735 
0736 /**
0737  * tb_property_get_next() - Get next property from directory
0738  * @dir: Directory holding properties
0739  * @prev: Previous property in the directory (%NULL returns the first)
0740  */
0741 struct tb_property *tb_property_get_next(struct tb_property_dir *dir,
0742                      struct tb_property *prev)
0743 {
0744     if (prev) {
0745         if (list_is_last(&prev->list, &dir->properties))
0746             return NULL;
0747         return list_next_entry(prev, list);
0748     }
0749     return list_first_entry_or_null(&dir->properties, struct tb_property,
0750                     list);
0751 }
0752 EXPORT_SYMBOL_GPL(tb_property_get_next);